Skip to content

Commit

Permalink
feat: add Action (#8)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
joachimdekker authored Mar 4, 2024
1 parent c34f258 commit 8ed6556
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 22 deletions.
53 changes: 53 additions & 0 deletions Aplib.Core/Action.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;

namespace Aplib.Core
{
/// <summary>
/// Describes an action that can be executed and guarded.
/// </summary>
public class Action
{
/// <summary>
/// Gets or sets the effect of the action.
/// </summary>
protected System.Action Effect { get; set; }

/// <summary>
/// Gets or sets the guard of the action.
/// </summary>
protected Func<bool> Guard { get; set; } = () => true;

/// <summary>
/// Initializes a new empty instance of the <see cref="Action{TQuery}"/> class.
/// </summary>
/// <remarks>Only meant for internal use</remarks>
protected internal Action()
{
Effect = () => { };
Guard = () => false;
}

/// <summary>
/// Initializes a new instance of the <see cref="Action{TQuery}"/> class.
/// </summary>
/// <param name="effect">The effect of the action.</param>
public Action(System.Action effect) => Effect = effect;

/// <summary>
/// Initializes a new instance of the <see cref="Action{TQuery}"/> class.
/// </summary>
/// <param name="effect">The effect of the action.</param>
/// <param name="guard">The guard of the action.</param>
public Action(System.Action effect, Func<bool> guard) : this(effect) => Guard = guard;

/// <summary>
/// Execute the action against the world.
/// </summary>
internal virtual void Execute() => Effect();

/// <summary>
/// Guard the action against unwanted execution. The result is stored and can be used in the effect.
/// </summary>
internal virtual bool IsActionable() => Guard();
}
}
4 changes: 4 additions & 0 deletions Aplib.Core/Aplib.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup Label="Tests">
<InternalsVisibleTo Include="Aplib.Tests"/>
</ItemGroup>

</Project>
48 changes: 48 additions & 0 deletions Aplib.Core/GuardedAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;

namespace Aplib.Core
{
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TQuery">The type of the query of the action</typeparam>
public class GuardedAction<TQuery> : Action
{
/// <summary>
/// Gets or sets the result of the guard.
/// </summary>
protected TQuery? StoredGuardResult { get; set; }

/// <summary>
/// Gets or sets the effect of the action.
/// </summary>
protected new System.Action<TQuery> Effect { get; set; }

/// <summary>
/// Gets or sets the guard of the action.
/// </summary>
protected new Func<TQuery?> Guard { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="GuardedAction{TQuery}"/> class.
/// </summary>
/// <param name="effect">The effect of the action.</param>
/// <param name="guard">The guard of the action.</param>
public GuardedAction(System.Action<TQuery> effect, Func<TQuery?> guard) : base()
{
Effect = effect;
Guard = guard;
}

/// <inheritdoc/>
internal override void Execute() => Effect(StoredGuardResult!);

/// <inheritdoc/>
internal override bool IsActionable()
{
StoredGuardResult = Guard();
return StoredGuardResult is not null;
}
}
}
9 changes: 0 additions & 9 deletions Aplib.Core/SonarTest.cs

This file was deleted.

157 changes: 157 additions & 0 deletions Aplib.Tests/Core/ActionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using Aplib.Core;
using Action = Aplib.Core.Action;

namespace Aplib.Tests.Core;

/// <summary>
/// Describes a set of tests for the <see cref="GuardedAction{TQuery}"/> class.
/// </summary>
public class ActionTests
{
/// <summary>
/// Given a side effect action with a string guard,
/// When the action is executed,
/// Then the result should be null.
/// </summary>
[Fact]
public void Execute_SideEffects_ReturnsCorrectEffect()
{
// Arrange
string? result = "abc";
Action action = new(effect: () => result = "def");

// Act
action.Execute();

// Assert
Assert.Equal("def", result);
}

/// <summary>
/// Given an action with no query,
/// When checking if the action is actionable,
/// Then the result should always be true.
/// </summary>
[Fact]
public void IsActionable_NoQuery_AlwaysTrue()
{
// Arrange
Action action = new(effect: () => { });

// Act
bool actionable = action.IsActionable();

// Assert
Assert.True(actionable);
}

/// <summary>
/// Given an action with a true query,
/// When checking if the action is actionable,
/// Then the result should be true.
/// </summary>
[Fact]
public void IsActionable_QueryWithTrue_ReturnsTrue()
{
// Arrange
Action action = new(effect: () => { }, guard: () => true);

// Act
bool actionable = action.IsActionable();

// Assert
Assert.True(actionable);
}

/// <summary>
/// Given an action with a false query,
/// When checking if the action is actionable,
/// Then the result should be false.
/// </summary>
[Fact]
public void IsActionable_QueryWithFalse_ReturnsFalse()
{
// Arrange
Action action = new(effect: () => { }, guard: () => false);

// Act
bool actionable = action.IsActionable();

// Assert
Assert.False(actionable);
}

/// <summary>
/// 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.
/// </summary>
[Fact]
public void Execute_WithGuard_ShouldInvokeQueryAndStoreResult()
{
// Arrange
int result = 0;
GuardedAction<int> action = new(guard: () => 42, effect: (guard) => result = guard);

// Act
_ = action.IsActionable();
action.Execute();

// Assert
Assert.Equal(42, result);
}

/// <summary>
/// Given an action with a non-null int guard,
/// When checking if the action is actionable,
/// Then the result should be true.
/// </summary>
[Fact]
public void IsActionable_QueryIsNotNull_IsActionable()
{
// Arrange
GuardedAction<int> action = new(guard: () => 10, effect: b => { });

// Act
bool result = action.IsActionable();

// Assert
Assert.True(result);
}

/// <summary>
/// Given an action with a false bool guard,
/// When checking if the action is actionable,
/// Then the result should be true.
/// </summary>
[Fact]
public void IsActionable_QueryIsFalse_IsActionable()
{
// Arrange
GuardedAction<bool> action = new(guard: () => false, effect: b => { });

// Act
bool result = action.IsActionable();

// Assert
Assert.True(result);
}

/// <summary>
/// Given an action with a null object guard,
/// When checking if the action is actionable,
/// Then the result should be false.
/// </summary>
[Fact]
public void IsActionable_QueryIsNull_IsNotActionable()
{
// Arrange
GuardedAction<object> action = new(guard: () => null!, effect: b => { });

// Act
bool result = action.IsActionable();

// Assert
Assert.False(result);
}
}
13 changes: 0 additions & 13 deletions Aplib.Tests/SonarTest.cs

This file was deleted.

0 comments on commit 8ed6556

Please sign in to comment.