1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! Test-only helpers for mutating process environment variables.
//!
//! **The lock is crate-wide, deliberately.** `std::env::set_var` is `unsafe` because it races any
//! concurrent `env::var`, and cargo runs a binary's tests on parallel threads. A per-module lock
//! can't exclude the real pairs: `config::config` writes `XDG_CONFIG_HOME` while `cli::doctor`
//! reads it, and `terminal::capabilities` writes `TERM_PROGRAM` while `cli::doctor` reads that.
//!
//! Two rules for any test touching the environment:
//!
//! 1. Take [`env_lock`] first and hold it for the whole test — readers included, since a read is
//! the other half of the same race.
//! 2. Mutate only through [`EnvGuard`], so a panicking assertion can't leak an `XDG_CONFIG_HOME`
//! pointing at a deleted tempdir and fail every later config test.
//!
//! The same lock serialises [`config::persistence::SuppressGuard`], which flips a process-global
//! `AtomicBool`. That read side is easy to miss because such a test touches no environment
//! variable of its own: anything observing the config gate — `list_export_stylesheets`,
//! `list_theme_names`, `read_theme_named`, `Config::save` — must take the lock, or it reads the
//! gate while another thread holds it closed and sees an inexplicably empty list.
//!
//! [`config::persistence::SuppressGuard`]: crate::config::persistence::SuppressGuard
use env;
use ;
/// Serialises every environment-touching test in the crate. Poisoning is ignored: [`EnvGuard`]'s
/// `Drop` has already restored the variable by the time a panicking test releases the lock.
/// Sets or clears one environment variable, restoring it on drop. Create only under [`env_lock`].
/// The crate-wide env lock plus suppressed config reads and writes, as one guard.
///
/// **Any test that can reach [`Config::save`] must hold this.** Nothing redirects
/// `~/.config/edamame` during a test run, so an unguarded save rewrites the *developer's own*
/// config — with exactly the damaging values tests assert (an update-check test recording
/// `v999.0.0` silences the real update notice forever). Suppressing the gate beats pointing
/// `XDG_CONFIG_HOME` at a tempdir: the write should be gone, not relocated, and `Config::save`
/// still returns `Ok(())`.
///
/// Reads are suppressed alongside writes because the gate is one flag (see
/// [`crate::config::persistence`]). The one test needing a real write takes [`env_lock`] plus an
/// [`EnvGuard`] on `XDG_CONFIG_HOME` instead.
/// Take [`ConfigIsolation`] for the current scope. Hold it for the whole test body and take it
/// only once — it is a mutex.