One child per test, carrying every hostile git setting at once (improvise-eahe)
tests/shipped_documents_load.rs re-ran itself in 18 child processes, one per hostile setting, and each child spawns several gits. So the binary took 2.1-3.3s against design-principles 7's 2s-per-binary tripwire. The six per-setting tests (53zz x2, vwjj, pr3i, a0i3 and 2iws/vav0) are now one test, a_hostile_git_environment_changes_nothing. It has two children: the gzip shape test and every_shipped_document_loads. Each carries every setting at once: - the template dir; - the global and system config (with trace2 targets); - XDG_CONFIG_HOME; - GIT_DIR and GIT_INDEX_FILE; - the five pathspec and attribute settings; - the six trace variables. Afterwards the test asserts the outer index is still empty. Each setting breaks the walk on its own, so a rival that drops one defence leaves that setting live, and the child fails. Every rival that was red against the per-setting tests is red against the combined one. The rivals that were green before are green now: - ICASE; - the trace2 names under the 0 override; - the GIT_CONFIG_GLOBAL and NOSYSTEM nulls, whose excludes witness vwjj's -c override masks. That last pair is filed as improvise-3j70. Measured on an Apple M4 Max, three workspace runs each: this binary went from 2.10-3.31s (load 37-39) to 0.57-0.91s (load 30-33). It is now below the next-largest binary in every run. The evidence sections keep their reasoning, under prose headings. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
@@ -490,7 +490,38 @@ in a scratch repository and asserts the walk lists it. All four were red
|
|||||||
against the `.gitignore` walk. The account, including the rivals, is under
|
against the `.gitignore` walk. The account, including the rivals, is under
|
||||||
`every_shipped_document_loads` above.
|
`every_shipped_document_loads` above.
|
||||||
|
|
||||||
### a_scratch_repo_ignores_an_inherited_git_dir
|
### a_hostile_git_environment_changes_nothing
|
||||||
|
|
||||||
|
improvise-eahe. Each section below it once had a test of its own, and each of those tests re-ran this binary in child processes, one child per setting (a0i3 alone ran ten). By #276 that was 18 children, and each one spawns several `git` processes, so this binary took 2.1–3.3s against the 2s-per-binary tripwire (`context/design-principles.md` §7).
|
||||||
|
|
||||||
|
**What changed.** The six tests are now one test, and its two children carry every hostile setting at once:
|
||||||
|
- **Children:** one re-runs `a_gzipped_document_is_walked` and one re-runs `every_shipped_document_loads`.
|
||||||
|
- **Settings:** each child gets the template dir, the global and system config, `XDG_CONFIG_HOME`, `GIT_DIR`/`GIT_INDEX_FILE`, the five pathspec and attribute settings, the six trace variables, and the config's trace2 targets.
|
||||||
|
- **Outer index:** the test then asserts the outer repository's index is still empty. That is the check that makes the inherited `GIT_DIR` visible, as in its section below.
|
||||||
|
|
||||||
|
**Why one child still separates the settings.** The sections below show that each setting breaks the walk on its own. So a rival that disables the defence against one setting leaves that setting live in the child, and the child fails. Every rival was re-run against the combined test. The ones that were red against the per-setting tests are still red:
|
||||||
|
- dropping `--template=`;
|
||||||
|
- the `--local-env-vars` loop;
|
||||||
|
- the `core.excludesFile` or `core.attributesFile` null;
|
||||||
|
- each of `GIT_LITERAL_PATHSPECS`, `GIT_GLOB_PATHSPECS`, `GIT_NOGLOB_PATHSPECS` and `GIT_ATTR_SOURCE`;
|
||||||
|
- the trace prefix: no strip, the exact name only, the exact three names, and the prefix minus `GIT_TRACE`, `_SETUP` or `_PERFORMANCE`;
|
||||||
|
- the trace2 `0` override.
|
||||||
|
|
||||||
|
**The ones green before are green now.**
|
||||||
|
- `GIT_ICASE_PATHSPECS` is green, as its section explains.
|
||||||
|
- The prefix minus `GIT_TRACE2_EVENT` is green, because the `0` override covers it. It was red against the per-setting test at ddcdf93a, before #276 added the override.
|
||||||
|
- **Dropping `GIT_CONFIG_GLOBAL=/dev/null` or `GIT_CONFIG_NOSYSTEM=1` is green, both at #276's head and here.** Their witness is an excludes line, and vwjj's `-c core.excludesFile=/dev/null` outranks every config file, so the witness cannot see them. Consolidation did not cause this. It is filed as improvise-3j70.
|
||||||
|
|
||||||
|
**Measured** on an Apple M4 Max (16 cores), three `cargo test --workspace` runs each, before and after. The median counts only binaries that ran a test:
|
||||||
|
|
||||||
|
| | this binary | load average | run median | ratio | next largest |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| before (#276, 18 children) | 2.10, 3.04, 3.31s | 37–39 (1 min) | 0.00–0.01s | ≈300× | 0.90s, one 2.66s spike |
|
||||||
|
| after (2 children) | 0.57, 0.59, 0.91s | 30–33 (1 min) | 0.00–0.02s | ≈30–90× | 1.02s, one 2.49s spike |
|
||||||
|
|
||||||
|
The median is at the 0.01s resolution cargo prints, so the ratio is coarse. Before, this binary was the outlier by more than two orders of magnitude and over the tripwire in every run. After, it is below the next-largest binary in every run and well under 2s. The occasional 2.5s spike in "next largest" is some other binary, in both runs; that is load, not this file.
|
||||||
|
|
||||||
|
### An inherited `GIT_DIR` (improvise-53zz)
|
||||||
|
|
||||||
improvise-53zz. The scratch repositories inherited the caller's git
|
improvise-53zz. The scratch repositories inherited the caller's git
|
||||||
environment, and two parts of it change what `git -C <scratch>` does.
|
environment, and two parts of it change what `git -C <scratch>` does.
|
||||||
@@ -526,7 +557,7 @@ Rivals, each run against the fixed file:
|
|||||||
libtest exits 0 when a filter matches nothing, and both of these tests would
|
libtest exits 0 when a filter matches nothing, and both of these tests would
|
||||||
then pass without running anything.
|
then pass without running anything.
|
||||||
|
|
||||||
### a_scratch_repo_ignores_the_users_global_and_system_excludes
|
### The user's global and system excludes (improvise-53zz)
|
||||||
|
|
||||||
improvise-53zz. The scratch repositories also read the user's own git config. A
|
improvise-53zz. The scratch repositories also read the user's own git config. A
|
||||||
`*.gz` line in a global `core.excludesFile` made `git add` refuse
|
`*.gz` line in a global `core.excludesFile` made `git add` refuse
|
||||||
@@ -545,7 +576,7 @@ Rivals:
|
|||||||
- Drop `GIT_CONFIG_NOSYSTEM=1`: red, in the system run. The system scope is
|
- Drop `GIT_CONFIG_NOSYSTEM=1`: red, in the system run. The system scope is
|
||||||
there so that this half of the fix has a witness at all.
|
there so that this half of the fix has a witness at all.
|
||||||
|
|
||||||
### a_scratch_repo_ignores_the_users_default_ignore_and_attributes_files
|
### The user's default ignore and attributes files (improvise-vwjj)
|
||||||
|
|
||||||
improvise-vwjj, found reviewing PR #248 at `f5cfc7ed`. The test above sets
|
improvise-vwjj, found reviewing PR #248 at `f5cfc7ed`. The test above sets
|
||||||
`core.excludesFile` *inside* a config file, so emptying the config files looks
|
`core.excludesFile` *inside* a config file, so emptying the config files looks
|
||||||
@@ -582,7 +613,7 @@ Rivals, each run against the fixed file:
|
|||||||
- Drop `core.attributesFile=/dev/null`: red, `fails under a default
|
- Drop `core.attributesFile=/dev/null`: red, `fails under a default
|
||||||
git/attributes`.
|
git/attributes`.
|
||||||
|
|
||||||
### a_scratch_repo_ignores_the_users_template_dir
|
### The user's template directory (improvise-pr3i)
|
||||||
|
|
||||||
improvise-pr3i, found reviewing PR #255 at `56ac72a2`. This is a third path to the
|
improvise-pr3i, found reviewing PR #255 at `56ac72a2`. This is a third path to the
|
||||||
53zz symptom. `git init` copies a template directory into the new `.git`, and
|
53zz symptom. `git init` copies a template directory into the new `.git`, and
|
||||||
@@ -610,7 +641,7 @@ the `-c` arguments in `run_git` beat relying on the absence of a config file.
|
|||||||
|
|
||||||
Rival: drop `--template=`. Red, as above.
|
Rival: drop `--template=`. Red, as above.
|
||||||
|
|
||||||
### the_walk_ignores_the_users_pathspec_and_attribute_settings
|
### Pathspec and attribute settings (improvise-a0i3)
|
||||||
|
|
||||||
improvise-a0i3. `git(root)` strips what `git rev-parse --local-env-vars` names,
|
improvise-a0i3. `git(root)` strips what `git rev-parse --local-env-vars` names,
|
||||||
and that list holds only the variables that point at a repository (`GIT_DIR`,
|
and that list holds only the variables that point at a repository (`GIT_DIR`,
|
||||||
@@ -651,7 +682,7 @@ Rivals, each dropping one name from `PATHSPEC_AND_ATTRIBUTE_SETTINGS`:
|
|||||||
variables, and a future spelling of the walk that depends on case would be
|
variables, and a future spelling of the walk that depends on case would be
|
||||||
exposed to it. The test covers it, but that coverage is vacuous today.
|
exposed to it. The test covers it, but that coverage is vacuous today.
|
||||||
|
|
||||||
### the_walk_ignores_a_git_trace_sent_to_stdout
|
### A git trace sent to stdout (improvise-2iws, improvise-vav0)
|
||||||
|
|
||||||
improvise-2iws, with improvise-vav0 folded in. The `GIT_TRACE*` family is
|
improvise-2iws, with improvise-vav0 folded in. The `GIT_TRACE*` family is
|
||||||
process-wide, like improvise-a0i3's settings, so `--local-env-vars` lists none
|
process-wide, like improvise-a0i3's settings, so `--local-env-vars` lists none
|
||||||
|
|||||||
+57
-133
@@ -211,41 +211,70 @@ fn told(out: &std::process::Output) -> String {
|
|||||||
format!("\n--- stdout\n{stdout}\n--- stderr\n{stderr}")
|
format!("\n--- stdout\n{stdout}\n--- stderr\n{stderr}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// improvise-pr3i: `git init` copies `GIT_TEMPLATE_DIR`, which no config null
|
/// Every hostile git setting these tests have met, at once, in one child per
|
||||||
/// reaches. Evidence: `context/test-evidence/tui.md`.
|
/// test (improvise-eahe). Each setting's own witness is a rival: `context/test-evidence/tui.md`.
|
||||||
#[test]
|
#[test]
|
||||||
fn a_scratch_repo_ignores_the_users_template_dir() {
|
fn a_hostile_git_environment_changes_nothing() {
|
||||||
|
// improvise-pr3i: a template whose `info/exclude` refuses `*.gz`.
|
||||||
let templates = tempfile::tempdir().expect("a temp dir");
|
let templates = tempfile::tempdir().expect("a temp dir");
|
||||||
write(templates.path(), "info/exclude", "*.gz\n");
|
write(templates.path(), "info/exclude", "*.gz\n");
|
||||||
let out = rerun_with_env(
|
// improvise-53zz: a global and a system config whose excludes refuse `*.gz`.
|
||||||
"a_gzipped_document_is_walked",
|
let home = tempfile::tempdir().expect("a temp dir");
|
||||||
&[("GIT_TEMPLATE_DIR", templates.path())],
|
let excludes = home.path().join("ignore");
|
||||||
|
std::fs::write(&excludes, "*.gz\n").expect("writes");
|
||||||
|
let config = home.path().join("gitconfig");
|
||||||
|
// improvise-vav0: the same config also sends every trace2 target to stdout.
|
||||||
|
let text = format!(
|
||||||
|
"[core]\n\texcludesFile = {}\n[trace2]\n\tnormalTarget = /dev/stdout\n\teventTarget = /dev/stdout\n\tperfTarget = /dev/stdout\n",
|
||||||
|
excludes.display()
|
||||||
);
|
);
|
||||||
assert!(
|
std::fs::write(&config, text).expect("writes");
|
||||||
out.status.success(),
|
// improvise-vwjj: git's default ignore and attributes files, no config line.
|
||||||
"the gzip shape test fails under a template info/exclude: {}",
|
let xdg = tempfile::tempdir().expect("a temp dir");
|
||||||
told(&out)
|
write(xdg.path(), "git/ignore", "*.gz\n");
|
||||||
|
write(
|
||||||
|
xdg.path(),
|
||||||
|
"git/attributes",
|
||||||
|
"*.gz working-tree-encoding=UTF-16\n",
|
||||||
);
|
);
|
||||||
}
|
// improvise-53zz: an inherited `GIT_DIR`, which beats `-C`.
|
||||||
|
|
||||||
/// improvise-53zz: a hook in a linked worktree exports `GIT_DIR`, which beats
|
|
||||||
/// `-C`. Evidence: `context/test-evidence/tui.md`.
|
|
||||||
#[test]
|
|
||||||
fn a_scratch_repo_ignores_an_inherited_git_dir() {
|
|
||||||
let outer = scratch_repo();
|
let outer = scratch_repo();
|
||||||
let dot_git = outer.path().join(".git");
|
let dot_git = outer.path().join(".git");
|
||||||
let out = rerun_with_env(
|
let index = dot_git.join("index");
|
||||||
"a_single_file_un_ignore_is_walked",
|
let stdout = Path::new("/dev/stdout");
|
||||||
&[
|
let one = Path::new("1");
|
||||||
("GIT_DIR", &dot_git),
|
let hostile: Vec<(&str, &Path)> = vec![
|
||||||
("GIT_INDEX_FILE", &dot_git.join("index")),
|
("GIT_TEMPLATE_DIR", templates.path()),
|
||||||
],
|
("GIT_CONFIG_GLOBAL", &config),
|
||||||
);
|
("GIT_CONFIG_SYSTEM", &config),
|
||||||
assert!(
|
("XDG_CONFIG_HOME", xdg.path()),
|
||||||
out.status.success(),
|
("GIT_DIR", &dot_git),
|
||||||
"the shape test fails under an inherited GIT_DIR: {}",
|
("GIT_INDEX_FILE", &index),
|
||||||
told(&out)
|
// improvise-a0i3: process-wide pathspec and attribute settings.
|
||||||
);
|
("GIT_LITERAL_PATHSPECS", one),
|
||||||
|
("GIT_GLOB_PATHSPECS", one),
|
||||||
|
("GIT_NOGLOB_PATHSPECS", one),
|
||||||
|
("GIT_ICASE_PATHSPECS", one),
|
||||||
|
("GIT_ATTR_SOURCE", Path::new("HEAD")),
|
||||||
|
// improvise-2iws: every trace that writes to its target on git 2.55.
|
||||||
|
("GIT_TRACE", stdout),
|
||||||
|
("GIT_TRACE_SETUP", stdout),
|
||||||
|
("GIT_TRACE_PERFORMANCE", stdout),
|
||||||
|
("GIT_TRACE2", stdout),
|
||||||
|
("GIT_TRACE2_EVENT", stdout),
|
||||||
|
("GIT_TRACE2_PERF", stdout),
|
||||||
|
];
|
||||||
|
for test in [
|
||||||
|
"a_gzipped_document_is_walked",
|
||||||
|
"every_shipped_document_loads",
|
||||||
|
] {
|
||||||
|
let out = rerun_with_env(test, &hostile);
|
||||||
|
assert!(
|
||||||
|
out.status.success(),
|
||||||
|
"{test} fails in a hostile git environment: {}",
|
||||||
|
told(&out)
|
||||||
|
);
|
||||||
|
}
|
||||||
let staged = git(outer.path())
|
let staged = git(outer.path())
|
||||||
.args(["ls-files"])
|
.args(["ls-files"])
|
||||||
.output()
|
.output()
|
||||||
@@ -256,108 +285,3 @@ fn a_scratch_repo_ignores_an_inherited_git_dir() {
|
|||||||
String::from_utf8_lossy(&staged.stdout)
|
String::from_utf8_lossy(&staged.stdout)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// improvise-53zz: a user's `*.gz` exclude refused the gzip shape test's `add`.
|
|
||||||
/// Evidence: `context/test-evidence/tui.md`.
|
|
||||||
#[test]
|
|
||||||
fn a_scratch_repo_ignores_the_users_global_and_system_excludes() {
|
|
||||||
let home = tempfile::tempdir().expect("a temp dir");
|
|
||||||
let excludes = home.path().join("ignore");
|
|
||||||
std::fs::write(&excludes, "*.gz\n").expect("writes");
|
|
||||||
let config = home.path().join("gitconfig");
|
|
||||||
let text = format!("[core]\n\texcludesFile = {}\n", excludes.display());
|
|
||||||
std::fs::write(&config, text).expect("writes");
|
|
||||||
for scope in ["GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM"] {
|
|
||||||
let out = rerun_with_env("a_gzipped_document_is_walked", &[(scope, &config)]);
|
|
||||||
assert!(
|
|
||||||
out.status.success(),
|
|
||||||
"the gzip shape test fails under a `*.gz` exclude in {scope}: {}",
|
|
||||||
told(&out)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// improvise-vwjj: git's DEFAULT ignore and attributes files need no config line.
|
|
||||||
/// Evidence: `context/test-evidence/tui.md`.
|
|
||||||
#[test]
|
|
||||||
fn a_scratch_repo_ignores_the_users_default_ignore_and_attributes_files() {
|
|
||||||
let files = [
|
|
||||||
("ignore", "*.gz\n"),
|
|
||||||
("attributes", "*.gz working-tree-encoding=UTF-16\n"),
|
|
||||||
];
|
|
||||||
for (name, text) in files {
|
|
||||||
let xdg = tempfile::tempdir().expect("a temp dir");
|
|
||||||
write(xdg.path(), &format!("git/{name}"), text);
|
|
||||||
let out = rerun_with_env(
|
|
||||||
"a_gzipped_document_is_walked",
|
|
||||||
&[("XDG_CONFIG_HOME", xdg.path())],
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
out.status.success(),
|
|
||||||
"the gzip shape test fails under a default git/{name}: {}",
|
|
||||||
told(&out)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// improvise-a0i3: pathspec magic and an attribute source are process-wide git
|
|
||||||
/// settings, and `--local-env-vars` lists none. Evidence: `context/test-evidence/tui.md`.
|
|
||||||
#[test]
|
|
||||||
fn the_walk_ignores_the_users_pathspec_and_attribute_settings() {
|
|
||||||
let settings = [
|
|
||||||
("GIT_LITERAL_PATHSPECS", "1"),
|
|
||||||
("GIT_GLOB_PATHSPECS", "1"),
|
|
||||||
("GIT_NOGLOB_PATHSPECS", "1"),
|
|
||||||
("GIT_ICASE_PATHSPECS", "1"),
|
|
||||||
("GIT_ATTR_SOURCE", "HEAD"),
|
|
||||||
];
|
|
||||||
let mut failed = Vec::new();
|
|
||||||
for (name, value) in settings {
|
|
||||||
for test in [
|
|
||||||
"a_gzipped_document_is_walked",
|
|
||||||
"every_shipped_document_loads",
|
|
||||||
] {
|
|
||||||
let out = rerun_with_env(test, &[(name, Path::new(value))]);
|
|
||||||
if !out.status.success() {
|
|
||||||
failed.push(format!("{test} under {name}={value}: {}", told(&out)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert!(failed.is_empty(), "{}", failed.join("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// improvise-2iws: a git trace sent to stdout lands in `ls-files -z`'s output
|
|
||||||
/// and is read as paths. Evidence: `context/test-evidence/tui.md`.
|
|
||||||
#[test]
|
|
||||||
fn the_walk_ignores_a_git_trace_sent_to_stdout() {
|
|
||||||
// Every variable of the family that writes to its target on git 2.55, so a
|
|
||||||
// fixed list of names shorter than the family leaves one behind.
|
|
||||||
let stdout = Path::new("/dev/stdout");
|
|
||||||
let mut traces: Vec<(&str, &Path)> = [
|
|
||||||
"GIT_TRACE",
|
|
||||||
"GIT_TRACE_SETUP",
|
|
||||||
"GIT_TRACE_PERFORMANCE",
|
|
||||||
"GIT_TRACE2",
|
|
||||||
"GIT_TRACE2_EVENT",
|
|
||||||
"GIT_TRACE2_PERF",
|
|
||||||
]
|
|
||||||
.map(|name| (name, stdout))
|
|
||||||
.to_vec();
|
|
||||||
// improvise-vav0: the same targets set in config, where no removal reaches.
|
|
||||||
let home = tempfile::tempdir().expect("a temp dir");
|
|
||||||
let config = home.path().join("gitconfig");
|
|
||||||
let text = "[trace2]\n\tnormalTarget = /dev/stdout\n\teventTarget = /dev/stdout\n\tperfTarget = /dev/stdout\n";
|
|
||||||
std::fs::write(&config, text).expect("writes");
|
|
||||||
traces.push(("GIT_CONFIG_GLOBAL", &config));
|
|
||||||
for test in [
|
|
||||||
"a_gzipped_document_is_walked",
|
|
||||||
"every_shipped_document_loads",
|
|
||||||
] {
|
|
||||||
let out = rerun_with_env(test, &traces);
|
|
||||||
assert!(
|
|
||||||
out.status.success(),
|
|
||||||
"{test} under a trace to stdout: {}",
|
|
||||||
told(&out)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user