After NotBefore<> support to control initialization order.
Requires<T> means that trait of type T will be initialized first, and asserts that at least one exists. The new NotBefore<T> means that trait of type T will be initialized first, but allows no traits. This allows traits to control initialization order for optional dependencies. They want to be initialized second so they can rely on the dependencies having been initialized. But if the dependencies are optional then to not throw if none are present. We apply this to Locomotor which was previously using AddFrameEndTask to work around trait order initialization. This improves the user experience as the initialization is applied whilst the loading screen is still visible, rather than the game starting and creating jank by performing initialization on the first tick.
This commit is contained in:
committed by
Paul Chote
parent
62e7c7a318
commit
2583a7af31
@@ -18,13 +18,23 @@ namespace OpenRA.Test
|
||||
interface IMock : ITraitInfoInterface { }
|
||||
class MockTraitInfo : TraitInfo { public override object Create(ActorInitializer init) { return null; } }
|
||||
class MockInheritInfo : MockTraitInfo { }
|
||||
|
||||
class MockAInfo : MockInheritInfo, IMock { }
|
||||
class MockBInfo : MockTraitInfo, Requires<MockAInfo>, Requires<IMock>, Requires<MockInheritInfo> { }
|
||||
class MockBInfo : MockTraitInfo, Requires<IMock>, Requires<MockInheritInfo> { }
|
||||
class MockCInfo : MockTraitInfo, Requires<MockBInfo> { }
|
||||
|
||||
class MockDInfo : MockTraitInfo, Requires<MockEInfo> { }
|
||||
class MockEInfo : MockTraitInfo, Requires<MockFInfo> { }
|
||||
class MockFInfo : MockTraitInfo, Requires<MockDInfo> { }
|
||||
|
||||
class MockGInfo : MockInheritInfo, IMock, NotBefore<MockAInfo> { }
|
||||
class MockHInfo : MockTraitInfo, NotBefore<IMock>, NotBefore<MockInheritInfo>, NotBefore<MockBInfo> { }
|
||||
class MockIInfo : MockTraitInfo, NotBefore<MockHInfo>, NotBefore<MockCInfo> { }
|
||||
|
||||
class MockJInfo : MockTraitInfo, NotBefore<MockKInfo> { }
|
||||
class MockKInfo : MockTraitInfo, NotBefore<MockLInfo> { }
|
||||
class MockLInfo : MockTraitInfo, NotBefore<MockJInfo> { }
|
||||
|
||||
[TestFixture]
|
||||
public class ActorInfoTest
|
||||
{
|
||||
@@ -39,7 +49,27 @@ namespace OpenRA.Test
|
||||
|
||||
for (var i = 0; i < orderedTraits.Length; i++)
|
||||
{
|
||||
var traitTypesThatMustOccurBeforeThisTrait = ActorInfo.PrerequisitesOf(orderedTraits[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);
|
||||
CollectionAssert.IsEmpty(traitTypesThatShouldOccurEarlier, "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();
|
||||
|
||||
CollectionAssert.AreEquivalent(unorderedTraits, 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);
|
||||
CollectionAssert.IsEmpty(traitTypesThatShouldOccurEarlier, "Dependency order has not been satisfied.");
|
||||
@@ -52,13 +82,22 @@ namespace OpenRA.Test
|
||||
var actorInfo = new ActorInfo("test", new MockBInfo(), new MockCInfo());
|
||||
var ex = Assert.Throws<YamlException>(() => actorInfo.TraitsInConstructOrder());
|
||||
|
||||
StringAssert.Contains(typeof(MockAInfo).Name, ex.Message, "Exception message did not report a missing dependency.");
|
||||
StringAssert.Contains(typeof(MockBInfo).Name, ex.Message, "Exception message did not report a missing dependency.");
|
||||
StringAssert.Contains(typeof(MockCInfo).Name, ex.Message, "Exception message did not report a missing dependency.");
|
||||
StringAssert.Contains(typeof(MockInheritInfo).Name, ex.Message, "Exception message did not report a missing dependency (from a base class).");
|
||||
StringAssert.Contains(typeof(IMock).Name, ex.Message, "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();
|
||||
|
||||
CollectionAssert.AreEquivalent(unorderedTraits, orderedTraits);
|
||||
}
|
||||
|
||||
[TestCase(TestName = "Trait ordering exception reports cyclic dependencies")]
|
||||
public void TraitOrderingReportsCyclicDependencies()
|
||||
{
|
||||
@@ -69,5 +108,16 @@ namespace OpenRA.Test
|
||||
StringAssert.Contains(typeof(MockEInfo).Name, ex.Message, "Exception message should report all cyclic dependencies.");
|
||||
StringAssert.Contains(typeof(MockFInfo).Name, ex.Message, "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());
|
||||
|
||||
StringAssert.Contains(typeof(MockJInfo).Name, ex.Message, "Exception message should report all cyclic dependencies.");
|
||||
StringAssert.Contains(typeof(MockKInfo).Name, ex.Message, "Exception message should report all cyclic dependencies.");
|
||||
StringAssert.Contains(typeof(MockLInfo).Name, ex.Message, "Exception message should report all cyclic dependencies.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user