From 8ed6556e3bcf63791fe5cb13e4ad6f700e9c7da2 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Mon, 4 Mar 2024 20:58:25 +0100 Subject: [PATCH] feat: add Action (#8) * Feat: Add Action * docs: fix documentation * Fix: Remove tests and mocks for sonar * feat: add actionable to action * Test: Add Tests for Action * fix: conform to Jens * refactor: add constructor * feat: Add the ability to not have a query/guard * fix: make constructor public * fix: don't let the build crash because of the tests * fix: Change visibility to internal * chore: simplify tests --- Aplib.Core/Action.cs | 53 +++++++++++ Aplib.Core/Aplib.Core.csproj | 4 + Aplib.Core/GuardedAction.cs | 48 ++++++++++ Aplib.Core/SonarTest.cs | 9 -- Aplib.Tests/Core/ActionTests.cs | 157 ++++++++++++++++++++++++++++++++ Aplib.Tests/SonarTest.cs | 13 --- 6 files changed, 262 insertions(+), 22 deletions(-) create mode 100644 Aplib.Core/Action.cs create mode 100644 Aplib.Core/GuardedAction.cs delete mode 100644 Aplib.Core/SonarTest.cs create mode 100644 Aplib.Tests/Core/ActionTests.cs delete mode 100644 Aplib.Tests/SonarTest.cs diff --git a/Aplib.Core/Action.cs b/Aplib.Core/Action.cs new file mode 100644 index 00000000..ff790496 --- /dev/null +++ b/Aplib.Core/Action.cs @@ -0,0 +1,53 @@ +using System; + +namespace Aplib.Core +{ + /// + /// Describes an action that can be executed and guarded. + /// + public class Action + { + /// + /// Gets or sets the effect of the action. + /// + protected System.Action Effect { get; set; } + + /// + /// Gets or sets the guard of the action. + /// + protected Func Guard { get; set; } = () => true; + + /// + /// Initializes a new empty instance of the class. + /// + /// Only meant for internal use + protected internal Action() + { + Effect = () => { }; + Guard = () => false; + } + + /// + /// Initializes a new instance of the class. + /// + /// The effect of the action. + public Action(System.Action effect) => Effect = effect; + + /// + /// Initializes a new instance of the class. + /// + /// The effect of the action. + /// The guard of the action. + public Action(System.Action effect, Func guard) : this(effect) => Guard = guard; + + /// + /// Execute the action against the world. + /// + internal virtual void Execute() => Effect(); + + /// + /// Guard the action against unwanted execution. The result is stored and can be used in the effect. + /// + internal virtual bool IsActionable() => Guard(); + } +} diff --git a/Aplib.Core/Aplib.Core.csproj b/Aplib.Core/Aplib.Core.csproj index b9fb3908..39c0aa88 100644 --- a/Aplib.Core/Aplib.Core.csproj +++ b/Aplib.Core/Aplib.Core.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/Aplib.Core/GuardedAction.cs b/Aplib.Core/GuardedAction.cs new file mode 100644 index 00000000..15423309 --- /dev/null +++ b/Aplib.Core/GuardedAction.cs @@ -0,0 +1,48 @@ +using System; + +namespace Aplib.Core +{ + /// + /// Describes an action that can be executed and guarded with a query that stores the result of the guard. + /// The result can be used in the effect. + /// + /// The type of the query of the action + public class GuardedAction : Action + { + /// + /// Gets or sets the result of the guard. + /// + protected TQuery? StoredGuardResult { get; set; } + + /// + /// Gets or sets the effect of the action. + /// + protected new System.Action Effect { get; set; } + + /// + /// Gets or sets the guard of the action. + /// + protected new Func Guard { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The effect of the action. + /// The guard of the action. + public GuardedAction(System.Action effect, Func guard) : base() + { + Effect = effect; + Guard = guard; + } + + /// + internal override void Execute() => Effect(StoredGuardResult!); + + /// + internal override bool IsActionable() + { + StoredGuardResult = Guard(); + return StoredGuardResult is not null; + } + } +} diff --git a/Aplib.Core/SonarTest.cs b/Aplib.Core/SonarTest.cs deleted file mode 100644 index 6b871815..00000000 --- a/Aplib.Core/SonarTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Aplib.Core -{ - public class SonarTest - { - public void Main() => Console.WriteLine("Hello World!"); - } -} diff --git a/Aplib.Tests/Core/ActionTests.cs b/Aplib.Tests/Core/ActionTests.cs new file mode 100644 index 00000000..16ad68d8 --- /dev/null +++ b/Aplib.Tests/Core/ActionTests.cs @@ -0,0 +1,157 @@ +using Aplib.Core; +using Action = Aplib.Core.Action; + +namespace Aplib.Tests.Core; + +/// +/// Describes a set of tests for the class. +/// +public class ActionTests +{ + /// + /// Given a side effect action with a string guard, + /// When the action is executed, + /// Then the result should be null. + /// + [Fact] + public void Execute_SideEffects_ReturnsCorrectEffect() + { + // Arrange + string? result = "abc"; + Action action = new(effect: () => result = "def"); + + // Act + action.Execute(); + + // Assert + Assert.Equal("def", result); + } + + /// + /// Given an action with no query, + /// When checking if the action is actionable, + /// Then the result should always be true. + /// + [Fact] + public void IsActionable_NoQuery_AlwaysTrue() + { + // Arrange + Action action = new(effect: () => { }); + + // Act + bool actionable = action.IsActionable(); + + // Assert + Assert.True(actionable); + } + + /// + /// Given an action with a true query, + /// When checking if the action is actionable, + /// Then the result should be true. + /// + [Fact] + public void IsActionable_QueryWithTrue_ReturnsTrue() + { + // Arrange + Action action = new(effect: () => { }, guard: () => true); + + // Act + bool actionable = action.IsActionable(); + + // Assert + Assert.True(actionable); + } + + /// + /// Given an action with a false query, + /// When checking if the action is actionable, + /// Then the result should be false. + /// + [Fact] + public void IsActionable_QueryWithFalse_ReturnsFalse() + { + // Arrange + Action action = new(effect: () => { }, guard: () => false); + + // Act + bool actionable = action.IsActionable(); + + // Assert + Assert.False(actionable); + } + + /// + /// Given a guarded action with an int guard, + /// When the action is guarded and executed, + /// Then the result should be the value of the guard. + /// + [Fact] + public void Execute_WithGuard_ShouldInvokeQueryAndStoreResult() + { + // Arrange + int result = 0; + GuardedAction action = new(guard: () => 42, effect: (guard) => result = guard); + + // Act + _ = action.IsActionable(); + action.Execute(); + + // Assert + Assert.Equal(42, result); + } + + /// + /// Given an action with a non-null int guard, + /// When checking if the action is actionable, + /// Then the result should be true. + /// + [Fact] + public void IsActionable_QueryIsNotNull_IsActionable() + { + // Arrange + GuardedAction action = new(guard: () => 10, effect: b => { }); + + // Act + bool result = action.IsActionable(); + + // Assert + Assert.True(result); + } + + /// + /// Given an action with a false bool guard, + /// When checking if the action is actionable, + /// Then the result should be true. + /// + [Fact] + public void IsActionable_QueryIsFalse_IsActionable() + { + // Arrange + GuardedAction action = new(guard: () => false, effect: b => { }); + + // Act + bool result = action.IsActionable(); + + // Assert + Assert.True(result); + } + + /// + /// Given an action with a null object guard, + /// When checking if the action is actionable, + /// Then the result should be false. + /// + [Fact] + public void IsActionable_QueryIsNull_IsNotActionable() + { + // Arrange + GuardedAction action = new(guard: () => null!, effect: b => { }); + + // Act + bool result = action.IsActionable(); + + // Assert + Assert.False(result); + } +} diff --git a/Aplib.Tests/SonarTest.cs b/Aplib.Tests/SonarTest.cs deleted file mode 100644 index 86f10414..00000000 --- a/Aplib.Tests/SonarTest.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Aplib.Tests; - -public class SonarTest -{ - [Fact] - public void TestMain() - { - Core.SonarTest test = new(); - test.Main(); - - Assert.True(true); - } -} \ No newline at end of file