From 56c334abf67abde649829a7aa8a4e94d87c7363c Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 24 Apr 2024 17:16:42 -0700 Subject: [PATCH] allow checkouts on Windows --- Cargo.lock | 77 +++++++++++++++++++++++++++ Cargo.toml | 4 ++ src/runner.rs | 10 ++++ tests/files/::colon::dir/::.txt | 1 - tests/files/::colon::dir/a.txt | 1 - tests/run_example.rs | 93 ++++++++++++++++++++++++++++----- 6 files changed, 171 insertions(+), 15 deletions(-) delete mode 100644 tests/files/::colon::dir/::.txt delete mode 100644 tests/files/::colon::dir/a.txt diff --git a/Cargo.lock b/Cargo.lock index a9678df15..42b302c18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,12 +75,34 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "camino" version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +[[package]] +name = "camino-tempfile" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb905055fa81e4d427f919b2cd0d76a998267de7d225ea767a1894743a5263c2" +dependencies = [ + "camino", + "tempfile", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.3.24" @@ -133,11 +155,23 @@ name = "datatest-stable" version = "0.2.7" dependencies = [ "camino", + "camino-tempfile", "fancy-regex", + "fs_extra", "libtest-mimic", "walkdir", ] +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "escape8259" version = "0.5.2" @@ -158,6 +192,18 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "heck" version = "0.4.1" @@ -199,6 +245,12 @@ dependencies = [ "threadpool", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "memchr" version = "2.7.2" @@ -256,6 +308,19 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.15" @@ -288,6 +353,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/Cargo.toml b/Cargo.toml index 51f07647a..f868f0d73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ fancy-regex = "0.13.0" libtest-mimic = "0.7.2" walkdir = "2.5.0" +[target.'cfg(unix)'.dev-dependencies] +camino-tempfile = "1.1.1" +fs_extra = "1.3.0" + [[test]] name = "example" harness = false diff --git a/src/runner.rs b/src/runner.rs index 5fa6eb4e6..ab92d9e50 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -8,6 +8,10 @@ use std::{path::Path, process::ExitCode}; #[doc(hidden)] pub fn runner(requirements: &[Requirements]) -> ExitCode { + if let Some(cwd) = custom_cwd() { + std::env::set_current_dir(&cwd).expect("set custom working directory"); + } + let args = Arguments::from_args(); let tests = find_tests(&args, requirements); @@ -23,6 +27,12 @@ pub fn runner(requirements: &[Requirements]) -> ExitCode { conclusion.exit_code() } +/// One of our tests requires that a custom working directory be set. This function is used to do +/// that. +fn custom_cwd() -> Option { + std::env::var("__DATATEST_CWD").ok().map(Utf8PathBuf::from) +} + fn find_tests(args: &Arguments, requirements: &[Requirements]) -> Vec { let tests: Vec<_> = if let Some(exact_filter) = exact_filter(args) { let exact_tests: Vec<_> = requirements diff --git a/tests/files/::colon::dir/::.txt b/tests/files/::colon::dir/::.txt deleted file mode 100644 index eaabfa772..000000000 --- a/tests/files/::colon::dir/::.txt +++ /dev/null @@ -1 +0,0 @@ -floop \ No newline at end of file diff --git a/tests/files/::colon::dir/a.txt b/tests/files/::colon::dir/a.txt deleted file mode 100644 index ca2fb7494..000000000 --- a/tests/files/::colon::dir/a.txt +++ /dev/null @@ -1 +0,0 @@ -flarp \ No newline at end of file diff --git a/tests/run_example.rs b/tests/run_example.rs index b884d0de0..c7b13e3ba 100644 --- a/tests/run_example.rs +++ b/tests/run_example.rs @@ -1,13 +1,21 @@ // Copyright (c) The datatest-stable Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 +static EXPECTED_LINES: &[&str] = &[ + "datatest-stable::example test_artifact::a.txt", + "datatest-stable::example test_artifact::b.txt", + "datatest-stable::example test_artifact_utf8::a.txt", + "datatest-stable::example test_artifact_utf8::c.skip.txt", + "datatest-stable::example test_artifact_utf8::b.txt", +]; + #[test] fn run_example() { - let output = std::process::Command::new("cargo") + let output = std::process::Command::new(cargo_bin()) .args(["nextest", "run", "--test=example", "--color=never"]) .env("__DATATEST_FULL_SCAN_FORBIDDEN", "1") .output() - .expect("Failed to run `cargo nextest`"); + .expect("`cargo nextest` was successful"); // It's a pain to make assertions on byte slices (even a subslice check isn't easy) // and it's also difficult to print nice error messages. So we just assume all @@ -16,27 +24,86 @@ fn run_example() { assert!( output.status.success(), - "Command failed (exit status: {}, stderr: {stderr})", + "nextest exited with 0 (exit status: {}, stderr: {stderr})", output.status ); - let lines: &[&str] = &[ + for line in EXPECTED_LINES + .iter() + .copied() + .chain(std::iter::once("5 tests run: 5 passed, 0 skipped")) + { + assert!( + stderr.contains(line), + "Expected to find substring\n {line}\nin stderr\n {stderr}", + ); + } +} + +#[cfg(unix)] +mod unix { + use super::*; + use camino_tempfile::Utf8TempDir; + + static EXPECTED_UNIX_LINES: &[&str] = &[ "datatest-stable::example test_artifact::::colon::dir/::.txt", "datatest-stable::example test_artifact::::colon::dir/a.txt", - "datatest-stable::example test_artifact::a.txt", "datatest-stable::example test_artifact_utf8::::colon::dir/::.txt", - "datatest-stable::example test_artifact::b.txt", "datatest-stable::example test_artifact_utf8::::colon::dir/a.txt", - "datatest-stable::example test_artifact_utf8::a.txt", - "datatest-stable::example test_artifact_utf8::c.skip.txt", - "datatest-stable::example test_artifact_utf8::b.txt", - "9 tests run: 9 passed, 0 skipped", ]; - for line in lines { + #[test] + fn run_example_with_colons() { + let temp_dir = Utf8TempDir::with_prefix("datatest-stable").expect("created temp dir"); + std::fs::create_dir_all(temp_dir.path().join("tests")).expect("created dir"); + let dest = temp_dir.path().join("tests/files"); + + // Make a copy of tests/files inside the temp dir. + fs_extra::dir::copy( + "tests/files", + temp_dir.path().join("tests"), + &fs_extra::dir::CopyOptions::new(), + ) + .expect("copied files"); + + // Add some files with colons in their names. (They can't be checked into the repo because + // it needs to be cloned on Windows.) + let colon_dir = dest.join("::colon::dir"); + std::fs::create_dir_all(&colon_dir).expect("created dir with colons"); + std::fs::write(colon_dir.join("::.txt"), b"floop").expect("wrote file with colons"); + std::fs::write(colon_dir.join("a.txt"), b"flarp").expect("wrote file with colons"); + + // Now run the tests. + let output = std::process::Command::new(cargo_bin()) + .args(["nextest", "run", "--test=example", "--color=never"]) + .env("__DATATEST_FULL_SCAN_FORBIDDEN", "1") + .env("__DATATEST_CWD", temp_dir.path()) + .output() + .expect("`cargo nextest` was successful"); + + let stderr = + std::str::from_utf8(&output.stderr).expect("cargo nextest stderr should be utf-8"); + assert!( - stderr.contains(line), - "Expected to find substring\n {line}\nin stderr\n {stderr}", + output.status.success(), + "nextest exited with 0 (exit status: {}, stderr: {stderr})", + output.status ); + + for line in EXPECTED_LINES + .iter() + .chain(EXPECTED_UNIX_LINES.iter()) + .copied() + .chain(std::iter::once("9 tests run: 9 passed, 0 skipped")) + { + assert!( + stderr.contains(line), + "Expected to find substring\n {line}\nin stderr\n {stderr}", + ); + } } } + +fn cargo_bin() -> String { + std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) +}