Chrono Mock
言語日本語

コマンドラインリファレンス

2 つのコマンドのすべてのフラグ、2 つの終了コード表、そして返ってくる内容の読み方。経緯や理由を含めた解説版は CLI と CI のページにあります。

2 つのコマンド

chrono run はアプリを別の日時で起動します。chrono calc は日付を計算し、その日付がなぜ興味深いのかを教えます。両者はステップの文法を共有しているので、一方で書いたずらし方は他方でも同じ意味になります。

chrono version は、3 つ目のずっと小さな問いに答えます。これはどのビルドか、実行ファイルは 2 つのコアのどちらか、どのバージョンのプロトコルを話すか。標準出力に 1 行だけ出力するので、スクリプトで取り込めます。

chrono license は 4 つ目に答えます。このビルドが配布されているライセンス、保証の免責、リンクされているサードパーティ製コンポーネント。ライセンス全文とサードパーティの通知がディスク上のどこにあるかも示し、実行中のコピーの隣にそれらが見当たらなければ、その旨を伝えます。 --components を付けると、要約の代わりに同梱されているすべてのコンポーネントをバージョンとともに一覧表示します。

chrono presets は、--preset が読むとおりにシナリオのカタログを一覧表示します。フォルダー内のすべてのシナリオを計算機の文法が読む言葉で示し、拒否されるファイルがあれば、その理由とともに示します。--json は chronomock.presets/1 を出力し、--dir で別のフォルダーを指定できます。

chrono run <target> [options]
chrono calc [options]
chrono version        # chrono 0.4.0 (x64, protocol 1)
chrono license        # GPL-3.0-only と、同梱されているもの
chrono presets        # --preset が読むとおりのカタログ
chrono --help          # すべてのコマンドの使い方

chrono run - オプション

フラグ動作
--at <moment> 開始する時点。セッションのタイムゾーンで解釈されます。絶対指定(2038-01-19T03:14:07)か、先頭に符号を付けた相対指定(+1d、-2h)。単位は s m h d w mo q y で、m は分、mo は月です。このフラグを省略すると、セッションの時計は実際の現在時刻から始まるため、--mode xN だけを指定すれば対象を単に速く動かすことになります。
--zone +HH:MM セッションのタイムゾーン。時点を記述するときのゾーンであり、対象のローカル時刻の呼び出しが返すゾーンでもあります。夏時間のない固定オフセットです。省略した場合はホスト自身のタイムゾーンになります。
--mode flow|frozen|xN flow は実時間の速度、frozen は時計を止め、xN は N 倍の速度で進めます(x60 で 1 時間が 1 分に、x1440 で 1 日が 1 分になります)。
--scale-duration 経過時間の軸も速めます。ティック カウント、割り込み時間、スリープ、タイマーが対象です。オブジェクト待機は回数を数えるだけで、長さは実際のままです。壁時計とストップウォッチは別の問いなので、既定ではオフです。日時を指定したタイマーや遅延は、このオプションの有無にかかわらずセッションの時計に従います。
--scale-qpc 高分解能カウンターもスケールします。Python 3.13 以降の monotonic、.NET の Stopwatch、Java の nanoTime は経過時間をここから読みます。既定ではオフです。このカウンターで描画時間を測っているアプリは、挙動がおかしく見えることがあります。
--ticks N N 回のハートビート(1 回が実時間で 1 秒)でセッションを終了し、報告します。指定しない場合、対象と、対象が起動したセッションの時計で動くプログラムがすべて終了するまで、ツールは接続を保ちます。すぐ終了してしまうランチャーは、起動したアプリにセッションを引き継ぎます。
--timeout <s> 指定した秒数が経過したら諦めて終了コード 6 で終了します。ハングしてはならないパイプライン向けです。このフラグとは別に、15 秒間何も応答しないコアは停止され、同じく 6 で終了します。どちらの場合も実行は途中で打ち切られたため、判定はありません。
--args "..." 対象に渡す引数を 1 つの文字列で指定します。Windows と同じ規則で分割されるので、空白を含む引数もそのまま渡ります。Chromium の対象では --user-data-dir と --remote-debugging-port は上書きされずに拒否されます。セッションには専用の隔離されたプロファイルが必要で、気づかないうちに実際のブラウザープロファイルで動いてしまうことだけは絶対に避けなければならないためです。
--cwd <dir> 対象をそのディレクトリで起動します。省略すると、このフラグが登場する前のすべての実行と同様に、対象はこちらのディレクトリを引き継ぎます。空の値は「ディレクトリなし」ではなく使い方の誤りです。未指定と空は別物で、Windows に渡せるのは未指定だけだからです。存在しないディレクトリを指定すると、何かを起動する前に target.cwd_missing でセッションが停止します。そうしないと、あとから「起動しない対象」として表に出てきてしまいます。
--preset <id> 時点と速度を presets/<id>.json から取得します。--at、--mode、--scale-duration とは併用できません。シナリオがその両方の唯一の情報源になるためです。
--param id=value シナリオのパラメーターを埋めます。繰り返し指定でき、--preset が必要です。run に限り、試用期間の開始日は既定で対象の実行ファイル自身の作成日になるため、試用期間シナリオは、入力しなくてもアプリをインストールした日から数え始めます。
--set-after T:M ハートビート T で速度を M に変更します。実行中の倍率は 1 以上で、セッション途中での停止はここでは提供されません。
--jump-after T:moment ハートビート T で、時計をその時点(絶対または相対、セッションのタイムゾーン)へジャンプさせます。
--force 最初の判定が「時刻の置き換えは効いていない」であっても、セッションを維持します。既定では対象が停止されます。何も証明しないセッションは、セッションがないよりも悪いためです。判定は変わらず、終了コードも変わりません。対象を動かし続けるかどうかだけを決めます。
--no-embedded アプリケーション内部のウェブページ(WebView2、Qt WebEngine)を実際の時計のままにし、デバッグポートを開きません。付けない場合、セッションはこの種のエンジンにローカルポートを開くよう求め、それを通じてそのページをセッションの時計に載せます。ポートが開いている間は、このコンピューター上の他のどのプログラムもそれを使ってそれらのページに到達できます。
--elevated-embedded 管理者として実行されるアプリケーション向けで、同じく管理者として実行している Chrono Mock から使います。このようなアプリケーションでは WebView2 がセッションがページに到達するための設定を無視するため、セッションは実行中だけコンピューターのレジストリ(HKLM、アプリケーションの .exe の名前)に値を 1 つ書き込み、終了時に削除します。アプリケーションの実行中は、このコンピューター上の他のどのプログラムもそのポートを通じてページに到達でき、アプリケーションは管理者権限を持っています。--no-embedded とは併用できません。
--keep-background-timers ウェブエンジン(Chromium または Electron アプリケーションのブラウザー、またはアプリケーション内部のエンジン)を、非表示ウィンドウでのタイマーの減速を無効にして起動します。そのため最小化したウィンドウもセッションの速度を保ちます。その結果、アプリケーションは非表示の間も全速で動作します。 --elevated-embedded で管理者として起動したアプリケーションには、この設定は適用されません。
--dry-run セッションがどうなるかを表示するだけで、何も起動しません。解決された時点とタイムゾーンとその出どころ、シナリオが各パラメーターに入れた値、分割後の引数、対象が使う 2 つの仕組みのどちらか。プロセスは起動せず、--report で指定されたものを含め、ファイルも書き込みません。実行可能な計画なら 0、対象が存在しなければ 2 で終了します。これは実際の実行が同じ事実に対して返すコードと同じです。計画には判定がなく、その旨が表示されます。判定があるのは実際のセッションだけです。 --json を付けると計画がデータで出力され、session.elevated_embedded と session.elevated_embedded_value も含まれます。後者は、このオプションが .exe の対象に適用される場合にレジストリ値の名前の元になるファイル名で、それ以外は null です。名前があっても書き込みが行われる保証ではなく、セッションが値を書き込むのは Chrono Mock 自身が管理者として実行されているときだけです。
--report <path> 人間向けのレポートをファイルに書き出します。判定が有効以外の場合、ファイルの先頭に「信頼できない証拠」のバナーが付きます。自らの疑わしさを隠す証拠は、ないほうがましだからです。判定が有効のセッションでも、セッションが置き換える時計をアプリケーションが一つも読まなかった場合や、アプリケーションが Go のプログラムで、その壁時計に Chrono Mock が届かない場合は、このバナーが付きます。ファイルには実行そのものも記録されます。バージョン、開始時刻、対象、そのフォルダー、フラグです。
--report-args --report と併用すると、対象の引数もそのファイルにそのまま書き込みます。既定では無効です。引数にパスワードが含まれることがあるためで、その場合ファイルには引数の数だけが記載されます。
--json レポートの代わりに、機械可読なレコードとしてセッションを出力します。ウィンドウ自身が使うのと同じストリームです。コンソールアプリ自身の出力は標準エラーへ流れるため、標準出力にはレコードだけが入ります。セッション終了時にまだ動いているアプリはそこへ書き続けるので、標準エラーを最後まで読むものはそれを待つことになり、実行時にその旨が表示されます。

chrono run - 終了コード

終了コードはアプリが返した値ではなく、セッションの判定です。アプリ自身の終了コードは、レポートとセッションデータの中で別に報告されます。そうなっている理由は CLI と CI のページで説明しています。

コード意味
0有効 - 時刻を読んだすべてのプロセスがセッションの時計を見た
10一部有効 - カバーできたチャネルとできなかったチャネルがある
11無効 - 置き換えが主要なチャネルに届かなかった
4判定不能 - カバレッジを確定できなかった。正直な「分からない」であり、合格を装うことは決してありません
12対象がインジェクションの直後に消え、セッションの時計で動くものが何も残らなかった。単一インスタンスのアプリがすでに実行中のコピーへ処理を引き渡したか、セッションが入り込めないプログラム(たいていはビット数が異なるもの)へ引き渡したことを示唆します。アプリを起動して終了する対象はランチャーであり、セッションはそのアプリのために続きます
1使い方の誤り - フラグが不正、式が不正、またはこのコマンドが受け付けないシナリオ
2対象を起動できなかった、またはインジェクトできなかった
3内部エラー - エンジンを起動できなかった、予期しない状態に陥った、またはセッションを閉じる前に停止した(エンジン自身の終了コードにかかわらず)。このような実行には判定がありません
5この版ではその操作はビルドされていない - たとえば、時点にカレンダーが必要で run がまだ読み込めないシナリオ。使い方の誤りとは意図的に区別しています。間違ったことではなく、まだ存在しないことを頼んだためです
6実行が途中で打ち切られた - --timeout が経過した、またはエンジンが 15 秒間応答を止めて停止された。これは判定ではありません。セッションが中断されたため、対象についてどちらの方向にも何も証明しません
7別のセッションがすでに実行中です - セッションは一度に一つだけです。終了してから再試行すれば十分です
8セッションは判定に達しましたが、その証拠が --report で指定したファイルにありません - ディスクがいっぱいだったか、別のプログラムが古いファイルを開いたままにしていて、そのファイルは元のまま残ります。判定はコマンドが出力するレポートに引き続き含まれます

レポートの読み方

--json を付けない場合、セッションはプレーンなレポートで終わります。各行は、セッションが観測した事実であるか、存在しないかのどちらかです。もっともらしい既定値で埋められるものは何もありません。

Chrono Mock - session report
  target:   .\build\test.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:08
            real elapsed 1.3s, fake elapsed 1.3s
  exited:   the target closed itself with code 3
  covered channels (substituted, with call counts, 10 calls in all):
            - pid 21240: GetSystemTimeAsFileTime (5 calls)
            - pid 21240: GetLocalTime (5 calls)
  observed channels (hooked but left real):
            - pid 21240: NtCreateUserProcess (0 calls)
  warnings:
            - the target opened a network connection ...
  • verdict - 親プロセスとすべての子プロセスを合わせたファミリー全体の判定と、プロセス数。終了コードが表すのはこの数値です。
  • exited - アプリ自身の終了コード。アプリが自分で終了した場合に限られます。--ticks で区切った実行では対象は動いたままで、Chromium のセッションはツール自身が閉じ、開始しなかったセッションにはこのコードがありません。その場合は、でっち上げの 0 を表示せず、この行そのものが出ません。クラッシュコードは大きな負の数で届くため、16 進数の表記も併記されます。
  • covered - 置き換えが届いたチャネルと、プロセスごとにそれぞれが実際に何回呼ばれたか。回数はプロセス間で合算されません。あるプロセスの監査が、別のプロセスの数値を借りてはならないためです。回数は 1048576 で止まり、それ以降は少なくともその回数と表示されます。
  • observed - フックはされたが、意図的に実際の値のまま残したチャネル(オブジェクト待機やマルチメディアタイマーなど)。独自の区分に入れてあるので、置き換え済みと読まれることはありません。
  • uncovered - アプリが要求したが、セッションがカバーしなかったチャネル。判定を一部有効にするのはこれです。
  • not fully cleaned up - 終了処理で取り除けなかったもの。正常な終了では、つまり通常は、この行は出ません。

chrono calc - オプション

自然言語はありません。各フラグが 1 ステップであり、フラグの順序がそのままステップの順序です。各ステップのあとの結果が出力されるので、間違ったステップは埋もれずに見えます。

フラグ動作
--base today|now|<moment> 起点。既定は today です。セッションのタイムゾーンで解決されます。2025-02-31 のようなありえない日付は拒否され、黙って翌月に正規化されることはありません。
--base-utc <moment> --base と同様ですが、開始点はセッションのタイムゾーンの壁時計の読みではなく、UTC の一時点です。末尾に Z を付けても付けなくても構いません。
--shift ±N<unit> 繰り返し指定できます。符号は必須です。単位は s m h d w mo q y bd またはその正式名。m は分、mo は月です。月・四半期・年は暦上の日付に丸めて適用されるため、1 月 31 日の 1 か月後は、固定のティック数ではなく 2 月の末日になります。bd(営業日)には --calendar が必要です。
--set-time HH:MM:SS現在の結果の時刻部分を設定します。
--snap <target> 期間の境界へジャンプします: som eom soq eoq soy eoy(月・四半期・年の初めまたは終わり)。初めは最初の日の 00:00:00、終わりは最後の日の 23:59:59 です。
--nearest nbd|pbd 前方または後方で最も近い営業日。現在の日がすでに営業日なら、その日を含みます。--calendar が必要です。
--to-zone +HH:MM ステップです。同じ瞬間を別の固定オフセットで表し直します。瞬間は保たれるので、エポック値は動かず、暦上の各フィールドだけが変わります。--zone と混同しないでください。
--zone +HH:MM セッションのタイムゾーン。today と now の解決と、結果の表示に使われます。省略した場合はホストのタイムゾーンです。
--calendar us-banking|us-federal|pl 祝日カレンダーを読み込みます。メタデータに営業日と祝日が加わり、bd と --nearest が使えるようになります。2 つのアメリカのカレンダーは、週末に当たる祝日の扱い方が異なります。これはまさに、テストで見つけたい種類の 1 日のずれです。 各カレンダーが答える年の範囲があります。pl は 2002 年から、us-federal は 1960 年から、us-banking は 2008 年からです。営業日のステップがその範囲より前の日に達すると終了コード 1 で拒否され、範囲より前の日付は判定なしと表示されます。カレンダーの祝日を法令と照合してから 1 年以上たっている場合、それより後の日を判定した結果には calendar_outdated が付きます。新しい祝日が抜けている可能性があるためです。
--preset <id> --param id=value presets/<id>.json の名前付きの時点を、パラメーターを埋めて使います。ステップ用のフラグおよび --analyze とは併用できませんが、--calendar、--zone、--format とは組み合わせられます。
--analyze <date> 貼り付けた日付を読み戻し、それが何であるかを示します。曖昧な 04/08 は推測せず、アメリカ式とポーランド式の両方の読みを表示します。この曖昧さは、誤ったバグ報告の実際の原因だからです。
--format <mask> 結果を独自のマスクでも出力し、テスト対象アプリの形式に合わせます: yyyy-MM-dd HH:mm:ss。トークンは .NET や Java の意味で大文字小文字を区別します。M は月、m は分です。
--json 上記のいずれに対しても機械向けの出力(chronomock.calc/1)を返します。パイプラインが日付を計算し、そのまま chrono run に渡せます。
# 四半期の最終営業日、アメリカの銀行カレンダー
chrono calc --base today --snap eoq --nearest pbd --calendar us-banking

chrono calc - 終了コード

別の、より小さな表です。電卓には報告すべきカバレッジの判定がありません。

コード意味
0計算できた
1使い方の誤り - フラグが不正、ありえない日付、範囲の超過、または営業日がまったく含まれないカレンダーファイル
5そのステップや単位がこの版ではビルドされていない、またはステップに必要なカレンダーが渡されていない。使い方の誤りとは意図的に分けてあり、スクリプトが「まだ」と「間違い」を区別できるようにしています

次に