diff --git a/.agents/docs/testing-strategy.md b/.agents/docs/testing-strategy.md index 4054257..11c4925 100644 --- a/.agents/docs/testing-strategy.md +++ b/.agents/docs/testing-strategy.md @@ -41,6 +41,7 @@ The matrix is interaction-aware. A platform root kind must be crossed with encod - On macOS and Linux, all 224,676 pairs from the bounded short absolute spelling set plus the multibyte set are checked through the production `relative_str` dispatch and the suffix-validation helper against the slow component oracle. - Absolute paths with native-invalid normal components prove that equal raw encoding cancels and distinct encoding with the same lossy rendering does not, through `relative`, `try_relative`, and `relative_with` on Unix and Windows. Unix explicit-cwd rows also prove that relative resolution preserves native-invalid cwd components for borrowed and owned context arguments. - 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. +- A Windows drive-relative path containing an embedded NUL deterministically fails in `std::path::absolute` before WinAPI dispatch. Native tests require `try_absolutize` and both target/base roles of `try_relative` to preserve the exact error kind and raw OS error, while their strict counterparts panic; CI requires this contract to register in both default and cached-cwd configurations. - Unavailable Unix cwd coverage pins exact successful output and `Cow` variants for cwd-independent fallible calls, compares dependent fallible calls with the direct `current_dir` error, preserves strict panic checks for `Path`, `str`, and `String` calls, and proves that a valid explicit cwd still succeeds. - A Unix child process successfully reads cwd before removing that directory, then requires default mode to expose the subsequent ambient error while `cached_current_dir` continues producing exact owned results from the successful process-lifetime snapshot. - 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. @@ -76,4 +77,5 @@ Any change to a public contract, platform branch, native-encoding comparison, cl - [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) +- [Windows drive-relative error propagation](../../tests/windows_drive_relative_errors.rs) - [Production dispatch and short-path oracle](../../src/impl_sugar_path.rs) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5b09716..5469839 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -89,6 +89,7 @@ jobs: utf8_string_receivers_borrow_only_from_the_receiver windows_path_equality_does_not_define_normalized_spelling windows_prefix_like_normal_components_remain_exactly_idempotent + windows_drive_relative_resolution_propagates_native_errors current_directory_is_cached_only_when_requested pure_relative_calls_initialize_and_observe_cwd_policy requested_cached_current_dir_configuration_is_active diff --git a/tests/windows_drive_relative_errors.rs b/tests/windows_drive_relative_errors.rs new file mode 100644 index 0000000..3d2fbb2 --- /dev/null +++ b/tests/windows_drive_relative_errors.rs @@ -0,0 +1,57 @@ +#![cfg(target_family = "windows")] + +use std::{io, path::Path}; + +use sugar_path::SugarPath; + +fn assert_same_error(actual: &io::Error, expected: &io::Error, context: &str) { + assert_eq!(actual.kind(), expected.kind(), "{context} error kind"); + assert_eq!(actual.raw_os_error(), expected.raw_os_error(), "{context} raw OS error"); +} + +#[test] +fn windows_drive_relative_resolution_propagates_native_errors() { + let invalid = "C:bad\0path"; + let expected = std::path::absolute(invalid).expect_err("an embedded NUL is invalid for WinAPI"); + assert_eq!(expected.kind(), io::ErrorKind::InvalidInput); + + assert_same_error( + &Path::new(invalid).try_absolutize().expect_err("drive-relative Path should fail"), + &expected, + "Path try_absolutize", + ); + assert_same_error( + &invalid.try_absolutize().expect_err("drive-relative str should fail"), + &expected, + "str try_absolutize", + ); + let owned = invalid.to_owned(); + assert_same_error( + &owned.try_absolutize().expect_err("drive-relative String should fail"), + &expected, + "String try_absolutize", + ); + assert!(std::panic::catch_unwind(|| Path::new(invalid).absolutize()).is_err()); + + let absolute_base = Path::new(r"C:\base"); + assert_same_error( + &Path::new(invalid).try_relative(absolute_base).expect_err("drive-relative target should fail"), + &expected, + "try_relative target", + ); + assert!( + std::panic::catch_unwind(|| Path::new(invalid).relative(absolute_base)).is_err(), + "strict relative should panic for an invalid drive-relative target", + ); + + let absolute_target = Path::new(r"C:\target"); + assert_same_error( + &absolute_target.try_relative(invalid).expect_err("drive-relative base should fail"), + &expected, + "try_relative base", + ); + assert!( + std::panic::catch_unwind(|| absolute_target.relative(invalid)).is_err(), + "strict relative should panic for an invalid drive-relative base", + ); +}