// dirstest — exercises lib/dirs against an env state pre-arranged by // the C driver (test/wcc/975_dirs_run.c). // // Pre-arranged env (set via setenv/unsetenv before exec'ing // `ww run lib/dirs/dirstest.ww`; see 975_dirs_run.c): // // HOME = (mkdtemp'd /tmp/wwdirs-XXXXXX) // XDG_CONFIG_HOME = /cfg (absolute → XDG branch) // XDG_CACHE_HOME = "relative/path" (non-abs → fallback to HOME) // XDG_DATA_HOME unset (fallback to HOME) // XDG_STATE_HOME unset (fallback to HOME) // WW_TEST_TMPDIR = (so this fixture can compute // the expected paths) // // Cohorts (one @test fn per row, in numeric order from main): // // 1. config("myapp") — XDG-set+absolute → /cfg/myapp // 2. cache("myapp") — XDG-set+relative → /.cache/myapp (abs-check fallback) // 3. data("myapp") — XDG-unset → /.local/share/myapp (HOME fallback) // 4. state("myapp") — XDG-unset → /.local/state/myapp (HOME fallback) // // Static-buffer gotcha: dirs.config / dirs.cache / dirs.data / // dirs.state all return views into the same module-level pathbuf. // Each @test calls EXACTLY ONE dirs.* function, builds its expected // string into a separate `scratch: [256]u8` BEFORE that call, and // asserts immediately. Don't call two dirs.* fns and compare across // — the second clobbers the first's view. // // A failing row aborts via the assert/abort builtin (task #5 @test // conversion). // // Don't `use io;` here — lib/os and lib/io collide on read/write/ // close (task #17). We need only os.getenv. package dirs_test; import dirs; import os; fn streq(a: str, b: str) bool = { if (a.len != b.len) { return false; }; let i: i32 = 0; for (i < a.len) { if (a[i] != b[i]) { return false; }; i += 1; }; return true; }; // scratch — local buffer for expected-path construction. Independent // of dirs.pathbuf; both views stay valid through the streq compare // because they live in different buffers. let scratch: [256]u8; fn putscratch(off: i32, s: str) i32 = { let i: i32 = 0; for (i < s.len) { if (off + i >= 255) { break; }; scratch[off + i] = s[i]; i += 1; }; return off + i; }; // expected — build "//" into scratch and return a // view. Mirrors dirs.build but writes into our scratch buffer so // the expected and got views can be compared without aliasing. fn expected(tmpl: str, sub: str, prog: str) str = { let off: i32 = 0; off = putscratch(off, tmpl); if (off < 255) { scratch[off] = 47u8; off += 1; }; // '/' off = putscratch(off, sub); if (off < 255) { scratch[off] = 47u8; off += 1; }; off = putscratch(off, prog); let r: str; r.ptr = &scratch[0]; r.len = off; return r; }; fn gettmpdir() str = { match (os.getenv("WW_TEST_TMPDIR")) { case let s: str => return s; case void => { abort(); let empty: str; return empty; }; }; }; // ---- 1: XDG_CONFIG_HOME=/cfg → /cfg/myapp -------------- @test fn test_config_xdg_set() void = { let tmpl = gettmpdir(); let exp = expected(tmpl, "cfg", "myapp"); let got = dirs.config("myapp"); assert(!(!streq(got, exp))); }; // ---- 2: XDG_CACHE_HOME="relative/path" → fallback to $HOME/.cache -- // // Exercises Hare's `if (!path::abs(path)) yield;` branch — a // non-absolute XDG_*_HOME silently falls through to the HOME path. @test fn test_cache_xdg_relative_fallback() void = { let tmpl = gettmpdir(); let exp = expected(tmpl, ".cache", "myapp"); let got = dirs.cache("myapp"); assert(!(!streq(got, exp))); }; // ---- 3: XDG_DATA_HOME unset → fallback to $HOME/.local/share ------- @test fn test_data_unset_fallback() void = { let tmpl = gettmpdir(); let exp = expected(tmpl, ".local/share", "myapp"); let got = dirs.data("myapp"); assert(!(!streq(got, exp))); }; // ---- 4: XDG_STATE_HOME unset → fallback to $HOME/.local/state ------ @test fn test_state_unset_fallback() void = { let tmpl = gettmpdir(); let exp = expected(tmpl, ".local/state", "myapp"); let got = dirs.state("myapp"); assert(!(!streq(got, exp))); };