diff --git a/Assets/Tests/Editor/PausePointTests.cs b/Assets/Tests/Editor/PausePointTests.cs
index b9285d962d..6338740356 100644
--- a/Assets/Tests/Editor/PausePointTests.cs
+++ b/Assets/Tests/Editor/PausePointTests.cs
@@ -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()
{
diff --git a/Assets/Tests/Editor/SourcePausePointCapture.meta b/Assets/Tests/Editor/SourcePausePointCapture.meta
new file mode 100644
index 0000000000..62983de051
--- /dev/null
+++ b/Assets/Tests/Editor/SourcePausePointCapture.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 52fc3ba85d1f42d5b6b3b8dc71c0b959
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tests/Editor/SourcePausePointCapture/SourcePausePointCaptureTests.cs b/Assets/Tests/Editor/SourcePausePointCapture/SourcePausePointCaptureTests.cs
new file mode 100644
index 0000000000..403d2528fb
--- /dev/null
+++ b/Assets/Tests/Editor/SourcePausePointCapture/SourcePausePointCaptureTests.cs
@@ -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
+{
+ ///
+ /// Verifies the Harmony-injected landing point: the armed fast-path no-op and the
+ /// formatted-variables handoff into the registry.
+ ///
+ [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