Move local teleport, player-mode, animation, shadow, and sealed-dungeon lifetimes out of GameWindow. Make player-mode entry transactional and isolate approach completions by controller lifetime and approach generation so stale callbacks cannot affect replacements. Co-authored-by: Codex <noreply@openai.com>
79 lines
3 KiB
C#
79 lines
3 KiB
C#
using AcDream.App.Interaction;
|
|
using AcDream.Core.Physics;
|
|
|
|
namespace AcDream.App.Tests.Interaction;
|
|
|
|
public sealed class PlayerApproachCompletionStateTests
|
|
{
|
|
[Fact]
|
|
public void CompletionMailbox_PreservesPublicationOrder()
|
|
{
|
|
var state = new PlayerApproachCompletionState();
|
|
IPlayerApproachCompletionSink lifetime = state.BeginControllerLifetime();
|
|
|
|
Assert.True(state.TryBeginApproach(out PlayerApproachToken token));
|
|
lifetime.PublishNaturalCompletion();
|
|
lifetime.PublishCancellation(WeenieError.NoObject);
|
|
|
|
Assert.True(state.TryTake(out PlayerApproachCompletion natural));
|
|
Assert.Equal(token, natural.Token);
|
|
Assert.True(natural.IsNatural);
|
|
Assert.Equal(WeenieError.None, natural.Error);
|
|
Assert.True(state.TryTake(out PlayerApproachCompletion cancellation));
|
|
Assert.False(cancellation.IsNatural);
|
|
Assert.Equal(WeenieError.NoObject, cancellation.Error);
|
|
Assert.False(state.TryTake(out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void Clear_DiscardsEveryPendingCompletion()
|
|
{
|
|
var state = new PlayerApproachCompletionState();
|
|
IPlayerApproachCompletionSink lifetime = state.BeginControllerLifetime();
|
|
Assert.True(state.TryBeginApproach(out _));
|
|
lifetime.PublishNaturalCompletion();
|
|
lifetime.PublishCancellation(WeenieError.ActionCancelled);
|
|
|
|
state.Clear();
|
|
|
|
Assert.False(state.TryTake(out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void RetiredLifetime_CannotPublishIntoReplacementLifetime()
|
|
{
|
|
var state = new PlayerApproachCompletionState();
|
|
IPlayerApproachCompletionSink stale = state.BeginControllerLifetime();
|
|
Assert.True(state.TryBeginApproach(out PlayerApproachToken staleToken));
|
|
state.RetireControllerLifetime(stale);
|
|
Assert.True(state.TryTake(out PlayerApproachCompletion retirement));
|
|
Assert.Equal(staleToken, retirement.Token);
|
|
Assert.False(retirement.IsNatural);
|
|
IPlayerApproachCompletionSink current = state.BeginControllerLifetime();
|
|
Assert.True(state.TryBeginApproach(out PlayerApproachToken currentToken));
|
|
|
|
stale.PublishNaturalCompletion();
|
|
current.PublishNaturalCompletion();
|
|
|
|
Assert.True(state.TryTake(out PlayerApproachCompletion completion));
|
|
Assert.Equal(currentToken, completion.Token);
|
|
Assert.False(state.TryTake(out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void Retirement_ReplacesQueuedSuccessWithMatchingCancellation()
|
|
{
|
|
var state = new PlayerApproachCompletionState();
|
|
IPlayerApproachCompletionSink lifetime = state.BeginControllerLifetime();
|
|
Assert.True(state.TryBeginApproach(out PlayerApproachToken token));
|
|
lifetime.PublishNaturalCompletion();
|
|
|
|
state.RetireControllerLifetime(lifetime);
|
|
|
|
Assert.True(state.TryTake(out PlayerApproachCompletion completion));
|
|
Assert.Equal(token, completion.Token);
|
|
Assert.False(completion.IsNatural);
|
|
Assert.Equal(WeenieError.ActionCancelled, completion.Error);
|
|
Assert.False(state.TryTake(out _));
|
|
}
|
|
}
|