|
28 | 28 | //! I/O panic:
|
29 | 29 | //!
|
30 | 30 | //! ```rust
|
31 |
| -//! use fail::fail_point; |
| 31 | +//! use fail::{fail_point, FailScenario}; |
32 | 32 | //!
|
33 | 33 | //! fn do_fallible_work() {
|
34 | 34 | //! fail_point!("read-dir");
|
35 | 35 | //! let _dir: Vec<_> = std::fs::read_dir(".").unwrap().collect();
|
36 | 36 | //! // ... do some work on the directory ...
|
37 | 37 | //! }
|
38 | 38 | //!
|
39 |
| -//! fail::setup(); |
| 39 | +//! let scenario = FailScenario::setup(); |
40 | 40 | //! do_fallible_work();
|
41 |
| -//! fail::teardown(); |
| 41 | +//! scenario.teardown(); |
42 | 42 | //! println!("done");
|
43 | 43 | //! ```
|
44 | 44 | //!
|
|
116 | 116 | //! Here's a example to show the process:
|
117 | 117 | //!
|
118 | 118 | //! ```rust
|
119 |
| -//! fail::setup(); |
| 119 | +//! use fail::FailScenario; |
| 120 | +//! |
| 121 | +//! let _scenario = FailScenario::setup(); |
120 | 122 | //! fail::cfg("p1", "sleep(100)").unwrap();
|
121 | 123 | //! println!("Global registry: {:?}", fail::list());
|
122 | 124 | //! {
|
|
165 | 167 | //! function we used earlier to return a `Result`:
|
166 | 168 | //!
|
167 | 169 | //! ```rust
|
168 |
| -//! use fail::fail_point; |
| 170 | +//! use fail::{fail_point, FailScenario}; |
169 | 171 | //! use std::io;
|
170 | 172 | //!
|
171 | 173 | //! fn do_fallible_work() -> io::Result<()> {
|
|
176 | 178 | //! }
|
177 | 179 | //!
|
178 | 180 | //! fn main() -> io::Result<()> {
|
179 |
| -//! fail::setup(); |
| 181 | +//! let scenario = FailScenario::setup(); |
180 | 182 | //! do_fallible_work()?;
|
181 |
| -//! fail::teardown(); |
| 183 | +//! scenario.teardown(); |
182 | 184 | //! println!("done");
|
183 | 185 | //! Ok(())
|
184 | 186 | //! }
|
@@ -264,6 +266,7 @@ use std::env::VarError;
|
264 | 266 | use std::fmt::Debug;
|
265 | 267 | use std::str::FromStr;
|
266 | 268 | use std::sync::atomic::{AtomicUsize, Ordering};
|
| 269 | +use std::sync::MutexGuard; |
267 | 270 | use std::sync::{Arc, Condvar, Mutex, RwLock, TryLockError};
|
268 | 271 | use std::time::{Duration, Instant};
|
269 | 272 | use std::{env, thread};
|
@@ -588,63 +591,80 @@ impl FailPointRegistry {
|
588 | 591 | lazy_static::lazy_static! {
|
589 | 592 | static ref REGISTRY_GROUP: RwLock<HashMap<thread::ThreadId, Arc<RwLock<Registry>>>> = Default::default();
|
590 | 593 | static ref REGISTRY_GLOBAL: FailPointRegistry = Default::default();
|
| 594 | + static ref SCENARIO: Mutex<&'static FailPointRegistry> = Mutex::new(®ISTRY_GLOBAL); |
591 | 595 | }
|
592 | 596 |
|
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); |
| 597 | +/// Test scenario with configured fail points. |
| 598 | +#[derive(Debug)] |
| 599 | +pub struct FailScenario<'a> { |
| 600 | + scenario_guard: MutexGuard<'a, &'static FailPointRegistry>, |
| 601 | +} |
| 602 | + |
| 603 | +impl<'a> FailScenario<'a> { |
| 604 | + /// Set up the global fail points registry. |
| 605 | + /// |
| 606 | + /// Configures global fail points specified in the `FAILPOINTS` environment variable. |
| 607 | + /// It does not otherwise change any existing fail point configuration. |
| 608 | + /// |
| 609 | + /// The format of `FAILPOINTS` is `failpoint=actions;...`, where |
| 610 | + /// `failpoint` is the name of the fail point. For more information |
| 611 | + /// about fail point actions see the [`cfg`](fn.cfg.html) function and |
| 612 | + /// the [`fail_point`](macro.fail_point.html) macro. |
| 613 | + /// |
| 614 | + /// `FAILPOINTS` may configure fail points that are not actually defined. In |
| 615 | + /// this case the configuration has no effect. |
| 616 | + /// |
| 617 | + /// This function should generally be called prior to running a test with fail |
| 618 | + /// points, and afterward paired with [`teardown`](fn.teardown.html). |
| 619 | + /// |
| 620 | + /// # Panics |
| 621 | + /// |
| 622 | + /// Panics if an action is not formatted correctly. |
| 623 | + pub fn setup() -> Self { |
| 624 | + let scenario_guard = SCENARIO.lock().unwrap_or_else(|e| e.into_inner()); |
| 625 | + let mut registry = scenario_guard.registry.write().unwrap(); |
| 626 | + cleanup(&mut registry); |
| 627 | + |
| 628 | + let failpoints = match env::var("FAILPOINTS") { |
| 629 | + Ok(s) => s, |
| 630 | + Err(VarError::NotPresent) => return Self { scenario_guard }, |
| 631 | + Err(e) => panic!("invalid failpoints: {:?}", e), |
| 632 | + }; |
| 633 | + for mut cfg in failpoints.trim().split(';') { |
| 634 | + cfg = cfg.trim(); |
| 635 | + if cfg.is_empty() { |
| 636 | + continue; |
| 637 | + } |
| 638 | + let (name, order) = partition(cfg, '='); |
| 639 | + match order { |
| 640 | + None => panic!("invalid failpoint: {:?}", cfg), |
| 641 | + Some(order) => { |
| 642 | + if let Err(e) = set(&mut registry, name.to_owned(), order) { |
| 643 | + panic!("unable to configure failpoint \"{}\": {}", name, e); |
| 644 | + } |
632 | 645 | }
|
633 | 646 | }
|
634 | 647 | }
|
| 648 | + Self { scenario_guard } |
| 649 | + } |
| 650 | + |
| 651 | + /// Tear down the global fail points registry. |
| 652 | + /// |
| 653 | + /// Clears the configuration of global fail points. Any paused fail |
| 654 | + /// points will be notified before they are deactivated. |
| 655 | + /// |
| 656 | + /// This function should generally be called after running a test with fail points. |
| 657 | + /// Calling `teardown` without previously calling `setup` results in a no-op. |
| 658 | + pub fn teardown(self) { |
| 659 | + drop(self); |
635 | 660 | }
|
636 | 661 | }
|
637 | 662 |
|
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); |
| 663 | +impl<'a> Drop for FailScenario<'a> { |
| 664 | + fn drop(&mut self) { |
| 665 | + let mut registry = self.scenario_guard.registry.write().unwrap(); |
| 666 | + cleanup(&mut registry); |
| 667 | + } |
648 | 668 | }
|
649 | 669 |
|
650 | 670 | /// Clean all registered fail points.
|
@@ -1099,7 +1119,7 @@ mod tests {
|
1099 | 1119 | "FAILPOINTS",
|
1100 | 1120 | "setup_and_teardown1=return;setup_and_teardown2=pause;",
|
1101 | 1121 | );
|
1102 |
| - setup(); |
| 1122 | + let scenario = FailScenario::setup(); |
1103 | 1123 |
|
1104 | 1124 | let group = FailPointRegistry::new();
|
1105 | 1125 | let handler = thread::spawn(move || {
|
@@ -1130,7 +1150,7 @@ mod tests {
|
1130 | 1150 | });
|
1131 | 1151 | assert!(rx.recv_timeout(Duration::from_millis(500)).is_err());
|
1132 | 1152 |
|
1133 |
| - teardown(); |
| 1153 | + scenario.teardown(); |
1134 | 1154 | assert_eq!(rx.recv_timeout(Duration::from_millis(500)).unwrap(), 0);
|
1135 | 1155 | assert_eq!(f1(), 0);
|
1136 | 1156 | }
|
|
0 commit comments