ww: preserve original environment for directory tests
This commit is contained in:
@@ -5850,25 +5850,24 @@ coordinator publication paths are absolute, so the child directory cannot
|
||||
reinterpret them. No runtime coordinator-global `chdir` was added; its cwd and
|
||||
`PWD` remain unchanged.
|
||||
|
||||
Each started product also receives a newly allocated run environment. It
|
||||
retains inherited entries in order except exact `TMPDIR=`, `LC_ALL=`, uppercase
|
||||
`PATH=`, and uppercase `PWD=` entries, then appends `LC_ALL=C`, the
|
||||
product-local absolute `TMPDIR`, the selected toolchain `PATH`, and
|
||||
`PWD=<pkggroup.dir>`. Removing prior `PWD` and `PATH` entries reproduces Go's
|
||||
observable last-wins result because WW's executor intentionally preserves
|
||||
duplicates and WW `os.getenv` returns the first one. Case-distinct and malformed
|
||||
entries remain untouched. The product observes exactly one uppercase `PWD`, at
|
||||
the appended position, and one effective uppercase `PATH`; section 11.32 owns
|
||||
the latter rule.
|
||||
Each started product also receives a newly allocated run environment. Section
|
||||
11.36 supersedes this slice's former test-process locale and temporary-directory
|
||||
policy: the vector is now a Go-like original-environment snapshot. It keeps the
|
||||
first occurrence of each normal case-sensitive key, omits later normal
|
||||
duplicates and raw empty entries, preserves nonempty malformed entries, excludes
|
||||
inherited uppercase `PATH` and `PWD`, and then appends the selected toolchain
|
||||
`PATH` and `PWD=<pkggroup.dir>`. Caller `LC_ALL` and `TMPDIR` therefore reach the
|
||||
user test; build-plan tools retain their separate pinned values.
|
||||
|
||||
The vector and its generated strings are product-local, dynamically sized,
|
||||
and published only after every checked allocation succeeds. Partial failure
|
||||
frees only initialized owned storage and never frees borrowed inherited
|
||||
strings. `exec.start` synchronously deep-copies the command before returning,
|
||||
after which the coordinator frees its run vector, generated `TMPDIR`, generated
|
||||
`PATH`, generated `PWD`, generated `-package` argument, and argv vector.
|
||||
Concurrent children therefore hold independent fork snapshots; no shared
|
||||
environment vector or process-global state is mutated.
|
||||
The vector, normalization table, and generated strings are product-local,
|
||||
dynamically sized, and published only after every checked allocation succeeds.
|
||||
Partial failure frees only initialized owned storage and never frees borrowed
|
||||
inherited strings. The normalization table is gone before launch. `exec.start`
|
||||
synchronously deep-copies the command before returning, after which the
|
||||
coordinator frees its run vector, generated `PATH`, generated `PWD`, generated
|
||||
`-package` argument, and argv vector. Concurrent children therefore hold
|
||||
independent fork snapshots; no shared environment vector or process-global
|
||||
state is mutated.
|
||||
|
||||
All dependency initialization occurs inside that product process. A production
|
||||
or test-only dependency reached by package `p` sees `p`'s directory. If the
|
||||
@@ -5931,8 +5930,8 @@ production and test-only dependency initialization; recompiled external
|
||||
self-import; direct/recursive/redundant/absolute/symlink roots; reversed roots
|
||||
and creation order; `-j 1`/parallel execution; filters/list/no-match; failure
|
||||
and timeout; no-test and build paths; `-c`, `-c -o`, running retained tests,
|
||||
direct binaries, and raw single files; exact tool cwd/argv/locale/TMPDIR/PATH;
|
||||
persistent data-only reuse and
|
||||
direct binaries, and raw single files; original test variables and exact tool
|
||||
cwd/argv/locale/TMPDIR/PATH; persistent data-only reuse and
|
||||
artifact/binary identity; and a deterministic post-build directory removal
|
||||
where the affected product reports `ENOENT` while its sibling succeeds.
|
||||
Checked command-global allocation-failure parity remains owned by
|
||||
@@ -7006,11 +7005,11 @@ The environment is synthesized at the three true test-process launch owners:
|
||||
`run_test_bin`, and WWstage `runsingletest` for raw single-file tests. Each
|
||||
uses the selected driver directory's canonical absolute spelling. The first
|
||||
effective uppercase inherited value is the suffix; absent and empty values
|
||||
have no suffix; ordinary uppercase duplicates are removed. Unrelated entries,
|
||||
including case-distinct and malformed names, retain their previous order and
|
||||
bytes. Directory products retain their established appended locale, temporary
|
||||
directory, and final `PWD`; raw tests retain caller cwd, `PWD`, stdin, and split
|
||||
streams.
|
||||
have no suffix; ordinary uppercase duplicates are removed. For directory
|
||||
products, section 11.36 owns unrelated entries: a Go-like original-environment
|
||||
snapshot retains caller locale and temporary-directory values, case-distinct
|
||||
and nonempty malformed entries, and the final package `PWD`. Raw tests retain
|
||||
caller cwd, `PWD`, other environment entries, stdin, and split streams.
|
||||
|
||||
The rule covers internal, external, and combined directory products;
|
||||
dependency initialization; filters and list mode; running retained tests; and
|
||||
@@ -7466,6 +7465,169 @@ large scheduling sets, failure rollback, stage parity, and artifact-byte
|
||||
identity. No persisted-byte contract changed: build workdir format remains
|
||||
`18`, test workdir format remains `19`, and semantic storage format remains `3`.
|
||||
|
||||
### 11.36 Implemented original environment for directory test processes
|
||||
|
||||
Every directory-owned test binary actually started by `ww test` now receives a
|
||||
Go-like snapshot of the caller environment instead of the package build plan's
|
||||
locale and temporary directory. On the supported Unix boundary, the snapshot
|
||||
keeps the first occurrence of every normal case-sensitive `key=value`, omits
|
||||
later normal duplicates and raw empty entries, and preserves nonempty malformed
|
||||
entries in order. The existing selected-toolchain `PATH` and package-directory
|
||||
`PWD` are then appended as the only test-command overrides. Caller `LC_ALL`,
|
||||
`TMPDIR`, empty-valued variables, case-distinct keys, and arbitrary variables
|
||||
therefore reach initialization and test code.
|
||||
|
||||
#### Pinned Go evidence and fact classification
|
||||
|
||||
The sole authority is official Go 1.26.5 at commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||
|
||||
- `invoke` initializes `cfg.OrigEnv` from
|
||||
`toolchain.FilterEnv(os.Environ())` before command work
|
||||
([`cmd/go/main.go`, lines 290–305](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/main.go#L290-L305)).
|
||||
`FilterEnv` removes only Go's internal toolchain-switch count variable
|
||||
([`cmd/go/internal/toolchain/select.go`, lines 50–59 and 74–85](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/toolchain/select.go#L50-L85));
|
||||
WW has no corresponding switch state.
|
||||
- `OrigEnv` is the startup environment and user binaries during `go test` use
|
||||
it instead of build-tool `CmdEnv`
|
||||
([`cmd/go/internal/cfg/cfg.go`, lines 328–333](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/cfg/cfg.go#L328-L333)).
|
||||
- Unix `copyenv`, `Getenv`, and `Environ` retain the first occurrence of a
|
||||
normal case-sensitive key, clear later duplicates, omit cleared or empty
|
||||
entries from `Environ`, and leave nonempty malformed entries present
|
||||
([`syscall/env_unix.go`, lines 20–50](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/syscall/env_unix.go#L20-L50),
|
||||
[66–84](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/syscall/env_unix.go#L66-L84),
|
||||
and [135–145](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/syscall/env_unix.go#L135-L145)).
|
||||
- `(*runTestActor).Act` clips `cfg.OrigEnv`, applies `AppendPATH` and
|
||||
`AppendPWD`, assigns the result to the package-directory command, and runs it
|
||||
([`cmd/go/internal/test/test.go`, lines 1661–1697](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1661-L1697)).
|
||||
The two appenders are defined at
|
||||
[`cmd/go/internal/base/env.go`, lines 15–45](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/base/env.go#L15-L45),
|
||||
and explicit-command duplicate removal prefers their later `PATH` and `PWD`
|
||||
values
|
||||
([`os/exec/exec.go`, lines 1231–1308](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/exec/exec.go#L1231-L1308)).
|
||||
- Official `test_env_term.txt` passes an explicitly empty caller `TERM` to a
|
||||
test and requires it to remain empty
|
||||
([lines 1–14](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_env_term.txt#L1-L14)).
|
||||
`test_cache_inputs.txt` changes caller `TESTKEY` and its `TestLookupEnv`
|
||||
requires the arbitrary variable to be present
|
||||
([lines 19–38](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_cache_inputs.txt#L19-L38)
|
||||
and [269–280](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_cache_inputs.txt#L269-L280)).
|
||||
|
||||
Those source rules and official assertions are behavior directly implemented or
|
||||
asserted by pinned Go. That an ordinary caller `LC_ALL`, `TMPDIR`, case-distinct
|
||||
key, or other variable survives unchanged is behavior derived from the pinned
|
||||
pipeline: none is removed or replaced after the original snapshot. First-value
|
||||
normalization, malformed-entry retention, raw-empty omission, and the separation
|
||||
from build-tool `CmdEnv` are directly implemented by the cited source.
|
||||
|
||||
#### Fresh four-axis audit and direct pre-fix measurements
|
||||
|
||||
The bounded audit considered all four permanent axes and selected only this
|
||||
test-runtime difference:
|
||||
|
||||
- **Go-like build:** pinned unresolved relocation handling gives missing
|
||||
`main.main` its dedicated link failure
|
||||
([`cmd/link/internal/ld/errors.go`, lines 45–65](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/errors.go#L45-L65)),
|
||||
asserted by `TestUndefinedRelocErrors`
|
||||
([`cmd/link/internal/ld/ld_test.go`, lines 20–45](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/ld_test.go#L20-L45)).
|
||||
Both WW stages rejected a selected `package main` without `fn main`, emitted
|
||||
identical linker diagnostics, and created no output. This candidate was
|
||||
aligned.
|
||||
- **Go-like test:** a direct `execve` arranger supplied duplicate `LC_ALL`,
|
||||
`TMPDIR`, arbitrary, and `PWD` variables; one empty-valued normal variable; a
|
||||
case-distinct key; repeated nonempty malformed entries; and one raw empty
|
||||
entry. Both stages exited `0` with empty stderr and byte-identical stdout
|
||||
(SHA-256
|
||||
`5b541239c8e9109c512b6ec7b8c59d4b18bf79391096fb14891d8de108786993`).
|
||||
The test reported caller arbitrary/empty/case-distinct values, but reported
|
||||
`LC_ALL` and `TMPDIR` as changed, two visible occurrences of the arbitrary
|
||||
normal key, and the raw empty entry still present. These are directly measured
|
||||
pre-fix WW facts and establish the selected difference.
|
||||
- **Go-like package:** `MultiplePackageError` and the directory scan reject
|
||||
conflicting selected declarations
|
||||
([`go/build/build.go`, lines 538–549](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L549)
|
||||
and [939–967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L967));
|
||||
`TestMultiplePackageImport` plus official `testdata/multi` asserts the result
|
||||
([`go/build/build_test.go`, lines 105–124](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L105-L124)).
|
||||
Both WW stages rejected an `alpha`/`beta` source directory with byte-identical
|
||||
diagnostics and no output. This candidate was aligned.
|
||||
- **Go-like import:** `unusedImports` and `errorUnusedPkg` require a nonblank
|
||||
renamed import to be used
|
||||
([`cmd/compile/internal/types2/resolver.go`, lines 706–740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740));
|
||||
official `importdecl0` asserts the alias case
|
||||
([`internal/types/testdata/check/importdecl0/importdecl0a.go`, lines 5–31](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L5-L31)).
|
||||
Both WW stages rejected an unused local alias for dotted import `fmt` with
|
||||
the same semantic diagnostic and no output. This candidate was aligned.
|
||||
|
||||
The command observations above are directly measured WW behavior. The linked
|
||||
rules are behavior directly implemented or asserted by pinned Go. Applying the
|
||||
original-environment pipeline at WW's directory-product launcher while leaving
|
||||
its raw single-file compatibility route intact is behavior derived from the
|
||||
pinned launch boundary and WW's local input model.
|
||||
|
||||
#### Ownership, final behavior, and preserved boundaries
|
||||
|
||||
`internal/wwpackage.runenv`, called only by `pkgstartrun`, is the semantic owner.
|
||||
It walks the coordinator's inherited vector in order, uses a bounded fallible
|
||||
open-addressed key table to retain the first normal case-sensitive occurrence,
|
||||
omits raw empty entries, retains nonempty malformed entries, and excludes exact
|
||||
uppercase `PATH` and `PWD`. It then appends the existing canonical selected
|
||||
driver `PATH` and `PWD=<pkggroup.dir>`. The table is freed before launch;
|
||||
`exec.start` deep-copies the command, after which the product-local vector and
|
||||
its two generated strings are freed. Concurrent products share no writable
|
||||
environment storage and the coordinator process is never mutated.
|
||||
|
||||
`toolenv` remains the separate build-plan owner. Compiler, assembler, in-driver
|
||||
archiver, linker, support generation, generated-main construction, request
|
||||
scratch, cwd, argv, stdin, diagnostics, and failure precedence are unchanged;
|
||||
those tools still receive their established `LC_ALL=C` and request-private
|
||||
`TMPDIR`. The raw single-file route already preserved caller locale and
|
||||
temporary-directory values and remains outside this directory-owned
|
||||
normalization slice. A directly invoked retained binary still inherits its
|
||||
invoker's concrete environment without coordinator policy.
|
||||
|
||||
Loading and platform source selection are unchanged. Production, internal-test,
|
||||
external-test, recompiled-for-test, support, and generated-main graph/action
|
||||
identity remain separate and unchanged. Canonical dotted package/import
|
||||
identity, declared names, file import bindings, physical-directory metadata,
|
||||
symbols, `.wwi`, source units, assembly, objects, archives, executables, modes,
|
||||
and artifact bytes do not contain the run environment. Imported or dependency
|
||||
initialization code observes the corrected values only inside the selected
|
||||
product process; no physical path or environment value becomes package, import,
|
||||
graph, action, artifact, publication, or persistence identity.
|
||||
|
||||
`ww build`, `ww run`, directory `ww test -c` or `-S`, no-test products, and
|
||||
loading/compiler/linker rejection start no test process, allocate no run
|
||||
environment, and retain their prior diagnostics and outputs. A started test
|
||||
observes the corrected snapshot before success, assertion failure, signal,
|
||||
timeout, interruption, or child-created descendants. Those outcomes continue
|
||||
through the existing process-group, capture, ordered-result, cancellation, and
|
||||
cleanup owners. For a running retained request, private build and run still
|
||||
precede guarded installation: runtime failure publishes nothing and preserves
|
||||
prior bytes, while success installs the same private executable bytes. Producer
|
||||
failure, output guard failure, and cleanup-only failure retain their existing
|
||||
rollback and diagnostic precedence.
|
||||
|
||||
Persistent work records and artifact invalidation are unchanged; test results
|
||||
are never cached. Environment-only changes perform the established warm final
|
||||
link and always run the private test, without changing committed action bytes.
|
||||
No `.new`, `.install`, `.wwtxn.*`, capture, result, process, or request scratch
|
||||
survives its existing cleanup boundary. This runtime-only metadata change alters
|
||||
no persisted-byte contract, so build workdir format remains `18`, test workdir
|
||||
format remains `19`, and semantic storage format remains `3`.
|
||||
|
||||
The expanded WW-native `directory_test_execution_working_directory` observer
|
||||
proves both-stage equality for nonempty and empty-valued variables; caller
|
||||
`LC_ALL` and `TMPDIR`; first-wins normal duplicates; case-distinct and repeated
|
||||
malformed entries; raw-empty omission; package `PWD` and selected-toolchain
|
||||
`PATH`; production and test-only dependency initialization; internal, external,
|
||||
combined, and test-only products; filters/list/no-match; recursive/equivalent
|
||||
selection; serial/parallel scheduling; failure, signal, timeout, and child setup
|
||||
failure; retained success and rollback; cold/warm/data-only persistence;
|
||||
unchanged artifact and binary bytes; build/no-test/compile-only/rejection
|
||||
nonexecution; unchanged raw/direct compatibility; exact tool locale/TMPDIR; and
|
||||
stage, transaction, capture, and workdir cleanup.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
11
docs/spec.md
11
docs/spec.md
@@ -770,6 +770,17 @@ compatibility route. It does not apply to build tools, `ww build`, `ww run`,
|
||||
compile-only or assembly-only test requests, no-test products, or later direct
|
||||
execution of a retained binary.
|
||||
|
||||
An executed directory product otherwise receives the caller's original Unix
|
||||
environment, not the build plan's tool environment. The snapshot keeps the
|
||||
first occurrence of each normal case-sensitive `key=value`, omits later normal
|
||||
duplicates and raw empty entries, and retains every nonempty malformed entry in
|
||||
order. It then applies only the effective `PATH` above and the package `PWD`.
|
||||
Consequently caller `LC_ALL`, `TMPDIR`, empty-valued variables, case-distinct
|
||||
keys, and arbitrary variables remain visible to production or test-only
|
||||
initialization and test bodies. The compiler, assembler, archiver, linker, and
|
||||
coordinator scratch continue to use their separate build-plan locale and
|
||||
temporary directory.
|
||||
|
||||
The same coordinator-executed product reads standard input from the null
|
||||
device. Its first read observes EOF regardless of the terminal, pipe, or file
|
||||
connected to the invoking command. Each parallel product owns a separate null
|
||||
|
||||
@@ -305,6 +305,16 @@ the private run and install only after success. Compile-only, assembly-only,
|
||||
no-test, rejected, and build requests start no test process and therefore have
|
||||
no test-PATH state.
|
||||
|
||||
For a directory product, the rest of the child vector is a Go-like snapshot of
|
||||
the caller environment rather than the build-tool environment: the first
|
||||
case-sensitive occurrence of every normal `key=value` survives, later normal
|
||||
duplicates and raw empty entries do not, and nonempty malformed entries remain
|
||||
in order. Only the effective `PATH` and package `PWD` are then appended.
|
||||
Caller `LC_ALL`, `TMPDIR`, empty values, case-distinct keys, and arbitrary
|
||||
variables therefore reach dependency initialization and selected tests.
|
||||
Build-plan compiler, assembler, archiver, linker, and scratch environments keep
|
||||
their existing pinned locale and request-private temporary directory.
|
||||
|
||||
Every such product also receives an independently opened null device as fd 0.
|
||||
Caller terminal, pipe, and file bytes remain with the coordinator; serial and
|
||||
parallel products observe immediate EOF rather than consuming a shared input
|
||||
|
||||
@@ -116,6 +116,11 @@ fn pkgallocstrs(cap: i32) ([]str | nomem) = {
|
||||
return value;
|
||||
};
|
||||
|
||||
fn pkgalloci32(cap: i32) ([]i32 | nomem) = {
|
||||
let value: []i32 = alloc([], cap: u64)?;
|
||||
return value;
|
||||
};
|
||||
|
||||
fn pkgallocbytes(cap: i32) ([]u8 | nomem) = {
|
||||
let value: []u8 = alloc([], cap: u64)?;
|
||||
return value;
|
||||
@@ -268,11 +273,81 @@ fn pkgfreestrs(values: []str) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// Go's AppendPATH and AppendPWD append, then os/exec keeps the last duplicate
|
||||
// values. WW's executor preserves duplicates, so discard earlier exact entries
|
||||
// and materialize the selected driver's sibling directory first in PATH.
|
||||
fn runenv(tmpdir: str, pwd: str, builder: str, out: *[]str, tmpowned: *str,
|
||||
pathowned: *str, pwdowned: *str) bool = {
|
||||
fn pkgenvkeylen(entry: str) i32 = {
|
||||
let i: i32 = 0;
|
||||
for (i < entry.len) {
|
||||
if (entry[i] == '=') { return i; };
|
||||
i += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pkgenvkeyequal(a: str, an: i32, b: str, bn: i32) bool = {
|
||||
if (an != bn) { return false; };
|
||||
let i: i32 = 0;
|
||||
for (i < an) {
|
||||
if (a[i] != b[i]) { return false; };
|
||||
i += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
fn pkgenvkeyis(entry: str, n: i32, name: str) bool = {
|
||||
return pkgenvkeyequal(entry, n, name, name.len);
|
||||
};
|
||||
|
||||
fn pkgenvhash(entry: str, n: i32) u64 = {
|
||||
let h: u64 = 14695981039346656037u64;
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
h = h ^ (entry[i]: u64);
|
||||
h = h * 1099511628211u64;
|
||||
i += 1;
|
||||
};
|
||||
return h;
|
||||
};
|
||||
|
||||
fn pkgenvtablecap(entries: i32) i32 = {
|
||||
if (entries == 0) { return 0; };
|
||||
if (entries > PKG_COUNT_MAX / 2) { return -1; };
|
||||
let need: i32 = entries * 2;
|
||||
let cap: i32 = PKG_INITIAL_CAP;
|
||||
for (cap < need) {
|
||||
if (cap > PKG_COUNT_MAX / 2) { return -1; };
|
||||
cap *= 2;
|
||||
};
|
||||
return cap;
|
||||
};
|
||||
|
||||
// Go snapshots user-test variables through Unix os.Environ before appending
|
||||
// PATH and PWD. WW's raw environment walker and executor do no such duplicate
|
||||
// normalization, so the launch owner must materialize that snapshot itself.
|
||||
fn pkgenvfirst(slots: []i32, inherited: []str, at: i32, n: i32) bool = {
|
||||
let slot: i32 = (pkgenvhash(inherited[at], n)
|
||||
% (slots.len: u64)): i32;
|
||||
let probes: i32 = 0;
|
||||
for (probes < slots.len) {
|
||||
let prior: i32 = slots[slot];
|
||||
if (prior < 0) {
|
||||
slots[slot] = at;
|
||||
return true;
|
||||
};
|
||||
let priorn: i32 = pkgenvkeylen(inherited[prior]);
|
||||
if (pkgenvkeyequal(inherited[at], n,
|
||||
inherited[prior], priorn)) {
|
||||
return false;
|
||||
};
|
||||
slot += 1;
|
||||
if (slot == slots.len) { slot = 0; };
|
||||
probes += 1;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// Go's appended PATH and PWD survive os/exec's later-value selection. WW's
|
||||
// executor preserves its concrete vector, so inherited copies are excluded.
|
||||
fn runenv(pwd: str, builder: str, out: *[]str, pathowned: *str,
|
||||
pwdowned: *str) bool = {
|
||||
let toolbin: str;
|
||||
let oom: bool = false;
|
||||
if (!pkgcanonicaldir(pkgdirname(builder), &toolbin, &oom)) {
|
||||
@@ -283,35 +358,71 @@ fn runenv(tmpdir: str, pwd: str, builder: str, out: *[]str, tmpowned: *str,
|
||||
return false;
|
||||
};
|
||||
let inherited: []str = os.getenvs();
|
||||
if (inherited.len > PKG_COUNT_MAX - 4) {
|
||||
if (inherited.len > PKG_COUNT_MAX - 2) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: package graph is too large");
|
||||
return false;
|
||||
};
|
||||
let allocation: ([]str | nomem) = pkgallocstrs(inherited.len + 4);
|
||||
let allocation: ([]str | nomem) = pkgallocstrs(inherited.len + 2);
|
||||
let env: []str;
|
||||
match (allocation) {
|
||||
case let value: []str => env = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: out of memory");
|
||||
return false;
|
||||
};
|
||||
};
|
||||
};
|
||||
let keyed: i32 = 0;
|
||||
let i: i32 = 0;
|
||||
for (i < inherited.len) {
|
||||
if (!strings.hasprefix(inherited[i], "TMPDIR=")
|
||||
&& !strings.hasprefix(inherited[i], "LC_ALL=")
|
||||
&& !strings.hasprefix(inherited[i], "PATH=")
|
||||
&& !strings.hasprefix(inherited[i], "PWD=")) {
|
||||
let n: i32 = pkgenvkeylen(inherited[i]);
|
||||
if (n >= 0 && !pkgenvkeyis(inherited[i], n, "PATH")
|
||||
&& !pkgenvkeyis(inherited[i], n, "PWD")) {
|
||||
keyed += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
let tablecap: i32 = pkgenvtablecap(keyed);
|
||||
if (tablecap < 0) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: package graph is too large");
|
||||
pkgfreestrs(env);
|
||||
return false;
|
||||
};
|
||||
let slots: []i32;
|
||||
slots.ptr = nil: *i32;
|
||||
slots.len = 0;
|
||||
slots.cap = 0;
|
||||
if (tablecap != 0) {
|
||||
let tableallocation: ([]i32 | nomem) = pkgalloci32(tablecap);
|
||||
match (tableallocation) {
|
||||
case let value: []i32 => slots = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: out of memory");
|
||||
pkgfreestrs(env);
|
||||
return false;
|
||||
};
|
||||
};
|
||||
slots.len = tablecap;
|
||||
i = 0;
|
||||
for (i < slots.len) { slots[i] = -1; i += 1; };
|
||||
};
|
||||
i = 0;
|
||||
for (i < inherited.len) {
|
||||
let n: i32 = pkgenvkeylen(inherited[i]);
|
||||
if (n < 0) {
|
||||
if (inherited[i].len != 0) { append(env, inherited[i]); };
|
||||
} else if (!pkgenvkeyis(inherited[i], n, "PATH")
|
||||
&& !pkgenvkeyis(inherited[i], n, "PWD")
|
||||
&& pkgenvfirst(slots, inherited, i, n)) {
|
||||
append(env, inherited[i]);
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
append(env, "LC_ALL=C");
|
||||
let tmpenv: str;
|
||||
if (!pkgstring(&tmpenv, "TMPDIR=", tmpdir)) {
|
||||
pkgfreestrs(env);
|
||||
return false;
|
||||
if (slots.ptr != nil) {
|
||||
os.free(slots.ptr: *void,
|
||||
(slots.cap: u64) * (size(i32): u64));
|
||||
};
|
||||
let pathenv: str;
|
||||
let pathok: bool = false;
|
||||
@@ -327,29 +438,24 @@ fn runenv(tmpdir: str, pwd: str, builder: str, out: *[]str, tmpowned: *str,
|
||||
case void => pathok = pkgstring(&pathenv, "PATH=", toolbin);
|
||||
};
|
||||
if (!pathok) {
|
||||
pkgfreeownedstr(tmpenv);
|
||||
pkgfreestrs(env);
|
||||
return false;
|
||||
};
|
||||
let pwdenv: str;
|
||||
if (!pkgstring(&pwdenv, "PWD=", pwd)) {
|
||||
pkgfreeownedstr(tmpenv);
|
||||
pkgfreeownedstr(pathenv);
|
||||
pkgfreestrs(env);
|
||||
return false;
|
||||
};
|
||||
append(env, tmpenv);
|
||||
append(env, pathenv);
|
||||
append(env, pwdenv);
|
||||
*out = env;
|
||||
*tmpowned = tmpenv;
|
||||
*pathowned = pathenv;
|
||||
*pwdowned = pwdenv;
|
||||
return true;
|
||||
};
|
||||
|
||||
fn freerunenv(env: []str, tmpowned: str, pathowned: str, pwdowned: str) void = {
|
||||
pkgfreeownedstr(tmpowned);
|
||||
fn freerunenv(env: []str, pathowned: str, pwdowned: str) void = {
|
||||
pkgfreeownedstr(pathowned);
|
||||
pkgfreeownedstr(pwdowned);
|
||||
pkgfreestrs(env);
|
||||
@@ -1624,11 +1730,9 @@ fn pkgstartrun(g: *pkggroup, filters: []str, timeoutarg: str,
|
||||
let i: i32 = 0;
|
||||
for (i < filters.len) { append(ra, filters[i]); i += 1; };
|
||||
let env: []str;
|
||||
let tmpowned: str;
|
||||
let pathowned: str;
|
||||
let pwdowned: str;
|
||||
if (!runenv(g.root, g.dir, builder, &env, &tmpowned,
|
||||
&pathowned, &pwdowned)) {
|
||||
if (!runenv(g.dir, builder, &env, &pathowned, &pwdowned)) {
|
||||
pkgfreeownedstr(packagearg);
|
||||
pkgfreestrs(ra);
|
||||
return false;
|
||||
@@ -1644,7 +1748,7 @@ fn pkgstartrun(g: *pkggroup, filters: []str, timeoutarg: str,
|
||||
rcmd.deadline.nsec = 0i64;
|
||||
rcmd.grace = 0i64: time.duration;
|
||||
exec.start(h, &rcmd);
|
||||
freerunenv(env, tmpowned, pathowned, pwdowned);
|
||||
freerunenv(env, pathowned, pwdowned);
|
||||
pkgfreeownedstr(packagearg);
|
||||
pkgfreestrs(ra);
|
||||
return true;
|
||||
|
||||
@@ -633,11 +633,16 @@ fn hexbytes(value: str) str = {
|
||||
|
||||
fn cwdtestenv(stage: str) []str = {
|
||||
let inherited: []str = os.getenvs();
|
||||
let env: []str = alloc([], (inherited.len + 10): u64)!;
|
||||
let env: []str = alloc([], (inherited.len + 24): u64)!;
|
||||
let i: i32 = 0;
|
||||
for (i < inherited.len) {
|
||||
if (!strings.hasprefix(inherited[i], "PWD=")
|
||||
&& !strings.hasprefix(inherited[i], "PATH=")
|
||||
&& !strings.hasprefix(inherited[i], "LC_ALL=")
|
||||
&& !strings.hasprefix(inherited[i], "TMPDIR=")
|
||||
&& !strings.hasprefix(inherited[i], "WW_CWD_ORIGINAL=")
|
||||
&& !strings.hasprefix(inherited[i], "WW_CWD_EMPTY=")
|
||||
&& !strings.hasprefix(inherited[i], "lc_all=")
|
||||
&& !strings.hasprefix(inherited[i], "WW_W6C=")
|
||||
&& !strings.hasprefix(inherited[i], "WW_W6A=")
|
||||
&& !strings.hasprefix(inherited[i], "WW_W6L=")
|
||||
@@ -649,6 +654,14 @@ fn cwdtestenv(stage: str) []str = {
|
||||
};
|
||||
append(env, "PATH=/ww-path-inherited-first");
|
||||
append(env, "PWD=/ww-cwd-inherited-first");
|
||||
append(env, "LC_ALL=C.UTF-8");
|
||||
append(env, "TMPDIR=/ww-caller-tmp-first");
|
||||
append(env, "WW_CWD_ORIGINAL=first");
|
||||
append(env, "WW_CWD_EMPTY=");
|
||||
append(env, "lc_all=case-kept");
|
||||
append(env, "WW_CWD_MALFORMED");
|
||||
append(env, "");
|
||||
append(env, "WW_CWD_MALFORMED");
|
||||
if (same(stage, "ww")) {
|
||||
append(env, strings.concat("WW_W6C=", driver("w6c")));
|
||||
append(env, strings.concat("WW_W6A=", driver("w6a")));
|
||||
@@ -660,6 +673,9 @@ fn cwdtestenv(stage: str) []str = {
|
||||
append(env, strings.concat("WW_W6L=", driver("w6l_ww")));
|
||||
};
|
||||
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
|
||||
append(env, "LC_ALL=C");
|
||||
append(env, "TMPDIR=/ww-caller-tmp-last");
|
||||
append(env, "WW_CWD_ORIGINAL=last");
|
||||
append(env, "PATH=/ww-path-inherited-last");
|
||||
append(env, "PWD=/ww-cwd-inherited-last");
|
||||
return env;
|
||||
@@ -679,6 +695,42 @@ fn cwdassertcallerpath(text: str, label: str) void = {
|
||||
cwdassertpath(text, label, "/ww-path-inherited-first", "2");
|
||||
};
|
||||
|
||||
fn cwdassertenvvalues(text: str, label: str) void = {
|
||||
assert(has(text, strings.concat(label, " LC_ALL=C.UTF-8\n")));
|
||||
assert(has(text, strings.concat(label,
|
||||
" TMPDIR=/ww-caller-tmp-first\n")));
|
||||
assert(has(text, strings.concat(label,
|
||||
" WW_CWD_ORIGINAL=first\n")));
|
||||
assert(has(text, strings.concat(label, " WW_CWD_EMPTY=\n")));
|
||||
assert(has(text, strings.concat(label, " lc_all=case-kept\n")));
|
||||
};
|
||||
|
||||
fn cwdassertdirectoryenv(text: str, label: str) void = {
|
||||
cwdassertenvvalues(text, label);
|
||||
assert(has(text, strings.concat(label, " LC_ALL-count=1\n")));
|
||||
assert(has(text, strings.concat(label, " TMPDIR-count=1\n")));
|
||||
assert(has(text, strings.concat(label,
|
||||
" WW_CWD_ORIGINAL-count=1\n")));
|
||||
assert(has(text, strings.concat(label,
|
||||
" WW_CWD_EMPTY-count=1\n")));
|
||||
assert(has(text, strings.concat(label, " lc_all-count=1\n")));
|
||||
assert(has(text, strings.concat(label, " malformed-count=2\n")));
|
||||
assert(has(text, strings.concat(label, " empty-entry-count=0\n")));
|
||||
};
|
||||
|
||||
fn cwdassertcallerenv(text: str, label: str) void = {
|
||||
cwdassertenvvalues(text, label);
|
||||
assert(has(text, strings.concat(label, " LC_ALL-count=2\n")));
|
||||
assert(has(text, strings.concat(label, " TMPDIR-count=2\n")));
|
||||
assert(has(text, strings.concat(label,
|
||||
" WW_CWD_ORIGINAL-count=2\n")));
|
||||
assert(has(text, strings.concat(label,
|
||||
" WW_CWD_EMPTY-count=1\n")));
|
||||
assert(has(text, strings.concat(label, " lc_all-count=1\n")));
|
||||
assert(has(text, strings.concat(label, " malformed-count=2\n")));
|
||||
assert(has(text, strings.concat(label, " empty-entry-count=1\n")));
|
||||
};
|
||||
|
||||
fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
|
||||
pwdcount: str, pwdlast: str, data: str, testdata: str,
|
||||
input: str) void = {
|
||||
@@ -724,6 +776,7 @@ fn cwdassertpackage(text: str, label: str, dir: str, data: str,
|
||||
testdata: str) void = {
|
||||
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
|
||||
"eof");
|
||||
cwdassertdirectoryenv(text, label);
|
||||
cwdasserttestpath(text, label);
|
||||
cwdassertmergedstreams(text, label, dir);
|
||||
};
|
||||
@@ -795,10 +848,10 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
clean(root);
|
||||
};
|
||||
|
||||
// Directory-package execution is one product-level runtime property. This
|
||||
// fixture keeps every source tree disposable while covering the variant,
|
||||
// dependency, routing, concurrency, persistence, and failure boundaries that
|
||||
// could otherwise accidentally derive a cwd from an action or request name.
|
||||
// Directory-package execution context is one product-level runtime property.
|
||||
// This fixture keeps every source tree disposable while covering the variant,
|
||||
// dependency, environment, routing, concurrency, persistence, and failure
|
||||
// boundaries that could otherwise leak build-plan or caller process state.
|
||||
@test fn directory_test_execution_working_directory() void = {
|
||||
let root: str = fresh();
|
||||
let caller: str = strings.concat(root, "/caller");
|
||||
@@ -872,12 +925,22 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
"};\n",
|
||||
"fn put(value: str) void = { putfd(os.STDOUT_FILENO, value); };\n",
|
||||
"fn puterr(value: str) void = { putfd(os.STDERR_FILENO, value); };\n",
|
||||
"fn putcount(value: i32) void = {\n",
|
||||
" if (value == 0) { put(\"0\\n\"); } else { if (value == 1) {\n",
|
||||
" put(\"1\\n\"); } else { if (value == 2) { put(\"2\\n\");\n",
|
||||
" } else { put(\"many\\n\"); }; }; };\n",
|
||||
"};\n",
|
||||
"export fn streams(label: str, context: str) void = {\n",
|
||||
" put(strings.concat(\"STREAM \", label, \" out-1 cwd=\", context, \"\\n\"));\n",
|
||||
" puterr(strings.concat(\"STREAM \", label, \" err-1 cwd=\", context, \"\\n\"));\n",
|
||||
" put(strings.concat(\"STREAM \", label, \" out-2 cwd=\", context, \"\\n\"));\n",
|
||||
" puterr(strings.concat(\"STREAM \", label, \" err-2 cwd=\", context, \"\\n\"));\n",
|
||||
"};\n");
|
||||
let envsource: str = strings.concat(
|
||||
" put(label); put(\" LC_ALL=\");\n match (os.getenv(\"LC_ALL\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" TMPDIR=\");\n match (os.getenv(\"TMPDIR\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" WW_CWD_ORIGINAL=\");\n match (os.getenv(\"WW_CWD_ORIGINAL\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" WW_CWD_EMPTY=\");\n match (os.getenv(\"WW_CWD_EMPTY\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" lc_all=\");\n match (os.getenv(\"lc_all\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n",
|
||||
" let lccount: i32 = 0; let tmpcount: i32 = 0;\n let originalcount: i32 = 0; let emptyvaluecount: i32 = 0;\n let lowercount: i32 = 0; let malformedcount: i32 = 0;\n let emptyentrycount: i32 = 0;\n i = 0; for (i < env.len) {\n if (strings.hasprefix(env[i], \"LC_ALL=\")) { lccount += 1; };\n if (strings.hasprefix(env[i], \"TMPDIR=\")) { tmpcount += 1; };\n if (strings.hasprefix(env[i], \"WW_CWD_ORIGINAL=\")) { originalcount += 1; };\n if (strings.hasprefix(env[i], \"WW_CWD_EMPTY=\")) { emptyvaluecount += 1; };\n if (strings.hasprefix(env[i], \"lc_all=\")) { lowercount += 1; };\n if (strings.compare(env[i], \"WW_CWD_MALFORMED\") == 0) { malformedcount += 1; };\n if (env[i].len == 0) { emptyentrycount += 1; };\n i += 1;\n };\n",
|
||||
" put(label); put(\" PWD-count=\"); putcount(count);\n put(label); put(\" PWD-last=\");\n if (last == env.len - 1) { put(\"yes\\n\"); } else { put(\"no\\n\"); };\n put(label); put(\" PATH-count=\"); putcount(pathcount);\n",
|
||||
" put(label); put(\" LC_ALL-count=\"); putcount(lccount);\n put(label); put(\" TMPDIR-count=\"); putcount(tmpcount);\n put(label); put(\" WW_CWD_ORIGINAL-count=\"); putcount(originalcount);\n put(label); put(\" WW_CWD_EMPTY-count=\"); putcount(emptyvaluecount);\n put(label); put(\" lc_all-count=\"); putcount(lowercount);\n put(label); put(\" malformed-count=\"); putcount(malformedcount);\n put(label); put(\" empty-entry-count=\"); putcount(emptyentrycount);\n");
|
||||
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
|
||||
"package probe;\n",
|
||||
"import os;\n",
|
||||
@@ -902,16 +965,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
" if (body.len == 0 || body[body.len - 1] != '\\n') { put(\"\\n\"); };\n",
|
||||
"};\n",
|
||||
"export fn run(label: str) void = {\n let buf: [4096]u8;\n let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n streams(label, cwd);\n put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n put(label); put(\" PWD=\");\n match (os.getenv(\"PWD\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" PATH=\");\n match (os.getenv(\"PATH\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n let env: []str = os.getenvs(); let count: i32 = 0; let pathcount: i32 = 0; let last: i32 = -1;\n let i: i32 = 0; for (i < env.len) {\n if (strings.hasprefix(env[i], \"PWD=\")) { count += 1; last = i; };\n if (strings.hasprefix(env[i], \"PATH=\")) { pathcount += 1; };\n i += 1;\n };\n",
|
||||
" put(label); put(\" PWD-count=\");\n",
|
||||
" if (count == 0) { put(\"0\\n\"); } else { if (count == 1) {\n",
|
||||
" put(\"1\\n\"); } else { if (count == 2) { put(\"2\\n\");\n",
|
||||
" } else { put(\"many\\n\"); }; }; };\n",
|
||||
" put(label); put(\" PWD-last=\");\n",
|
||||
" if (last == env.len - 1) { put(\"yes\\n\"); } else { put(\"no\\n\"); };\n",
|
||||
" put(label); put(\" PATH-count=\");\n",
|
||||
" if (pathcount == 0) { put(\"0\\n\"); } else { if (pathcount == 1) {\n",
|
||||
" put(\"1\\n\"); } else { if (pathcount == 2) { put(\"2\\n\");\n",
|
||||
" } else { put(\"many\\n\"); }; }; };\n",
|
||||
envsource,
|
||||
" emitfile(label, \"data\", \"data.txt\");\n",
|
||||
" emitfile(label, \"testdata\", \"testdata/input.txt\");\n",
|
||||
" let input: [1]u8;\n let inputn: i64 = os.read(os.STDIN_FILENO, &input[0], 1u64);\n put(label); put(\" stdin=\");\n if (inputn == 0i64) { put(\"eof\\n\"); } else {\n if (inputn == 1i64) { put(\"data\\n\"); } else { put(\"<error>\\n\"); };\n };\n",
|
||||
@@ -1078,6 +1132,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
expectexit(&outc, 0);
|
||||
cwdassertrecord(outc.stdout, "p4-internal-only", p4, p4, "1", "yes",
|
||||
"p4-data", "p4-testdata", "eof");
|
||||
cwdassertdirectoryenv(outc.stdout, "p4-internal-only");
|
||||
cwdassertpath(outc.stdout, "p4-internal-only",
|
||||
strings.concat(repo(), "/out/bin"), "1");
|
||||
assert(os.remove(strings.concat(p4, "/created.txt")) == 0);
|
||||
@@ -1224,6 +1279,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(has(outc.stderr,
|
||||
"test package must match production package or <package>_test\n"));
|
||||
assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH="));
|
||||
assert(!has(outc.stdout, " LC_ALL=")
|
||||
&& !has(outw.stdout, " LC_ALL="));
|
||||
assert(directoryisempty(rejectcwork));
|
||||
assert(directoryisempty(rejectwwork));
|
||||
|
||||
@@ -1239,6 +1296,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(!has(out.stdout, "cmdno-init"));
|
||||
assert(!has(out.stdout, "cmdno-main"));
|
||||
assert(!has(out.stdout, " PATH="));
|
||||
assert(!has(out.stdout, " LC_ALL=")
|
||||
&& !has(out.stdout, " TMPDIR="));
|
||||
assert(!os.exists(strings.concat(plain, "/created.txt")));
|
||||
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
|
||||
let commandbin: str = strings.concat(root, "/cmdno.bin");
|
||||
@@ -1251,6 +1310,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(os.exists(commandbin));
|
||||
assert(!has(out.stdout, "cmdno-init") && !has(out.stdout, "cmdno-main"));
|
||||
assert(!has(out.stdout, " PATH="));
|
||||
assert(!has(out.stdout, " LC_ALL=")
|
||||
&& !has(out.stdout, " TMPDIR="));
|
||||
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
|
||||
|
||||
// Compile-only paths do not run. A published binary has no embedded cwd or
|
||||
@@ -1267,6 +1328,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(!os.exists(strings.concat(p2, "/created.txt")));
|
||||
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
|
||||
assert(!has(out.stdout, " PATH="));
|
||||
assert(!has(out.stdout, " LC_ALL=")
|
||||
&& !has(out.stdout, " TMPDIR="));
|
||||
assert(os.remove(defaultbin) == 0);
|
||||
|
||||
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
|
||||
@@ -1288,6 +1351,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
assert(same(readfile(p1cbin), readfile(p1wbin)));
|
||||
assert(!has(outc.stdout, "dep-init") && !has(outw.stdout, "dep-init"));
|
||||
assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH="));
|
||||
assert(!has(outc.stdout, " LC_ALL=")
|
||||
&& !has(outw.stdout, " LC_ALL="));
|
||||
|
||||
let directbin: []str = [p1cbin, "-package=p1"];
|
||||
runcommandinputenvdir(root, "cwd-published-direct", directbin, cenv,
|
||||
@@ -1300,6 +1365,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
cwdassertrecord(out.stdout, "p1-internal", caller,
|
||||
"/ww-cwd-inherited-first", "2", "no", "caller-data",
|
||||
"caller-testdata", "data");
|
||||
cwdassertcallerenv(out.stdout, "dep-init");
|
||||
cwdassertcallerenv(out.stdout, "p1-internal");
|
||||
cwdassertcallerpath(out.stdout, "dep-init");
|
||||
cwdassertcallerpath(out.stdout, "p1-internal");
|
||||
cwdassertsplitstreams(out.stdout, out.stderr, "dep-init", caller);
|
||||
@@ -1328,6 +1395,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
|
||||
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
|
||||
"caller-testdata", "data");
|
||||
cwdassertcallerenv(outc.stdout, "p4-internal-only");
|
||||
cwdasserttestpath(outc.stdout, "p4-internal-only");
|
||||
cwdassertsplitstreams(outc.stdout, outc.stderr,
|
||||
"p4-internal-only", caller);
|
||||
@@ -1666,6 +1734,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
"p2-data", "p2-testdata", "eof");
|
||||
cwdassertrecord(out.stdout, "p2-external", p2, p2, "1", "yes",
|
||||
"p2-data", "p2-testdata", "eof");
|
||||
cwdassertdirectoryenv(out.stdout, "dep-init");
|
||||
cwdassertdirectoryenv(out.stdout, "p2-external");
|
||||
cwdassertpath(out.stdout, "dep-init", strings.concat(root,
|
||||
":/ww-path-inherited-first"), "1");
|
||||
cwdassertpath(out.stdout, "p2-external", strings.concat(root,
|
||||
|
||||
Reference in New Issue
Block a user