Skip to content

Commit 566cde5

Browse files
committed
Add crate isolation of fail points
1 parent 4bf6fc9 commit 566cde5

File tree

5 files changed

+120
-3
lines changed

5 files changed

+120
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.6.0 - 2025-08-31
2+
3+
- Add `crate-isolation` feature for crate seperation of fail points (#85)
4+
15
# 0.5.1 - 2022-10-08
26

37
- Switch to 2021 edition and use once cell (#61)

Cargo.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fail"
3-
version = "0.5.1"
3+
version = "0.6.0"
44
authors = ["The TiKV Project Developers"]
55
license = "Apache-2.0"
66
keywords = ["failpoints", "fail"]
@@ -20,6 +20,16 @@ rand = "0.8"
2020

2121
[features]
2222
failpoints = []
23+
# Automatically prefix fail point names with the name of the crate the fail point
24+
# is currently located.
25+
#
26+
# This can be used to prevent conflicting fail points from two seperate libraries
27+
# being triggered accidentally.
28+
#
29+
# WARNING:
30+
# This feature should *NOT* be enabled outside of tests/dev-dependencies
31+
# due to crate features being additive in nature.
32+
crate-isolation = []
2333

2434
[package.metadata.docs.rs]
2535
all-features = true

src/lib.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,16 @@
222222
//! - Fail points might have the same name, in which case they take the
223223
//! same actions. Be careful about duplicating fail point names, either within
224224
//! a single crate, or across multiple crates.
225+
//!
226+
//! ### Crate isolation of fail points
227+
//!
228+
//! When the `crate-isolation` feature is enabled, any fail point name given
229+
//! will be prefixed with the name of the crate where the fail point is defined
230+
//! provided by Cargo's `CARGO_PKG_NAME` compile time environment variable.
231+
//!
232+
//! This feature should **not** be enabled outside `dev-dependencies` / test dependencies
233+
//! due to crate features being additive in nature.
234+
//!
225235
226236
#![deny(missing_docs, missing_debug_implementations)]
227237

@@ -670,6 +680,12 @@ pub fn eval<R, F: FnOnce(Option<String>) -> R>(name: &str, f: F) -> Option<R> {
670680
/// The `FAILPOINTS` environment variable accepts this same syntax for its fail
671681
/// point actions.
672682
///
683+
/// ### Crate isolation of fail points
684+
///
685+
/// When the `crate-isolation` feature is enabled, any fail point name given
686+
/// will be prefixed with the name of the crate where the fail point is defined
687+
/// provided by Cargo's `CARGO_PKG_NAME` compile time environment variable.
688+
///
673689
/// A call to `cfg` with a particular fail point name overwrites any existing actions for
674690
/// that fail point, including those set via the `FAILPOINTS` environment variable.
675691
pub fn cfg<S: Into<String>>(name: S, actions: &str) -> Result<(), String> {
@@ -819,19 +835,35 @@ fn set(
819835
/// and`$e` must be a function or closure that accepts an `Option<String>` and
820836
/// returns the same type as the enclosing function.
821837
///
838+
/// ### Automatic crate separation
839+
///
840+
/// When the `crate-isolation` feature is enabled, any fail point name given
841+
/// will be prefixed with the name of the crate where the fail point is defined
842+
/// provided by Cargo's `CARGO_PKG_NAME` compile time environment variable.
843+
///
844+
/// For example, if our crate name is `my-crate`, the following snippet will be
845+
/// only be triggered when targeting the `my-crate::fail-point-1` fail point:
846+
///
847+
/// ```rust
848+
/// # #[macro_use] extern crate fail;
849+
/// fn function_return_unit() {
850+
/// fail_point!("fail-point-1");
851+
/// }
852+
/// ```
853+
///
822854
/// For more examples see the [crate documentation](index.html). For more
823855
/// information about controlling fail points see the [`cfg`](fn.cfg.html)
824856
/// function.
825857
#[macro_export]
826858
#[cfg(feature = "failpoints")]
827859
macro_rules! fail_point {
828860
($name:expr) => {{
829-
$crate::eval($name, |_| {
861+
$crate::eval($crate::fail_point_name!($name), |_| {
830862
panic!("Return is not supported for the fail point \"{}\"", $name);
831863
});
832864
}};
833865
($name:expr, $e:expr) => {{
834-
if let Some(res) = $crate::eval($name, $e) {
866+
if let Some(res) = $crate::eval($crate::fail_point_name!($name), $e) {
835867
return res;
836868
}
837869
}};
@@ -851,6 +883,24 @@ macro_rules! fail_point {
851883
($name:expr, $cond:expr, $e:expr) => {{}};
852884
}
853885

886+
#[doc(hidden)]
887+
#[macro_export]
888+
#[cfg(all(feature = "failpoints", feature = "crate-isolation"))]
889+
macro_rules! fail_point_name {
890+
($name:expr) => {
891+
concat!(env!("CARGO_PKG_NAME"), "::", $name)
892+
};
893+
}
894+
895+
#[doc(hidden)]
896+
#[macro_export]
897+
#[cfg(all(feature = "failpoints", not(feature = "crate-isolation")))]
898+
macro_rules! fail_point_name {
899+
($name:expr) => {
900+
$name
901+
};
902+
}
903+
854904
#[cfg(test)]
855905
mod tests {
856906
use super::*;
@@ -1035,6 +1085,7 @@ mod tests {
10351085
// like `test_pause` maybe also affected, so it's better keep it here.
10361086
#[test]
10371087
#[cfg_attr(not(feature = "failpoints"), ignore)]
1088+
#[cfg_attr(feature = "crate-isolation", ignore)]
10381089
fn test_setup_and_teardown() {
10391090
let f1 = || {
10401091
fail_point!("setup_and_teardown1", |_| 1);

tests/no_use_import.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::*;
44

55
#[test]
66
#[cfg_attr(not(feature = "failpoints"), ignore)]
7+
#[cfg_attr(feature = "crate-isolation", ignore)]
78
fn test_return() {
89
let f = || {
910
fail::fail_point!("return", |s: Option<String>| s

tests/tests.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::*;
88
use fail::fail_point;
99

1010
#[test]
11+
#[cfg_attr(feature = "crate-isolation", ignore)]
1112
fn test_off() {
1213
let f = || {
1314
fail_point!("off", |_| 2);
@@ -21,6 +22,7 @@ fn test_off() {
2122

2223
#[test]
2324
#[cfg_attr(not(feature = "failpoints"), ignore)]
25+
#[cfg_attr(feature = "crate-isolation", ignore)]
2426
fn test_return() {
2527
let f = || {
2628
fail_point!("return", |s: Option<String>| s
@@ -38,6 +40,7 @@ fn test_return() {
3840

3941
#[test]
4042
#[cfg_attr(not(feature = "failpoints"), ignore)]
43+
#[cfg_attr(feature = "crate-isolation", ignore)]
4144
fn test_sleep() {
4245
let f = || {
4346
fail_point!("sleep");
@@ -55,6 +58,7 @@ fn test_sleep() {
5558
#[test]
5659
#[should_panic]
5760
#[cfg_attr(not(feature = "failpoints"), ignore)]
61+
#[cfg_attr(feature = "crate-isolation", ignore)]
5862
fn test_panic() {
5963
let f = || {
6064
fail_point!("panic");
@@ -65,6 +69,7 @@ fn test_panic() {
6569

6670
#[test]
6771
#[cfg_attr(not(feature = "failpoints"), ignore)]
72+
#[cfg_attr(feature = "crate-isolation", ignore)]
6873
fn test_print() {
6974
struct LogCollector(Arc<Mutex<Vec<String>>>);
7075
impl log::Log for LogCollector {
@@ -99,6 +104,7 @@ fn test_print() {
99104

100105
#[test]
101106
#[cfg_attr(not(feature = "failpoints"), ignore)]
107+
#[cfg_attr(feature = "crate-isolation", ignore)]
102108
fn test_pause() {
103109
let f = || {
104110
fail_point!("pause");
@@ -131,6 +137,7 @@ fn test_pause() {
131137
}
132138

133139
#[test]
140+
#[cfg_attr(feature = "crate-isolation", ignore)]
134141
fn test_yield() {
135142
let f = || {
136143
fail_point!("yield");
@@ -141,6 +148,7 @@ fn test_yield() {
141148

142149
#[test]
143150
#[cfg_attr(not(feature = "failpoints"), ignore)]
151+
#[cfg_attr(feature = "crate-isolation", ignore)]
144152
fn test_callback() {
145153
let f1 = || {
146154
fail_point!("cb");
@@ -162,6 +170,7 @@ fn test_callback() {
162170

163171
#[test]
164172
#[cfg_attr(not(feature = "failpoints"), ignore)]
173+
#[cfg_attr(feature = "crate-isolation", ignore)]
165174
fn test_delay() {
166175
let f = || fail_point!("delay");
167176
let timer = Instant::now();
@@ -172,6 +181,7 @@ fn test_delay() {
172181

173182
#[test]
174183
#[cfg_attr(not(feature = "failpoints"), ignore)]
184+
#[cfg_attr(feature = "crate-isolation", ignore)]
175185
fn test_freq_and_count() {
176186
let f = || {
177187
fail_point!("freq_and_count", |s: Option<String>| s
@@ -193,6 +203,7 @@ fn test_freq_and_count() {
193203

194204
#[test]
195205
#[cfg_attr(not(feature = "failpoints"), ignore)]
206+
#[cfg_attr(feature = "crate-isolation", ignore)]
196207
fn test_condition() {
197208
let f = |_enabled| {
198209
fail_point!("condition", _enabled, |_| 2);
@@ -214,3 +225,43 @@ fn test_list() {
214225
fail::cfg("list", "return").unwrap();
215226
assert!(fail::list().contains(&("list".to_string(), "return".to_string())));
216227
}
228+
229+
#[cfg(feature = "crate-isolation")]
230+
#[test]
231+
#[cfg_attr(not(feature = "failpoints"), ignore)]
232+
fn test_crate_isolation_return_parse() {
233+
let f = || {
234+
fail_point!("isolated_return_parse", |s: Option<String>| s
235+
.map_or(2, |s| s.parse().unwrap()));
236+
0
237+
};
238+
assert_eq!(f(), 0);
239+
240+
fail::cfg("isolated_return_parse", "return(1000)").unwrap();
241+
assert_eq!(f(), 0);
242+
243+
fail::cfg("fail::isolated_return_parse", "return(1000)").unwrap();
244+
assert_eq!(f(), 1000);
245+
246+
fail::cfg("fail::isolated_return_parse", "return").unwrap();
247+
assert_eq!(f(), 2);
248+
}
249+
250+
#[cfg(feature = "crate-isolation")]
251+
#[test]
252+
#[cfg_attr(not(feature = "failpoints"), ignore)]
253+
fn test_crate_isolation_return_default() {
254+
let f = || {
255+
fail_point!("isolated_return_default");
256+
0
257+
};
258+
assert_eq!(f(), 0);
259+
260+
fail::cfg("isolated_return_default", "return(1000)").unwrap();
261+
let result = std::panic::catch_unwind(f)
262+
.expect("callback should not panic as fail point should not be set");
263+
assert_eq!(result, 0);
264+
265+
fail::cfg("fail::isolated_return_default", "return(1000)").unwrap();
266+
std::panic::catch_unwind(f).expect_err("callback should panic as fail point is set");
267+
}

0 commit comments

Comments
 (0)