From d8c0e60e254935c74cc89556b3caab788b8f6e37 Mon Sep 17 00:00:00 2001 From: Tyler Carrol Date: Tue, 22 Apr 2025 20:46:26 -0400 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20feature:=20Create=20State=20Mac?= =?UTF-8?q?hine=20Base=20Class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TJC.StateMachine.Tests/Mocks/RevolverMock.cs | 30 +++++++++ .../Mocks/RevolverStates.cs | 8 +++ .../TJC.StateMachine.Tests.csproj | 5 ++ .../Tests/RevolverMockTests.cs | 61 +++++++++++++++++++ .../Tests/StatePropertyAccessibility.cs | 26 ++++++++ TJC.StateMachine.Tests/UnitTest1.cs | 9 --- TJC.StateMachine/Class1.cs | 4 -- TJC.StateMachine/StateMachineBase.cs | 15 +++++ Testing.sln | 23 ++++--- 9 files changed, 158 insertions(+), 23 deletions(-) create mode 100644 TJC.StateMachine.Tests/Mocks/RevolverMock.cs create mode 100644 TJC.StateMachine.Tests/Mocks/RevolverStates.cs create mode 100644 TJC.StateMachine.Tests/Tests/RevolverMockTests.cs create mode 100644 TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs delete mode 100644 TJC.StateMachine.Tests/UnitTest1.cs delete mode 100644 TJC.StateMachine/Class1.cs create mode 100644 TJC.StateMachine/StateMachineBase.cs diff --git a/TJC.StateMachine.Tests/Mocks/RevolverMock.cs b/TJC.StateMachine.Tests/Mocks/RevolverMock.cs new file mode 100644 index 0000000..dcc6256 --- /dev/null +++ b/TJC.StateMachine.Tests/Mocks/RevolverMock.cs @@ -0,0 +1,30 @@ +namespace TJC.StateMachine.Tests.Mocks +{ + internal class RevolverMock() + : StateMachineBase(RevolverStates.Loaded) + { + public int BulletsLoaded { get; private set; } = 6; + + public bool TryShoot() + { + switch (State) + { + case RevolverStates.Empty: + return false; + case RevolverStates.Loaded: + BulletsLoaded--; + if (BulletsLoaded == 0) + State = RevolverStates.Empty; + return true; + default: + throw new InvalidOperationException($"Unknown State [{State}] for method {nameof(TryShoot)}"); + } + } + + public void Reload() + { + BulletsLoaded = 6; + State = RevolverStates.Loaded; + } + } +} \ No newline at end of file diff --git a/TJC.StateMachine.Tests/Mocks/RevolverStates.cs b/TJC.StateMachine.Tests/Mocks/RevolverStates.cs new file mode 100644 index 0000000..7aa9d3b --- /dev/null +++ b/TJC.StateMachine.Tests/Mocks/RevolverStates.cs @@ -0,0 +1,8 @@ +namespace TJC.StateMachine.Tests.Mocks +{ + internal enum RevolverStates + { + Loaded, + Empty + } +} \ No newline at end of file diff --git a/TJC.StateMachine.Tests/TJC.StateMachine.Tests.csproj b/TJC.StateMachine.Tests/TJC.StateMachine.Tests.csproj index 830fac4..3051be1 100644 --- a/TJC.StateMachine.Tests/TJC.StateMachine.Tests.csproj +++ b/TJC.StateMachine.Tests/TJC.StateMachine.Tests.csproj @@ -6,6 +6,7 @@ enable false true + Tests @@ -15,6 +16,10 @@ + + + + diff --git a/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs b/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs new file mode 100644 index 0000000..6156f15 --- /dev/null +++ b/TJC.StateMachine.Tests/Tests/RevolverMockTests.cs @@ -0,0 +1,61 @@ +using TJC.StateMachine.Tests.Mocks; + +namespace TJC.StateMachine.Tests.Tests +{ + [TestClass] + public class RevolverMockTests + { + [TestMethod] + public void EnsureRevolverStartsFull() + { + var revolver = new RevolverMock(); + Assert.AreEqual(6, revolver.BulletsLoaded); + } + + [TestMethod] + public void EnsureRevolverShootingLowersBulletsToZeroThenRequiresReloading() + { + var revolver = new RevolverMock(); + + var result = revolver.TryShoot(); + Assert.IsTrue(result); + Assert.AreEqual(5, revolver.BulletsLoaded); + + result = revolver.TryShoot(); + Assert.IsTrue(result); + Assert.AreEqual(4, revolver.BulletsLoaded); + + result = revolver.TryShoot(); + Assert.IsTrue(result); + Assert.AreEqual(3, revolver.BulletsLoaded); + + result = revolver.TryShoot(); + Assert.IsTrue(result); + Assert.AreEqual(2, revolver.BulletsLoaded); + + result = revolver.TryShoot(); + Assert.IsTrue(result); + Assert.AreEqual(1, revolver.BulletsLoaded); + + result = revolver.TryShoot(); + Assert.IsTrue(result); + Assert.AreEqual(0, revolver.BulletsLoaded); + + result = revolver.TryShoot(); + Assert.IsFalse(result); + Assert.AreEqual(0, revolver.BulletsLoaded); + } + + [TestMethod] + public void EnsureReloadingResetsBulletsTo6() + { + var revolver = new RevolverMock(); + + Assert.AreEqual(6, revolver.BulletsLoaded); + revolver.TryShoot(); + Assert.AreEqual(5, revolver.BulletsLoaded); + revolver.Reload(); + Assert.AreEqual(6, revolver.BulletsLoaded); + } + } +} diff --git a/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs b/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs new file mode 100644 index 0000000..83d4f4a --- /dev/null +++ b/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using TJC.StateMachine.Tests.Mocks; + +namespace TJC.StateMachine.Tests.Tests +{ + [TestClass] + public class StatePropertyAccessibility + { + [TestMethod] + public void EnsurePropertyStateIsNotPubliclyAccessible() + { + var prop = typeof(StateMachineBase).GetProperty("State", BindingFlags.Instance | BindingFlags.Public); + Assert.IsNull(prop); + } + + [TestMethod] + public void EnsurePropertyStateIsProtectedAccessible() + { + var prop = typeof(StateMachineBase).GetProperty("State", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(prop); + var getter = prop.GetMethod; + Assert.IsNotNull(getter); + Assert.IsTrue(getter.IsFamily); + } + } +} \ No newline at end of file diff --git a/TJC.StateMachine.Tests/UnitTest1.cs b/TJC.StateMachine.Tests/UnitTest1.cs deleted file mode 100644 index 93f02af..0000000 --- a/TJC.StateMachine.Tests/UnitTest1.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TJC.Rename.Tests -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void TestMethod1() { } - } -} diff --git a/TJC.StateMachine/Class1.cs b/TJC.StateMachine/Class1.cs deleted file mode 100644 index 27cdaac..0000000 --- a/TJC.StateMachine/Class1.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace TJC.Rename -{ - public class Class1 { } -} diff --git a/TJC.StateMachine/StateMachineBase.cs b/TJC.StateMachine/StateMachineBase.cs new file mode 100644 index 0000000..6e0fc99 --- /dev/null +++ b/TJC.StateMachine/StateMachineBase.cs @@ -0,0 +1,15 @@ +namespace TJC.StateMachine +{ + /// + /// State machine pattern base class. + /// + /// State Type. + /// Initial State of the State Machine. + public class StateMachineBase(T initialState) + { + /// + /// State of the state machine. + /// + protected T State { get; set; } = initialState; + } +} \ No newline at end of file diff --git a/Testing.sln b/Testing.sln index b4d4c06..48a2a7b 100644 --- a/Testing.sln +++ b/Testing.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34316.72 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TJC.Rename", "TJC.Rename\TJC.Rename.csproj", "{73B82231-E966-42EC-8635-5B98C99D8B4C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TJC.StateMachine", "TJC.StateMachine\TJC.StateMachine.csproj", "{0759E8E9-2315-4AC0-898A-A0896F4CB2A3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TJC.Rename.Tests", "TJC.Rename.Tests\TJC.Rename.Tests.csproj", "{D10EE1DD-1F71-4828-908D-1DEAA0285E71}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TJC.StateMachine.Tests", "TJC.StateMachine.Tests\TJC.StateMachine.Tests.csproj", "{2F309AA3-B442-467B-8B73-7AA31F5840F2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,16 +13,19 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {73B82231-E966-42EC-8635-5B98C99D8B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {73B82231-E966-42EC-8635-5B98C99D8B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {73B82231-E966-42EC-8635-5B98C99D8B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {73B82231-E966-42EC-8635-5B98C99D8B4C}.Release|Any CPU.Build.0 = Release|Any CPU - {D10EE1DD-1F71-4828-908D-1DEAA0285E71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D10EE1DD-1F71-4828-908D-1DEAA0285E71}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D10EE1DD-1F71-4828-908D-1DEAA0285E71}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D10EE1DD-1F71-4828-908D-1DEAA0285E71}.Release|Any CPU.Build.0 = Release|Any CPU + {0759E8E9-2315-4AC0-898A-A0896F4CB2A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0759E8E9-2315-4AC0-898A-A0896F4CB2A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0759E8E9-2315-4AC0-898A-A0896F4CB2A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0759E8E9-2315-4AC0-898A-A0896F4CB2A3}.Release|Any CPU.Build.0 = Release|Any CPU + {2F309AA3-B442-467B-8B73-7AA31F5840F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F309AA3-B442-467B-8B73-7AA31F5840F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F309AA3-B442-467B-8B73-7AA31F5840F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F309AA3-B442-467B-8B73-7AA31F5840F2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {55B3F5F4-C774-4815-8438-CD3E90055A85} + EndGlobalSection EndGlobal From edd25af8eb077e657419fb442d4f747702277041 Mon Sep 17 00:00:00 2001 From: Tyler Carrol Date: Tue, 22 Apr 2025 20:48:14 -0400 Subject: [PATCH 2/3] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15a61af..3ad1d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Added + +- Created state machine base class From 265b605ce89079847cad422c51bec6071911525b Mon Sep 17 00:00:00 2001 From: TylerCarrol <25536704+TylerCarrol@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:51:28 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=B9=20chore:=20[MegaLinter]=20Appl?= =?UTF-8?q?y=20[1]=20automatic=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TJC.StateMachine.Tests/Mocks/RevolverMock.cs | 9 +++++---- TJC.StateMachine.Tests/Mocks/RevolverStates.cs | 4 ++-- .../Tests/StatePropertyAccessibility.cs | 12 +++++++++--- TJC.StateMachine/StateMachineBase.cs | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/TJC.StateMachine.Tests/Mocks/RevolverMock.cs b/TJC.StateMachine.Tests/Mocks/RevolverMock.cs index dcc6256..9091f3f 100644 --- a/TJC.StateMachine.Tests/Mocks/RevolverMock.cs +++ b/TJC.StateMachine.Tests/Mocks/RevolverMock.cs @@ -1,7 +1,6 @@ namespace TJC.StateMachine.Tests.Mocks { - internal class RevolverMock() - : StateMachineBase(RevolverStates.Loaded) + internal class RevolverMock() : StateMachineBase(RevolverStates.Loaded) { public int BulletsLoaded { get; private set; } = 6; @@ -17,7 +16,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)}" + ); } } @@ -27,4 +28,4 @@ public void Reload() State = RevolverStates.Loaded; } } -} \ No newline at end of file +} diff --git a/TJC.StateMachine.Tests/Mocks/RevolverStates.cs b/TJC.StateMachine.Tests/Mocks/RevolverStates.cs index 7aa9d3b..689e5ce 100644 --- a/TJC.StateMachine.Tests/Mocks/RevolverStates.cs +++ b/TJC.StateMachine.Tests/Mocks/RevolverStates.cs @@ -3,6 +3,6 @@ internal enum RevolverStates { Loaded, - Empty + Empty, } -} \ No newline at end of file +} diff --git a/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs b/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs index 83d4f4a..ec8d0bf 100644 --- a/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs +++ b/TJC.StateMachine.Tests/Tests/StatePropertyAccessibility.cs @@ -9,18 +9,24 @@ public class StatePropertyAccessibility [TestMethod] public void EnsurePropertyStateIsNotPubliclyAccessible() { - var prop = typeof(StateMachineBase).GetProperty("State", BindingFlags.Instance | BindingFlags.Public); + var prop = typeof(StateMachineBase).GetProperty( + "State", + BindingFlags.Instance | BindingFlags.Public + ); Assert.IsNull(prop); } [TestMethod] public void EnsurePropertyStateIsProtectedAccessible() { - var prop = typeof(StateMachineBase).GetProperty("State", BindingFlags.Instance | BindingFlags.NonPublic); + var prop = typeof(StateMachineBase).GetProperty( + "State", + BindingFlags.Instance | BindingFlags.NonPublic + ); Assert.IsNotNull(prop); var getter = prop.GetMethod; Assert.IsNotNull(getter); Assert.IsTrue(getter.IsFamily); } } -} \ No newline at end of file +} diff --git a/TJC.StateMachine/StateMachineBase.cs b/TJC.StateMachine/StateMachineBase.cs index 6e0fc99..e1fd8e6 100644 --- a/TJC.StateMachine/StateMachineBase.cs +++ b/TJC.StateMachine/StateMachineBase.cs @@ -12,4 +12,4 @@ public class StateMachineBase(T initialState) /// protected T State { get; set; } = initialState; } -} \ No newline at end of file +}