diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 15e1d540..46fc4c80 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -227,6 +227,15 @@ jobs:
run: cargo run -p windows-platform-probes --bin probe-ioring --locked
- name: probe magnitudes (completion port)
run: cargo run -p windows-platform-probes --bin probe-completion-port --locked
+ # Both halves of the long-path pair, deliberately. Either alone says
+ # nothing: the finding is the *difference* between two executables that
+ # differ only in whether `build.rs` embedded the `longPathAware` manifest,
+ # so a run that reported one of them would be reporting a number with no
+ # baseline to read it against.
+ - name: probe magnitudes (long path, manifest-aware)
+ run: cargo run -p windows-platform-probes --bin probe-long-path-aware --locked
+ - name: probe magnitudes (long path, manifest-unaware)
+ run: cargo run -p windows-platform-probes --bin probe-long-path-unaware --locked
# The NUMA questions the 2026-08-30 design session could not answer, run
# against whatever machine the runner fleet supplies.
diff --git a/CHECKLIST-mutation-survivors.md b/CHECKLIST-mutation-survivors.md
index 25fb7403..8938b33d 100644
--- a/CHECKLIST-mutation-survivors.md
+++ b/CHECKLIST-mutation-survivors.md
@@ -92,7 +92,8 @@ to the engineer, not to whoever picks up this checklist.
tracked as `SH-13.4` on the branch that ships the topology and queue crates.
The sink has since landed here
([crates/windows-platform-probes/src/report.rs](crates/windows-platform-probes/src/report.rs))
- and all eight probes in this repository route through it, so what remains of
+ and every probe in this repository routes through it -- stated without a count,
+ because the count moves as stages land -- so what remains of
`SH-13.4` is the six branch-only probes, and it stays named rather than linked
because that branch checklist is still not in this repository. The crate's own
durable work now has a home to link:
diff --git a/Cargo.lock b/Cargo.lock
index 176b4850..55c81d5c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -219,6 +219,7 @@ dependencies = [
"windows-placement-probe",
"windows-sys",
"windows-threadpool-sys",
+ "wtf-string",
]
[[package]]
diff --git a/crates/windows-platform-probes/CHECKLIST.md b/crates/windows-platform-probes/CHECKLIST.md
index ea01dcd0..a69a2369 100644
--- a/crates/windows-platform-probes/CHECKLIST.md
+++ b/crates/windows-platform-probes/CHECKLIST.md
@@ -29,7 +29,7 @@ piece of work rather than a correction to that one.
- [ ] **M1.1** -- Decide how a formatted line reaches the sink, because that choice is what makes the
rest mechanical. Every renderer today writes through `let _ = writeln!(out, ...)` against a
- `String`'s `fmt::Write` -- roughly 156 sites across the eight probes -- so the sink must accept
+ `String`'s `fmt::Write` -- upwards of 160 sites across the probes -- so the sink must accept
*formatted* output, not just `&str`, or every site grows a `format!` and an allocation per line.
The options differ in what they cost callers, and the choice is the engineer's:
(a) give `Report` a method taking `fmt::Arguments` plus a `report_line!` macro, so a call site stays
@@ -41,7 +41,7 @@ piece of work rather than a correction to that one.
(c) leave the renderers writing to a `String` and flush it to the sink at each line boundary, which
streams without touching the call sites but keeps two buffers.
-- [ ] **M1.2** -- Convert the eight renderers to write into the sink as they measure, and simplify
+- [ ] **M1.2** -- Convert every renderer to write into the sink as it measures, and simplify
`emit_report` accordingly: once lines leave as they are produced, catching the unwind is no longer
what makes partial output work, and the `catch_unwind`/`resume_unwind` pair should be removed rather
than left as machinery that no longer earns its place. Keep `Captured` working -- it is what every
diff --git a/crates/windows-platform-probes/Cargo.toml b/crates/windows-platform-probes/Cargo.toml
index b1a41e4e..d7f7f6e0 100644
--- a/crates/windows-platform-probes/Cargo.toml
+++ b/crates/windows-platform-probes/Cargo.toml
@@ -47,6 +47,17 @@ path = "src/bin/ioring.rs"
name = "probe-pool-growth"
path = "src/bin/pool_growth.rs"
+# These two are the same code, and that is the measurement: they differ only in
+# whether `build.rs` embeds the `longPathAware` manifest, which is not a runtime
+# switch and so cannot be a flag on one binary.
+[[bin]]
+name = "probe-long-path-aware"
+path = "src/bin/long_path_aware.rs"
+
+[[bin]]
+name = "probe-long-path-unaware"
+path = "src/bin/long_path_unaware.rs"
+
[dependencies]
# **Every workspace dependency below is path-only, with no `version`**, because
# this crate is never distributed at all -- not to a registry, and not as a
@@ -68,6 +79,19 @@ windows-threadpool-sys = { path = "../windows-threadpool-sys" }
# somewhere and compared against something it does not describe. That banner is
# `windows-placement-probe`'s to render, not a second copy here.
windows-placement-probe = { path = "../windows-placement-probe" }
+# The long-path probe measures a length against `MAX_PATH`, and `MAX_PATH` counts
+# UTF-16 code units. `OsStr::len` counts Rust's platform encoding -- WTF-8 here --
+# so the two disagree the moment a non-ASCII character appears in `%TEMP%`, which
+# would put an attempt on the wrong side of the ceiling the probe exists to
+# characterize. This crate holds a string in the encoding Windows uses, so its
+# `len` is the number under test rather than a conversion of one.
+#
+# That is the whole of what it is used for here: a code-unit count, at every site
+# where a length is compared against a Windows limit.
+# `encode_wide().count()` would give the same number, and the choice of this crate
+# over that is a vocabulary one -- the workspace's own WTF-16 type naming the
+# quantity rather than an ad-hoc count that happens to agree.
+wtf-string = { path = "../wtf-string" }
[dependencies.windows-sys]
version = "0.61.2"
@@ -76,6 +100,10 @@ features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Storage_FileSystem",
+ # SetCurrentDirectoryW, for the long-path probe: the current directory is
+ # half of what a relative path resolves against, so the probe has to place
+ # it deliberately rather than inherit whatever launched it.
+ "Win32_System_Environment",
"Win32_System_Diagnostics_Debug",
"Win32_System_IO",
"Win32_System_Pipes",
diff --git a/crates/windows-platform-probes/DESIGN-NOTES.md b/crates/windows-platform-probes/DESIGN-NOTES.md
index 5d7b1167..b0b88b74 100644
--- a/crates/windows-platform-probes/DESIGN-NOTES.md
+++ b/crates/windows-platform-probes/DESIGN-NOTES.md
@@ -498,3 +498,72 @@ that introduced the sink.
The ordering was deliberate. The sink had to exist before the probes could be
peeled off their originating branch in reviewable stages, and a design that
streams is a different design, not a later revision of this one.
+
+## The long-path probe: a pair of binaries, and a second declined hardening
+
+
+
+The `longPathAware` opt-in has two halves and neither is a runtime switch: a
+machine-wide registry value, and a per-executable manifest. Nothing a process can
+read off itself tells it whether the manifest half applies, so the question
+"does the opt-in lift `MAX_PATH` for a relative path?" cannot be answered by one
+binary with a flag. It is answered by two binaries that differ *only* in the
+manifest, and the finding is the difference between their reports.
+
+`build.rs` embeds the manifest with `rustc-link-arg-bin` naming
+`probe-long-path-aware` specifically, never `rustc-link-arg-bins`: the plural
+form would opt every binary in the crate into long paths and silently change what
+all the others measure. The aware binary does not assert its own manifest either;
+it reads a `cargo::rustc-cfg` the build script emits from the same guarded block
+that does the embedding, so the label and the linker cannot disagree. Before that,
+a non-MSVC target skipped the block and produced two binaries with no manifest
+between them, one of which still reported `manifest longPathAware : yes` -- the
+one failure this probe cannot make loudly, because the whole finding is the
+difference between the pair.
+
+### `measure` moves the process's current directory, and that is the point
+
+
+
+A relative path resolves against the current directory, so half of what is under
+test is *where the process is*. The probe therefore sets the current directory
+deliberately rather than inheriting whatever launched it, restores it in a `Drop`
+guard, and removes the tree it built.
+
+This is the same tension the error-mode probe records in
+[The concurrency hardening is knowingly declined](#the-concurrency-hardening-is-knowingly-declined):
+a probe may change process-wide state, a component may not. As there, **the
+concurrency hardening is knowingly declined.** `measure` is not safe to call
+concurrently, and its rustdoc says so. Giving each call a unique root would not
+fix it, because the current directory is per-process rather than per-call: two
+concurrent runs would still fight over the one thing being measured. The
+serialization lives in the tests, which take a mutex, and the binaries are
+single-threaded and call `measure` once.
+
+The declined alternative is worth naming so it is not re-proposed: threading the
+directory through as an explicit parameter and never calling
+`SetCurrentDirectoryW` would make the function safe, and would also stop it
+measuring the thing it exists to measure -- a *relative* path's resolution, which
+is defined against the process's current directory and nothing else.
+
+### The ceiling is applied to the path as written
+
+
+
+`MAX_PATH` is compared against the literal path handed to the call, before `..`
+is collapsed. That matters here because the `..` shape's literal is five units
+longer than its canonical form, so the two readings disagree in a five-unit band
+-- and the probe classifies every row on that number.
+
+Measured rather than assumed, using this crate's own un-manifested binary with
+the deep level forced to 21: plain resolved to 258 and **opened**, `..` resolved
+to 263 and was **refused**, against a content ceiling of 259. Had the collapse
+come first, both would have been 258 and both would have opened.
+
+The obvious shortcut does not settle this and should not be used. Reaching for
+`cmd.exe` measures `cmd`'s manifest, not the un-opted-in case: on the development
+host -- Windows 11 build 26200, `cmd.exe` 10.0.26100.1 -- `cmd` carries
+`longPathAware` in its own manifest beside `dpiAware`, so a long path that opens
+there says nothing about the ceiling. That is a fact about that binary on that
+build rather than about `cmd` for all time, which is exactly why the probe rests
+on a binary this workspace builds and manifests itself.
diff --git a/crates/windows-platform-probes/build.rs b/crates/windows-platform-probes/build.rs
new file mode 100644
index 00000000..05963230
--- /dev/null
+++ b/crates/windows-platform-probes/build.rs
@@ -0,0 +1,50 @@
+// Copyright (c) Mike Grier.
+
+//! Embeds `longPathAware` into **one** binary, so the long-path opt-in can be
+//! measured rather than read about.
+//!
+//! The opt-in has two halves and neither is a runtime switch: a machine-wide
+//! registry value, and a per-executable manifest. The manifest half is what
+//! this adds, and it is added to `probe-long-path-aware` **alone** --
+//! `probe-long-path-unaware` is the same code without it, because a comparison
+//! needs both sides and the un-opted-in case is what most consumers of this
+//! workspace actually have.
+//!
+//! `rustc-link-arg-bin` rather than `rustc-link-arg-bins`: the latter would
+//! opt every probe in this crate into long paths, silently changing what all
+//! the others measure -- stated without a count on purpose, so it stays true as
+//! probes are added.
+
+fn main() {
+ // Declared unconditionally, because the cfg's *absence* is as meaningful as
+ // its presence and an undeclared name would warn under `unexpected_cfgs`.
+ println!("cargo::rustc-check-cfg=cfg(long_path_manifest_embedded)");
+
+ // Only the MSVC linker understands these, and this crate is Windows-only
+ // anyway; guarding keeps a cross-compile from failing on a flag its linker
+ // has never heard of.
+ if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") {
+ let manifest =
+ std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("long-path-aware.manifest");
+ println!("cargo::rerun-if-changed=long-path-aware.manifest");
+ println!("cargo::rustc-link-arg-bin=probe-long-path-aware=/MANIFEST:EMBED");
+ println!(
+ "cargo::rustc-link-arg-bin=probe-long-path-aware=/MANIFESTINPUT:{}",
+ manifest.display()
+ );
+
+ // The aware binary reads this rather than claiming the opt-in outright,
+ // so its report cannot say `manifest longPathAware : yes` for an
+ // executable this script did not manifest.
+ //
+ // That mattered: with the claim hardcoded, a non-MSVC target skipped the
+ // block above and produced two binaries with *no* manifest between them,
+ // one of which still announced it had one. Both halves then measured the
+ // un-opted-in case and a reader comparing them would conclude the opt-in
+ // does not work -- a wrong answer, silently, in the one place this probe
+ // cannot fail loudly instead, because the whole finding is the difference
+ // between the two. Emitting the flag from the same branch that does the
+ // embedding is what keeps the label and the linker in step.
+ println!("cargo::rustc-cfg=long_path_manifest_embedded");
+ }
+}
diff --git a/crates/windows-platform-probes/long-path-aware.manifest b/crates/windows-platform-probes/long-path-aware.manifest
new file mode 100644
index 00000000..cca3cfd5
--- /dev/null
+++ b/crates/windows-platform-probes/long-path-aware.manifest
@@ -0,0 +1,9 @@
+
+
+
+
+
+ true
+
+
+
diff --git a/crates/windows-platform-probes/src/bin/long_path_aware.rs b/crates/windows-platform-probes/src/bin/long_path_aware.rs
new file mode 100644
index 00000000..b251c5c3
--- /dev/null
+++ b/crates/windows-platform-probes/src/bin/long_path_aware.rs
@@ -0,0 +1,32 @@
+// Copyright (c) Mike Grier.
+
+//! Measures the long-path opt-in **with** `longPathAware` in the manifest.
+//!
+//! **An experiment, not a component.** These probes measure platform behaviour
+//! and are not for production use. See this crate's DESIGN-NOTES.md.
+//!
+//! Its twin, `probe-long-path-unaware`, is the same code without the manifest.
+//! Run both: one row of results proves nothing, because the difference between
+//! them is the whole measurement.
+
+use windows_platform_probes::long_path_report;
+use windows_platform_probes::report::emit_report;
+
+fn main() {
+ // The probe's whole output policy, and it is one line: hand the renderer to
+ // the sink. Nothing here or below names a stream -- that is chosen once, in
+ // `report`, so retargeting a probe is not a rewrite.
+ //
+ // The manifest is this binary's whole difference from its twin, and it is a
+ // claim about the build rather than something measured at runtime: the two
+ // halves of the opt-in are a machine-wide registry value and a per-executable
+ // manifest, neither of which is a switch a process can read off itself.
+ //
+ // So the claim is *derived* from the flag `build.rs` sets in the same branch
+ // that embeds the manifest, never hardcoded. Hardcoding `true` here would let
+ // a target whose linker the script skips -- anything non-MSVC -- report
+ // `manifest longPathAware : yes` for an executable carrying no manifest,
+ // while both halves quietly measured the same un-opted-in case.
+ let manifest_aware = cfg!(long_path_manifest_embedded);
+ emit_report(|out| long_path_report::render(out, manifest_aware));
+}
diff --git a/crates/windows-platform-probes/src/bin/long_path_unaware.rs b/crates/windows-platform-probes/src/bin/long_path_unaware.rs
new file mode 100644
index 00000000..8cd32d70
--- /dev/null
+++ b/crates/windows-platform-probes/src/bin/long_path_unaware.rs
@@ -0,0 +1,25 @@
+// Copyright (c) Mike Grier.
+
+//! Measures the long-path opt-in **without** `longPathAware` in the manifest.
+//!
+//! **An experiment, not a component.** These probes measure platform behaviour
+//! and are not for production use. See this crate's DESIGN-NOTES.md.
+//!
+//! This is the case most consumers of this workspace actually have, which is
+//! why it is measured rather than assumed: a library cannot add a manifest to
+//! someone else's executable, so whatever this reports is what a caller who has
+//! not opted in will meet.
+
+use windows_platform_probes::long_path_report;
+use windows_platform_probes::report::emit_report;
+
+fn main() {
+ // The probe's whole output policy, and it is one line: hand the renderer to
+ // the sink. Nothing here or below names a stream -- that is chosen once, in
+ // `report`, so retargeting a probe is not a rewrite.
+ //
+ // `false` is this binary's whole difference from its twin: `build.rs`
+ // embeds the manifest into `probe-long-path-aware` alone, so this one is
+ // the same code compiled without the opt-in.
+ emit_report(|out| long_path_report::render(out, false));
+}
diff --git a/crates/windows-platform-probes/src/lib.rs b/crates/windows-platform-probes/src/lib.rs
index bc3e537f..c16c9ebe 100644
--- a/crates/windows-platform-probes/src/lib.rs
+++ b/crates/windows-platform-probes/src/lib.rs
@@ -104,6 +104,7 @@
//! | [`completion_port::measure`] | ignored | IOCP association, and `CreateThreadpoolIo`, foreclose `IoRing` use of a handle |
//! | [`cancel_io::cancel_against_idle_thread`] | binary only | `CancelSynchronousIo` is point-in-time against an idle thread |
//! | [`cancel_io::cancel_against_busy_thread`] | binary only | it can block indefinitely against a thread re-entering synchronous I/O |
+//! | [`long_path::measure`] | binary only | whether the `longPathAware` manifest opt-in lifts `MAX_PATH` for a *relative* path -- binary only because the answer is the difference between two differently-manifested executables, which no single in-process test can observe |
#![cfg(windows)]
#![forbid(unsafe_op_in_unsafe_fn)]
@@ -115,6 +116,8 @@ pub mod device_map;
pub mod error_mode;
pub mod handle_state;
pub mod ioring;
+pub mod long_path;
+pub mod long_path_report;
pub mod pool_growth;
pub mod report;
pub mod worker_context;
diff --git a/crates/windows-platform-probes/src/long_path.rs b/crates/windows-platform-probes/src/long_path.rs
new file mode 100644
index 00000000..646d510c
--- /dev/null
+++ b/crates/windows-platform-probes/src/long_path.rs
@@ -0,0 +1,706 @@
+// Copyright (c) Mike Grier.
+
+//! Does the long-path opt-in lift `MAX_PATH` for a **relative** path, and does
+//! it change how that path is parsed?
+//!
+//! **An experiment, not a component.** These probes measure platform behaviour
+//! and are not for production use. Do not call them from production code, and
+//! do not lift a technique out of here. See this crate's DESIGN-NOTES.md.
+//!
+//! # The question, and why reading could not settle it
+//!
+//! Microsoft's *Maximum Path Length Limitation* puts "relative paths are always
+//! limited to a total of MAX_PATH characters" inside the `\\?\` **prefix**
+//! section, where it is a consequence of that mechanism -- the prefix cannot be
+//! applied to a relative path. Its separate long-path opt-in section says the
+//! restriction is removed from a list of functions that includes `CreateFileW`,
+//! and excludes nothing. So the documented answer is that the opt-in covers
+//! relative paths.
+//!
+//! That reading produced two wrong answers in one PR #56 review cycle, in
+//! opposite directions, which is the reason this exists as a measurement.
+//!
+//! # The hypothesis this is built to falsify
+//!
+//! A plausible implementation of the opt-in is to regularize the path and
+//! prepend `\\?\` before proceeding as usual. That prefix is precisely what
+//! disables `.`, `..` and forward-slash translation -- so if that is how it
+//! works, a relative path using any of those could resolve **under** `MAX_PATH`
+//! and fail **over** it. A discontinuity at a length boundary is the worst kind
+//! to meet in production, and no page states it.
+//!
+//! So each shape is measured at both lengths. A shape that works short and
+//! fails long is the sharp edge; a shape that works at both is evidence the
+//! opt-in does not re-parse.
+//!
+//! # Reading the result
+//!
+//! Run both binaries. `probe-long-path-aware` carries `longPathAware` in its
+//! manifest; `probe-long-path-unaware` is the same code without it, because the
+//! un-opted-in case is what most consumers of this workspace actually have.
+//! The registry half (`LongPathsEnabled`) is a machine setting and is reported
+//! rather than assumed, since a result gathered without it says nothing.
+//!
+//! **The un-opted-in half is a baseline, not a counter-example**, and its report
+//! says so. `MAX_PATH` applying to a process that never opted in is what
+//! `MAX_PATH` means; only a refusal with *both* halves in effect would bear on
+//! the documented reading. The verdict consults both before drawing any
+//! conclusion, so the unaware binary reports what it is -- the case the aware one
+//! is read against -- rather than announcing a contradiction it did not test.
+
+use std::ffi::OsStr;
+use std::os::windows::ffi::OsStrExt;
+use std::path::{Path, PathBuf};
+
+use windows_sys::Win32::Foundation::{
+ CloseHandle, ERROR_ALREADY_EXISTS, ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, GetLastError,
+ INVALID_HANDLE_VALUE,
+};
+use windows_sys::Win32::Storage::FileSystem::{
+ CREATE_ALWAYS, CreateDirectoryW, CreateFileW, FILE_ATTRIBUTE_NORMAL, FILE_GENERIC_READ,
+ FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
+};
+// `SetCurrentDirectoryW` lives under Environment rather than FileSystem,
+// because the current directory is per-process environment state rather than a
+// file operation.
+use windows_sys::Win32::System::Environment::{GetCurrentDirectoryW, SetCurrentDirectoryW};
+use wtf_string::Wtf16String;
+
+/// Windows's classic path ceiling, **counting the terminating NUL**.
+const MAX_PATH: usize = 260;
+
+/// The longest path content that fits under the ceiling, terminator excluded.
+///
+/// The distinction is the whole subject of this probe, so it is spelled out
+/// rather than folded into a comparison: a path of exactly `MAX_PATH` content
+/// units does *not* fit, because the NUL needs the last one. Comparing against
+/// `MAX_PATH` instead would classify that path as under the ceiling while
+/// Windows refused it for length -- a row asserting both at once, at exactly the
+/// boundary this probe exists to characterize.
+///
+/// This is the convention the rest of the workspace already states and tests --
+/// see `windows-namespace-request-sys` and `windows-file-enumeration-sys`, whose
+/// `path` modules define the same pair and assert that the content ceiling is
+/// one less than `MAX_PATH`. A probe that measured against a different ceiling
+/// than the crates whose designs rest on it would be answering a question nobody
+/// asked.
+///
+/// Public because the report has to *print* it. A column headed `> MAX` next to
+/// a module defining `MAX_PATH` as 260 is read as "over 260", and at a resolved
+/// length of exactly 260 that reading is wrong in the one place this probe is
+/// supposed to be exact. The renderer states the number instead of naming a
+/// constant the reader cannot see.
+pub const MAX_PATH_CONTENT: usize = MAX_PATH - 1;
+
+/// One directory level of the deep tree. Short, so the depth rather than the
+/// width is what carries the length, and free of `.` so no segment is itself a
+/// relative operator.
+const SEGMENT: &str = "aaaaaaaa";
+
+/// The file every attempt tries to open.
+const TARGET: &str = "target.txt";
+
+/// A path shape, and whether it is expected to survive `\\?\` parsing.
+///
+/// The three differ only in features the prefix disables, which is what makes
+/// the comparison a test of the hypothesis rather than of path length alone.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum Shape {
+ /// Plain backslash-separated segments. Legal under `\\?\` too, so this is
+ /// the control: it isolates length from parsing.
+ Plain,
+ /// Contains `b\..`, which cancels to nothing -- but only if something
+ /// resolves it. `\\?\` does not.
+ DotDot,
+ /// Uses `/` as the separator. Win32 converts it; `\\?\` does not.
+ ForwardSlash,
+}
+
+impl Shape {
+ /// A short word for a table.
+ #[must_use]
+ pub fn label(self) -> &'static str {
+ match self {
+ Self::Plain => "plain",
+ Self::DotDot => "with `..`",
+ Self::ForwardSlash => "forward slashes",
+ }
+ }
+
+ /// Whether `\\?\` parsing would still resolve this shape.
+ ///
+ /// The prediction the hypothesis makes: if the opt-in prefixes internally,
+ /// the two shapes answering `false` here fail once the path grows past
+ /// `MAX_PATH`, while `Plain` keeps working.
+ #[must_use]
+ pub fn survives_verbatim_parsing(self) -> bool {
+ matches!(self, Self::Plain)
+ }
+}
+
+/// What one attempt did.
+#[derive(Clone, Debug)]
+pub struct Attempt {
+ /// The shape tried.
+ pub shape: Shape,
+ /// Total length the call had to resolve: current directory plus the
+ /// relative path, **as written**, not the length of the relative part alone
+ /// and not the length after `..` is collapsed.
+ ///
+ /// That distinction is load-bearing for the `..` shape, whose literal is
+ /// five units longer than its canonical form, so it was **measured** rather
+ /// than assumed: forcing the deep level to 21 on the development host put
+ /// plain at 258 and `..` at 263 against a content ceiling of 259, and in a
+ /// binary with no `longPathAware` manifest plain **opened** while `..` was
+ /// **refused**. Had Windows collapsed `..` before applying the ceiling, both
+ /// would have been 258 and both would have opened. So the length Windows
+ /// compares is the one written, and this is that number.
+ ///
+ /// The measurement above is the evidence, and it is self-contained: it uses
+ /// this crate's own un-manifested binary, so it does not depend on any
+ /// assumption about some other program's manifest.
+ ///
+ /// That independence is the point, because the obvious shortcut is unsound.
+ /// Reaching for `cmd.exe` to try a long path measures whatever `cmd`'s
+ /// manifest says, not the un-opted-in case: on the development host --
+ /// Windows 11 build 26200, `cmd.exe` 10.0.26100.1 -- `cmd` carries
+ /// `longPathAware` in its own manifest, beside `dpiAware`, so a path that
+ /// opens there says nothing about the ceiling. Stated with the build because
+ /// it is a fact about that binary on that host rather than about `cmd`
+ /// forever: it has not always been so, and a probe that assumed it either way
+ /// would be resting on someone else's manifest instead of measuring.
+ ///
+ /// **In UTF-16 code units**, which is the unit `MAX_PATH` itself is
+ /// expressed in. Counting Rust's platform encoding instead would disagree
+ /// the moment a non-ASCII character appeared in the temporary directory's
+ /// path, and would put an attempt on the wrong side of the ceiling.
+ pub resolved_len: usize,
+ /// Whether that total is too long to fit under the ceiling.
+ ///
+ /// That is `> MAX_PATH_CONTENT` (259), **not** `> MAX_PATH` (260): a path of
+ /// exactly 260 content units does not fit, because the terminator needs the
+ /// last one. The field name is older than the distinction and is kept for the
+ /// column it feeds; the comparison is the one the sibling crates make.
+ pub over_max_path: bool,
+ /// Whether `CreateFileW` opened the file.
+ pub opened: bool,
+ /// The Win32 error when it did not.
+ pub error: u32,
+}
+
+/// Everything one run observed.
+#[derive(Clone, Debug)]
+pub struct Observation {
+ /// Whether this binary declares `longPathAware`.
+ pub manifest_aware: bool,
+ /// Whether the machine has `LongPathsEnabled` set to 1.
+ /// `None` when the run was refused before the registry could be read.
+ ///
+ /// A `bool` cannot say "not consulted", and the difference matters: reading
+ /// an unconsulted flag as `false` made a refused run report `LongPathsEnabled
+ /// : unset or 0` and "the machine half of the opt-in is absent" on a host
+ /// where it is set to 1 -- a measured-sounding claim about a query that was
+ /// never issued. The refusal has to come first, because reading the registry
+ /// spawns a process and that is one of the calls that hangs, so the honest
+ /// answer is a third state rather than a default.
+ pub registry_enabled: Option,
+ /// Every attempt, short ones first.
+ pub attempts: Vec,
+ /// Set when the apparatus itself failed, in which case the attempts say
+ /// nothing about the machine.
+ pub apparatus_error: Option,
+}
+
+/// A null-terminated wide string, as Win32 wants.
+fn wide(path: &OsStr) -> Vec {
+ path.encode_wide().chain(std::iter::once(0)).collect()
+}
+
+/// Read `LongPathsEnabled`, which is half the opt-in and is a machine setting
+/// rather than anything this process controls.
+///
+/// Reported rather than assumed: a run on a machine without it measures the
+/// un-opted-in case whatever the manifest says, and reading the answer as
+/// though the opt-in were active would invert the conclusion.
+#[must_use]
+pub fn registry_enabled() -> bool {
+ // Read through `reg.exe` rather than taking a registry dependency for one
+ // value in a probe. A missing key, a non-zero exit and an unparsable value
+ // all mean the same thing here: not enabled.
+ std::process::Command::new("reg")
+ .args([
+ "query",
+ r"HKLM\SYSTEM\CurrentControlSet\Control\FileSystem",
+ "/v",
+ "LongPathsEnabled",
+ ])
+ .output()
+ .ok()
+ .filter(|out| out.status.success())
+ .map(|out| enabled_in(&String::from_utf8_lossy(&out.stdout)))
+ .unwrap_or(false)
+}
+
+/// Whether `reg query`'s output says the machine half of the opt-in is on.
+///
+/// Separated from the spawn so the reading is testable against captured output
+/// rather than against whatever the developing machine happens to be set to.
+///
+/// Any nonzero value counts as enabled: this is a boolean flag stored in a
+/// DWORD, so the value that is not zero is the one that means yes.
+fn enabled_in(stdout: &str) -> bool {
+ registry_dword(stdout, "LongPathsEnabled").is_some_and(|value| value != 0)
+}
+
+/// The DWORD `reg query ... /v ` printed, if it printed one.
+///
+/// Parsed as a whole token rather than searched for as a substring. `reg.exe`
+/// prints the value in hex, so a substring test for `0x1` also matches `0x10`
+/// and every other value that merely starts that way -- which would report a
+/// machine as opted in on the strength of an unrelated setting.
+fn registry_dword(stdout: &str, name: &str) -> Option {
+ stdout.lines().find_map(|line| {
+ let mut tokens = line.split_whitespace();
+ // `reg.exe` prints ` REG_DWORD 0x1`, and this probe queries
+ // one value, so a line that does not have that shape is not the answer.
+ if tokens.next()? != name || tokens.next()? != "REG_DWORD" {
+ return None;
+ }
+ let digits = tokens.next()?.strip_prefix("0x")?;
+ u32::from_str_radix(digits, 16).ok()
+ })
+}
+
+/// Create one directory by absolute `\\?\` path, so building the apparatus
+/// never depends on the behaviour under test.
+fn create_dir_verbatim(path: &Path) -> Result<(), String> {
+ let verbatim = PathBuf::from(format!(r"\\?\{}", path.display()));
+ let wide = wide(verbatim.as_os_str());
+ // SAFETY: `wide` is a live null-terminated buffer for the duration of the
+ // call, and a null security descriptor requests the default.
+ let created = unsafe { CreateDirectoryW(wide.as_ptr(), std::ptr::null()) };
+ if created == 0 {
+ // SAFETY: called immediately after the failing call.
+ let error = unsafe { GetLastError() };
+ // Already there is success for our purposes: the apparatus is a shape on
+ // disk, not a thing this run must be the one to have created.
+ if error != ERROR_ALREADY_EXISTS {
+ return Err(format!("CreateDirectoryW({verbatim:?}) failed: {error}"));
+ }
+ }
+ Ok(())
+}
+
+/// Build a directory chain `depth` levels deep under `root`, returning the
+/// relative path that reaches the bottom.
+fn build_tree(root: &Path, depth: usize) -> Result {
+ let mut absolute = root.to_path_buf();
+ let mut relative = PathBuf::new();
+ for _ in 0..depth {
+ absolute.push(SEGMENT);
+ relative.push(SEGMENT);
+ create_dir_verbatim(&absolute)?;
+ }
+ Ok(relative)
+}
+
+/// Write the target file at the bottom of the chain, by absolute `\\?\` path.
+fn create_target(bottom: &Path) -> Result<(), String> {
+ let verbatim = PathBuf::from(format!(r"\\?\{}\{TARGET}", bottom.display()));
+ let wide = wide(verbatim.as_os_str());
+ // SAFETY: `wide` is live and null-terminated; the handle is closed below.
+ let handle = unsafe {
+ CreateFileW(
+ wide.as_ptr(),
+ FILE_GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ std::ptr::null(),
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ std::ptr::null_mut(),
+ )
+ };
+ if handle == INVALID_HANDLE_VALUE {
+ // SAFETY: called immediately after the failing call.
+ return Err(format!("could not create the target: {}", unsafe {
+ GetLastError()
+ }));
+ }
+ // SAFETY: `handle` is a live handle this function just opened.
+ unsafe { CloseHandle(handle) };
+ Ok(())
+}
+
+/// Render one relative path of the requested shape reaching `depth` levels down.
+fn relative_path(depth: usize, shape: Shape) -> String {
+ let mut parts: Vec = (0..depth).map(|_| SEGMENT.to_string()).collect();
+ match shape {
+ Shape::Plain | Shape::ForwardSlash => {}
+ Shape::DotDot => {
+ // A descent that immediately cancels. Placed at the bottom so the
+ // path is at its longest when the operator appears -- the position
+ // where a prefix-then-parse implementation would be least able to
+ // resolve it.
+ parts.push("b".to_string());
+ parts.push("..".to_string());
+ }
+ }
+ parts.push(TARGET.to_string());
+ let separator = if shape == Shape::ForwardSlash {
+ "/"
+ } else {
+ r"\"
+ };
+ parts.join(separator)
+}
+
+/// Try to open the target through one relative path, from the current
+/// directory, with no prefix of any kind.
+fn attempt(current_dir_len: usize, depth: usize, shape: Shape) -> Attempt {
+ let relative = relative_path(depth, shape);
+ // Plus one for the separator Windows inserts when it joins the two. Both
+ // lengths are UTF-16 code units, which is the unit `MAX_PATH` is expressed
+ // in -- see `current_dir_len`'s construction in `measure`. The relative
+ // part is built from ASCII constants here, so its two counts agree today;
+ // it is measured the same way regardless, because a unit that is only
+ // correct while the input happens to be ASCII is one waiting to be wrong.
+ let resolved_len = current_dir_len + 1 + Wtf16String::from_os_str(OsStr::new(&relative)).len();
+ let wide = wide(OsStr::new(&relative));
+ // SAFETY: `wide` is a live null-terminated buffer for the duration of the
+ // call; the handle, if any, is closed below.
+ let handle = unsafe {
+ CreateFileW(
+ wide.as_ptr(),
+ FILE_GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ std::ptr::null(),
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ std::ptr::null_mut(),
+ )
+ };
+ let opened = handle != INVALID_HANDLE_VALUE;
+ let error = if opened {
+ // SAFETY: `handle` is a live handle this call just opened.
+ unsafe { CloseHandle(handle) };
+ 0
+ } else {
+ // SAFETY: called immediately after the failing call.
+ unsafe { GetLastError() }
+ };
+ Attempt {
+ shape,
+ resolved_len,
+ over_max_path: resolved_len > MAX_PATH_CONTENT,
+ opened,
+ error,
+ }
+}
+
+/// This process's current directory, as a null-terminated wide string.
+///
+/// Sized then fetched, which is this API's documented shape: a zero length with
+/// a null buffer returns the size *including* the terminator, and the filling
+/// call returns the count *excluding* it.
+fn current_directory() -> Result, String> {
+ // SAFETY: the documented sizing form -- a zero length with a null buffer,
+ // which writes nothing and returns the required size.
+ let needed = unsafe { GetCurrentDirectoryW(0, std::ptr::null_mut()) };
+ if needed == 0 {
+ // SAFETY: called immediately after the failing call.
+ return Err(format!("GetCurrentDirectoryW sizing failed: {}", unsafe {
+ GetLastError()
+ }));
+ }
+ let mut buffer = vec![0_u16; needed as usize];
+ // SAFETY: `buffer` has `needed` elements, which is the size the call above
+ // asked for, and is writable for that length.
+ let written = unsafe { GetCurrentDirectoryW(needed, buffer.as_mut_ptr()) };
+ if written == 0 || written >= needed {
+ // SAFETY: called immediately after the failing call.
+ return Err(format!("GetCurrentDirectoryW failed: {}", unsafe {
+ GetLastError()
+ }));
+ }
+ Ok(buffer)
+}
+
+/// The temporary tree and the process state this experiment borrows.
+///
+/// **A guard, because both are leaks if `measure` returns early**, and it
+/// returns early on every apparatus failure. This is a library function, so
+/// neither is excused by the probe binaries exiting straight afterwards: a test
+/// or any other caller keeps running in the process whose current directory was
+/// moved.
+///
+/// The tree is the sharper of the two. It is deliberately longer than
+/// `MAX_PATH`, which is the very property that stops Explorer and `del` from
+/// removing it -- so litter left in `%TEMP%` by a probe about long paths is
+/// litter that is hard to clear up by hand.
+struct Apparatus {
+ root: PathBuf,
+ /// Where the process was before [`Self::enter`], if it moved at all.
+ previous_directory: Option>,
+}
+
+impl Apparatus {
+ fn new(root: PathBuf) -> Self {
+ Self {
+ root,
+ previous_directory: None,
+ }
+ }
+
+ /// Move the process into `directory`, remembering where it was.
+ fn enter(&mut self, directory: &Path) -> Result<(), String> {
+ // Captured *before* the move, or there is nothing to go back to.
+ let previous = current_directory()?;
+ let wide = wide(directory.as_os_str());
+ // SAFETY: `wide` is a live null-terminated buffer for the call.
+ if unsafe { SetCurrentDirectoryW(wide.as_ptr()) } == 0 {
+ // SAFETY: called immediately after the failing call.
+ return Err(format!("SetCurrentDirectoryW failed: {}", unsafe {
+ GetLastError()
+ }));
+ }
+ self.previous_directory = Some(previous);
+ Ok(())
+ }
+}
+
+impl Drop for Apparatus {
+ fn drop(&mut self) {
+ // **Restore the directory first, and that ordering is load-bearing.**
+ // A process's current directory holds a handle on it, so removing the
+ // tree while parked inside it fails -- the cleanup would silently do
+ // nothing and leave exactly the litter this guard exists to prevent.
+ if let Some(previous) = &self.previous_directory {
+ // SAFETY: `previous` is the null-terminated buffer
+ // `GetCurrentDirectoryW` filled, still live here.
+ unsafe { SetCurrentDirectoryW(previous.as_ptr()) };
+ }
+
+ // By verbatim path, for the same reason the tree was built by one: the
+ // deep branch is past `MAX_PATH`, so an ordinary path would fail to
+ // reach it on a host that has not opted in -- which is half the hosts
+ // this probe is meant to run on.
+ let verbatim = PathBuf::from(format!(r"\\?\{}", self.root.display()));
+ // Best-effort: a failure here leaves litter, which is worth neither a
+ // panic in a `Drop` nor a field on an observation about path lengths.
+ let _ = std::fs::remove_dir_all(&verbatim);
+ }
+}
+
+/// Run the experiment.
+///
+/// `manifest_aware` is what the *caller* knows about its own manifest -- the
+/// process cannot ask Windows whether it opted in, so the two binaries pass
+/// their own answer and are named for it.
+///
+/// The temporary tree and the current directory are both restored before this
+/// returns, on every path including the apparatus failures -- see `Apparatus`.
+///
+/// # Not safe to call concurrently
+///
+/// This borrows **process-wide** state: it moves the current directory, and it
+/// builds its tree under a root named after the process id. Two calls at once
+/// in one process share both -- one would remove the tree the other was still
+/// using, and they would fight over the directory. A unique root per call would
+/// not fix that, because there is one current directory per process however the
+/// trees are named.
+///
+/// The probe binaries call this once and exit, so this costs them nothing. A
+/// caller running it from a test suite must serialize its calls; the tests
+/// beside this module do exactly that.
+#[must_use]
+pub fn measure(manifest_aware: bool) -> Observation {
+ // First, before anything at all: both `temp_dir()` and the `reg.exe` spawn
+ // below hang on an over-long temporary directory, so neither may run first.
+ // `registry_enabled` is `None` here rather than `false`: the query was never
+ // issued, and saying "not enabled" would be a claim about the machine.
+ if let Some(error) = temp_dir_refusal(
+ std::env::var_os("TMP").as_deref(),
+ std::env::var_os("TEMP").as_deref(),
+ ) {
+ return Observation {
+ manifest_aware,
+ registry_enabled: None,
+ attempts: Vec::new(),
+ apparatus_error: Some(error),
+ };
+ }
+
+ let mut observation = Observation {
+ manifest_aware,
+ registry_enabled: Some(registry_enabled()),
+ attempts: Vec::new(),
+ apparatus_error: None,
+ };
+
+ let root = std::env::temp_dir().join(format!("long-path-probe-{}", std::process::id()));
+ if let Err(error) = create_dir_verbatim(&root) {
+ observation.apparatus_error = Some(error);
+ return observation;
+ }
+ // From here on the tree exists, so every exit below has something to clean
+ // up -- including the early returns, which is why this is a guard.
+ let mut apparatus = Apparatus::new(root.clone());
+
+ // Deep enough that the resolved path clears `MAX_PATH` with room to spare,
+ // and shallow enough that the short case stays well under it.
+ let deep = 40;
+ let shallow = 1;
+
+ // Only the side effect is wanted. Each attempt spells its own relative path,
+ // because the spelling is what is under test.
+ if let Err(error) = build_tree(&root, deep) {
+ observation.apparatus_error = Some(error);
+ return observation;
+ }
+ // `b`, for the `..` shape to descend into and immediately leave.
+ for depth in [shallow, deep] {
+ let mut bottom = root.clone();
+ for _ in 0..depth {
+ bottom.push(SEGMENT);
+ }
+ if let Err(error) = create_dir_verbatim(&bottom.join("b")) {
+ observation.apparatus_error = Some(error);
+ return observation;
+ }
+ if let Err(error) = create_target(&bottom) {
+ observation.apparatus_error = Some(error);
+ return observation;
+ }
+ }
+
+ // The current directory is the short root for every attempt, so the length
+ // under test lives in the relative path rather than in the cwd.
+ if let Err(error) = apparatus.enter(&root) {
+ observation.apparatus_error = Some(error);
+ return observation;
+ }
+ // **UTF-16 code units, not bytes.** `MAX_PATH` counts what Windows counts,
+ // and `OsStr::len` counts Rust's platform encoding -- which is WTF-8 here,
+ // so a non-ASCII character in `%TEMP%` makes the two disagree and can put
+ // an attempt on the wrong side of the ceiling in the report. `Wtf16String`
+ // is the workspace's own answer to exactly this: it holds the string in the
+ // encoding Windows uses, so its `len` is the number under test rather than
+ // a conversion of one.
+ let current_dir_len = Wtf16String::from_os_str(root.as_os_str()).len();
+
+ for depth in [shallow, deep] {
+ for shape in [Shape::Plain, Shape::DotDot, Shape::ForwardSlash] {
+ observation
+ .attempts
+ .push(attempt(current_dir_len, depth, shape));
+ }
+ }
+
+ observation
+}
+
+/// The longest temporary directory this probe will run in, **in UTF-16 units**.
+///
+/// A chosen limit, not a derived one. A long enough temporary directory makes a
+/// `longPathAware` process hang rather than fail, and this sits far enough short
+/// of that to not care where exactly it starts.
+const MAX_TEMP_DIR: usize = 200;
+
+/// Why this run must refuse to start, if it must, given `%TMP%` and `%TEMP%`.
+///
+/// Runs before anything that could resolve a path, because the failure it avoids
+/// is a hang: there is nothing to check afterwards when the call never returns.
+///
+/// The order mirrors `GetTempPath`: `%TMP%` first, then `%TEMP%`, then fallbacks
+/// that are always short. Checking both unconditionally would refuse a run whose
+/// `%TMP%` is perfectly usable merely because a stale `%TEMP%` sits beside it.
+fn temp_dir_refusal(tmp: Option<&OsStr>, temp: Option<&OsStr>) -> Option {
+ let (name, value) = match (tmp, temp) {
+ (Some(value), _) => ("TMP", value),
+ (None, Some(value)) => ("TEMP", value),
+ (None, None) => return None,
+ };
+
+ // The apparatus is built through `\\?\` paths so that creating it never
+ // depends on the behaviour under test, and this crate composes that prefix by
+ // concatenation. Three shapes of temporary directory make that composition
+ // wrong rather than merely long, and all are refused for the same reason the
+ // length is: the probe would report an apparatus failure, or measure a path
+ // that is not the one it names, and either way say nothing about the ceiling.
+ //
+ // Checked in this order because each check needs the previous one to have
+ // passed to be able to say anything true: the prefix tests read the value as
+ // text, and reading an ill-formed value as text is exactly the substitution
+ // the second refusal exists to prevent. Classifying first and validating
+ // afterwards would refuse an ill-formed value under whichever prefix its
+ // replacement characters happened to spell.
+
+ // `Path::display` substitutes U+FFFD for an unpaired surrogate, so a name
+ // containing one would compose a verbatim path naming a different file --
+ // silently, and in the apparatus rather than in the measurement.
+ let Some(text) = value.to_str() else {
+ return Some(format!(
+ "%{name}% is not well-formed UTF-16. The apparatus composes its `\\\\?\\` \
+ paths as text, which would replace the ill-formed part and name a \
+ different file. Point %{name}% somewhere expressible."
+ ));
+ };
+
+ // `\\?\` and `\\.\` open with two backslashes but are the device namespace,
+ // not UNC, so they are separated out ahead of the UNC test rather than
+ // reported as a server share the machine does not have. The refusal is not
+ // only about the doubled prefix: `\\?\` turns off the path normalisation that
+ // this probe exists to measure, so a run rooted there would measure the
+ // verbatim path's ceiling and label it the ordinary one.
+ if let Some(prefix) = [r"\\?\", r"\\.\"]
+ .into_iter()
+ .find(|prefix| text.starts_with(prefix))
+ {
+ return Some(format!(
+ "%{name}% starts with `{prefix}`, which names the device namespace rather \
+ than an ordinary directory. This probe composes its own `\\\\?\\` prefix by \
+ concatenation, and `\\\\?\\` additionally turns off the path normalisation \
+ the probe measures. Point %{name}% at an ordinary local directory."
+ ));
+ }
+
+ // A UNC root needs `\\?\UNC\server\share`, not `\\?\` glued to `\\server`.
+ // Supporting it properly is not the problem -- it is three lines -- but it
+ // could not be exercised on any machine this workspace is developed or tested
+ // on, and an untested path through the apparatus is worth less than an honest
+ // refusal.
+ if text.starts_with(r"\\") {
+ return Some(format!(
+ "%{name}% is a UNC path. This probe builds its apparatus through `\\\\?\\` \
+ paths, which spell a UNC root differently, and it does not implement that \
+ spelling. Point %{name}% at a local directory."
+ ));
+ }
+
+ // UTF-16 units, the unit Windows counts, for the reason `resolved_len`
+ // records: a non-ASCII character makes a byte count disagree.
+ let units = Wtf16String::from_os_str(value).len();
+ if units <= MAX_TEMP_DIR {
+ return None;
+ }
+
+ Some(format!(
+ "%{name}% is {units} UTF-16 units, over this probe's limit of {MAX_TEMP_DIR}. \
+ A temporary directory that long makes a longPathAware process hang rather \
+ than fail, so the run is refused instead. Point %{name}% somewhere shorter."
+ ))
+}
+
+/// Whether an error means "the path was rejected for length", as opposed to a
+/// genuine absence.
+///
+/// Windows reports an over-long path as `ERROR_PATH_NOT_FOUND` rather than
+/// anything length-specific, which is why the apparatus creates every target
+/// first: a `NOT_FOUND` from a file that provably exists is the length refusal.
+#[must_use]
+pub fn is_refusal(attempt: &Attempt) -> bool {
+ !attempt.opened && matches!(attempt.error, ERROR_PATH_NOT_FOUND | ERROR_FILE_NOT_FOUND)
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/crates/windows-platform-probes/src/long_path/tests.rs b/crates/windows-platform-probes/src/long_path/tests.rs
new file mode 100644
index 00000000..876008ed
--- /dev/null
+++ b/crates/windows-platform-probes/src/long_path/tests.rs
@@ -0,0 +1,363 @@
+// Copyright (c) 2026 Mike Grier
+//! Tests for [`measure`](super::measure)'s apparatus.
+//!
+//! **These assert what the experiment gives back, not what it found.** What it
+//! finds is a fact about the host -- whether the manifest and the registry
+//! setting lift `MAX_PATH` -- and asserting that here would encode this
+//! machine's configuration into the suite. What must hold on every host is that
+//! running the experiment leaves the process and the disk as it found them.
+//!
+//! # Why these serialize
+//!
+//! `measure` borrows two pieces of **process-wide** state: the current
+//! directory, and a temporary root named after the process id. This crate's
+//! tests run as threads in one process, so two of these running at once would
+//! share both -- one call removing the tree another was still using, and the
+//! two fighting over the current directory.
+//!
+//! That is a property of `measure` rather than a defect in it, and the fix is
+//! not a per-call unique root: the current directory is one per process however
+//! the directories are named, so concurrent calls could not work whatever the
+//! tree was called. See `measure`'s own documentation. These tests therefore
+//! take a lock, which is also what a consumer would have to do.
+//!
+//! Found by writing the third test below, which failed until the lock existed.
+
+use std::ffi::OsString;
+use std::os::windows::ffi::OsStringExt as _;
+use std::path::PathBuf;
+use std::sync::{Mutex, MutexGuard};
+
+use wtf_string::Wtf16String;
+
+use super::measure;
+
+/// Serializes the tests here, for the reason in this module's documentation.
+static APPARATUS: Mutex<()> = Mutex::new(());
+
+/// Take the lock, ignoring poisoning.
+///
+/// A panic in one of these tests leaves the mutex poisoned, which would turn
+/// one real failure into three and hide which was the original.
+fn exclusive() -> MutexGuard<'static, ()> {
+ APPARATUS
+ .lock()
+ .unwrap_or_else(|poisoned| poisoned.into_inner())
+}
+
+/// Where this process's current directory points.
+fn current_directory() -> PathBuf {
+ std::env::current_dir().expect("the test process must have a current directory")
+}
+
+/// The tree `measure` builds, named as it names it.
+fn apparatus_root() -> PathBuf {
+ std::env::temp_dir().join(format!("long-path-probe-{}", std::process::id()))
+}
+
+#[test]
+fn measuring_leaves_the_current_directory_where_it_found_it() {
+ // **The leak that matters most in a library.** `measure` moves the process
+ // into its temporary root so the length under test lives in the relative
+ // path rather than in the current directory. The process is shared, so
+ // failing to move back would silently re-root every later relative path in
+ // whatever called this -- a test, or any other consumer.
+ let _lock = exclusive();
+ let before = current_directory();
+
+ let _ = measure(false);
+
+ assert_eq!(
+ current_directory(),
+ before,
+ "the experiment left the process parked somewhere else"
+ );
+}
+
+#[test]
+fn measuring_removes_the_tree_it_built() {
+ // The tree is deliberately deeper than `MAX_PATH`, which is exactly what
+ // stops Explorer and `del` from clearing it up -- so litter from a probe
+ // about long paths is litter that is awkward to remove by hand.
+ let _lock = exclusive();
+
+ let _ = measure(false);
+
+ assert!(
+ !apparatus_root().exists(),
+ "the experiment left its temporary tree behind at {}",
+ apparatus_root().display()
+ );
+}
+
+#[test]
+fn the_apparatus_is_cleaned_up_even_when_the_experiment_is_run_twice() {
+ // Two calls in one process, which is the shape a test run takes and the
+ // shape the probe binaries never do.
+ //
+ // A leak from the first run does *not* surface as an apparatus error, and it
+ // is worth saying so: `create_dir_verbatim` treats `ERROR_ALREADY_EXISTS` as
+ // success and `create_target` opens with `CREATE_ALWAYS`, so a surviving tree
+ // is silently reused and the second run reports itself perfectly healthy. The
+ // assertion that does the work here is the `exists()` check below -- the
+ // second `apparatus_error` is a control on the run itself, not the leak
+ // detector.
+ let _lock = exclusive();
+ let before = current_directory();
+
+ let first = measure(false);
+ let second = measure(false);
+
+ assert_eq!(first.apparatus_error, None, "first run");
+ assert_eq!(second.apparatus_error, None, "second run");
+ assert!(
+ !apparatus_root().exists(),
+ "{} survived",
+ apparatus_root().display()
+ );
+ assert_eq!(current_directory(), before);
+}
+
+#[test]
+fn the_resolved_length_is_counted_in_the_unit_max_path_uses() {
+ // **The bug this guards is invisible on an ASCII host**, which is every
+ // machine this has run on so far. `MAX_PATH` counts UTF-16 code units;
+ // `OsStr::len` counts Rust's platform encoding, which is WTF-8 here. The
+ // two agree for ASCII and diverge for anything else, so a `%TEMP%` with a
+ // non-ASCII character made the reported length too large and could push an
+ // attempt onto the wrong side of the ceiling in the report.
+ //
+ // Asserted on the encoding rather than through `measure`, because the fault
+ // needs a non-ASCII temporary directory that this test cannot conjure --
+ // and pinning the relationship is what actually stops the regression.
+ let ascii = OsString::from("C:\\Temp");
+ assert_eq!(
+ Wtf16String::from_os_str(&ascii).len(),
+ ascii.len(),
+ "the two units must agree for ASCII, or this test proves nothing below"
+ );
+
+ // U+00E9 is one UTF-16 unit and two WTF-8 bytes; U+4E2D is one and three.
+ // A path Windows sees as shorter than `MAX_PATH` can therefore look longer
+ // when measured in bytes -- the direction that matters, since it would
+ // report a refusal as expected when it was not.
+ for accented in ["C:\\Tempé", "C:\\Temp中"] {
+ let path = OsString::from(accented);
+ let wide = Wtf16String::from_os_str(&path).len();
+
+ assert!(
+ wide < path.len(),
+ "{accented:?} must expose the divergence: {wide} wide vs {} bytes",
+ path.len()
+ );
+ assert_eq!(
+ wide,
+ accented.chars().count(),
+ "every character here is one UTF-16 unit, so the wide count is the character count"
+ );
+ }
+}
+
+#[test]
+fn an_over_long_tmp_is_refused_before_the_call_that_would_hang() {
+ // The limit is a flat number, so the test is just that it is applied where it
+ // says: at the limit run, one past it refuse.
+ let ok = OsString::from("c".repeat(super::MAX_TEMP_DIR));
+ assert!(
+ super::temp_dir_refusal(Some(&ok), None).is_none(),
+ "the limit itself must run"
+ );
+
+ let bad = OsString::from("c".repeat(super::MAX_TEMP_DIR + 1));
+ let refusal = super::temp_dir_refusal(Some(&bad), None).expect("one past the limit refuses");
+ assert!(
+ refusal.contains("%TMP%") && refusal.contains(&(super::MAX_TEMP_DIR + 1).to_string()),
+ "the refusal must name the variable and its length, or it cannot be acted on: {refusal}"
+ );
+}
+
+#[test]
+fn temp_is_only_consulted_when_tmp_is_unset() {
+ // `GetTempPath` reads `%TMP%` first, so a short `%TMP%` decides the run and a
+ // long `%TEMP%` beside it is never looked at. Refusing on it would turn a
+ // perfectly usable configuration into a false apparatus error.
+ let short = OsString::from("c".repeat(20));
+ let long = OsString::from("c".repeat(super::MAX_TEMP_DIR + 1));
+
+ assert!(
+ super::temp_dir_refusal(Some(&short), Some(&long)).is_none(),
+ "a usable %TMP% wins, whatever %TEMP% says"
+ );
+ assert!(
+ super::temp_dir_refusal(None, Some(&long)).is_some(),
+ "with %TMP% unset, %TEMP% is what GetTempPath uses and what can hang"
+ );
+ assert!(
+ super::temp_dir_refusal(None, None).is_none(),
+ "with neither set the fallbacks are short and the call returns"
+ );
+}
+
+#[test]
+fn the_refusal_length_is_counted_in_utf16_units() {
+ // Counting Rust's platform encoding instead would disagree the moment a
+ // non-ASCII character appeared, and disagreeing here means either a false
+ // refusal or a real hang.
+ //
+ // The fixture is chosen so every reading gives a different answer, and only
+ // one of them is right: 200 characters outside the BMP are 200 chars (which
+ // would be allowed, since the limit is 200), 400 UTF-16 units (refused, and
+ // correct), and 800 bytes of WTF-8.
+ let astral = OsString::from("\u{1F600}".repeat(super::MAX_TEMP_DIR));
+ assert_eq!(
+ Wtf16String::from_os_str(&astral).len(),
+ 400,
+ "the fixture must be over the limit in UTF-16 units specifically"
+ );
+
+ let refusal =
+ super::temp_dir_refusal(Some(&astral), None).expect("400 UTF-16 units is over the limit");
+ assert!(
+ refusal.contains("400"),
+ "the length reported must be the one Windows counts: {refusal}"
+ );
+}
+
+#[test]
+fn a_unc_temp_dir_is_refused_rather_than_spelled_wrong() {
+ // The apparatus composes `\\?\` by concatenation, and a UNC root needs
+ // `\\?\UNC\server\share` rather than `\\?\` glued onto `\\server`. Gluing
+ // produces a malformed path, so the run would fail in the apparatus and
+ // report nothing about the ceiling.
+ let unc = OsString::from(r"\\server\share\temp");
+ let refusal = super::temp_dir_refusal(Some(&unc), None).expect("a UNC root is refused");
+
+ assert!(
+ refusal.contains("UNC") && refusal.contains("%TMP%"),
+ "the refusal must name the shape and the variable: {refusal}"
+ );
+
+ // The control: an ordinary local path with the same prefix character must
+ // still run. `\` alone is not UNC, and refusing it would reject a rooted
+ // path on the current drive.
+ let rooted = OsString::from(r"\temp");
+ assert!(
+ super::temp_dir_refusal(Some(&rooted), None).is_none(),
+ "a single leading separator is a rooted local path, not a UNC root"
+ );
+}
+
+#[test]
+fn a_temp_dir_that_is_not_well_formed_utf16_is_refused() {
+ // `Path::display` substitutes U+FFFD for an unpaired surrogate, so composing
+ // the apparatus's verbatim paths as text would name a different file --
+ // silently, and in the apparatus rather than in the measurement.
+ //
+ // 0xD800 is a lone high surrogate: valid in a Windows path, not expressible
+ // as a Rust `str`.
+ let ill_formed = OsString::from_wide(&[0x0043, 0x003A, 0x005C, 0xD800]);
+ assert!(
+ ill_formed.to_str().is_none(),
+ "the fixture must actually be ill-formed, or the test proves nothing"
+ );
+
+ let refusal =
+ super::temp_dir_refusal(Some(&ill_formed), None).expect("an ill-formed path is refused");
+ assert!(
+ refusal.contains("well-formed") && refusal.contains("%TMP%"),
+ "the refusal must say what is wrong with it: {refusal}"
+ );
+}
+
+#[test]
+fn a_device_namespace_temp_dir_is_refused_as_itself_and_not_as_unc() {
+ // `\\?\` and `\\.\` open with the two backslashes a UNC root does, so a
+ // prefix test written for UNC claims them too. The refusal would then tell
+ // someone with a perfectly local temporary directory that it is a server
+ // share, which is the defect this whole probe is written against: a report
+ // asserting something the run never established.
+ for prefix in [r"\\?\", r"\\.\"] {
+ let value = OsString::from(format!(r"{prefix}C:\Temp"));
+ let refusal = super::temp_dir_refusal(Some(&value), None)
+ .unwrap_or_else(|| panic!("{prefix} is refused"));
+
+ assert!(
+ refusal.contains(prefix),
+ "the refusal must name the prefix it actually found: {refusal}"
+ );
+ assert!(
+ !refusal.contains("UNC"),
+ "a device-namespace path is not a UNC root and must not be called one: {refusal}"
+ );
+ }
+}
+
+#[test]
+fn an_ill_formed_temp_dir_is_classified_before_its_prefix_is_read() {
+ // Ordering, pinned: the prefix tests read the value as text, and reading an
+ // ill-formed value as text is the exact substitution the well-formedness
+ // refusal exists to prevent. Classifying first would report this value under
+ // whichever prefix its replacement characters happened to spell -- here,
+ // UNC -- and the explanation would be about the wrong thing.
+ let ill_formed = OsString::from_wide(&[0x005C, 0x005C, 0x0073, 0xD800]);
+ assert!(
+ ill_formed.to_str().is_none(),
+ "the fixture must actually be ill-formed, or the test proves nothing"
+ );
+
+ let refusal = super::temp_dir_refusal(Some(&ill_formed), None)
+ .expect("an ill-formed path is refused whatever it starts with");
+ assert!(
+ refusal.contains("well-formed"),
+ "the refusal must be about the encoding, not the prefix: {refusal}"
+ );
+ assert!(
+ !refusal.contains("UNC"),
+ "an ill-formed value must not be classified by text it cannot be read as: {refusal}"
+ );
+}
+
+/// What `reg query HKLM\...\FileSystem /v LongPathsEnabled` prints, verbatim
+/// apart from the value, which is the part under test.
+fn reg_output(value: &str) -> String {
+ format!(
+ "\r\n\
+ HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\FileSystem\r\n\
+ LongPathsEnabled REG_DWORD {value}\r\n\r\n"
+ )
+}
+
+#[test]
+fn the_registry_value_is_read_as_a_number_and_not_as_a_substring() {
+ // The reading this replaces tested the output for the substring `0x1`. The
+ // failure that costs something is the false negative: every nonzero value
+ // that does not happen to begin with a 1 read as disabled, so a machine set
+ // to `0x2` would be reported as not opted in and the run's conclusion would
+ // invert. `0x2` is therefore the case that matters here.
+ assert!(
+ super::enabled_in(®_output("0x2")),
+ "any nonzero value is the flag set, not just the ones spelled with a 1"
+ );
+ assert!(super::enabled_in(®_output("0x1")), "0x1 is enabled");
+ assert!(!super::enabled_in(®_output("0x0")), "0x0 is disabled");
+}
+
+#[test]
+fn a_registry_answer_that_is_not_the_queried_value_reads_as_disabled() {
+ // Absent, wrong type, and unparsable all mean the same thing here, and it is
+ // the same thing a missing key means: not opted in. The point of parsing the
+ // whole line is that a value belonging to something else can no longer be
+ // mistaken for this one.
+ for stdout in [
+ String::new(),
+ " SomethingElse REG_DWORD 0x1\r\n".to_string(),
+ " LongPathsEnabled REG_SZ 0x1\r\n".to_string(),
+ " LongPathsEnabled REG_DWORD enabled\r\n".to_string(),
+ " LongPathsEnabled REG_DWORD\r\n".to_string(),
+ ] {
+ assert!(
+ !super::enabled_in(&stdout),
+ "only the queried DWORD may report the machine as opted in: {stdout:?}"
+ );
+ }
+}
diff --git a/crates/windows-platform-probes/src/long_path_report.rs b/crates/windows-platform-probes/src/long_path_report.rs
new file mode 100644
index 00000000..614fec75
--- /dev/null
+++ b/crates/windows-platform-probes/src/long_path_report.rs
@@ -0,0 +1,419 @@
+// Copyright (c) Mike Grier.
+
+//! Renders one long-path run. Shared by the two binaries, which differ only in
+//! whether their manifest declares `longPathAware`.
+//!
+//! **An experiment, not a component.** These probes measure platform behaviour
+//! and are not for production use. See this crate's DESIGN-NOTES.md.
+
+use std::fmt::Write as _;
+
+use crate::long_path::{MAX_PATH_CONTENT, Observation, Shape, is_refusal};
+
+#[cfg(test)]
+mod tests;
+
+/// The probe's whole report, composed into `out`.
+///
+/// Takes the flag rather than a finished [`Observation`], and runs the
+/// measurement itself, so that this is the crate's ordinary renderer shape:
+/// `main` is one [`crate::report::emit_report`] call and everything the probe
+/// knows is composed *inside* it.
+///
+/// The difference is not cosmetic. Passing an `&Observation` would mean the
+/// measurement ran while evaluating the argument -- before this function is
+/// entered, with `out` still empty -- so a panic anywhere in it would print
+/// nothing at all, not even the banner that every captured report is supposed to
+/// carry. Composing the banner and the header first makes the buffer worth
+/// emitting from the moment measurement begins.
+pub fn render(out: &mut String, manifest_aware: bool) {
+ preamble(out);
+ // Everything above is already in `out`, so the report survives whatever this
+ // does.
+ body(out, &crate::long_path::measure(manifest_aware));
+}
+
+/// Everything that can be written before the measurement runs.
+///
+/// Split out so [`body`] can be tested. The ordering is the point rather than an
+/// artefact: this must reach `out` before [`crate::long_path::measure`] is
+/// called, or a panic inside the measurement prints nothing at all.
+fn preamble(out: &mut String) {
+ // First line of the report, and part of the composed text rather than
+ // written out here: a captured report must carry the line naming the
+ // machine that produced it, and the taint marker with it.
+ let _ = writeln!(
+ out,
+ "{}",
+ windows_placement_probe::fingerprint::banner_line()
+ );
+ let _ = writeln!(
+ out,
+ "== does the long-path opt-in lift MAX_PATH for a relative path? ==\n"
+ );
+}
+
+/// Everything the report says once the measurement is in hand.
+///
+/// Takes a finished [`Observation`] so it is a pure function of data, which is
+/// the whole reason it exists apart from [`render`]: `render` cannot be called
+/// without building a directory tree and moving the process's current directory,
+/// so anything left inside it is untestable. What lives here is not formatting --
+/// it is the length-refusal gating, the ceiling the column compares against, the
+/// registry warning, and the apparatus-error early return, two of which are
+/// fixes for defects a review round had to find by reading output.
+fn body(out: &mut String, observation: &Observation) {
+ let _ = writeln!(
+ out,
+ "manifest longPathAware : {}",
+ if observation.manifest_aware {
+ "yes"
+ } else {
+ "no"
+ }
+ );
+ let _ = writeln!(
+ out,
+ "LongPathsEnabled : {}",
+ match observation.registry_enabled {
+ Some(true) => "1",
+ Some(false) => "unset or 0",
+ None => "not consulted (the run was refused first)",
+ }
+ );
+
+ // Only when it was read and was off. `None` means the query never happened,
+ // and warning that "the machine half of the opt-in is absent" on that basis
+ // is a claim about the machine drawn from a measurement that was not taken --
+ // observed reporting `unset or 0` on a host whose value is 1.
+ if observation.registry_enabled == Some(false) {
+ let _ = writeln!(
+ out,
+ "\n*** The machine half of the opt-in is absent, so this run measures the\n\
+ *** un-opted-in case whatever the manifest says. The rows below are still\n\
+ *** real, but they answer a different question than the one intended."
+ );
+ }
+
+ if let Some(error) = &observation.apparatus_error {
+ let _ = writeln!(
+ out,
+ "\n*** APPARATUS FAILED: {error}\n\
+ *** Nothing below says anything about the machine."
+ );
+ return;
+ }
+
+ // The ceiling is printed rather than named. `> MAX` beside a module that
+ // defines `MAX_PATH` as 260 reads as "over 260", and the number that actually
+ // governs is 259 -- the terminator takes the last unit. Those differ at
+ // exactly one length, which is exactly the length this probe exists to be
+ // right about.
+ let _ = writeln!(
+ out,
+ "\n{:<18} {:>8} {:>8} {:<10} error",
+ "shape",
+ "resolved",
+ format!("> {MAX_PATH_CONTENT}"),
+ "result"
+ );
+ for attempt in &observation.attempts {
+ let _ = writeln!(
+ out,
+ "{:<18} {:>8} {:>8} {:<10} {}",
+ attempt.shape.label(),
+ attempt.resolved_len,
+ if attempt.over_max_path { "yes" } else { "no" },
+ if attempt.opened { "opened" } else { "REFUSED" },
+ if attempt.opened {
+ String::new()
+ } else {
+ // `is_refusal` distinguishes a length rejection from a genuine
+ // absence, and says nothing about length itself -- so it is only
+ // the length refusal when the row is *also* over the ceiling.
+ // Below it, the same not-found means the apparatus is wrong, and
+ // labelling that "the length refusal" would blame the ceiling for
+ // a file that was simply missing.
+ //
+ // Over the ceiling it is only *unambiguously* the length refusal
+ // for a shape that survives verbatim parsing. For the other two it
+ // is exactly the ambiguity this probe exists to resolve: if the
+ // system prepends `\\?\` past the ceiling, `..` and `/` stop being
+ // resolved, and the path as written then names something that was
+ // never created -- a genuine absence, and the sharp edge itself.
+ // Calling that "the length refusal" would file the finding as its
+ // own control.
+ format!(
+ "{}{}",
+ attempt.error,
+ match (
+ attempt.over_max_path,
+ is_refusal(attempt),
+ attempt.shape.survives_verbatim_parsing()
+ ) {
+ (true, true, true) =>
+ " (not-found; this shape survives `\\\\?\\` parsing and the target \
+ exists, so this is the length refusal)",
+ (true, true, false) =>
+ " (not-found; either the length refusal or this shape ceasing to \
+ resolve past the ceiling -- the verdict below decides which)",
+ (false, true, _) =>
+ " (not-found BELOW the ceiling; the apparatus is wrong, not the path length)",
+ _ => "",
+ }
+ )
+ }
+ );
+ }
+
+ let _ = writeln!(out, "\n{}", verdict(observation));
+}
+
+/// What the rows mean, stated rather than left for the reader to infer.
+fn verdict(observation: &Observation) -> String {
+ let long: Vec<_> = observation
+ .attempts
+ .iter()
+ .filter(|attempt| attempt.over_max_path)
+ .collect();
+ if long.is_empty() {
+ return "-- no attempt exceeded MAX_PATH, so this run tested nothing.".to_string();
+ }
+
+ // The control, and it is not optional. Every verdict below is a claim about a
+ // *change* at the ceiling -- "stopped resolving past it while working below
+ // it" -- and a change needs both sides. Reading only the long attempts would
+ // let a shape that never worked at any length be reported as the sharp edge,
+ // which is this probe's headline finding and the opposite of what happened.
+ //
+ // Two different situations, and they must not share a sentence.
+ //
+ // A shape with **no** below-ceiling attempt was never tried there. The shallow
+ // depth is meant to land under the ceiling for every shape, and the shapes do
+ // not cross together -- `..` carries an extra `\b\..`, so it reaches the
+ // ceiling five units sooner than plain. A current directory long enough to
+ // separate them puts `..` over while plain is still under.
+ //
+ // A shape that *was* tried below the ceiling and did not open is the opposite:
+ // the apparatus is broken.
+ //
+ // Collapsing the two announced "an apparatus or platform problem" on runs
+ // where every attempt opened and nothing failed at all. Measured with a
+ // 215-character `%TEMP%`, where all six attempts opened and the report still
+ // named a problem -- the report asserting a cause it did not establish, which
+ // is the one thing this verdict exists to avoid.
+ //
+ // That measurement predates the limit on `%TMP%`/`%TEMP%` length, which now
+ // refuses a temporary directory long enough to reach either state. Both are
+ // kept because a change to `shallow` or `SEGMENT` would put them back, and
+ // nothing else would notice.
+ //
+ // Paired on `over_max_path` rather than on depth, since it is the ceiling
+ // rather than the depth that decides whether an attempt is a control.
+ // Whether anything *failed* is a separate question, answered below: with some
+ // attempts under the ceiling the run can say the apparatus is sound, and with
+ // none it cannot say anything about the apparatus at all.
+ let tried_below = |shape: Shape| {
+ observation
+ .attempts
+ .iter()
+ .any(|attempt| attempt.shape == shape && !attempt.over_max_path)
+ };
+ let opened_below = |shape: Shape| {
+ observation
+ .attempts
+ .iter()
+ .any(|attempt| attempt.shape == shape && !attempt.over_max_path && attempt.opened)
+ };
+ let shapes = [Shape::Plain, Shape::DotDot, Shape::ForwardSlash];
+
+ let broken: Vec<&str> = shapes
+ .into_iter()
+ .filter(|shape| tried_below(*shape) && !opened_below(*shape))
+ .map(Shape::label)
+ .collect();
+ if !broken.is_empty() {
+ return format!(
+ "-- NO VERDICT. These shapes were tried below the ceiling and did not open:\n \
+ {}. The apparatus is wrong, so nothing here is a finding about\n \
+ the ceiling.",
+ broken.join(", ")
+ );
+ }
+
+ let untried: Vec<&str> = shapes
+ .into_iter()
+ .filter(|shape| !tried_below(*shape))
+ .map(Shape::label)
+ .collect();
+ if !untried.is_empty() {
+ // Whether *anything* landed below the ceiling, which decides whether this
+ // report can say a word about the apparatus.
+ //
+ // "Every attempt that did land below the ceiling opened" is true when some
+ // did, and vacuously true when none did -- and a vacuous truth offered as
+ // affirmative evidence is the same defect this whole cascade exists to
+ // prevent. Measured with a 226-character `%TEMP%`: all six attempts were
+ // over the ceiling and every one was REFUSED, and the report told the
+ // reader that everything below the ceiling had opened. The rows directly
+ // above it said otherwise. That length is now refused outright, so this
+ // guard defends against a change to `shallow` or `SEGMENT` rather than
+ // against a host.
+ let anything_below = observation
+ .attempts
+ .iter()
+ .any(|attempt| !attempt.over_max_path);
+
+ if !anything_below {
+ return format!(
+ "-- NO VERDICT. No attempt landed below {MAX_PATH_CONTENT} on this run, so there is no\n \
+ baseline to read the long case against for any shape: {}.\n \
+ This says nothing about the apparatus either way -- it was never\n \
+ exercised under the ceiling. The shallow depth is meant to land\n \
+ there, so either it or the segment length has been changed.",
+ untried.join(", ")
+ );
+ }
+
+ return format!(
+ "-- NO VERDICT. These shapes were never tried *below* {MAX_PATH_CONTENT} on this run,\n \
+ so there is no baseline to read the long case against: {}.\n \
+ Every attempt that did land below the ceiling opened, so this is not an\n \
+ apparatus fault: the shallow depth is meant to land under the ceiling\n \
+ for every shape, and for these it did not.",
+ untried.join(", ")
+ );
+ }
+
+ // The reference shape must have been tried *above* the ceiling too, or there
+ // is nothing to conclude about lifting. Reading "no long plain attempt
+ // opened" as "plain was refused" conflates a refusal with an attempt that was
+ // never made.
+ //
+ // Measured, not hypothesised -- but reached by changing a constant, because
+ // no host configuration can produce it. This state needs the *deep* plain
+ // attempt to be under the ceiling, and the deep relative path is a fixed 370
+ // units (40 levels of an 8-unit `SEGMENT`, separators, `target.txt`), so it
+ // resolves to at least ~392 whatever the temporary directory costs.
+ //
+ // Forced by setting the deep level to 21 on the development host, the run
+ // produced plain at 258 opened and `..` at 263 refused -- and the verdict
+ // announced "even the plain shape was refused past the ceiling", which the
+ // run had not observed and which the table directly contradicted.
+ //
+ // The five-unit straddle that `..` creates belongs to the guard above, not
+ // this one; repeating it here would point the next reader at a host setting
+ // that cannot reach this branch.
+ let plain_long: Vec<_> = long
+ .iter()
+ .filter(|attempt| attempt.shape == Shape::Plain)
+ .collect();
+ if plain_long.is_empty() {
+ return "-- NO VERDICT. No plain-shape attempt exceeded MAX_PATH, so the reference\n \
+ shape was never tested against the ceiling on this run and there is\n \
+ nothing to say about lifting."
+ .to_string();
+ }
+
+ // *Why* a long attempt failed, which neither guard above constrains. Both
+ // verdicts below are claims about the ceiling, and "did not open" is not
+ // evidence about the ceiling: a sharing violation, an access denial, or an
+ // antivirus hold on the target fails the open without the length being
+ // involved at all. `apparatus_error` does not catch it either, because a
+ // failed attempt is a result rather than a broken apparatus.
+ //
+ // The crate already owns the discriminator -- `is_refusal` exists to separate
+ // a length rejection from a genuine absence -- and it was being spent on a
+ // parenthetical in the table while the verdict that carries the finding
+ // ignored it.
+ let unexplained: Vec = long
+ .iter()
+ .filter(|attempt| !attempt.opened && !is_refusal(attempt))
+ .map(|attempt| format!("{} (error {})", attempt.shape.label(), attempt.error))
+ .collect();
+ if !unexplained.is_empty() {
+ return format!(
+ "-- NO VERDICT. These attempts past the ceiling failed for something that is\n \
+ not a length refusal, so nothing here bears on MAX_PATH: {}.",
+ unexplained.join(", ")
+ );
+ }
+
+ // Sound only because of the three checks above: every shape opened below the
+ // ceiling, plain was actually tried above it, and every long failure was a
+ // length refusal rather than some other error.
+ let plain_long_opened = plain_long.iter().any(|attempt| attempt.opened);
+ let reparsing: Vec<&str> = long
+ .iter()
+ .filter(|attempt| !attempt.shape.survives_verbatim_parsing() && !attempt.opened)
+ .map(|attempt| attempt.shape.label())
+ .collect();
+
+ // Whether the opt-in was actually in effect, which is the one input every
+ // guard above ignores and the only one that decides what a refusal *means*.
+ //
+ // Both halves have to hold. With neither, or with only one, a refusal past
+ // the ceiling is the documented outcome rather than a counter-example to it:
+ // `MAX_PATH` applying to a process that has not opted in is what `MAX_PATH`
+ // is. Reading it as "the documented reading is wrong" was this report's
+ // flagship line in `probe-long-path-unaware` on every correctly configured
+ // host -- announcing that Microsoft's documentation is refuted by the run
+ // that most exactly confirms it, and doing so from the half of the pair whose
+ // entire job is to be the baseline the other half is read against.
+ // `None` for the registry cannot reach here -- a refused run returns from
+ // `body` at the apparatus-error block above -- but it is spelled out rather
+ // than lumped in with `Some(false)`, because "not consulted" is not "off" and
+ // conflating them is the defect this third state was introduced to remove.
+ let missing = match (observation.manifest_aware, observation.registry_enabled) {
+ (false, Some(false)) => Some("neither half of the opt-in was in effect"),
+ (false, Some(true)) => Some("this binary carries no `longPathAware` manifest"),
+ (true, Some(false)) => Some("the machine's `LongPathsEnabled` is unset or 0"),
+ (_, None) => Some("the machine's `LongPathsEnabled` was never read"),
+ (true, Some(true)) => None,
+ };
+
+ // Both directions have to consult it, not just the refusal. "Lifted" credited
+ // to an opt-in that was not in effect would be the same error facing the other
+ // way, and a ceiling that lifts without opting in is a far stranger result
+ // than either verdict below describes.
+ if let Some(missing) = missing {
+ if plain_long_opened {
+ return format!(
+ "-- UNEXPECTED, and not a verdict on the opt-in. A relative path resolved\n \
+ past the ceiling while the opt-in was NOT in effect:\n \
+ {missing}.\n \
+ Nothing here credits the opt-in with that, because it was not on.\n \
+ Establish why this host lifts the ceiling unopted before reading any\n \
+ comparison against it."
+ );
+ }
+ return format!(
+ "-- BASELINE, not a finding. `MAX_PATH` applied to a relative path past the\n \
+ ceiling, which is what it does when the opt-in is not in effect:\n \
+ {missing}.\n \
+ This is the case the opted-in half is read against; it neither supports\n \
+ nor contradicts the documented reading."
+ );
+ }
+
+ if !plain_long_opened {
+ return "-- MAX_PATH was NOT lifted for a relative path: the opt-in was in effect --\n \
+ manifest and registry both -- and even the plain shape was refused past\n \
+ the ceiling. The documented reading is wrong for this configuration."
+ .to_string();
+ }
+ if reparsing.is_empty() {
+ "-- MAX_PATH was lifted for a relative path, and the path was still parsed\n\
+ normally: `..` and forward slashes resolved past the ceiling exactly as\n\
+ they do below it. No evidence of a prefix-then-parse implementation."
+ .to_string()
+ } else {
+ format!(
+ "-- SHARP EDGE. Length was lifted, but these shapes stopped resolving past\n\
+ the ceiling while working below it: {}.\n\
+ That is the signature of regularize-then-prefix: `\\\\?\\` disables exactly\n\
+ these features, so a relative path changes meaning at MAX_PATH.",
+ reparsing.join(", ")
+ )
+ }
+}
diff --git a/crates/windows-platform-probes/src/long_path_report/tests.rs b/crates/windows-platform-probes/src/long_path_report/tests.rs
new file mode 100644
index 00000000..e3e45f73
--- /dev/null
+++ b/crates/windows-platform-probes/src/long_path_report/tests.rs
@@ -0,0 +1,656 @@
+// Copyright (c) Mike Grier.
+
+//! Tests for the verdict.
+//!
+//! Every defect these pin was real, and every one reached a review round rather
+//! than a build: the verdict turns data into English, and English can be false in
+//! ways neither the compiler nor a passing test suite could see. Round after
+//! round found them by reading output -- stated without a count, because the
+//! count moved every time and a stale one here was itself a finding. This file
+//! is the attempt to stop finding them that way.
+//!
+//! So each test is named for the wrong sentence it forbids, and asserts **both**
+//! halves: that the right claim is made, and that the wrong one is not. Asserting
+//! only the former would let a verdict pass by saying everything at once.
+//!
+//! `verdict` is a pure function of an [`Observation`], which is why this costs
+//! nothing to test -- no filesystem, no Win32, no privileges, no current
+//! directory to restore. That the probe's own apparatus is expensive to exercise
+//! is not a reason for its conclusions to be untested.
+
+use windows_sys::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND};
+
+use super::verdict;
+use crate::long_path::{Attempt, Observation, Shape};
+
+const SHAPES: [Shape; 3] = [Shape::Plain, Shape::DotDot, Shape::ForwardSlash];
+
+/// An attempt that landed under the ceiling.
+fn below(shape: Shape, opened: bool) -> Attempt {
+ Attempt {
+ shape,
+ resolved_len: 78,
+ over_max_path: false,
+ opened,
+ error: if opened { 0 } else { ERROR_PATH_NOT_FOUND },
+ }
+}
+
+/// An attempt that landed over the ceiling.
+fn above(shape: Shape, opened: bool, error: u32) -> Attempt {
+ Attempt {
+ shape,
+ resolved_len: 429,
+ over_max_path: true,
+ opened,
+ error: if opened { 0 } else { error },
+ }
+}
+
+/// The three below-ceiling attempts of a healthy run: every shape opened.
+fn healthy_below() -> Vec {
+ SHAPES.into_iter().map(|shape| below(shape, true)).collect()
+}
+
+/// The three above-ceiling attempts, all with the same outcome.
+fn all_above(opened: bool, error: u32) -> Vec {
+ SHAPES
+ .into_iter()
+ .map(|shape| above(shape, opened, error))
+ .collect()
+}
+
+fn observation(
+ manifest_aware: bool,
+ registry_enabled: bool,
+ attempts: Vec,
+) -> Observation {
+ Observation {
+ manifest_aware,
+ registry_enabled: Some(registry_enabled),
+ attempts,
+ apparatus_error: None,
+ }
+}
+
+/// A run in which everything is in place and the ceiling genuinely lifted.
+fn opted_in_and_lifted() -> Observation {
+ let mut attempts = healthy_below();
+ attempts.extend(all_above(true, 0));
+ observation(true, true, attempts)
+}
+
+#[test]
+fn a_refusal_without_the_opt_in_is_not_evidence_against_the_documentation() {
+ // The flagship line of `probe-long-path-unaware` on every correctly
+ // configured host, and it said "The documented reading is wrong for this
+ // configuration". The documented reading is about the *opt-in*; a binary with
+ // no manifest never opted in, so `MAX_PATH` applying to it is what `MAX_PATH`
+ // means. The run that most exactly confirms the documentation was reporting
+ // that it refutes it.
+ let mut attempts = healthy_below();
+ attempts.extend(all_above(false, ERROR_PATH_NOT_FOUND));
+ let text = verdict(&observation(false, true, attempts));
+
+ assert!(
+ text.contains("BASELINE"),
+ "an un-opted-in refusal is the baseline the opted-in half is read against: {text}"
+ );
+ assert!(
+ !text.contains("documented reading is wrong"),
+ "nothing here bears on the documentation, which describes the opted-in case: {text}"
+ );
+ assert!(
+ text.contains("longPathAware"),
+ "the report must name which half was missing, or the reader cannot tell: {text}"
+ );
+}
+
+#[test]
+fn a_refusal_with_the_opt_in_in_effect_is_evidence_against_the_documentation() {
+ // The control for the test above. Without it, deleting the whole conclusion
+ // would leave that one green -- it asserts an absence, and an absence is
+ // satisfied by saying nothing at all.
+ let mut attempts = healthy_below();
+ attempts.extend(all_above(false, ERROR_PATH_NOT_FOUND));
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("documented reading is wrong"),
+ "with both halves in effect a refusal *is* the counter-example: {text}"
+ );
+ assert!(
+ !text.contains("BASELINE"),
+ "this run opted in, so it is not the baseline: {text}"
+ );
+}
+
+#[test]
+fn a_ceiling_that_lifts_without_the_opt_in_is_not_credited_to_the_opt_in() {
+ // The same error facing the other way. A host that lifts the ceiling for a
+ // binary that never opted in is a stranger result than either headline
+ // verdict describes, and reporting it as "MAX_PATH was lifted" would credit
+ // an opt-in that was switched off.
+ let text = verdict(&opted_in_and_lifted_but(false, true));
+
+ assert!(
+ text.contains("UNEXPECTED"),
+ "lifting without the opt-in is not the ordinary lifted verdict: {text}"
+ );
+ assert!(
+ !text.contains("No evidence of a prefix-then-parse"),
+ "that conclusion is about how the opt-in behaves, and it was not on: {text}"
+ );
+}
+
+/// [`opted_in_and_lifted`] with the two halves of the opt-in overridden.
+fn opted_in_and_lifted_but(manifest_aware: bool, registry_enabled: bool) -> Observation {
+ Observation {
+ manifest_aware,
+ registry_enabled: Some(registry_enabled),
+ ..opted_in_and_lifted()
+ }
+}
+
+#[test]
+fn a_shape_never_tried_below_the_ceiling_is_not_reported_as_a_fault() {
+ // Measured at a 215-character `%TEMP%`, before the 200-unit limit existed:
+ // every attempt opened, nothing failed, and the report announced "an
+ // apparatus or platform problem". The shapes do not cross the ceiling
+ // together -- `..` carries an extra `\b\..` -- so a deep enough current
+ // directory pushed it over while plain was still under.
+ //
+ // No longer reachable from `%TEMP%`, which is now capped well below that.
+ // Kept for the same reason as the case below: a change to `shallow` or
+ // `SEGMENT` would put it back, and nothing else would notice.
+ let attempts = vec![
+ below(Shape::Plain, true),
+ // No below-ceiling attempt for `..` at all: it is over even when short.
+ above(Shape::DotDot, true, 0),
+ below(Shape::ForwardSlash, true),
+ above(Shape::Plain, true, 0),
+ above(Shape::DotDot, true, 0),
+ above(Shape::ForwardSlash, true, 0),
+ ];
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("never tried"),
+ "the missing baseline is the fact, and it is not a failure: {text}"
+ );
+ // Only the live half of this is kept. The original also forbade "apparatus or
+ // platform problem", which was the pre-fix wording -- and since that string no
+ // longer exists anywhere the renderer can emit, the conjunct was trivially
+ // true and would have stayed green under any regression. An assertion that
+ // cannot fail is worse than none: it reads as coverage.
+ assert!(
+ !text.contains("apparatus is wrong"),
+ "nothing failed, so naming a fault sends the reader hunting for one: {text}"
+ );
+}
+
+#[test]
+fn a_shape_that_failed_below_the_ceiling_is_reported_as_a_fault() {
+ // The other half of the split. Here something really did fail where it should
+ // not have, and the report must say so rather than blaming the ceiling.
+ let attempts = vec![
+ below(Shape::Plain, true),
+ below(Shape::DotDot, false),
+ below(Shape::ForwardSlash, true),
+ above(Shape::Plain, false, ERROR_PATH_NOT_FOUND),
+ above(Shape::DotDot, false, ERROR_PATH_NOT_FOUND),
+ above(Shape::ForwardSlash, false, ERROR_PATH_NOT_FOUND),
+ ];
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("apparatus is wrong"),
+ "a shape failing below the ceiling is a broken apparatus: {text}"
+ );
+ assert!(
+ !text.contains("documented reading is wrong"),
+ "a broken apparatus cannot support a claim about the documentation: {text}"
+ );
+}
+
+#[test]
+fn a_long_failure_that_is_not_a_length_refusal_yields_no_verdict() {
+ // Both headline verdicts once branched on "did not open" alone, so a sharing
+ // violation or an access denial produced a claim about `MAX_PATH` from
+ // evidence bearing on neither. The crate already owned the discriminator --
+ // `is_refusal` -- and was spending it on a parenthetical in the table.
+ let mut attempts = healthy_below();
+ attempts.extend(all_above(false, ERROR_ACCESS_DENIED));
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("NO VERDICT") && text.contains("not a length refusal"),
+ "an access denial says nothing about the ceiling: {text}"
+ );
+ assert!(
+ !text.contains("documented reading is wrong"),
+ "that headline needs a length refusal, which this is not: {text}"
+ );
+ // In context, not as a bare number: `ERROR_ACCESS_DENIED.to_string()` is "5",
+ // and a substring search for "5" is satisfied by any verdict that happens to
+ // contain the digit -- including the ceiling, 259.
+ assert!(
+ text.contains(&format!("(error {ERROR_ACCESS_DENIED})")),
+ "the unexpected error must be named, or it cannot be chased: {text}"
+ );
+}
+
+#[test]
+fn a_reference_shape_never_tried_above_the_ceiling_yields_no_verdict() {
+ // "No long plain attempt opened" was read as "plain was refused", conflating a
+ // refusal with an attempt that was never made. Reproduced by forcing the deep
+ // level to 21, which is the only way to reach it: **no `%TEMP%` length can**,
+ // and saying otherwise would send the next reader looking for a host
+ // configuration that does not exist.
+ //
+ // The deep relative path is a fixed 370 units -- 40 levels of an 8-unit
+ // `SEGMENT` plus separators plus `target.txt` -- so the deep plain attempt is
+ // never below about 392 against a ceiling of 259. With the shipped `deep = 40`
+ // this state is unreachable, and the guard is defence against a future change
+ // to `deep` or `SEGMENT` rather than against anything a host can do. That is
+ // exactly why it is worth a test: nothing else would notice it rotting.
+ let attempts = vec![
+ below(Shape::Plain, true),
+ below(Shape::DotDot, true),
+ below(Shape::ForwardSlash, true),
+ // A shallower tree: plain still lands under the ceiling at depth, so it is
+ // never tested above it.
+ below(Shape::Plain, true),
+ above(Shape::DotDot, false, ERROR_PATH_NOT_FOUND),
+ below(Shape::ForwardSlash, true),
+ ];
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("NO VERDICT"),
+ "with no plain attempt above the ceiling there is nothing to conclude: {text}"
+ );
+ assert!(
+ !text.contains("even the plain shape was refused"),
+ "plain was never tried above the ceiling, so it was not refused there: {text}"
+ );
+}
+
+#[test]
+fn the_sharp_edge_names_only_shapes_that_worked_below_the_ceiling() {
+ // The probe's headline finding. It claims a shape "stopped resolving past the
+ // ceiling while working below it", and for a long time computed that from the
+ // long attempts alone -- so a shape that failed at *both* lengths would have
+ // been reported as the sharp edge, which is the opposite of what happened.
+ let attempts = vec![
+ below(Shape::Plain, true),
+ below(Shape::DotDot, true),
+ below(Shape::ForwardSlash, true),
+ above(Shape::Plain, true, 0),
+ above(Shape::DotDot, false, ERROR_PATH_NOT_FOUND),
+ above(Shape::ForwardSlash, true, 0),
+ ];
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("SHARP EDGE") && text.contains(Shape::DotDot.label()),
+ "`..` worked below and failed above, which is exactly the sharp edge: {text}"
+ );
+ assert!(
+ !text.contains(Shape::ForwardSlash.label()),
+ "forward slashes resolved past the ceiling, so naming them is false: {text}"
+ );
+}
+
+#[test]
+fn a_fully_opted_in_run_that_lifts_reports_no_prefix_then_parse() {
+ // The ordinary success, and the shape of the aware binary's real output. Its
+ // presence keeps the tests above honest: they forbid wrong sentences, and
+ // without this one the whole function could satisfy them by never concluding.
+ let text = verdict(&opted_in_and_lifted());
+
+ assert!(
+ text.contains("No evidence of a prefix-then-parse"),
+ "every shape resolved past the ceiling, which is the negative result: {text}"
+ );
+ assert!(
+ !text.contains("NO VERDICT") && !text.contains("SHARP EDGE"),
+ "nothing was withheld and nothing broke: {text}"
+ );
+}
+
+#[test]
+fn a_run_with_no_long_attempt_tested_nothing_and_says_so() {
+ // The degenerate case: no attempt reached the ceiling at all. Only a shallower
+ // tree can produce it -- a short `%TEMP%` cannot, because the deep attempts
+ // carry a fixed 370 units whatever the current directory costs. There is no
+ // finding here in either direction.
+ let text = verdict(&observation(true, true, healthy_below()));
+
+ assert!(
+ text.contains("tested nothing"),
+ "without an attempt past the ceiling the run establishes nothing: {text}"
+ );
+ assert!(
+ !text.contains("lifted") && !text.contains("SHARP EDGE"),
+ "no conclusion is available from attempts that never crossed: {text}"
+ );
+}
+
+#[test]
+fn an_absent_registry_half_is_named_even_when_the_manifest_is_present() {
+ // The state `probe-long-path-aware` reaches on a machine whose administrator
+ // never set `LongPathsEnabled` -- which includes a stock CI runner, so this is
+ // the aware binary's likely output wherever this repository builds. The
+ // manifest alone does not opt in, so a refusal here is still the baseline, and
+ // the report has to name the *registry* as the missing half or the reader will
+ // check the one thing that is present.
+ let mut attempts = healthy_below();
+ attempts.extend(all_above(false, ERROR_PATH_NOT_FOUND));
+ let text = verdict(&observation(true, false, attempts));
+
+ assert!(
+ text.contains("BASELINE"),
+ "one half is not an opt-in, so this is still the baseline: {text}"
+ );
+ assert!(
+ text.contains("LongPathsEnabled"),
+ "the missing half is the machine setting, and must be the one named: {text}"
+ );
+ assert!(
+ !text.contains("no `longPathAware` manifest"),
+ "this binary *has* the manifest; naming it sends the reader to the wrong half: {text}"
+ );
+}
+
+#[test]
+fn both_halves_absent_are_reported_as_neither_rather_than_as_one() {
+ // The commonest real-world state for the un-opted-in half: a machine without
+ // the registry value running a binary without the manifest. Naming only one
+ // would imply the other was in place.
+ let mut attempts = healthy_below();
+ attempts.extend(all_above(false, ERROR_PATH_NOT_FOUND));
+ let text = verdict(&observation(false, false, attempts));
+
+ assert!(
+ text.contains("BASELINE") && text.contains("neither half"),
+ "with both absent the report must say so, not pick one: {text}"
+ );
+ assert!(
+ !text.contains("LongPathsEnabled") && !text.contains("no `longPathAware` manifest"),
+ "naming a single half implies the other was present: {text}"
+ );
+}
+
+#[test]
+fn a_broken_apparatus_outranks_a_missing_baseline() {
+ // Both conditions at once: one shape failed below the ceiling (the apparatus
+ // is wrong) while another was never tried below it (no baseline). The order
+ // matters because only one message is emitted, and the apparatus fault is the
+ // one that invalidates the whole run -- reporting the missing baseline instead
+ // would describe a consequence and hide the cause.
+ let attempts = vec![
+ below(Shape::Plain, false),
+ // No below-ceiling attempt for `..` at all.
+ above(Shape::DotDot, true, 0),
+ below(Shape::ForwardSlash, true),
+ above(Shape::Plain, false, ERROR_PATH_NOT_FOUND),
+ above(Shape::DotDot, true, 0),
+ above(Shape::ForwardSlash, true, 0),
+ ];
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("apparatus is wrong") && text.contains(Shape::Plain.label()),
+ "the fault is the cause and must be what the reader is told: {text}"
+ );
+ assert!(
+ !text.contains("never tried"),
+ "the missing baseline is a consequence here, and would hide the fault: {text}"
+ );
+}
+
+#[test]
+fn a_run_with_nothing_below_the_ceiling_claims_nothing_about_the_apparatus() {
+ // The state every other test in this file misses, and the reason it lasted
+ // as long as it did: each of them seeds at least one below-ceiling attempt, so the
+ // sentence "every attempt that did land below the ceiling opened" always had
+ // something to be true *of*. With none, it is vacuously true and was still
+ // being offered as affirmative evidence that the apparatus was sound.
+ //
+ // Measured rather than imagined, at a 226-character `%TEMP%` -- but only
+ // before the 200-unit limit existed. Such a temporary directory put even the
+ // shallow attempts over the ceiling, and the unaware binary refused all six
+ // while the report announced that everything below the ceiling had opened.
+ //
+ // The limit now refuses that configuration up front, so no `%TEMP%` reaches
+ // this state; it takes a change to `shallow` or `SEGMENT`. Kept because that
+ // is exactly the change nothing else would catch.
+ let attempts = all_above(false, ERROR_PATH_NOT_FOUND);
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("No attempt landed below"),
+ "with nothing under the ceiling that is the fact to report: {text}"
+ );
+ assert!(
+ text.contains("says nothing about the apparatus"),
+ "an untested apparatus must be described as untested, not as sound: {text}"
+ );
+ assert!(
+ !text.contains("did land below the ceiling opened"),
+ "nothing landed below the ceiling, so that claim has no subject: {text}"
+ );
+ // `apparatus fault:` and not `not an apparatus fault`: the latter spans a line
+ // break in the emitted text (`not an\n apparatus fault:`), so asserting it
+ // would be an assertion that cannot fail -- which is the defect an earlier
+ // round caught in this same file, and which I reintroduced writing this pair.
+ assert!(
+ !text.contains("apparatus fault:"),
+ "the run never exercised the apparatus, so it cannot clear it: {text}"
+ );
+}
+
+#[test]
+fn a_run_with_something_below_the_ceiling_still_clears_the_apparatus() {
+ // The control. Without it, deleting the non-vacuous branch entirely would
+ // leave the test above green, since it asserts absences.
+ let attempts = vec![
+ below(Shape::Plain, true),
+ above(Shape::DotDot, false, ERROR_PATH_NOT_FOUND),
+ below(Shape::ForwardSlash, true),
+ above(Shape::Plain, false, ERROR_PATH_NOT_FOUND),
+ above(Shape::ForwardSlash, false, ERROR_PATH_NOT_FOUND),
+ ];
+ let text = verdict(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("did land below the ceiling opened") && text.contains("apparatus fault:"),
+ "plain and forward slashes opened below, so the apparatus is demonstrably sound: {text}"
+ );
+ assert!(
+ !text.contains("No attempt landed below"),
+ "two attempts did land below the ceiling: {text}"
+ );
+}
+
+// ---------------------------------------------------------------------------
+// `body` -- everything the report says once the measurement is in hand.
+//
+// Untestable until `render` was split, because `render` performs the
+// measurement itself and so cannot be called without building a directory tree
+// and moving the process's current directory. What follows is not formatting
+// coverage: some of it fixes defects a review round had to find by reading a
+// probe's output, which is the slowest possible detector.
+// ---------------------------------------------------------------------------
+
+/// The report body for an observation, as a string.
+fn body_of(observation: &Observation) -> String {
+ let mut out = String::new();
+ super::body(&mut out, observation);
+ out
+}
+
+#[test]
+fn the_ceiling_column_prints_the_number_it_actually_compares() {
+ // The header read `> MAX` beside a module defining `MAX_PATH` as 260, while
+ // the comparison was against 259. Those differ at exactly one length -- the
+ // length this probe exists to be exact about -- so a row could read `260 yes`
+ // under a heading meaning "over 260".
+ let text = body_of(&observation(true, true, healthy_below()));
+
+ assert!(
+ text.contains("> 259"),
+ "the column must name the ceiling it compares against: {text}"
+ );
+ assert!(
+ !text.contains("> MAX"),
+ "`> MAX` is read as 'over 260', which is the one wrong reading: {text}"
+ );
+}
+
+#[test]
+fn a_not_found_below_the_ceiling_is_not_called_a_length_refusal() {
+ // `is_refusal` separates a length rejection from a genuine absence and says
+ // nothing about length itself. Applied to every failed attempt, it annotated
+ // under-ceiling rows "so this is the length refusal" -- blaming the ceiling
+ // for a file that was simply missing.
+ let attempts = vec![below(Shape::Plain, false)];
+ let text = body_of(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("BELOW the ceiling") && text.contains("apparatus is wrong"),
+ "a not-found under the ceiling is an apparatus fault: {text}"
+ );
+ assert!(
+ !text.contains("this is the length refusal"),
+ "length is the one thing that annotation asserts and this row is not over: {text}"
+ );
+}
+
+#[test]
+fn a_not_found_above_the_ceiling_is_called_a_length_refusal() {
+ // The control for the test above: without it, deleting the annotation
+ // entirely would leave that one green, since it asserts an absence.
+ let attempts = vec![above(Shape::Plain, false, ERROR_PATH_NOT_FOUND)];
+ let text = body_of(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("this is the length refusal"),
+ "over the ceiling, a not-found on a file that exists is the length refusal: {text}"
+ );
+}
+
+#[test]
+fn an_absent_registry_half_is_flagged_before_any_row_is_read() {
+ // The machine half is a precondition for the whole run, so the warning has to
+ // come before the table rather than after it -- a reader who has already
+ // interpreted the rows has been misled by the time a footnote arrives.
+ let text = body_of(&observation(true, false, healthy_below()));
+
+ let warning = text
+ .find("machine half of the opt-in is absent")
+ .expect("the absent registry half must be reported");
+ let table = text
+ .find("shape")
+ .expect("the table header must be present");
+ assert!(
+ warning < table,
+ "the warning must precede the rows it qualifies: {text}"
+ );
+}
+
+#[test]
+fn an_apparatus_failure_stops_the_report_before_the_rows() {
+ // When the apparatus failed the attempts are meaningless, and printing them
+ // under a heading that invites comparison is worse than printing nothing:
+ // the numbers look like findings.
+ let attempts = vec![above(Shape::Plain, false, ERROR_PATH_NOT_FOUND)];
+ let observation = Observation {
+ apparatus_error: Some("could not build the tree".to_string()),
+ ..observation(true, true, attempts)
+ };
+ let text = body_of(&observation);
+
+ assert!(
+ text.contains("APPARATUS FAILED") && text.contains("could not build the tree"),
+ "the failure and its cause must both be reported: {text}"
+ );
+ assert!(
+ !text.contains("shape") && !text.contains("REFUSED"),
+ "rows gathered by a broken apparatus must not be shown at all: {text}"
+ );
+}
+
+#[test]
+fn a_refused_run_claims_nothing_about_the_registry() {
+ // A run refused for an over-long temporary directory never issues the
+ // registry query, because reading it spawns a process and that is one of the
+ // calls that hangs. Reporting the unread flag as `false` made the report say
+ // `LongPathsEnabled : unset or 0` and "the machine half of the opt-in is
+ // absent" on a host where the value is 1 -- a measured-sounding claim about a
+ // query that was never made, immediately above an apparatus error saying
+ // nothing below described the machine.
+ let observation = Observation {
+ manifest_aware: true,
+ registry_enabled: None,
+ attempts: Vec::new(),
+ apparatus_error: Some("%TMP% is too long".to_string()),
+ };
+ let text = body_of(&observation);
+
+ assert!(
+ text.contains("not consulted"),
+ "an unread setting must be reported as unread: {text}"
+ );
+ assert!(
+ !text.contains("unset or 0"),
+ "that is a claim about the machine, and no query was issued: {text}"
+ );
+ assert!(
+ !text.contains("machine half of the opt-in is absent"),
+ "warning that it is absent asserts the same unmade measurement: {text}"
+ );
+ assert!(
+ text.contains("APPARATUS FAILED"),
+ "the reason the run established nothing must still be reported: {text}"
+ );
+}
+
+#[test]
+fn a_registry_half_that_was_read_and_is_off_is_still_flagged() {
+ // The control. `Some(false)` is a real measurement and must keep warning --
+ // without this, suppressing the warning entirely would satisfy the test above.
+ let text = body_of(&observation(true, false, healthy_below()));
+
+ assert!(
+ text.contains("machine half of the opt-in is absent"),
+ "a registry half that was read and is off is a real finding: {text}"
+ );
+ assert!(
+ text.contains("unset or 0") && !text.contains("not consulted"),
+ "it was consulted, and the report should say what it found: {text}"
+ );
+}
+
+#[test]
+fn a_shape_that_cannot_survive_verbatim_parsing_is_not_called_a_length_refusal() {
+ // The annotation for a not-found past the ceiling used to say "the target
+ // provably exists, so this is the length refusal" for every shape. For `..`
+ // and forward slashes that is the ambiguity the probe exists to resolve
+ // rather than a fact about the run: if the system prepends `\\?\` past the
+ // ceiling, those shapes stop being resolved and the path as written names
+ // something never created -- a genuine absence, and the sharp edge itself.
+ // Calling it the length refusal files the finding as its own control.
+ let attempts = vec![above(Shape::DotDot, false, ERROR_PATH_NOT_FOUND)];
+ let text = body_of(&observation(true, true, attempts));
+
+ assert!(
+ text.contains("either the length refusal or this shape ceasing to"),
+ "for a shape `\\\\?\\` would break, the cause is genuinely undecided here: {text}"
+ );
+ assert!(
+ !text.contains("so this is the length refusal"),
+ "asserting the length refusal would rule out the sharp edge by fiat: {text}"
+ );
+}
diff --git a/tools/check-encoding.ps1 b/tools/check-encoding.ps1
index 56c635b2..45a68a72 100644
--- a/tools/check-encoding.ps1
+++ b/tools/check-encoding.ps1
@@ -48,7 +48,11 @@ $Patterns = @(
$TextExtensions = @(
'.rs', '.toml', '.md', '.txt', '.json', '.yaml', '.yml',
'.ps1', '.psm1', '.psd1', '.sh', '.cfg', '.ini', '.ts',
- '.lock', '.gitignore', '.gitattributes', '.vscodeignore'
+ '.lock', '.gitignore', '.gitattributes', '.vscodeignore',
+ # Win32 side-by-side manifests. XML that declares its own `encoding="UTF-8"`
+ # and is read by the linker, so a BOM or a mojibake edit would be consumed
+ # rather than merely displayed.
+ '.manifest'
)
function Test-IsTextFile([string]$file) {