diff --git a/lib/encoding/hex/hex_test.ww b/lib/encoding/hex/hex_test.ww index efc4c3bd..f8a681d4 100644 --- a/lib/encoding/hex/hex_test.ww +++ b/lib/encoding/hex/hex_test.ww @@ -9,7 +9,11 @@ package hex_test; import bytes; import errors; -import hex; +// Use the directory package's canonical identity. A recursive lib/... test +// request also reaches this production package through crypto.sha256's +// encoding.hex import, and one physical package must retain one identity in +// the command-scoped package universe. +import encoding.hex; import io; import memio; diff --git a/test/package/package_test.ww b/test/package/package_test.ww index c6db9540..fd9e85c6 100644 --- a/test/package/package_test.ww +++ b/test/package/package_test.ww @@ -644,9 +644,9 @@ fn workescape(s: str) str = { assert(has(outc.stdout, strings.concat(" -> ", root, "/route_test.test\n"))); let cwhite: str = readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-same.s")); + "/route.test.sepwork/__ww-test-000-same.s")); let cexternal: str = readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-external.s")); + "/route.test.sepwork/__ww-test-001-external.s")); // The explicit package `-c` outputs are caller-owned artifacts. Release // the shared C-stage tree before asking the WW driver to acquire the same // stem; the driver never deletes a pre-existing `.sepwork` path. @@ -659,9 +659,9 @@ fn workescape(s: str) str = { assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(same(cwhite, readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-same.s")))); + "/route.test.sepwork/__ww-test-000-same.s")))); assert(same(cexternal, readfile(strings.concat(root, - "/route.test.sepwork/__ww-test-external.s")))); + "/route.test.sepwork/__ww-test-001-external.s")))); clean(root); }; @@ -801,11 +801,11 @@ fn workescape(s: str) str = { let buildstdout: str = strings.dup(out.stdout); let buildstderr: str = strings.dup(out.stderr); let sameunit: str = readfile(strings.concat(sharedwork, - "__ww-test-same.unit.ww")); + "__ww-test-000-same.unit.ww")); let externalunit: str = readfile(strings.concat(sharedwork, - "__ww-test-external.unit.ww")); + "__ww-test-001-external.unit.ww")); let externalproduction: str = readfile(strings.concat(sharedwork, - "pkg.unit.ww")); + "__ww-test-001-external-production.unit.ww")); assert(!os.exists(strings.concat(externalbin, ".sepwork"))); assert(has(sameunit, "//ww:module api\n")); assert(has(sameunit, "//ww:module __same\n")); @@ -835,7 +835,8 @@ fn workescape(s: str) str = { assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE")); assert(!has(readfile(strings.concat(sharedwork, "api.unit.ww")), "TEST_DEPENDENCY_MUST_NOT_COMPILE")); - let artifacts: []str = ["implementation", "api", "pkg", + let artifacts: []str = ["implementation", "api", + "__ww-test-001-external-production", "__same", "__external", "test"]; let ai: i32 = 0; for (ai < artifacts.len) { @@ -843,27 +844,32 @@ fn workescape(s: str) str = { assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".a"))); ai += 1; }; - assert(os.exists(strings.concat(sharedwork, "__ww-test-same.o"))); - assert(os.exists(strings.concat(sharedwork, "__ww-test-external.o"))); - assert(!os.exists(strings.concat(sharedwork, "__ww-test-same.wwi"))); + assert(os.exists(strings.concat(sharedwork, "__ww-test-000-same.o"))); + assert(os.exists(strings.concat(sharedwork, + "__ww-test-001-external.o"))); assert(!os.exists(strings.concat(sharedwork, - "__ww-test-external.wwi"))); - assert(!os.exists(strings.concat(sharedwork, "__ww-test-same.a"))); + "__ww-test-000-same.wwi"))); assert(!os.exists(strings.concat(sharedwork, - "__ww-test-external.a"))); + "__ww-test-001-external.wwi"))); + assert(!os.exists(strings.concat(sharedwork, + "__ww-test-000-same.a"))); + assert(!os.exists(strings.concat(sharedwork, + "__ww-test-001-external.a"))); assert(os.exists(samebin)); assert(os.exists(externalbin)); if (i == 0) { let ctrace: str = readfile(compilertrace); assert(occurrences(ctrace, "implementation.unit.ww") == 1); assert(occurrences(ctrace, "api.unit.ww") == 1); - assert(occurrences(ctrace, "pkg.unit.ww") == 1); + assert(occurrences(ctrace, + "__ww-test-001-external-production.unit.ww") == 1); assert(occurrences(ctrace, "/__same.unit.ww") == 1); assert(occurrences(ctrace, "/__external.unit.ww") == 1); assert(occurrences(ctrace, "/test.unit.ww") == 1); - assert(occurrences(ctrace, "__ww-test-same.unit.ww") == 1); assert(occurrences(ctrace, - "__ww-test-external.unit.ww") == 1); + "__ww-test-000-same.unit.ww") == 1); + assert(occurrences(ctrace, + "__ww-test-001-external.unit.ww") == 1); assert(occurrences(ctrace, "-T --test-support-module") == 2); let ltrace: str = readfile(linkertrace); @@ -876,10 +882,12 @@ fn workescape(s: str) str = { assert(has(samelink, strings.concat(sharedwork, "implementation.a"))); assert(has(samelink, strings.concat(sharedwork, "__same.a"))); - assert(!has(samelink, strings.concat(sharedwork, "pkg.a"))); + assert(!has(samelink, strings.concat(sharedwork, + "__ww-test-001-external-production.a"))); assert(!has(samelink, strings.concat(sharedwork, "__external.a"))); - assert(has(externallink, strings.concat(sharedwork, "pkg.a"))); + assert(has(externallink, strings.concat(sharedwork, + "__ww-test-001-external-production.a"))); assert(has(externallink, strings.concat(sharedwork, "api.a"))); assert(has(externallink, strings.concat(sharedwork, "implementation.a"))); @@ -938,11 +946,14 @@ fn workescape(s: str) str = { assert(has(readfile(strings.concat(warmwork, "__same.unit.ww")), "package __same;")); assert(has(readfile(strings.concat(warmwork, - "__ww-test-same.unit.ww")), "SAME_TEST_SOURCE")); + "__ww-test-000-same.unit.ww")), "SAME_TEST_SOURCE")); assert(has(readfile(strings.concat(warmwork, "__external.unit.ww")), "package __external;")); assert(has(readfile(strings.concat(warmwork, - "__ww-test-external.unit.ww")), "EXTERNAL_TEST_SOURCE")); + "__ww-test-001-external.unit.ww")), "EXTERNAL_TEST_SOURCE")); + assert(has(readfile(strings.concat(warmwork, + "__ww-test-001-external-production.unit.ww")), + "PACKAGE_PRODUCTION_A")); runcommandenv(root, "variant-warm-reuse", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); @@ -979,6 +990,877 @@ fn workescape(s: str) str = { clean(root); }; +@test fn multi_directory_shared_package_plan() void = { + let root: str = fresh(); + let leaf: str = strings.concat(root, "/leaf"); + let common: str = strings.concat(root, "/common"); + let suite: str = strings.concat(root, "/suite"); + let alpha: str = strings.concat(suite, "/alpha"); + let beta: str = strings.concat(suite, "/beta"); + let alphasame: str = strings.concat(alpha, "/_alpha_same"); + let alphaexternal: str = strings.concat(alpha, "/_alpha_external"); + let betasame: str = strings.concat(beta, "/_beta_same"); + let betaexternal: str = strings.concat(beta, "/_beta_external"); + let alphashadow: str = strings.concat(root, "/_alpha_same"); + let betashadow: str = strings.concat(root, "/_beta_same"); + let dirs: []str = [leaf, common, suite, alpha, beta, alphasame, + alphaexternal, betasame, betaexternal, alphashadow, betashadow]; + let di: i32 = 0; + for (di < dirs.len) { + assert(os.mkdir(dirs[di], 448i32) == 0); + di += 1; + }; + writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( + "package leaf;\n", + "// MULTIDIR_LEAF\n", + "export fn value() i32 = { return 40; };\n")); + writefile(strings.concat(common, "/common.ww"), strings.concat( + "package common;\nimport leaf;\n", + "// MULTIDIR_COMMON\n", + "export fn value() i32 = { return leaf.value(); };\n")); + writefile(strings.concat(common, "/common_test.ww"), strings.concat( + "package common;\n", + "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE\n")); + writefile(strings.concat(alphasame, "/_alpha_same.ww"), + "package _alpha_same;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(alphaexternal, "/_alpha_external.ww"), + "package _alpha_external;\nexport fn value() i32 = { return 1; };\n"); + writefile(strings.concat(betasame, "/_beta_same.ww"), + "package _beta_same;\nexport fn value() i32 = { return 2; };\n"); + writefile(strings.concat(betaexternal, "/_beta_external.ww"), + "package _beta_external;\nexport fn value() i32 = { return 2; };\n"); + // The explicit -I root contains deliberately wrong shadows. Each selected + // directory must retain its own first lookup root inside the union plan. + writefile(strings.concat(alphashadow, "/_alpha_same.ww"), + "package _alpha_same;\nexport fn value() i32 = { return 99; };\n"); + writefile(strings.concat(betashadow, "/_beta_same.ww"), + "package _beta_same;\nexport fn value() i32 = { return 99; };\n"); + writefile(strings.concat(alpha, "/a.ww"), strings.concat( + "package alpha;\nimport common;\n", + "// MULTIDIR_ALPHA_A\n", + "export fn value() i32 = { return common.value(); };\n")); + writefile(strings.concat(alpha, "/z.ww"), strings.concat( + "package alpha;\n", + "// MULTIDIR_ALPHA_Z\n", + "fn secret() i32 = { return 1; };\n", + "export fn exported() i32 = { return 1; };\n")); + writefile(strings.concat(alpha, "/same_test.ww"), strings.concat( + "package alpha;\nimport _alpha_same;\n", + "// MULTIDIR_ALPHA_SAME\n", + "@test fn alpha_same_runs() void = {\n", + " assert(value() + secret() + _alpha_same.value() == 42);\n};\n")); + writefile(strings.concat(alpha, "/external_test.ww"), strings.concat( + "package alpha_test;\nimport alpha;\nimport _alpha_external;\n", + "// MULTIDIR_ALPHA_EXTERNAL\n", + "@test fn alpha_external_runs() void = {\n", + " assert(alpha.value() + alpha.exported()", + " + _alpha_external.value() == 42);\n};\n")); + writefile(strings.concat(beta, "/beta.ww"), strings.concat( + "package beta;\nimport common;\nimport alpha;\n", + "// MULTIDIR_BETA\n", + "fn secret() i32 = { return 2; };\n", + "export fn value() i32 = { return common.value(); };\n", + "export fn exported() i32 = { return alpha.exported() + 1; };\n")); + writefile(strings.concat(beta, "/same_test.ww"), strings.concat( + "package beta;\nimport _beta_same;\n", + "// MULTIDIR_BETA_SAME\n", + "@test fn beta_same_runs() void = {\n", + " assert(value() + secret() + _beta_same.value() == 44);\n};\n")); + writefile(strings.concat(beta, "/external_test.ww"), strings.concat( + "package beta_test;\nimport beta;\nimport _beta_external;\n", + "// MULTIDIR_BETA_EXTERNAL\n", + "@test fn beta_external_runs() void = {\n", + " assert(beta.value() + beta.exported()", + " + _beta_external.value() == 44);\n};\n")); + + let compilertrace: str = strings.concat(root, "/compiler.trace"); + let linkertrace: str = strings.concat(root, "/linker.trace"); + let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh"); + let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh"); + writefile(compilertrace, ""); + writefile(linkertrace, ""); + writeexecutable(compilerwrapper, strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n", + "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); + writeexecutable(linkerwrapper, strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_LINKER_TRACE\"\n", + "exec \"$WW_PACKAGE_W6L\" \"$@\"\n")); + let baseenv: []str = os.getenvs(); + let traceenv: []str = alloc([], (baseenv.len + 6): u64)!; + let ei: i32 = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_W6C=") + && !strings.hasprefix(baseenv[ei], "WW_W6L=") + && !strings.hasprefix(baseenv[ei], + "WW_PACKAGE_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], + "WW_PACKAGE_LINKER_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L=")) { + append(traceenv, baseenv[ei]); + }; + ei += 1; + }; + append(traceenv, strings.concat("WW_W6C=", compilerwrapper)); + append(traceenv, strings.concat("WW_W6L=", linkerwrapper)); + append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", + compilertrace)); + append(traceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace)); + append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); + append(traceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l"))); + // The WW driver intentionally takes sibling tools rather than WW_W6C + // overrides. Run an exact copied driver beside narrow tracing wrappers so + // its real compiler/linker actions are observable without changing that + // production contract. + let wwtracebin: str = strings.concat(repo(), "/out/package-trace-", + workescape(root)); + assert(os.mkdir(wwtracebin, 448i32) == 0); + let wwtracedriver: str = strings.concat(wwtracebin, "/ww_ww"); + let wwcompilertrace: str = strings.concat(root, "/ww-compiler.trace"); + let wwlinkertrace: str = strings.concat(root, "/ww-linker.trace"); + writefile(wwcompilertrace, ""); + writefile(wwlinkertrace, ""); + writeexecutable(wwtracedriver, readfile(driver("ww_ww"))); + writeexecutable(strings.concat(wwtracebin, "/wwtest"), + readfile(driver("wwtest"))); + writeexecutable(strings.concat(wwtracebin, "/w6c_ww"), strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_COMPILER_TRACE\"\n", + "exec \"$WW_PACKAGE_W6C_WW\" \"$@\"\n")); + writeexecutable(strings.concat(wwtracebin, "/w6a_ww"), strings.concat( + "#!/bin/sh\nexec \"$WW_PACKAGE_W6A_WW\" \"$@\"\n")); + writeexecutable(strings.concat(wwtracebin, "/w6l_ww"), strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_LINKER_TRACE\"\n", + "exec \"$WW_PACKAGE_W6L_WW\" \"$@\"\n")); + let wwtraceenv: []str = alloc([], (baseenv.len + 5): u64)!; + ei = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_LINKER_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C_WW=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6A_WW=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L_WW=")) { + append(wwtraceenv, baseenv[ei]); + }; + ei += 1; + }; + append(wwtraceenv, strings.concat("WW_PACKAGE_WW_COMPILER_TRACE=", + wwcompilertrace)); + append(wwtraceenv, strings.concat("WW_PACKAGE_WW_LINKER_TRACE=", + wwlinkertrace)); + append(wwtraceenv, strings.concat("WW_PACKAGE_W6C_WW=", driver("w6c_ww"))); + append(wwtraceenv, strings.concat("WW_PACKAGE_W6A_WW=", driver("w6a_ww"))); + append(wwtraceenv, strings.concat("WW_PACKAGE_W6L_WW=", driver("w6l_ww"))); + + let bins: []str = [strings.concat(alpha, "/alpha.test"), + strings.concat(alpha, "/alpha_test.test"), + strings.concat(beta, "/beta.test"), + strings.concat(beta, "/beta_test.test")]; + let rootkeys: []str = ["__ww-test-000-same", + "__ww-test-001-external", "__ww-test-002-same", + "__ww-test-003-external"]; + let alphaartifact: str = "__ww-test-001-external-production"; + let betaartifact: str = "__ww-test-003-external-production"; + let expectedtests: []str = ["alpha_same_runs ... ok\n", + "alpha_external_runs ... ok\n", "beta_same_runs ... ok\n", + "beta_external_runs ... ok\n"]; + let sharedworkroot: str = strings.concat(bins[0], ".sepwork"); + let sharedwork: str = strings.concat(sharedworkroot, "/"); + let referencebins: []str = ["", "", "", ""]; + let referenceroots: []str = ["", "", "", ""]; + let referenceout: str = ""; + let referenceerr: str = ""; + let referencecommonwwi: str = ""; + let referencecommona: str = ""; + let referencealphaa: str = ""; + let stages: []str = ["ww", "ww", "ww_ww"]; + let tags: []str = ["c1", "c2", "ww"]; + let out: commandout; + let si: i32 = 0; + for (si < stages.len) { + let stagepath: str = driver(stages[si]); + if (si == 2) { stagepath = wwtracedriver; }; + let av: []str = [stagepath, "test", "-c", "-j", "1", + "-I", suite, "-I", root, strings.concat(suite, "/...")]; + if (si == 0) { + runcommandenv(root, "multidir-c1", av, traceenv, + (120i64 * (time.second: i64)): time.duration, &out); + } else { if (si == 2) { + runcommandenv(root, strings.concat("multidir-", tags[si]), av, + wwtraceenv, + (120i64 * (time.second: i64)): time.duration, &out); + } else { + runcommand(root, strings.concat("multidir-", tags[si]), av, + (120i64 * (time.second: i64)): time.duration, &out); + }; }; + expectexit(&out, 0); + let buildstdout: str = strings.dup(out.stdout); + let buildstderr: str = strings.dup(out.stderr); + let commonunit: str = readfile(strings.concat(sharedwork, + "common.unit.ww")); + let alphaunit: str = readfile(strings.concat(sharedwork, + alphaartifact, ".unit.ww")); + let betaunit: str = readfile(strings.concat(sharedwork, + betaartifact, ".unit.ww")); + assert(has(commonunit, "//ww:module leaf\n")); + assert(!has(commonunit, "//ww:module test\n")); + assert(has(alphaunit, "//ww:module common\n")); + assert(!has(alphaunit, "//ww:module leaf\n")); + assert(has(alphaunit, "MULTIDIR_ALPHA_A")); + assert(has(alphaunit, "MULTIDIR_ALPHA_Z")); + assert(has(betaunit, "//ww:module common\n")); + assert(has(betaunit, "//ww:module alpha\n")); + assert(!has(betaunit, "//ww:module leaf\n")); + assert(!has(commonunit, "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE")); + let roots: []str = [readfile(strings.concat(sharedwork, + rootkeys[0], ".unit.ww")), readfile(strings.concat(sharedwork, + rootkeys[1], ".unit.ww")), readfile(strings.concat(sharedwork, + rootkeys[2], ".unit.ww")), readfile(strings.concat(sharedwork, + rootkeys[3], ".unit.ww"))]; + assert(has(roots[0], "//ww:module _alpha_same\n")); + assert(has(roots[0], "//ww:module common\n")); + assert(has(roots[0], "//ww:module test\n")); + assert(!has(roots[0], "//ww:module leaf\n")); + assert(!has(roots[0], "//ww:module alpha\n")); + assert(has(roots[0], "MULTIDIR_ALPHA_A")); + assert(has(roots[0], "MULTIDIR_ALPHA_Z")); + assert(has(roots[1], "//ww:module alpha\n")); + assert(has(roots[1], "//ww:module _alpha_external\n")); + assert(has(roots[1], "//ww:module test\n")); + assert(!has(roots[1], "//ww:module common\n")); + assert(!has(roots[1], "//ww:module _alpha_same\n")); + assert(has(roots[2], "//ww:module _beta_same\n")); + assert(has(roots[2], "//ww:module common\n")); + assert(has(roots[2], "//ww:module alpha\n")); + assert(has(roots[2], "//ww:module test\n")); + assert(!has(roots[2], "//ww:module leaf\n")); + assert(!has(roots[2], "//ww:module beta\n")); + assert(has(roots[3], "//ww:module beta\n")); + assert(has(roots[3], "//ww:module _beta_external\n")); + assert(has(roots[3], "//ww:module test\n")); + assert(!has(roots[3], "//ww:module common\n")); + let products: []str = ["leaf", "common", alphaartifact, betaartifact, + "_alpha_same", "_alpha_external", "_beta_same", + "_beta_external", "test"]; + let pi: i32 = 0; + for (pi < products.len) { + assert(os.exists(strings.concat(sharedwork, products[pi], + ".unit.ww"))); + assert(os.exists(strings.concat(sharedwork, products[pi], ".wwi"))); + assert(os.exists(strings.concat(sharedwork, products[pi], ".a"))); + pi += 1; + }; + let ri: i32 = 0; + for (ri < rootkeys.len) { + assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".o"))); + assert(!os.exists(strings.concat(sharedwork, rootkeys[ri], ".wwi"))); + assert(!os.exists(strings.concat(sharedwork, rootkeys[ri], ".a"))); + assert(os.exists(bins[ri])); + if (ri > 0) { + assert(!os.exists(strings.concat(bins[ri], ".sepwork"))); + }; + let runav: []str = [bins[ri]]; + runcommand(root, strings.concat("multidir-run-", tags[si], "-", + rootkeys[ri]), runav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, expectedtests[ri])); + ri += 1; + }; + if (si == 0) { + let ctrace: str = readfile(compilertrace); + let once: []str = ["leaf", "common", alphaartifact, betaartifact, + "_alpha_same", "_alpha_external", "_beta_same", + "_beta_external", "test"]; + pi = 0; + for (pi < once.len) { + assert(occurrences(ctrace, strings.concat("/", once[pi], + ".unit.ww")) == 1); + pi += 1; + }; + // alpha is both a selected external-test production package and a + // direct dependency of beta. Those identities must intern to the one + // owning production action above, never a second alpha.unit.ww. + assert(occurrences(ctrace, "/alpha.unit.ww") == 0); + ri = 0; + for (ri < rootkeys.len) { + assert(occurrences(ctrace, strings.concat(rootkeys[ri], + ".unit.ww")) == 1); + ri += 1; + }; + assert(pos(ctrace, "/leaf.unit.ww") + < pos(ctrace, "/common.unit.ww")); + assert(pos(ctrace, "/common.unit.ww") + < pos(ctrace, strings.concat("/", alphaartifact, + ".unit.ww"))); + assert(pos(ctrace, "/common.unit.ww") + < pos(ctrace, strings.concat("/", betaartifact, + ".unit.ww"))); + let ltrace: str = readfile(linkertrace); + assert(occurrences(ltrace, "\n") == 4); + ri = 0; + for (ri < bins.len) { + let link: str = linecontaining(ltrace, + strings.concat("-o ", bins[ri], " ")); + assert(has(link, strings.concat(sharedwork, rootkeys[ri], ".o"))); + assert(has(link, strings.concat(sharedwork, "common.a"))); + assert(has(link, strings.concat(sharedwork, "leaf.a"))); + assert(has(link, strings.concat(sharedwork, "test.a"))); + if (ri < 2) { + assert(!has(link, strings.concat(sharedwork, + betaartifact, ".a"))); + assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); + assert(!has(link, strings.concat(sharedwork, + "_beta_external.a"))); + } else { + assert(has(link, strings.concat(sharedwork, + alphaartifact, ".a"))); + assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); + assert(!has(link, strings.concat(sharedwork, + "_alpha_external.a"))); + }; + if (ri == 0) { + assert(has(link, strings.concat(sharedwork, "_alpha_same.a"))); + assert(!has(link, strings.concat(sharedwork, + alphaartifact, ".a"))); + assert(!has(link, strings.concat(sharedwork, + "_alpha_external.a"))); + } else { if (ri == 1) { + assert(has(link, strings.concat(sharedwork, + alphaartifact, ".a"))); + assert(has(link, strings.concat(sharedwork, + "_alpha_external.a"))); + assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); + } else { if (ri == 2) { + assert(has(link, strings.concat(sharedwork, "_beta_same.a"))); + assert(!has(link, strings.concat(sharedwork, + betaartifact, ".a"))); + assert(!has(link, strings.concat(sharedwork, + "_beta_external.a"))); + } else { + assert(has(link, strings.concat(sharedwork, + betaartifact, ".a"))); + assert(has(link, strings.concat(sharedwork, + "_beta_external.a"))); + assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); + }; }; }; + ri += 1; + }; + referenceout = buildstdout; + referenceerr = buildstderr; + referencecommonwwi = strings.dup(readfile(strings.concat(sharedwork, + "common.wwi"))); + referencecommona = strings.dup(readfile(strings.concat(sharedwork, + "common.a"))); + referencealphaa = strings.dup(readfile(strings.concat(sharedwork, + alphaartifact, ".a"))); + ri = 0; + for (ri < bins.len) { + referencebins[ri] = strings.dup(readfile(bins[ri])); + referenceroots[ri] = strings.dup(roots[ri]); + ri += 1; + }; + } else { + assert(same(referenceout, buildstdout)); + assert(same(referenceerr, buildstderr)); + assert(same(referencecommonwwi, + readfile(strings.concat(sharedwork, "common.wwi")))); + assert(same(referencecommona, + readfile(strings.concat(sharedwork, "common.a")))); + assert(same(referencealphaa, + readfile(strings.concat(sharedwork, alphaartifact, ".a")))); + ri = 0; + for (ri < bins.len) { + assert(same(referencebins[ri], readfile(bins[ri]))); + assert(same(referenceroots[ri], roots[ri])); + ri += 1; + }; + }; + if (si == 2) { + let wwtrace: str = readfile(wwcompilertrace); + let once: []str = ["leaf", "common", alphaartifact, betaartifact, + "_alpha_same", "_alpha_external", "_beta_same", + "_beta_external", "test"]; + pi = 0; + for (pi < once.len) { + assert(occurrences(wwtrace, strings.concat("/", once[pi], + ".unit.ww")) == 1); + pi += 1; + }; + assert(occurrences(wwtrace, "/alpha.unit.ww") == 0); + ri = 0; + for (ri < rootkeys.len) { + assert(occurrences(wwtrace, strings.concat(rootkeys[ri], + ".unit.ww")) == 1); + ri += 1; + }; + assert(occurrences(readfile(wwlinkertrace), "\n") == 4); + }; + if (si + 1 < stages.len) { + clean(sharedworkroot); + ri = 0; + for (ri < bins.len) { clean(bins[ri]); ri += 1; }; + }; + si += 1; + }; + + clean(sharedworkroot); + let bi: i32 = 0; + for (bi < bins.len) { clean(bins[bi]); bi += 1; }; + let warmroot: str = strings.concat(root, "/warm-c"); + let warmwwroot: str = strings.concat(root, "/warm-ww"); + assert(os.mkdir(warmroot, 448i32) == 0); + assert(os.mkdir(warmwwroot, 448i32) == 0); + let warmc: []str = [driver("ww"), "test", "-j", "1", "-w", + warmroot, "-I", suite, "-I", root, + strings.concat(suite, "/...")]; + runcommandenv(root, "multidir-warm-cold", warmc, traceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let warmstdout: str = strings.dup(out.stdout); + let warmstderr: str = strings.dup(out.stderr); + let warmtrace: str = readfile(compilertrace); + let warmparallel: []str = [driver("ww"), "test", "-j", "4", "-w", + warmroot, "-I", suite, "-I", root, + strings.concat(suite, "/...")]; + runcommandenv(root, "multidir-warm-reuse", warmparallel, traceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(warmstdout, out.stdout)); + assert(same(warmstderr, out.stderr)); + assert(same(warmtrace, readfile(compilertrace))); + let warmkey: str = strings.concat("/d_", workescape(suite), "_p_alpha/"); + let warmwork: str = strings.concat(warmroot, warmkey); + assert(os.exists(strings.concat(warmwork, + "__ww-test-000-same.unit.ww"))); + assert(os.exists(strings.concat(warmwork, + "__ww-test-003-external.unit.ww"))); + let warmww: []str = [wwtracedriver, "test", "-j", "4", "-w", + warmwwroot, "-I", suite, "-I", root, + strings.concat(suite, "/...")]; + runcommandenv(root, "multidir-warm-ww", warmww, wwtraceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(warmstdout, out.stdout)); + assert(same(warmstderr, out.stderr)); + let warmwwtrace: str = strings.dup(readfile(wwcompilertrace)); + runcommandenv(root, "multidir-warm-ww-reuse", warmww, wwtraceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(warmstdout, out.stdout)); + assert(same(warmstderr, out.stderr)); + assert(same(warmwwtrace, readfile(wwcompilertrace))); + let warmwwwork: str = strings.concat(warmwwroot, warmkey); + assert(same(readfile(strings.concat(warmwork, "common.wwi")), + readfile(strings.concat(warmwwwork, "common.wwi")))); + assert(same(readfile(strings.concat(warmwork, "common.a")), + readfile(strings.concat(warmwwwork, "common.a")))); + assert(same(readfile(strings.concat(warmwork, + "__ww-test-000-same.unit.ww")), readfile(strings.concat(warmwwwork, + "__ww-test-000-same.unit.ww")))); + clean(wwtracebin); + clean(root); +}; + +@test fn shared_package_resolution_context_conflict_is_stable() void = { + let root: str = fresh(); + let suite: str = strings.concat(root, "/suite"); + let a: str = strings.concat(suite, "/a"); + let b: str = strings.concat(suite, "/b"); + let good: str = strings.concat(suite, "/good"); + let aleaf: str = strings.concat(a, "/_leaf"); + let bleaf: str = strings.concat(b, "/_leaf"); + let common: str = strings.concat(root, "/common"); + let dirs: []str = [suite, a, b, good, aleaf, bleaf, common]; + let di: i32 = 0; + for (di < dirs.len) { assert(os.mkdir(dirs[di], 448i32) == 0); di += 1; }; + writefile(strings.concat(aleaf, "/_leaf.ww"), + "package _leaf;\nexport fn value() i32 = { return 20; };\n"); + writefile(strings.concat(bleaf, "/_leaf.ww"), + "package _leaf;\nexport fn value() i32 = { return 21; };\n"); + writefile(strings.concat(common, "/common.ww"), strings.concat( + "package common;\nimport _leaf;\n", + "export fn value() i32 = { return _leaf.value(); };\n")); + writefile(strings.concat(a, "/a.ww"), + "package a;\nimport common;\nfn value() i32 = { return common.value(); };\n"); + writefile(strings.concat(a, "/a_test.ww"), + "package a;\n@test fn a_context() void = { assert(value() == 20); };\n"); + writefile(strings.concat(b, "/b.ww"), + "package b;\nimport common;\nfn value() i32 = { return common.value(); };\n"); + writefile(strings.concat(b, "/b_test.ww"), + "package b;\n@test fn b_context() void = { assert(value() == 21); };\n"); + writefile(strings.concat(good, "/good.ww"), + "package good;\nfn value() i32 = { return 42; };\n"); + writefile(strings.concat(good, "/good_test.ww"), + "package good;\n@test fn unrelated_runs() void = { assert(value() == 42); };\n"); + let stages: []str = ["ww", "ww_ww"]; + let jobs: []str = ["1", "4"]; + let diagnostics: []str = ["", ""]; + let referenceout: str = ""; + let out: commandout; + let i: i32 = 0; + for (i < stages.len) { + let av: []str = [driver(stages[i]), "test", "-j", jobs[i], + "-I", root, strings.concat(suite, "/...")]; + runcommand(root, strings.concat("context-conflict-", stages[i]), av, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + assert(has(out.stderr, + "ww: package _leaf resolves to more than one location\n")); + assert(has(out.stderr, strings.concat("FAIL ", a, + " [a] (build exit 1)\n"))); + assert(has(out.stderr, strings.concat("FAIL ", b, + " [b] (build exit 1)\n"))); + assert(!has(out.stderr, strings.concat("FAIL ", good, " ["))); + assert(has(out.stdout, "unrelated_runs ... ok\n")); + assert(has(out.stdout, strings.concat("ok ", good, + " [good, same-package]\n"))); + diagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); + if (i == 0) { referenceout = strings.dup(out.stdout); } + else { assert(same(referenceout, out.stdout)); }; + i += 1; + }; + assert(same(diagnostics[0], diagnostics[1])); + clean(root); +}; + +@test fn mixed_named_test_and_toolchain_test_actions_do_not_collide() void = { + let root: str = fresh(); + let suite: str = strings.concat(root, "/suite"); + let named: str = strings.concat(suite, "/a_named"); + let consumer: str = strings.concat(suite, "/b_consumer"); + assert(os.mkdir(suite, 448i32) == 0); + assert(os.mkdir(named, 448i32) == 0); + assert(os.mkdir(consumer, 448i32) == 0); + writefile(strings.concat(named, "/named.ww"), strings.concat( + "package test;\n// MIXED_USER_TEST_PRODUCTION\n", + "fn hidden() i32 = { return 19; };\n", + "export fn value() i32 = { return 42; };\n")); + writefile(strings.concat(named, "/same_test.ww"), strings.concat( + "package test;\n@test fn named_same_runs() void = {", + " assert(hidden() == 19); };\n")); + writefile(strings.concat(named, "/external_test.ww"), strings.concat( + "package test_test;\nimport test;\n", + "@test fn named_external_runs() void = {", + " assert(test.value() == 42); };\n")); + writefile(strings.concat(consumer, "/consumer.ww"), strings.concat( + "package consumer;\nimport test;\n", + "// MIXED_EXPLICIT_TOOLCHAIN_TEST_IMPORT\n", + "export fn value() i32 = { return 42; };\n")); + writefile(strings.concat(consumer, "/same_test.ww"), strings.concat( + "package consumer;\n@test fn consumer_same_runs() void = {", + " assert(value() == 42); };\n")); + writefile(strings.concat(consumer, "/external_test.ww"), strings.concat( + "package consumer_test;\nimport consumer;\n", + "@test fn consumer_external_runs() void = {", + " assert(consumer.value() == 42); };\n")); + let linktrace: str = strings.concat(root, "/mixed-link.trace"); + let linkwrapper: str = strings.concat(root, "/mixed-w6l.sh"); + writefile(linktrace, ""); + writeexecutable(linkwrapper, strings.concat( + "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MIXED_LINK_TRACE\"\n", + "exec \"$WW_MIXED_W6L\" \"$@\"\n")); + let baseenv: []str = os.getenvs(); + let linkenv: []str = alloc([], (baseenv.len + 3): u64)!; + let ei: i32 = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_W6L=") + && !strings.hasprefix(baseenv[ei], "WW_MIXED_LINK_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_MIXED_W6L=")) { + append(linkenv, baseenv[ei]); + }; + ei += 1; + }; + append(linkenv, strings.concat("WW_W6L=", linkwrapper)); + append(linkenv, strings.concat("WW_MIXED_LINK_TRACE=", linktrace)); + append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l"))); + let bins: []str = [strings.concat(named, "/test.test"), + strings.concat(named, "/test_test.test"), + strings.concat(consumer, "/consumer.test"), + strings.concat(consumer, "/consumer_test.test")]; + let keys: []str = ["__ww-test-000-same", "__ww-test-001-external", + "__ww-test-002-same", "__ww-test-003-external"]; + let expected: []str = ["named_same_runs ... ok\n", + "named_external_runs ... ok\n", "consumer_same_runs ... ok\n", + "consumer_external_runs ... ok\n"]; + let workroot: str = strings.concat(bins[0], ".sepwork"); + let work: str = strings.concat(workroot, "/"); + let referenceunits: []str = ["", "", "", ""]; + let stages: []str = ["ww", "ww_ww"]; + let out: commandout; + let si: i32 = 0; + for (si < stages.len) { + let av: []str = [driver(stages[si]), "test", "-c", "-j", "1", + "-I", root, strings.concat(suite, "/...")]; + if (si == 0) { + runcommandenv(root, "mixed-actions-c", av, linkenv, + (120i64 * (time.second: i64)): time.duration, &out); + } else { + runcommand(root, "mixed-actions-ww", av, + (120i64 * (time.second: i64)): time.duration, &out); + }; + expectexit(&out, 0); + let namedprod: str = readfile(strings.concat(work, + "__ww-test-001-external-production.unit.ww")); + let normaltest: str = readfile(strings.concat(work, "test.unit.ww")); + let support: str = readfile(strings.concat(work, "__wwtest.unit.ww")); + assert(has(namedprod, "MIXED_USER_TEST_PRODUCTION")); + assert(!has(normaltest, "MIXED_USER_TEST_PRODUCTION")); + assert(has(normaltest, "//ww:module-reset test\n")); + assert(has(support, "//ww:module-reset __wwtest\n")); + let suffixes: []str = [".unit.ww", ".wwi", ".a"]; + let actions: []str = ["__ww-test-001-external-production", + "test", "__wwtest"]; + let ai: i32 = 0; + for (ai < actions.len) { + let xi: i32 = 0; + for (xi < suffixes.len) { + assert(os.exists(strings.concat(work, actions[ai], suffixes[xi]))); + xi += 1; + }; + ai += 1; + }; + let ri: i32 = 0; + for (ri < keys.len) { + let unit: str = readfile(strings.concat(work, keys[ri], ".unit.ww")); + assert(has(unit, "//ww:module __wwtest\n")); + if (ri == 1 || ri == 2) { + assert(has(unit, "//ww:module test\n")); + }; + if (ri == 3) { + assert(has(unit, "//ww:module consumer\n")); + }; + let runav: []str = [bins[ri]]; + runcommand(root, strings.concat("mixed-run-", stages[si], "-", + keys[ri]), runav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, expected[ri])); + if (si == 0) { referenceunits[ri] = strings.dup(unit); } + else { assert(same(referenceunits[ri], unit)); }; + ri += 1; + }; + if (si == 0) { + let links: str = readfile(linktrace); + assert(occurrences(links, "\n") == 4); + let namedsame: str = linecontaining(links, + strings.concat("-o ", bins[0], " ")); + let namedexternal: str = linecontaining(links, + strings.concat("-o ", bins[1], " ")); + let consumersame: str = linecontaining(links, + strings.concat("-o ", bins[2], " ")); + let consumerexternal: str = linecontaining(links, + strings.concat("-o ", bins[3], " ")); + assert(has(namedsame, strings.concat(work, "__wwtest.a"))); + assert(!has(namedsame, strings.concat(work, "test.a"))); + assert(has(namedexternal, strings.concat(work, + "__ww-test-001-external-production.a"))); + assert(has(namedexternal, strings.concat(work, "__wwtest.a"))); + assert(!has(namedexternal, strings.concat(work, "test.a"))); + assert(has(consumersame, strings.concat(work, "test.a"))); + assert(has(consumersame, strings.concat(work, "__wwtest.a"))); + assert(!has(consumersame, strings.concat(work, + "__ww-test-001-external-production.a"))); + assert(has(consumerexternal, strings.concat(work, "test.a"))); + assert(has(consumerexternal, strings.concat(work, "__wwtest.a"))); + assert(!has(consumerexternal, strings.concat(work, + "__ww-test-001-external-production.a"))); + }; + if (si == 0) { + clean(workroot); + let bi: i32 = 0; + for (bi < bins.len) { clean(bins[bi]); bi += 1; }; + }; + si += 1; + }; + clean(root); +}; + +@test fn long_shared_link_closure_is_complete() void = { + let root: str = fresh(); + let d1: str = strings.concat(root, + "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + let d2: str = strings.concat(d1, + "/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + let d3: str = strings.concat(d2, + "/cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"); + let target: str = strings.concat(d3, "/target"); + assert(os.mkdir(d1, 448i32) == 0); + assert(os.mkdir(d2, 448i32) == 0); + assert(os.mkdir(d3, 448i32) == 0); + assert(os.mkdir(target, 448i32) == 0); + let names: []str = ["p00", "p01", "p02", "p03", "p04", "p05", + "p06", "p07", "p08", "p09", "p10", "p11", "p12", "p13"]; + let i: i32 = 0; + for (i < names.len) { + let dir: str = strings.concat(root, "/", names[i]); + assert(os.mkdir(dir, 448i32) == 0); + let source: str = ""; + if (i + 1 < names.len) { + source = strings.concat("package ", names[i], ";\nimport ", + names[i + 1], ";\nexport fn value() i32 = { return ", + names[i + 1], ".value(); };\n"); + } else { + source = strings.concat("package ", names[i], + ";\nexport fn value() i32 = { return 42; };\n"); + }; + writefile(strings.concat(dir, "/", names[i], ".ww"), source); + i += 1; + }; + writefile(strings.concat(target, "/target.ww"), strings.concat( + "package target;\nimport p00;\n", + "fn value() i32 = { return p00.value(); };\n")); + writefile(strings.concat(target, "/target_test.ww"), strings.concat( + "package target;\n@test fn complete_long_closure() void = {", + " assert(value() == 42); };\n")); + let trace: str = strings.concat(root, "/long-link.trace"); + let wrapper: str = strings.concat(root, "/long-w6l.sh"); + writefile(trace, ""); + writeexecutable(wrapper, strings.concat( + "#!/bin/sh\nfor arg in \"$@\"; do printf '%s\\n' \"$arg\"; done", + " >> \"$WW_LONG_LINK_TRACE\"\nexec \"$WW_LONG_W6L\" \"$@\"\n")); + let baseenv: []str = os.getenvs(); + let env: []str = alloc([], (baseenv.len + 3): u64)!; + let ei: i32 = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_W6L=") + && !strings.hasprefix(baseenv[ei], "WW_LONG_LINK_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_LONG_W6L=")) { + append(env, baseenv[ei]); + }; + ei += 1; + }; + append(env, strings.concat("WW_W6L=", wrapper)); + append(env, strings.concat("WW_LONG_LINK_TRACE=", trace)); + append(env, strings.concat("WW_LONG_W6L=", driver("w6l"))); + let bin: str = strings.concat(target, "/target.test"); + let workroot: str = strings.concat(bin, ".sepwork"); + let work: str = strings.concat(workroot, "/"); + let cargv: []str = [driver("ww"), "test", "-c", "-I", root, target]; + let out: commandout; + runcommandenv(root, "long-link-c", cargv, env, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let cstdout: str = strings.dup(out.stdout); + let cstderr: str = strings.dup(out.stderr); + let cbin: str = readfile(bin); + let rootunit: str = readfile(strings.concat(work, + "__ww-test-000-same.unit.ww")); + assert(has(rootunit, "//ww:module p00\n")); + assert(!has(rootunit, "//ww:module p01\n")); + let linkargs: str = readfile(trace); + assert(linkargs.len > 8192); + assert(!has(linkargs, ".wwi\n")); + i = 0; + for (i < names.len) { + assert(os.exists(strings.concat(work, names[i], ".wwi"))); + assert(os.exists(strings.concat(work, names[i], ".a"))); + assert(has(linkargs, strings.concat(work, names[i], ".a\n"))); + i += 1; + }; + assert(has(linkargs, strings.concat(work, "test.a\n"))); + let runav: []str = [bin]; + runcommand(root, "long-link-run-c", runav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, "complete_long_closure ... ok\n")); + clean(workroot); clean(bin); + let wargv: []str = [driver("ww_ww"), "test", "-c", "-I", root, target]; + runcommand(root, "long-link-ww", wargv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(same(cstdout, out.stdout)); + assert(same(cstderr, out.stderr)); + assert(same(cbin, readfile(bin))); + i = 0; + for (i < names.len) { + assert(os.exists(strings.concat(work, names[i], ".a"))); + i += 1; + }; + runcommand(root, "long-link-run-ww", runav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + assert(has(out.stdout, "complete_long_closure ... ok\n")); + clean(root); +}; + +@test fn multi_directory_test_failures_are_isolated() void = { + let root: str = fresh(); + let suite: str = strings.concat(root, "/suite"); + let alpha: str = strings.concat(suite, "/alpha"); + let beta: str = strings.concat(suite, "/beta"); + assert(os.mkdir(suite, 448i32) == 0); + assert(os.mkdir(alpha, 448i32) == 0); + assert(os.mkdir(beta, 448i32) == 0); + writefile(strings.concat(alpha, "/alpha.ww"), strings.concat( + "package alpha;\n", + "fn hidden() i32 = { return 41; };\n", + "export fn shown() i32 = { return 41; };\n")); + writefile(strings.concat(alpha, "/same_test.ww"), strings.concat( + "package alpha;\n", + "@test fn alpha_same_survives() void = {", + " assert(hidden() == 41); };\n")); + writefile(strings.concat(alpha, "/external_test.ww"), strings.concat( + "package alpha_test;\nimport alpha;\n", + "@test fn alpha_external_fails() void = {", + " assert(alpha.hidden() == 41); };\n")); + writefile(strings.concat(beta, "/beta.ww"), strings.concat( + "package beta;\n", + "fn hidden() i32 = { return 42; };\n", + "export fn shown() i32 = { return 42; };\n")); + writefile(strings.concat(beta, "/same_test.ww"), strings.concat( + "package beta;\n", + "@test fn beta_same_survives() void = {", + " assert(hidden() == 42); };\n")); + writefile(strings.concat(beta, "/external_test.ww"), strings.concat( + "package beta_test;\nimport beta;\n", + "@test fn beta_external_survives() void = {", + " assert(beta.shown() == 42); };\n")); + let stages: []str = ["ww", "ww_ww"]; + let jobs: []str = ["1", "4"]; + let diagnostics: []str = ["", ""]; + let referenceout: str = ""; + let out: commandout; + let i: i32 = 0; + for (i < stages.len) { + let av: []str = [driver(stages[i]), "test", "-j", jobs[i], + "-I", root, strings.concat(suite, "/...")]; + runcommand(root, strings.concat("multidir-failure-", stages[i]), av, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + assert(has(out.stdout, "alpha_same_survives ... ok\n")); + assert(has(out.stdout, "beta_same_survives ... ok\n")); + assert(has(out.stdout, "beta_external_survives ... ok\n")); + assert(has(out.stdout, strings.concat("ok ", alpha, + " [alpha, same-package]\n"))); + assert(has(out.stdout, strings.concat("ok ", beta, + " [beta, same-package]\n"))); + assert(has(out.stdout, strings.concat("ok ", beta, + " [beta_test, external]\n"))); + assert(!has(out.stdout, "alpha_external_fails ...")); + assert(has(out.stderr, "has no exported declaration 'hidden'")); + assert(has(out.stderr, strings.concat("FAIL ", alpha, + " [alpha_test] (build exit 1)\n"))); + assert(!has(out.stderr, strings.concat("FAIL ", alpha, + " [alpha]"))); + assert(!has(out.stderr, strings.concat("FAIL ", beta, " ["))); + let alphapos: i32 = pos(out.stdout, strings.concat("ok ", alpha, + " [alpha, same-package]\n")); + let betapos: i32 = pos(out.stdout, strings.concat("ok ", beta, + " [beta, same-package]\n")); + assert(alphapos >= 0 && alphapos < betapos); + diagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); + if (i == 0) { referenceout = strings.dup(out.stdout); } + else { assert(same(referenceout, out.stdout)); }; + i += 1; + }; + assert(same(diagnostics[0], diagnostics[1])); + clean(root); +}; + // One failing root is a product failure, not permission for the shared build // request to suppress an independently compilable sibling root. @test fn sibling_test_variant_failures_are_isolated() void = { @@ -1049,6 +1931,104 @@ fn workescape(s: str) str = { }; assert(same(externaldiagnostics[0], externaldiagnostics[1])); assert(same(samediagnostics[0], samediagnostics[1])); + + // A first persistent union pass may contain a root-local compiler failure. + // Its successfully committed actions still become reusable: only the + // uncommitted failing root is compiled again on the next request in both + // driver stages. + let warmroot: str = strings.concat(root, "/warm-partial"); + assert(os.mkdir(warmroot, 448i32) == 0); + let trace: str = strings.concat(root, "/warm-partial.trace"); + let wrapper: str = strings.concat(root, "/warm-partial-w6c.sh"); + writefile(trace, ""); + writeexecutable(wrapper, strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n", + "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); + let baseenv: []str = os.getenvs(); + let traceenv: []str = alloc([], (baseenv.len + 3): u64)!; + let ei: i32 = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_W6C=") + && !strings.hasprefix(baseenv[ei], + "WW_PACKAGE_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=")) { + append(traceenv, baseenv[ei]); + }; + ei += 1; + }; + append(traceenv, strings.concat("WW_W6C=", wrapper)); + append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", trace)); + append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); + let warmav: []str = [driver("ww"), "test", "-j", "4", "-w", + warmroot, "-I", root, badexternal]; + runcommandenv(root, "isolated-warm-first", warmav, traceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + let firsttrace: str = readfile(trace); + assert(occurrences(firsttrace, + "/__ww-test-000-same.unit.new") == 1); + assert(occurrences(firsttrace, + "/__ww-test-001-external.unit.new") == 1); + runcommandenv(root, "isolated-warm-reuse", warmav, traceenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + let secondtrace: str = readfile(trace); + assert(occurrences(secondtrace, + "/__ww-test-000-same.unit.new") == 1); + assert(occurrences(secondtrace, + "/__ww-test-001-external.unit.new") == 2); + + let wwtracebin: str = strings.concat(repo(), "/out/partial-trace-", + workescape(root)); + assert(os.mkdir(wwtracebin, 448i32) == 0); + let wwdriver: str = strings.concat(wwtracebin, "/ww_ww"); + let wwtrace: str = strings.concat(root, "/warm-partial-ww.trace"); + let wwwarmroot: str = strings.concat(root, "/warm-partial-ww"); + assert(os.mkdir(wwwarmroot, 448i32) == 0); + writefile(wwtrace, ""); + writeexecutable(wwdriver, readfile(driver("ww_ww"))); + writeexecutable(strings.concat(wwtracebin, "/wwtest"), + readfile(driver("wwtest"))); + writeexecutable(strings.concat(wwtracebin, "/w6c_ww"), strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_COMPILER_TRACE\"\n", + "exec \"$WW_PACKAGE_W6C_WW\" \"$@\"\n")); + writeexecutable(strings.concat(wwtracebin, "/w6a_ww"), + readfile(driver("w6a_ww"))); + writeexecutable(strings.concat(wwtracebin, "/w6l_ww"), + readfile(driver("w6l_ww"))); + let wwenv: []str = alloc([], (baseenv.len + 2): u64)!; + ei = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], + "WW_PACKAGE_WW_COMPILER_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C_WW=")) { + append(wwenv, baseenv[ei]); + }; + ei += 1; + }; + append(wwenv, strings.concat("WW_PACKAGE_WW_COMPILER_TRACE=", wwtrace)); + append(wwenv, strings.concat("WW_PACKAGE_W6C_WW=", driver("w6c_ww"))); + let wwwarmav: []str = [wwdriver, "test", "-j", "4", "-w", + wwwarmroot, "-I", root, badexternal]; + runcommandenv(root, "isolated-warm-ww-first", wwwarmav, wwenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + let wwfirst: str = readfile(wwtrace); + assert(occurrences(wwfirst, + "/__ww-test-000-same.unit.new") == 1); + assert(occurrences(wwfirst, + "/__ww-test-001-external.unit.new") == 1); + runcommandenv(root, "isolated-warm-ww-reuse", wwwarmav, wwenv, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + let wwsecond: str = readfile(wwtrace); + assert(occurrences(wwsecond, + "/__ww-test-000-same.unit.new") == 1); + assert(occurrences(wwsecond, + "/__ww-test-001-external.unit.new") == 2); + clean(wwtracebin); clean(root); }; @@ -1091,8 +2071,9 @@ fn workescape(s: str) str = { (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rootunit: str = readfile(strings.concat(work, - "__ww-test-external.unit.ww")); - let produnit: str = readfile(strings.concat(work, "test.unit.ww")); + "__ww-test-000-external.unit.ww")); + let produnit: str = readfile(strings.concat(work, + "__ww-test-000-external-production.unit.ww")); let supportunit: str = readfile(strings.concat(work, "__wwtest.unit.ww")); assert(has(rootunit, "//ww:module test\n")); @@ -1104,8 +2085,10 @@ fn workescape(s: str) str = { assert(has(produnit, "USER_TEST_PACKAGE_Z")); assert(has(supportunit, "//ww:module-reset __wwtest\n")); assert(has(supportunit, "package test;\n")); - assert(os.exists(strings.concat(work, "test.wwi"))); - assert(os.exists(strings.concat(work, "test.a"))); + assert(os.exists(strings.concat(work, + "__ww-test-000-external-production.wwi"))); + assert(os.exists(strings.concat(work, + "__ww-test-000-external-production.a"))); assert(os.exists(strings.concat(work, "__wwtest.wwi"))); assert(os.exists(strings.concat(work, "__wwtest.a"))); let runav: []str = [bin]; @@ -1187,7 +2170,7 @@ fn workescape(s: str) str = { assert(occurrences(readfile(trace), "shared.unit.ww") == 1); let work: str = strings.concat(bin, ".sepwork/"); let unit: str = readfile(strings.concat(work, - "__ww-test-same.unit.ww")); + "__ww-test-000-same.unit.ww")); assert(has(unit, "//ww:module left\n")); assert(has(unit, "//ww:module right\n")); assert(!has(unit, "//ww:module shared\n")); @@ -1212,12 +2195,16 @@ fn workescape(s: str) str = { let right: str = strings.concat(root, "/right"); let conflict: str = strings.concat(root, "/conflict"); let privatepkg: str = strings.concat(root, "/privatepkg"); + let exportroot: str = strings.concat(root, "/exportroot"); + let exportdep: str = strings.concat(root, "/dupexport"); assert(os.mkdir(missing, 448i32) == 0); assert(os.mkdir(cycle, 448i32) == 0); assert(os.mkdir(left, 448i32) == 0); assert(os.mkdir(right, 448i32) == 0); assert(os.mkdir(conflict, 448i32) == 0); assert(os.mkdir(privatepkg, 448i32) == 0); + assert(os.mkdir(exportroot, 448i32) == 0); + assert(os.mkdir(exportdep, 448i32) == 0); writefile(strings.concat(missing, "/missing.ww"), "package missing;\nfn local() void = { };\n"); writefile(strings.concat(missing, "/missing_test.ww"), strings.concat( @@ -1246,6 +2233,11 @@ fn workescape(s: str) str = { writefile(strings.concat(privatepkg, "/external_test.ww"), strings.concat( "package privatepkg_test;\nimport privatepkg;\n", "@test fn private_use() void = { assert(privatepkg.hidden() == 2); };\n")); + writefile(strings.concat(exportroot, "/exportroot.ww"), strings.concat( + "package dupexport;\nimport dupexport;\n", + "export fn value() i32 = { return dupexport.value(); };\n")); + writefile(strings.concat(exportdep, "/dupexport.ww"), + "package dupexport;\nexport fn value() i32 = { return 1; };\n"); rejectpackagestable(root, "missing", missing, "cannot find package nowhere"); rejectpackagestable(root, "cycle", cycle, "ww: dependency cycle:"); @@ -1253,6 +2245,23 @@ fn workescape(s: str) str = { "conflicting package names conflict and other"); rejectpackagestable(root, "private", privatepkg, "package 'privatepkg' has no exported declaration 'hidden'"); + let exportdiagnostics: []str = ["", ""]; + let stages: []str = ["ww", "ww_ww"]; + let out: commandout; + let i: i32 = 0; + for (i < stages.len) { + let av: []str = [driver(stages[i]), "build", "-p", "-I", root, + "-o", strings.concat(root, "/bad-export-", stages[i], ".a"), + exportroot]; + runcommand(root, strings.concat("duplicate-export-module-", stages[i]), + av, (60i64 * (time.second: i64)): time.duration, &out); + assert(out.termination == exec.termination.EXIT && out.code != 0); + assert(has(out.stderr, + "product closure contains multiple packages named dupexport")); + exportdiagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); + i += 1; + }; + assert(same(exportdiagnostics[0], exportdiagnostics[1])); clean(root); }; @@ -1406,7 +2415,7 @@ fn workescape(s: str) str = { "ww test: -o needs -c for a package target\n")); // -w on a package target forwards to the coordinator: one shared - // persistent driver workdir per directory plan under the root, + // persistent driver workdir for the complete selected request, // reuse owned by the driver's content-identity contract. The // byte stream must match the plain run, cold and warm. let wdroot: str = strings.concat(root, "/wd"); @@ -1454,7 +2463,7 @@ fn workescape(s: str) str = { clean(root); }; -@test fn persistent_package_workdirs_do_not_collide() void = { +@test fn persistent_package_request_workdir_is_stable() void = { let root: str = fresh(); let tree: str = strings.concat(root, "/tree"); let a: str = strings.concat(tree, "/a"); @@ -1483,12 +2492,11 @@ fn workescape(s: str) str = { " [b_c, same-package]\n"))); assert(has(cold, strings.concat("ok ", ab, " [c, same-package]\n"))); - let akey: str = strings.concat(work, "/d_", workescape(a), "_p_", + let key: str = strings.concat(work, "/d_", workescape(tree), "_p_", workescape("b_c")); - let abkey: str = strings.concat(work, "/d_", workescape(ab), "_p_", - workescape("c")); - assert(!same(akey, abkey)); - assert(os.exists(akey) && os.exists(abkey)); + assert(os.exists(key)); + assert(os.exists(strings.concat(key, "/__ww-test-000-same.unit.ww"))); + assert(os.exists(strings.concat(key, "/__ww-test-001-same.unit.ww"))); runcommand(root, "workkey-warm", av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); @@ -1581,6 +2589,104 @@ fn runtimepath(relative: str) str = { clean(root); }; +// Explicit linker search/library flags are retained by the package builder +// and reach both native linkers in their accepted argv forms. This is a +// real manifest-free build/run check, not a parser-only simulation. +@test fn linker_flag_stage_parity() void = { + let root: str = fresh(); + let pkg: str = strings.concat(root, "/mainpkg"); + let extra: str = strings.concat(root, "/extra"); + let linktrace: str = strings.concat(root, "/link.trace"); + let cwrapper: str = strings.concat(root, "/trace-w6l.sh"); + assert(os.mkdir(pkg, 448i32) == 0); + assert(os.mkdir(extra, 448i32) == 0); + writefile(linktrace, ""); + writeexecutable(cwrapper, strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_LINK_FLAG_TRACE\"\n", + "exec \"$WW_LINK_FLAG_W6L\" \"$@\"\n")); + writefile(strings.concat(pkg, "/main.ww"), + "package main;\nfn main() void = {};\n"); + writefile(strings.concat(extra, "/extra.ww"), + "package extra;\nexport fn marker() i32 = { return 7; };\n"); + let extralib: str = strings.concat(root, "/libextra.a"); + let libav: []str = [driver("ww"), "build", "-p", "-o", extralib, + extra]; + let out: commandout; + runcommand(root, "link-flags-library", libav, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let cbin: str = strings.concat(root, "/c-main"); + let wwbin: str = strings.concat(root, "/ww-main"); + let cav: []str = [driver("ww"), "build", "-L", root, + "-l", "extra", "-o", cbin, pkg]; + let baseenv: []str = os.getenvs(); + let cenv: []str = alloc([], (baseenv.len + 3): u64)!; + let ei: i32 = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_W6L=") + && !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_W6L=")) { + append(cenv, baseenv[ei]); + }; + ei += 1; + }; + append(cenv, strings.concat("WW_W6L=", cwrapper)); + append(cenv, strings.concat("WW_LINK_FLAG_TRACE=", linktrace)); + append(cenv, strings.concat("WW_LINK_FLAG_W6L=", driver("w6l"))); + runcommandenv(root, "link-flags-c", cav, cenv, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let ctraced: str = readfile(linktrace); + assert(has(ctraced, strings.concat(" -L ", root, " -l extra"))); + + // WWstage intentionally locates sibling tools. Copy the real driver beside + // a tracing linker and exact compiler/assembler siblings to inspect its + // joined native-linker flag contract. + let tracedir: str = strings.concat(repo(), "/out/link-flag-trace-", + workescape(root)); + assert(os.mkdir(tracedir, 448i32) == 0); + let tracedriver: str = strings.concat(tracedir, "/ww_ww"); + writeexecutable(tracedriver, readfile(driver("ww_ww"))); + writeexecutable(strings.concat(tracedir, "/w6c_ww"), + readfile(driver("w6c_ww"))); + writeexecutable(strings.concat(tracedir, "/w6a_ww"), + readfile(driver("w6a_ww"))); + writeexecutable(strings.concat(tracedir, "/w6l_ww"), strings.concat( + "#!/bin/sh\n", + "printf '%s\\n' \"$*\" >> \"$WW_LINK_FLAG_TRACE\"\n", + "exec \"$WW_LINK_FLAG_W6L_WW\" \"$@\"\n")); + let wenv: []str = alloc([], (baseenv.len + 2): u64)!; + ei = 0; + for (ei < baseenv.len) { + if (!strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_TRACE=") + && !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_W6L_WW=")) { + append(wenv, baseenv[ei]); + }; + ei += 1; + }; + append(wenv, strings.concat("WW_LINK_FLAG_TRACE=", linktrace)); + append(wenv, strings.concat("WW_LINK_FLAG_W6L_WW=", driver("w6l_ww"))); + let twav: []str = [tracedriver, "build", "-L", root, + "-l", "extra", "-o", wwbin, pkg]; + runcommandenv(root, "link-flags-ww", twav, wenv, + (60i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let wtraced: str = readfile(linktrace); + assert(has(wtraced, strings.concat(" -L", root, " -lextra"))); + assert(same(readfile(cbin), readfile(wwbin))); + let crun: []str = [cbin]; + let wrun: []str = [wwbin]; + runcommand(root, "link-flags-c-run", crun, + (30i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + runcommand(root, "link-flags-ww-run", wrun, + (30i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + clean(tracedir); + clean(root); +}; + // The published test binary's OWN command surface: repeated glob args, // -list (with and without a glob), and -timeout-ms. Only a direct // invocation of the `-c` artifact proves these; every driver route