-
Notifications
You must be signed in to change notification settings - Fork 0
Guard NINJA_ENV overrides with mockable Env #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f929fb9
f432ca2
248afec
d8ed0bf
ebf596e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,49 @@ | ||||||||||||||||||||||||||||||||||||||
| use mockable::MockEnv; | ||||||||||||||||||||||||||||||||||||||
| use netsuke::runner::NINJA_ENV; | ||||||||||||||||||||||||||||||||||||||
| use rstest::rstest; | ||||||||||||||||||||||||||||||||||||||
| use std::env::VarError; | ||||||||||||||||||||||||||||||||||||||
| use support::env_lock::EnvLock; | ||||||||||||||||||||||||||||||||||||||
| use support::ninja_env::override_ninja_env; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| #[expect( | ||||||||||||||||||||||||||||||||||||||
| unused, | ||||||||||||||||||||||||||||||||||||||
| reason = "support module exports helpers unused in these tests" | ||||||||||||||||||||||||||||||||||||||
| )] | ||||||||||||||||||||||||||||||||||||||
| mod support; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+13
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace lint-group Using the lint group Apply this diff: -#[expect(
- unused,
- reason = "support module exports helpers unused in these tests"
-)]
+#[expect(
+ dead_code,
+ reason = "support module exposes helpers not used in this test crate"
+)]
mod support;If some submodules export additional helpers, prefer placing the narrow 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| #[rstest] | ||||||||||||||||||||||||||||||||||||||
| #[case(Some("orig"))] | ||||||||||||||||||||||||||||||||||||||
| #[case(None)] | ||||||||||||||||||||||||||||||||||||||
| #[case(Some(""))] | ||||||||||||||||||||||||||||||||||||||
| fn override_ninja_env_restores(#[case] original: Option<&'static str>) { | ||||||||||||||||||||||||||||||||||||||
| let mut env = MockEnv::new(); | ||||||||||||||||||||||||||||||||||||||
| match original { | ||||||||||||||||||||||||||||||||||||||
| Some(val) => { | ||||||||||||||||||||||||||||||||||||||
| let returned = val.to_string(); | ||||||||||||||||||||||||||||||||||||||
| env.expect_raw() | ||||||||||||||||||||||||||||||||||||||
| .withf(|k| k == NINJA_ENV) | ||||||||||||||||||||||||||||||||||||||
| .times(1) | ||||||||||||||||||||||||||||||||||||||
| .return_once(move |_| Ok(returned)); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| None => { | ||||||||||||||||||||||||||||||||||||||
| env.expect_raw() | ||||||||||||||||||||||||||||||||||||||
| .withf(|k| k == NINJA_ENV) | ||||||||||||||||||||||||||||||||||||||
| .times(1) | ||||||||||||||||||||||||||||||||||||||
| .return_once(|_| Err(VarError::NotPresent)); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| let _guard = override_ninja_env(EnvLock::acquire(), &env, "new"); | ||||||||||||||||||||||||||||||||||||||
| assert_eq!(std::env::var(NINJA_ENV).as_deref(), Ok("new")); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| match original { | ||||||||||||||||||||||||||||||||||||||
| Some(val) => assert_eq!(std::env::var(NINJA_ENV).as_deref(), Ok(val)), | ||||||||||||||||||||||||||||||||||||||
| None => assert!(std::env::var(NINJA_ENV).is_err()), | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| let _cleanup = EnvLock::acquire(); | ||||||||||||||||||||||||||||||||||||||
| // SAFETY: `EnvLock` serialises this mutation; see above for details. | ||||||||||||||||||||||||||||||||||||||
| unsafe { std::env::remove_var(NINJA_ENV) }; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Comment on lines
+41
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hold Acquire Apply this diff: - match original {
+ let _lock = EnvLock::acquire();
+ match original {
Some(val) => assert_eq!(std::env::var(NINJA_ENV).as_deref(), Ok(val)),
None => assert!(std::env::var(NINJA_ENV).is_err()),
}
- let _cleanup = EnvLock::acquire();
- // SAFETY: `EnvLock` serialises this mutation; see above for details.
+ // SAFETY: `EnvLock` is held via `_lock`, serialising this mutation; see above for details.
unsafe { std::env::remove_var(NINJA_ENV) };📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| //! Override and restore [`NINJA_ENV`] for tests. | ||
| //! | ||
| //! Provides a helper that sets [`NINJA_ENV`] while ensuring it is restored | ||
| //! afterwards. This uses [`EnvLock`] to serialise mutations to the global | ||
| //! environment and captures the previous value through a `mockable::Env` | ||
| //! implementation so tests can inject their own state. | ||
|
|
||
| use super::env_lock::EnvLock; | ||
| use mockable::Env; | ||
| use netsuke::runner::NINJA_ENV; | ||
|
|
||
| /// Guard that resets `NINJA_ENV` on drop. | ||
| /// | ||
| /// Holding the guard keeps the environment override in place. Dropping it | ||
| /// restores the prior value while releasing the environment lock, cleaning up | ||
| /// global state even if a test panics. | ||
| #[must_use] | ||
| #[derive(Debug)] | ||
| pub struct NinjaEnvGuard { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| _lock: EnvLock, | ||
| original: Option<String>, | ||
| } | ||
|
|
||
| /// Set [`NINJA_ENV`] to `value`, returning a guard that restores the previous | ||
| /// value when dropped. | ||
| /// | ||
| /// # Thread Safety | ||
| /// | ||
| /// This function is **not thread-safe**. Callers must supply an | ||
| /// [`EnvLock`](super::env_lock::EnvLock), which is stored in the returned guard | ||
| /// to serialise the mutation and ensure restoration occurs before the lock is | ||
| /// released. | ||
| /// | ||
| /// Drop order is enforced: dropping the guard restores [`NINJA_ENV`] and only | ||
| /// then releases the lock. | ||
| /// | ||
| /// # Examples | ||
| /// ```ignore | ||
| /// use mockable::DefaultEnv; | ||
| /// use crate::support::{env_lock::EnvLock, ninja_env::override_ninja_env}; | ||
| /// let env = DefaultEnv::new(); | ||
| /// let _guard = override_ninja_env(EnvLock::acquire(), &env, "/usr/bin/ninja"); | ||
| /// ``` | ||
| #[cfg_attr( | ||
| not(test), | ||
| expect(unused_code, reason = "only some tests override NINJA_ENV") | ||
| )] | ||
| pub fn override_ninja_env(lock: EnvLock, env: &impl Env, value: &str) -> NinjaEnvGuard { | ||
| let original = env.raw(NINJA_ENV).ok(); | ||
| // Safety: `EnvLock` serialises this mutation. `set_var` is `unsafe` on Rust | ||
| // 2024 and the guard restores the prior value on drop. | ||
| unsafe { std::env::set_var(NINJA_ENV, value) }; | ||
| NinjaEnvGuard { | ||
| _lock: lock, | ||
| original, | ||
| } | ||
| } | ||
|
|
||
| impl Drop for NinjaEnvGuard { | ||
| fn drop(&mut self) { | ||
| // Safety: the guard holds [`EnvLock`] for its lifetime, so these | ||
| // `set_var`/`remove_var` calls are serialised. Both functions are | ||
| // `unsafe` on Rust 2024. | ||
| unsafe { | ||
| if let Some(ref val) = self.original { | ||
| std::env::set_var(NINJA_ENV, val); | ||
| } else { | ||
| std::env::remove_var(NINJA_ENV); | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add module-level docs and import
serial_testAdd a module-level
//!doc comment per guidelines and import theserialattribute used below.Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents