using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.Input; using Robust.Shared.Input.Binding; using Robust.Shared.Players; namespace Robust.UnitTesting.Shared.Input.Binding { [TestFixture, TestOf(typeof(CommandBindRegistry))] public sealed class CommandBindRegistry_Test : RobustUnitTest { private sealed class TypeA { } private sealed class TypeB { } private sealed class TypeC { } private sealed class TestInputCmdHandler : InputCmdHandler { // these vars only for tracking / debugging during testing private readonly string name; public readonly Type? ForType; public TestInputCmdHandler(Type? forType = null, string name = "") { ForType = forType; this.name = name; } public override string ToString() { return name; } public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message) { return false; } } [TestCase(1,1)] [TestCase(10,10)] public void ResolvesHandlers_WhenNoDependencies(int handlersPerType, int numFunctions) { var registry = new CommandBindRegistry(new DummySawmill()); var allHandlers = new Dictionary>(); for (int i = 0; i < numFunctions; i++) { var bkf = new BoundKeyFunction(i.ToString()); var theseHandlers = new List(); allHandlers[bkf] = theseHandlers; var aHandlers = new List(); var bHandlers = new List(); var cHandlers = new List(); for (int j = 0; j < handlersPerType; j++) { aHandlers.Add(new TestInputCmdHandler(typeof(TypeA))); bHandlers.Add(new TestInputCmdHandler(typeof(TypeB))); cHandlers.Add(new TestInputCmdHandler(typeof(TypeC))); } theseHandlers.AddRange(aHandlers); theseHandlers.AddRange(bHandlers); theseHandlers.AddRange(cHandlers); CommandBinds.Builder .Bind(bkf, aHandlers) .Register(registry); CommandBinds.Builder .Bind(bkf, bHandlers) .Register(registry); CommandBinds.Builder .Bind(bkf, cHandlers) .Register(registry); } //order doesn't matter, just verify that all handlers are returned foreach (var bkfToExpectedHandlers in allHandlers) { var bkf = bkfToExpectedHandlers.Key; var expectedHandlers = bkfToExpectedHandlers.Value; HashSet returnedHandlers = registry.GetHandlers(bkf).ToHashSet(); CollectionAssert.AreEqual(returnedHandlers, expectedHandlers); } // type b stuff should no longer fire CommandBinds.Unregister(registry); foreach (var bkfToExpectedHandlers in allHandlers) { var bkf = bkfToExpectedHandlers.Key; var expectedHandlers = bkfToExpectedHandlers.Value; expectedHandlers.RemoveAll(handler => ((TestInputCmdHandler) handler).ForType == typeof(TypeB)); HashSet returnedHandlers = registry.GetHandlers(bkf).ToHashSet(); CollectionAssert.AreEqual(returnedHandlers, expectedHandlers); } } [TestCase(true, false)] [TestCase(false, true)] [TestCase(true, true)] public void ResolvesHandlers_WithDependency(bool before, bool after) { var registry = new CommandBindRegistry(new DummySawmill()); var bkf = new BoundKeyFunction("test"); var aHandler1 = new TestInputCmdHandler( ); var aHandler2 = new TestInputCmdHandler(); var bHandler1 = new TestInputCmdHandler(); var bHandler2 = new TestInputCmdHandler(); var cHandler1 = new TestInputCmdHandler(); var cHandler2 = new TestInputCmdHandler(); // a handler 2 should run after both b and c handlers for all the below cases if (before && after) { CommandBinds.Builder .Bind(bkf, aHandler1) .BindAfter(bkf, aHandler2, typeof(TypeB), typeof(TypeC)) .Register(registry); CommandBinds.Builder .BindBefore(bkf, bHandler1, typeof(TypeA)) .BindBefore(bkf, bHandler2, typeof(TypeA)) .Register(registry); CommandBinds.Builder .BindBefore(bkf, cHandler1, typeof(TypeA)) .BindBefore(bkf, cHandler2, typeof(TypeA)) .Register(registry); } else if (before) { CommandBinds.Builder .Bind(bkf, aHandler1) .Bind(bkf, aHandler2) .Register(registry); CommandBinds.Builder .BindBefore(bkf, bHandler1, typeof(TypeA)) .BindBefore(bkf, bHandler2, typeof(TypeA)) .Register(registry); CommandBinds.Builder .BindBefore(bkf, cHandler1, typeof(TypeA)) .BindBefore(bkf, cHandler2, typeof(TypeA)) .Register(registry); } else if (after) { CommandBinds.Builder .Bind(bkf, aHandler1) .BindAfter(bkf, aHandler2, typeof(TypeB), typeof(TypeC)) .Register(registry); CommandBinds.Builder .Bind(bkf, bHandler1) .Bind(bkf, bHandler2) .Register(registry); CommandBinds.Builder .Bind(bkf, cHandler1) .Bind(bkf, cHandler2) .Register(registry); } var returnedHandlers = registry.GetHandlers(bkf); // b1 , b2, c1, c2 should be fired before a2 bool foundB1 = false, foundB2 = false, foundC1 = false, foundC2 = false; foreach (var returnedHandler in returnedHandlers) { if (returnedHandler == bHandler1) { foundB1 = true; } else if (returnedHandler == bHandler2) { foundB2 = true; } else if (returnedHandler == cHandler1) { foundC1= true; } else if (returnedHandler == cHandler2) { foundC2 = true; } else if (returnedHandler == aHandler2) { Assert.True(foundB1 && foundB2 && foundC1 && foundC2, "bind registry didn't respect" + " handler dependency order"); } } var expectedHandlers = new []{aHandler1, aHandler2, bHandler1, bHandler2, cHandler1, cHandler2}; var returnedHandlerSet = new HashSet(returnedHandlers); foreach (var expectedHandler in expectedHandlers) { Assert.True(returnedHandlerSet.Contains(expectedHandler)); } } [Test] public void ThrowsError_WhenCircularDependency() { var registry = new CommandBindRegistry(new DummySawmill()); var bkf = new BoundKeyFunction("test"); var aHandler1 = new TestInputCmdHandler(); var aHandler2 = new TestInputCmdHandler(); var bHandler1 = new TestInputCmdHandler(); var bHandler2 = new TestInputCmdHandler(); var cHandler1 = new TestInputCmdHandler(); var cHandler2 = new TestInputCmdHandler(); CommandBinds.Builder .Bind(bkf, aHandler1) .BindAfter(bkf, aHandler2, typeof(TypeB), typeof(TypeC)) .Register(registry); CommandBinds.Builder .Bind(bkf, bHandler1) .Bind(bkf, bHandler2) .Register(registry); CommandBinds.Builder .Bind(bkf, cHandler1) .BindAfter(bkf, cHandler2, typeof(TypeA)) .Register(registry); Assert.Throws(registry.RebuildGraph); } } }