Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions Assets/Tests/Editor/ControlPlayModeUseCaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,60 @@ public async Task ExecuteAsync_WhenPlayBlockedWithoutSavedErrors_ReturnsEmptyDia
Assert.That(response.Message, Does.Contain("no saved compiler diagnostics"));
}

[Test]
public async Task ExecuteAsync_WhenPlayStartSaveFails_DoesNotEnterPlayMode()
{
// Verifies dirty Scene/Prefab save failure blocks Edit→Play instead of prompting or hanging.
Assert.That(EditorApplication.isPlaying, Is.False);
StubEditorUnsavedChangesQuietSaver quietSaver = new(
saveFailures: new[] { "Scene: Assets/Scenes/Sample.unity" },
remainingAfterSave: System.Array.Empty<string>());
ControlPlayModeUseCase useCase = new ControlPlayModeUseCase(
new StubCompilationFailureProvider(System.Array.Empty<ControlPlayModeCompileError>()),
new StubCompilationFailureGate(false),
quietSaver);
ControlPlayModeSchema schema = new ControlPlayModeSchema
{
Action = PlayModeAction.Play,
};

ControlPlayModeResponse response = await useCase.ExecuteAsync(schema, CancellationToken.None);

Assert.That(quietSaver.SaveCallCount, Is.EqualTo(1));
Assert.That(EditorApplication.isPlaying, Is.False);
Assert.That(response.Changed, Is.False);
Assert.That(response.IsPlaying, Is.False);
Assert.That(response.Message, Does.Contain("could not be saved"));
Assert.That(response.Message, Does.Contain("Scene: Assets/Scenes/Sample.unity"));
}

[Test]
public async Task ExecuteAsync_WhenPlayStartLeavesUnsavedChanges_DoesNotEnterPlayMode()
{
// Verifies remaining dirty editor state after a quiet save still blocks Play start.
Assert.That(EditorApplication.isPlaying, Is.False);
StubEditorUnsavedChangesQuietSaver quietSaver = new(
saveFailures: System.Array.Empty<string>(),
remainingAfterSave: new[] { "Prefab Stage: Assets/Prefabs/Hud.prefab" });
ControlPlayModeUseCase useCase = new ControlPlayModeUseCase(
new StubCompilationFailureProvider(System.Array.Empty<ControlPlayModeCompileError>()),
new StubCompilationFailureGate(false),
quietSaver);
ControlPlayModeSchema schema = new ControlPlayModeSchema
{
Action = PlayModeAction.Play,
};

ControlPlayModeResponse response = await useCase.ExecuteAsync(schema, CancellationToken.None);

Assert.That(quietSaver.SaveCallCount, Is.EqualTo(1));
Assert.That(quietSaver.DetectCallCount, Is.EqualTo(1));
Assert.That(EditorApplication.isPlaying, Is.False);
Assert.That(response.Changed, Is.False);
Assert.That(response.Message, Does.Contain("unsaved scene or prefab changes"));
Assert.That(response.Message, Does.Contain("Prefab Stage: Assets/Prefabs/Hud.prefab"));
}

private sealed class StubCompilationFailureProvider : IControlPlayModeCompilationFailureProvider
{
private readonly ControlPlayModeCompileError[] _errors;
Expand Down Expand Up @@ -289,5 +343,32 @@ public bool HasScriptCompilationFailed()
return _hasScriptCompilationFailed;
}
}

private sealed class StubEditorUnsavedChangesQuietSaver : IEditorUnsavedChangesQuietSaver
{
private readonly string[] _saveFailures;
private readonly string[] _remainingAfterSave;

public int SaveCallCount { get; private set; }
public int DetectCallCount { get; private set; }

public StubEditorUnsavedChangesQuietSaver(string[] saveFailures, string[] remainingAfterSave)
{
_saveFailures = saveFailures;
_remainingAfterSave = remainingAfterSave;
}

public string[] DetectUnsavedEditorChanges()
{
DetectCallCount++;
return _remainingAfterSave;
}

public string[] SaveUnsavedEditorChanges()
{
SaveCallCount++;
return _saveFailures;
}
}
}
}
6 changes: 6 additions & 0 deletions Assets/Tests/Editor/PausePointCaptureModeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ public void Pause()
{
PauseCount++;
}

public void Resume()
{
// Why zero: Unity's isPaused is a bool; Option B Resume must fully clear pause.
PauseCount = 0;
}
}
}
}
78 changes: 78 additions & 0 deletions Assets/Tests/Editor/PausePointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,77 @@ public void Clear_WhenPausePointWasHit_ReportsAlreadyHitMessage()
UloopPausePointSnapshot snapshot = UloopPausePointRegistry.Clear("jump");

Assert.That(snapshot.Message, Is.EqualTo("Pause point was already hit (auto-disarmed); nothing to clear."));
Assert.That(_pauseController.IsPaused, Is.False);
}

[Test]
public void Clear_AfterHit_ShouldResumeEditorPause()
{
// Verifies Option B: Clear resumes even when the pause was left from a pause-point hit.
UloopPausePointRegistry.Enable("jump", 30);
UloopPausePoint.Pause("jump");
Assert.That(_pauseController.IsPaused, Is.True);

UloopPausePointRegistry.Clear("jump");

Assert.That(_pauseController.IsPaused, Is.False);
}

[Test]
public void ClearAll_AfterHit_ShouldResumeEditorPause()
{
// Verifies ClearAll also resumes under Option B.
UloopPausePointRegistry.Enable("jump", 30);
UloopPausePoint.Pause("jump");

UloopPausePointRegistry.ClearAll();

Assert.That(_pauseController.IsPaused, Is.False);
}

[Test]
public void ApplyCaptureWindowExpirations_WhenHitPastTimeout_ShouldExpireAndResume()
{
// Verifies abandoned SingleShot hits still expire at ExpiresAtUtc and resume without a Clear poll.
UloopPausePointRegistry.Enable("jump", 30);
UloopPausePoint.Pause("jump");
_nowUtc = _nowUtc.AddSeconds(31);

UloopPausePointRegistry.ApplyCaptureWindowExpirations();

UloopPausePointSnapshot status = UloopPausePointRegistry.GetStatus("jump");
Assert.That(status.Status, Is.EqualTo(UloopPausePointStatus.Expired));
Assert.That(_pauseController.IsPaused, Is.False);
}

[Test]
public void ResumeEditorPauseForClientDisconnect_WhenPaused_ShouldResumeOnMainThreadApply()
{
// Verifies disconnect only arms a pending flag; main-thread apply resumes once (Option B).
UloopPausePointRegistry.Enable("jump", 30);
UloopPausePoint.Pause("jump");

UloopPausePointRegistry.ResumeEditorPauseForClientDisconnect();
Assert.That(_pauseController.IsPaused, Is.True);
Assert.That(_pauseController.ResumeCount, Is.EqualTo(0));

UloopPausePointRegistry.ApplyPendingClientDisconnectResume();

Assert.That(_pauseController.IsPaused, Is.False);
Assert.That(_pauseController.ResumeCount, Is.EqualTo(1));
}

[Test]
public void ApplyPendingClientDisconnectResume_WhenNotPaused_ShouldDiscardPendingFlag()
{
// Verifies a disconnect request while already running does not call Resume.
Assert.That(_pauseController.IsPaused, Is.False);

UloopPausePointRegistry.ResumeEditorPauseForClientDisconnect();
UloopPausePointRegistry.ApplyPendingClientDisconnectResume();

Assert.That(_pauseController.ResumeCount, Is.EqualTo(0));
Assert.That(_pauseController.IsPaused, Is.False);
}

[Test]
Expand Down Expand Up @@ -924,12 +995,19 @@ private sealed class FakePauseController : IUloopPausePointPauseController
public bool IsPlaying { get; private set; } = true;
public bool IsPaused { get; private set; }
public int PauseCount { get; private set; }
public int ResumeCount { get; private set; }

public void Pause()
{
PauseCount++;
IsPaused = true;
}

public void Resume()
{
ResumeCount++;
IsPaused = false;
}
}
}
}
6 changes: 6 additions & 0 deletions Assets/Tests/Editor/PausePointToolModeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ public void Pause()
{
PauseCount++;
}

public void Resume()
{
// Why zero: Unity's isPaused is a bool; Option B Resume must fully clear pause.
PauseCount = 0;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ public void Pause()
{
PauseCount++;
}

public void Resume()
{
// Why zero: Unity's isPaused is a bool; Option B Resume must fully clear pause.
PauseCount = 0;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,12 @@ public void Pause()
{
PauseCount++;
}

public void Resume()
{
// Why zero: Unity's isPaused is a bool; Option B Resume must fully clear pause.
PauseCount = 0;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ public void Pause()
{
IsPaused = true;
}

public void Resume()
{
IsPaused = false;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ public void Pause()
{
IsPaused = true;
}

public void Resume()
{
IsPaused = false;
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions Assets/Tests/PlayMode/SimulateKeyboardTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,11 @@ public void Pause()
{
IsPaused = true;
}

public void Resume()
{
IsPaused = false;
}
}

private static BadgeVisual RequireBadgeVisual(string keyName)
Expand Down
5 changes: 5 additions & 0 deletions Assets/Tests/PlayMode/SimulateMouseInputTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,11 @@ public void Pause()
{
IsPaused = true;
}

public void Resume()
{
IsPaused = false;
}
}

#endregion
Expand Down
Loading
Loading