Skip to content

Commit

Permalink
add GameStateTests.CloningAndViewAndEqualityBehaveCorrectly; add ctor…
Browse files Browse the repository at this point in the history
…: GameStatePlayerView(GameState state)
  • Loading branch information
Konrad Jamrozik committed Jan 2, 2024
1 parent 70d42f0 commit 7497185
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/api/GameStateSchemaFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace UfoGameLib.Api;

// kja try to dedup the dependency of this filter on UfoGameLib.State.GameSession.StateJsonSerializerOptions.
// kja2-1 try to dedup the dependency of this filter on UfoGameLib.State.GameSession.StateJsonSerializerOptions.
/// <summary>
/// This schema filter ensures that OpenAPI schema generated from C# classes is generated properly.
/// This means following transformations:
Expand Down
31 changes: 6 additions & 25 deletions src/game-lib-tests/GameSessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,7 @@ public void LoadingPreviousGameStateOverridesCurrentState()

GameStatePlayerView stateView = controller.GameStatePlayerView;
int savedTurn = stateView.CurrentTurn;
// kja todo: add Clone() test that:
// Assert.That(!stateView.StateReferenceEquals(startingGameState));
// Assert.That(startingGameState.IsEqualTo(session.CurrentGameState));
// See VerifyGameSatesByJsonDiff
// See usage of clone in AddCurrentStateToPastStates
// See CurrentGameStateSerializedAsJsonString
GameState startingGameState = session.CurrentGameState.Clone();
GameState initialGameState = session.CurrentGameState.Clone();

// Act 1: Save game
controller.SaveGameState();
Expand All @@ -136,8 +130,8 @@ public void LoadingPreviousGameStateOverridesCurrentState()
controller.AdvanceTime();

// kja move this into clone-specific test
// Assume that startingGameState has not been modified by advancing time.
Assert.That(startingGameState.Timeline.CurrentTurn, Is.EqualTo(savedTurn));
// Assume that initialGameState has not been modified by advancing time.
Assert.That(initialGameState.Timeline.CurrentTurn, Is.EqualTo(savedTurn));

// Assert that advancing time didn't modify reference to current game state
Assert.That(stateView.StateReferenceEquals(controller.GameStatePlayerView));
Expand All @@ -146,25 +140,12 @@ public void LoadingPreviousGameStateOverridesCurrentState()

// Act 3: Load game
GameState loadedGameState = controller.Load();

// kja implement IEquatable so this Is.EqualTo works. See TODO below.
// See VerifyGameSatesByJsonDiff
Assert.That(loadedGameState, Is.EqualTo(session.CurrentGameState));
// kja this will fail because since I updated to NUnit 4.0.0, the objects are considered equal,
// because NUnit 4.0.0 compares all public properties. See:
// https://github.com/nunit/nunit/pull/4436
// https://github.com/nunit/nunit/issues/4394
// Above are mentioned in:
// https://docs.nunit.org/articles/nunit/release-notes/framework.html#enhancements
// linked from:
// https://docs.nunit.org/articles/nunit/release-notes/breaking-changes.html#nunit-40
// I triggered this bug by this change:
// https://github.com/konrad-jamrozik/game/commit/fa17b0985af7adde4f135be3d231555b6e7621ee#diff-718fb94a7176526686c9940ce6d3b5350e548e26a234b86a7cdd4817e68b3b52R10
// kja should this be IsSameAs?
Assert.That(loadedGameState, Is.Not.EqualTo(startingGameState));

Assert.That(loadedGameState, Is.Not.EqualTo(initialGameState));
Assert.That(stateView.CurrentTurn, Is.EqualTo(savedTurn), "savedTurn");
Assert.That(
startingGameState,
initialGameState,
Is.Not.EqualTo(loadedGameState),
"starting state should not be equal to final state");
}
Expand Down
91 changes: 91 additions & 0 deletions src/game-lib-tests/GameStateTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using UfoGameLib.State;

namespace UfoGameLib.Tests;

public class GameStateTests
{
[SetUp]
public void Setup()
{
}

/// <summary>
/// This test proves that:
/// - cloning of a game state works as expected
/// - equality of game states works as expected
/// - equality of player views works as expected
/// - equality of states referenced by player views works as expected
///
/// Specifically:
///
/// Given:
/// - An original game state, which is an arbitrary game state (here: initial game state))
/// - A player view of it, and a second player view of it
/// - A clone of that game state
/// - A player view of the clone of that game state
/// - A modified clone of that game state
///
/// The following holds:
/// - Game state player views retain references to game states they were created with
/// - A clone of game state is equal to the original (source of the cloned) game state
/// - A game state player view referencing a cloned game state is
/// equal to a player view of the original game state, because both these
/// views reference game states that are equal.
/// - A modified clone of a game state is not equal to the original game state
/// - A game state player view referencing a modified clone of a game state
/// is not equal to a player view of the original game state, because these
/// views reference game states that are not equal.
/// </summary>
[Test]
public void CloningAndViewAndEqualityBehaveCorrectly()
{
var originalGameState = GameState.NewInitialGameState();
var originalGameStateView = new GameStatePlayerView(originalGameState);
var originalGameStateView2 = new GameStatePlayerView(originalGameState);
// Act: clone game state
var clonedState = originalGameState.Clone();
var clonedStateView = new GameStatePlayerView(clonedState);

Assert.Multiple(() =>
{
// kja implement IEquatable so this Is.EqualTo works as expected.
// See VerifyGameStatesByJsonDiff
// See usage of clone in AddCurrentStateToPastStates
// See CurrentGameStateSerializedAsJsonString
// Act: exercise game state equality
Assert.That(clonedState, Is.EqualTo(originalGameState));

// Act: exercise game state view equality
Assert.That(clonedStateView, Is.EqualTo(originalGameStateView));

// Act: exercise game state view game state reference equality
Assert.That(originalGameStateView2.StateReferenceEquals(originalGameStateView));
Assert.That(!originalGameStateView.StateReferenceEquals(clonedStateView));

// Arrange: modify the cloned game state
clonedState.Timeline.CurrentTurn += 1;

/*
// kja this will fail because since I updated to NUnit 4.0.0, the objects are considered equal,
// kja should this be IsSameAs?
This assertion is necessary, in addition to:
Assert.That(clonedState, Is.EqualTo(initialState));
Without properly implemented equality the "Is.EqualTo" assert would pass,
even though the objects are not equal.
This is because NUnit 4.0.0 compares all public properties. See:
https://github.com/nunit/nunit/pull/4436
https://github.com/nunit/nunit/issues/4394
Above are mentioned in:
https://docs.nunit.org/articles/nunit/release-notes/framework.html#enhancements
linked from:
https://docs.nunit.org/articles/nunit/release-notes/breaking-changes.html#nunit-40
I originally migrated to this new behavior by updating NUnit to 4.0.0, in this commit:
https://github.com/konrad-jamrozik/game/commit/fa17b0985af7adde4f135be3d231555b6e7621ee#diff-718fb94a7176526686c9940ce6d3b5350e548e26a234b86a7cdd4817e68b3b52R10
*/
Assert.That(clonedState, Is.Not.EqualTo(originalGameState));
Assert.That(clonedStateView, Is.Not.EqualTo(originalGameStateView));
});
}
}
21 changes: 10 additions & 11 deletions src/game-lib/State/GameStatePlayerView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@

namespace UfoGameLib.State;

public class GameStatePlayerView
public class GameStatePlayerView(GameState state)
{
private readonly GameSession _session;
private readonly GameState _state = state;

public GameStatePlayerView(GameSession session)
public GameStatePlayerView(GameSession session) : this(session.CurrentGameState)
{
_session = session;
}

public int CurrentTurn => _session.CurrentGameState.Timeline.CurrentTurn;
public Missions Missions => _session.CurrentGameState.Missions;
public MissionSites MissionSites => _session.CurrentGameState.MissionSites;
public Assets Assets => _session.CurrentGameState.Assets;
public Agents TerminatedAgents => _session.CurrentGameState.TerminatedAgents;
public int CurrentTurn => _state.Timeline.CurrentTurn;
public Missions Missions => _state.Missions;
public MissionSites MissionSites => _state.MissionSites;
public Assets Assets => _state.Assets;
public Agents TerminatedAgents => _state.TerminatedAgents;

public bool StateReferenceEquals(GameState state)
=> ReferenceEquals(state, _session.CurrentGameState);
=> ReferenceEquals(state, _state);

public bool StateReferenceEquals(GameStatePlayerView view)
=> ReferenceEquals(view._session.CurrentGameState, _session.CurrentGameState);
=> ReferenceEquals(view._state, _state);
}

0 comments on commit 7497185

Please sign in to comment.