ci: preserve WSL regressions under one platform boundary
This commit is contained in:
1 parent
efc0108c03
commit
f3f9443e0f
3 files changed
+134
-66
No files matched your search
@@ -0,0 +1,62 @@
|
||||
# Check the platform budget before allocating native runners
|
||||
|
||||
## Status
|
||||
|
||||
Implemented for review.
|
||||
|
||||
## Context
|
||||
|
||||
The platform-condition budget ran only during packaging. A Windows-only WSL
|
||||
regression introduced one additional test-level condition, leaving the source
|
||||
count at 485 against a budget of 484. All ten PR checks passed because none ran
|
||||
that budget checker. Preview preparation then failed before any package build.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Preview run 35882296442 failed in `Check platform cfg budget` on main commit
|
||||
`1d26c2dc9860a6cfb61c14b630c12d36dbf95e43`.
|
||||
- The added condition guarded the WSL truecolor regression, not a new production
|
||||
platform branch. Five related WSL forwarding tests repeated the same Windows
|
||||
compilation condition.
|
||||
- Sharing that condition at the WSL test-module boundary preserves all five test
|
||||
bodies and lowers the repository count to 481 without moving source outside
|
||||
the scanner's declared roots or adding an exclusion.
|
||||
|
||||
## Decision
|
||||
|
||||
- Keep the WSL forwarding regressions together under one Windows test module in
|
||||
their existing owning source file. Other identity and PATH tests retain their
|
||||
existing boundaries. Production environment behavior is unchanged.
|
||||
- Tighten the existing budget from 484 to the measured count of 481.
|
||||
- Execute the same authoritative platform checker in required lint before matrix
|
||||
planning. Keep the packaging check as an independent release precondition.
|
||||
- Run the checker's contract tests with the existing lightweight CI contracts.
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
Raising the budget, deleting the new regression, excluding test files from the
|
||||
scanner, or ignoring the packaging failure would hide the mismatch. None is
|
||||
needed. This change also does not turn the historical text-count heuristic into
|
||||
a claim that architectural coupling has been proven absent.
|
||||
|
||||
## Consequences
|
||||
|
||||
The five WSL tests share one platform boundary and remain Windows-only. Budget
|
||||
failures are visible before native runner allocation. Legitimate future scanner
|
||||
policy changes still need their own reproducer, tests and review.
|
||||
|
||||
## Validation
|
||||
|
||||
The platform checker reports 481 against 481. Validation also compares the test
|
||||
function inventory, runs the Windows identity/WSL regression group, and checks
|
||||
that lint executes the gate before planning without automatic budget updates or
|
||||
failure suppression.
|
||||
|
||||
## Supersedes
|
||||
|
||||
None.
|
||||
|
||||
## Revisit when
|
||||
|
||||
The text-based platform counter is replaced with a reviewed semantic contract,
|
||||
or native runner planning is moved to another shared preflight owner.
|
||||
+71
-65
@@ -304,77 +304,83 @@ mod tests {
|
||||
assert_eq!(entries.iter().filter(|entry| same_directory(entry, directory)).count(), 1);
|
||||
}
|
||||
|
||||
// WSL forwarding has one Windows compilation boundary for its whole contract.
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn wslenv_entries_match_variables() {
|
||||
// 透传表里的路径条目是字面量。变量改名而这里忘记跟着改,就会静默丢掉
|
||||
// WSL 侧的可达性——用一个断言把它变成编译期之后立刻可见的失败。
|
||||
let names: Vec<&str> = WSLENV_ENTRIES.iter().copied().map(variable_name).collect();
|
||||
assert!(names.contains(&CLI_ENV), "{CLI_ENV} missing from WSLENV passthrough: {names:?}");
|
||||
assert!(names.contains(&BIN_DIR_ENV), "{BIN_DIR_ENV} missing: {names:?}");
|
||||
assert!(names.contains(&PROCESS_ENV), "{PROCESS_ENV} missing: {names:?}");
|
||||
assert!(names.contains(&crate::runtime_api::ENDPOINT_ENV));
|
||||
assert!(names.contains(&"COLORTERM"));
|
||||
}
|
||||
mod wsl {
|
||||
use super::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn wsl_receives_truecolor_without_replacing_an_explicit_forwarding_rule() {
|
||||
let mut env = HashMap::from([
|
||||
("COLORTERM".to_owned(), "truecolor".to_owned()),
|
||||
("WSLENV".to_owned(), "KEEP/p".to_owned()),
|
||||
]);
|
||||
apply(&mut env, 8);
|
||||
assert_eq!(env["COLORTERM"], "truecolor");
|
||||
assert!(env["WSLENV"].split(':').any(|entry| entry == "COLORTERM"));
|
||||
assert!(env["WSLENV"].split(':').any(|entry| entry == "KEEP/p"));
|
||||
env.insert("WSLENV".into(), "KEEP/p:COLORTERM/u".into());
|
||||
env.insert("COLORTERM".into(), "24bit".into());
|
||||
apply(&mut env, 8);
|
||||
assert_eq!(env["COLORTERM"], "24bit");
|
||||
let entries: Vec<_> =
|
||||
env["WSLENV"].split(':').filter(|entry| variable_name(entry) == "COLORTERM").collect();
|
||||
assert_eq!(entries, ["COLORTERM/u"]);
|
||||
}
|
||||
#[test]
|
||||
fn wslenv_entries_match_variables() {
|
||||
// 透传表里的路径条目是字面量。变量改名而这里忘记跟着改,就会静默丢掉
|
||||
// WSL 侧的可达性——用一个断言把它变成编译期之后立刻可见的失败。
|
||||
let names: Vec<&str> = WSLENV_ENTRIES.iter().copied().map(variable_name).collect();
|
||||
assert!(
|
||||
names.contains(&CLI_ENV),
|
||||
"{CLI_ENV} missing from WSLENV passthrough: {names:?}"
|
||||
);
|
||||
assert!(names.contains(&BIN_DIR_ENV), "{BIN_DIR_ENV} missing: {names:?}");
|
||||
assert!(names.contains(&PROCESS_ENV), "{PROCESS_ENV} missing: {names:?}");
|
||||
assert!(names.contains(&crate::runtime_api::ENDPOINT_ENV));
|
||||
assert!(names.contains(&"COLORTERM"));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn wslenv_keeps_foreign_entries() {
|
||||
let mut env = HashMap::new();
|
||||
// `shell_detect::wsl_cwd_report_env` 先写过 cwd 上报的透传条目。
|
||||
env.insert("WSLENV".to_owned(), "PROMPT_COMMAND".to_owned());
|
||||
apply(&mut env, 3);
|
||||
#[test]
|
||||
fn wsl_receives_truecolor_without_replacing_an_explicit_forwarding_rule() {
|
||||
let mut env = HashMap::from([
|
||||
("COLORTERM".to_owned(), "truecolor".to_owned()),
|
||||
("WSLENV".to_owned(), "KEEP/p".to_owned()),
|
||||
]);
|
||||
apply(&mut env, 8);
|
||||
assert_eq!(env["COLORTERM"], "truecolor");
|
||||
assert!(env["WSLENV"].split(':').any(|entry| entry == "COLORTERM"));
|
||||
assert!(env["WSLENV"].split(':').any(|entry| entry == "KEEP/p"));
|
||||
env.insert("WSLENV".into(), "KEEP/p:COLORTERM/u".into());
|
||||
env.insert("COLORTERM".into(), "24bit".into());
|
||||
apply(&mut env, 8);
|
||||
assert_eq!(env["COLORTERM"], "24bit");
|
||||
let entries: Vec<_> = env["WSLENV"]
|
||||
.split(':')
|
||||
.filter(|entry| variable_name(entry) == "COLORTERM")
|
||||
.collect();
|
||||
assert_eq!(entries, ["COLORTERM/u"]);
|
||||
}
|
||||
|
||||
let wslenv = env.get("WSLENV").expect("WSLENV");
|
||||
let entries: Vec<&str> = wslenv.split(':').collect();
|
||||
assert!(entries.contains(&"PROMPT_COMMAND"), "cwd reporting must survive: {wslenv}");
|
||||
assert!(entries.contains(&PANE_ENV), "pane identity must cross into WSL: {wslenv}");
|
||||
// 路径必须带 `/p`,否则来宾拿到无法执行的 `D:\…` 字面量。
|
||||
assert!(entries.contains(&"NEBULA_CLI/p"), "cli path needs translation: {wslenv}");
|
||||
}
|
||||
#[test]
|
||||
fn wslenv_keeps_foreign_entries() {
|
||||
let mut env = HashMap::new();
|
||||
// `shell_detect::wsl_cwd_report_env` 先写过 cwd 上报的透传条目。
|
||||
env.insert("WSLENV".to_owned(), "PROMPT_COMMAND".to_owned());
|
||||
apply(&mut env, 3);
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn wslenv_merge_is_idempotent() {
|
||||
let mut env = HashMap::new();
|
||||
apply(&mut env, 4);
|
||||
let once = env.get("WSLENV").cloned().expect("WSLENV");
|
||||
apply(&mut env, 4);
|
||||
assert_eq!(env.get("WSLENV"), Some(&once));
|
||||
}
|
||||
let wslenv = env.get("WSLENV").expect("WSLENV");
|
||||
let entries: Vec<&str> = wslenv.split(':').collect();
|
||||
assert!(entries.contains(&"PROMPT_COMMAND"), "cwd reporting must survive: {wslenv}");
|
||||
assert!(entries.contains(&PANE_ENV), "pane identity must cross into WSL: {wslenv}");
|
||||
// 路径必须带 `/p`,否则来宾拿到无法执行的 `D:\…` 字面量。
|
||||
assert!(entries.contains(&"NEBULA_CLI/p"), "cli path needs translation: {wslenv}");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn wslenv_respects_foreign_flags_for_same_variable() {
|
||||
let mut env = HashMap::new();
|
||||
// 宿主已用 `/l`(列表)传同名变量:尊重它,不改成我们的 `/p`,
|
||||
// 否则会破坏那个工具原有的转换语义。
|
||||
env.insert("WSLENV".to_owned(), "NEBULA_CLI/l".to_owned());
|
||||
apply(&mut env, 5);
|
||||
#[test]
|
||||
fn wslenv_merge_is_idempotent() {
|
||||
let mut env = HashMap::new();
|
||||
apply(&mut env, 4);
|
||||
let once = env.get("WSLENV").cloned().expect("WSLENV");
|
||||
apply(&mut env, 4);
|
||||
assert_eq!(env.get("WSLENV"), Some(&once));
|
||||
}
|
||||
|
||||
let wslenv = env.get("WSLENV").expect("WSLENV");
|
||||
let entries: Vec<&str> = wslenv.split(':').collect();
|
||||
assert!(entries.contains(&"NEBULA_CLI/l"));
|
||||
assert!(!entries.contains(&"NEBULA_CLI/p"));
|
||||
#[test]
|
||||
fn wslenv_respects_foreign_flags_for_same_variable() {
|
||||
let mut env = HashMap::new();
|
||||
// 宿主已用 `/l`(列表)传同名变量:尊重它,不改成我们的 `/p`,
|
||||
// 否则会破坏那个工具原有的转换语义。
|
||||
env.insert("WSLENV".to_owned(), "NEBULA_CLI/l".to_owned());
|
||||
apply(&mut env, 5);
|
||||
|
||||
let wslenv = env.get("WSLENV").expect("WSLENV");
|
||||
let entries: Vec<&str> = wslenv.split(':').collect();
|
||||
assert!(entries.contains(&"NEBULA_CLI/l"));
|
||||
assert!(!entries.contains(&"NEBULA_CLI/p"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
484
|
||||
481
|
||||
Reference in new issue
Block a user