diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs index cce7eb280e..e721634d7c 100644 --- a/OpenRA.Game/GameRules/ActorInfo.cs +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -100,7 +100,7 @@ namespace OpenRA var unresolved = source.Except(resolved); var testResolve = new Func((a, b) => a == b || a.IsAssignableFrom(b)); - var more = unresolved.Where(u => u.Dependencies.All(d => resolved.Exists(r => testResolve(d, r.Type)))); + var more = unresolved.Where(u => u.Dependencies.All(d => resolved.Exists(r => testResolve(d, r.Type)) && !unresolved.Any(u1 => testResolve(d, u1.Type)))); // Re-evaluate the vars above until sorted while (more.Any()) @@ -122,14 +122,14 @@ namespace OpenRA exceptionString += u.Type + ": { " + string.Join(", ", deps) + " }\r\n"; } - throw new Exception(exceptionString); + throw new YamlException(exceptionString); } constructOrderCache = resolved.Select(r => r.Trait).ToList(); return constructOrderCache; } - static IEnumerable PrerequisitesOf(ITraitInfo info) + public static IEnumerable PrerequisitesOf(ITraitInfo info) { return info .GetType() diff --git a/OpenRA.Test/OpenRA.Game/ActorInfoTest.cs b/OpenRA.Test/OpenRA.Game/ActorInfoTest.cs index 5d09ad3f65..b3219869e3 100644 --- a/OpenRA.Test/OpenRA.Game/ActorInfoTest.cs +++ b/OpenRA.Test/OpenRA.Game/ActorInfoTest.cs @@ -10,7 +10,6 @@ #endregion using System; -using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using NUnit.Framework; @@ -34,20 +33,26 @@ namespace OpenRA.Test [TestCase(TestName = "Trait ordering sorts in dependency order correctly")] public void TraitOrderingSortsCorrectly() { - var actorInfo = new ActorInfo("test", new MockCInfo(), new MockBInfo(), new MockAInfo()); + var unorderedTraits = new ITraitInfo[] { new MockBInfo(), new MockCInfo(), new MockAInfo(), new MockBInfo() }; + var actorInfo = new ActorInfo("test", unorderedTraits); + var orderedTraits = actorInfo.TraitsInConstructOrder().ToArray(); - var i = new List(actorInfo.TraitsInConstructOrder()); + CollectionAssert.AreEquivalent(unorderedTraits, orderedTraits); - Assert.That(i[0], Is.InstanceOf()); - Assert.That(i[1].GetType().Name, Is.EqualTo("MockBInfo")); - Assert.That(i[2].GetType().Name, Is.EqualTo("MockCInfo")); + for (var i = 0; i < orderedTraits.Length; i++) + { + var traitTypesThatMustOccurBeforeThisTrait = ActorInfo.PrerequisitesOf(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 exception reports missing dependencies")] public void TraitOrderingReportsMissingDependencies() { var actorInfo = new ActorInfo("test", new MockBInfo(), new MockCInfo()); - var ex = Assert.Throws(() => actorInfo.TraitsInConstructOrder()); + var ex = Assert.Throws(() => 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."); @@ -60,7 +65,7 @@ namespace OpenRA.Test public void TraitOrderingReportsCyclicDependencies() { var actorInfo = new ActorInfo("test", new MockDInfo(), new MockEInfo(), new MockFInfo()); - var ex = Assert.Throws(() => actorInfo.TraitsInConstructOrder()); + var ex = Assert.Throws(() => actorInfo.TraitsInConstructOrder()); StringAssert.Contains(typeof(MockDInfo).Name, ex.Message, "Exception message should report all cyclic dependencies."); StringAssert.Contains(typeof(MockEInfo).Name, ex.Message, "Exception message should report all cyclic dependencies.");