diff --git a/CHANGELOG.md b/CHANGELOG.md index c455722..612e496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- OnStateChanged method + ## [0.1.0] - 2025-04-23 ### Added diff --git a/TJC.StateMachine.Tests/Mocks/RevolverMock.cs b/TJC.StateMachine.Tests/Mocks/RevolverMock.cs index dcc6256..af389b0 100644 --- a/TJC.StateMachine.Tests/Mocks/RevolverMock.cs +++ b/TJC.StateMachine.Tests/Mocks/RevolverMock.cs @@ -1,10 +1,11 @@ namespace TJC.StateMachine.Tests.Mocks { - internal class RevolverMock() - : StateMachineBase(RevolverStates.Loaded) + internal class RevolverMock() : StateMachineBase(RevolverStates.Loaded) { public int BulletsLoaded { get; private set; } = 6; + internal uint StateChanges = 0; + public bool TryShoot() { switch (State) @@ -17,7 +18,9 @@ public bool TryShoot() State = RevolverStates.Empty; return true; default: - throw new InvalidOperationException($"Unknown State [{State}] for method {nameof(TryShoot)}"); + throw new InvalidOperationException( + $"Unknown State [{State}] for method {nameof(TryShoot)}" + ); } } @@ -26,5 +29,10 @@ public void Reload() BulletsLoaded = 6; State = RevolverStates.Loaded; } + + protected override void OnStateChanged() + { + StateChanges++; + } } -} \ No newline at end of file +} diff --git a/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs b/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs index 6156f15..9289925 100644 --- a/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs +++ b/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs @@ -57,5 +57,40 @@ public void EnsureReloadingResetsBulletsTo6() revolver.Reload(); Assert.AreEqual(6, revolver.BulletsLoaded); } + + [TestMethod] + public void EnsureEmptyingChangesStateAndReloadingChangesStateAgain() + { + var revolver = new RevolverMock(); + + Assert.AreEqual(0, revolver.StateChanges); + + revolver.TryShoot(); + revolver.TryShoot(); + revolver.TryShoot(); + revolver.TryShoot(); + revolver.TryShoot(); + revolver.TryShoot(); + + Assert.AreEqual(1, revolver.StateChanges); + + revolver.Reload(); + + Assert.AreEqual(2, revolver.StateChanges); + } + + [TestMethod] + public void EnsureReloadingMultipleTimesDoesNotChangeState() + { + var revolver = new RevolverMock(); + + Assert.AreEqual(0, revolver.StateChanges); + + revolver.Reload(); + revolver.Reload(); + revolver.Reload(); + + Assert.AreEqual(0, revolver.StateChanges); + } } } diff --git a/TJC.StateMachine/StateMachineBase.cs b/TJC.StateMachine/StateMachineBase.cs index 6e0fc99..8c33b4f 100644 --- a/TJC.StateMachine/StateMachineBase.cs +++ b/TJC.StateMachine/StateMachineBase.cs @@ -5,11 +5,30 @@ /// /// State Type. /// Initial State of the State Machine. - public class StateMachineBase(T initialState) + public abstract class StateMachineBase(T initialState) { + private T _state = initialState; + /// /// State of the state machine. /// - protected T State { get; set; } = initialState; + protected T State + { + get => _state; + set + { + if (Equals(_state, value)) + return; + _state = value; + OnStateChanged(); + } + } + + /// + /// Called on state change. + /// Can be used to add tracing & debugging. + /// Should not be used to notify of state changes externally. + /// + protected abstract void OnStateChanged(); } -} \ No newline at end of file +}