Date testing in a pipeline
The window is for exploring. This is the other half: a command-line build of about 1.6 MB with no runtime to install, whose exit code is the coverage verdict rather than the target's own.
The exit code is the verdict
This is the design decision that makes the tool scriptable. When a session ends, the exit code answers "did the time substitution take effect", not "what did the application return". A pipeline can therefore fail on a substitution that silently did not happen - which is the failure worth failing on, because everything downstream of it is a test that proved nothing.
The target's own exit code is still reported, in the session data.
| Code | Meaning |
|---|---|
| 0 | works - every process that read time saw the session clock |
| 10 | works partially - some channels covered, some not |
| 11 | does not work - the substitution did not reach the key channels |
| 4 | undetermined - coverage could not be established |
| 1 | usage error - a bad flag or a bad expression |
| 2 | the target could not be launched or injected |
| 12 | the target vanished, suggesting a single-instance application |
| 3 | internal error - the engine could not start or hit an unexpected state |
| 5 | the operation is not built in this release |
Every flag of both commands, and the same table for the calculator, are on the command line reference.
Bounded runs
A pipeline cannot wait for a desktop application to be closed by a human.
--ticks N ends the session after N heartbeats - one real second each -
and reports what it found up to that point. Coverage is established in the first
moments of the process anyway, so a short bound is usually all that is needed.
# Ten seconds, then stop and report chrono run ".\build\test.exe" --preset year-rollover --ticks 10 --report evidence.txt
Machine output and evidence
--jsonemits the session as machine-readable records rather than the human report - the same facts, including the per-channel coverage.--report <path>writes the human report to a file, which makes a reasonable build artefact. A session whose verdict is anything but works leads that file with an unreliable-evidence banner rather than burying the caveat at the bottom.- The calculator speaks
--jsontoo, so a pipeline can compute the date it needs and feed it back in.
# Work out the date, then run at it chrono calc --base today --snap eom --json chrono run ".\build\test.exe" --preset month-end --ticks 10 --json
A step, roughly
- name: Month-end behaviour
shell: pwsh
run: |
.\chrono.exe run ".\build\test.exe" --preset month-end --ticks 10 --report evidence.txt
# a non-zero exit here means the substitution did not take, so the
# test below it would have proven nothing
- name: Keep the evidence
uses: actions/upload-artifact@v7
with:
name: chrono-evidence
path: evidence.txt
Two honest caveats about runners
- It needs a Windows runner. There is no Linux or macOS build, and there will not be - on Linux, libfaketime already covers this.
- Antivirus on a hosted runner may block the injection. Shifting time means injecting a library into the target process, which is a documented Windows technique that malware also uses. A runner that blocks it produces an exit code saying so rather than a false pass - but it is a thing to find out before you depend on it.
For the same reason, this project's own end-to-end suite runs on real machines rather than on a hosted runner. Its continuous integration builds and unit-tests everything, and the substitution itself is exercised by hand against real applications on both 32-bit and 64-bit builds before a release.