| use cfg_expr::{ |
| error::Reason, |
| expr::{Predicate as P, TargetPredicate as TP}, |
| targets::*, |
| Expression, ParseError, |
| }; |
| |
| macro_rules! test_validate { |
| (ok [$($text:expr => [$($expected:expr),*$(,)?]),+$(,)?]) => { |
| $( |
| let val_expr = Expression::parse($text).unwrap(); |
| let mut preds = val_expr.predicates().enumerate(); |
| |
| $( |
| let actual = preds.next().unwrap(); |
| |
| similar_asserts::assert_eq!($expected, actual.1, "failed @ index {}", actual.0); |
| )* |
| |
| if let Some((_, additional)) = preds.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.to_owned(), |
| span: $range, |
| reason: Reason::$reason, |
| }; |
| |
| similar_asserts::assert_eq!(expected, act_err); |
| }; |
| |
| ($text:expr => $unexpected:expr; $range:expr) => { |
| let act_err = Expression::parse($text).unwrap_err(); |
| |
| let expected = ParseError { |
| original: $text.to_owned(), |
| span: $range, |
| reason: Reason::Unexpected($unexpected), |
| }; |
| |
| similar_asserts::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); |
| // Keys can't begin with a number |
| 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] |
| #[allow(clippy::cognitive_complexity)] |
| fn handles_single_predicate() { |
| test_validate!(ok [ |
| "cfg(key)" => [P::Flag("key")], |
| "unix" => [P::Target(TP::Family(Family::unix))], |
| "target_arch = \"mips\"" => [P::Target(TP::Arch(Arch::mips))], |
| "feature = \"awesome\"" => [P::Feature("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::Target(TP::HasAtomic(HasAtomic::Pointer))], |
| "target_has_atomic = \"4\"" => [P::Target(TP::HasAtomic(HasAtomic::IntegerSize(4)))], |
| "target_has_atomic = \"64\"" => [P::Target(TP::HasAtomic(HasAtomic::IntegerSize(64)))], |
| "target_has_atomic = \"128\" " => [P::Target(TP::HasAtomic(HasAtomic::IntegerSize(128)))], |
| "panic = \"unwind\"" => [P::Target(TP::Panic(Panic("unwind".into())))], |
| "panic = \"abort\"" => [P::Target(TP::Panic(Panic("abort".into())))], |
| ]); |
| } |
| |
| #[test] |
| fn handles_simple_funcs() { |
| test_validate!(ok [ |
| "any()" => [], |
| "all()" => [], |
| "not(key = \"value\")" => [P::KeyValue { key: "key", val: "value" }], |
| ]); |
| } |
| |
| #[test] |
| fn fails_invalid_funcs() { |
| 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()".to_owned(), |
| 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)".to_owned(), |
| 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)))".to_owned(), |
| 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" }], |
| ]); |
| } |