Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 12 additions & 4 deletions TJC.StateMachine.Tests/Mocks/RevolverMock.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
namespace TJC.StateMachine.Tests.Mocks
{
internal class RevolverMock()
: StateMachineBase<RevolverStates>(RevolverStates.Loaded)
internal class RevolverMock() : StateMachineBase<RevolverStates>(RevolverStates.Loaded)
{
public int BulletsLoaded { get; private set; } = 6;

internal uint StateChanges = 0;

public bool TryShoot()
{
switch (State)
Expand All @@ -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)}"
);
}
}

Expand All @@ -26,5 +29,10 @@ public void Reload()
BulletsLoaded = 6;
State = RevolverStates.Loaded;
}

protected override void OnStateChanged()
{
StateChanges++;
}
}
}
}
35 changes: 35 additions & 0 deletions TJC.StateMachine.Tests/Tests/RevolverMockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,40 @@ public void EnsureReloadingResetsBulletsTo6()
revolver.Reload();
Assert.AreEqual(6, revolver.BulletsLoaded);
}

[TestMethod]
public void EnsureEmptyingChangesStateAndReloadingChangesStateAgain()
{
var revolver = new RevolverMock();

Assert.AreEqual<uint>(0, revolver.StateChanges);

revolver.TryShoot();
revolver.TryShoot();
revolver.TryShoot();
revolver.TryShoot();
revolver.TryShoot();
revolver.TryShoot();

Assert.AreEqual<uint>(1, revolver.StateChanges);

revolver.Reload();

Assert.AreEqual<uint>(2, revolver.StateChanges);
}

[TestMethod]
public void EnsureReloadingMultipleTimesDoesNotChangeState()
{
var revolver = new RevolverMock();

Assert.AreEqual<uint>(0, revolver.StateChanges);

revolver.Reload();
revolver.Reload();
revolver.Reload();

Assert.AreEqual<uint>(0, revolver.StateChanges);
}
}
}
25 changes: 22 additions & 3 deletions TJC.StateMachine/StateMachineBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,30 @@
/// </summary>
/// <typeparam name="T">State Type.</typeparam>
/// <param name="initialState">Initial State of the State Machine.</param>
public class StateMachineBase<T>(T initialState)
public abstract class StateMachineBase<T>(T initialState)
{
private T _state = initialState;

/// <summary>
/// State of the state machine.
/// </summary>
protected T State { get; set; } = initialState;
protected T State
{
get => _state;
set
{
if (Equals(_state, value))
return;
_state = value;
OnStateChanged();
}
}

/// <summary>
/// Called on state change.
/// Can be used to add tracing &amp; debugging.
/// Should not be used to notify of state changes externally.
/// </summary>
protected abstract void OnStateChanged();
}
}
}
Loading