返回 CodeWhale
with-hermetic-test-home.test.sh
根目录 / scripts / with-hermetic-test-home.test.sh
1 #!/bin/sh
2 # Exercise the real boundary using only synthetic homes and a fake toolchain.
3 # Quoted child programs and the literal argv probe expand only in the child.
4 # shellcheck disable=SC2016
5 set -eu
6 repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
7 fixture=$(mktemp -d "${TMPDIR:-/tmp}/hermetic-home-proof.XXXXXX")
8 trap 'rm -rf -- "$fixture"' EXIT
9
10 mkdir -p "$fixture/bin" "$fixture/toolchain" "$fixture/tmp with spaces"
11 mkdir -p "$fixture/outer/home/.codewhale/fleets" "$fixture/cargo" "$fixture/rustup"
12 printf '%s\n' 'My fleet' > "$fixture/outer/home/.codewhale/fleets/selected"
13 printf '%s\n' '[invalid fixture' > "$fixture/outer/home/.codewhale/fleets/my-fleet.toml"
14 printf '%s\n' '#!/bin/sh' 'printf "%s\n" "$TEST_TOOLCHAIN/rustc"' > "$fixture/bin/rustup"
15 chmod +x "$fixture/bin/rustup"
16 fixture_stack=20971520
17
18 run_fixture() {
19 env -i HOME="$fixture/outer/home" \
20 CODEWHALE_HOME="$fixture/outer/home/.codewhale" \
21 CODEWHALE_CONFIG_PATH="$fixture/outer/poison.toml" \
22 DEEPSEEK_CONFIG_PATH="$fixture/outer/legacy-poison.toml" \
23 DEEPSEEK_HOME="$fixture/outer/legacy-home" \
24 CODEX_HOME="$fixture/outer/codex" \
25 OPENAI_API_KEY=synthetic-outer-key \
26 RUST_MIN_STACK="$fixture_stack" \
27 CARGO_HOME="$fixture/cargo" RUSTUP_HOME="$fixture/rustup" \
28 TEST_FIXTURE="$fixture" TEST_TOOLCHAIN="$fixture/toolchain" \
29 TMPDIR="$fixture/tmp with spaces" \
30 CODEWHALE_DEV_CACHE_QUIET=1 CODEWHALE_SCCACHE=0 \
31 PATH="$fixture/bin:/usr/bin:/bin" "$@"
32 }
33
34 run_isolated() {
35 run_fixture "$repo_root/scripts/with-hermetic-test-home.sh" "$@"
36 }
37
38 run_isolated sh -c '
39 set -eu
40 test "$HOME" != "$1/outer/home"
41 test "$USERPROFILE" = "$HOME"
42 test -d "$HOME/.codewhale"
43 test -d "$HOME/AppData/Roaming"
44 test -d "$HOME/AppData/Local"
45 test "$APPDATA" = "$HOME/AppData/Roaming"
46 test "$LOCALAPPDATA" = "$HOME/AppData/Local"
47 test ! -e "$HOME/.codewhale/fleets/selected"
48 test -z "${CODEWHALE_HOME+x}"
49 test -z "${CODEWHALE_CONFIG_PATH+x}"
50 test -z "${DEEPSEEK_CONFIG_PATH+x}"
51 test -z "${DEEPSEEK_HOME+x}"
52 test "$CODEX_HOME" != "$1/outer/codex"
53 test -d "$XDG_CONFIG_HOME"
54 test -d "$CODEX_HOME"
55 test -z "$OPENAI_API_KEY"
56 test "$RUST_MIN_STACK" = 20971520
57 test "$CARGO_HOME" = "$1/cargo"
58 test "$RUSTUP_HOME" = "$1/rustup"
59 case "$PATH" in "$1/toolchain:"*) ;; *) exit 1 ;; esac
60 test "$2" = '\''one argument $(not run)'\''
61 printf "%s\n" "$HOME" > "$1/child-home"
62 ' sh "$fixture" 'one argument $(not run)'
63 child_home=$(cat "$fixture/child-home")
64 test ! -d "${child_home%/*}"
65 test "$(cat "$fixture/outer/home/.codewhale/fleets/selected")" = 'My fleet'
66 test "$(cat "$fixture/outer/home/.codewhale/fleets/my-fleet.toml")" = '[invalid fixture'
67 printf '%s\n' 'ok 1 - isolated homes, credentials, argv, toolchain and outer state'
68
69 status=0
70 fixture_stack=
71 run_isolated sh -c 'set -e; test "$RUST_MIN_STACK" = 16777216; printf "%s\n" "$HOME" > "$1/failing-home"; exit 37' sh "$fixture" || status=$?
72 test "$status" -eq 37
73 child_home=$(cat "$fixture/failing-home")
74 test ! -d "${child_home%/*}"
75 test -f "$fixture/outer/home/.codewhale/fleets/selected"
76 printf '%s\n' 'ok 2 - child failure status and owned-home cleanup'
77
78 status=0
79 run_isolated > "$fixture/usage" 2>&1 || status=$?
80 test "$status" -eq 2
81 printf '%s\n' 'ok 3 - missing command is rejected'
82
83 # Exercise the actual developer entry point without invoking a Rust tool.
84 # The cache remains outside the disposable HOME, including Cargo's literal
85 # build-dir template and --config argument needed for template expansion.
86 cat > "$fixture/toolchain/cargo" <<'EOF'
87 #!/bin/sh
88 set -eu
89 if [ "${1:-}" = --version ]; then
90 printf '%s\n' 'cargo 1.97.0 (synthetic)'
91 exit 0
92 fi
93 test "$HOME" != "$TEST_FIXTURE/outer/home" || {
94 printf '%s\n' 'dev-test left ambient HOME visible' >&2
95 exit 1
96 }
97 test "$USERPROFILE" = "$HOME"
98 test "$APPDATA" = "$HOME/AppData/Roaming"
99 test "$LOCALAPPDATA" = "$HOME/AppData/Local"
100 test -d "$APPDATA"
101 test -d "$LOCALAPPDATA"
102 test ! -e "$HOME/.codewhale/fleets/selected"
103 test -z "${CODEWHALE_HOME+x}"
104 test -z "${CODEWHALE_CONFIG_PATH+x}"
105 test -z "${DEEPSEEK_CONFIG_PATH+x}"
106 test -z "${DEEPSEEK_HOME+x}"
107 test -z "$OPENAI_API_KEY"
108 test "$CARGO_HOME" = "$TEST_FIXTURE/cargo"
109 test "$RUSTUP_HOME" = "$TEST_FIXTURE/rustup"
110 test "$RUST_MIN_STACK" = 16777216
111 test "$CARGO_BUILD_BUILD_DIR" = "$TEST_FIXTURE/outer/home/.cache/codewhale/build/{workspace-path-hash}"
112 test "$CARGO_BUILD_BUILD_DIR" = "$CODEWHALE_CACHE_ROOT/build/{workspace-path-hash}"
113 printf '%s\n' "$HOME" > "$TEST_FIXTURE/dev-home"
114 printf '%s\n' "$@" > "$TEST_FIXTURE/dev-argv"
115 # A real libtest run prints this; dev-test.sh refuses a filtered green without
116 # it. TEST_CARGO_SILENT drops it so the refusal itself can be exercised.
117 if [ -z "${TEST_CARGO_SILENT:-}" ]; then
118 printf '%s\n' 'test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s'
119 fi
120 exit "${TEST_CARGO_STATUS:-0}"
121 EOF
122 printf '%s\n' '#!/bin/sh' 'printf "%s\n" "commit-hash: synthetic"' > "$fixture/toolchain/rustc"
123 printf '%s\n' '#!/bin/sh' 'exit 0' > "$fixture/bin/cargo-nextest"
124 chmod +x "$fixture/toolchain/cargo" "$fixture/toolchain/rustc" "$fixture/bin/cargo-nextest"
125 ln -s "$fixture/toolchain/cargo" "$fixture/bin/cargo"
126 ln -s "$fixture/toolchain/rustc" "$fixture/bin/rustc"
127
128 count=3
129 for runner in 0 1; do
130 for area in config tui-integration; do
131 run_fixture env CODEWHALE_DEV_NEXTEST="$runner" \
132 "$repo_root/scripts/dev-test.sh" "$area" 'one argument $(not run)' > "$fixture/dev-output"
133 {
134 printf '%s\n' --config "build.build-dir = \"$fixture/outer/home/.cache/codewhale/build/{workspace-path-hash}\""
135 if [ "$runner" -eq 1 ]; then
136 printf '%s\n' nextest run
137 else
138 printf '%s\n' test
139 fi
140 if [ "$area" = config ]; then
141 printf '%s\n' -p codewhale-config --lib
142 else
143 printf '%s\n' -p codewhale-tui --test integration
144 fi
145 printf '%s\n' --locked 'one argument $(not run)'
146 } > "$fixture/expected-argv"
147 cmp "$fixture/expected-argv" "$fixture/dev-argv"
148 child_home=$(cat "$fixture/dev-home")
149 test ! -d "${child_home%/*}"
150 test -d "$fixture/outer/home/.cache/codewhale/build"
151 count=$((count + 1))
152 printf 'ok %s - dev-test runner=%s area=%s isolates config and preserves persistent cache and argv\n' "$count" "$runner" "$area"
153 done
154 done
155
156 status=0
157 run_fixture env CODEWHALE_DEV_NEXTEST=0 TEST_CARGO_STATUS=37 \
158 "$repo_root/scripts/dev-test.sh" config > "$fixture/dev-output" || status=$?
159 test "$status" -eq 37
160 child_home=$(cat "$fixture/dev-home")
161 test ! -d "${child_home%/*}"
162 test "$(cat "$fixture/outer/home/.codewhale/fleets/selected")" = 'My fleet'
163 printf '%s\n' 'ok 8 - dev-test preserves failure status and cleans only its temporary home'
164
165 # Exercise Windows cygpath path normalization, backslash toolchain resolution,
166 # and AppData provisioning required by sccache and Windows known-folder lookups.
167 mkdir -p "$fixture/win-bin" "$fixture/win-toolchain/bin"
168 cat > "$fixture/win-bin/cygpath" <<'EOF'
169 #!/bin/sh
170 set -eu
171 case "${1:-}" in
172 -m|-u) shift ;;
173 *) exit 2 ;;
174 esac
175 printf '%s\n' "$1" | tr '\\' '/'
176 EOF
177 cat > "$fixture/win-bin/rustup" <<'EOF'
178 #!/bin/sh
179 set -eu
180 printf '%s\n' "$TEST_TOOLCHAIN\\bin\\rustc.exe"
181 EOF
182 chmod +x "$fixture/win-bin/cygpath" "$fixture/win-bin/rustup"
183
184 run_win_fixture() {
185 env -i HOME="$fixture/outer/home" \
186 USERPROFILE="$fixture/outer/home" \
187 APPDATA="$fixture/outer/home/AppData/Roaming" \
188 LOCALAPPDATA="$fixture/outer/home/AppData/Local" \
189 CARGO_HOME="$fixture/cargo" RUSTUP_HOME="$fixture/rustup" \
190 TEST_FIXTURE="$fixture" TEST_TOOLCHAIN="$fixture/win-toolchain" \
191 TMPDIR="$fixture/tmp with spaces" \
192 PATH="$fixture/win-bin:/usr/bin:/bin" "$@"
193 }
194
195 run_win_fixture "$repo_root/scripts/with-hermetic-test-home.sh" sh -c '
196 set -eu
197 test "$HOME" != "$1/outer/home"
198 test "$USERPROFILE" = "$HOME"
199 test "$APPDATA" = "$HOME/AppData/Roaming"
200 test "$LOCALAPPDATA" = "$HOME/AppData/Local"
201 test -d "$HOME/AppData/Roaming"
202 test -d "$HOME/AppData/Local"
203 test -d "$HOME/.codewhale"
204 test "$APPDATA" != "$1/outer/home/AppData/Roaming"
205 test "$LOCALAPPDATA" != "$1/outer/home/AppData/Local"
206 # Verify toolchain_bin stripped the backslash executable properly
207 case "$PATH" in "$1/win-toolchain/bin:"*) ;; *) exit 1 ;; esac
208 # Verify sccache config-dir resolution target (RoamingAppData/Mozilla/sccache)
209 sccache_cfg_parent="$APPDATA/Mozilla/sccache"
210 mkdir -p "$sccache_cfg_parent"
211 printf "%s\n" "test-config = true" > "$sccache_cfg_parent/config"
212 test -f "$HOME/AppData/Roaming/Mozilla/sccache/config"
213 test ! -e "$1/outer/home/AppData/Roaming/Mozilla/sccache/config"
214 printf "%s\n" "$HOME" > "$1/win-child-home"
215 ' sh "$fixture"
216 win_child_home=$(cat "$fixture/win-child-home")
217 test ! -d "${win_child_home%/*}"
218 printf '%s\n' 'ok 9 - cygpath path normalization, backslash toolchain resolution and hermetic AppData'
219
220 # The guard 8b6dad20e introduced: libtest exits 0 when a filter matches
221 # nothing, which has been mistaken for a pass here before. With an explicit
222 # filter and no `test result:` line, dev-test.sh must refuse that green.
223 status=0
224 run_fixture env CODEWHALE_DEV_NEXTEST=0 TEST_CARGO_SILENT=1 \
225 "$repo_root/scripts/dev-test.sh" config 'filter $(matching nothing)' \
226 > "$fixture/dev-output" 2>&1 || status=$?
227 test "$status" -ne 0
228 grep -q 'refusing green' "$fixture/dev-output"
229 # nextest fails loud on an empty selection, so the guard must not wrap it.
230 run_fixture env CODEWHALE_DEV_NEXTEST=1 TEST_CARGO_SILENT=1 \
231 "$repo_root/scripts/dev-test.sh" config 'filter $(matching nothing)' \
232 > "$fixture/dev-output" 2>&1
233 printf '%s\n' 'ok 10 - a filtered libtest run with no test result refuses green'
234
235 printf '%s\n' 'test result: 10 passed; 0 failed'
236
236 lines BASH