1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-22 04:53:29 +01:00

code cleanup

This commit is contained in:
Ell 2022-10-27 10:22:25 +02:00
parent 8745a3237e
commit 791c66b098
15 changed files with 799 additions and 801 deletions

View file

@ -13,9 +13,6 @@ namespace MLEM.Data {
/// A dynamic enum uses <see cref="BigInteger"/> as its underlying type, allowing for an arbitrary number of enum values to be created, even when a <see cref="FlagsAttribute"/>-like structure is used that would only allow for up to 64 values in a regular enum. /// A dynamic enum uses <see cref="BigInteger"/> as its underlying type, allowing for an arbitrary number of enum values to be created, even when a <see cref="FlagsAttribute"/>-like structure is used that would only allow for up to 64 values in a regular enum.
/// All enum operations including <see cref="And{T}"/>, <see cref="Or{T}"/>, <see cref="Xor{T}"/> and <see cref="Neg{T}"/> are supported and can be implemented in derived classes using operator overloads. /// All enum operations including <see cref="And{T}"/>, <see cref="Or{T}"/>, <see cref="Xor{T}"/> and <see cref="Neg{T}"/> are supported and can be implemented in derived classes using operator overloads.
/// To create a custom dynamic enum, simply create a class that extends <see cref="DynamicEnum"/>. New values can then be added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>. /// To create a custom dynamic enum, simply create a class that extends <see cref="DynamicEnum"/>. New values can then be added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
///
/// This class, and its entire concept, are extremely terrible. If you intend on using this, there's probably at least one better solution available.
/// Though if, for some weird reason, you need a way to have more than 64 distinct flags, this is a pretty good solution.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used: /// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used:

View file

@ -11,6 +11,7 @@ using MLEM.Ui.Style;
#if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER #if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER
using System.Net.Http; using System.Net.Http;
#else #else
using System.Net; using System.Net;
#endif #endif

View file

@ -24,7 +24,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Content\Fonts\Symbola-Emoji.ttf" /> <Content Include="Content\Fonts\Symbola-Emoji.ttf" />
</ItemGroup> </ItemGroup>
<Target Name="RestoreDotnetTools" BeforeTargets="Restore"> <Target Name="RestoreDotnetTools" BeforeTargets="Restore">

View file

@ -2,29 +2,29 @@
using MLEM.Cameras; using MLEM.Cameras;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class CameraTests {
private TestGame game; public class CameraTests {
[SetUp] private TestGame game;
public void SetUp() {
this.game = TestGame.Create();
}
[TearDown]
public void TearDown() {
this.game?.Dispose();
}
[Test]
public void TestConversions([Range(-4, 4, 4F)] float x, [Range(-4, 4, 4F)] float y) {
var camera = new Camera(this.game.GraphicsDevice);
var pos = new Vector2(x, y);
var cam = camera.ToCameraPos(pos);
var ret = camera.ToWorldPos(cam);
Assert.AreEqual(pos, ret);
}
[SetUp]
public void SetUp() {
this.game = TestGame.Create();
} }
[TearDown]
public void TearDown() {
this.game?.Dispose();
}
[Test]
public void TestConversions([Range(-4, 4, 4F)] float x, [Range(-4, 4, 4F)] float y) {
var camera = new Camera(this.game.GraphicsDevice);
var pos = new Vector2(x, y);
var cam = camera.ToCameraPos(pos);
var ret = camera.ToWorldPos(cam);
Assert.AreEqual(pos, ret);
}
} }

View file

@ -1,31 +1,31 @@
using MLEM.Extensions; using MLEM.Extensions;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class CollectionTests {
[Test] public class CollectionTests {
public void TestCombinations() {
var things = new[] {
new[] {'1', '2', '3'},
new[] {'A', 'B'},
new[] {'+', '-'}
};
var expected = new[] { [Test]
new[] {'1', 'A', '+'}, new[] {'1', 'A', '-'}, new[] {'1', 'B', '+'}, new[] {'1', 'B', '-'}, public void TestCombinations() {
new[] {'2', 'A', '+'}, new[] {'2', 'A', '-'}, new[] {'2', 'B', '+'}, new[] {'2', 'B', '-'}, var things = new[] {
new[] {'3', 'A', '+'}, new[] {'3', 'A', '-'}, new[] {'3', 'B', '+'}, new[] {'3', 'B', '-'} new[] {'1', '2', '3'},
}; new[] {'A', 'B'},
Assert.AreEqual(things.Combinations(), expected); new[] {'+', '-'}
};
var indices = new[] { var expected = new[] {
new[] {0, 0, 0}, new[] {0, 0, 1}, new[] {0, 1, 0}, new[] {0, 1, 1}, new[] {'1', 'A', '+'}, new[] {'1', 'A', '-'}, new[] {'1', 'B', '+'}, new[] {'1', 'B', '-'},
new[] {1, 0, 0}, new[] {1, 0, 1}, new[] {1, 1, 0}, new[] {1, 1, 1}, new[] {'2', 'A', '+'}, new[] {'2', 'A', '-'}, new[] {'2', 'B', '+'}, new[] {'2', 'B', '-'},
new[] {2, 0, 0}, new[] {2, 0, 1}, new[] {2, 1, 0}, new[] {2, 1, 1} new[] {'3', 'A', '+'}, new[] {'3', 'A', '-'}, new[] {'3', 'B', '+'}, new[] {'3', 'B', '-'}
}; };
Assert.AreEqual(things.IndexCombinations(), indices); Assert.AreEqual(things.Combinations(), expected);
}
var indices = new[] {
new[] {0, 0, 0}, new[] {0, 0, 1}, new[] {0, 1, 0}, new[] {0, 1, 1},
new[] {1, 0, 0}, new[] {1, 0, 1}, new[] {1, 1, 0}, new[] {1, 1, 1},
new[] {2, 0, 0}, new[] {2, 0, 1}, new[] {2, 1, 0}, new[] {2, 1, 1}
};
Assert.AreEqual(things.IndexCombinations(), indices);
} }
} }

View file

@ -9,78 +9,78 @@ using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
using Vector2 = Microsoft.Xna.Framework.Vector2; using Vector2 = Microsoft.Xna.Framework.Vector2;
namespace Tests { namespace Tests;
public class DataTests {
private readonly TestObject testObject = new() { public class DataTests {
Vec = new Vector2(10, 20),
Point = new Point(20, 30),
Dir = Direction2.Left,
OtherTest = new TestObject {
Vec = new Vector2(70, 30),
Dir = Direction2.Right
}
};
[Test] private readonly TestObject testObject = new() {
public void TestJsonSerializers() { Vec = new Vector2(10, 20),
var serializer = JsonConverters.AddAll(new JsonSerializer()); Point = new Point(20, 30),
Dir = Direction2.Left,
OtherTest = new TestObject {
Vec = new Vector2(70, 30),
Dir = Direction2.Right
}
};
var writer = new StringWriter(); [Test]
serializer.Serialize(writer, this.testObject); public void TestJsonSerializers() {
var ret = writer.ToString(); var serializer = JsonConverters.AddAll(new JsonSerializer());
Assert.AreEqual(ret, "{\"Vec\":\"10 20\",\"Point\":\"20 30\",\"OtherTest\":{\"Vec\":\"70 30\",\"Point\":\"0 0\",\"OtherTest\":null,\"Dir\":\"Right\"},\"Dir\":\"Left\"}"); var writer = new StringWriter();
serializer.Serialize(writer, this.testObject);
var ret = writer.ToString();
var read = serializer.Deserialize<TestObject>(new JsonTextReader(new StringReader(ret))); Assert.AreEqual(ret, "{\"Vec\":\"10 20\",\"Point\":\"20 30\",\"OtherTest\":{\"Vec\":\"70 30\",\"Point\":\"0 0\",\"OtherTest\":null,\"Dir\":\"Right\"},\"Dir\":\"Left\"}");
Assert.AreEqual(this.testObject, read);
var read = serializer.Deserialize<TestObject>(new JsonTextReader(new StringReader(ret)));
Assert.AreEqual(this.testObject, read);
}
[Test]
public void TestJsonTypeSafety() {
var serializer = new JsonSerializer {TypeNameHandling = TypeNameHandling.Auto};
// normal generic data holder should crush the time span down to a string due to its custom serializer
var data = new GenericDataHolder();
data.SetData("Time", TimeSpan.FromMinutes(5));
var read = DataTests.SerializeAndDeserialize(serializer, data);
Assert.IsNotInstanceOf<TimeSpan>(read.GetData<object>("Time"));
Assert.Throws<InvalidCastException>(() => read.GetData<TimeSpan>("Time"));
// json type safe generic data holder should wrap the time span to ensure that it stays a time span
var safeData = new JsonTypeSafeGenericDataHolder();
safeData.SetData("Time", TimeSpan.FromMinutes(5));
var safeRead = DataTests.SerializeAndDeserialize(serializer, safeData);
Assert.IsInstanceOf<TimeSpan>(safeRead.GetData<object>("Time"));
Assert.DoesNotThrow(() => safeRead.GetData<TimeSpan>("Time"));
}
private static T SerializeAndDeserialize<T>(JsonSerializer serializer, T t) {
var writer = new StringWriter();
serializer.Serialize(writer, t);
return serializer.Deserialize<T>(new JsonTextReader(new StringReader(writer.ToString())));
}
private class TestObject {
public Vector2 Vec;
public Point Point;
public Direction2 Dir { get; set; }
public TestObject OtherTest;
protected bool Equals(TestObject other) {
return this.Vec.Equals(other.Vec) && this.Point.Equals(other.Point) && object.Equals(this.OtherTest, other.OtherTest) && this.Dir == other.Dir;
} }
[Test] public override bool Equals(object obj) {
public void TestJsonTypeSafety() { return object.ReferenceEquals(this, obj) || obj is TestObject other && this.Equals(other);
var serializer = new JsonSerializer {TypeNameHandling = TypeNameHandling.Auto};
// normal generic data holder should crush the time span down to a string due to its custom serializer
var data = new GenericDataHolder();
data.SetData("Time", TimeSpan.FromMinutes(5));
var read = DataTests.SerializeAndDeserialize(serializer, data);
Assert.IsNotInstanceOf<TimeSpan>(read.GetData<object>("Time"));
Assert.Throws<InvalidCastException>(() => read.GetData<TimeSpan>("Time"));
// json type safe generic data holder should wrap the time span to ensure that it stays a time span
var safeData = new JsonTypeSafeGenericDataHolder();
safeData.SetData("Time", TimeSpan.FromMinutes(5));
var safeRead = DataTests.SerializeAndDeserialize(serializer, safeData);
Assert.IsInstanceOf<TimeSpan>(safeRead.GetData<object>("Time"));
Assert.DoesNotThrow(() => safeRead.GetData<TimeSpan>("Time"));
} }
private static T SerializeAndDeserialize<T>(JsonSerializer serializer, T t) { public override int GetHashCode() {
var writer = new StringWriter(); return HashCode.Combine(this.Vec, this.Point, this.OtherTest, (int) this.Dir);
serializer.Serialize(writer, t);
return serializer.Deserialize<T>(new JsonTextReader(new StringReader(writer.ToString())));
}
private class TestObject {
public Vector2 Vec;
public Point Point;
public Direction2 Dir { get; set; }
public TestObject OtherTest;
protected bool Equals(TestObject other) {
return this.Vec.Equals(other.Vec) && this.Point.Equals(other.Point) && object.Equals(this.OtherTest, other.OtherTest) && this.Dir == other.Dir;
}
public override bool Equals(object obj) {
return object.ReferenceEquals(this, obj) || obj is TestObject other && this.Equals(other);
}
public override int GetHashCode() {
return HashCode.Combine(this.Vec, this.Point, this.OtherTest, (int) this.Dir);
}
} }
} }
} }

View file

@ -5,56 +5,56 @@ using MLEM.Data;
using MLEM.Textures; using MLEM.Textures;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class TestDataTextureAtlas {
[Test] public class TestDataTextureAtlas {
public void Test([Values(0, 4)] int regionX, [Values(0, 4)] int regionY) {
using var game = TestGame.Create();
using var texture = new Texture2D(game.GraphicsDevice, 1, 1);
var region = new TextureRegion(texture, regionX, regionY, 1, 1);
var atlas = DataTextureAtlas.LoadAtlasData(region, game.RawContent, "Texture.atlas");
Assert.AreEqual(12, atlas.Regions.Count());
// no pivot [Test]
var plant = atlas["Plant"]; public void Test([Values(0, 4)] int regionX, [Values(0, 4)] int regionY) {
Assert.AreEqual(plant.Area, new Rectangle(96 + regionX, 0 + regionY, 16, 32)); using var game = TestGame.Create();
Assert.AreEqual(plant.PivotPixels, Vector2.Zero); using var texture = new Texture2D(game.GraphicsDevice, 1, 1);
var region = new TextureRegion(texture, regionX, regionY, 1, 1);
var atlas = DataTextureAtlas.LoadAtlasData(region, game.RawContent, "Texture.atlas");
Assert.AreEqual(12, atlas.Regions.Count());
// no added offset // no pivot
var table = atlas["LongTableUp"]; var plant = atlas["Plant"];
Assert.AreEqual(table.Area, new Rectangle(0 + regionX, 32 + regionY, 64, 48)); Assert.AreEqual(plant.Area, new Rectangle(96 + regionX, 0 + regionY, 16, 32));
Assert.AreEqual(table.PivotPixels, new Vector2(16, 48 - 32)); Assert.AreEqual(plant.PivotPixels, Vector2.Zero);
// added offset // no added offset
var table2 = atlas["LongTableDown"]; var table = atlas["LongTableUp"];
Assert.AreEqual(table2.Area, new Rectangle(64 + regionX, 32 + regionY, 64, 48)); Assert.AreEqual(table.Area, new Rectangle(0 + regionX, 32 + regionY, 64, 48));
Assert.AreEqual(table2.PivotPixels, new Vector2(112 - 64, 48 - 32)); Assert.AreEqual(table.PivotPixels, new Vector2(16, 48 - 32));
// negative pivot // added offset
var negativePivot = atlas["TestRegionNegativePivot"]; var table2 = atlas["LongTableDown"];
Assert.AreEqual(negativePivot.Area, new Rectangle(0 + regionX, 32 + regionY, 16, 16)); Assert.AreEqual(table2.Area, new Rectangle(64 + regionX, 32 + regionY, 64, 48));
Assert.AreEqual(negativePivot.PivotPixels, new Vector2(-32, 46 - 32)); Assert.AreEqual(table2.PivotPixels, new Vector2(112 - 64, 48 - 32));
// cpy (pivot pixels should be identical to LongTableUp because they're region-internal) // negative pivot
var copy1 = atlas["Copy1"]; var negativePivot = atlas["TestRegionNegativePivot"];
Assert.AreEqual(copy1.Area, new Rectangle(0 + 16 + regionX, 32 + regionY, 64, 48)); Assert.AreEqual(negativePivot.Area, new Rectangle(0 + regionX, 32 + regionY, 16, 16));
Assert.AreEqual(copy1.PivotPixels, new Vector2(16, 48 - 32)); Assert.AreEqual(negativePivot.PivotPixels, new Vector2(-32, 46 - 32));
var copy2 = atlas["Copy2"];
Assert.AreEqual(copy2.Area, new Rectangle(0 + 32 + regionX, 32 + 4 + regionY, 64, 48));
Assert.AreEqual(copy2.PivotPixels, new Vector2(16, 48 - 32));
// frm // cpy (pivot pixels should be identical to LongTableUp because they're region-internal)
var copy3 = atlas["Copy3"]; var copy1 = atlas["Copy1"];
Assert.AreEqual(copy3.Area, new Rectangle(0 + 2 + regionX, 32 + 4 + regionY, 64, 48)); Assert.AreEqual(copy1.Area, new Rectangle(0 + 16 + regionX, 32 + regionY, 64, 48));
Assert.AreEqual(copy3.PivotPixels, new Vector2(16, 48 - 32)); Assert.AreEqual(copy1.PivotPixels, new Vector2(16, 48 - 32));
var copy2 = atlas["Copy2"];
Assert.AreEqual(copy2.Area, new Rectangle(0 + 32 + regionX, 32 + 4 + regionY, 64, 48));
Assert.AreEqual(copy2.PivotPixels, new Vector2(16, 48 - 32));
// data // frm
var data = atlas["DataTest"]; var copy3 = atlas["Copy3"];
Assert.AreEqual("ThisIsSomeData", data.GetData<string>("DataPoint1")); Assert.AreEqual(copy3.Area, new Rectangle(0 + 2 + regionX, 32 + 4 + regionY, 64, 48));
Assert.AreEqual("3.5", data.GetData<string>("DataPoint2")); Assert.AreEqual(copy3.PivotPixels, new Vector2(16, 48 - 32));
Assert.AreEqual("---", data.GetData<string>("DataPoint3"));
}
// data
var data = atlas["DataTest"];
Assert.AreEqual("ThisIsSomeData", data.GetData<string>("DataPoint1"));
Assert.AreEqual("3.5", data.GetData<string>("DataPoint2"));
Assert.AreEqual("---", data.GetData<string>("DataPoint3"));
} }
} }

View file

@ -2,63 +2,63 @@ using Microsoft.Xna.Framework;
using MLEM.Misc; using MLEM.Misc;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class DirectionTests {
[Test] public class DirectionTests {
public void TestDirections() {
Assert.AreEqual(new Vector2(0.5F, 0.5F).ToDirection(), Direction2.DownRight);
Assert.AreEqual(new Vector2(0.25F, 0.5F).ToDirection(), Direction2.DownRight);
Assert.AreEqual(new Vector2(0.15F, 0.5F).ToDirection(), Direction2.Down);
}
[Test]
public void Test90Directions() {
Assert.AreEqual(new Vector2(0.75F, 0.5F).To90Direction(), Direction2.Right);
Assert.AreEqual(new Vector2(0.5F, 0.5F).To90Direction(), Direction2.Down);
Assert.AreEqual(new Vector2(0.25F, 0.5F).To90Direction(), Direction2.Down);
}
[Test]
public void TestRotations() {
// rotate cw
Assert.AreEqual(Direction2.Up.RotateCw(), Direction2.Right);
Assert.AreEqual(Direction2.Up.RotateCw(true), Direction2.UpRight);
Assert.AreEqual(Direction2.Left.RotateCw(), Direction2.Up);
Assert.AreEqual(Direction2.UpLeft.RotateCw(), Direction2.UpRight);
// rotate ccw
Assert.AreEqual(Direction2.Up.RotateCcw(), Direction2.Left);
Assert.AreEqual(Direction2.Up.RotateCcw(true), Direction2.UpLeft);
Assert.AreEqual(Direction2.Left.RotateCcw(), Direction2.Down);
Assert.AreEqual(Direction2.UpLeft.RotateCcw(), Direction2.DownLeft);
// rotate 360 degrees
foreach (var dir in Direction2Helper.AllExceptNone) {
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, true, false, 4), dir);
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, true, true, 8), dir);
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, false, false, 4), dir);
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, false, true, 8), dir);
}
// rotate by with start Up
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Right), Direction2.Down);
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Down), Direction2.Left);
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Left), Direction2.Up);
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Up), Direction2.Right);
// rotate by with start Left
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Right, Direction2.Left), Direction2.Down);
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Down, Direction2.Left), Direction2.Left);
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Left, Direction2.Left), Direction2.Up);
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Up, Direction2.Left), Direction2.Right);
}
private static Direction2 RotateMultipleTimes(Direction2 dir, bool clockwise, bool fortyFiveDegrees, int times) {
for (var i = 0; i < times; i++)
dir = clockwise ? dir.RotateCw(fortyFiveDegrees) : dir.RotateCcw(fortyFiveDegrees);
return dir;
}
[Test]
public void TestDirections() {
Assert.AreEqual(new Vector2(0.5F, 0.5F).ToDirection(), Direction2.DownRight);
Assert.AreEqual(new Vector2(0.25F, 0.5F).ToDirection(), Direction2.DownRight);
Assert.AreEqual(new Vector2(0.15F, 0.5F).ToDirection(), Direction2.Down);
} }
[Test]
public void Test90Directions() {
Assert.AreEqual(new Vector2(0.75F, 0.5F).To90Direction(), Direction2.Right);
Assert.AreEqual(new Vector2(0.5F, 0.5F).To90Direction(), Direction2.Down);
Assert.AreEqual(new Vector2(0.25F, 0.5F).To90Direction(), Direction2.Down);
}
[Test]
public void TestRotations() {
// rotate cw
Assert.AreEqual(Direction2.Up.RotateCw(), Direction2.Right);
Assert.AreEqual(Direction2.Up.RotateCw(true), Direction2.UpRight);
Assert.AreEqual(Direction2.Left.RotateCw(), Direction2.Up);
Assert.AreEqual(Direction2.UpLeft.RotateCw(), Direction2.UpRight);
// rotate ccw
Assert.AreEqual(Direction2.Up.RotateCcw(), Direction2.Left);
Assert.AreEqual(Direction2.Up.RotateCcw(true), Direction2.UpLeft);
Assert.AreEqual(Direction2.Left.RotateCcw(), Direction2.Down);
Assert.AreEqual(Direction2.UpLeft.RotateCcw(), Direction2.DownLeft);
// rotate 360 degrees
foreach (var dir in Direction2Helper.AllExceptNone) {
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, true, false, 4), dir);
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, true, true, 8), dir);
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, false, false, 4), dir);
Assert.AreEqual(DirectionTests.RotateMultipleTimes(dir, false, true, 8), dir);
}
// rotate by with start Up
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Right), Direction2.Down);
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Down), Direction2.Left);
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Left), Direction2.Up);
Assert.AreEqual(Direction2.Right.RotateBy(Direction2.Up), Direction2.Right);
// rotate by with start Left
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Right, Direction2.Left), Direction2.Down);
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Down, Direction2.Left), Direction2.Left);
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Left, Direction2.Left), Direction2.Up);
Assert.AreEqual(Direction2.Up.RotateBy(Direction2.Up, Direction2.Left), Direction2.Right);
}
private static Direction2 RotateMultipleTimes(Direction2 dir, bool clockwise, bool fortyFiveDegrees, int times) {
for (var i = 0; i < times; i++)
dir = clockwise ? dir.RotateCw(fortyFiveDegrees) : dir.RotateCcw(fortyFiveDegrees);
return dir;
}
} }

View file

@ -8,150 +8,150 @@ using MLEM.Formatting.Codes;
using MLEM.Textures; using MLEM.Textures;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class FontTests {
private TestGame game; public class FontTests {
private GenericFont font;
private TextFormatter formatter;
[SetUp] private TestGame game;
public void SetUp() { private GenericFont font;
this.game = TestGame.Create(); private TextFormatter formatter;
this.font = this.game.UiSystem.Style.Font;
this.formatter = this.game.UiSystem.TextFormatter;
}
[TearDown]
public void TearDown() {
this.game?.Dispose();
}
[Test]
public void TestRegularSplit() {
Assert.AreEqual(this.font.SplitStringSeparate(
"Note that the default style does not contain any textures or font files and, as such, is quite bland. However, the default style is quite easy to override, as can be seen in the code for this demo.",
65, 0.1F), new[] {"Note that the default style does ", "not contain any textures or font ", "files and, as such, is quite ", "bland. However, the default ", "style is quite easy to override, ", "as can be seen in the code for ", "this demo."});
var formatted = this.formatter.Tokenize(this.font,
"Select the demo you want to see below using your mouse, touch input, your keyboard or a controller. Check the demos' <c CornflowerBlue><l https://github.com/Ellpeck/MLEM/tree/main/Demos>source code</l></c> for more in-depth explanations of their functionality or the <c CornflowerBlue><l https://mlem.ellpeck.de/>website</l></c> for tutorials and API documentation.");
formatted.Split(this.font, 90, 0.1F);
Assert.AreEqual(formatted.DisplayString, "Select the demo you want to see below using \nyour mouse, touch input, your keyboard or a \ncontroller. Check the demos' source code for \nmore in-depth explanations of their \nfunctionality or the website for tutorials and \nAPI documentation.");
var tokens = new[] {
"Select the demo you want to see below using \nyour mouse, touch input, your keyboard or a \ncontroller. Check the demos' ",
string.Empty,
"source code",
string.Empty,
" for \nmore in-depth explanations of their \nfunctionality or the ",
string.Empty,
"website",
string.Empty,
" for tutorials and \nAPI documentation."
};
for (var i = 0; i < tokens.Length; i++)
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
}
[Test]
public void TestLongLineSplit() {
var expectedDisplay = new[] {"This_is_a_really_long_line_to_s", "ee_if_splitting_without_spaces_", "works_properly._I_also_want_to_", "see_if_it_works_across_multiple", "_lines_or_just_on_the_first_one. ", "But after this, I want the text to ", "continue normally before ", "changing_back_to_being_really_", "long_oh_yes"};
Assert.AreEqual(this.font.SplitStringSeparate(
"This_is_a_really_long_line_to_see_if_splitting_without_spaces_works_properly._I_also_want_to_see_if_it_works_across_multiple_lines_or_just_on_the_first_one. But after this, I want the text to continue normally before changing_back_to_being_really_long_oh_yes",
65, 0.1F), expectedDisplay);
var formatted = this.formatter.Tokenize(this.font,
"This_is_a_really_long_line_to_see_if_<c Blue>splitting</c>_without_spaces_works_properly._I_also_want_to_see_if_it_works_across_multiple_<c Yellow>lines</c>_or_just_on_the_first_one. But after this, I want the <b>text</b> to continue normally before changing_back_<i>to</i>_being_really_long_oh_yes");
formatted.Split(this.font, 65, 0.1F);
Assert.AreEqual(formatted.DisplayString, string.Join('\n', expectedDisplay));
var tokens = new[] {
"This_is_a_really_long_line_to_s\nee_if_",
"splitting",
"_without_spaces_\nworks_properly._I_also_want_to_\nsee_if_it_works_across_multiple\n_",
"lines",
"_or_just_on_the_first_one. \nBut after this, I want the ",
"text",
" to \ncontinue normally before \nchanging_back_",
"to",
"_being_really_\nlong_oh_yes"
};
for (var i = 0; i < tokens.Length; i++)
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
}
[Test]
public void TestNewlineSplit() {
var formatted = this.formatter.Tokenize(this.font,
"This is a pretty long line with regular <c Blue>content</c> that will be split.\nNow this is a new line with additional regular <c Blue>content</c> that is forced into a new line.");
formatted.Split(this.font, 65, 0.1F);
Assert.AreEqual(formatted.DisplayString, "This is a pretty long line with \nregular content that will be \nsplit.\nNow this is a new line with \nadditional regular content that \nis forced into a new line.");
var tokens = new[] {
"This is a pretty long line with \nregular ",
"content",
" that will be \nsplit.\nNow this is a new line with \nadditional regular ",
"content",
" that \nis forced into a new line."
};
for (var i = 0; i < tokens.Length; i++)
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
}
[Test]
public void TestMacros() {
this.formatter.Macros.Add(new Regex("<testmacro>"), (_, _, _) => "<test1>");
this.formatter.Macros.Add(new Regex("<test1>"), (_, _, _) => "<test2>blue");
this.formatter.Macros.Add(new Regex("<test2>"), (_, _, _) => "<c Blue>");
const string strg = "This text uses a bunch of non-breaking~spaces to see if macros work. Additionally, it uses a macro that resolves into a bunch of other macros and then, at the end, into <testmacro> text</c>.";
const string goal = "This text uses a bunch of non-breaking\u00A0spaces to see if macros work. Additionally, it uses a macro that resolves into a bunch of other macros and then, at the end, into <c Blue>blue text</c>.";
Assert.AreEqual(this.formatter.ResolveMacros(strg), goal);
// test recursive macros
this.formatter.Macros.Add(new Regex("<rec1>"), (_, _, _) => "<rec2>");
this.formatter.Macros.Add(new Regex("<rec2>"), (_, _, _) => "<rec1>");
Assert.Throws<ArithmeticException>(() => this.formatter.ResolveMacros("Test <rec1> string"));
}
[Test]
public void TestFormatting() {
this.formatter.AddImage("Test", new TextureRegion((Texture2D) null, 0, 8, 24, 24));
const string strg = "Lorem Ipsum <i Test> is simply dummy text of the <i Test> printing and typesetting <i Test> industry. Lorem Ipsum has been the industry's standard dummy text <i Test> ever since the <i Test> 1500s, when <i Test><i Test><i Test><i Test><i Test><i Test><i Test> an unknown printer took a galley of type and scrambled it to make a type specimen book.";
var ret = this.formatter.Tokenize(this.font, strg);
Assert.AreEqual(ret.Tokens.Length, 13);
Assert.AreEqual(ret.DisplayString, "Lorem Ipsum \u2003 is simply dummy text of the \u2003 printing and typesetting \u2003 industry. Lorem Ipsum has been the industry's standard dummy text \u2003 ever since the \u2003 1500s, when \u2003\u2003\u2003\u2003\u2003\u2003\u2003 an unknown printer took a galley of type and scrambled it to make a type specimen book.");
Assert.AreEqual(ret.AllCodes.Length, 12);
}
[Test]
public void TestConsistency() {
void CompareSizes(string s) {
var spriteFont = ((GenericSpriteFont) this.font).Font;
Assert.AreEqual(spriteFont.MeasureString(s), this.font.MeasureString(s));
}
CompareSizes("This is a very simple test string");
CompareSizes("This\n is a very\nsimple test string");
CompareSizes("\nThis is a very simple test string");
CompareSizes("This is a very simple test string\n");
CompareSizes("This is a very simple test string\n\n\n\n\n");
}
[Test]
public void TestSpecialCharacters() {
void CompareSizes(string s) {
var spriteFont = ((GenericSpriteFont) this.font).Font;
Assert.AreNotEqual(spriteFont.MeasureString(s), this.font.MeasureString(s));
}
CompareSizes($"This is a very simple{GenericFont.Nbsp}test string");
CompareSizes($"This is a very simple{GenericFont.Emsp}test string");
CompareSizes($"This is a very simple{GenericFont.Zwsp}test string");
Assert.AreEqual(new Vector2(this.font.LineHeight, this.font.LineHeight), this.font.MeasureString(GenericFont.Emsp.ToString()));
Assert.AreEqual(new Vector2(0, this.font.LineHeight), this.font.MeasureString(GenericFont.Zwsp.ToString()));
}
[SetUp]
public void SetUp() {
this.game = TestGame.Create();
this.font = this.game.UiSystem.Style.Font;
this.formatter = this.game.UiSystem.TextFormatter;
} }
[TearDown]
public void TearDown() {
this.game?.Dispose();
}
[Test]
public void TestRegularSplit() {
Assert.AreEqual(this.font.SplitStringSeparate(
"Note that the default style does not contain any textures or font files and, as such, is quite bland. However, the default style is quite easy to override, as can be seen in the code for this demo.",
65, 0.1F), new[] {"Note that the default style does ", "not contain any textures or font ", "files and, as such, is quite ", "bland. However, the default ", "style is quite easy to override, ", "as can be seen in the code for ", "this demo."});
var formatted = this.formatter.Tokenize(this.font,
"Select the demo you want to see below using your mouse, touch input, your keyboard or a controller. Check the demos' <c CornflowerBlue><l https://github.com/Ellpeck/MLEM/tree/main/Demos>source code</l></c> for more in-depth explanations of their functionality or the <c CornflowerBlue><l https://mlem.ellpeck.de/>website</l></c> for tutorials and API documentation.");
formatted.Split(this.font, 90, 0.1F);
Assert.AreEqual(formatted.DisplayString, "Select the demo you want to see below using \nyour mouse, touch input, your keyboard or a \ncontroller. Check the demos' source code for \nmore in-depth explanations of their \nfunctionality or the website for tutorials and \nAPI documentation.");
var tokens = new[] {
"Select the demo you want to see below using \nyour mouse, touch input, your keyboard or a \ncontroller. Check the demos' ",
string.Empty,
"source code",
string.Empty,
" for \nmore in-depth explanations of their \nfunctionality or the ",
string.Empty,
"website",
string.Empty,
" for tutorials and \nAPI documentation."
};
for (var i = 0; i < tokens.Length; i++)
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
}
[Test]
public void TestLongLineSplit() {
var expectedDisplay = new[] {"This_is_a_really_long_line_to_s", "ee_if_splitting_without_spaces_", "works_properly._I_also_want_to_", "see_if_it_works_across_multiple", "_lines_or_just_on_the_first_one. ", "But after this, I want the text to ", "continue normally before ", "changing_back_to_being_really_", "long_oh_yes"};
Assert.AreEqual(this.font.SplitStringSeparate(
"This_is_a_really_long_line_to_see_if_splitting_without_spaces_works_properly._I_also_want_to_see_if_it_works_across_multiple_lines_or_just_on_the_first_one. But after this, I want the text to continue normally before changing_back_to_being_really_long_oh_yes",
65, 0.1F), expectedDisplay);
var formatted = this.formatter.Tokenize(this.font,
"This_is_a_really_long_line_to_see_if_<c Blue>splitting</c>_without_spaces_works_properly._I_also_want_to_see_if_it_works_across_multiple_<c Yellow>lines</c>_or_just_on_the_first_one. But after this, I want the <b>text</b> to continue normally before changing_back_<i>to</i>_being_really_long_oh_yes");
formatted.Split(this.font, 65, 0.1F);
Assert.AreEqual(formatted.DisplayString, string.Join('\n', expectedDisplay));
var tokens = new[] {
"This_is_a_really_long_line_to_s\nee_if_",
"splitting",
"_without_spaces_\nworks_properly._I_also_want_to_\nsee_if_it_works_across_multiple\n_",
"lines",
"_or_just_on_the_first_one. \nBut after this, I want the ",
"text",
" to \ncontinue normally before \nchanging_back_",
"to",
"_being_really_\nlong_oh_yes"
};
for (var i = 0; i < tokens.Length; i++)
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
}
[Test]
public void TestNewlineSplit() {
var formatted = this.formatter.Tokenize(this.font,
"This is a pretty long line with regular <c Blue>content</c> that will be split.\nNow this is a new line with additional regular <c Blue>content</c> that is forced into a new line.");
formatted.Split(this.font, 65, 0.1F);
Assert.AreEqual(formatted.DisplayString, "This is a pretty long line with \nregular content that will be \nsplit.\nNow this is a new line with \nadditional regular content that \nis forced into a new line.");
var tokens = new[] {
"This is a pretty long line with \nregular ",
"content",
" that will be \nsplit.\nNow this is a new line with \nadditional regular ",
"content",
" that \nis forced into a new line."
};
for (var i = 0; i < tokens.Length; i++)
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
}
[Test]
public void TestMacros() {
this.formatter.Macros.Add(new Regex("<testmacro>"), (_, _, _) => "<test1>");
this.formatter.Macros.Add(new Regex("<test1>"), (_, _, _) => "<test2>blue");
this.formatter.Macros.Add(new Regex("<test2>"), (_, _, _) => "<c Blue>");
const string strg = "This text uses a bunch of non-breaking~spaces to see if macros work. Additionally, it uses a macro that resolves into a bunch of other macros and then, at the end, into <testmacro> text</c>.";
const string goal = "This text uses a bunch of non-breaking\u00A0spaces to see if macros work. Additionally, it uses a macro that resolves into a bunch of other macros and then, at the end, into <c Blue>blue text</c>.";
Assert.AreEqual(this.formatter.ResolveMacros(strg), goal);
// test recursive macros
this.formatter.Macros.Add(new Regex("<rec1>"), (_, _, _) => "<rec2>");
this.formatter.Macros.Add(new Regex("<rec2>"), (_, _, _) => "<rec1>");
Assert.Throws<ArithmeticException>(() => this.formatter.ResolveMacros("Test <rec1> string"));
}
[Test]
public void TestFormatting() {
this.formatter.AddImage("Test", new TextureRegion((Texture2D) null, 0, 8, 24, 24));
const string strg = "Lorem Ipsum <i Test> is simply dummy text of the <i Test> printing and typesetting <i Test> industry. Lorem Ipsum has been the industry's standard dummy text <i Test> ever since the <i Test> 1500s, when <i Test><i Test><i Test><i Test><i Test><i Test><i Test> an unknown printer took a galley of type and scrambled it to make a type specimen book.";
var ret = this.formatter.Tokenize(this.font, strg);
Assert.AreEqual(ret.Tokens.Length, 13);
Assert.AreEqual(ret.DisplayString, "Lorem Ipsum \u2003 is simply dummy text of the \u2003 printing and typesetting \u2003 industry. Lorem Ipsum has been the industry's standard dummy text \u2003 ever since the \u2003 1500s, when \u2003\u2003\u2003\u2003\u2003\u2003\u2003 an unknown printer took a galley of type and scrambled it to make a type specimen book.");
Assert.AreEqual(ret.AllCodes.Length, 12);
}
[Test]
public void TestConsistency() {
void CompareSizes(string s) {
var spriteFont = ((GenericSpriteFont) this.font).Font;
Assert.AreEqual(spriteFont.MeasureString(s), this.font.MeasureString(s));
}
CompareSizes("This is a very simple test string");
CompareSizes("This\n is a very\nsimple test string");
CompareSizes("\nThis is a very simple test string");
CompareSizes("This is a very simple test string\n");
CompareSizes("This is a very simple test string\n\n\n\n\n");
}
[Test]
public void TestSpecialCharacters() {
void CompareSizes(string s) {
var spriteFont = ((GenericSpriteFont) this.font).Font;
Assert.AreNotEqual(spriteFont.MeasureString(s), this.font.MeasureString(s));
}
CompareSizes($"This is a very simple{GenericFont.Nbsp}test string");
CompareSizes($"This is a very simple{GenericFont.Emsp}test string");
CompareSizes($"This is a very simple{GenericFont.Zwsp}test string");
Assert.AreEqual(new Vector2(this.font.LineHeight, this.font.LineHeight), this.font.MeasureString(GenericFont.Emsp.ToString()));
Assert.AreEqual(new Vector2(0, this.font.LineHeight), this.font.MeasureString(GenericFont.Zwsp.ToString()));
}
} }

View file

@ -2,38 +2,38 @@
using MLEM.Input; using MLEM.Input;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class KeybindTests {
[Test] public class KeybindTests {
public void TestCombinationOrder() {
Assert.AreEqual(0, new Keybind.Combination(Keys.A).CompareTo(new Keybind.Combination(Keys.B)));
Assert.AreEqual(1, new Keybind.Combination(Keys.A, Keys.LeftShift).CompareTo(new Keybind.Combination(Keys.B))); [Test]
Assert.AreEqual(1, new Keybind.Combination(Keys.A, Keys.LeftShift, Keys.RightShift).CompareTo(new Keybind.Combination(Keys.B))); public void TestCombinationOrder() {
Assert.AreEqual(1, new Keybind.Combination(Keys.A, Keys.LeftShift, Keys.RightShift).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift))); Assert.AreEqual(0, new Keybind.Combination(Keys.A).CompareTo(new Keybind.Combination(Keys.B)));
Assert.AreEqual(-1, new Keybind.Combination(Keys.A).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift))); Assert.AreEqual(1, new Keybind.Combination(Keys.A, Keys.LeftShift).CompareTo(new Keybind.Combination(Keys.B)));
Assert.AreEqual(-1, new Keybind.Combination(Keys.A).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift, Keys.RightShift))); Assert.AreEqual(1, new Keybind.Combination(Keys.A, Keys.LeftShift, Keys.RightShift).CompareTo(new Keybind.Combination(Keys.B)));
Assert.AreEqual(-1, new Keybind.Combination(Keys.A, Keys.LeftShift).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift, Keys.RightShift))); Assert.AreEqual(1, new Keybind.Combination(Keys.A, Keys.LeftShift, Keys.RightShift).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift)));
}
[Test]
public void TestKeybindOrder() {
Assert.AreEqual(0, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(2, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(2, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(3, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(0, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl).CompareTo(new Keybind(Keys.B, Keys.LeftAlt)));
Assert.AreEqual(1, new Keybind(Keys.A, Keys.LeftShift, Keys.RightShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl).CompareTo(new Keybind(Keys.B, Keys.LeftAlt)));
Assert.AreEqual(-2, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift)));
Assert.AreEqual(-2, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C)));
Assert.AreEqual(-3, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl)));
Assert.AreEqual(0, new Keybind(Keys.A, Keys.LeftAlt).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl)));
Assert.AreEqual(-1, new Keybind(Keys.A, Keys.LeftAlt).CompareTo(new Keybind(Keys.B, Keys.LeftShift, Keys.RightShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl)));
}
Assert.AreEqual(-1, new Keybind.Combination(Keys.A).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift)));
Assert.AreEqual(-1, new Keybind.Combination(Keys.A).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift, Keys.RightShift)));
Assert.AreEqual(-1, new Keybind.Combination(Keys.A, Keys.LeftShift).CompareTo(new Keybind.Combination(Keys.B, Keys.LeftShift, Keys.RightShift)));
} }
[Test]
public void TestKeybindOrder() {
Assert.AreEqual(0, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(2, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(2, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(3, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl).CompareTo(new Keybind(Keys.B)));
Assert.AreEqual(0, new Keybind(Keys.A, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl).CompareTo(new Keybind(Keys.B, Keys.LeftAlt)));
Assert.AreEqual(1, new Keybind(Keys.A, Keys.LeftShift, Keys.RightShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl).CompareTo(new Keybind(Keys.B, Keys.LeftAlt)));
Assert.AreEqual(-2, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift)));
Assert.AreEqual(-2, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C)));
Assert.AreEqual(-3, new Keybind(Keys.A).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl)));
Assert.AreEqual(0, new Keybind(Keys.A, Keys.LeftAlt).CompareTo(new Keybind(Keys.B, Keys.LeftShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl)));
Assert.AreEqual(-1, new Keybind(Keys.A, Keys.LeftAlt).CompareTo(new Keybind(Keys.B, Keys.LeftShift, Keys.RightShift).Add(Keys.B, Keys.RightShift).Add(Keys.C, Keys.RightControl)));
}
} }

View file

@ -3,69 +3,69 @@ using MLEM.Extensions;
using MLEM.Misc; using MLEM.Misc;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class NumberTests {
[Test] public class NumberTests {
public void TestRounding() {
Assert.AreEqual(1.25F.Floor(), 1);
Assert.AreEqual(-1.25F.Floor(), -1);
Assert.AreEqual(1.25F.Ceil(), 2); [Test]
Assert.AreEqual(-1.25F.Ceil(), -2); public void TestRounding() {
Assert.AreEqual(1.25F.Floor(), 1);
Assert.AreEqual(-1.25F.Floor(), -1);
Assert.AreEqual(new Vector2(5, 2.5F).FloorCopy(), new Vector2(5, 2)); Assert.AreEqual(1.25F.Ceil(), 2);
Assert.AreEqual(new Vector2(5, 2.5F).CeilCopy(), new Vector2(5, 3)); Assert.AreEqual(-1.25F.Ceil(), -2);
Assert.AreEqual(new Vector2(5.25F, 2).FloorCopy(), new Vector2(5, 2));
Assert.AreEqual(new Vector2(5.25F, 2).CeilCopy(), new Vector2(6, 2));
}
[Test]
public void TestEquals() {
Assert.IsTrue(0.25F.Equals(0.26F, 0.01F));
Assert.IsFalse(0.25F.Equals(0.26F, 0.009F));
Assert.IsTrue(new Vector2(0.25F, 0).Equals(new Vector2(0.3F, 0), 0.1F));
Assert.IsFalse(new Vector2(0.25F, 0.5F).Equals(new Vector2(0.3F, 0.25F), 0.1F));
Assert.IsTrue(new Vector3(0.25F, 0, 3.5F).Equals(new Vector3(0.3F, 0, 3.45F), 0.1F));
Assert.IsFalse(new Vector3(0.25F, 0.5F, 0).Equals(new Vector3(0.3F, 0.25F, 0), 0.1F));
Assert.IsTrue(new Vector4(0.25F, 0, 3.5F, 0.75F).Equals(new Vector4(0.3F, 0, 3.45F, 0.7F), 0.1F));
Assert.IsFalse(new Vector4(0.25F, 0.5F, 0, 1).Equals(new Vector4(0.3F, 0.25F, 0, 0.9F), 0.1F));
}
[Test]
public void TestPointExtensions() {
Assert.AreEqual(new Point(4, 3).Multiply(3), new Point(12, 9));
Assert.AreEqual(new Point(17, 12).Divide(3), new Point(5, 4));
Assert.AreEqual(new Point(4, 12).Transform(Matrix.CreateTranslation(2, 0, 0) * Matrix.CreateScale(2, 2, 2)), new Point(12, 24));
}
[Test]
public void TestMatrixOps([Range(0.5F, 2, 0.5F)] float scale, [Range(-0.5F, 0.5F, 1)] float rotationX, [Range(-0.5F, 0.5F, 1)] float rotationY, [Range(-0.5F, 0.5F, 1)] float rotationZ) {
var rotation = Matrix.CreateRotationX(rotationX) * Matrix.CreateRotationY(rotationY) * Matrix.CreateRotationZ(rotationZ);
var matrix = rotation * Matrix.CreateScale(scale, scale, scale);
Assert.IsTrue(matrix.Scale().Equals(new Vector3(scale), 0.001F), $"{matrix.Scale()} does not equal {new Vector2(scale)}");
Assert.IsTrue(matrix.Rotation().Equals(Quaternion.CreateFromRotationMatrix(rotation), 0.001F), $"{matrix.Rotation()} does not equal {Quaternion.CreateFromRotationMatrix(rotation)}");
Assert.IsTrue(matrix.RotationVector().Equals(new Vector3(rotationX, rotationY, rotationZ), 0.001F), $"{matrix.RotationVector()} does not equal {new Vector3(rotationX, rotationY, rotationZ)}");
// check against decomposed results
matrix.Decompose(out var sc, out var rot, out _);
Assert.AreEqual(matrix.Rotation(), rot);
Assert.AreEqual(matrix.Scale(), sc);
}
[Test]
public void TestPenetrate() {
new RectangleF(2, 2, 4, 4).Penetrate(new RectangleF(3, 2, 4, 4), out var normal, out var penetration);
Assert.AreEqual(normal, new Vector2(1, 0));
Assert.AreEqual(penetration, 3);
new RectangleF(-10, 10, 5, 5).Penetrate(new RectangleF(25, 25, 10, 10), out normal, out penetration);
Assert.AreEqual(normal, Vector2.Zero);
Assert.AreEqual(penetration, 0);
}
Assert.AreEqual(new Vector2(5, 2.5F).FloorCopy(), new Vector2(5, 2));
Assert.AreEqual(new Vector2(5, 2.5F).CeilCopy(), new Vector2(5, 3));
Assert.AreEqual(new Vector2(5.25F, 2).FloorCopy(), new Vector2(5, 2));
Assert.AreEqual(new Vector2(5.25F, 2).CeilCopy(), new Vector2(6, 2));
} }
[Test]
public void TestEquals() {
Assert.IsTrue(0.25F.Equals(0.26F, 0.01F));
Assert.IsFalse(0.25F.Equals(0.26F, 0.009F));
Assert.IsTrue(new Vector2(0.25F, 0).Equals(new Vector2(0.3F, 0), 0.1F));
Assert.IsFalse(new Vector2(0.25F, 0.5F).Equals(new Vector2(0.3F, 0.25F), 0.1F));
Assert.IsTrue(new Vector3(0.25F, 0, 3.5F).Equals(new Vector3(0.3F, 0, 3.45F), 0.1F));
Assert.IsFalse(new Vector3(0.25F, 0.5F, 0).Equals(new Vector3(0.3F, 0.25F, 0), 0.1F));
Assert.IsTrue(new Vector4(0.25F, 0, 3.5F, 0.75F).Equals(new Vector4(0.3F, 0, 3.45F, 0.7F), 0.1F));
Assert.IsFalse(new Vector4(0.25F, 0.5F, 0, 1).Equals(new Vector4(0.3F, 0.25F, 0, 0.9F), 0.1F));
}
[Test]
public void TestPointExtensions() {
Assert.AreEqual(new Point(4, 3).Multiply(3), new Point(12, 9));
Assert.AreEqual(new Point(17, 12).Divide(3), new Point(5, 4));
Assert.AreEqual(new Point(4, 12).Transform(Matrix.CreateTranslation(2, 0, 0) * Matrix.CreateScale(2, 2, 2)), new Point(12, 24));
}
[Test]
public void TestMatrixOps([Range(0.5F, 2, 0.5F)] float scale, [Range(-0.5F, 0.5F, 1)] float rotationX, [Range(-0.5F, 0.5F, 1)] float rotationY, [Range(-0.5F, 0.5F, 1)] float rotationZ) {
var rotation = Matrix.CreateRotationX(rotationX) * Matrix.CreateRotationY(rotationY) * Matrix.CreateRotationZ(rotationZ);
var matrix = rotation * Matrix.CreateScale(scale, scale, scale);
Assert.IsTrue(matrix.Scale().Equals(new Vector3(scale), 0.001F), $"{matrix.Scale()} does not equal {new Vector2(scale)}");
Assert.IsTrue(matrix.Rotation().Equals(Quaternion.CreateFromRotationMatrix(rotation), 0.001F), $"{matrix.Rotation()} does not equal {Quaternion.CreateFromRotationMatrix(rotation)}");
Assert.IsTrue(matrix.RotationVector().Equals(new Vector3(rotationX, rotationY, rotationZ), 0.001F), $"{matrix.RotationVector()} does not equal {new Vector3(rotationX, rotationY, rotationZ)}");
// check against decomposed results
matrix.Decompose(out var sc, out var rot, out _);
Assert.AreEqual(matrix.Rotation(), rot);
Assert.AreEqual(matrix.Scale(), sc);
}
[Test]
public void TestPenetrate() {
new RectangleF(2, 2, 4, 4).Penetrate(new RectangleF(3, 2, 4, 4), out var normal, out var penetration);
Assert.AreEqual(normal, new Vector2(1, 0));
Assert.AreEqual(penetration, 3);
new RectangleF(-10, 10, 5, 5).Penetrate(new RectangleF(25, 25, 10, 10), out normal, out penetration);
Assert.AreEqual(normal, Vector2.Zero);
Assert.AreEqual(penetration, 0);
}
} }

View file

@ -4,124 +4,124 @@ using Microsoft.Xna.Framework;
using MLEM.Pathfinding; using MLEM.Pathfinding;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class PathfindingTests {
[Test] public class PathfindingTests {
public void TestConsistency() {
var area = new[] {
"XXXX",
"X X",
"X X",
"XXXX"
};
var noDiagonals = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 2), area, false).ToArray(); [Test]
Assert.AreEqual(noDiagonals.Length, 3); public void TestConsistency() {
Assert.AreEqual(noDiagonals[0], new Point(1, 1)); var area = new[] {
Assert.AreEqual(noDiagonals[2], new Point(2, 2)); "XXXX",
"X X",
"X X",
"XXXX"
};
var diagonals = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 2), area, true).ToArray(); var noDiagonals = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 2), area, false).ToArray();
Assert.AreEqual(diagonals.Length, 2); Assert.AreEqual(noDiagonals.Length, 3);
Assert.AreEqual(diagonals[0], new Point(1, 1)); Assert.AreEqual(noDiagonals[0], new Point(1, 1));
Assert.AreEqual(diagonals[1], new Point(2, 2)); Assert.AreEqual(noDiagonals[2], new Point(2, 2));
}
[Test]
public void TestPathCost() {
var area = new[] {
"XXXXXXXX",
"X 5 X",
"X 5 X",
"XXXXXXXX"
};
var firstPath = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(3, 1), area, false).ToArray();
var firstExpected = new[] {new Point(1, 1), new Point(1, 2), new Point(2, 2), new Point(3, 2), new Point(3, 1)};
Assert.AreEqual(firstPath, firstExpected);
var secondPath = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(5, 2), area, false).ToArray();
var secondExpected = firstExpected.Concat(new[] {new Point(4, 1), new Point(5, 1), new Point(5, 2)}).ToArray();
Assert.AreEqual(secondPath, secondExpected);
}
[Test]
public void TestBlocked() {
var area = new[] {
"XXXX",
"X XX",
"XX X",
"X X",
"XXXX"
};
// non-diagonal pathfinding should get stuck in the corner
Assert.IsNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 3), area, false));
// diagonal pathfinding should be able to cross the diagonal gap
Assert.IsNotNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 3), area, true));
}
[Test]
public void TestSpecialDirections() {
var area = new[] {
"XXXX",
"X XX",
"X X",
"XXXX",
"X X",
"XXXX"
};
// both types of traditional pathfinding should get stuck
Assert.IsNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 4), area, false));
Assert.IsNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 4), area, true));
// but if we define a link across the wall, it should work
Assert.IsNotNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 4), area, false, (p, n) => {
if (p.X == 2 && p.Y == 2)
n.Add(new Point(1, 4));
}));
}
[Test]
public void TestCostsAndMultipleGoals() {
var area = new[] {
"XXXXXXXX",
"X 2 X",
"XXXXX X",
"X 53 X",
"X XXX X",
"X X X X",
"XXXXXXXX"
};
var pathfinder = PathfindingTests.CreatePathfinder(area, false);
// try to find paths to each goal individually
var goals = new[] {new Point(1, 5), new Point(3, 5), new Point(5, 5)};
var goalCosts = new[] {19, float.PositiveInfinity, 9};
for (var i = 0; i < goals.Length; i++) {
pathfinder.TryFindPath(new Point(1, 1), new[] {goals[i]}, out _, out var cost);
Assert.AreEqual(goalCosts[i], cost);
}
// try to find paths to the best goal
var expected = new[] {new Point(1, 1), new Point(2, 1), new Point(3, 1), new Point(4, 1), new Point(5, 1), new Point(5, 2), new Point(5, 3), new Point(5, 4), new Point(5, 5)};
pathfinder.TryFindPath(new Point(1, 1), goals, out var path, out var bestCost);
Assert.AreEqual(bestCost, 9);
Assert.AreEqual(expected, path);
}
private static Stack<Point> FindPathInArea(Point start, Point end, IEnumerable<string> area, bool allowDiagonals, AStar2.CollectAdditionalNeighbors collectAdditionalNeighbors = null) {
return PathfindingTests.CreatePathfinder(area, allowDiagonals, collectAdditionalNeighbors).FindPath(start, end);
}
private static AStar2 CreatePathfinder(IEnumerable<string> area, bool allowDiagonals, AStar2.CollectAdditionalNeighbors collectAdditionalNeighbors = null) {
var costs = area.Select(s => s.Select(c => c switch {
' ' => 1,
'X' => float.PositiveInfinity,
_ => (float) char.GetNumericValue(c)
}).ToArray()).ToArray();
return new AStar2((_, p2) => costs[p2.Y][p2.X], allowDiagonals, 1, 64, collectAdditionalNeighbors);
}
var diagonals = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 2), area, true).ToArray();
Assert.AreEqual(diagonals.Length, 2);
Assert.AreEqual(diagonals[0], new Point(1, 1));
Assert.AreEqual(diagonals[1], new Point(2, 2));
} }
[Test]
public void TestPathCost() {
var area = new[] {
"XXXXXXXX",
"X 5 X",
"X 5 X",
"XXXXXXXX"
};
var firstPath = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(3, 1), area, false).ToArray();
var firstExpected = new[] {new Point(1, 1), new Point(1, 2), new Point(2, 2), new Point(3, 2), new Point(3, 1)};
Assert.AreEqual(firstPath, firstExpected);
var secondPath = PathfindingTests.FindPathInArea(new Point(1, 1), new Point(5, 2), area, false).ToArray();
var secondExpected = firstExpected.Concat(new[] {new Point(4, 1), new Point(5, 1), new Point(5, 2)}).ToArray();
Assert.AreEqual(secondPath, secondExpected);
}
[Test]
public void TestBlocked() {
var area = new[] {
"XXXX",
"X XX",
"XX X",
"X X",
"XXXX"
};
// non-diagonal pathfinding should get stuck in the corner
Assert.IsNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 3), area, false));
// diagonal pathfinding should be able to cross the diagonal gap
Assert.IsNotNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 3), area, true));
}
[Test]
public void TestSpecialDirections() {
var area = new[] {
"XXXX",
"X XX",
"X X",
"XXXX",
"X X",
"XXXX"
};
// both types of traditional pathfinding should get stuck
Assert.IsNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 4), area, false));
Assert.IsNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 4), area, true));
// but if we define a link across the wall, it should work
Assert.IsNotNull(PathfindingTests.FindPathInArea(new Point(1, 1), new Point(2, 4), area, false, (p, n) => {
if (p.X == 2 && p.Y == 2)
n.Add(new Point(1, 4));
}));
}
[Test]
public void TestCostsAndMultipleGoals() {
var area = new[] {
"XXXXXXXX",
"X 2 X",
"XXXXX X",
"X 53 X",
"X XXX X",
"X X X X",
"XXXXXXXX"
};
var pathfinder = PathfindingTests.CreatePathfinder(area, false);
// try to find paths to each goal individually
var goals = new[] {new Point(1, 5), new Point(3, 5), new Point(5, 5)};
var goalCosts = new[] {19, float.PositiveInfinity, 9};
for (var i = 0; i < goals.Length; i++) {
pathfinder.TryFindPath(new Point(1, 1), new[] {goals[i]}, out _, out var cost);
Assert.AreEqual(goalCosts[i], cost);
}
// try to find paths to the best goal
var expected = new[] {new Point(1, 1), new Point(2, 1), new Point(3, 1), new Point(4, 1), new Point(5, 1), new Point(5, 2), new Point(5, 3), new Point(5, 4), new Point(5, 5)};
pathfinder.TryFindPath(new Point(1, 1), goals, out var path, out var bestCost);
Assert.AreEqual(bestCost, 9);
Assert.AreEqual(expected, path);
}
private static Stack<Point> FindPathInArea(Point start, Point end, IEnumerable<string> area, bool allowDiagonals, AStar2.CollectAdditionalNeighbors collectAdditionalNeighbors = null) {
return PathfindingTests.CreatePathfinder(area, allowDiagonals, collectAdditionalNeighbors).FindPath(start, end);
}
private static AStar2 CreatePathfinder(IEnumerable<string> area, bool allowDiagonals, AStar2.CollectAdditionalNeighbors collectAdditionalNeighbors = null) {
var costs = area.Select(s => s.Select(c => c switch {
' ' => 1,
'X' => float.PositiveInfinity,
_ => (float) char.GetNumericValue(c)
}).ToArray()).ToArray();
return new AStar2((_, p2) => costs[p2.Y][p2.X], allowDiagonals, 1, 64, collectAdditionalNeighbors);
}
} }

View file

@ -3,24 +3,24 @@ using MLEM.Data.Content;
using MLEM.Font; using MLEM.Font;
using MLEM.Startup; using MLEM.Startup;
namespace Tests { namespace Tests;
public class TestGame : MlemGame {
public RawContentManager RawContent { get; private set; } public class TestGame : MlemGame {
private TestGame() {} public RawContentManager RawContent { get; private set; }
protected override void LoadContent() { private TestGame() {}
base.LoadContent();
this.RawContent = new RawContentManager(this.Services, this.Content.RootDirectory);
this.UiSystem.Style.Font = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("TestFont"));
}
public static TestGame Create() {
var game = new TestGame();
game.RunOneFrame();
return game;
}
protected override void LoadContent() {
base.LoadContent();
this.RawContent = new RawContentManager(this.Services, this.Content.RootDirectory);
this.UiSystem.Style.Font = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("TestFont"));
} }
public static TestGame Create() {
var game = new TestGame();
game.RunOneFrame();
return game;
}
} }

View file

@ -4,106 +4,106 @@ using MLEM.Data;
using MLEM.Textures; using MLEM.Textures;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class TexturePackerTests {
private Texture2D testTexture; public class TexturePackerTests {
private Texture2D disposedTestTexture;
private TestGame game;
[SetUp] private Texture2D testTexture;
public void SetUp() { private Texture2D disposedTestTexture;
this.game = TestGame.Create(); private TestGame game;
this.testTexture = new Texture2D(this.game.GraphicsDevice, 256, 256);
this.disposedTestTexture = new Texture2D(this.game.GraphicsDevice, 16, 16);
}
[TearDown]
public void TearDown() {
this.game?.Dispose();
this.testTexture?.Dispose();
this.disposedTestTexture?.Dispose();
}
[Test]
public void TestPacking() {
using var packer = new RuntimeTexturePacker();
for (var i = 0; i < 5; i++) {
var width = 16 * (i + 1);
packer.Add(new TextureRegion(this.testTexture, 0, 0, width, 64), r => {
Assert.AreEqual(r.Width, width);
Assert.AreEqual(r.Height, 64);
});
}
packer.Pack(this.game.GraphicsDevice);
Assert.AreEqual(packer.PackedTexture.Width, 16 + 32 + 48 + 64 + 80);
Assert.AreEqual(packer.PackedTexture.Height, 64);
}
[Test]
public void TestDisposal() {
using var packer = new RuntimeTexturePacker(128, disposeTextures: true);
packer.Add(new TextureRegion(this.disposedTestTexture), TexturePackerTests.StubResult);
packer.Add(new TextureRegion(this.disposedTestTexture, 0, 0, 8, 8), TexturePackerTests.StubResult);
packer.Pack(this.game.GraphicsDevice);
Assert.True(this.disposedTestTexture.IsDisposed);
Assert.False(packer.PackedTexture.IsDisposed);
}
[Test]
public void TestBounds() {
// test forced max width
using var packer = new RuntimeTexturePacker(128);
Assert.Throws<InvalidOperationException>(() => {
packer.Add(new TextureRegion(this.testTexture, 0, 0, 256, 128), TexturePackerTests.StubResult);
});
// test auto-expanding width
using var packer2 = new RuntimeTexturePacker(128, true);
Assert.DoesNotThrow(() => {
packer2.Add(new TextureRegion(this.testTexture, 0, 0, 256, 128), TexturePackerTests.StubResult);
});
packer2.Pack(this.game.GraphicsDevice);
// test power of two forcing
using var packer3 = new RuntimeTexturePacker(128, forcePowerOfTwo: true);
packer3.Add(new TextureRegion(this.testTexture, 0, 0, 37, 170), TexturePackerTests.StubResult);
packer3.Pack(this.game.GraphicsDevice);
Assert.AreEqual(64, packer3.PackedTexture.Width);
Assert.AreEqual(256, packer3.PackedTexture.Height);
// test square forcing
using var packer4 = new RuntimeTexturePacker(128, forceSquare: true);
packer4.Add(new TextureRegion(this.testTexture, 0, 0, 37, 170), TexturePackerTests.StubResult);
packer4.Pack(this.game.GraphicsDevice);
Assert.AreEqual(170, packer4.PackedTexture.Width);
Assert.AreEqual(170, packer4.PackedTexture.Height);
}
[Test]
public void TestPackMultipleTimes() {
using var packer = new RuntimeTexturePacker(1024);
// pack the first time
var results = 0;
for (var i = 0; i < 10; i++)
packer.Add(new TextureRegion(this.testTexture, 0, 0, 64, 64), _ => results++);
packer.Pack(this.game.GraphicsDevice);
Assert.AreEqual(10, results);
// pack without resizing
packer.Add(new TextureRegion(this.testTexture, 0, 0, 0, 0), _ => results++);
packer.Pack(this.game.GraphicsDevice);
Assert.AreEqual(11, results);
// pack and force a resize
packer.Add(new TextureRegion(this.testTexture, 0, 0, 64, 64), _ => results++);
packer.Pack(this.game.GraphicsDevice);
// all callbacks are called again, so we add 11 again, as well as the callback we just added
Assert.AreEqual(2 * 11 + 1, results);
}
private static void StubResult(TextureRegion region) {}
[SetUp]
public void SetUp() {
this.game = TestGame.Create();
this.testTexture = new Texture2D(this.game.GraphicsDevice, 256, 256);
this.disposedTestTexture = new Texture2D(this.game.GraphicsDevice, 16, 16);
} }
[TearDown]
public void TearDown() {
this.game?.Dispose();
this.testTexture?.Dispose();
this.disposedTestTexture?.Dispose();
}
[Test]
public void TestPacking() {
using var packer = new RuntimeTexturePacker();
for (var i = 0; i < 5; i++) {
var width = 16 * (i + 1);
packer.Add(new TextureRegion(this.testTexture, 0, 0, width, 64), r => {
Assert.AreEqual(r.Width, width);
Assert.AreEqual(r.Height, 64);
});
}
packer.Pack(this.game.GraphicsDevice);
Assert.AreEqual(packer.PackedTexture.Width, 16 + 32 + 48 + 64 + 80);
Assert.AreEqual(packer.PackedTexture.Height, 64);
}
[Test]
public void TestDisposal() {
using var packer = new RuntimeTexturePacker(128, disposeTextures: true);
packer.Add(new TextureRegion(this.disposedTestTexture), TexturePackerTests.StubResult);
packer.Add(new TextureRegion(this.disposedTestTexture, 0, 0, 8, 8), TexturePackerTests.StubResult);
packer.Pack(this.game.GraphicsDevice);
Assert.True(this.disposedTestTexture.IsDisposed);
Assert.False(packer.PackedTexture.IsDisposed);
}
[Test]
public void TestBounds() {
// test forced max width
using var packer = new RuntimeTexturePacker(128);
Assert.Throws<InvalidOperationException>(() => {
packer.Add(new TextureRegion(this.testTexture, 0, 0, 256, 128), TexturePackerTests.StubResult);
});
// test auto-expanding width
using var packer2 = new RuntimeTexturePacker(128, true);
Assert.DoesNotThrow(() => {
packer2.Add(new TextureRegion(this.testTexture, 0, 0, 256, 128), TexturePackerTests.StubResult);
});
packer2.Pack(this.game.GraphicsDevice);
// test power of two forcing
using var packer3 = new RuntimeTexturePacker(128, forcePowerOfTwo: true);
packer3.Add(new TextureRegion(this.testTexture, 0, 0, 37, 170), TexturePackerTests.StubResult);
packer3.Pack(this.game.GraphicsDevice);
Assert.AreEqual(64, packer3.PackedTexture.Width);
Assert.AreEqual(256, packer3.PackedTexture.Height);
// test square forcing
using var packer4 = new RuntimeTexturePacker(128, forceSquare: true);
packer4.Add(new TextureRegion(this.testTexture, 0, 0, 37, 170), TexturePackerTests.StubResult);
packer4.Pack(this.game.GraphicsDevice);
Assert.AreEqual(170, packer4.PackedTexture.Width);
Assert.AreEqual(170, packer4.PackedTexture.Height);
}
[Test]
public void TestPackMultipleTimes() {
using var packer = new RuntimeTexturePacker(1024);
// pack the first time
var results = 0;
for (var i = 0; i < 10; i++)
packer.Add(new TextureRegion(this.testTexture, 0, 0, 64, 64), _ => results++);
packer.Pack(this.game.GraphicsDevice);
Assert.AreEqual(10, results);
// pack without resizing
packer.Add(new TextureRegion(this.testTexture, 0, 0, 0, 0), _ => results++);
packer.Pack(this.game.GraphicsDevice);
Assert.AreEqual(11, results);
// pack and force a resize
packer.Add(new TextureRegion(this.testTexture, 0, 0, 64, 64), _ => results++);
packer.Pack(this.game.GraphicsDevice);
// all callbacks are called again, so we add 11 again, as well as the callback we just added
Assert.AreEqual(2 * 11 + 1, results);
}
private static void StubResult(TextureRegion region) {}
} }

View file

@ -8,143 +8,143 @@ using MLEM.Ui.Elements;
using MLEM.Ui.Style; using MLEM.Ui.Style;
using NUnit.Framework; using NUnit.Framework;
namespace Tests { namespace Tests;
public class UiTests {
private TestGame game; public class UiTests {
[SetUp] private TestGame game;
public void SetUp() {
this.game = TestGame.Create();
}
[TearDown]
public void TearDown() {
this.game?.Dispose();
}
[Test]
public void TestInvalidPanel() {
var invalidPanel = new Panel(Anchor.Center, Vector2.Zero, Vector2.Zero) {
SetWidthBasedOnChildren = true,
SetHeightBasedOnChildren = true
};
invalidPanel.AddChild(new Paragraph(Anchor.AutoRight, 1, "This is some test text!", true));
invalidPanel.AddChild(new VerticalSpace(1));
Assert.Throws<ArithmeticException>(() => this.AddAndUpdate(invalidPanel));
}
[Test]
public void TestOddlyAlignedPanel() {
var oddPanel = new Panel(Anchor.Center, Vector2.One, Vector2.Zero, true) {SetWidthBasedOnChildren = true};
oddPanel.AddChild(new Group(Anchor.TopCenter, new Vector2(100), false));
oddPanel.AddChild(new Group(Anchor.AutoRight, new Vector2(120), false));
this.AddAndUpdate(oddPanel);
Assert.AreEqual(120 + 10, oddPanel.DisplayArea.Width);
}
[Test]
public void TestComplexPanel() {
var group = new Group(Anchor.TopLeft, Vector2.One, false);
var panel = group.AddChild(new Panel(Anchor.Center, new Vector2(150, 150), Vector2.Zero, false, true, false) {
ChildPadding = new Padding(5, 5, 5, 5)
});
for (var i = 0; i < 5; i++) {
var button = panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) {
SetHeightBasedOnChildren = true,
Padding = new Padding(0, 0, 0, 1),
ChildPadding = new Padding(3)
});
button.AddChild(new Group(Anchor.AutoLeft, new Vector2(0.5F, 30), false) {
CanBeMoused = false
});
}
this.AddAndUpdate(group);
// group has 1 panel with 1 scroll bar, and the panel's 10 children
Assert.AreEqual(1, group.GetChildren().Count());
Assert.AreEqual(12, group.GetChildren(regardGrandchildren: true).Count());
// panel 1 scroll bar and 5 buttons, each button has 1 group, so 11 grandchildren
Assert.AreEqual(6, panel.GetChildren().Count());
Assert.AreEqual(11, panel.GetChildren(regardGrandchildren: true).Count());
var testBtn = panel.GetChildren<Button>().First();
// panel's width is 150, minus child padding of 5 on each side, and scroll bar's width of 5 and gap of 1
const int panelContentWidth = 150 - 5 - 5 - 5 - 1;
Assert.AreEqual(testBtn.DisplayArea.Width, panelContentWidth);
// button's width, minus child padding of 3 left and 3 right, divided by 2 because of group's width
Assert.AreEqual(testBtn.GetChildren<Group>().Single().DisplayArea.Width, (panelContentWidth - 3 - 3) * 0.5F);
}
[Test]
public void TestAbsoluteAutoSize() {
var parent = new Panel(Anchor.AutoLeft, new Vector2(200, 100), Vector2.Zero) {
ChildPadding = Padding.Empty
};
var el1 = parent.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 0.75F)) {
AutoSizeAddedAbsolute = new Vector2(-50, 25)
});
var el2 = parent.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.25F, -0.5F)) {
AutoSizeAddedAbsolute = new Vector2(-25, 50)
});
this.AddAndUpdate(parent);
Assert.AreEqual(0.5F * 200 - 50, el1.DisplayArea.Width);
Assert.AreEqual(0.75F * 100 + 25, el1.DisplayArea.Height);
const float el2Width = 0.25F * 200 - 25;
Assert.AreEqual(el2Width, el2.DisplayArea.Width);
Assert.AreEqual(0.5F * el2Width + 50, el2.DisplayArea.Height);
}
[Test]
public void TestStyle() {
var style = new StyleProp<string>();
Assert.AreEqual(null, style.Value);
style = style.OrStyle("from style");
Assert.AreEqual("from style", style.Value);
style = "custom";
Assert.AreEqual("custom", style.Value);
style = style.OrStyle("from style again");
Assert.AreEqual("custom", style.Value);
var copy = style.OrStyle("copy from style", byte.MaxValue);
var weakCopy = style.OrStyle("weak copy");
Assert.AreEqual("copy from style", copy.Value);
Assert.AreEqual("custom", weakCopy.Value);
Assert.AreEqual("custom", style.Value);
}
[Test]
public void TestAutoAreaPerformance() {
var stopwatch = new Stopwatch();
for (var i = 1; i <= 100; i++) {
var totalUpdates = 0;
var main = new Group(Anchor.TopLeft, new Vector2(50)) {
OnAreaUpdated = _ => totalUpdates++
};
var group = main;
for (var g = 0; g < i; g++) {
group = group.AddChild(new Group(Anchor.TopLeft, Vector2.One) {
OnAreaUpdated = _ => totalUpdates++
});
}
stopwatch.Restart();
this.AddAndUpdate(main);
stopwatch.Stop();
var allChildren = main.GetChildren(regardGrandchildren: true);
TestContext.WriteLine($"{allChildren.Count()} children, {totalUpdates} updates total, took {stopwatch.Elapsed.TotalMilliseconds * 1000000}ns");
}
}
private void AddAndUpdate(Element element) {
foreach (var root in this.game.UiSystem.GetRootElements())
this.game.UiSystem.Remove(root.Name);
this.game.UiSystem.Add("Test", element);
element.ForceUpdateArea();
}
[SetUp]
public void SetUp() {
this.game = TestGame.Create();
} }
[TearDown]
public void TearDown() {
this.game?.Dispose();
}
[Test]
public void TestInvalidPanel() {
var invalidPanel = new Panel(Anchor.Center, Vector2.Zero, Vector2.Zero) {
SetWidthBasedOnChildren = true,
SetHeightBasedOnChildren = true
};
invalidPanel.AddChild(new Paragraph(Anchor.AutoRight, 1, "This is some test text!", true));
invalidPanel.AddChild(new VerticalSpace(1));
Assert.Throws<ArithmeticException>(() => this.AddAndUpdate(invalidPanel));
}
[Test]
public void TestOddlyAlignedPanel() {
var oddPanel = new Panel(Anchor.Center, Vector2.One, Vector2.Zero, true) {SetWidthBasedOnChildren = true};
oddPanel.AddChild(new Group(Anchor.TopCenter, new Vector2(100), false));
oddPanel.AddChild(new Group(Anchor.AutoRight, new Vector2(120), false));
this.AddAndUpdate(oddPanel);
Assert.AreEqual(120 + 10, oddPanel.DisplayArea.Width);
}
[Test]
public void TestComplexPanel() {
var group = new Group(Anchor.TopLeft, Vector2.One, false);
var panel = group.AddChild(new Panel(Anchor.Center, new Vector2(150, 150), Vector2.Zero, false, true, false) {
ChildPadding = new Padding(5, 5, 5, 5)
});
for (var i = 0; i < 5; i++) {
var button = panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) {
SetHeightBasedOnChildren = true,
Padding = new Padding(0, 0, 0, 1),
ChildPadding = new Padding(3)
});
button.AddChild(new Group(Anchor.AutoLeft, new Vector2(0.5F, 30), false) {
CanBeMoused = false
});
}
this.AddAndUpdate(group);
// group has 1 panel with 1 scroll bar, and the panel's 10 children
Assert.AreEqual(1, group.GetChildren().Count());
Assert.AreEqual(12, group.GetChildren(regardGrandchildren: true).Count());
// panel 1 scroll bar and 5 buttons, each button has 1 group, so 11 grandchildren
Assert.AreEqual(6, panel.GetChildren().Count());
Assert.AreEqual(11, panel.GetChildren(regardGrandchildren: true).Count());
var testBtn = panel.GetChildren<Button>().First();
// panel's width is 150, minus child padding of 5 on each side, and scroll bar's width of 5 and gap of 1
const int panelContentWidth = 150 - 5 - 5 - 5 - 1;
Assert.AreEqual(testBtn.DisplayArea.Width, panelContentWidth);
// button's width, minus child padding of 3 left and 3 right, divided by 2 because of group's width
Assert.AreEqual(testBtn.GetChildren<Group>().Single().DisplayArea.Width, (panelContentWidth - 3 - 3) * 0.5F);
}
[Test]
public void TestAbsoluteAutoSize() {
var parent = new Panel(Anchor.AutoLeft, new Vector2(200, 100), Vector2.Zero) {
ChildPadding = Padding.Empty
};
var el1 = parent.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 0.75F)) {
AutoSizeAddedAbsolute = new Vector2(-50, 25)
});
var el2 = parent.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.25F, -0.5F)) {
AutoSizeAddedAbsolute = new Vector2(-25, 50)
});
this.AddAndUpdate(parent);
Assert.AreEqual(0.5F * 200 - 50, el1.DisplayArea.Width);
Assert.AreEqual(0.75F * 100 + 25, el1.DisplayArea.Height);
const float el2Width = 0.25F * 200 - 25;
Assert.AreEqual(el2Width, el2.DisplayArea.Width);
Assert.AreEqual(0.5F * el2Width + 50, el2.DisplayArea.Height);
}
[Test]
public void TestStyle() {
var style = new StyleProp<string>();
Assert.AreEqual(null, style.Value);
style = style.OrStyle("from style");
Assert.AreEqual("from style", style.Value);
style = "custom";
Assert.AreEqual("custom", style.Value);
style = style.OrStyle("from style again");
Assert.AreEqual("custom", style.Value);
var copy = style.OrStyle("copy from style", byte.MaxValue);
var weakCopy = style.OrStyle("weak copy");
Assert.AreEqual("copy from style", copy.Value);
Assert.AreEqual("custom", weakCopy.Value);
Assert.AreEqual("custom", style.Value);
}
[Test]
public void TestAutoAreaPerformance() {
var stopwatch = new Stopwatch();
for (var i = 1; i <= 100; i++) {
var totalUpdates = 0;
var main = new Group(Anchor.TopLeft, new Vector2(50)) {
OnAreaUpdated = _ => totalUpdates++
};
var group = main;
for (var g = 0; g < i; g++) {
group = group.AddChild(new Group(Anchor.TopLeft, Vector2.One) {
OnAreaUpdated = _ => totalUpdates++
});
}
stopwatch.Restart();
this.AddAndUpdate(main);
stopwatch.Stop();
var allChildren = main.GetChildren(regardGrandchildren: true);
TestContext.WriteLine($"{allChildren.Count()} children, {totalUpdates} updates total, took {stopwatch.Elapsed.TotalMilliseconds * 1000000}ns");
}
}
private void AddAndUpdate(Element element) {
foreach (var root in this.game.UiSystem.GetRootElements())
this.game.UiSystem.Remove(root.Name);
this.game.UiSystem.Add("Test", element);
element.ForceUpdateArea();
}
} }