Summary
Changing the process-wide current working directory (CWD) with std::env::set_current_dir inside tests makes Rust test suites flaky: tests that read the CWD can observe another test's temporary directory mid-flight. The standard ecosystem remedies — running tests serially, guarding the CWD with a mutex/RAII guard, or using #[serial] — are partial by construction. They only serialize participants who opt in. A plain test that reads the CWD without holding the guard or without the attribute remains vulnerable. The stronger fix is to remove the mutation entirely by dependency-injecting the directory into the function under test as a &Path parameter.
Key Points
cargo test -- --test-threads=1serializes the entire suite to fix a handful of tests, destroying parallelism; it is widely called an anti-pattern.- A process-wide CWD guard such as
rskit_testutil::CurrentDirGuardguarantees only that concurrent guard holders cannot interleave and that the directory is restored on drop. serial_test's#[serial]orders only annotated tests against each other.- Guard and
#[serial]are both opt-in. A non-participating CWD-reading test still observes another test's tempdir mid-flight. - The class-eliminating fix is to stop moving the process CWD in tests: pass an explicit directory (e.g.
tmp.path()) to the function under test. - If no test mutates the CWD, no reader can be victimized, and guard/attribute conventions stop being load-bearing.
Concepts
std::env::set_current_dir: process-global mutable state. In Rust's parallel test runtime, one test's mutation can race with any other test's read of the current directory.- CurrentDirGuard: a
ReentrantMutex-based RAII guard fromrskit_testutilthat restores the previous directory on drop and prevents concurrent guard holders from interleaving. Similar patterns appear in projects like , which ship a plus an helper.