os: getenvs() []str + single-walker getenv (Hare env surface)

Mirror Hare's os env surface: getenvs() builds an owned []str of
NAME=VALUE entries from the rt_envp table (platform_environ.ha:41),
and getenv iterates it (environ.ha:32) so there is exactly one env
walker. strings.dup is unusable here -- lib/strings imports os, so os
importing strings would cycle; the owned copy is inlined (dup.ha:7).
test_getenvs_entries pins the []str shape non-vacuously.
This commit is contained in:
2026-06-21 23:41:18 +09:00
parent b817e5d498
commit 93b671808c
2 changed files with 108 additions and 29 deletions

View File

@@ -91,6 +91,34 @@ fn streq(a: str, b: str) bool = {
};
};
// ---- getenvs: direct non-vacuous coverage of the []str shape --------
//
// getenvs() is public API (task #28) and the single env walker getenv
// routes through. getenv's cases exercise the walk transitively, but
// none asserts the []str RETURN shape — its count or "NAME=VALUE" entry
// text. This row pins both: the slice must be non-empty, and every
// expected entry must appear verbatim (name, '=', value duped intact).
// Fails if getenvs miscounts (len 0 → the inner search finds nothing)
// or dups the wrong bytes (off-by-one truncation → no streq match).
// The "WW_TEST_EMPTY=" row also pins the empty-value entry text.
@test fn test_getenvs_entries() void = {
let env: []str = os.getenvs();
assert(!(env.len == 0));
let want: [2]str = ["WW_TEST_GETENV=hello-world", "WW_TEST_EMPTY="];
let w: i32 = 0;
for (w < 2) {
let found: bool = false;
let i: i32 = 0;
for (i < env.len) {
if (streq(env[i], want[w])) { found = true; };
i += 1;
};
assert(found);
w += 1;
};
};
// ---- alloc/free: mmap-backed runtime allocator ----------------------
//
// Direct round-trip. Write-then-read-back proves the returned page is