Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .agents/docs/testing-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ The matrix is interaction-aware. A platform root kind must be crossed with encod
- Windows drive-relative rows apply the same exact comparison before shared-context cancellation and preserve invalid-wide components while resolving against explicit borrowed and owned cwd arguments.
- Unavailable Unix cwd coverage pins exact successful output and `Cow` variants for cwd-independent fallible calls, preserves the ambient error and panic checks for dependent `Path`, `str`, and `String` calls, and proves that a valid explicit cwd still succeeds.
- Known-UTF-8 `str` and `String` receivers prove exact clean-trailing and dirty normalization contracts, receiver-only borrowing, explicit and ambient relative context forwarding, and cached-cwd behavior on Unix and Windows. Unix deleted-cwd rows also prove cwd-independent success and strict cwd-dependent panics for string receivers.
- A Linux child process starts in a real cwd containing an invalid native byte and requires `absolutize`, `try_absolutize`, `relative`, and `try_relative` to preserve that byte exactly under both cwd policies. Linux supplies this executable filesystem case because macOS rejects the invalid-byte directory name; explicit-cwd tests continue to cover native-invalid Unix composition independently of the host filesystem.

## Follow-up coverage status

Expand Down Expand Up @@ -71,5 +72,6 @@ Any change to a public contract, platform branch, native-encoding comparison, cl
- [Relative ownership and lifetime contracts](../../tests/relative_borrowing.rs)
- [Unavailable-cwd relative behavior](../../tests/relative_without_cwd.rs)
- [Known-UTF-8 string receiver ownership](../../tests/string_receiver_contracts.rs)
- [Native-invalid ambient cwd preservation](../../tests/non_utf8_ambient_cwd.rs)
- [Native-invalid exact comparison](../../tests/invalid_encoding.rs)
- [Production dispatch and short-path oracle](../../src/impl_sugar_path.rs)
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ jobs:
current_directory_is_cached_only_when_requested
pure_relative_calls_initialize_and_observe_cwd_policy
failed_cwd_lookup_does_not_poison_later_relative_calls
non_utf8_ambient_cwd_is_preserved
requested_cached_current_dir_configuration_is_active
- os: windows-latest
expected_host: x86_64-pc-windows-msvc
Expand Down
90 changes: 90 additions & 0 deletions tests/non_utf8_ambient_cwd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#![cfg(target_os = "linux")]

use std::{
env,
ffi::OsString,
fs,
os::unix::ffi::{OsStrExt, OsStringExt},
path::{Path, PathBuf},
process::Command,
time::{SystemTime, UNIX_EPOCH},
};

use sugar_path::SugarPath;

const CHILD_ENV: &str = "SUGAR_PATH_NON_UTF8_AMBIENT_CWD_CHILD";
const NON_UTF8_COMPONENT: &[u8] = b"non_utf8-\x80";

fn assert_exact_path(actual: &Path, expected: &Path, context: &str) {
assert_eq!(
actual.as_os_str().as_bytes(),
expected.as_os_str().as_bytes(),
"{context} must preserve native cwd bytes",
);
}

#[test]
fn non_utf8_ambient_cwd_is_preserved() {
if env::var_os(CHILD_ENV).is_some() {
let non_utf8_component = OsString::from_vec(NON_UTF8_COMPONENT.to_vec());
let cwd = env::current_dir().expect("read the child's current directory");
assert!(
cwd
.as_os_str()
.as_bytes()
.windows(NON_UTF8_COMPONENT.len())
.any(|window| window == NON_UTF8_COMPONENT),
"test setup must enter the non-UTF-8 cwd",
);
let root = cwd
.parent()
.and_then(Path::parent)
.expect("the child cwd has the temporary root as a grandparent");

let input = Path::new("pkg/entry.js");
let expected_absolute = cwd.join(input);
assert_exact_path(input.absolutize().as_ref(), &expected_absolute, "absolutize");
assert_exact_path(
input.try_absolutize().expect("resolve against the native cwd").as_ref(),
&expected_absolute,
"try_absolutize",
);

let target = Path::new("target.js");
let base = root.join("anchor");
let expected_relative = PathBuf::from("..").join(&non_utf8_component).join("leaf").join(target);
assert_exact_path(target.relative(&base).as_ref(), &expected_relative, "relative");
assert_exact_path(
target.try_relative(&base).expect("resolve relative paths against the native cwd").as_ref(),
&expected_relative,
"try_relative",
);
return;
}

let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock is after the Unix epoch")
.as_nanos();
let root =
env::temp_dir().join(format!("sugar-path-non-utf8-cwd-{}-{unique}", std::process::id()));
let cwd = root.join(OsString::from_vec(NON_UTF8_COMPONENT.to_vec())).join("leaf");
fs::create_dir_all(&cwd).expect("create the non-UTF-8 child cwd");

let output = Command::new(env::current_exe().expect("find the integration-test executable"))
.args(["--exact", "non_utf8_ambient_cwd_is_preserved", "--nocapture"])
.env(CHILD_ENV, "1")
.current_dir(&cwd)
.output()
.expect("run the integration test in a child process");

if root.exists() {
fs::remove_dir_all(&root).expect("clean up the non-UTF-8 child cwd");
}
assert!(
output.status.success(),
"child test failed\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}