Files
ww/test/sep/importdir_test.ww

218 lines
8.8 KiB
Plaintext

package importdir_test;
import os;
import os.exec;
import strings;
import testenv;
import time;
fn fail(why: str) void = {
let m: str = strings.concat("importdir FAIL: ", why, "\n");
os.write(2, m.ptr, m.len: u64);
assert(false);
};
fn mkdir(path: str) void = { assert(os.mkdir(path, 493) == 0); };
fn command(td: str, name: str, argv: []str,
out: *testenv.commandout) void = {
testenv.runcommand(td, td, name, argv,
(240i64 * (time.second: i64)): time.duration, out);
};
fn expectcode(td: str, name: str, argv: []str, want: i32) void = {
let out: testenv.commandout;
command(td, name, argv, &out);
if (out.termination != exec.termination.EXIT || out.code != want) {
fail(strings.concat(name, " exit != expected: ", out.stderr));
};
};
fn samefile(a: str, b: str, why: str) void = {
if (!testenv.same(testenv.readfile(a), testenv.readfile(b))) { fail(why); };
};
@test fn import_is_directory_package() void = {
let td: str = testenv.fresh();
let early: str = strings.concat(td, "/early");
let late: str = strings.concat(td, "/late");
let earlyex: str = strings.concat(early, "/example");
let lateex: str = strings.concat(late, "/example");
let base: str = strings.concat(lateex, "/base");
let bar: str = strings.concat(lateex, "/bar");
let foo: str = strings.concat(lateex, "/foo");
let app: str = strings.concat(td, "/app");
let checks: str = strings.concat(td, "/checks");
mkdir(early); mkdir(late); mkdir(earlyex); mkdir(lateex);
mkdir(base); mkdir(bar); mkdir(foo); mkdir(app); mkdir(checks);
let decoy: str = strings.concat(
"package foo;\n// DECOY_FILE\n",
"export fn value() i32 = { return 99; };\n");
let basesrc: str = strings.concat(
"package base;\n",
"export fn value() i32 = { return 37; };\n");
let barsrc: str = strings.concat(
"package bar;\nimport example.base;\n",
"export fn value() i32 = { return base.value() + 3; };\n");
let afoosrc: str = strings.concat(
"package foo;\n// A_OWNED\n",
"fn localdelta() i32 = { return 2; };\n");
let zfoosrc: str = strings.concat(
"package foo;\nimport example.bar;\n// Z_OWNED\n",
"export fn value() i32 = { return bar.value() + localdelta(); };\n");
let appsrc: str = strings.concat(
"package main;\nimport example.foo;\n",
"fn main() i32 = { return foo.value(); };\n");
testenv.writefile(strings.concat(earlyex, "/foo.ww"), decoy);
testenv.writefile(strings.concat(base, "/base.ww"), basesrc);
testenv.writefile(strings.concat(bar, "/bar.ww"), barsrc);
testenv.writefile(strings.concat(foo, "/a.ww"), afoosrc);
testenv.writefile(strings.concat(foo, "/z.ww"), zfoosrc);
testenv.writefile(strings.concat(app, "/main.ww"), appsrc);
let stems: []str = [strings.concat(td, "/c1"),
strings.concat(td, "/c2"), strings.concat(td, "/ww")];
let drivers: []str = ["ww", "ww", "ww_ww"];
let tags: []str = ["c1", "c2", "ww"];
let i: i32 = 0;
for (i < stems.len) {
let av: []str = [testenv.driver(drivers[i]), "build",
"-I", early, "-I", late, "-o", stems[i], app];
expectcode(td, strings.concat("build_", tags[i]), av, 0);
let rav: []str = [stems[i]];
expectcode(td, strings.concat("run_", tags[i]), rav, 42);
i += 1;
};
let cwork: str = strings.concat(stems[0], ".sepwork/");
let cwork2: str = strings.concat(stems[1], ".sepwork/");
let wwork: str = strings.concat(stems[2], ".sepwork/");
let appidentity: str = testenv.localpackageidentity(app, "main");
let foounit: str = testenv.readfile(strings.concat(cwork,
"example.foo.unit.ww"));
let wantfoo: str = strings.concat("//ww:module-reset example.foo\n", afoosrc,
"\n//ww:module-reset example.foo\n", zfoosrc, "\n");
let foovoucher: str = "//ww:direct-export example.bar ";
if (!strings.hasprefix(foounit, wantfoo)
|| testenv.occurrences(foounit, "//ww:direct-export ") != 1
|| testenv.occurrences(foounit, foovoucher) != 1
|| testenv.pos(foounit, foovoucher) != wantfoo.len
|| testenv.has(foounit, "DECOY_FILE")
|| testenv.has(foounit, "//ww:module ")) {
fail("foo owner prefix/direct-export voucher mismatch");
};
let wantroot: str = strings.concat("//ww:module-reset ", appidentity,
"\n", appsrc,
"\n");
let rootunit: str = testenv.readfile(strings.concat(cwork,
appidentity, ".unit.ww"));
let rootvoucher: str = "//ww:direct-export example.foo ";
if (!strings.hasprefix(rootunit, wantroot)
|| testenv.occurrences(rootunit, "//ww:direct-export ") != 1
|| testenv.occurrences(rootunit, rootvoucher) != 1
|| testenv.pos(rootunit, rootvoucher) != wantroot.len) {
fail("root owner prefix/direct-export voucher mismatch");
};
let keys: []str = ["example.base", "example.bar", "example.foo",
appidentity];
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
i = 0;
for (i < keys.len) {
let j: i32 = 0;
for (j < suffixes.len) {
let rel: str = strings.concat(keys[i], suffixes[j]);
samefile(strings.concat(cwork, rel), strings.concat(cwork2, rel),
strings.concat(rel, " changed across clean C builds"));
samefile(strings.concat(cwork, rel), strings.concat(wwork, rel),
strings.concat(rel, " differs between stages"));
j += 1;
};
i += 1;
};
samefile(stems[0], stems[1], "C executables are not deterministic");
samefile(stems[0], stems[2], "C/WW executables differ");
// The same canonical directory is now observed as an imported dependency,
// a literal root, an explicit logical root, an equivalent path spelling,
// and a symlink spelling. Every route must publish the one example.foo
// action with byte-identical owner unit, export, and archive.
let fooalias: str = strings.concat(td, "/foo-alias");
assert(os.symlink(foo, fooalias) == 0);
let rootouts: []str = [strings.concat(td, "/foo-root-c"),
strings.concat(td, "/foo-root-w"),
strings.concat(td, "/foo-logical-c"),
strings.concat(td, "/foo-logical-w"),
strings.concat(td, "/foo-equivalent-c"),
strings.concat(td, "/foo-equivalent-w"),
strings.concat(td, "/foo-relative-c"),
strings.concat(td, "/foo-relative-w"),
strings.concat(td, "/foo-alias-c"),
strings.concat(td, "/foo-alias-w")];
let rootdrivers: []str = ["ww", "ww_ww", "ww", "ww_ww", "ww",
"ww_ww", "ww", "ww_ww", "ww", "ww_ww"];
let roottags: []str = ["literal-c", "literal-w", "logical-c",
"logical-w", "equivalent-c", "equivalent-w", "relative-c",
"relative-w", "alias-c", "alias-w"];
let roottargets: []str = [foo, foo, "example.foo", "example.foo",
strings.concat(foo, "/../foo"), strings.concat(foo, "/../foo"),
"late/example/foo", "late/example/foo", fooalias, fooalias];
i = 0;
for (i < rootouts.len) {
let rav: []str = [testenv.driver(rootdrivers[i]), "build",
"-I", early, "-I", late, "-o", rootouts[i], roottargets[i]];
expectcode(td, strings.concat("foo_root_", roottags[i]), rav, 0);
let rw: str = strings.concat(rootouts[i], ".sepwork/");
let rsuffixes: []str = [".unit.ww", ".wwi", ".a"];
let ri: i32 = 0;
for (ri < rsuffixes.len) {
let artifact: str = strings.concat("example.foo", rsuffixes[ri]);
samefile(strings.concat(cwork, artifact), strings.concat(rw, artifact),
"canonical foo action changed across root spellings");
ri += 1;
};
i += 1;
};
let cerr: testenv.commandout;
let werr: testenv.commandout;
let cav: []str = [testenv.driver("ww"), "build", "-I", early,
"-o", strings.concat(td, "/bad-c"), app];
let wav: []str = [testenv.driver("ww_ww"), "build", "-I", early,
"-o", strings.concat(td, "/bad-w"), app];
command(td, "file_only_c", cav, &cerr);
command(td, "file_only_w", wav, &werr);
if (cerr.termination != exec.termination.EXIT || cerr.code == 0
|| werr.termination != exec.termination.EXIT || werr.code == 0
|| !testenv.same(cerr.stderr, werr.stderr)
|| !testenv.has(cerr.stderr, "cannot find package example.foo")
|| !testenv.has(cerr.stderr, strings.concat(app, "/main.ww:"))) {
fail("file-only import rejection is not stable and package-attributed");
};
testenv.writefile(strings.concat(checks, "/checks.ww"),
"package checks;\nexport fn ready() bool = { return true; };\n");
testenv.writefile(strings.concat(checks, "/checks_test.ww"), strings.concat(
"package checks;\nimport example.foo;\n",
"@test fn directory_import() void = { assert(foo.value() == 42); };\n"));
let testbins: []str = [strings.concat(td, "/test-c"),
strings.concat(td, "/test-w")];
let testworks: []str = [strings.concat(td, "/test-work-c"),
strings.concat(td, "/test-work-w")];
i = 0;
for (i < 2) {
let tav: []str = [testenv.driver(drivers[i * 2]), "test", "-w",
testworks[i], "-I", early, "-I", late, "-o", testbins[i],
checks];
expectcode(td, strings.concat("test_build_", tags[i * 2]), tav, 0);
let trav: []str = [testbins[i]];
expectcode(td, strings.concat("test_run_", tags[i * 2]), trav, 0);
i += 1;
};
samefile(testbins[0], testbins[1], "directory-test binaries differ");
samefile(strings.concat(testworks[0], "/example.foo.a"),
strings.concat(testworks[1], "/example.foo.a"),
"directory-test dependency archives differ");
testenv.clean(td);
};