Initial commit: OpenRA game engine
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled

Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
let5sne.win10
2026-01-10 21:46:54 +08:00
commit 9cf6ebb986
4065 changed files with 635973 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using OpenRA.Primitives;
namespace OpenRA.Test
{
[TestFixture]
sealed class ActionQueueTest
{
[TestCase(TestName = "ActionQueue performs actions in order of time, then insertion order.")]
public void ActionsArePerformedOrderedByTimeThenByInsertionOrder()
{
var list = new List<int>();
var queue = new ActionQueue();
queue.Add(() => list.Add(1), 0);
queue.Add(() => list.Add(7), 2);
queue.Add(() => list.Add(8), 2);
queue.Add(() => list.Add(4), 1);
queue.Add(() => list.Add(2), 0);
queue.Add(() => list.Add(3), 0);
queue.Add(() => list.Add(9), 2);
queue.Add(() => list.Add(5), 1);
queue.Add(() => list.Add(6), 1);
queue.PerformActions(1);
queue.PerformActions(2);
queue.PerformActions(3);
if (!list.SequenceEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]))
Assert.Fail("Actions were not performed in the correct order. Actual order was: " + string.Join(", ", list));
}
}
}

View File

@@ -0,0 +1,123 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Linq;
using NUnit.Framework;
using OpenRA.Traits;
namespace OpenRA.Test
{
interface IMock : ITraitInfoInterface { }
class MockTraitInfo : TraitInfo { public override object Create(ActorInitializer init) { return null; } }
class MockInheritInfo : MockTraitInfo { }
sealed class MockAInfo : MockInheritInfo, IMock { }
sealed class MockBInfo : MockTraitInfo, Requires<IMock>, Requires<MockInheritInfo> { }
sealed class MockCInfo : MockTraitInfo, Requires<MockBInfo> { }
sealed class MockDInfo : MockTraitInfo, Requires<MockEInfo> { }
sealed class MockEInfo : MockTraitInfo, Requires<MockFInfo> { }
sealed class MockFInfo : MockTraitInfo, Requires<MockDInfo> { }
sealed class MockGInfo : MockInheritInfo, IMock, NotBefore<MockAInfo> { }
sealed class MockHInfo : MockTraitInfo, NotBefore<IMock>, NotBefore<MockInheritInfo>, NotBefore<MockBInfo> { }
sealed class MockIInfo : MockTraitInfo, NotBefore<MockHInfo>, NotBefore<MockCInfo> { }
sealed class MockJInfo : MockTraitInfo, NotBefore<MockKInfo> { }
sealed class MockKInfo : MockTraitInfo, NotBefore<MockLInfo> { }
sealed class MockLInfo : MockTraitInfo, NotBefore<MockJInfo> { }
[TestFixture]
sealed class ActorInfoTest
{
[TestCase(TestName = "Trait ordering sorts in dependency order correctly")]
public void TraitOrderingSortsCorrectly()
{
var unorderedTraits = new TraitInfo[] { new MockBInfo(), new MockCInfo(), new MockAInfo(), new MockBInfo() };
var actorInfo = new ActorInfo("test", unorderedTraits);
var orderedTraits = actorInfo.TraitsInConstructOrder().ToArray();
Assert.That(unorderedTraits, Is.EquivalentTo(orderedTraits));
for (var i = 0; i < orderedTraits.Length; i++)
{
var traitTypesThatMustOccurBeforeThisTrait =
ActorInfo.PrerequisitesOf(orderedTraits[i]).Concat(ActorInfo.OptionalPrerequisitesOf(orderedTraits[i]));
var traitTypesThatOccurAfterThisTrait = orderedTraits.Skip(i + 1).Select(ti => ti.GetType());
var traitTypesThatShouldOccurEarlier = traitTypesThatOccurAfterThisTrait.Intersect(traitTypesThatMustOccurBeforeThisTrait);
Assert.That(traitTypesThatShouldOccurEarlier, Is.Empty, "Dependency order has not been satisfied.");
}
}
[TestCase(TestName = "Trait ordering sorts in optional dependency order correctly")]
public void OptionalTraitOrderingSortsCorrectly()
{
var unorderedTraits = new TraitInfo[] { new MockHInfo(), new MockIInfo(), new MockGInfo(), new MockHInfo() };
var actorInfo = new ActorInfo("test", unorderedTraits);
var orderedTraits = actorInfo.TraitsInConstructOrder().ToArray();
Assert.That(unorderedTraits, Is.EquivalentTo(orderedTraits));
for (var i = 0; i < orderedTraits.Length; i++)
{
var traitTypesThatMustOccurBeforeThisTrait =
ActorInfo.PrerequisitesOf(orderedTraits[i]).Concat(ActorInfo.OptionalPrerequisitesOf(orderedTraits[i]));
var traitTypesThatOccurAfterThisTrait = orderedTraits.Skip(i + 1).Select(ti => ti.GetType());
var traitTypesThatShouldOccurEarlier = traitTypesThatOccurAfterThisTrait.Intersect(traitTypesThatMustOccurBeforeThisTrait);
Assert.That(traitTypesThatShouldOccurEarlier, Is.Empty, "Dependency order has not been satisfied.");
}
}
[TestCase(TestName = "Trait ordering exception reports missing dependencies")]
public void TraitOrderingReportsMissingDependencies()
{
var actorInfo = new ActorInfo("test", new MockBInfo(), new MockCInfo());
var ex = Assert.Throws<YamlException>(() => actorInfo.TraitsInConstructOrder());
Assert.That(ex.Message, Does.Contain(nameof(MockBInfo)), "Exception message did not report a missing dependency.");
Assert.That(ex.Message, Does.Contain(nameof(MockCInfo)), "Exception message did not report a missing dependency.");
Assert.That(ex.Message, Does.Contain(nameof(MockInheritInfo)), "Exception message did not report a missing dependency (from a base class).");
Assert.That(ex.Message, Does.Contain(nameof(IMock)), "Exception message did not report a missing dependency (from an interface).");
}
[TestCase(TestName = "Trait ordering allows optional dependencies to be missing")]
public void TraitOrderingAllowsMissingOptionalDependencies()
{
var unorderedTraits = new TraitInfo[] { new MockHInfo(), new MockIInfo() };
var actorInfo = new ActorInfo("test", unorderedTraits);
var orderedTraits = actorInfo.TraitsInConstructOrder().ToArray();
Assert.That(unorderedTraits, Is.EquivalentTo(orderedTraits));
}
[TestCase(TestName = "Trait ordering exception reports cyclic dependencies")]
public void TraitOrderingReportsCyclicDependencies()
{
var actorInfo = new ActorInfo("test", new MockDInfo(), new MockEInfo(), new MockFInfo());
var ex = Assert.Throws<YamlException>(() => actorInfo.TraitsInConstructOrder());
Assert.That(ex.Message, Does.Contain(nameof(MockDInfo)), "Exception message should report all cyclic dependencies.");
Assert.That(ex.Message, Does.Contain(nameof(MockEInfo)), "Exception message should report all cyclic dependencies.");
Assert.That(ex.Message, Does.Contain(nameof(MockFInfo)), "Exception message should report all cyclic dependencies.");
}
[TestCase(TestName = "Trait ordering exception reports cyclic optional dependencies")]
public void TraitOrderingReportsCyclicOptionalDependencies()
{
var actorInfo = new ActorInfo("test", new MockJInfo(), new MockKInfo(), new MockLInfo());
var ex = Assert.Throws<YamlException>(() => actorInfo.TraitsInConstructOrder());
Assert.That(ex.Message, Does.Contain(nameof(MockJInfo)), "Exception message should report all cyclic dependencies.");
Assert.That(ex.Message, Does.Contain(nameof(MockKInfo)), "Exception message should report all cyclic dependencies.");
Assert.That(ex.Message, Does.Contain(nameof(MockLInfo)), "Exception message should report all cyclic dependencies.");
}
}
}

View File

@@ -0,0 +1,41 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using NUnit.Framework;
namespace OpenRA.Test
{
[TestFixture]
sealed class CPosTest
{
[TestCase(TestName = "Packing x,y and layer into int")]
public void PackUnpackBits()
{
var values = new int[] { -2048, -1024, 0, 1024, 2047 };
var layerValues = new byte[] { 0, 128, 255 };
foreach (var x in values)
{
foreach (var y in values)
{
foreach (var layer in layerValues)
{
var cell = new CPos(x, y, layer);
Assert.That(x, Is.EqualTo(cell.X));
Assert.That(y, Is.EqualTo(cell.Y));
Assert.That(layer, Is.EqualTo(cell.Layer));
}
}
}
}
}
}

View File

@@ -0,0 +1,91 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Linq;
using NUnit.Framework;
namespace OpenRA.Test
{
[TestFixture]
sealed class CoordinateTest
{
[TestCase(TestName = "Test CPos to MPos conversion and back again.")]
public void CPosConversionRoundtrip()
{
foreach (var gridType in Enum.GetValues<MapGridType>())
{
var expected = new CellCoordsRegion(new CPos(-12, -12), new CPos(12, 12));
var actual = expected.Select(pos => pos.ToMPos(gridType).ToCPos(gridType)).ToArray();
Assert.That(expected, Is.EqualTo(actual));
}
}
[TestCase(TestName = "Test MPos to CPos conversion and back again.")]
public void MPosConversionRoundtrip()
{
foreach (var gridType in Enum.GetValues<MapGridType>())
{
var expected = new MapCoordsRegion(new MPos(-12, -12), new MPos(12, 12));
var actual = expected.Select(pos => pos.ToCPos(gridType).ToMPos(gridType)).ToArray();
Assert.That(expected, Is.EqualTo(actual));
}
}
[TestCase(TestName = "Test directional movement of ToCPos.")]
public void TestIsometricCPosConversion()
{
const MapGridType Isometric = MapGridType.RectangularIsometric;
Assert.That(new CPos(0, 0), Is.EqualTo(new MPos(0, 0).ToCPos(Isometric)));
Assert.That(new CPos(1, 1), Is.EqualTo(new MPos(0, 2).ToCPos(Isometric)));
Assert.That(new CPos(2, 2), Is.EqualTo(new MPos(0, 4).ToCPos(Isometric)));
Assert.That(new CPos(3, 3), Is.EqualTo(new MPos(0, 6).ToCPos(Isometric)));
Assert.That(new CPos(1, 0), Is.EqualTo(new MPos(0, 1).ToCPos(Isometric)));
Assert.That(new CPos(2, 0), Is.EqualTo(new MPos(1, 2).ToCPos(Isometric)));
Assert.That(new CPos(3, 0), Is.EqualTo(new MPos(1, 3).ToCPos(Isometric)));
Assert.That(new CPos(0, 1), Is.EqualTo(new MPos(-1, 1).ToCPos(Isometric)));
Assert.That(new CPos(0, 2), Is.EqualTo(new MPos(-1, 2).ToCPos(Isometric)));
Assert.That(new CPos(0, 3), Is.EqualTo(new MPos(-2, 3).ToCPos(Isometric)));
Assert.That(new CPos(1, -1), Is.EqualTo(new MPos(1, 0).ToCPos(Isometric)));
Assert.That(new CPos(2, -2), Is.EqualTo(new MPos(2, 0).ToCPos(Isometric)));
Assert.That(new CPos(3, -3), Is.EqualTo(new MPos(3, 0).ToCPos(Isometric)));
}
[TestCase(TestName = "Test directional movement of ToMPos.")]
public void TestIsometricMPosConversion()
{
const MapGridType Isometric = MapGridType.RectangularIsometric;
Assert.That(new MPos(0, 0), Is.EqualTo(new CPos(0, 0).ToMPos(Isometric)));
Assert.That(new MPos(0, 2), Is.EqualTo(new CPos(1, 1).ToMPos(Isometric)));
Assert.That(new MPos(0, 4), Is.EqualTo(new CPos(2, 2).ToMPos(Isometric)));
Assert.That(new MPos(0, 6), Is.EqualTo(new CPos(3, 3).ToMPos(Isometric)));
Assert.That(new MPos(0, 1), Is.EqualTo(new CPos(1, 0).ToMPos(Isometric)));
Assert.That(new MPos(1, 2), Is.EqualTo(new CPos(2, 0).ToMPos(Isometric)));
Assert.That(new MPos(1, 3), Is.EqualTo(new CPos(3, 0).ToMPos(Isometric)));
Assert.That(new MPos(-1, 1), Is.EqualTo(new CPos(0, 1).ToMPos(Isometric)));
Assert.That(new MPos(-1, 2), Is.EqualTo(new CPos(0, 2).ToMPos(Isometric)));
Assert.That(new MPos(-2, 3), Is.EqualTo(new CPos(0, 3).ToMPos(Isometric)));
Assert.That(new MPos(1, 0), Is.EqualTo(new CPos(1, -1).ToMPos(Isometric)));
Assert.That(new MPos(2, 0), Is.EqualTo(new CPos(2, -2).ToMPos(Isometric)));
Assert.That(new MPos(3, 0), Is.EqualTo(new CPos(3, -3).ToMPos(Isometric)));
}
}
}

View File

@@ -0,0 +1,833 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using NUnit.Framework.Internal;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA.Test
{
[TestFixture]
sealed class FieldLoaderTest
{
sealed class TypeInfo
{
#pragma warning disable CS0169
#pragma warning disable CS0649
#pragma warning disable CA1823 // Avoid unused private fields
#pragma warning disable IDE0044 // Add readonly modifier
#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable RCS1170 // Use read-only auto-implemented property
int privateField;
public int PublicField;
[FieldLoader.Ignore]
int privateIgnoreField;
[FieldLoader.Ignore]
public int PublicIgnoreField;
[FieldLoader.Require]
int privateRequiredField;
[FieldLoader.Require]
public int PublicRequiredField;
[FieldLoader.LoadUsing(nameof(LoadInt32))]
int privateLoadUsingField;
[FieldLoader.LoadUsing(nameof(LoadInt32))]
public int PublicLoadUsingField;
int PrivateProperty { get; set; }
public int PublicProperty { get; set; }
static int privateStaticField;
public static int PublicStaticField;
static object LoadInt32(MiniYaml _) => 123;
#pragma warning restore RCS1170 // Use read-only auto-implemented property
#pragma warning restore IDE0051 // Remove unused private members
#pragma warning restore IDE0044 // Add readonly modifier
#pragma warning restore CA1823 // Avoid unused private fields
#pragma warning disable CS0649
#pragma warning restore CS0169
}
[Test]
public void GetTypeLoadInfo()
{
var infos = FieldLoader.GetTypeLoadInfo(typeof(TypeInfo)).ToList();
Assert.That(infos, Has.Count.EqualTo(5));
Assert.That(infos[0].Field, Is.EqualTo(typeof(TypeInfo).GetField(nameof(TypeInfo.PublicField))));
Assert.That(infos[0].Attribute, Is.EqualTo(FieldLoader.SerializeAttribute.Default));
Assert.That(infos[0].YamlName, Is.EqualTo(nameof(TypeInfo.PublicField)));
Assert.That(infos[0].Loader, Is.Null);
Assert.That(infos[1].Field, Is.EqualTo(typeof(TypeInfo).GetField("privateRequiredField", BindingFlags.NonPublic | BindingFlags.Instance)));
Assert.That(infos[1].Attribute, Is.EqualTo(new FieldLoader.RequireAttribute()));
Assert.That(infos[1].YamlName, Is.EqualTo("privateRequiredField"));
Assert.That(infos[1].Loader, Is.Null);
Assert.That(infos[2].Field, Is.EqualTo(typeof(TypeInfo).GetField(nameof(TypeInfo.PublicRequiredField))));
Assert.That(infos[2].Attribute, Is.EqualTo(new FieldLoader.RequireAttribute()));
Assert.That(infos[2].YamlName, Is.EqualTo(nameof(TypeInfo.PublicRequiredField)));
Assert.That(infos[2].Loader, Is.Null);
Assert.That(infos[3].Field, Is.EqualTo(typeof(TypeInfo).GetField("privateLoadUsingField", BindingFlags.NonPublic | BindingFlags.Instance)));
Assert.That(infos[3].Attribute, Is.EqualTo(new FieldLoader.LoadUsingAttribute("LoadInt32")));
Assert.That(infos[3].YamlName, Is.EqualTo("privateLoadUsingField"));
Assert.That(infos[3].Loader(new MiniYaml(null)), Is.EqualTo(123));
Assert.That(infos[4].Field, Is.EqualTo(typeof(TypeInfo).GetField(nameof(TypeInfo.PublicLoadUsingField))));
Assert.That(infos[4].Attribute, Is.EqualTo(new FieldLoader.LoadUsingAttribute("LoadInt32")));
Assert.That(infos[4].YamlName, Is.EqualTo(nameof(TypeInfo.PublicLoadUsingField)));
Assert.That(infos[4].Loader(new MiniYaml(null)), Is.EqualTo(123));
}
[Test]
public void GetValue_UnknownField()
{
static void Act() => FieldLoader.GetValue<object>("field", "test");
Assert.That(Act, Throws.TypeOf<NotImplementedException>().And.Message.EqualTo("FieldLoader: Missing field `[Type] test` on `Object`"));
}
static IEnumerable<TestCaseData> GetValue_InvalidValue_TestCases()
{
return
[
new TestCaseData(null) { TypeArgs = [typeof(int)] },
new TestCaseData("test") { TypeArgs = [typeof(int)] },
new TestCaseData("1.2") { TypeArgs = [typeof(int)] },
new TestCaseData((int.MaxValue + 1L).ToString(CultureInfo.InvariantCulture)) { TypeArgs = [typeof(int)] },
new TestCaseData(null) { TypeArgs = [typeof(ushort)] },
new TestCaseData("test") { TypeArgs = [typeof(ushort)] },
new TestCaseData("1.2") { TypeArgs = [typeof(ushort)] },
new TestCaseData((ushort.MaxValue + 1L).ToString(CultureInfo.InvariantCulture)) { TypeArgs = [typeof(ushort)] },
new TestCaseData(null) { TypeArgs = [typeof(long)] },
new TestCaseData("test") { TypeArgs = [typeof(long)] },
new TestCaseData("1.2") { TypeArgs = [typeof(long)] },
new TestCaseData((long.MaxValue + 1UL).ToString(CultureInfo.InvariantCulture)) { TypeArgs = [typeof(long)] },
new TestCaseData(null) { TypeArgs = [typeof(float)] },
new TestCaseData("test") { TypeArgs = [typeof(float)] },
new TestCaseData("1,2") { TypeArgs = [typeof(float)] },
new TestCaseData(null) { TypeArgs = [typeof(decimal)] },
new TestCaseData("test") { TypeArgs = [typeof(decimal)] },
new TestCaseData("1,2") { TypeArgs = [typeof(decimal)] },
new TestCaseData(null) { TypeArgs = [typeof(Color)] },
new TestCaseData("test") { TypeArgs = [typeof(Color)] },
new TestCaseData(null) { TypeArgs = [typeof(Hotkey)] },
new TestCaseData("test") { TypeArgs = [typeof(Hotkey)] },
new TestCaseData(null) { TypeArgs = [typeof(WDist)] },
new TestCaseData("test") { TypeArgs = [typeof(WDist)] },
new TestCaseData(null) { TypeArgs = [typeof(WVec)] },
new TestCaseData("test") { TypeArgs = [typeof(WVec)] },
new TestCaseData("1,2") { TypeArgs = [typeof(WVec)] },
new TestCaseData("1,test,3") { TypeArgs = [typeof(WVec)] },
new TestCaseData("1,2,3,4") { TypeArgs = [typeof(WVec)] },
new TestCaseData(null) { TypeArgs = [typeof(WVec[])] },
new TestCaseData("test") { TypeArgs = [typeof(WVec[])] },
new TestCaseData("1,2") { TypeArgs = [typeof(WVec[])] },
new TestCaseData("1,test,3") { TypeArgs = [typeof(WVec[])] },
new TestCaseData("1,2,3,4") { TypeArgs = [typeof(WVec[])] },
new TestCaseData(null) { TypeArgs = [typeof(WPos)] },
new TestCaseData("test") { TypeArgs = [typeof(WPos)] },
new TestCaseData("1,2") { TypeArgs = [typeof(WPos)] },
new TestCaseData("1,test,3") { TypeArgs = [typeof(WPos)] },
new TestCaseData("1,2,3,4") { TypeArgs = [typeof(WPos)] },
new TestCaseData(null) { TypeArgs = [typeof(WAngle)] },
new TestCaseData("test") { TypeArgs = [typeof(WAngle)] },
new TestCaseData("1,2") { TypeArgs = [typeof(WAngle)] },
new TestCaseData(null) { TypeArgs = [typeof(WRot)] },
new TestCaseData("test") { TypeArgs = [typeof(WRot)] },
new TestCaseData("1,2") { TypeArgs = [typeof(WRot)] },
new TestCaseData("1,test,3") { TypeArgs = [typeof(WRot)] },
new TestCaseData("1,2,3,4") { TypeArgs = [typeof(WRot)] },
new TestCaseData(null) { TypeArgs = [typeof(CPos)] },
new TestCaseData("test") { TypeArgs = [typeof(CPos)] },
new TestCaseData("1") { TypeArgs = [typeof(CPos)] },
new TestCaseData("1,test,3") { TypeArgs = [typeof(CPos)] },
new TestCaseData("1,2,3,4") { TypeArgs = [typeof(CPos)] },
new TestCaseData(null) { TypeArgs = [typeof(CPos[])] },
new TestCaseData("test") { TypeArgs = [typeof(CPos[])] },
new TestCaseData("1") { TypeArgs = [typeof(CPos[])] },
new TestCaseData("1,test") { TypeArgs = [typeof(CPos[])] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(CPos[])] },
new TestCaseData(null) { TypeArgs = [typeof(CVec)] },
new TestCaseData("test") { TypeArgs = [typeof(CVec)] },
new TestCaseData("1") { TypeArgs = [typeof(CVec)] },
new TestCaseData("1,test") { TypeArgs = [typeof(CVec)] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(CVec)] },
new TestCaseData(null) { TypeArgs = [typeof(CVec[])] },
new TestCaseData("test") { TypeArgs = [typeof(CVec[])] },
new TestCaseData("1") { TypeArgs = [typeof(CVec[])] },
new TestCaseData("1,test") { TypeArgs = [typeof(CVec[])] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(CVec[])] },
new TestCaseData(null) { TypeArgs = [typeof(BooleanExpression)] },
new TestCaseData(null) { TypeArgs = [typeof(IntegerExpression)] },
new TestCaseData(null) { TypeArgs = [typeof(MapGridType)] },
new TestCaseData(null) { TypeArgs = [typeof(bool)] },
new TestCaseData("test") { TypeArgs = [typeof(bool)] },
new TestCaseData(null) { TypeArgs = [typeof(int2[])] },
new TestCaseData("test") { TypeArgs = [typeof(int2[])] },
new TestCaseData("1") { TypeArgs = [typeof(int2[])] },
new TestCaseData("1,test") { TypeArgs = [typeof(int2[])] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(int2[])] },
new TestCaseData(null) { TypeArgs = [typeof(Size)] },
new TestCaseData("test") { TypeArgs = [typeof(Size)] },
new TestCaseData("1") { TypeArgs = [typeof(Size)] },
new TestCaseData("1,test") { TypeArgs = [typeof(Size)] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(Size)] },
new TestCaseData(null) { TypeArgs = [typeof(int2)] },
new TestCaseData("test") { TypeArgs = [typeof(int2)] },
new TestCaseData("1") { TypeArgs = [typeof(int2)] },
new TestCaseData("1,test") { TypeArgs = [typeof(int2)] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(int2)] },
new TestCaseData(null) { TypeArgs = [typeof(float2)] },
new TestCaseData("test") { TypeArgs = [typeof(float2)] },
new TestCaseData("1") { TypeArgs = [typeof(float2)] },
new TestCaseData("1,test") { TypeArgs = [typeof(float2)] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(float2)] },
new TestCaseData(null) { TypeArgs = [typeof(float3)] },
new TestCaseData("test") { TypeArgs = [typeof(float3)] },
new TestCaseData("1") { TypeArgs = [typeof(float3)] },
new TestCaseData("1,test") { TypeArgs = [typeof(float3)] },
new TestCaseData("1,2,3,4") { TypeArgs = [typeof(float3)] },
new TestCaseData(null) { TypeArgs = [typeof(Rectangle)] },
new TestCaseData("test") { TypeArgs = [typeof(Rectangle)] },
new TestCaseData("1,2,3") { TypeArgs = [typeof(Rectangle)] },
new TestCaseData("1,test,3,4") { TypeArgs = [typeof(Rectangle)] },
new TestCaseData("1,2,3,4,5") { TypeArgs = [typeof(Rectangle)] },
new TestCaseData(null) { TypeArgs = [typeof(DateTime)] },
new TestCaseData("test") { TypeArgs = [typeof(DateTime)] },
new TestCaseData("2000-01-01") { TypeArgs = [typeof(DateTime)] },
];
}
[TestCaseSource(nameof(GetValue_InvalidValue_TestCases))]
public void GetValue_InvalidValue<T>(string input)
{
void Act() => FieldLoader.GetValue<T>("field", input);
Assert.That(Act, Throws.TypeOf<YamlException>().And.Message.EqualTo($"FieldLoader: Cannot parse `{input}` into `field.{typeof(T).FullName}`"));
}
[TestCase(TypeArgs = [typeof(BooleanExpression)])]
[TestCase(TypeArgs = [typeof(IntegerExpression)])]
public void GetValue_InvalidValue<T>()
{
static void Act() => FieldLoader.GetValue<T>("field", "");
Assert.That(Act, Throws.TypeOf<YamlException>().And.Message.EqualTo($"FieldLoader: Cannot parse `` into `field.{typeof(T).FullName}`: Empty expression"));
}
static IEnumerable<TestCaseData> GetValue_Primitive_TestCases()
{
return
[
new TestCaseData(123),
new TestCaseData((ushort)123),
new TestCaseData(123L),
new TestCaseData(123.4f),
new TestCaseData(123m),
new TestCaseData("test"),
new TestCaseData(Color.CornflowerBlue),
new TestCaseData(new Hotkey(Keycode.A, Modifiers.Shift)),
new TestCaseData(new WDist(123)),
new TestCaseData(new WVec(123, 456, 789)),
new TestCaseData(new WPos(123, 456, 789)),
new TestCaseData(new WAngle(123)),
new TestCaseData(new WRot(new WAngle(123), new WAngle(456), new WAngle(789))),
new TestCaseData(new CPos(123, 456)),
new TestCaseData(new CPos(123, 456, 78)),
new TestCaseData(new CVec(123, 456)),
new TestCaseData(MapGridType.RectangularIsometric),
new TestCaseData((MapGridType)byte.MaxValue),
new TestCaseData(SystemActors.World | SystemActors.EditorWorld),
new TestCaseData(true),
new TestCaseData(new Size(123, 456)),
new TestCaseData(new int2(123, 456)),
new TestCaseData(new float2(123, 456)),
new TestCaseData(new float3(123, 456, 789)),
new TestCaseData(new Rectangle(123, 456, 789, 123)),
];
}
[TestCaseSource(nameof(GetValue_Primitive_TestCases))]
public void GetValue_Primitive<T>(T expected)
{
var actual = FieldLoader.GetValue<T>("field", $" {expected} ");
Assert.That(actual, Is.EqualTo(expected));
}
[Test]
public void GetValue_NullString()
{
var actual = FieldLoader.GetValue<string>("field", null);
Assert.That(actual, Is.Null);
}
[Test]
public void GetValue_BooleanExpression()
{
var actual = FieldLoader.GetValue<BooleanExpression>("field", " true ");
Assert.That(actual.Expression, Is.EqualTo("true"));
}
[Test]
public void GetValue_IntegerExpression()
{
var actual = FieldLoader.GetValue<IntegerExpression>("field", " 1 + 2 ");
Assert.That(actual.Expression, Is.EqualTo("1 + 2"));
}
[Test]
public void GetValue_DateTime()
{
var expected = new DateTime(2000, 1, 1);
var input = expected.ToString("yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture);
var actual = FieldLoader.GetValue<DateTime>("field", $" {input} ");
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase("123%", 1.23f)]
[TestCase("%123", 1.23f)]
[TestCase("123.456%", 1.23456f)]
[TestCase("%123.456", 1.23456f)]
public void GetValue_FloatPercentage(string input, float expected)
{
var actual = FieldLoader.GetValue<float>("field", $" {input} ");
Assert.That(actual, Is.EqualTo(expected));
}
static IEnumerable<TestCaseData> GetValue_DecimalPercentage_TestCases()
{
return
[
new TestCaseData("123%", 1.23m),
new TestCaseData("%123", 1.23m),
new TestCaseData("123.456%", 1.23456m),
new TestCaseData("%123.456", 1.23456m),
];
}
[TestCaseSource(nameof(GetValue_DecimalPercentage_TestCases))]
public void GetValue_DecimalPercentage(string input, decimal expected)
{
var actual = FieldLoader.GetValue<decimal>("field", $" {input} ");
Assert.That(actual, Is.EqualTo(expected));
}
[Test]
public void GetValue_float3_TwoElements()
{
var actual = FieldLoader.GetValue<float3>("field", "123,456");
Assert.That(actual, Is.EqualTo(new float3(123, 456, 0)));
}
[Test]
public void GetValue_WVecArray()
{
var actual = FieldLoader.GetValue<WVec[]>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new WVec[] { new(1, 2, 3), new(4, 5, 6) }));
}
[Test]
public void GetValue_CPosArray()
{
var actual = FieldLoader.GetValue<CPos[]>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new CPos[] { new(1, 2), new(3, 4), new(5, 6) }));
}
[Test]
public void GetValue_CVecArray()
{
var actual = FieldLoader.GetValue<CVec[]>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new CVec[] { new(1, 2), new(3, 4), new(5, 6) }));
}
[Test]
public void GetValue_int2Array()
{
var actual = FieldLoader.GetValue<int2[]>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new int2[] { new(1, 2), new(3, 4), new(5, 6) }));
}
[Test]
public void GetValue_WVecImmutableArray()
{
var actual = FieldLoader.GetValue<ImmutableArray<WVec>>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new WVec[] { new(1, 2, 3), new(4, 5, 6) }));
}
[Test]
public void GetValue_CPosImmutableArray()
{
var actual = FieldLoader.GetValue<ImmutableArray<CPos>>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new CPos[] { new(1, 2), new(3, 4), new(5, 6) }));
}
[Test]
public void GetValue_CVecImmutableArray()
{
var actual = FieldLoader.GetValue<ImmutableArray<CVec>>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new CVec[] { new(1, 2), new(3, 4), new(5, 6) }));
}
[Test]
public void GetValue_int2ImmutableArray()
{
var actual = FieldLoader.GetValue<ImmutableArray<int2>>("field", " 1 , 2 , 3 , 4 , 5 , 6 , , ");
Assert.That(actual, Is.EqualTo(new int2[] { new(1, 2), new(3, 4), new(5, 6) }));
}
[TestCase(null, null)]
[TestCase("", null)]
[TestCase("123", 123)]
public void GetValue_Nullable(string input, int? expected)
{
var actual = FieldLoader.GetValue<int?>("field", $" {input} ");
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, new int[] { })]
[TestCase("", new int[] { })]
[TestCase("1", new int[] { 1 })]
[TestCase("1,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,,3", new int[] { 1, 3 })]
[TestCase(" 1 , 2 , 3 ", new int[] { 1, 2, 3 })]
[TestCase(" 1 , , 3 ", new int[] { 1, 3 })]
[TestCase("1,1,2,2,3", new int[] { 1, 1, 2, 2, 3 })]
[TestCase("1,1,1,1", new int[] { 1, 1, 1, 1 })]
public void GetValue_Array(string input, int[] expected)
{
var actual = FieldLoader.GetValue<int[]>("field", input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, new int[] { })]
[TestCase("", new int[] { })]
[TestCase("1", new int[] { 1 })]
[TestCase("1,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,,3", new int[] { 1, 3 })]
[TestCase(" 1 , 2 , 3 ", new int[] { 1, 2, 3 })]
[TestCase(" 1 , , 3 ", new int[] { 1, 3 })]
[TestCase("1,1,2,2,3", new int[] { 1, 1, 2, 2, 3 })]
[TestCase("1,1,1,1", new int[] { 1, 1, 1, 1 })]
public void GetValue_List(string input, int[] expected)
{
var actual = FieldLoader.GetValue<List<int>>("field", input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, new int[] { })]
[TestCase("", new int[] { })]
[TestCase("1", new int[] { 1 })]
[TestCase("1,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,,3", new int[] { 1, 3 })]
[TestCase(" 1 , 2 , 3 ", new int[] { 1, 2, 3 })]
[TestCase(" 1 , , 3 ", new int[] { 1, 3 })]
[TestCase("1,1,2,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,1,1,1", new int[] { 1 })]
public void GetValue_HashSet(string input, int[] expected)
{
var actual = FieldLoader.GetValue<HashSet<int>>("field", input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null)]
[TestCase("")]
[TestCase("1")]
[TestCase("1,2,3")]
[TestCase("1,,3")]
[TestCase(" 1 , 2 , 3 ")]
[TestCase(" 1 , , 3 ")]
[TestCase("1,1,2,2,3")]
[TestCase("1,1,1,1")]
public void GetValue_BitSet(string input)
{
var actual = FieldLoader.GetValue<BitSet<FieldLoaderTest>>("field", input);
var expected = input == null
? new BitSet<FieldLoaderTest>([])
: new BitSet<FieldLoaderTest>(input.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null)]
[TestCase("")]
[TestCase("1")]
[TestCase("1,2")]
public void GetValue_Dictionary(string input)
{
var actual = FieldLoader.GetValue<Dictionary<int, int>>("field", input);
Assert.That(actual, Is.Empty);
}
[TestCase(null, new int[] { })]
[TestCase("", new int[] { })]
[TestCase("1", new int[] { 1 })]
[TestCase("1,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,,3", new int[] { 1, 3 })]
[TestCase(" 1 , 2 , 3 ", new int[] { 1, 2, 3 })]
[TestCase(" 1 , , 3 ", new int[] { 1, 3 })]
[TestCase("1,1,2,2,3", new int[] { 1, 1, 2, 2, 3 })]
[TestCase("1,1,1,1", new int[] { 1, 1, 1, 1 })]
public void GetValue_ImmutableArray(string input, int[] expected)
{
var actual = FieldLoader.GetValue<ImmutableArray<int>>("field", input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, new int[] { })]
[TestCase("", new int[] { })]
[TestCase("1", new int[] { 1 })]
[TestCase("1,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,,3", new int[] { 1, 3 })]
[TestCase(" 1 , 2 , 3 ", new int[] { 1, 2, 3 })]
[TestCase(" 1 , , 3 ", new int[] { 1, 3 })]
[TestCase("1,1,2,2,3", new int[] { 1, 2, 3 })]
[TestCase("1,1,1,1", new int[] { 1 })]
public void GetValue_FrozenSet(string input, int[] expected)
{
var actual = FieldLoader.GetValue<FrozenSet<int>>("field", input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null)]
[TestCase("")]
[TestCase("1")]
[TestCase("1,2")]
public void GetValue_FrozenDictionary(string input)
{
var actual = FieldLoader.GetValue<FrozenDictionary<int, int>>("field", input);
Assert.That(actual, Is.Empty);
}
[TestCase(TypeArgs = [typeof(int[][])])]
[TestCase(TypeArgs = [typeof(List<List<int>>)])]
[TestCase(TypeArgs = [typeof(HashSet<HashSet<int>>)])]
[TestCase(TypeArgs = [typeof(ImmutableArray<ImmutableArray<int>>)])]
[TestCase(TypeArgs = [typeof(FrozenSet<FrozenSet<int>>)])]
public void GetValue_NestedCollections<T>()
{
var actual = FieldLoader.GetValue<T>("field", "1,2,3");
Assert.That(actual, Is.EquivalentTo(new int[][] { [1], [2], [3] }));
}
[Test]
public void GetValue_TypeConverter()
{
// We don't have hardcoded handling for sbyte, but a TypeConverter for it does exist.
var actual = FieldLoader.GetValue<sbyte>("field", " 123 ");
Assert.That(actual, Is.EqualTo(123));
}
[Test]
public void GetValue_TypeConverter_Invalid()
{
static void Act() => FieldLoader.GetValue<sbyte>("field", " test ");
Assert.That(Act, Throws.TypeOf<YamlException>().And.Message.EqualTo($"FieldLoader: Cannot parse `test` into `field.{typeof(sbyte).FullName}`"));
}
sealed class LoadFieldOrPropertyTarget
{
#pragma warning disable IDE0044 // Add readonly modifier
#pragma warning disable RCS1170 // Use read-only auto-implemented property
int privateIntField;
int PrivateIntProp { get; set; }
public int PublicIntField;
public int PublicIntProp { get; set; }
public int GetPrivateIntField() => privateIntField;
public int GetPrivateIntProp() => PrivateIntProp;
#pragma warning restore IDE0044 // Add readonly modifier
#pragma warning restore RCS1170 // Use read-only auto-implemented property
}
[Test]
public void LoadFieldOrProperty()
{
var target = new LoadFieldOrPropertyTarget();
FieldLoader.LoadFieldOrProperty(target, $" {nameof(LoadFieldOrPropertyTarget.PublicIntField)} ", "12");
FieldLoader.LoadFieldOrProperty(target, $" {nameof(LoadFieldOrPropertyTarget.PublicIntProp)} ", "34");
FieldLoader.LoadFieldOrProperty(target, " privateIntField ", "56");
FieldLoader.LoadFieldOrProperty(target, " PrivateIntProp ", "78");
void Act() => FieldLoader.LoadFieldOrProperty(target, "unknown", "");
Assert.That(target.PublicIntField, Is.EqualTo(12));
Assert.That(target.PublicIntProp, Is.EqualTo(34));
Assert.That(target.GetPrivateIntField(), Is.EqualTo(56));
Assert.That(target.GetPrivateIntProp(), Is.EqualTo(78));
Assert.That(Act, Throws.TypeOf<NotImplementedException>().And.Message.EqualTo("FieldLoader: Missing field `unknown` on `LoadFieldOrPropertyTarget`"));
}
sealed class LoadTarget
{
public int Int;
public string String;
public string Unset;
}
[Test]
public void Load()
{
var target = new LoadTarget() { Unset = "unset" };
var yaml = new MiniYaml(
null,
[
new MiniYamlNode(nameof(LoadTarget.Int), "123"),
new MiniYamlNode(nameof(LoadTarget.String), "test"),
]);
FieldLoader.Load(target, yaml);
Assert.That(target.Int, Is.EqualTo(123));
Assert.That(target.String, Is.EqualTo("test"));
Assert.That(target.Unset, Is.EqualTo("unset"));
}
[Test]
public void Load_Generic()
{
var expected = new LoadTarget();
var yaml = new MiniYaml(
null,
[
new MiniYamlNode(nameof(LoadTarget.Int), "123"),
new MiniYamlNode(nameof(LoadTarget.String), "test"),
]);
FieldLoader.Load(expected, yaml);
var actual = FieldLoader.Load<LoadTarget>(yaml);
Assert.That(actual.Int, Is.EqualTo(expected.Int));
Assert.That(actual.String, Is.EqualTo(expected.String));
Assert.That(actual.Unset, Is.EqualTo(expected.Unset));
}
sealed class LoadDictionaryTarget
{
public Dictionary<int, int> Dictionary;
}
[Test]
public void Load_Dictionary()
{
var target = new LoadDictionaryTarget();
var yaml = new MiniYaml(
null,
[
new MiniYamlNode(
nameof(LoadDictionaryTarget.Dictionary),
new MiniYaml(
null,
[
new MiniYamlNode("12", "34"),
new MiniYamlNode("56", "78")
]))
]);
FieldLoader.Load(target, yaml);
Assert.That(target.Dictionary, Is.EquivalentTo(new Dictionary<int, int> { { 12, 34 }, { 56, 78 } }));
}
sealed class LoadFrozenDictionaryTarget
{
public FrozenDictionary<int, int> Dictionary;
}
[Test]
public void Load_FrozenDictionary()
{
var target = new LoadFrozenDictionaryTarget();
var yaml = new MiniYaml(
null,
[
new MiniYamlNode(
nameof(LoadFrozenDictionaryTarget.Dictionary),
new MiniYaml(
null,
[
new MiniYamlNode("12", "34"),
new MiniYamlNode("56", "78")
]))
]);
FieldLoader.Load(target, yaml);
Assert.That(target.Dictionary, Is.EquivalentTo(new Dictionary<int, int> { { 12, 34 }, { 56, 78 } }));
}
sealed class LoadRequiredTarget
{
[FieldLoader.Require]
public int Int1 = 1;
[FieldLoader.Require]
public int Int2 = 2;
[FieldLoader.Require]
public int Int3 = 3;
public int Int4 = 4;
public int Int5 = 5;
}
[Test]
public void Load_Required()
{
var target = new LoadRequiredTarget();
var yaml = new MiniYaml(
null,
[
new MiniYamlNode(nameof(LoadRequiredTarget.Int1), "123"),
new MiniYamlNode(nameof(LoadRequiredTarget.Int4), "456"),
]);
void Act() => FieldLoader.Load(target, yaml);
Assert.That(Act,
Throws.TypeOf<FieldLoader.MissingFieldsException>().And
.Message.EqualTo($"{nameof(LoadRequiredTarget.Int2)}, {nameof(LoadRequiredTarget.Int3)}"));
Assert.That(target.Int1, Is.EqualTo(123));
Assert.That(target.Int2, Is.EqualTo(2));
Assert.That(target.Int3, Is.EqualTo(3));
Assert.That(target.Int4, Is.EqualTo(456));
Assert.That(target.Int5, Is.EqualTo(5));
}
sealed class LoadIgnoreTarget
{
[FieldLoader.Ignore]
public int Int1 = 1;
[FieldLoader.Ignore]
public int Int2 = 2;
public int Int3 = 3;
}
[Test]
public void Load_Ignore()
{
var target = new LoadIgnoreTarget();
var yaml = new MiniYaml(
null,
[
new MiniYamlNode(nameof(LoadIgnoreTarget.Int1), "123"),
new MiniYamlNode(nameof(LoadIgnoreTarget.Int3), "456"),
]);
FieldLoader.Load(target, yaml);
Assert.That(target.Int1, Is.EqualTo(1));
Assert.That(target.Int2, Is.EqualTo(2));
Assert.That(target.Int3, Is.EqualTo(456));
}
sealed class LoadUsingTarget
{
[FieldLoader.LoadUsing(nameof(LoadInt))]
public int Int1 = 1;
[FieldLoader.LoadUsing(nameof(LoadInt), true)]
public int Int2 = 2;
[FieldLoader.LoadUsing(nameof(LoadInt))]
public int Int3 = 3;
[FieldLoader.LoadUsing(nameof(LoadInt), true)]
public int Int4 = 4;
public int Int5 = 5;
static object LoadInt(MiniYaml yaml) => Exts.ParseInt32Invariant(yaml.NodeWithKey("ForLoadUsing").Value.Value);
}
[Test]
public void Load_Using()
{
var target = new LoadUsingTarget();
var yaml = new MiniYaml(
null,
[
new MiniYamlNode("ForLoadUsing", "100"),
new MiniYamlNode(nameof(LoadUsingTarget.Int1), "12"),
new MiniYamlNode(nameof(LoadUsingTarget.Int2), "34"),
new MiniYamlNode(nameof(LoadUsingTarget.Int5), "56"),
]);
void Act() => FieldLoader.Load(target, yaml);
Assert.That(Act,
Throws.TypeOf<FieldLoader.MissingFieldsException>().And
.Message.EqualTo(nameof(LoadRequiredTarget.Int4)));
Assert.That(target.Int1, Is.EqualTo(100));
Assert.That(target.Int2, Is.EqualTo(100));
Assert.That(target.Int3, Is.EqualTo(100));
Assert.That(target.Int4, Is.EqualTo(4));
Assert.That(target.Int5, Is.EqualTo(56));
}
sealed class LoadUsingMissingTarget
{
[FieldLoader.LoadUsing("unknown")]
public int Int = 1;
}
[Test]
public void Load_UsingMissing()
{
var target = new LoadUsingMissingTarget();
var yaml = new MiniYaml(null);
void Act() => FieldLoader.Load(target, yaml);
Assert.That(Act,
Throws.TypeOf<InvalidOperationException>().And
.Message.EqualTo("LoadUsingMissingTarget does not specify a loader function 'unknown'"));
Assert.That(target.Int, Is.EqualTo(1));
}
}
}

View File

@@ -0,0 +1,343 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA.Test
{
[TestFixture]
sealed class FieldSaverTest
{
static IEnumerable<TestCaseData> FormatValue_Primitive_TestCases()
{
return
[
new TestCaseData(123),
new TestCaseData((ushort)123),
new TestCaseData(123L),
new TestCaseData(123.4f),
new TestCaseData(123m),
new TestCaseData("test"),
new TestCaseData(Color.CornflowerBlue),
new TestCaseData(new Hotkey(Keycode.A, Modifiers.Shift)),
new TestCaseData(new WDist(123)),
new TestCaseData(new WVec(123, 456, 789)),
new TestCaseData(new WPos(123, 456, 789)),
new TestCaseData(new WAngle(123)),
new TestCaseData(new WRot(new WAngle(123), new WAngle(456), new WAngle(789))),
new TestCaseData(new CPos(123, 456)),
new TestCaseData(new CPos(123, 456, 78)),
new TestCaseData(new CVec(123, 456)),
new TestCaseData(new BooleanExpression("true")),
new TestCaseData(new IntegerExpression("1 + 2")),
new TestCaseData(MapGridType.RectangularIsometric),
new TestCaseData(SystemActors.World | SystemActors.EditorWorld),
new TestCaseData(true),
new TestCaseData(new Size(123, 456)),
new TestCaseData(new int2(123, 456)),
new TestCaseData(new float2(123, 456)),
new TestCaseData(new float3(123, 456, 789)),
new TestCaseData(new Rectangle(123, 456, 789, 123)),
];
}
[TestCaseSource(nameof(FormatValue_Primitive_TestCases))]
public void FormatVaue_Primitive<T>(T expected)
{
var actual = FieldSaver.FormatValue(expected);
Assert.That(actual, Is.EqualTo(expected.ToString()));
}
[Test]
public void FormatValue_Null()
{
var actual = FieldSaver.FormatValue(null);
Assert.That(actual, Is.EqualTo(""));
}
[Test]
public void FormatValue_DateTime()
{
var input = new DateTime(2000, 1, 1);
var actual = FieldSaver.FormatValue(input);
Assert.That(actual, Is.EqualTo(input.ToString("yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture)));
}
[Test]
public void FormatValue_WVecArray()
{
var actual = FieldSaver.FormatValue(new WVec[] { new(1, 2, 3), new(4, 5, 6) });
Assert.That(actual, Is.EqualTo("1,2,3, 4,5,6"));
}
[Test]
public void FormatValue_CPosArray()
{
var actual = FieldSaver.FormatValue(new CPos[] { new(1, 2), new(3, 4), new(5, 6) });
Assert.That(actual, Is.EqualTo("1,2, 3,4, 5,6"));
}
[Test]
public void FormatValue_CVecArray()
{
var actual = FieldSaver.FormatValue(new CVec[] { new(1, 2), new(3, 4), new(5, 6) });
Assert.That(actual, Is.EqualTo("1,2, 3,4, 5,6"));
}
[Test]
public void FormatValue_int2Array()
{
var actual = FieldSaver.FormatValue(new int2[] { new(1, 2), new(3, 4), new(5, 6) });
Assert.That(actual, Is.EqualTo("1,2, 3,4, 5,6"));
}
[Test]
public void FormatValue_WVecImmutableArray()
{
var actual = FieldSaver.FormatValue(new WVec[] { new(1, 2, 3), new(4, 5, 6) }.ToImmutableArray());
Assert.That(actual, Is.EqualTo("1,2,3, 4,5,6"));
}
[Test]
public void FormatValue_CPosImmutableArray()
{
var actual = FieldSaver.FormatValue(new CPos[] { new(1, 2), new(3, 4), new(5, 6) }.ToImmutableArray());
Assert.That(actual, Is.EqualTo("1,2, 3,4, 5,6"));
}
[Test]
public void FormatValue_CVecImmutableArray()
{
var actual = FieldSaver.FormatValue(new CVec[] { new(1, 2), new(3, 4), new(5, 6) }.ToImmutableArray());
Assert.That(actual, Is.EqualTo("1,2, 3,4, 5,6"));
}
[Test]
public void FormatValue_int2ImmutableArray()
{
var actual = FieldSaver.FormatValue(new int2[] { new(1, 2), new(3, 4), new(5, 6) }.ToImmutableArray());
Assert.That(actual, Is.EqualTo("1,2, 3,4, 5,6"));
}
[TestCase(null, "")]
[TestCase(123, "123")]
public void FormatValue_Nullable(int? input, string expected)
{
var actual = FieldSaver.FormatValue(input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, "")]
[TestCase(new int[] { }, "")]
[TestCase(new int[] { 1 }, "1")]
[TestCase(new int[] { 1, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 2, 2, 3 }, "1, 1, 2, 2, 3")]
[TestCase(new int[] { 1, 1, 1, 1 }, "1, 1, 1, 1")]
public void FormatValue_Array(int[] input, string expected)
{
var actual = FieldSaver.FormatValue(input);
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, "")]
[TestCase(new int[] { }, "")]
[TestCase(new int[] { 1 }, "1")]
[TestCase(new int[] { 1, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 2, 2, 3 }, "1, 1, 2, 2, 3")]
[TestCase(new int[] { 1, 1, 1, 1 }, "1, 1, 1, 1")]
public void FormatValue_List(int[] input, string expected)
{
var actual = FieldSaver.FormatValue(input?.ToList());
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase(null, "")]
[TestCase(new int[] { }, "")]
[TestCase(new int[] { 1 }, "1")]
[TestCase(new int[] { 1, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 2, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 1, 1 }, "1")]
public void FormatValue_HashSet(int[] input, string expected)
{
var actual = FieldSaver.FormatValue(input?.ToHashSet());
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase("")]
[TestCase("1")]
[TestCase("1,2,3")]
[TestCase("1,1,2,2,3")]
[TestCase("1,1,1,1")]
public void FormatValue_BitSet(string input)
{
var actual = FieldSaver.FormatValue(new BitSet<FieldSaverTest>(input));
Assert.That(actual, Is.EqualTo(input));
}
[Test]
public void FormatValue_Dictionary()
{
var input = new Dictionary<int, int> { { 12, 34 }, { 56, 78 } };
var actual = FieldSaver.FormatValue(input);
Assert.That(actual, Is.EqualTo($"12: 34{Environment.NewLine}56: 78{Environment.NewLine}"));
}
[TestCase(null, "")]
[TestCase(new int[] { }, "")]
[TestCase(new int[] { 1 }, "1")]
[TestCase(new int[] { 1, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 2, 2, 3 }, "1, 1, 2, 2, 3")]
[TestCase(new int[] { 1, 1, 1, 1 }, "1, 1, 1, 1")]
public void FormatValue_ImmutableArray(int[] input, string expected)
{
var actual = FieldSaver.FormatValue(input?.ToImmutableArray());
Assert.That(actual, Is.EqualTo(expected));
}
[Test]
public void FormatValue_ImmutableArray_Default()
{
var actual = FieldSaver.FormatValue(default(ImmutableArray<int>));
Assert.That(actual, Is.Empty);
}
[TestCase(null, "")]
[TestCase(new int[] { }, "")]
[TestCase(new int[] { 1 }, "1")]
[TestCase(new int[] { 1, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 2, 2, 3 }, "1, 2, 3")]
[TestCase(new int[] { 1, 1, 1, 1 }, "1")]
public void FormatValue_FrozenSet(int[] input, string expected)
{
var actual = FieldSaver.FormatValue(input?.ToFrozenSet());
Assert.That(actual, Is.EqualTo(expected));
}
[Test]
public void FormatValue_FrozenDictionary()
{
var input = new Dictionary<int, int> { { 12, 34 }, { 56, 78 } }.ToFrozenDictionary();
var actual = FieldSaver.FormatValue(input);
Assert.That(actual, Is.EqualTo($"12: 34{Environment.NewLine}56: 78{Environment.NewLine}"));
}
sealed class FieldTarget
{
public int Int = 123;
}
[Test]
public void FormatValue_Field()
{
var actual = FieldSaver.FormatValue(new FieldTarget(), typeof(FieldTarget).GetField(nameof(FieldTarget.Int)));
Assert.That(actual, Is.EqualTo("123"));
}
[Test]
public void SaveField()
{
var actual = FieldSaver.SaveField(new FieldTarget(), nameof(FieldTarget.Int));
var actualString = MiniYamlExts.WriteToString([actual]);
var expectedString = MiniYamlExts.WriteToString([new MiniYamlNode(nameof(FieldTarget.Int), "123")]);
Assert.That(actualString, Is.EqualTo(expectedString));
}
sealed class SaveTarget
{
public int Int = 123;
public string String = "test";
public int[] IntArray = [1, 2, 3];
public Dictionary<string, string> StringDictionary = new() { { "a", "b" }, { "c", "d" } };
}
[Test]
public void Save()
{
var expected = new MiniYaml(
null,
[
new MiniYamlNode(nameof(SaveTarget.Int), "123"),
new MiniYamlNode(nameof(SaveTarget.String), "test"),
new MiniYamlNode(nameof(SaveTarget.IntArray), "1, 2, 3"),
new MiniYamlNode(
nameof(SaveTarget.StringDictionary),
new MiniYaml(null, [new MiniYamlNode("a", "b"), new MiniYamlNode("c", "d")])),
]);
var actual = FieldSaver.Save(new SaveTarget());
var actualString = MiniYamlExts.WriteToString(actual.Nodes);
var expectedString = MiniYamlExts.WriteToString(expected.Nodes);
Assert.That(actual.Value, Is.EqualTo(expected.Value));
Assert.That(actualString, Is.EqualTo(expectedString));
}
[Test]
public void SaveDifferences()
{
var expected = new MiniYaml(
null,
[
new MiniYamlNode(nameof(SaveTarget.String), ""),
new MiniYamlNode(nameof(SaveTarget.IntArray), "1, 2, 4"),
]);
var actual = FieldSaver.SaveDifferences(new SaveTarget { IntArray = [1, 2, 4], String = null }, new SaveTarget());
var actualString = MiniYamlExts.WriteToString(actual.Nodes);
var expectedString = MiniYamlExts.WriteToString(expected.Nodes);
Assert.That(actual.Value, Is.EqualTo(expected.Value));
Assert.That(actualString, Is.EqualTo(expectedString));
}
[Test]
public void SaveDifferences_DifferentTypes()
{
static void Act() => FieldSaver.SaveDifferences(new object(), new SaveTarget());
Assert.That(Act, Throws.TypeOf<InvalidOperationException>().And.Message.EqualTo("FieldSaver: can't diff objects of different types"));
}
}
}

View File

@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using NUnit.Framework;
namespace OpenRA.Test
{
[TestFixture]
sealed class FluentTest
{
readonly string pluralForms = @"
label-players = {$player ->
[one] One player
*[other] {$player} players
}
";
[TestCase(TestName = "Fluent Plural Terms")]
public void TestOne()
{
var bundle = new FluentBundle("en", pluralForms, e => Console.WriteLine(e.Message));
var label = bundle.GetMessage("label-players", ["player", 1]);
Assert.That("One player", Is.EqualTo(label));
label = bundle.GetMessage("label-players", ["player", 2]);
Assert.That("2 players", Is.EqualTo(label));
}
}
}

View File

@@ -0,0 +1,44 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using NUnit.Framework;
using OpenRA.Widgets;
namespace OpenRA.Test
{
[TestFixture]
sealed class MediatorTest
{
[TestCase(TestName = "Mediator test")]
public void Test()
{
var mediator = new Mediator();
var testHandler = new TestHandler();
mediator.Subscribe(testHandler);
mediator.Send(new TestNotificaton());
Assert.That(testHandler.WasNotified, Is.True);
}
}
sealed class TestHandler : INotificationHandler<TestNotificaton>
{
public bool WasNotified { get; set; }
public void Handle(TestNotificaton notification)
{
WasNotified = true;
}
}
sealed class TestNotificaton { }
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.IO;
using NUnit.Framework;
using OpenRA.Traits;
namespace OpenRA.Test
{
[TestFixture]
sealed class OrderTest
{
static byte[] RoundTripOrder(byte[] bytes)
{
return Order.Deserialize(null, new BinaryReader(new MemoryStream(bytes))).Serialize();
}
[TestCase(TestName = "Order data persists over serialization (empty)")]
public void SerializeEmpty()
{
var o = new Order().Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (unqueued)")]
public void SerializeUnqueued()
{
var o = new Order("Test", null, false).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (queued)")]
public void SerializeQueued()
{
var o = new Order("Test", null, true).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (pos target)")]
public void SerializePos()
{
var o = new Order("Test", null, Target.FromPos(new WPos(int.MinValue, 0, int.MaxValue)), false).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (invalid target)")]
public void SerializeInvalid()
{
var o = new Order("Test", null, Target.Invalid, false).Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
[TestCase(TestName = "Order data persists over serialization (extra fields)")]
public void SerializeExtra()
{
var o = new Order("Test", null, Target.Invalid, true)
{
TargetString = "TargetString",
ExtraLocation = new CPos(2047, 2047, 128),
ExtraData = uint.MaxValue,
IsImmediate = true,
}.Serialize();
Assert.That(RoundTripOrder(o), Is.EqualTo(o));
}
}
}

View File

@@ -0,0 +1,43 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.IO;
using NUnit.Framework;
namespace OpenRA.Test
{
[TestFixture]
sealed class PlatformTest
{
string supportDir;
string engineDir;
[SetUp]
public void SetUp()
{
supportDir = Platform.SupportDir;
engineDir = Platform.EngineDir;
}
[TestCase(TestName = "Returns literal paths")]
public void ResolvePath()
{
Assert.That(Platform.ResolvePath("^SupportDir|testpath"),
Is.EqualTo(Path.Combine(supportDir, "testpath")));
Assert.That(Platform.ResolvePath("^EngineDir|Foo.dll"),
Is.EqualTo(Path.Combine(engineDir, "Foo.dll")));
Assert.That(Platform.ResolvePath("testpath"),
Is.EqualTo("testpath"));
}
}
}

View File

@@ -0,0 +1,164 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.IO;
using System.Linq;
using NUnit.Framework;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Test
{
[TestFixture]
sealed class PngTests
{
[Test]
public void Save_ShouldProduceValidPngFile()
{
// Arrange
var colors = Enumerable.Range(0, 256).Select(i => Color.FromArgb(i, i, i)).ToArray();
var png = new Png(new byte[10 * 20], SpriteFrameType.Indexed8, 10, 20, colors);
// Act
var result = png.Save();
// Assert
Assert.That(Png.Verify(new MemoryStream(result)), Is.True);
}
[Test]
public void Save_Method_Should_Write_Indexed8_Palette_If_256_Colors_Or_Less()
{
// Arrange
var colors = Enumerable.Range(0, 256).Select(i => Color.FromArgb(i, i, i)).ToArray();
var png = new Png(new byte[10 * 20], SpriteFrameType.Indexed8, 10, 20, colors);
// Act
var result = png.Save();
// Assert
// Byte at index 25 contains color type information
// 0x03 represents Indexed8 with a palette
var colorTypeByte = result[25];
Assert.That(colorTypeByte, Is.EqualTo(0x03));
}
[Test]
public void Save_Method_Should_Write_Rgba32_If_Alpha_Channel_Required()
{
// Arrange
var png = new Png(new byte[10 * 20 * 4], SpriteFrameType.Rgba32, 10, 20);
// Act
var result = png.Save();
// Assert
// Byte at index 25 contains color type information
// 0x06 represents RGBA32 with alpha
var colorTypeByte = result[25];
Assert.That(colorTypeByte, Is.EqualTo(0x06));
}
[Test]
public void Save_ShouldThrowException_WhenDataLenghtNotEqualExpectedLenght()
{
// Arrange
var colors = Enumerable.Range(0, 256).Select(i => Color.FromArgb(i, i, i)).ToArray();
// Act
void TestDelegate() => new Png(new byte[10 * 20], SpriteFrameType.Indexed8, 100, 20, colors);
// Assert
var ex = Assert.Throws<InvalidDataException>(TestDelegate);
Assert.That(ex.Message, Is.EqualTo("Input data does not match expected length"));
}
[Test]
public void PngConstructor_InvalidSignature_ThrowsInvalidDataException()
{
// Arrange
byte[] invalidSignature = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
// Act & Assert
var exception = Assert.Throws<InvalidDataException>(() => new Png(new MemoryStream(invalidSignature)));
Assert.That("PNG Signature is bogus", Does.Contain(exception.Message));
}
[Test]
public void PngConstructor_HeaderNotFirst_ThrowsInvalidDataException()
{
// Arrange
var invalidPngData = new byte[]
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
0x00, 0x00, 0x00, 0x00,
};
// Act & Assert
var exception = Assert.Throws<InvalidDataException>(() => new Png(new MemoryStream(invalidPngData)));
Assert.That("Invalid PNG file - header does not appear first.", Does.Contain(exception.Message));
}
[Test]
public void PngConstructor_DuplicateIhdrHeader_ThrowsInvalidDataException()
{
// Arrange
var invalidPngData = new byte[]
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00,
};
using (var stream = new MemoryStream(invalidPngData))
{
// Act & Assert
var exception = Assert.Throws<EndOfStreamException>(() => new Png(stream));
}
}
[Test]
[Ignore("Failing test should be fixed")]
public void PngConstructor_CompressionMethodNotSupported_ThrowsInvalidDataException()
{
// Arrange
var invalidPngData = new byte[]
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00,
};
using (var stream = new MemoryStream(invalidPngData))
{
// Act & Assert
var exception = Assert.Throws<InvalidDataException>(() => new Png(stream));
Assert.That("Compression method not supported", Is.EqualTo(exception.Message));
}
}
[Test]
public void Constructor_ThrowsExceptionForNullPaletteIfTypeIsIndexed8()
{
// Arrange
const int Width = 100;
const int Height = 100;
const SpriteFrameType Type = SpriteFrameType.Indexed8;
// Act and Assert
Assert.Throws<InvalidDataException>(() => new Png(new byte[Width * Height], Type, Width, Height, null));
}
}
}

View File

@@ -0,0 +1,66 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using NUnit.Framework;
using OpenRA.Primitives;
namespace OpenRA.Test
{
[TestFixture]
sealed class PriorityArrayTest
{
[TestCase(0)]
[TestCase(5)]
[TestCase(int.MinValue)]
[TestCase(int.MaxValue)]
public void PriorityArraySequentialTest(int initialValue)
{
var input = new KeyValuePair<int, int>[]
{
new(0, 1),
new(1, 5),
new(2, 3),
new(3, 2),
new(4, 8),
new(5, 7),
new(6, 4),
new(7, 6)
};
var expected = new KeyValuePair<int, int>[]
{
new(0, 1),
new(3, 2),
new(2, 3),
new(6, 4),
new(1, 5),
new(7, 6),
new(5, 7),
new(4, 8)
};
var pa = new PriorityArray<int>(8, initialValue);
foreach (var kv in input)
pa[kv.Key] = kv.Value;
var readback = new KeyValuePair<int, int>[8];
for (var i = 0; i < 8; i++)
{
var index = pa.GetMinIndex();
readback[i] = new KeyValuePair<int, int>(index, pa[index]);
pa[index] = int.MaxValue;
}
Assert.That(readback, Is.EquivalentTo(expected));
}
}
}

View File

@@ -0,0 +1,156 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using OpenRA.Mods.Common;
using OpenRA.Support;
namespace OpenRA.Test
{
[TestFixture]
sealed class PriorityQueueTest
{
readonly struct Int32Comparer : IComparer<int>
{
public int Compare(int x, int y) => x.CompareTo(y);
}
[TestCase(1, 123)]
[TestCase(1, 1234)]
[TestCase(1, 12345)]
[TestCase(2, 123)]
[TestCase(2, 1234)]
[TestCase(2, 12345)]
[TestCase(10, 123)]
[TestCase(10, 1234)]
[TestCase(10, 12345)]
[TestCase(15, 123)]
[TestCase(15, 1234)]
[TestCase(15, 12345)]
[TestCase(16, 123)]
[TestCase(16, 1234)]
[TestCase(16, 12345)]
[TestCase(17, 123)]
[TestCase(17, 1234)]
[TestCase(17, 12345)]
[TestCase(100, 123)]
[TestCase(100, 1234)]
[TestCase(100, 12345)]
[TestCase(1000, 123)]
[TestCase(1000, 1234)]
[TestCase(1000, 12345)]
public void PriorityQueueAddThenRemoveTest(int count, int seed)
{
var mt = new MersenneTwister(seed);
var values = Enumerable.Range(0, count).ToList();
var shuffledValues = values.Shuffle(mt).ToArray();
var queue = new Primitives.PriorityQueue<int, Int32Comparer>(default);
Assert.That(queue.Empty, Is.True, "New queue should start out empty.");
Assert.Throws<InvalidOperationException>(() => queue.Peek(), "Peeking at an empty queue should throw.");
Assert.Throws<InvalidOperationException>(() => queue.Pop(), "Popping an empty queue should throw.");
foreach (var value in shuffledValues)
{
queue.Add(value);
Assert.That(queue.Empty, Is.False, "Queue should not be empty - items have been added.");
}
foreach (var value in values)
{
Assert.That(value, Is.EqualTo(queue.Peek()), "Peek returned the wrong item - should be in order.");
Assert.That(queue.Empty, Is.False, "Queue should not be empty yet.");
Assert.That(value, Is.EqualTo(queue.Pop()), "Pop returned the wrong item - should be in order.");
}
Assert.That(queue.Empty, Is.True, "Queue should now be empty.");
Assert.Throws<InvalidOperationException>(() => queue.Peek(), "Peeking at an empty queue should throw.");
Assert.Throws<InvalidOperationException>(() => queue.Pop(), "Popping an empty queue should throw.");
}
[TestCase(15, 123)]
[TestCase(15, 1234)]
[TestCase(15, 12345)]
[TestCase(16, 123)]
[TestCase(16, 1234)]
[TestCase(16, 12345)]
[TestCase(17, 123)]
[TestCase(17, 1234)]
[TestCase(17, 12345)]
[TestCase(100, 123)]
[TestCase(100, 1234)]
[TestCase(100, 12345)]
[TestCase(1000, 123)]
[TestCase(1000, 1234)]
[TestCase(1000, 12345)]
public void PriorityQueueAddAndRemoveInterleavedTest(int count, int seed)
{
var mt = new MersenneTwister(seed);
var shuffledValues = Enumerable.Range(0, count).Shuffle(mt).ToArray();
var queue = new Primitives.PriorityQueue<int, Int32Comparer>(default);
Assert.That(queue.Empty, Is.True, "New queue should start out empty.");
Assert.Throws<InvalidOperationException>(() => queue.Peek(), "Peeking at an empty queue should throw.");
Assert.Throws<InvalidOperationException>(() => queue.Pop(), "Popping an empty queue should throw.");
foreach (var value in shuffledValues.Take(10))
{
queue.Add(value);
Assert.That(queue.Empty, Is.False, "Queue should not be empty - items have been added.");
}
foreach (var value in shuffledValues.Take(10).Order().Take(5))
{
Assert.That(value, Is.EqualTo(queue.Peek()), "Peek returned the wrong item - should be in order.");
Assert.That(queue.Empty, Is.False, "Queue should not be empty yet.");
Assert.That(value, Is.EqualTo(queue.Pop()), "Pop returned the wrong item - should be in order.");
}
foreach (var value in shuffledValues.Skip(10).Take(5))
{
queue.Add(value);
Assert.That(queue.Empty, Is.False, "Queue should not be empty - items have been added.");
}
foreach (var value in shuffledValues.Take(10).Order().Skip(5)
.Concat(shuffledValues.Skip(10).Take(5)).Order().Take(5))
{
Assert.That(value, Is.EqualTo(queue.Peek()), "Peek returned the wrong item - should be in order.");
Assert.That(queue.Empty, Is.False, "Queue should not be empty yet.");
Assert.That(value, Is.EqualTo(queue.Pop()), "Pop returned the wrong item - should be in order.");
}
foreach (var value in shuffledValues.Skip(15))
{
queue.Add(value);
Assert.That(queue.Empty, Is.False, "Queue should not be empty - items have been added.");
}
foreach (var value in shuffledValues.Take(10).Order().Skip(5)
.Concat(shuffledValues.Skip(10).Take(5)).Order().Skip(5)
.Concat(shuffledValues.Skip(15)).Order())
{
Assert.That(value, Is.EqualTo(queue.Peek()), "Peek returned the wrong item - should be in order.");
Assert.That(queue.Empty, Is.False, "Queue should not be empty yet.");
Assert.That(value, Is.EqualTo(queue.Pop()), "Pop returned the wrong item - should be in order.");
}
Assert.That(queue.Empty, Is.True, "Queue should now be empty.");
Assert.Throws<InvalidOperationException>(() => queue.Peek(), "Peeking at an empty queue should throw.");
Assert.Throws<InvalidOperationException>(() => queue.Pop(), "Popping an empty queue should throw.");
}
}
}

View File

@@ -0,0 +1,35 @@
using NUnit.Framework;
using OpenRA.Primitives;
namespace OpenRA.Test
{
[TestFixture]
sealed class Sha1Tests
{
/// <summary>
/// https://en.wikipedia.org/wiki/SHA-1#Examples_and_pseudocode.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="expected">The expected hex string of the SHA1.</param>
[TestCase("The quick brown fox jumps over the lazy dog", "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")]
[TestCase("The quick brown fox jumps over the lazy cog", "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3")]
[TestCase("", "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
public void Sha1HexConvert(string input, string expected)
{
var actual = CryptoUtil.SHA1Hash(input);
Assert.That(expected, Is.EqualTo(actual));
}
[TestCase(0xFF0000FF, "0000FF")]
[TestCase(0xFF00FFFF, "00FFFF")]
[TestCase(0xFFFF00FF, "FF00FF")]
[TestCase(0xAAFF00FF, "FF00FFAA")]
public void ColorsToHex(uint value, string expected)
{
var color = Color.FromArgb(value);
var actual = color.ToString();
Assert.That(expected, Is.EqualTo(actual));
}
}
}

View File

@@ -0,0 +1,92 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using NUnit.Framework;
using OpenRA.Primitives;
namespace OpenRA.Test
{
[TestFixture]
sealed class SpatiallyPartitionedTest
{
[TestCase(TestName = "SpatiallyPartitioned.At works")]
public void SpatiallyPartitionedAtTest()
{
var partition = new SpatiallyPartitioned<object>(5, 5, 2);
var a = new object();
partition.Add(a, new Rectangle(0, 0, 1, 1));
Assert.That(partition.At(new int2(0, 0)), Does.Contain(a), "a is not present after add");
Assert.That(partition.At(new int2(0, 1)), Does.Not.Contain(a), "a is present in the wrong location");
Assert.That(partition.At(new int2(1, 0)), Does.Not.Contain(a), "a is present in the wrong location");
var b = new object();
partition.Add(b, new Rectangle(1, 1, 2, 2));
Assert.That(partition.At(new int2(0, 1)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.At(new int2(1, 0)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.At(new int2(1, 1)), Does.Contain(b), "b is not present after add");
Assert.That(partition.At(new int2(2, 2)), Does.Contain(b), "b is not present after add");
Assert.That(partition.At(new int2(2, 3)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.At(new int2(3, 2)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.At(new int2(3, 3)), Does.Not.Contain(b), "b is present in the wrong location");
partition[b] = new Rectangle(4, 4, 1, 1);
Assert.That(partition.At(new int2(0, 0)), Does.Contain(a), "a wrongly changed location when b was updated");
Assert.That(partition.At(new int2(4, 4)), Does.Contain(b), "b is not present at the new location in the extreme corner of the partition");
Assert.That(partition.At(new int2(1, 1)), Does.Not.Contain(b), "b is still present at the old location after update");
partition.Remove(a);
Assert.That(partition.At(new int2(0, 0)), Does.Not.Contain(b), "a is still present after removal");
Assert.That(partition.At(new int2(4, 4)), Does.Contain(b), "b wrongly changed location when a was removed");
}
[TestCase(TestName = "SpatiallyPartitioned.InBox works")]
public void SpatiallyPartitionedInBoxTest()
{
var partition = new SpatiallyPartitioned<object>(5, 5, 2);
var a = new object();
partition.Add(a, new Rectangle(0, 0, 1, 1));
Assert.That(partition.InBox(new Rectangle(0, 0, 0, 0)), Does.Not.Contain(a), "Searching an empty area should not return a");
Assert.That(partition.InBox(new Rectangle(0, 0, 1, 1)), Does.Contain(a), "a is not present after add");
Assert.That(partition.InBox(new Rectangle(0, 1, 1, 1)), Does.Not.Contain(a), "a is present in the wrong location");
Assert.That(partition.InBox(new Rectangle(1, 0, 1, 1)), Does.Not.Contain(a), "a is present in the wrong location");
var b = new object();
partition.Add(b, new Rectangle(1, 1, 2, 2));
Assert.That(partition.InBox(new Rectangle(0, 1, 1, 1)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.InBox(new Rectangle(1, 0, 1, 1)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.InBox(new Rectangle(1, 1, 1, 1)), Does.Contain(b), "b is not present after add");
Assert.That(partition.InBox(new Rectangle(2, 2, 1, 1)), Does.Contain(b), "b is not present after add");
Assert.That(partition.InBox(new Rectangle(2, 3, 1, 1)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.InBox(new Rectangle(3, 2, 1, 1)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(partition.InBox(new Rectangle(3, 3, 1, 1)), Does.Not.Contain(b), "b is present in the wrong location");
Assert.That(new[] { b }, Is.EquivalentTo(partition.InBox(new Rectangle(1, 1, 1, 1))),
"Searching within a single partition bin did not return the correct result");
Assert.That(partition.InBox(new Rectangle(0, 0, 5, 5)), Is.Unique,
"Searching the whole partition returned duplicates of some items");
Assert.That(new[] { a, b }, Is.EquivalentTo(partition.InBox(new Rectangle(0, 0, 5, 5))),
"Searching the whole partition did not return all items");
Assert.That(new[] { a, b }, Is.EquivalentTo(partition.InBox(new Rectangle(-10, -10, 25, 25))),
"Searching an area larger than the partition did not return all items");
partition[b] = new Rectangle(4, 4, 1, 1);
Assert.That(partition.InBox(new Rectangle(0, 0, 1, 1)), Does.Contain(a), "a wrongly changed location when b was updated");
Assert.That(partition.InBox(new Rectangle(4, 4, 1, 1)), Does.Contain(b), "b is not present at the new location in the extreme corner of the partition");
Assert.That(partition.InBox(new Rectangle(1, 1, 1, 1)), Does.Not.Contain(b), "b is still present at the old location after update");
partition.Remove(a);
Assert.That(partition.InBox(new Rectangle(0, 0, 1, 1)), Does.Not.Contain(a), "a is still present after removal");
Assert.That(partition.InBox(new Rectangle(4, 4, 1, 1)), Does.Contain(b), "b wrongly changed location when a was removed");
}
}
}

View File

@@ -0,0 +1,73 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace OpenRA.Test
{
[TestFixture]
sealed class StreamExtsTests
{
[TestCase(TestName = "ReadAllLines is equivalent to ReadAllLinesAsMemory")]
public void ReadAllLines()
{
foreach (var source in new[]
{
"abc",
"abc\n",
"abc\r\n",
"abc\ndef",
"abc\r\ndef",
"abc\n\n\n",
"abc\r\n\r\n\r\n",
"abc\n\n\ndef",
"abc\r\n\r\n\r\ndef",
"abc\ndef\nghi\n",
"abc\r\ndef\r\nghi\r\n",
"abc\ndef\nghi\njkl",
"abc\r\ndef\r\nghi\r\njkl",
new string('a', 126),
new string('a', 126) + '\n',
new string('a', 126) + "\r\n",
new string('a', 126) + "b",
new string('a', 126) + "\nb",
new string('a', 126) + "\r\nb",
new string('a', 127),
new string('a', 127) + '\n',
new string('a', 127) + "\r\n",
new string('a', 127) + "b",
new string('a', 127) + "\nb",
new string('a', 127) + "\r\nb",
new string('a', 128),
new string('a', 128) + '\n',
new string('a', 128) + "\r\n",
new string('a', 128) + "b",
new string('a', 128) + "\nb",
new string('a', 128) + "\r\nb",
new string('a', 129),
new string('a', 129) + '\n',
new string('a', 129) + "\r\n",
new string('a', 129) + "b",
new string('a', 129) + "\nb",
new string('a', 129) + "\r\nb",
})
{
var bytes = Encoding.UTF8.GetBytes(source);
var lines = new MemoryStream(bytes).ReadAllLines().ToArray();
var linesAsMemory = new MemoryStream(bytes).ReadAllLinesAsMemory().Select(l => l.ToString()).ToArray();
Assert.That(linesAsMemory, Is.EquivalentTo(lines));
}
}
}
}

View File

@@ -0,0 +1,464 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using OpenRA.Support;
namespace OpenRA.Test
{
[TestFixture]
sealed class VariableExpressionTest
{
readonly IReadOnlyDictionary<string, int> testValues = new Dictionary<string, int>
{
{ "t", 5 },
{ "t-1", 7 },
{ "one", 1 },
{ "five", 5 }
};
void AssertFalse(string expression)
{
Assert.That(new BooleanExpression(expression).Evaluate(testValues), Is.False, expression);
}
void AssertTrue(string expression)
{
Assert.That(new BooleanExpression(expression).Evaluate(testValues), Is.True, expression);
}
void AssertValue(string expression, int value)
{
Assert.That(value, Is.EqualTo(new IntegerExpression(expression).Evaluate(testValues)), expression);
}
void AssertParseFailure(string expression, string errorMessage)
{
var actualErrorMessage = Assert.Throws<InvalidDataException>(
() => new IntegerExpression(expression).Evaluate(testValues), expression).Message;
Assert.That(errorMessage, Is.EqualTo(actualErrorMessage), expression + " ===> " + actualErrorMessage);
}
[TestCase(TestName = "Numbers")]
public void TestNumbers()
{
AssertParseFailure("1a", "Number 1 and variable merged at index 0");
AssertValue("0", 0);
AssertValue("1", 1);
AssertValue("12", 12);
AssertValue("-1", -1);
AssertValue("-12", -12);
}
[TestCase(TestName = "Variables")]
public void TestVariables()
{
AssertValue("one", 1);
AssertValue("five", 5);
}
[TestCase(TestName = "Boolean Constants")]
public void TestBoolConsts()
{
AssertValue(" true", 1);
AssertValue(" true ", 1);
AssertValue("true", 1);
AssertValue("false", 0);
AssertValue("tru", 0);
AssertValue("fals", 0);
AssertValue("tr", 0);
AssertValue("fal", 0);
}
[TestCase(TestName = "Booleans")]
public void TestBooleans()
{
AssertValue("false", 0);
AssertValue("true", 1);
}
[TestCase(TestName = "AND operation")]
public void TestAnd()
{
AssertTrue("true && true");
AssertFalse("false && false");
AssertFalse("true && false");
AssertFalse("false && true");
AssertValue("2 && false", 0);
AssertValue("false && 2", 0);
AssertValue("3 && 2", 1);
AssertValue("2 && 3", 1);
}
[TestCase(TestName = "OR operation")]
public void TestOR()
{
AssertTrue("true || true");
AssertFalse("false || false");
AssertTrue("true || false");
AssertTrue("false || true");
AssertValue("2 || false", 1);
AssertValue("false || 2", 1);
AssertValue("3 || 2", 1);
AssertValue("2 || 3", 1);
}
[TestCase(TestName = "Equals operation")]
public void TestEquals()
{
AssertTrue("true == true");
AssertTrue("false == false");
AssertFalse("true == false");
AssertFalse("false == true");
AssertTrue("1 == 1");
AssertTrue("0 == 0");
AssertFalse("1 == 0");
AssertTrue("1 == true");
AssertFalse("1 == false");
AssertTrue("0 == false");
AssertFalse("0 == true");
AssertValue("12 == 12", 1);
AssertValue("1 == 12", 0);
}
[TestCase(TestName = "Not-equals operation")]
public void TestNotEquals()
{
AssertFalse("true != true");
AssertFalse("false != false");
AssertTrue("true != false");
AssertTrue("false != true");
AssertValue("1 != 2", 1);
AssertValue("1 != 1", 0);
AssertFalse("1 != true");
AssertFalse("0 != false");
AssertTrue("1 != false");
AssertTrue("0 != true");
}
[TestCase(TestName = "NOT operation")]
public void TestNOT()
{
AssertValue("!true", 0);
AssertValue("!false", 1);
AssertValue("!!true", 1);
AssertValue("!!false", 0);
AssertValue("!0", 1);
AssertValue("!1", 0);
AssertValue("!5", 0);
AssertValue("!!5", 1);
AssertValue("!-5", 0);
}
[TestCase(TestName = "Relation operations")]
public void TestRelations()
{
AssertValue("2 < 5", 1);
AssertValue("0 < 5", 1);
AssertValue("5 < 2", 0);
AssertValue("5 < 5", 0);
AssertValue("-5 < 0", 1);
AssertValue("-2 < -5", 0);
AssertValue("-5 < -2", 1);
AssertValue("-5 < -5", 0);
AssertValue("-7 < 5", 1);
AssertValue("0 <= 5", 1);
AssertValue("2 <= 5", 1);
AssertValue("5 <= 2", 0);
AssertValue("5 <= 5", 1);
AssertValue("5 <= 0", 0);
AssertValue("-2 <= -5", 0);
AssertValue("-5 <= -2", 1);
AssertValue("-5 <= -5", 1);
AssertValue("-7 <= 5", 1);
AssertValue("0 <= -5", 0);
AssertValue("-5 <= 0", 1);
AssertValue("5 > 2", 1);
AssertValue("0 > 5", 0);
AssertValue("2 > 5", 0);
AssertValue("5 > 5", 0);
AssertValue("5 > 0", 1);
AssertValue("-2 > -5", 1);
AssertValue("-7 > -5", 0);
AssertValue("-5 > -5", 0);
AssertValue("-4 > -5", 1);
AssertValue("5 >= 0", 1);
AssertValue("0 >= 5", 0);
AssertValue("5 >= 2", 1);
AssertValue("2 >= 5", 0);
AssertValue("5 >= 5", 1);
AssertValue("-5 >= 0", 0);
AssertValue("0 >= -5", 1);
AssertValue("-7 >= 5", 0);
AssertValue("-5 >= -5", 1);
AssertValue("-4 >= -5", 1);
}
[TestCase(TestName = "Relation Mixed Precedence")]
public void TestRelationMixedPrecedence()
{
AssertValue("5 <= 5 && 2 > 1", 1);
AssertValue("5 > 5 || 2 > 1", 1);
AssertValue("5 > 5 || 1 > 1", 0);
AssertValue("5 <= 5 == 2 > 1", 1);
AssertValue("5 > 5 == 2 > 1", 0);
AssertValue("5 > 5 == 1 > 1", 1);
AssertValue("5 <= 5 != 2 > 1", 0);
AssertValue("5 > 5 != 2 > 1", 1);
AssertValue("5 > 5 != 1 > 1", 0);
AssertValue("5 > 5 != 1 >= 1", 1);
}
[TestCase(TestName = "AND-OR Precedence")]
public void TestAndOrPrecedence()
{
AssertTrue("true && false || true");
AssertFalse("false || false && true");
AssertTrue("true && !true || !false");
AssertFalse("false || !true && !false");
}
[TestCase(TestName = "Parenthesis")]
public void TestParens()
{
AssertTrue("(true)");
AssertTrue("((true))");
AssertFalse("(false)");
AssertFalse("((false))");
}
[TestCase(TestName = "Arithmetic")]
public void TestArithmetic()
{
AssertValue("~0", ~0);
AssertValue("-0", 0);
AssertValue("-a", 0);
AssertValue("-true", -1);
AssertValue("~-0", -1);
AssertValue("2 + 3", 5);
AssertValue("2 + 0", 2);
AssertValue("2 + 3", 5);
AssertValue("5 - 3", 2);
AssertValue("5 - -3", 8);
AssertValue("5 - 0", 5);
AssertValue("2 * 3", 6);
AssertValue("2 * 0", 0);
AssertValue("2 * -3", -6);
AssertValue("-2 * 3", -6);
AssertValue("-2 * -3", 6);
AssertValue("6 / 3", 2);
AssertValue("7 / 3", 2);
AssertValue("-6 / 3", -2);
AssertValue("6 / -3", -2);
AssertValue("-6 / -3", 2);
AssertValue("8 / 3", 2);
AssertValue("6 % 3", 0);
AssertValue("7 % 3", 1);
AssertValue("8 % 3", 2);
AssertValue("7 % 0", 0);
AssertValue("-7 % 3", -1);
AssertValue("7 % -3", 1);
AssertValue("-7 % -3", -1);
AssertValue("8 / 0", 0);
}
[TestCase(TestName = "Arithmetic Mixed")]
public void TestArithmeticMixed()
{
AssertValue("~~0", 0);
AssertValue("-~0", 1);
AssertValue("~- 0", -1);
AssertValue("2 * 3 + 4", 10);
AssertValue("2 * 3 - 4", 2);
AssertValue("2 + 3 * 4", 14);
AssertValue("2 + 3 % 4", 5);
AssertValue("2 + 3 / 4", 2);
AssertValue("2 * 3 / 4", 1);
AssertValue("8 / 2 == 4", 1);
AssertValue("~2 + ~3", -7);
AssertValue("~(~2 + ~3)", 6);
}
[TestCase(TestName = "Hyphen")]
public void TestHyphen()
{
AssertValue("t-1", 7);
AssertValue("-t-1", -7);
AssertValue("t - 1", 4);
AssertValue("-1", -1);
}
[TestCase(TestName = "Parenthesis and mixed operations")]
public void TestMixedParens()
{
AssertTrue("(!false)");
AssertTrue("!(false)");
AssertFalse("!(!false)");
AssertTrue("(true) || (false)");
AssertTrue("true && (false || true)");
AssertTrue("(true && false) || true");
AssertTrue("!(true && false) || false");
AssertTrue("((true != true) == false) && true");
AssertFalse("(true != false) == false && true");
AssertTrue("true || ((true != false) != !(false && true))");
AssertFalse("((true != false) != !(false && true))");
}
[TestCase(TestName = "Test parser errors")]
public void TestParseErrors()
{
AssertParseFailure("()", "Empty parenthesis at index 0");
AssertParseFailure("! && true", "Missing value or sub-expression or there is an extra operator `!` at index 0 or `&&` at index 2");
AssertParseFailure("(true", "Unclosed opening parenthesis at index 0");
AssertParseFailure(")true", "Unmatched closing parenthesis at index 0");
AssertParseFailure("false)", "Unmatched closing parenthesis at index 5");
AssertParseFailure("false(", "Missing binary operation before `(` at index 5");
AssertParseFailure("(", "Missing value or sub-expression at end for `(` operator");
AssertParseFailure(")", "Unmatched closing parenthesis at index 0");
AssertParseFailure("false!", "Missing binary operation before `!` at index 5");
AssertParseFailure("true false", "Missing binary operation before `false` at index 5");
AssertParseFailure("true & false", "Unexpected character '&' at index 5 - should it be `&&`?");
AssertParseFailure("true | false", "Unexpected character '|' at index 5 - should it be `||`?");
AssertParseFailure("true : false", "Invalid character ':' at index 5");
AssertParseFailure("true & false && !", "Unexpected character '&' at index 5 - should it be `&&`?");
AssertParseFailure("(true && !)", "Missing value or sub-expression or there is an extra operator `!` at index 9 or `)` at index 10");
AssertParseFailure("&& false", "Missing value or sub-expression at beginning for `&&` operator");
AssertParseFailure("false ||", "Missing value or sub-expression at end for `||` operator");
AssertParseFailure("1 <", "Missing value or sub-expression at end for `<` operator");
AssertParseFailure("-1a", "Number -1 and variable merged at index 0");
}
[TestCase(TestName = "Test hyphen parser errors")]
public void TestParseHyphenErrors()
{
AssertParseFailure("-", "Missing value or sub-expression at end for `-` operator");
AssertParseFailure("-1-1", "Missing binary operation before `-1` at index 2");
AssertParseFailure("5-1", "Missing binary operation before `-1` at index 1");
AssertParseFailure("6 -1", "Missing binary operation before `-1` at index 2");
AssertParseFailure("t -1", "Missing binary operation before `-1` at index 2");
}
[TestCase(TestName = "Test mixed characters at end of identifier parser errors")]
public void TestParseMixedEndErrors()
{
AssertParseFailure("t- 1", "Invalid identifier end character at index 1 for `t-`");
AssertParseFailure("t-", "Invalid identifier end character at index 1 for `t-`");
AssertParseFailure("t. 1", "Invalid identifier end character at index 1 for `t.`");
AssertParseFailure("t.", "Invalid identifier end character at index 1 for `t.`");
AssertParseFailure("t@ 1", "Invalid identifier end character at index 1 for `t@`");
AssertParseFailure("t@", "Invalid identifier end character at index 1 for `t@`");
AssertParseFailure("t$ 1", "Invalid identifier end character at index 1 for `t$`");
AssertParseFailure("t$", "Invalid identifier end character at index 1 for `t$`");
}
[TestCase(TestName = "Test binary operator whitespace parser errors")]
public void TestParseSpacedBinaryOperatorErrors()
{
// `t-1` is valid variable name and `t- 1` starts with an invalid variable name.
// `6 -1`, `6-1`, `t -1` contain `-1` and are missing a binary operator.
AssertParseFailure("6- 1", "Missing whitespace at index 2, before `-` operator.");
AssertParseFailure("6+ 1", "Missing whitespace at index 2, before `+` operator.");
AssertParseFailure("t+ 1", "Missing whitespace at index 2, before `+` operator.");
AssertParseFailure("6 +1", "Missing whitespace at index 3, after `+` operator.");
AssertParseFailure("t +1", "Missing whitespace at index 3, after `+` operator.");
AssertParseFailure("6+1", "Missing whitespace at index 2, before `+` operator.");
AssertParseFailure("t+1", "Missing whitespace at index 2, before `+` operator.");
AssertParseFailure("6* 1", "Missing whitespace at index 2, before `*` operator.");
AssertParseFailure("t* 1", "Missing whitespace at index 2, before `*` operator.");
AssertParseFailure("6 *1", "Missing whitespace at index 3, after `*` operator.");
AssertParseFailure("t *1", "Missing whitespace at index 3, after `*` operator.");
AssertParseFailure("6*1", "Missing whitespace at index 2, before `*` operator.");
AssertParseFailure("t*1", "Missing whitespace at index 2, before `*` operator.");
AssertParseFailure("6/ 1", "Missing whitespace at index 2, before `/` operator.");
AssertParseFailure("t/ 1", "Missing whitespace at index 2, before `/` operator.");
AssertParseFailure("6 /1", "Missing whitespace at index 3, after `/` operator.");
AssertParseFailure("t /1", "Missing whitespace at index 3, after `/` operator.");
AssertParseFailure("6/1", "Missing whitespace at index 2, before `/` operator.");
AssertParseFailure("t/1", "Missing whitespace at index 2, before `/` operator.");
AssertParseFailure("6% 1", "Missing whitespace at index 2, before `%` operator.");
AssertParseFailure("t% 1", "Missing whitespace at index 2, before `%` operator.");
AssertParseFailure("6 %1", "Missing whitespace at index 3, after `%` operator.");
AssertParseFailure("t %1", "Missing whitespace at index 3, after `%` operator.");
AssertParseFailure("6%1", "Missing whitespace at index 2, before `%` operator.");
AssertParseFailure("t%1", "Missing whitespace at index 2, before `%` operator.");
AssertParseFailure("6< 1", "Missing whitespace at index 2, before `<` operator.");
AssertParseFailure("t< 1", "Missing whitespace at index 2, before `<` operator.");
AssertParseFailure("6 <1", "Missing whitespace at index 3, after `<` operator.");
AssertParseFailure("t <1", "Missing whitespace at index 3, after `<` operator.");
AssertParseFailure("6<1", "Missing whitespace at index 2, before `<` operator.");
AssertParseFailure("t<1", "Missing whitespace at index 2, before `<` operator.");
AssertParseFailure("6> 1", "Missing whitespace at index 2, before `>` operator.");
AssertParseFailure("t> 1", "Missing whitespace at index 2, before `>` operator.");
AssertParseFailure("6 >1", "Missing whitespace at index 3, after `>` operator.");
AssertParseFailure("t >1", "Missing whitespace at index 3, after `>` operator.");
AssertParseFailure("6>1", "Missing whitespace at index 2, before `>` operator.");
AssertParseFailure("t>1", "Missing whitespace at index 2, before `>` operator.");
AssertParseFailure("6&& 1", "Missing whitespace at index 3, before `&&` operator.");
AssertParseFailure("t&& 1", "Missing whitespace at index 3, before `&&` operator.");
AssertParseFailure("6 &&1", "Missing whitespace at index 4, after `&&` operator.");
AssertParseFailure("t &&1", "Missing whitespace at index 4, after `&&` operator.");
AssertParseFailure("6&&1", "Missing whitespace at index 3, before `&&` operator.");
AssertParseFailure("t&&1", "Missing whitespace at index 3, before `&&` operator.");
AssertParseFailure("6|| 1", "Missing whitespace at index 3, before `||` operator.");
AssertParseFailure("t|| 1", "Missing whitespace at index 3, before `||` operator.");
AssertParseFailure("6 ||1", "Missing whitespace at index 4, after `||` operator.");
AssertParseFailure("t ||1", "Missing whitespace at index 4, after `||` operator.");
AssertParseFailure("6||1", "Missing whitespace at index 3, before `||` operator.");
AssertParseFailure("t||1", "Missing whitespace at index 3, before `||` operator.");
AssertParseFailure("6== 1", "Missing whitespace at index 3, before `==` operator.");
AssertParseFailure("t== 1", "Missing whitespace at index 3, before `==` operator.");
AssertParseFailure("6 ==1", "Missing whitespace at index 4, after `==` operator.");
AssertParseFailure("t ==1", "Missing whitespace at index 4, after `==` operator.");
AssertParseFailure("6==1", "Missing whitespace at index 3, before `==` operator.");
AssertParseFailure("t==1", "Missing whitespace at index 3, before `==` operator.");
AssertParseFailure("6!= 1", "Missing whitespace at index 3, before `!=` operator.");
AssertParseFailure("t!= 1", "Missing whitespace at index 3, before `!=` operator.");
AssertParseFailure("6 !=1", "Missing whitespace at index 4, after `!=` operator.");
AssertParseFailure("t !=1", "Missing whitespace at index 4, after `!=` operator.");
AssertParseFailure("6!=1", "Missing whitespace at index 3, before `!=` operator.");
AssertParseFailure("t!=1", "Missing whitespace at index 3, before `!=` operator.");
AssertParseFailure("6<= 1", "Missing whitespace at index 3, before `<=` operator.");
AssertParseFailure("t<= 1", "Missing whitespace at index 3, before `<=` operator.");
AssertParseFailure("6 <=1", "Missing whitespace at index 4, after `<=` operator.");
AssertParseFailure("t <=1", "Missing whitespace at index 4, after `<=` operator.");
AssertParseFailure("6<=1", "Missing whitespace at index 3, before `<=` operator.");
AssertParseFailure("t<=1", "Missing whitespace at index 3, before `<=` operator.");
AssertParseFailure("6>= 1", "Missing whitespace at index 3, before `>=` operator.");
AssertParseFailure("t>= 1", "Missing whitespace at index 3, before `>=` operator.");
AssertParseFailure("6 >=1", "Missing whitespace at index 4, after `>=` operator.");
AssertParseFailure("t >=1", "Missing whitespace at index 4, after `>=` operator.");
AssertParseFailure("6>=1", "Missing whitespace at index 3, before `>=` operator.");
AssertParseFailure("t>=1", "Missing whitespace at index 3, before `>=` operator.");
}
[TestCase(TestName = "Undefined symbols are treated as `false` (0) values")]
public void TestUndefinedSymbols()
{
AssertFalse("undef1 || undef2");
AssertValue("undef1", 0);
AssertValue("undef1 + undef2", 0);
}
}
}