Skip to content

Commit d473e38

Browse files
committed
revert FailScenario
Signed-off-by: Xintao <[email protected]>
1 parent 18eafb7 commit d473e38

File tree

1 file changed

+81
-59
lines changed

1 file changed

+81
-59
lines changed

src/lib.rs

Lines changed: 81 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@
2828
//! I/O panic:
2929
//!
3030
//! ```rust
31-
//! use fail::fail_point;
31+
//! use fail::{fail_point, FailScenario};
3232
//!
3333
//! fn do_fallible_work() {
3434
//! fail_point!("read-dir");
3535
//! let _dir: Vec<_> = std::fs::read_dir(".").unwrap().collect();
3636
//! // ... do some work on the directory ...
3737
//! }
3838
//!
39-
//! fail::setup();
39+
//! let scenario = FailScenario::setup();
4040
//! do_fallible_work();
41-
//! fail::teardown();
41+
//! scenario.teardown();
4242
//! println!("done");
4343
//! ```
4444
//!
@@ -83,7 +83,8 @@
8383
//! by default. You can pass the setting from environment variables to the global registry.
8484
//! Sometimes you need different tests to use different registries and don’t want their
8585
//! behavior to interfere with each other. You can create a local registry and then register
86-
//! threads that need to share the same registry.
86+
//! threads that need to share the same registry. Or you can still use `FailScenario` to
87+
//! sequentially configure different global registry.
8788
//!
8889
//! Here's a basic pattern for writing unit tests tests with fail points:
8990
//!
@@ -116,7 +117,9 @@
116117
//! Here's a example to show the process:
117118
//!
118119
//! ```rust
119-
//! fail::setup();
120+
//! use fail::FailScenario;
121+
//!
122+
//! let _scenario = FailScenario::setup();
120123
//! fail::cfg("p1", "sleep(100)").unwrap();
121124
//! println!("Global registry: {:?}", fail::list());
122125
//! {
@@ -165,7 +168,7 @@
165168
//! function we used earlier to return a `Result`:
166169
//!
167170
//! ```rust
168-
//! use fail::fail_point;
171+
//! use fail::{fail_point, FailScenario};
169172
//! use std::io;
170173
//!
171174
//! fn do_fallible_work() -> io::Result<()> {
@@ -176,9 +179,9 @@
176179
//! }
177180
//!
178181
//! fn main() -> io::Result<()> {
179-
//! fail::setup();
182+
//! let scenario = FailScenario::setup();
180183
//! do_fallible_work()?;
181-
//! fail::teardown();
184+
//! scenario.teardown();
182185
//! println!("done");
183186
//! Ok(())
184187
//! }
@@ -264,6 +267,7 @@ use std::env::VarError;
264267
use std::fmt::Debug;
265268
use std::str::FromStr;
266269
use std::sync::atomic::{AtomicUsize, Ordering};
270+
use std::sync::MutexGuard;
267271
use std::sync::{Arc, Condvar, Mutex, RwLock, TryLockError};
268272
use std::time::{Duration, Instant};
269273
use std::{env, thread};
@@ -588,63 +592,81 @@ impl FailPointRegistry {
588592
lazy_static::lazy_static! {
589593
static ref REGISTRY_GROUP: RwLock<HashMap<thread::ThreadId, Arc<RwLock<Registry>>>> = Default::default();
590594
static ref REGISTRY_GLOBAL: FailPointRegistry = Default::default();
595+
static ref SCENARIO: Mutex<&'static FailPointRegistry> = Mutex::new(&REGISTRY_GLOBAL);
591596
}
592597

593-
/// Set up the global fail points registry.
594-
///
595-
/// Configures global fail points specified in the `FAILPOINTS` environment variable.
596-
/// It does not otherwise change any existing fail point configuration.
597-
///
598-
/// The format of `FAILPOINTS` is `failpoint=actions;...`, where
599-
/// `failpoint` is the name of the fail point. For more information
600-
/// about fail point actions see the [`cfg`](fn.cfg.html) function and
601-
/// the [`fail_point`](macro.fail_point.html) macro.
602-
///
603-
/// `FAILPOINTS` may configure fail points that are not actually defined. In
604-
/// this case the configuration has no effect.
605-
///
606-
/// This function should generally be called prior to running a test with fail
607-
/// points, and afterward paired with [`teardown`](fn.teardown.html).
608-
///
609-
/// # Panics
610-
///
611-
/// Panics if an action is not formatted correctly.
612-
pub fn setup() {
613-
let mut registry = REGISTRY_GLOBAL.registry.write().unwrap();
614-
cleanup(&mut registry);
615-
616-
let failpoints = match env::var("FAILPOINTS") {
617-
Ok(s) => s,
618-
Err(VarError::NotPresent) => return,
619-
Err(e) => panic!("invalid failpoints: {:?}", e),
620-
};
621-
for mut cfg in failpoints.trim().split(';') {
622-
cfg = cfg.trim();
623-
if cfg.is_empty() {
624-
continue;
625-
}
626-
let (name, order) = partition(cfg, '=');
627-
match order {
628-
None => panic!("invalid failpoint: {:?}", cfg),
629-
Some(order) => {
630-
if let Err(e) = set(&mut registry, name.to_owned(), order) {
631-
panic!("unable to configure failpoint \"{}\": {}", name, e);
598+
/// Test scenario with configured fail points.
599+
#[derive(Debug)]
600+
pub struct FailScenario<'a> {
601+
scenario_guard: MutexGuard<'a, &'static FailPointRegistry>,
602+
}
603+
604+
impl<'a> FailScenario<'a> {
605+
/// Set up the system for a fail points scenario.
606+
///
607+
/// Configures global fail points specified in the `FAILPOINTS` environment variable.
608+
/// It does not otherwise change any existing fail point configuration.
609+
///
610+
/// The format of `FAILPOINTS` is `failpoint=actions;...`, where
611+
/// `failpoint` is the name of the fail point. For more information
612+
/// about fail point actions see the [`cfg`](fn.cfg.html) function and
613+
/// the [`fail_point`](macro.fail_point.html) macro.
614+
///
615+
/// `FAILPOINTS` may configure fail points that are not actually defined. In
616+
/// this case the configuration has no effect.
617+
///
618+
/// This function should generally be called prior to running a test with fail
619+
/// points, and afterward paired with [`teardown`](#method.teardown).
620+
///
621+
/// # Panics
622+
///
623+
/// Panics if an action is not formatted correctly.
624+
pub fn setup() -> Self {
625+
// Cleanup first, in case of previous failed/panic'ed test scenarios.
626+
let scenario_guard = SCENARIO.lock().unwrap_or_else(|e| e.into_inner());
627+
let mut registry = scenario_guard.registry.write().unwrap();
628+
cleanup(&mut registry);
629+
630+
let failpoints = match env::var("FAILPOINTS") {
631+
Ok(s) => s,
632+
Err(VarError::NotPresent) => return Self { scenario_guard },
633+
Err(e) => panic!("invalid failpoints: {:?}", e),
634+
};
635+
for mut cfg in failpoints.trim().split(';') {
636+
cfg = cfg.trim();
637+
if cfg.is_empty() {
638+
continue;
639+
}
640+
let (name, order) = partition(cfg, '=');
641+
match order {
642+
None => panic!("invalid failpoint: {:?}", cfg),
643+
Some(order) => {
644+
if let Err(e) = set(&mut registry, name.to_owned(), order) {
645+
panic!("unable to configure failpoint \"{}\": {}", name, e);
646+
}
632647
}
633648
}
634649
}
650+
Self { scenario_guard }
651+
}
652+
653+
/// Tear down the global fail points registry.
654+
///
655+
/// Clears the configuration of global fail points. Any paused fail
656+
/// points will be notified before they are deactivated.
657+
///
658+
/// This function should generally be called after running a test with fail points.
659+
/// Calling `teardown` without previously calling `setup` results in a no-op.
660+
pub fn teardown(self) {
661+
drop(self);
635662
}
636663
}
637664

638-
/// Tear down the global fail points registry.
639-
///
640-
/// Clears the configuration of global fail points. Any paused fail
641-
/// points will be notified before they are deactivated.
642-
///
643-
/// This function should generally be called after running a test with fail points.
644-
/// Calling `teardown` without previously calling `setup` results in a no-op.
645-
pub fn teardown() {
646-
let mut registry = REGISTRY_GLOBAL.registry.write().unwrap();
647-
cleanup(&mut registry);
665+
impl<'a> Drop for FailScenario<'a> {
666+
fn drop(&mut self) {
667+
let mut registry = self.scenario_guard.registry.write().unwrap();
668+
cleanup(&mut registry);
669+
}
648670
}
649671

650672
/// Clean all registered fail points.
@@ -1099,7 +1121,7 @@ mod tests {
10991121
"FAILPOINTS",
11001122
"setup_and_teardown1=return;setup_and_teardown2=pause;",
11011123
);
1102-
setup();
1124+
let scenario = FailScenario::setup();
11031125

11041126
let group = FailPointRegistry::new();
11051127
let handler = thread::spawn(move || {
@@ -1130,7 +1152,7 @@ mod tests {
11301152
});
11311153
assert!(rx.recv_timeout(Duration::from_millis(500)).is_err());
11321154

1133-
teardown();
1155+
scenario.teardown();
11341156
assert_eq!(rx.recv_timeout(Duration::from_millis(500)).unwrap(), 0);
11351157
assert_eq!(f1(), 0);
11361158
}

0 commit comments

Comments
 (0)