use crate::cfg_expr::{
error::{ParseError, Reason},
expr::{Expression, Predicate as P},
};
macro_rules! test_validate {
(ok [$($text:expr => [$($expected:expr),*$(,)?]),+$(,)?]) => {
$(
let val_expr = Expression::parse($text).unwrap();
let mut predicates = val_expr.predicates().enumerate();
$(
let actual = predicates.next().unwrap();
assert_eq!($expected, actual.1, "failed @ index {}", actual.0);
)*
if let Some((_, additional)) = predicates.next() {
assert!(false, "found additional requirement {additional:?}");
}
)+
};
}
macro_rules! err {
($text:expr => $reason:ident @ $range:expr) => {
let act_err = Expression::parse($text).unwrap_err();
let expected = ParseError { original: $text.into(), span: $range, reason: Reason::$reason };
assert_eq!(expected, act_err);
};
($text:expr => $unexpected:expr; $range:expr) => {
let act_err = Expression::parse($text).unwrap_err();
let expected = ParseError {
original: $text.into(),
span: $range,
reason: Reason::Unexpected($unexpected),
};
assert_eq!(expected, act_err);
};
}
#[test]
fn fails_empty() {
err!("" => Empty @ 0..0);
err!(" " => Empty @ 0..1);
err!("\n\t\n" => Empty @ 0..3);
}
#[test]
fn fails_malformed() {
err!("," => &["<key>", "all", "any", "not"]; 0..1);
err!("8" => &["<key>", "all", "any", "not"]; 0..1);
err!("=" => &["<key>", "all", "any", "not"]; 0..1);
err!("(" => &["<key>", "all", "any", "not"]; 0..1);
err!("key =" => &["\"<value>\""]; 5..5);
err!("key1, key2" => MultipleRootPredicates @ 0..10);
err!("key1, key2, " => MultipleRootPredicates @ 0..16);
err!("key1 = \"v\", key2" => MultipleRootPredicates @ 0..16);
}
#[test]
fn fails_unbalanced_parens() {
err!("not(key" => UnclosedParens @ 3..7);
err!("key)" => UnopenedParens @ 3..4);
err!("foo (" => &["=", ",", ")"]; 4..5);
}
#[test]
fn fails_unbalanced_quotes() {
err!("key = \"value" => UnclosedQuotes @ 6..12);
err!("key = \"" => UnclosedQuotes @ 6..7);
err!("key = \"value, key = \"value\"" => &[",", ")"]; 21..26);
err!("all(key = \"value), key = \"value\"" => &[",", ")"]; 26..31);
err!("not(key = \"value)" => UnclosedQuotes @ 10..17);
}
#[test]
fn handles_single_predicate() {
test_validate!(ok [
"cfg(key)" => [P::Flag("key")],
"unix" => [P::Flag("unix")],
"target_arch = \"mips\"" => [P::KeyValue{ key: "target_arch", val: "mips" }],
"feature = \"awesome\"" => [P::KeyValue{ key: "feature", val: "awesome" }],
"_key" => [P::Flag("_key")],
" key" => [P::Flag("key")],
" key " => [P::Flag("key")],
" key = \"val\"" => [P::KeyValue{ key: "key", val: "val" }],
"key=\"\"" => [P::KeyValue{ key: "key", val: "" }],
" key=\"7\" " => [P::KeyValue{ key: "key", val: "7" }],
"key = \"7 q\" " => [P::KeyValue{ key: "key", val: "7 q" }],
"target_has_atomic = \"ptr\"" => [P::KeyValue{ key: "target_has_atomic", val: "ptr" }],
"target_has_atomic = \"4\"" => [P::KeyValue{ key: "target_has_atomic", val: "4" }],
"target_has_atomic = \"64\"" => [P::KeyValue{ key: "target_has_atomic", val: "64" }],
"target_has_atomic = \"128\" " => [P::KeyValue{ key: "target_has_atomic", val: "128" }],
"panic = \"unwind\"" => [P::KeyValue{ key: "panic", val: "unwind" }],
"panic = \"abort\"" => [P::KeyValue{ key: "panic", val: "abort" }],
]);
}
#[test]
fn handles_simple_fns() {
test_validate!(ok [
"any()" => [],
"all()" => [],
"not(key = \"value\")" => [P::KeyValue { key: "key", val: "value" }],
]);
}
#[test]
fn fails_invalid_fns() {
err!("nope()" => &["=", ",", ")"]; 4..5);
err!("all(nope())" => &["=", ",", ")"]; 8..9);
err!("any(,)" => &["<key>", ")", "all", "any", "not"]; 4..5);
err!("blah(key)" => &["=", ",", ")"]; 4..5);
}
#[test]
fn ensures_not_has_one_predicate() {
assert_eq!(Expression::parse("not()").unwrap_err(), ParseError {
original: "not()".into(),
span: 0..5,
reason: Reason::InvalidNot(0)
});
assert_eq!(Expression::parse("not(key_one, key_two)").unwrap_err(), ParseError {
original: "not(key_one, key_two)".into(),
span: 0..21,
reason: Reason::InvalidNot(2),
});
assert_eq!(Expression::parse("any(not(not(key_one, key_two)))").unwrap_err(), ParseError {
original: "any(not(not(key_one, key_two)))".into(),
span: 8..29,
reason: Reason::InvalidNot(2),
});
test_validate!(ok [
"not(key)" => [P::Flag("key")],
"not(key,)" => [P::Flag("key")],
"not(key = \"value\")" => [P::KeyValue { key: "key", val: "value" }],
"not(key = \"value\",)" => [P::KeyValue { key: "key", val: "value" }],
"not(not(not(key = \"value\",)))" => [P::KeyValue { key: "key", val: "value" }],
]);
}