using System; using System.Collections.Generic; using NUnit.Framework; namespace Chernobyl.StateMachine { /// /// Tests for types that implement the interface. /// public abstract class StateTests : EnumerationTests { public StateTests() : base(true) {} [Test, Description("A test to ensure that IState.Entered is raised and " + "IState.Left is not raised when the IState.ParentState is set")] public void EnteredRaisedWhenParentSet() { IState state = CreateState(); EventChecker enteredEventChecker = new EventChecker(); EventChecker leftEventChecker = new EventChecker(); state.Entered += enteredEventChecker.EventHandler; state.Left += leftEventChecker.EventHandler; state.ParentState = new State(); Assert.True(enteredEventChecker.WasEventInvoked, "IState.Entered should have been invoked but it was not."); Assert.False(leftEventChecker.WasEventInvoked, "IState.Left should not have been invoked but it was."); } [Test, Description("A test to ensure that IState.Entered is not raised " + "and IState.Left is not raised when the IState.ParentState is set")] public void EnteredNotRaisedWhenParentIsSetTwiceWithSameValue() { IState state = CreateState(); IState parent = new State(); state.ParentState = parent; EventChecker enteredEventChecker = new EventChecker(); EventChecker leftEventChecker = new EventChecker(); state.Entered += enteredEventChecker.EventHandler; state.Left += leftEventChecker.EventHandler; state.ParentState = parent; Assert.False(enteredEventChecker.WasEventInvoked, "IState.Entered should not have been invoked but it was."); Assert.False(leftEventChecker.WasEventInvoked, "IState.Left should not have been invoked but it was."); } // Left is raised when ParentState is set to null. // Left is not raised when ParentState is set to null twice. [Test, Description("A test to ensure that IState.Entered is raised and " + "IState.Left is not raised when the IState.ParentState is set")] public void LeftRaisedWhenParentSetToNull() { IState state = CreateState(); IState parent = new State(); state.ParentState = parent; EventChecker enteredEventChecker = new EventChecker(); EventChecker leftEventChecker = new EventChecker(); state.Entered += enteredEventChecker.EventHandler; state.Left += leftEventChecker.EventHandler; state.ParentState = null; Assert.False(enteredEventChecker.WasEventInvoked, "IState.Entered should have been invoked but it was not."); Assert.True(leftEventChecker.WasEventInvoked, "IState.Left should not have been invoked but it was."); } [Test, Description("A test to ensure that IState.Entered is not raised " + "and IState.Left is not raised when the IState.ParentState is set")] public void LeftNotRaisedWhenParentIsSetTwiceToNullTwice() { IState state = CreateState(); IState parent = new State(); state.ParentState = parent; state.ParentState = null; EventChecker enteredEventChecker = new EventChecker(); EventChecker leftEventChecker = new EventChecker(); state.Entered += enteredEventChecker.EventHandler; state.Left += leftEventChecker.EventHandler; state.ParentState = null; Assert.False(enteredEventChecker.WasEventInvoked, "IState.Entered should not have been invoked but it was."); Assert.False(leftEventChecker.WasEventInvoked, "IState.Left should not have been invoked but it was."); } [Test, Description("A test to ensure that IState.ParentState is set when " + "IState.ChildState is set.")] public void ParentSetWhenChildSet() { IState parent = CreateState(); IState child = CreateState(); parent.ChildState = child; Assert.AreEqual(parent, child.ParentState, "IState.ParentState was not set to the expected value."); } [Test, Description("A test to ensure that IState.ChildState is set when " + "IState.ParentState is set.")] public void ChildSetWhenParentSet() { IState parent = CreateState(); IState child = CreateState(); child.ParentState = parent; Assert.AreEqual(child, parent.ChildState, "IState.ParentState was not set to the expected value."); } [Test, Description("A test to ensure that IState.ParentState is set to " + "null when IState.ChildState is set to null.")] public void ParentUnsetWhenChildSetToNull() { IState parent = CreateState(); IState child = CreateState(); parent.ChildState = child; parent.ChildState = null; Assert.AreEqual(null, child.ParentState, "IState.ParentState was not set to the expected value."); } [Test, Description("A test to ensure that IState.ChildState is set to " + "null when IState.ParentState is set to null.")] public void ChildUnsetWhenParentSetToNull() { IState parent = CreateState(); IState child = CreateState(); child.ParentState = parent; child.ParentState = null; Assert.AreEqual(null, parent.ChildState, "IState.ParentState was not set to the expected value."); } [Test, Description("A test to ensure that IState.ParentState is set to " + "null when IState.ChildState is set to another IState.")] public void ParentUnsetWhenChildSetToAnotherState() { IState parent = CreateState(); IState child = CreateState(); IState anotherChild = CreateState(); parent.ChildState = child; parent.ChildState = anotherChild; Assert.AreEqual(null, child.ParentState, "IState.ParentState was not set to the expected value."); } [Test, Description("A test to ensure that IState.ChildState is set to " + "null when IState.ParentState is set to another IState.")] public void ChildUnsetWhenParentSetToAnotherState() { IState parent = CreateState(); IState anotherParent = CreateState(); IState child = CreateState(); child.ParentState = parent; child.ParentState = anotherParent; Assert.AreEqual(null, parent.ChildState, "IState.ParentState was not set to the expected value."); } [Test, Description("A test to ensure that deactivating (i.e. leaving) an" + "IState causes all of its descendant IStates to be deactivated and " + "in the correct order.")] public void DeactivatingStateDeactivatesDescendantStates() { // IState machine should look like this: // state1 -> state2 -> state3 -> state4 IState state4 = CreateState(); IState state3 = state4.ParentState = CreateState(); IState state2 = state3.ParentState = CreateState(); IState state1 = state2.ParentState = CreateState(); // We need to ensure the order of deactivation was correct. The order // of deactivation should be: state3, state2, state1. List deactivationOrder = new List(3); state2.Left += (sender, e) => deactivationOrder.Add((IState)sender); state3.Left += (sender, e) => deactivationOrder.Add((IState)sender); state4.Left += (sender, e) => deactivationOrder.Add((IState)sender); // Deactivate state2 and the sub-states below it. state2.ParentState = null; // Ensure all the states are deactivated. Assert.AreEqual(null, state2.ParentState, "Second sub-state was not deactivated."); Assert.AreEqual(null, state3.ParentState, "Third sub-state was not deactivated."); Assert.AreEqual(null, state4.ParentState, "Fourth sub-state was not deactivated."); // Check the order of deactivation. Assert.AreEqual(state4, deactivationOrder[0], "The third sub-state was not deactivated first; it should have been."); Assert.AreEqual(state3, deactivationOrder[1], "The second sub-state was not deactivated second; it should have been."); Assert.AreEqual(state2, deactivationOrder[2], "The first sub-state was not deactivated third; it should have been."); } /// /// A helper class that is used to check whether an event was invoked or /// not. /// public class EventChecker { /// /// An event handler that can be assigned to an event. When this /// method is raised, will be set to /// true. /// /// The instance that generated the event. /// The instance /// containing the event data. public void EventHandler(object sender, EventArgs e) { WasEventInvoked = true; } /// /// True if was /// invoked, false if otherwise. /// public bool WasEventInvoked { get; set; } } /// /// Creates the instance that is to be tested. This /// method should always create a new and never /// return the same instance twice. /// /// The instance to be tested. protected abstract IState CreateState(); protected override IEnumerable CreateSingleItemEnumerable() { return CreateState(); } protected override IEnumerable CreateManyItemEnumerable() { IState item6 = new State(); IState item5 = new State { ChildState = item6 }; IState item4 = new State { ChildState = item5 }; IState item3 = new State { ChildState = item4 }; IState item2 = new State { ChildState = item3 }; IState item1 = new State { ChildState = item2 }; IState item0 = CreateState(); item0.ChildState = item1; _orderedItems = new[] { item0, item1, item2, item3, item4, item5, item6 }; return item0; } protected override IEnumerable OrderedItems { get { return _orderedItems; } } /// /// The backing field to . /// IEnumerable _orderedItems; } }