Skip to content
Merged
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
47 changes: 36 additions & 11 deletions crates/ogar-adapter-csharp/tests/parity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,30 @@ fn unique_tmp_dir(tag: &str) -> PathBuf {
dir
}

fn dotnet() -> Command {
fn dotnet(home: &Path) -> Command {
let mut cmd = Command::new("dotnet");
cmd.env("DOTNET_CLI_TELEMETRY_OPTOUT", "1")
.env("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1")
.env("DOTNET_NOLOGO", "1");
.env("DOTNET_NOLOGO", "1")
// Isolate the dotnet temp/home tree per invocation. The flaky
// failure is the .NET runtime's cross-process NAMED-MUTEX shared
// memory, which NuGet's first-run MigrationRunner opens. On Linux
// that shm lives at `$TMPDIR/.dotnet/shm` (falling back to
// `/tmp/.dotnet/shm` when TMPDIR is unset) — NOT under HOME or
// DOTNET_CLI_HOME. Two concurrent `dotnet` processes (the two
// parity tests run on separate threads in one binary; CI also runs
// jobs in parallel) then race on the one shared `.../shm/session<N>`
// path: one loses with `mkdir(...) == EEXIST` or `stat(...) ==
// ENOENT` in NuGet.Common.Migrations.MigrationRunner — an
// intermittent hard failure with nothing to do with the code under
// test. **`TMPDIR` is the controlling variable** (it relocates the
// shm dir); HOME/DOTNET_CLI_HOME/NUGET_PACKAGES are additionally
// isolated as hygiene. Each test owns its own `home`, so the shm
// dirs never collide.
.env("TMPDIR", home)
.env("HOME", home)
.env("DOTNET_CLI_HOME", home)
.env("NUGET_PACKAGES", home.join("nuget"));
cmd
}

Expand All @@ -61,7 +80,9 @@ fn dotnet() -> Command {
/// rather than silently skip. CI images therefore MUST provision the
/// `net8.0` SDK (pure BCL, no NuGet — see the module doc).
fn dotnet_available() -> bool {
let present = dotnet().arg("--version").output().is_ok();
let probe_home = unique_tmp_dir("probe-home");
let present = dotnet(&probe_home).arg("--version").output().is_ok();
let _ = fs::remove_dir_all(&probe_home);
assert!(
present || std::env::var_os("CI").is_none(),
"`dotnet` not found but `CI` is set — the C# parity loop requires the \
Expand All @@ -83,8 +104,8 @@ fn scaffold_project(dir: &Path, program_cs: &str) {
fs::write(dir.join("Program.cs"), program_cs).expect("write Program.cs");
}

fn dotnet_build(dir: &Path) {
let build = dotnet()
fn dotnet_build(dir: &Path, home: &Path) {
let build = dotnet(home)
.current_dir(dir)
.args(["build", "-v", "q"])
.output()
Expand All @@ -98,8 +119,8 @@ fn dotnet_build(dir: &Path) {
);
}

fn dotnet_run(dir: &Path) -> String {
let run = dotnet()
fn dotnet_run(dir: &Path, home: &Path) -> String {
let run = dotnet(home)
.current_dir(dir)
.args(["run", "-v", "q", "--no-build"])
.output()
Expand All @@ -124,13 +145,14 @@ fn emitted_library_builds_and_dump_matches_ground_truth() {
return;
}
let dir = unique_tmp_dir("build-dump");
let home = unique_tmp_dir("build-dump-home");
scaffold_project(
&dir,
&format!("using {NAMESPACE};\nConsole.Write(OgarCapabilitySurface.Dump());\n"),
);

dotnet_build(&dir);
let dump = dotnet_run(&dir);
dotnet_build(&dir, &home);
let dump = dotnet_run(&dir, &home);

// Compare the dump against the Rust-side ogar_vocab ground truth
// exactly like the Python loop — the SAME comparison function
Expand All @@ -140,6 +162,7 @@ fn emitted_library_builds_and_dump_matches_ground_truth() {
ogar_adapter_python::ground_truth::assert_dump_matches("csharp", &dump);

let _ = fs::remove_dir_all(&dir);
let _ = fs::remove_dir_all(&home);
}

#[test]
Expand All @@ -152,6 +175,7 @@ fn facet_bytes_built_in_rust_decode_correctly_in_csharp() {
return;
}
let dir = unique_tmp_dir("facet");
let home = unique_tmp_dir("facet-home");

// Rust builds a known 16-byte facet BY HAND, straight from the
// documented layout (facet.rs / ruff_spo_address::Facet doc):
Expand Down Expand Up @@ -183,8 +207,8 @@ fn facet_bytes_built_in_rust_decode_correctly_in_csharp() {
);
scaffold_project(&dir, &program_cs);

dotnet_build(&dir);
let got = dotnet_run(&dir);
dotnet_build(&dir, &home);
let got = dotnet_run(&dir, &home);

let expected = format!(
"{classid:08X}\n{}\n{}",
Expand All @@ -205,4 +229,5 @@ fn facet_bytes_built_in_rust_decode_correctly_in_csharp() {
);

let _ = fs::remove_dir_all(&dir);
let _ = fs::remove_dir_all(&home);
}
Loading