GGua 日本語リファレンス
英語版が正式な参照です。内容に差異がある場合は、英語版を優先してください。
GUIDE / RECORDING

Recording実践

Gua.Testing.Recordingを使うと、click、値入力、選択、key操作などを座標ではなくSemantic actionとして保存し、回帰テストや不具合再現で繰り返し再生できます。固定delayだけのmacroでは不安定になる複数stepのユーザーフローを、host完了確認と状態wait付きで再現するために必要なgame engine非依存の機能です。

実際の利用場面

場面Recordingの価値
ログイン→設定→保存の回帰flow複数操作を1つのversion管理可能なscenarioへまとめる
不具合の再現手順を共有座標説明ではなく安定したID・roleで再現する
同じflowをGodot/Unityのfixtureで実行IGuaContextに対してengine非依存で再生する
passwordを含む操作平文を保存せずsecretKeyだけを記録する
Semantic targetID / role / focus→Enqueue actionrequestIdを発行→Host completion同じrequestId→recording.jsontarget・timing・revision→Replay + wait各stepを再解決
図1: queueへの追加だけを成功扱いせず、host完了eventまで確認してからstepを記録・再生します。

導入

scenario test projectへ追加powershell
dotnet add package Gua.Testing.Recording

ゲーム側と関連Guaパッケージの版を揃えます。取得後の版の記録・固定はバージョンの管理を参照してください。

操作を記録する

ログインflowを記録csharp
using Gua.Testing.Recording;

var recorder = new GuaRecorder(host.Context);

await recorder.ClickAsync(
    new GuaRecordingTarget(Id: "open-login"));

var emailTarget = new GuaRecordingTarget(
    Role: "textbox", Name: "Email", Scope: "login-dialog");
await recorder.SetValueAsync(
    emailTarget,
    "player@example.com",
    waitCondition: GuaWaitConditions.Visible("email"));

var passwordTarget = new GuaRecordingTarget(Id: "password");
await recorder.SetValueAsync(
    passwordTarget,
    password,
    sensitive: true,
    secretKey: "login-password");

await recorder.FocusAsync(passwordTarget);
await recorder.PressKeyAsync("Enter");
GuaRecordingFile.Save("recordings/login.json", recorder.Recording);

各stepのwaitConditionは、そのstepがtargetを解決してactionをenqueueする前に評価されます。この例では先行するclickの後、email stepがdialog内のemail nodeを待ちます。また、SetValueAsyncはtargetをfocusしないため、target未指定のPressKeyAsyncでEnterを現在focusへ送る前にpasswordを明示的にfocusします。GuaRecorderはこれらのaction、monotonicな相対時刻、操作前後のrevision、request IDを保存します。

targetの選び方

target適する場面注意
Idゲーム側に安定IDがある最優先。scene再構成でも変えない
Role + Nameaccessible nameで一意に特定できる同名がある場合はScopeを追加
CurrentFocus現在focus中のControlへkey入力press_keyだけで利用可能
座標fallbackSemantic targetが存在しないlegacyデータ既定で再生拒否。通常は使わない

固定delayよりconditionを使う

recordingにconditionがあるstepは、既定のPreferConditionsで記録時delayの代わりに状態を待ちます。対応conditionはvisible、hidden、enabled、disabled、focused、unfocused、checked、unchecked、text、valueです。

代表的なwait conditioncsharp
GuaWaitConditions.Visible("login-dialog")
GuaWaitConditions.Enabled("submit")
GuaWaitConditions.Text("status", "Ready")
GuaWaitConditions.Value("volume", "0.8")

秘密値を注入して再生する

保存済みflowを再生csharp
var recording = GuaRecordingFile.Load("recordings/login.json");

var result = await GuaReplayer.ReplayAsync(
    host.Context,
    recording,
    new GuaReplayOptions
    {
        TimingMode = GuaReplayTimingMode.PreferConditions,
        SecretResolver = key => key == "login-password" ? password : null,
        ActionTimeout = TimeSpan.FromSeconds(5),
        PollInterval = TimeSpan.FromMilliseconds(50),
    });

sensitiveなset_valueはrecording JSONへ値を書かず、secretKeyだけを保存します。再生時にSecretResolverが値を返さなければ失敗します。secretをlogやartifactへ出力しないresolverをtest側で用意してください。

再生時の安全境界

  • 各stepでSemantic targetを現在のUI Treeから解決し直します。一意でなければ失敗します。
  • 各actionは同じrequest IDのhost完了eventまで待ち、無関係なeventを消費しません。
  • 座標fallbackはFailOnCoordinateFallback = trueが既定です。許可する場合もcaller提供のCoordinateExecutorが必要です。
  • recording schema version、timing順序、revision、secret leak、action引数はload時に検証されます。

retained diagnosticsから取り込む

既にruntime diagnosticsへoperation/event historyが残っている場合、GuaRecordingFile.ImportDiagnosticsでrequest IDが一致するenqueueとcompletionを対応付けられます。結果にはpaired request数、unpaired step数、古いpayloadのsynthetic timingを使ったかどうかが含まれます。

diagnostics historyをrecordingへ変換csharp
var imported = GuaRecordingFile.ImportDiagnostics(context.GetDiagnosticsJson());

if (imported.UnpairedStepCount != 0)
    throw new InvalidOperationException("Diagnostics contains unpaired operations.");

GuaRecordingFile.Save("recordings/imported.json", imported.Recording);

有限のゲーム入力区間を再生する

通常のReplayAsyncは各stepのcompletionを待ってから次へ進みます。有限の入力offsetを扱う場合は、Gua.Testing.RecordingのGuaReplayer.ReplayTimedSegmentAsyncをIGuaTimedSegmentHostと明示的に使います。同じパッケージのGuaRuntimeSegmentHostは、初期化済みの順序付きgame-input pumpを持つlocal Gua.Runtimeに接続します。

hold、jump、明示releaseを行うcsharp
using Gua.Runtime;
using Gua.Testing.Recording;

// runtime is local; its initialized adapter applies game input through a FIFO pump.
var segmentHost = new GuaRuntimeSegmentHost(runtime, adapterAppliesInOrder: true);
var segment = new GuaTimedSegment(
    SchemaVersion: 1, DurationMilliseconds: 300, MaxLatenessMilliseconds: 50,
    ExecutionTimeoutMilliseconds: 1000, CleanupTimeoutMilliseconds: 500,
    Inputs: [
        new(0, GuaGameInputKind.Keyboard, GuaGameInputOperation.Down,
            "KeyW", LeaseMilliseconds: 5000),
        new(100, GuaGameInputKind.Keyboard, GuaGameInputOperation.Press, "Space"),
        new(300, GuaGameInputKind.Keyboard, GuaGameInputOperation.Up, "KeyW"),
    ]);
var result = await GuaReplayer.ReplayTimedSegmentAsync(segmentHost, segment);
if (result.Outcome != GuaSegmentOutcome.Succeeded || !result.NeutralConfirmed)
    throw new InvalidOperationException("Timed input or cleanup failed.");

offsetは区間開始からの時間です。実行/cleanupの期限を指定し、holdを明示releaseで閉じ、結果と中立確認を両方調べます。同梱hostはrealtime FIFOのみで、厳密な適用時刻や同tick適用を証明しません。UI actionと条件waitは区間の外で行います。組み込みMCP/InspectorのReplayは逐次実行で、Timed Segmentの実行は提供しません。plan保存・読込・変換と時間契約を参照してください。

RecordingをTraceへ添付する

GuaRecordingTrace.Attachは検証したRecordingをGua.Testing.GuaTraceSessionのstepへ明示的に添付します。AttachTimedResultは値を含まない時間結果を添付します。証拠の添付であって再生可能性の保証ではなく、Traceはこれを再生しません。Traceの作成・保存済み証拠の表示を参照してください。