Chrono Mock

Command line reference

Every flag of both commands, both exit code tables, and how to read what comes back. The narrative version, with the reasoning, is on the CLI and CI page.

Two commands

chrono run starts an application at a different date and time. chrono calc works out a date and tells you what makes it interesting. They share one step grammar, so a shift written for one means the same in the other.

chrono version answers a third and much smaller question: which build this is, which of the two cores the executable is, and the protocol version it speaks. One line on stdout, so a script can capture it.

chrono run <target> [options]
chrono calc [options]
chrono version        # chrono 0.1.0 (x64, protocol 1)
chrono --help          # usage for all of them

chrono run - options

FlagWhat it does
--at <moment> The moment to start at, in the session zone. Absolute (2038-01-19T03:14:07) or relative with a leading sign (+1d, -2h). Units: s m h d w mo q y - m is minutes and mo is months. Without this flag the session clock starts at the real current time, so --mode xN on its own simply runs the target faster.
--zone +HH:MM The session zone: the zone the moment is written in, and the one the target's local-time calls report. A fixed offset, with no daylight saving. Without it, the host's own zone.
--mode flow|frozen|xN flow runs at real speed, frozen holds the clock still, xN runs it N times faster (x60 turns an hour into a minute, x1440 a day).
--scale-duration Also speed up the duration axis: tick counts, sleeps and timers. Off by default, because a wall clock and a stopwatch are different questions.
--scale-qpc Also scale the high-resolution counter, which is where Python 3.13+ monotonic, .NET Stopwatch and Java nanoTime read elapsed time. Off by default: an application that times its rendering off that counter can look distorted.
--ticks N End the session after N heartbeats, one real second each, and report. Without it the tool stays attached until the target exits.
--timeout <s> Give up after this many seconds and exit 6, for a pipeline that must not hang. Independently of this flag, a core that says nothing for 15 seconds is stopped and also exits 6 - in both cases the run is cut short, so it carries no verdict.
--args "..." Arguments for the target, as one string, split by the same rule Windows uses - so an argument containing spaces survives. For a Chromium target, --user-data-dir and --remote-debugging-port are refused rather than overridden: the session needs its own isolated profile, and quietly running on your real browser profile is the one thing that must not happen.
--cwd <dir> Start the target in that directory. Without it the target inherits ours, which is what every run did before this flag existed. An empty value is a usage error rather than "no directory" - absent and empty are different things, and only absent is something Windows can be given. A directory that does not exist stops the session with target.cwd_missing before anything is launched, instead of surfacing later as a target that would not start.
--preset <id> Take the moment and the speed from presets/<id>.json. Exclusive with --at, --mode and --scale-duration, since the preset is then the single source of both.
--param id=value Fill a preset parameter. Repeatable, requires --preset. In run only, a trial start date defaults to the target executable's own creation date, so a trial preset counts from the day the application was installed without you typing it.
--set-after T:M At heartbeat T, change the speed to M. In flight the multiplier is at least 1 - freezing mid-session is not offered here.
--jump-after T:moment At heartbeat T, jump the clock to that moment, absolute or relative, in the session zone.
--force Keep the session even when the opening verdict says the substitution did not take effect - by default the target is stopped instead, because a session that proved nothing is worse than no session. The verdict does not change, and neither does the exit code. This only decides whether the target keeps running.
--report <path> Write the human report to a file. A verdict other than works leads that file with an unreliable-evidence banner, because evidence that hides its own doubt is worse than none.
--json Emit the session as machine-readable records instead of the report - the same stream the window itself consumes.

chrono run - exit codes

The exit code is the session verdict, not what the application returned. The application's own exit code is reported separately, in the report and in the session data - the CLI and CI page explains why round that way.

CodeMeaning
0works - every process that read time saw the session clock
10works partially - some channels covered, some not
11does not work - the substitution did not reach the key channels
4undetermined - coverage could not be established. An honest "cannot tell", never a pretended pass
12the target vanished right after injection, which suggests a single-instance application handing over to a copy already running
1usage error - a bad flag, a bad expression, or a preset this command will not accept
2the target could not be launched or injected
3internal error - the engine could not start or hit an unexpected state
5the operation is not built in this release - for example a preset whose moment needs a calendar, which run cannot load yet. Deliberately distinct from a usage error: you asked for something that does not exist yet, not for something wrong
6the run was cut short - --timeout expired, or the engine stopped answering for 15 seconds and was stopped. This is not a verdict: the session was interrupted, so it proves nothing about the target either way

Reading the report

Without --json the session ends in a plain report. Every line is either a fact the session observed or absent - nothing is filled in with a plausible default.

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):
            - 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 - the family verdict over the parent and every child process, with the process count. This is the number the exit code carries.
  • exited - the application's own exit code, and only when it closed itself. A run bounded by --ticks leaves the target running, a Chromium session is one the tool closes itself, and a session that never started has no such code - the line is then absent rather than showing a made-up zero. A crash code arrives as a large negative number, so it also gets its hexadecimal form.
  • covered - channels the substitution reached, with how many times each was actually called, per process. Counts are never summed across processes: one process's audit must not borrow another's numbers.
  • observed - channels hooked but deliberately left real, such as object waits and multimedia timers. Their own bucket, so they are never read as substituted.
  • uncovered - channels the application asked for and the session did not cover. This is what turns a verdict into partially.
  • not fully cleaned up - anything teardown could not remove. Absent on a clean end, which is the normal case.

chrono calc - options

No natural language: each flag is one step, and the order of the flags is the order of the steps. The result after every step is printed, so a wrong step is visible rather than buried.

FlagWhat it does
--base today|now|<moment> Where to start, default today. Resolved in the session zone. An impossible day such as 2025-02-31 is refused, never quietly normalised into the next month.
--shift ±N<unit> Repeatable. The sign is required. Units: s m h d w mo q y bd, or their full names. m is minutes, mo is months. Months, quarters and years fold onto the calendar date with a clamp, so a month after 31 January is the end of February, not a fixed number of ticks. bd (business days) needs --calendar.
--set-time HH:MM:SSSet the time of day on the current result.
--snap <target> Jump to a period boundary: som eom soq eoq soy eoy (start or end of month, quarter, year). A start is the first day at 00:00:00, an end the last day at 23:59:59.
--nearest nbd|pbd The nearest business day forward or back, the current day included when it already is one. Needs --calendar.
--to-zone +HH:MM A step: re-express the same instant in another fixed offset. The instant is preserved, so the epoch value does not move and only the civil fields do. Not to be confused with --zone.
--zone +HH:MM The session zone, used to resolve today and now and to render the result. Without it, the host's zone.
--calendar us-banking|us-federal|pl Load a holiday calendar. It adds business day and holiday to the metadata and enables bd and --nearest. The two American calendars differ in how they observe a holiday falling at a weekend, which is exactly the kind of off-by-one day a test is meant to find.
--preset <id> --param id=value A named moment from presets/<id>.json, with its parameters filled in. Exclusive with the step flags and with --analyze, and it composes with --calendar, --zone and --format.
--analyze <date> Read a pasted date back and say what it is. An ambiguous 04/08 is shown in both readings, American and Polish, rather than guessed - that ambiguity is a real source of wrong bug reports.
--format <mask> Print the result in your own mask as well, so it matches the format of the application under test: yyyy-MM-dd HH:mm:ss. Tokens are case sensitive in the .NET and Java sense - M is month, m is minute.
--json Machine output (chronomock.calc/1) for any of the above, so a pipeline can compute a date and feed it straight back into chrono run.
# Last business day of the quarter, American banking calendar
chrono calc --base today --snap eoq --nearest pbd --calendar us-banking

chrono calc - exit codes

A separate, smaller table: the calculator has no coverage verdict to report.

CodeMeaning
0computed
1usage error - a bad flag, an impossible date, a range overflow, or a calendar file with no business day in it
5the step or unit is not built in this release, or the step needs a calendar you did not pass. Separate from a usage error on purpose, so a script can tell "not yet" from "wrong"

Next