mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-28 20:34:07 +08:00
Support thread-safe feature-flag-dependant tests
This also allows scoped feature tests that makes testing feature flags thread-safe. As in you can guarantee that the test actually has the correct feature flag value, regardless of which other tests are running in parallell.
This commit is contained in:
parent
f1cd43d58b
commit
63b23713f2
|
@ -106,12 +106,24 @@ pub const METADATA: &[FeatureMetadata] = &[
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
thread_local!(
|
||||||
|
#[cfg(any(test, feature = "fish-ffi-tests"))]
|
||||||
|
static LOCAL_FEATURES: std::cell::RefCell<Option<Features>> = std::cell::RefCell::new(None);
|
||||||
|
);
|
||||||
|
|
||||||
/// The singleton shared feature set.
|
/// The singleton shared feature set.
|
||||||
static FEATURES: Features = Features::new();
|
static FEATURES: Features = Features::new();
|
||||||
|
|
||||||
/// Perform a feature test on the global set of features.
|
/// Perform a feature test on the global set of features.
|
||||||
pub fn test(flag: FeatureFlag) -> bool {
|
pub fn test(flag: FeatureFlag) -> bool {
|
||||||
|
#[cfg(any(test, feature = "fish-ffi-tests"))]
|
||||||
|
{
|
||||||
|
LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).test(flag))
|
||||||
|
}
|
||||||
|
#[cfg(not(any(test, feature = "fish-ffi-tests")))]
|
||||||
|
{
|
||||||
FEATURES.test(flag)
|
FEATURES.test(flag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use test as feature_test;
|
pub use test as feature_test;
|
||||||
|
@ -119,7 +131,7 @@ pub use test as feature_test;
|
||||||
/// Set a flag.
|
/// Set a flag.
|
||||||
#[cfg(any(test, feature = "fish-ffi-tests"))]
|
#[cfg(any(test, feature = "fish-ffi-tests"))]
|
||||||
pub(self) fn set(flag: FeatureFlag, value: bool) {
|
pub(self) fn set(flag: FeatureFlag, value: bool) {
|
||||||
FEATURES.set(flag, value);
|
LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).set(flag, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a comma-separated feature-flag string, updating ourselves with the values.
|
/// Parses a comma-separated feature-flag string, updating ourselves with the values.
|
||||||
|
@ -127,7 +139,20 @@ pub(self) fn set(flag: FeatureFlag, value: bool) {
|
||||||
/// The special group name "all" may be used for those who like to live on the edge.
|
/// The special group name "all" may be used for those who like to live on the edge.
|
||||||
/// Unknown features are silently ignored.
|
/// Unknown features are silently ignored.
|
||||||
pub fn set_from_string<'a>(str: impl Into<&'a wstr>) {
|
pub fn set_from_string<'a>(str: impl Into<&'a wstr>) {
|
||||||
FEATURES.set_from_string(str);
|
let wstr: &wstr = str.into();
|
||||||
|
#[cfg(any(test, feature = "fish-ffi-tests"))]
|
||||||
|
{
|
||||||
|
LOCAL_FEATURES.with(|fc| {
|
||||||
|
fc.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&FEATURES)
|
||||||
|
.set_from_string(wstr)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[cfg(not(any(test, feature = "fish-ffi-tests")))]
|
||||||
|
{
|
||||||
|
FEATURES.set_from_string(wstr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Features {
|
impl Features {
|
||||||
|
@ -151,8 +176,7 @@ impl Features {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[widestrs]
|
#[widestrs]
|
||||||
fn set_from_string<'a>(&self, str: impl Into<&'a wstr>) {
|
fn set_from_string<'a>(&self, str: &wstr) {
|
||||||
let str: &wstr = str.into();
|
|
||||||
let whitespace = "\t\n\0x0B\0x0C\r "L.as_char_slice();
|
let whitespace = "\t\n\0x0B\0x0C\r "L.as_char_slice();
|
||||||
for entry in str.as_char_slice().split(|c| *c == ',') {
|
for entry in str.as_char_slice().split(|c| *c == ',') {
|
||||||
if entry.is_empty() {
|
if entry.is_empty() {
|
||||||
|
@ -193,6 +217,24 @@ impl Features {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "fish-ffi-tests"))]
|
||||||
|
pub fn scoped_test(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
|
||||||
|
LOCAL_FEATURES.with(|fc| {
|
||||||
|
assert!(
|
||||||
|
fc.borrow().is_none(),
|
||||||
|
"scoped_test() does not support nesting"
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = Features::new();
|
||||||
|
f.set(flag, value);
|
||||||
|
*fc.borrow_mut() = Some(f);
|
||||||
|
|
||||||
|
test_fn();
|
||||||
|
|
||||||
|
*fc.borrow_mut() = None;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[widestrs]
|
#[widestrs]
|
||||||
fn test_feature_flags() {
|
fn test_feature_flags() {
|
||||||
|
@ -216,3 +258,26 @@ fn test_feature_flags() {
|
||||||
"stderr-nocaret"L
|
"stderr-nocaret"L
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scoped() {
|
||||||
|
scoped_test(FeatureFlag::qmark_noglob, true, || {
|
||||||
|
assert!(test(FeatureFlag::qmark_noglob));
|
||||||
|
});
|
||||||
|
|
||||||
|
set(FeatureFlag::qmark_noglob, true);
|
||||||
|
|
||||||
|
scoped_test(FeatureFlag::qmark_noglob, false, || {
|
||||||
|
assert!(!test(FeatureFlag::qmark_noglob));
|
||||||
|
});
|
||||||
|
|
||||||
|
set(FeatureFlag::qmark_noglob, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_nested_scopes_not_supported() {
|
||||||
|
scoped_test(FeatureFlag::qmark_noglob, true, || {
|
||||||
|
scoped_test(FeatureFlag::qmark_noglob, false, || {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user