flan/test/programs/time.flan

77 lines
3.7 KiB
Plaintext

;;;; The clock and the environment.
;;;;
;;;; Every line of output here is an invariant and not a reading, and that is
;;;; forced rather than chosen: this file is in the corpus @x86 builds twice
;;;; and diffs, and the acceptance table matches its stdout exactly, so a
;;;; timestamp or an elapsed count would fail a correct compiler on the second
;;;; run. What is left is what a clock actually has to promise — that it does
;;;; not go backwards, that a sleep does not return early, that the two faces
;;;; of one clock describe one instant — and those are the properties worth
;;;; pinning anyway. A test that asserted "this took under 3ms" would be a
;;;; test of the machine's load.
(defn main [] i32
;; Monotonic, twice. The whole contract in one line: it never goes
;; backwards. Equal is allowed and is not a bug — two reads inside one tick
;; of a coarse timer are the same nanosecond.
(let [t1 (monotonic-ns)
t2 (monotonic-ns)]
(println (>= t2 t1)))
;; And the origin is the first read rather than boot, so the first readings
;; a program takes are small. Bounded rather than pinned, and the bound is
;; deliberately loose: what separates this clock from a boot-relative one is
;; hours, so a minute proves it and a second only proved it on hardware. The
;; tighter bound was a reading after all — memcheck runs this program on a
;; synthetic CPU tens of times slower than the metal, the second genuinely
;; elapsed, and the line went false under a tool that has nothing to say
;; about clocks. A minute is past anything a simulator adds and still short
;; of the smallest thing the assertion is meant to catch.
(println (< (monotonic-ns) (* 60 ns-per-second)))
;; The f64 face is the i64 one divided, and what is checked is that the two
;; describe the same instant: a later reading in seconds is at or past an
;; earlier reading in nanoseconds converted the same way. A clock whose two
;; faces came from different sources fails this.
(let [a (/ (f64 (monotonic-ns)) 1000000000.0)
b (monotonic-seconds)]
(println (>= b a)))
;; The wall clock is a date, so the invariant is a date one: it is after
;; 2020 and before 2100. That pins the epoch and the unit at once — a clock
;; counting microseconds, or counting from boot, fails both halves.
(let [now (unix-seconds)]
(println (and (> now 1577836800.0) (< now 4102444800.0))))
;; Sleep is specified as *at least*, so at-least is what is asserted; the
;; upper bound belongs to the scheduler and not to this language. Two
;; milliseconds because the shortest sleep a default kernel actually
;; performs is a timer tick, and a shorter request would make this a test of
;; how that kernel was configured.
(let [before (monotonic-ns)]
(sleep-ns (* 2 ns-per-millisecond))
(println (>= (- (monotonic-ns) before) (* 2 ns-per-millisecond))))
;; Zero and negative return at once rather than being refused, which is what
;; a deadline already passed produces. That they return at all is the
;; assertion; nothing here is timed.
(sleep-ns 0)
(sleep-ns -1)
(sleep-seconds 0.0)
(println "slept")
;; ── The environment ──────────────────────────────────────────────
;; A variable nothing sets. None is the answer, and it is a different answer
;; from a variable set to nothing.
(match (getenv "FLAN_NO_SUCH_VARIABLE_AT_ALL")
(Some v) (println "unexpectedly set")
None (println "unset"))
;; PATH is set for every process that gets as far as running this, and the
;; only portable thing about its contents is that there are some.
(match (getenv "PATH")
(Some v) (println (> (len v) 0))
None (println "no PATH"))
0)