lib/time+test: add types, ops, now
Replaces the lib/time placeholder (a monotonic(*timespec) shim that
predated lib/os's syscall surface). Ships Hare's time module first
cut per ref/hare/time/{duration,instant,arithm,+linux/functions}.ha:
- duration (i64 ns); nanosecond / microsecond / millisecond / second
constants.
- instant (sec, nsec) — Hare's layout, NOT POSIX's nsec:u32. Matches
Linux struct timespec on 64-bit, so &instant lands directly in
clock_gettime.
- clock enum: realtime + monotonic only. The rest of Hare's set
(process_cpu / thread_cpu / boot / realtime_alarm / boot_alarm / tai)
graduates when a caller actually needs it (rule 9).
- now(c: clock) instant — aborts on EINVAL/EFAULT (mirrors Hare's
abort-on-impossible-errno). Deliberately NOT (instant | oserror) to
sidestep task #9's 1-word-payload tagged-return trap.
- add / diff / compare on instants per ref/hare/time/arithm.ha
verbatim.
Deferred to follow-up commits when a caller surfaces: sleep, format /
strftime, time.chrono / time.date calendar, timezone, time.error
sum-return shape, time.unix helpers, time.mult, conversion helpers.
Tests at lib/time/timetest.ww (15 rows, table-pattern, semantic per
Class B doctrine). Driver test/wcc/977_time_run.c slotted between
976_stat_run and 978_intdiv_signed in the 9xx _run band.
examples/cmatrix migrates to the new API in the same commit (sole
pre-existing caller — bisect-clean per rule 11).
Class B bug #16 (sign-extend before IDIVQ) was surfaced by the
negative-duration rows during this work; landed 63332fe..4fa4bcf
before this commit. Rows 8-9 (addneg_noborrow / addneg_borrow) are
the load-bearing Class B exercisers; would have stayed silently wrong
pre-#16.
lib/time's own .s output has a cstage/wwstage label-counter skew in
add (cstage emits add_ct_7/_ce_8/_end_6 vs wwstage _6/_7/_5). Filed
as task #15; non-bootstrap-blocking since lib/time is outside the
selfhost toolchain transitive chain.
This commit is contained in:
6
Makefile
6
Makefile
@@ -252,7 +252,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_dyn_ww $(BIN)/test_selfcheck $(BIN)/test_at_test_ww \
|
$(BIN)/test_dyn_ww $(BIN)/test_selfcheck $(BIN)/test_at_test_ww \
|
||||||
$(BIN)/test_fmt_run $(BIN)/test_log_run $(BIN)/test_fnmatch_run \
|
$(BIN)/test_fmt_run $(BIN)/test_log_run $(BIN)/test_fnmatch_run \
|
||||||
$(BIN)/test_shlex_run $(BIN)/test_getenv_run $(BIN)/test_dirs_run \
|
$(BIN)/test_shlex_run $(BIN)/test_getenv_run $(BIN)/test_dirs_run \
|
||||||
$(BIN)/test_stat_run \
|
$(BIN)/test_stat_run $(BIN)/test_time_run \
|
||||||
$(BIN)/test_intdiv_signed \
|
$(BIN)/test_intdiv_signed \
|
||||||
$(BIN)/test_memio_run $(BIN)/test_temp_run $(BIN)/test_getopt_run \
|
$(BIN)/test_memio_run $(BIN)/test_temp_run $(BIN)/test_getopt_run \
|
||||||
$(BIN)/test_base32_run $(BIN)/test_base64_run \
|
$(BIN)/test_base32_run $(BIN)/test_base64_run \
|
||||||
@@ -585,6 +585,10 @@ $(BIN)/test_stat_run: test/wcc/976_stat_run.c $(BIN)/ww $(BIN)/w6c \
|
|||||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BIN)/test_time_run: test/wcc/977_time_run.c $(BIN)/ww $(BIN)/w6c \
|
||||||
|
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
$(BIN)/test_intdiv_signed: test/wcc/978_intdiv_signed.c $(BIN)/ww $(BIN)/w6c \
|
$(BIN)/test_intdiv_signed: test/wcc/978_intdiv_signed.c $(BIN)/ww $(BIN)/w6c \
|
||||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|||||||
@@ -189,12 +189,9 @@ fn setup() (*void | initerr) = {
|
|||||||
|
|
||||||
// ---- clock helper -----------------------------------------------------
|
// ---- clock helper -----------------------------------------------------
|
||||||
|
|
||||||
fn now_ns() (u64 | initerr) = {
|
fn now_ns() u64 = {
|
||||||
let ts: time.timespec; // uninitialised — keep the type for the shape
|
let i = time.now(time.clock.monotonic);
|
||||||
if (time.monotonic(&ts) != 0) {
|
let v = i.sec * 1000000000i64 + i.nsec;
|
||||||
return "clock_gettime failed": initerr;
|
|
||||||
};
|
|
||||||
let v = ts.sec * 1000000000i64 + ts.nsec;
|
|
||||||
return v: u64;
|
return v: u64;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -468,9 +465,9 @@ fn run(seed: u64) (i32 | initerr) = {
|
|||||||
// ---- entry ------------------------------------------------------------
|
// ---- entry ------------------------------------------------------------
|
||||||
|
|
||||||
export fn main() i32 = {
|
export fn main() i32 = {
|
||||||
// `!` — unwrap the success variant or abort. The clock is a
|
// Clock is a pre-init dependency; time.now aborts on
|
||||||
// pre-init dependency; if it fails there's nothing to tear down.
|
// syscall failure (EINVAL / EFAULT are programmer errors).
|
||||||
let seed = now_ns()!;
|
let seed = now_ns();
|
||||||
|
|
||||||
// match-as-expression: every arm `yield`s the exit code, and the
|
// match-as-expression: every arm `yield`s the exit code, and the
|
||||||
// whole match resolves to a single i32. One return site for the
|
// whole match resolves to a single i32. One return site for the
|
||||||
|
|||||||
@@ -1,15 +1,95 @@
|
|||||||
// time — clocks. We expose monotonic-ns via a syscall trampoline.
|
// time — clocks, instants, durations. Mirrors Hare's lib/time
|
||||||
// Linux clock_gettime is syscall 228; clock id 1 is CLOCK_MONOTONIC.
|
// (ref/hare/time/duration.ha, instant.ha, arithm.ha,
|
||||||
// Until we can pass struct-by-value the user supplies a buffer.
|
// +linux/functions.ha). Calendar / date / strftime / timezone /
|
||||||
|
// sleep live in separate Hare modules and graduate when callers /
|
||||||
|
// supporting stdlib arrive.
|
||||||
|
//
|
||||||
|
// `duration` is a NAMED alias of i64 (lib/math/random precedent
|
||||||
|
// at lib/math/random/random.ww:8); ww treats NAMED as a newtype,
|
||||||
|
// so cross-i64 arithmetic inside this module needs explicit casts.
|
||||||
|
// Hare's structural alias semantics let those casts vanish, but
|
||||||
|
// our type checker is strict.
|
||||||
|
|
||||||
@symbol("rt_syscall") fn syscall(num: i64, a: i64, b: i64, c: i64) i64;
|
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64;
|
||||||
|
@symbol("rt_abort") fn abort(msg: str) void;
|
||||||
|
|
||||||
def CLOCK_MONOTONIC: i64 = 1;
|
|
||||||
def SYS_CLOCK_GETTIME: i64 = 228;
|
def SYS_CLOCK_GETTIME: i64 = 228;
|
||||||
|
|
||||||
// timespec is {sec, nsec} — 16 bytes. Caller passes a pointer.
|
// ref/hare/time/duration.ha:6. 290y representable range.
|
||||||
type timespec = struct { sec: i64, nsec: i64 };
|
export type duration = i64;
|
||||||
|
|
||||||
export fn monotonic(ts: *timespec) i32 = {
|
// ref/hare/time/duration.ha:9-18. Plan-9 naming (lowercase)
|
||||||
return syscall(SYS_CLOCK_GETTIME, CLOCK_MONOTONIC, ts: i64, 0): i32;
|
// diverges from Hare's uppercase per project rule 4.
|
||||||
|
export def nanosecond: duration = 1i64;
|
||||||
|
export def microsecond: duration = 1000i64;
|
||||||
|
export def millisecond: duration = 1000000i64;
|
||||||
|
export def second: duration = 1000000000i64;
|
||||||
|
|
||||||
|
// ref/hare/time/instant.ha:9. (sec, nsec) pair — NOT POSIX struct
|
||||||
|
// timespec (which uses u32 nsec). Layout matches Linux's struct
|
||||||
|
// timespec on 64-bit (i64+i64) so we can pass &instant directly
|
||||||
|
// to clock_gettime.
|
||||||
|
export type instant = struct {
|
||||||
|
sec: i64,
|
||||||
|
nsec: i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/time/+linux/functions.ha:84. First cut exposes only
|
||||||
|
// realtime and monotonic; Hare's process_cpu / thread_cpu / boot /
|
||||||
|
// realtime_alarm / boot_alarm / tai graduate when a caller needs
|
||||||
|
// them (CLAUDE.md rule 9 — Hare-fidelity, no premature surface).
|
||||||
|
export type clock = enum i32 {
|
||||||
|
realtime = 0,
|
||||||
|
monotonic = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/time/+linux/functions.ha:138. Hare's now() also aborts
|
||||||
|
// on impossible errnos. (instant | oserror) is deliberately not
|
||||||
|
// the return shape — EINVAL / EFAULT are programmer errors (bad
|
||||||
|
// clock id, bad ptr), and a 1-word-payload sum return walks into
|
||||||
|
// task #9's cgen-divergence trap.
|
||||||
|
export fn now(c: clock) instant = {
|
||||||
|
let i: instant;
|
||||||
|
let rc = syscall2(SYS_CLOCK_GETTIME, (c as i32): i64, (&i): i64);
|
||||||
|
if (rc != 0i64) { abort("time.now: clock_gettime failed"); };
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/time/arithm.ha:9. Adds duration to instant. The
|
||||||
|
// negative-duration branch normalises nsec into [0, second).
|
||||||
|
export fn add(i: instant, x: duration) instant = {
|
||||||
|
let r: instant;
|
||||||
|
let xi: i64 = x: i64;
|
||||||
|
let sec: i64 = second: i64;
|
||||||
|
let nsec: i64 = nanosecond: i64;
|
||||||
|
if (xi == 0i64) {
|
||||||
|
r.sec = i.sec;
|
||||||
|
r.nsec = i.nsec;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (xi > 0i64) {
|
||||||
|
r.sec = i.sec + (i.nsec + xi) / sec;
|
||||||
|
r.nsec = (i.nsec + xi) % sec;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
r.sec = i.sec + (i.nsec + xi - sec + nsec) / sec;
|
||||||
|
r.nsec = (i.nsec + (xi % sec) + sec) % sec;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/time/arithm.ha:26. Returns duration from a to b.
|
||||||
|
// Sign convention: b - a.
|
||||||
|
export fn diff(a: instant, b: instant) duration = {
|
||||||
|
let sec: i64 = second: i64;
|
||||||
|
let v: i64 = ((b.sec - a.sec) * sec) + (b.nsec - a.nsec);
|
||||||
|
return v: duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/time/arithm.ha:32. -1 if a < b, 0 if equal, +1 if a > b.
|
||||||
|
export fn compare(a: instant, b: instant) i8 = {
|
||||||
|
if (a.sec < b.sec) { return -1i8; };
|
||||||
|
if (a.sec > b.sec) { return 1i8; };
|
||||||
|
if (a.nsec < b.nsec) { return -1i8; };
|
||||||
|
if (a.nsec > b.nsec) { return 1i8; };
|
||||||
|
return 0i8;
|
||||||
};
|
};
|
||||||
|
|||||||
180
lib/time/timetest.ww
Normal file
180
lib/time/timetest.ww
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
// timetest — exercises lib/time. Run with `out/bin/ww run
|
||||||
|
// lib/time/timetest.ww`. Same `signalled`-then-fail()-with-+10
|
||||||
|
// pattern as fmttest / logtest / stattest so a non-zero exit
|
||||||
|
// pinpoints the offending scenario.
|
||||||
|
|
||||||
|
use os;
|
||||||
|
use time;
|
||||||
|
|
||||||
|
let signalled: i32 = 0;
|
||||||
|
|
||||||
|
fn fail() void = { os.exit(signalled + 10); };
|
||||||
|
|
||||||
|
// ---- constants: load-bearing names + values ----------------------------
|
||||||
|
//
|
||||||
|
// Without these multipliers every caller would re-derive 1e9 in
|
||||||
|
// place. The values themselves are nailed down by Hare's
|
||||||
|
// ref/hare/time/duration.ha:9-18.
|
||||||
|
|
||||||
|
@test fn constants() void = {
|
||||||
|
if ((time.nanosecond: i64) != 1i64) { fail(); };
|
||||||
|
if ((time.microsecond: i64) != 1000i64) { fail(); };
|
||||||
|
if ((time.millisecond: i64) != 1000000i64) { fail(); };
|
||||||
|
if ((time.second: i64) != 1000000000i64) { fail(); };
|
||||||
|
|
||||||
|
// "Roundtrip" check: derived multiples land on the canonical
|
||||||
|
// nanosecond count. Catches a future re-derivation drift.
|
||||||
|
if (5i64 * (time.second: i64) != 5000000000i64) { fail(); };
|
||||||
|
if (3i64 * (time.millisecond: i64) != 3000000i64) { fail(); };
|
||||||
|
if (7i64 * (time.microsecond: i64) != 7000i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- now(monotonic) is monotonic across two calls ----------------------
|
||||||
|
|
||||||
|
@test fn nowmonotonic() void = {
|
||||||
|
let a = time.now(time.clock.monotonic);
|
||||||
|
let b = time.now(time.clock.monotonic);
|
||||||
|
// Two back-to-back calls — b can equal a (same ns tick) but
|
||||||
|
// must never precede it.
|
||||||
|
if (time.compare(a, b) > 0i8) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- now(realtime) returns a post-2020 epoch ---------------------------
|
||||||
|
//
|
||||||
|
// Sanity check that the syscall plumbing actually lands real bytes
|
||||||
|
// in the instant. 2020-01-01 UTC = 1577836800 unix seconds; any
|
||||||
|
// past-2020 wall clock satisfies this. (CI / test machines running
|
||||||
|
// pre-2020 would fail here; acceptable trade for catching a
|
||||||
|
// zero-init / wrong-slot regression.)
|
||||||
|
|
||||||
|
@test fn nowrealtime() void = {
|
||||||
|
let t = time.now(time.clock.realtime);
|
||||||
|
if (t.sec < 1577836800i64) { fail(); };
|
||||||
|
if (t.nsec < 0i64) { fail(); };
|
||||||
|
if (t.nsec >= 1000000000i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- add: zero duration is identity ------------------------------------
|
||||||
|
|
||||||
|
@test fn addzero() void = {
|
||||||
|
let a: time.instant;
|
||||||
|
a.sec = 100i64; a.nsec = 500i64;
|
||||||
|
let r = time.add(a, 0i64);
|
||||||
|
if (r.sec != 100i64) { fail(); };
|
||||||
|
if (r.nsec != 500i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- add: positive duration, no carry ----------------------------------
|
||||||
|
|
||||||
|
@test fn addpos_nocarry() void = {
|
||||||
|
let a: time.instant;
|
||||||
|
a.sec = 10i64; a.nsec = 100i64;
|
||||||
|
let r = time.add(a, 200i64);
|
||||||
|
if (r.sec != 10i64) { fail(); };
|
||||||
|
if (r.nsec != 300i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- add: positive duration, carries into next second ------------------
|
||||||
|
|
||||||
|
@test fn addpos_carry() void = {
|
||||||
|
let a: time.instant;
|
||||||
|
a.sec = 10i64; a.nsec = 999999900i64;
|
||||||
|
let r = time.add(a, 200i64);
|
||||||
|
if (r.sec != 11i64) { fail(); };
|
||||||
|
if (r.nsec != 100i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- add: full-second duration -----------------------------------------
|
||||||
|
|
||||||
|
@test fn addpos_fullsecond() void = {
|
||||||
|
let a: time.instant;
|
||||||
|
a.sec = 5i64; a.nsec = 0i64;
|
||||||
|
let r = time.add(a, (time.second: i64) * 3i64);
|
||||||
|
if (r.sec != 8i64) { fail(); };
|
||||||
|
if (r.nsec != 0i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- add: negative duration, no borrow ---------------------------------
|
||||||
|
|
||||||
|
@test fn addneg_noborrow() void = {
|
||||||
|
let a: time.instant;
|
||||||
|
a.sec = 10i64; a.nsec = 500i64;
|
||||||
|
let r = time.add(a, -100i64);
|
||||||
|
if (r.sec != 10i64) { fail(); };
|
||||||
|
if (r.nsec != 400i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- add: negative duration, borrows from previous second --------------
|
||||||
|
|
||||||
|
@test fn addneg_borrow() void = {
|
||||||
|
let a: time.instant;
|
||||||
|
a.sec = 10i64; a.nsec = 100i64;
|
||||||
|
let r = time.add(a, -200i64);
|
||||||
|
if (r.sec != 9i64) { fail(); };
|
||||||
|
if (r.nsec != 999999900i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- diff: simple subtraction ------------------------------------------
|
||||||
|
|
||||||
|
@test fn diffsimple() void = {
|
||||||
|
let a: time.instant; a.sec = 10i64; a.nsec = 100i64;
|
||||||
|
let b: time.instant; b.sec = 12i64; b.nsec = 300i64;
|
||||||
|
let d: i64 = time.diff(a, b): i64;
|
||||||
|
// b - a = 2.0000002 sec = 2_000_000_200 ns
|
||||||
|
if (d != 2000000200i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- diff: reversed sign -----------------------------------------------
|
||||||
|
|
||||||
|
@test fn diffneg() void = {
|
||||||
|
let a: time.instant; a.sec = 12i64; a.nsec = 300i64;
|
||||||
|
let b: time.instant; b.sec = 10i64; b.nsec = 100i64;
|
||||||
|
let d: i64 = time.diff(a, b): i64;
|
||||||
|
if (d != -2000000200i64) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- compare: -1 / 0 / +1 ladder ---------------------------------------
|
||||||
|
|
||||||
|
@test fn comparelt() void = {
|
||||||
|
let a: time.instant; a.sec = 1i64; a.nsec = 0i64;
|
||||||
|
let b: time.instant; b.sec = 2i64; b.nsec = 0i64;
|
||||||
|
if (time.compare(a, b) != -1i8) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn comparegt() void = {
|
||||||
|
let a: time.instant; a.sec = 2i64; a.nsec = 0i64;
|
||||||
|
let b: time.instant; b.sec = 1i64; b.nsec = 0i64;
|
||||||
|
if (time.compare(a, b) != 1i8) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn compareeq() void = {
|
||||||
|
let a: time.instant; a.sec = 5i64; a.nsec = 42i64;
|
||||||
|
let b: time.instant; b.sec = 5i64; b.nsec = 42i64;
|
||||||
|
if (time.compare(a, b) != 0i8) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn comparensec_only() void = {
|
||||||
|
let a: time.instant; a.sec = 5i64; a.nsec = 100i64;
|
||||||
|
let b: time.instant; b.sec = 5i64; b.nsec = 200i64;
|
||||||
|
if (time.compare(a, b) != -1i8) { fail(); };
|
||||||
|
if (time.compare(b, a) != 1i8) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
export fn main() i32 = {
|
||||||
|
signalled = 1; constants();
|
||||||
|
signalled = 2; nowmonotonic();
|
||||||
|
signalled = 3; nowrealtime();
|
||||||
|
signalled = 4; addzero();
|
||||||
|
signalled = 5; addpos_nocarry();
|
||||||
|
signalled = 6; addpos_carry();
|
||||||
|
signalled = 7; addpos_fullsecond();
|
||||||
|
signalled = 8; addneg_noborrow();
|
||||||
|
signalled = 9; addneg_borrow();
|
||||||
|
signalled = 10; diffsimple();
|
||||||
|
signalled = 11; diffneg();
|
||||||
|
signalled = 12; comparelt();
|
||||||
|
signalled = 13; comparegt();
|
||||||
|
signalled = 14; compareeq();
|
||||||
|
signalled = 15; comparensec_only();
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
49
test/wcc/977_time_run.c
Normal file
49
test/wcc/977_time_run.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 977_time_run — execute the lib/time @test fixture under the C-side
|
||||||
|
* `ww run` driver and assert exit 0.
|
||||||
|
*
|
||||||
|
* Same thin-wrapper shape as 970_fmt_run / 971_log_run / 976_stat_run:
|
||||||
|
* timetest.ww carries its own `export fn main()` that drives the
|
||||||
|
* @test fns and signals which case failed via the exit code.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
runwait(const char *cmd)
|
||||||
|
{
|
||||||
|
int rc = system(cmd);
|
||||||
|
if (rc == -1) return -1;
|
||||||
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
const char *bin = getenv("BIN");
|
||||||
|
if (!bin) bin = "out/bin";
|
||||||
|
char absbin[1024];
|
||||||
|
if (bin[0] != '/') {
|
||||||
|
char cwd[1024];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||||
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||||
|
bin = absbin;
|
||||||
|
}
|
||||||
|
char cwd[1024];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||||
|
|
||||||
|
const char *src = "lib/time/timetest.ww";
|
||||||
|
char path[1024], cmd[2048];
|
||||||
|
snprintf(path, sizeof path, "%s/%s", cwd, src);
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, path);
|
||||||
|
int rc = runwait(cmd);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "time_run FAIL: %s exited %d\n", src, rc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("time_run: %s ok\n", src);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user