Files

160 lines
5.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MrGameEng.Atlases;
using Xunit;
namespace MrGameEng.Atlases.Tests;
public class ShelfPackerTests
{
private static List<PackItem> Squares(int count, int size) =>
Enumerable.Range(0, count).Select(i => new PackItem($"item{i:D3}", size, size)).ToList();
[Fact]
public void Pack_PlacesEveryItem_WithinPageBounds()
{
var result = ShelfPacker.Pack(Squares(50, 60), maxPageSize: 256, padding: 2);
Assert.Equal(50, result.Placements.Count);
foreach (var p in result.Placements)
{
var (pageWidth, pageHeight) = result.PageSizes[p.Page];
Assert.True(p.X >= 2 && p.Y >= 2);
Assert.True(p.X + p.Width <= pageWidth);
Assert.True(p.Y + p.Height <= pageHeight);
}
}
[Fact]
public void Pack_NoTwoPlacements_Overlap()
{
var items = new List<PackItem>();
var random = new Random(42);
for (var i = 0; i < 200; i++)
{
items.Add(new PackItem($"r{i:D3}", random.Next(4, 90), random.Next(4, 90)));
}
var result = ShelfPacker.Pack(items, maxPageSize: 512, padding: 2);
var byPage = result.Placements.GroupBy(p => p.Page);
foreach (var page in byPage)
{
var list = page.ToList();
for (var i = 0; i < list.Count; i++)
{
for (var j = i + 1; j < list.Count; j++)
{
var a = list[i];
var b = list[j];
var separated =
a.X + a.Width + 2 <= b.X
|| b.X + b.Width + 2 <= a.X
|| a.Y + a.Height + 2 <= b.Y
|| b.Y + b.Height + 2 <= a.Y;
Assert.True(separated, $"{a.Key} overlaps {b.Key} (padding included)");
}
}
}
}
[Fact]
public void Pack_Overflows_ToMultiplePages()
{
// 9 квадратов 100² на страницу 256² помещаются максимум по 4.
var result = ShelfPacker.Pack(Squares(9, 100), maxPageSize: 256, padding: 2);
Assert.True(result.PageSizes.Count >= 3);
Assert.Equal(
Enumerable.Range(0, result.PageSizes.Count),
result.Placements.Select(p => p.Page).Distinct().Order()
);
}
[Fact]
public void Pack_OversizedItem_GetsDedicatedPage()
{
var items = Squares(2, 30);
items.Add(new PackItem("huge", 500, 40));
var result = ShelfPacker.Pack(items, maxPageSize: 256, padding: 2);
var huge = result.Placements.Single(p => p.Key == "huge");
Assert.Single(result.Placements, p => p.Page == huge.Page);
Assert.True(result.PageSizes[huge.Page].Width >= 504);
}
[Fact]
public void Pack_IsDeterministic_RegardlessOfInputOrder()
{
var items = Squares(30, 20)
.Concat(Squares(10, 50).Select(i => i with { Key = "b" + i.Key }))
.ToList();
var shuffled = items.AsEnumerable().Reverse().ToList();
var a = ShelfPacker.Pack(items, 128, 2);
var b = ShelfPacker.Pack(shuffled, 128, 2);
Assert.Equal(
a.Placements.OrderBy(p => p.Key, StringComparer.Ordinal),
b.Placements.OrderBy(p => p.Key, StringComparer.Ordinal)
);
}
[Fact]
public void Pack_TrimsPages_ToPowerOfTwo()
{
var result = ShelfPacker.Pack(Squares(1, 50), maxPageSize: 2048, padding: 2);
Assert.Equal((64, 64), result.PageSizes.Single());
}
[Theory]
[InlineData(1, 1)]
[InlineData(64, 64)]
[InlineData(65, 128)]
[InlineData(2048, 2048)]
public void NextPowerOfTwo_RoundsUp(int value, int expected)
{
Assert.Equal(expected, ShelfPacker.NextPowerOfTwo(value));
}
[Fact]
public void Pack_NonPowerOfTwoLimit_SharedPagesNeverExceedIt()
{
// 40² с padding на лимит 100: POT-округление дало бы 128 > лимита.
var result = ShelfPacker.Pack(Squares(10, 40), maxPageSize: 100, padding: 2);
Assert.All(
result.PageSizes,
size =>
{
Assert.True(size.Width <= 100, $"page width {size.Width} > 100");
Assert.True(size.Height <= 100, $"page height {size.Height} > 100");
}
);
}
[Fact]
public void Pack_OversizedItem_PageHasExactPaddedSize()
{
var result = ShelfPacker.Pack([new PackItem("big", 300, 50)], maxPageSize: 128, padding: 2);
Assert.Equal((304, 54), result.PageSizes.Single());
}
[Fact]
public void Pack_OversizedAmongSmall_DoesNotSplitSharedPage()
{
// Широкий и низкий негабарит сортируется в конец по высоте; раньше он закрывал
// наполовину заполненную общую страницу, и остаток уезжал на новую.
var items = new List<PackItem> { new("big", 300, 10) };
for (var i = 0; i < 4; i++)
{
items.Add(new PackItem($"s{i}", 30, 30));
}
var result = ShelfPacker.Pack(items, maxPageSize: 128, padding: 2);
Assert.Equal(2, result.PageSizes.Count); // отдельная страница big + одна общая
}
}