1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Cross-engine regression tests for the `now-ms` builtin.
//
// `now-ms` returns the current Unix epoch in milliseconds as f64. Before
// the addition, the only timer was `now` (seconds), which was too coarse
// for sub-second perf bisection — agents had to manually trim programs
// and re-run, paying 5x the trial cost on every perf-regression hunt.
//
// These tests pin parity across tree / VM / Cranelift:
// - returns a positive number (epoch starts 1970)
// - is monotonic across a `sleep` boundary
// - the delta over a known sleep is non-zero
// - returns a numeric type the rest of the arithmetic surface accepts
//
// The python codegen path is exercised separately by the round-trip
// test in `src/builtins.rs` (name registry) and the snapshot tests under
// `src/codegen/python.rs`.
use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run_ok(engine: &str, src: &str, entry: &str) -> String {
let out = ilo()
.args([src, engine, entry])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} {src:?} unexpectedly failed: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
#[cfg(feature = "cranelift")]
const ENGINES_ALL: &[&str] = &["--vm", "--jit"];
#[cfg(not(feature = "cranelift"))]
const ENGINES_ALL: &[&str] = &["--vm"];
#[test]
fn now_ms_positive_cross_engine() {
// Unix epoch is always positive — cheap deterministic smoke test.
let src = "f>b;>now-ms 0";
for engine in ENGINES_ALL {
assert_eq!(
run_ok(engine, src, "f"),
"true",
"{engine}: now-ms must return a positive number"
);
}
}
#[test]
fn now_ms_after_sleep_is_monotonic_cross_engine() {
// Two samples with a `sleep` between must satisfy `t1 >= t0`. The
// bare comparison `>=t1 t0` would fire as a guard, so bind first.
let src = "f>b;t0=now-ms;sleep 10;t1=now-ms;r=>=t1 t0;r";
for engine in ENGINES_ALL {
assert_eq!(
run_ok(engine, src, "f"),
"true",
"{engine}: now-ms must not move backwards across sleep"
);
}
}
#[test]
fn now_ms_delta_after_sleep_is_at_least_a_few_ms_cross_engine() {
// After `sleep 20`, the elapsed should be >= 10ms in practice on any
// reasonable host. We assert the lower-bound that the OS sleep takes
// *some* observable time, not an exact value, so the test isn't
// flaky on noisy CI runners. The point is that `now-ms` is fine
// enough resolution to see this at all (which `now` in seconds is
// not).
let src = "f>b;t0=now-ms;sleep 20;t1=now-ms;dt=-t1 t0;>=dt 10";
for engine in ENGINES_ALL {
assert_eq!(
run_ok(engine, src, "f"),
"true",
"{engine}: now-ms delta after `sleep 20` should be at least 10ms"
);
}
}
#[test]
fn now_ms_returns_number_arith_compatible_cross_engine() {
// The verifier types `now-ms` as `n`, so it must compose with the
// numeric arithmetic surface. `+now-ms 0` is the identity and
// should equal `now-ms` itself within a small (sub-ms) window.
// We assert non-negative and integer-truncated for printing.
let src = "f>b;v=+now-ms 0;>=v 0";
for engine in ENGINES_ALL {
assert_eq!(
run_ok(engine, src, "f"),
"true",
"{engine}: now-ms must compose with `+` as a number"
);
}
}
#[test]
fn now_ms_is_finer_grained_than_now_seconds_cross_engine() {
// Sanity: `now-ms` returns ~1000x the seconds value. Compute the
// ratio's floor and assert it's in [900, 1100] to leave room for
// the sample skew between the two calls. This is the property
// that motivates the builtin — `now` alone cannot observe a
// sub-second phase boundary.
let src = "f>b;ms=now-ms;s=now;r=/ms s;>=r 900";
for engine in ENGINES_ALL {
assert_eq!(
run_ok(engine, src, "f"),
"true",
"{engine}: now-ms should be ~1000x now-seconds"
);
}
}