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
57 changes: 57 additions & 0 deletions Assets/Tests/Editor/PausePointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,63 @@ public async Task Clear_WhenIdIsEmptyAndAllIsFalse_ReturnsValidationFailureRespo
Assert.That(response.Message, Is.EqualTo("Id must not be null or empty."));
}

[Test]
public void HitWithCapturedVariables_WhenPausePointIsEnabled_StoresCapturedVariablesInSnapshot()
{
// Verifies the source-pause-point hit path threads captured variables through to the snapshot.
UloopPausePointRegistry.Enable("jump", 30);
UloopCapturedVariable[] capturedVariables =
{
new("speed", UloopCapturedVariableScope.Local, "System.Int32", "5", string.Empty, string.Empty, 0)
};

UloopPausePointSnapshot snapshot = UloopPausePointRegistry.HitWithCapturedVariables(
"jump", capturedVariables, true);

Assert.That(snapshot.CapturedVariables, Is.EqualTo(capturedVariables));
Assert.That(snapshot.CapturedVariablesTruncated, Is.True);
Assert.That(_pauseController.PauseCount, Is.EqualTo(1));
}

[Test]
public void Hit_WhenPausePointIsEnabled_ReportsEmptyCapturedVariables()
{
// Verifies the plain marker path (no source pause point) reports an empty, non-null list.
UloopPausePointRegistry.Enable("jump", 30);

UloopPausePoint.Pause("jump");

UloopPausePointSnapshot snapshot = UloopPausePointRegistry.GetStatus("jump");
Assert.That(snapshot.CapturedVariables, Is.Empty);
Assert.That(snapshot.CapturedVariablesTruncated, Is.False);
}

[Test]
public void IsArmed_WhenPausePointIsEnabled_ReturnsTrue()
{
// Verifies the injected Capture code's fast path recognizes an armed marker.
UloopPausePointRegistry.Enable("jump", 30);

Assert.That(UloopPausePointRegistry.IsArmed("jump"), Is.True);
}

[Test]
public void IsArmed_WhenPausePointIsNotEnabled_ReturnsFalse()
{
// Verifies the injected Capture code's fast path no-ops for an id that was never enabled.
Assert.That(UloopPausePointRegistry.IsArmed("jump"), Is.False);
}

[Test]
public void IsArmed_WhenPausePointWasAlreadyHit_ReturnsFalse()
{
// Verifies a one-shot marker disarms itself so a second pass through the same line no-ops.
UloopPausePointRegistry.Enable("jump", 30);
UloopPausePoint.Pause("jump");

Assert.That(UloopPausePointRegistry.IsArmed("jump"), Is.False);
}

[Test]
public void PauseMethod_WhenSourceIsScanned_UsesUnityEditorConditionalWithoutDebugBreak()
{
Expand Down
8 changes: 8 additions & 0 deletions Assets/Tests/Editor/SourcePausePointCapture.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections;
using System.Linq;
using System.Threading.Tasks;

using NUnit.Framework;

using UnityEngine;
using UnityEngine.TestTools;

using io.github.hatayama.UnityCliLoop.FirstPartyTools;
using io.github.hatayama.UnityCliLoop.Runtime;

namespace io.github.hatayama.UnityCliLoop.Tests.Editor
{
/// <summary>
/// Verifies the Harmony-injected landing point: the armed fast-path no-op and the
/// formatted-variables handoff into the registry.
/// </summary>
[TestFixture]
public sealed class SourcePausePointCaptureTests
{
private FakePausePointPauseController _pauseController;

[SetUp]
public void SetUp()
{
_pauseController = new FakePausePointPauseController();
UloopPausePointRegistry.ConfigureForTests(_pauseController, () => DateTime.UtcNow);
}

[TearDown]
public void TearDown()
{
UloopPausePointRegistry.ResetForTests();
}

[Test]
public void Capture_WhenPausePointIsEnabled_RecordsFormattedVariablesInSnapshot()
{
// Verifies an armed marker's hit threads formatted locals/parameters into the snapshot.
UloopPausePointRegistry.Enable("jump", 30);
object[] parameters = { "damage", 3 };
object[] locals = { "speed", 5 };

SourcePausePointCapture.Capture("jump", null, parameters, locals);

UloopPausePointSnapshot snapshot = UloopPausePointRegistry.GetStatus("jump");
Assert.That(snapshot.IsHit, Is.True);
Assert.That(snapshot.CapturedVariables.Select(v => v.Name), Is.EquivalentTo(new[] { "speed", "damage" }));
Assert.That(snapshot.CapturedVariablesTruncated, Is.False);
Assert.That(_pauseController.PauseCount, Is.EqualTo(1));
}

[Test]
public void Capture_WhenPausePointIsNotArmed_DoesNotPauseOrRecordAHit()
{
// Verifies the IsArmed fast path no-ops when the marker was never enabled.
SourcePausePointCapture.Capture("never-enabled", null, Array.Empty<object>(), Array.Empty<object>());

UloopPausePointSnapshot snapshot = UloopPausePointRegistry.GetStatus("never-enabled");
Assert.That(snapshot.IsHit, Is.False);
Assert.That(_pauseController.PauseCount, Is.EqualTo(0));
}

[Test]
public void Capture_WhenPausePointWasAlreadyHit_IgnoresSecondCall()
{
// Verifies a one-shot marker disarms itself so a second pass through the same line no-ops.
UloopPausePointRegistry.Enable("jump", 30);
SourcePausePointCapture.Capture("jump", null, Array.Empty<object>(), Array.Empty<object>());

SourcePausePointCapture.Capture("jump", null, Array.Empty<object>(), Array.Empty<object>());

Assert.That(_pauseController.PauseCount, Is.EqualTo(1));
}

[UnityTest]
public IEnumerator Capture_WhenCalledOffMainThread_RecordsHitOnNextMainThreadTick()
{
// Verifies an off-main-thread Capture call is marshalled to the main thread
// (must-fix 2): EditorApplication.isPaused and the registry's own bookkeeping are
// main-thread-only, so the hit must land via MainThreadSwitcher's continuation queue
// rather than running inline on the calling background thread.
UloopPausePointRegistry.Enable("jump", 30);
object[] locals = { "speed", 5 };

Task.Run(() => SourcePausePointCapture.Capture("jump", null, Array.Empty<object>(), locals));

float timeoutTime = Time.realtimeSinceStartup + 5f;
UloopPausePointSnapshot snapshot = UloopPausePointRegistry.GetStatus("jump");
while (!snapshot.IsHit && Time.realtimeSinceStartup < timeoutTime)
{
yield return null;
snapshot = UloopPausePointRegistry.GetStatus("jump");
}

Assert.That(snapshot.IsHit, Is.True, "hit should be recorded on the main thread within timeout");
Assert.That(snapshot.CapturedVariables.Select(v => v.Name), Is.EquivalentTo(new[] { "speed" }));
Assert.That(_pauseController.PauseCount, Is.EqualTo(1));
}

private sealed class FakePausePointPauseController : IUloopPausePointPauseController
{
public int PauseCount { get; private set; }
public bool IsPlaying => true;
public bool IsPaused => PauseCount > 0;

public void Pause()
{
PauseCount++;
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading