Let `gix_testtools::Env` undo multiple changes to the same var
Unified · Split
| @@ -2579,6 +2579,7 @@ dependencies = [ · 2578 unmodified lines | |||
| 2579 | "is_ci", | 2579 | "is_ci", |
| 2580 | "once_cell", | 2580 | "once_cell", |
| 2581 | "parking_lot", | 2581 | "parking_lot", |
| 2582 | "serial_test", | ||
| 2582 | "tar", | 2583 | "tar", |
| 2583 | "tempfile", | 2584 | "tempfile", |
| 2584 | "winnow", | 2585 | "winnow", |
| @@ -49,6 +49,9 @@ xz2 = { version = "0.1.6", optional = true } · 48 unmodified lines | |||
| 49 | 49 | ||
| 50 | document-features = { version = "0.2.1", optional = true } | 50 | document-features = { version = "0.2.1", optional = true } |
| 51 | 51 | ||
| 52 | [dev-dependencies] | ||
| 53 | serial_test = { version = "3.1.0", default-features = false } | ||
| 54 | |||
| 52 | [package.metadata.docs.rs] | 55 | [package.metadata.docs.rs] |
| 53 | all-features = true | 56 | all-features = true |
| 54 | features = ["document-features"] | 57 | features = ["document-features"] |
| @@ -823,7 +823,7 @@ fn family_name() -> &'static str { · 822 unmodified lines | |||
| 823 | } | 823 | } |
| 824 | } | 824 | } |
| 825 | 825 | ||
| 826 | /// A utility to set environment variables, while unsetting them (or resetting them to their previous value) on drop. | 826 | /// A utility to set and unset environment variables, while restoring or removing them on drop. |
| 827 | #[derive(Default)] | 827 | #[derive(Default)] |
| 828 | pub struct Env<'a> { | 828 | pub struct Env<'a> { |
| 829 | altered_vars: Vec<(&'a str, Option<OsString>)>, | 829 | altered_vars: Vec<(&'a str, Option<OsString>)>, |
| @@ -845,7 +845,7 @@ impl<'a> Env<'a> { · 15 unmodified lines | |||
| 845 | self | 845 | self |
| 846 | } | 846 | } |
| 847 | 847 | ||
| 848 | /// Set `var` to `value`. | 848 | /// Unset `var`. |
| 849 | pub fn unset(mut self, var: &'a str) -> Self { | 849 | pub fn unset(mut self, var: &'a str) -> Self { |
| 850 | let prev = std::env::var_os(var); | 850 | let prev = std::env::var_os(var); |
| 851 | std::env::remove_var(var); | 851 | std::env::remove_var(var); |
| @@ -856,7 +856,7 @@ impl<'a> Env<'a> { · 4 unmodified lines | |||
| 856 | 856 | ||
| 857 | impl<'a> Drop for Env<'a> { | 857 | impl<'a> Drop for Env<'a> { |
| 858 | fn drop(&mut self) { | 858 | fn drop(&mut self) { |
| 859 | for (var, prev_value) in &self.altered_vars { | 859 | for (var, prev_value) in self.altered_vars.iter().rev() { |
| 860 | match prev_value { | 860 | match prev_value { |
| 861 | Some(value) => std::env::set_var(var, value), | 861 | Some(value) => std::env::set_var(var, value), |
| 862 | None => std::env::remove_var(var), | 862 | None => std::env::remove_var(var), |
| @@ -0,0 +1,93 @@ | |||
| 1 | use std::env; | ||
| 2 | |||
| 3 | use gix_testtools::Env; | ||
| 4 | use serial_test::serial; | ||
| 5 | |||
| 6 | // We rely on these not already existing, to test `Env` without using or rewriting it. | ||
| 7 | static VAR1: &str = "VAR_03FC4045_6043_4A61_9D15_852236CB632B"; | ||
| 8 | static VAR2: &str = "VAR_8C135840_05DB_4F3A_BFDD_FC755EC35B89"; | ||
| 9 | static VAR3: &str = "VAR_9B23A2BE_E20B_4670_93E2_3A6A8D47F274"; | ||
| 10 | |||
| 11 | struct TestEnv; | ||
| 12 | |||
| 13 | impl TestEnv { | ||
| 14 | fn new() -> Self { | ||
| 15 | assert_eq!(env::var_os(VAR1), None); | ||
| 16 | assert_eq!(env::var_os(VAR2), None); | ||
| 17 | assert_eq!(env::var_os(VAR3), None); | ||
| 18 | Self | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | impl Drop for TestEnv { | ||
| 23 | fn drop(&mut self) { | ||
| 24 | env::remove_var(VAR1); | ||
| 25 | env::remove_var(VAR2); | ||
| 26 | env::remove_var(VAR3); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | #[test] | ||
| 31 | #[serial] | ||
| 32 | fn nonoverlapping() { | ||
| 33 | let _meta = TestEnv::new(); | ||
| 34 | env::set_var(VAR1, "old1"); | ||
| 35 | env::set_var(VAR2, "old2"); | ||
| 36 | { | ||
| 37 | let _env = Env::new().set(VAR1, "new1").unset(VAR2).set(VAR3, "new3"); | ||
| 38 | assert_eq!(env::var_os(VAR1), Some("new1".into())); | ||
| 39 | assert_eq!(env::var_os(VAR2), None); | ||
| 40 | assert_eq!(env::var_os(VAR3), Some("new3".into())); | ||
| 41 | } | ||
| 42 | assert_eq!(env::var_os(VAR1), Some("old1".into())); | ||
| 43 | assert_eq!(env::var_os(VAR2), Some("old2".into())); | ||
| 44 | assert_eq!(env::var_os(VAR3), None); | ||
| 45 | } | ||
| 46 | |||
| 47 | #[test] | ||
| 48 | #[serial] | ||
| 49 | fn overlapping_reset() { | ||
| 50 | let _meta = TestEnv::new(); | ||
| 51 | { | ||
| 52 | let _env = Env::new().set(VAR1, "new1A").set(VAR1, "new1B"); | ||
| 53 | assert_eq!(env::var_os(VAR1), Some("new1B".into())); | ||
| 54 | } | ||
| 55 | assert_eq!(env::var_os(VAR1), None); | ||
| 56 | } | ||
| 57 | |||
| 58 | #[test] | ||
| 59 | #[serial] | ||
| 60 | fn overlapping_unset() { | ||
| 61 | let _meta = TestEnv::new(); | ||
| 62 | env::set_var(VAR1, "old1"); | ||
| 63 | { | ||
| 64 | let _env = Env::new().unset(VAR1).unset(VAR1); | ||
| 65 | assert_eq!(env::var_os(VAR1), None); | ||
| 66 | } | ||
| 67 | assert_eq!(env::var_os(VAR1), Some("old1".into())); | ||
| 68 | } | ||
| 69 | |||
| 70 | #[test] | ||
| 71 | #[serial] | ||
| 72 | fn overlapping_combo() { | ||
| 73 | let _meta = TestEnv::new(); | ||
| 74 | env::set_var(VAR1, "old1"); | ||
| 75 | env::set_var(VAR2, "old2"); | ||
| 76 | { | ||
| 77 | let _env = Env::new() | ||
| 78 | .set(VAR1, "new1A") | ||
| 79 | .unset(VAR2) | ||
| 80 | .set(VAR1, "new1B") | ||
| 81 | .unset(VAR3) | ||
| 82 | .set(VAR2, "new2") | ||
| 83 | .set(VAR3, "new3") | ||
| 84 | .unset(VAR1) | ||
| 85 | .unset(VAR3); | ||
| 86 | assert_eq!(env::var_os(VAR1), None); | ||
| 87 | assert_eq!(env::var_os(VAR2), Some("new2".into())); | ||
| 88 | assert_eq!(env::var_os(VAR3), None); | ||
| 89 | } | ||
| 90 | assert_eq!(env::var_os(VAR1), Some("old1".into())); | ||
| 91 | assert_eq!(env::var_os(VAR2), Some("old2".into())); | ||
| 92 | assert_eq!(env::var_os(VAR3), None); | ||
| 93 | } | ||