using MrGameEng.Graphics; using Xunit; namespace MrGameEng.Graphics.Tests; public class SpriteBatcherTests { private static SpriteInstance Instance(byte layer) => new() { Layer = layer }; [Fact] public void Sort_OrdersByKey_ReturningOriginalIndices() { var batcher = new SpriteBatcher(initialCapacity: 2); batcher.Submit(Instance(2), SpriteSortKey.Make(2, 0f, 0)); batcher.Submit(Instance(0), SpriteSortKey.Make(0, 0f, 0)); batcher.Submit(Instance(1), SpriteSortKey.Make(1, 0f, 0)); var order = batcher.Sort(); Assert.Equal(3, order.Length); Assert.Equal(0, batcher[order[0]].Layer); Assert.Equal(1, batcher[order[1]].Layer); Assert.Equal(2, batcher[order[2]].Layer); } [Fact] public void Submit_GrowsBeyondInitialCapacity() { var batcher = new SpriteBatcher(initialCapacity: 1); for (var i = 0; i < 100; i++) { batcher.Submit(Instance(0), (ulong)(100 - i)); } Assert.Equal(100, batcher.Count); var order = batcher.Sort(); Assert.Equal(99, order[0]); // последний сабмит имеет наименьший ключ } [Fact] public void Sort_IsStable_EqualKeysKeepSubmissionOrder() { var batcher = new SpriteBatcher(); var key = SpriteSortKey.Make(1, 5f, 7); for (byte i = 0; i < 50; i++) { batcher.Submit(Instance(i), key); // одинаковый ключ у всех } var order = batcher.Sort(); for (var i = 0; i < 50; i++) { Assert.Equal(i, order[i]); } } [Fact] public void Sort_IsStable_WithinMixedKeys() { var batcher = new SpriteBatcher(); var keyA = SpriteSortKey.Make(0, 0f, 1); var keyB = SpriteSortKey.Make(2, 3f, 9); // Чередуем два ключа: внутри каждой группы порядок сабмита должен сохраниться. for (byte i = 0; i < 20; i++) { batcher.Submit(Instance(i), i % 2 == 0 ? keyA : keyB); } var order = batcher.Sort(); var expectedA = new[] { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 }; var expectedB = new[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 }; Assert.Equal(expectedA, order[..10].ToArray()); Assert.Equal(expectedB, order[10..].ToArray()); } [Fact] public void Sort_LargeRandomSet_FullyOrdered() { var batcher = new SpriteBatcher(initialCapacity: 16); var random = new Random(123); var keys = new ulong[5000]; for (var i = 0; i < keys.Length; i++) { keys[i] = (ulong)random.NextInt64(); batcher.Submit(Instance(0), keys[i]); } var order = batcher.Sort(); for (var i = 1; i < order.Length; i++) { Assert.True(keys[order[i - 1]] <= keys[order[i]]); } } [Fact] public void ChunkedSubmit_MergesInChunkOrder_AndCountsCulled() { var batcher = new SpriteBatcher(); var key = SpriteSortKey.Make(0, 0f, 0); // одинаковый ключ — порядок задаётся слиянием чанков batcher.BeginChunks([3, 2]); var writer1 = batcher.GetChunkWriter(1); // чанки могут заполняться в любом порядке (параллельно) writer1.Add(Instance(10), key); writer1.AddCulled(); batcher.EndChunk(1, in writer1); var writer0 = batcher.GetChunkWriter(0); writer0.Add(Instance(1), key); writer0.Add(Instance(2), key); batcher.EndChunk(0, in writer0); var accepted = batcher.CommitChunks(); Assert.Equal(3, accepted); Assert.Equal(1, batcher.LastChunkCulled); var order = batcher.Sort(); Assert.Equal(1, batcher[order[0]].Layer); // сначала чанк 0... Assert.Equal(2, batcher[order[1]].Layer); Assert.Equal(10, batcher[order[2]].Layer); // ...затем чанк 1 — стабильно } [Fact] public void ChunkedSubmit_GrowsMainArrays_WhenNeeded() { var batcher = new SpriteBatcher(initialCapacity: 2); batcher.BeginChunks([10]); var writer = batcher.GetChunkWriter(0); for (byte i = 0; i < 10; i++) { writer.Add(Instance(i), i); } batcher.EndChunk(0, in writer); Assert.Equal(10, batcher.CommitChunks()); Assert.Equal(10, batcher.Count); Assert.Equal(0, batcher[batcher.Sort()[0]].Layer); } [Fact] public void Clear_ResetsCount_KeepsWorking() { var batcher = new SpriteBatcher(); batcher.Submit(Instance(0), 5); batcher.Clear(); Assert.Equal(0, batcher.Count); batcher.Submit(Instance(1), 1); Assert.Equal(1, batcher.Count); Assert.Equal(1, batcher[batcher.Sort()[0]].Layer); } }