use gix_attributes::State;use gix_pathspec::{MagicSignature, SearchMode}; use crate::parse::{NormalizedPattern, check_against_baseline, check_valid_inputs}; #[test]fn repeated_matcher_keywords() { let input = vec![ (":(glob,glob)", pat_with_search_mode(SearchMode::PathAwareGlob)), (":(literal,literal)", pat_with_search_mode(SearchMode::Literal)), (":(top,top)", pat_with_sig(MagicSignature::TOP)), (":(icase,icase)", pat_with_sig(MagicSignature::ICASE)), (":(attr,attr)", pat_with_attrs(vec![])), (":!^(exclude,exclude)", pat_with_sig(MagicSignature::EXCLUDE)), ]; check_valid_inputs(input);} #[test]fn glob_negations_are_always_literal() { check_valid_inputs([("!a", pat_with_path("!a")), (r"\!a", pat_with_path(r"\!a"))]);} #[test]fn literal_default_prevents_parsing() { let pattern = gix_pathspec::parse( ":".as_bytes(), gix_pathspec::Defaults { signature: MagicSignature::EXCLUDE, search_mode: SearchMode::PathAwareGlob, literal: true, }, ) .expect("valid"); assert!(!pattern.is_nil()); assert_eq!(pattern.path(), ":"); assert!(matches!(pattern.search_mode, SearchMode::Literal)); let input = ":(literal)f[o][o]"; let pattern = gix_pathspec::parse( input.as_bytes(), gix_pathspec::Defaults { signature: MagicSignature::TOP, search_mode: SearchMode::Literal, literal: true, }, ) .expect("valid"); assert_eq!(pattern.path(), input, "no parsing happens at all"); assert!(matches!(pattern.search_mode, SearchMode::Literal)); let pattern = gix_pathspec::parse( input.as_bytes(), gix_pathspec::Defaults { signature: MagicSignature::TOP, search_mode: SearchMode::Literal, literal: false, }, ) .expect("valid"); assert_eq!(pattern.path(), "f[o][o]", "in literal default mode, we still parse"); assert!(matches!(pattern.search_mode, SearchMode::Literal));} #[test]fn there_is_no_pathspec_pathspec() { check_against_baseline(":"); let pattern = gix_pathspec::parse(":".as_bytes(), Default::default()).expect("valid"); assert!(pattern.is_nil()); let actual: NormalizedPattern = pattern.into(); assert_eq!(actual, pat_with_path("")); let pattern = gix_pathspec::parse( ":".as_bytes(), gix_pathspec::Defaults { signature: MagicSignature::EXCLUDE, search_mode: SearchMode::PathAwareGlob, literal: false, }, ) .expect("valid"); assert!(pattern.is_nil());} #[test]fn defaults_are_used() -> crate::Result { let defaults = gix_pathspec::Defaults { signature: MagicSignature::EXCLUDE, search_mode: SearchMode::Literal, literal: false, }; let p = gix_pathspec::parse(".".as_bytes(), defaults)?; assert_eq!(p.path(), "."); assert_eq!(p.signature, defaults.signature); assert_eq!(p.search_mode, defaults.search_mode); assert!(p.attributes.is_empty()); assert!(!p.is_nil()); Ok(())} #[test]fn literal_from_defaults_is_overridden_by_element_glob() -> crate::Result { let defaults = gix_pathspec::Defaults { search_mode: SearchMode::Literal, ..Default::default() }; let p = gix_pathspec::parse(":(glob)*override".as_bytes(), defaults)?; assert_eq!(p.path(), "*override"); assert_eq!(p.signature, MagicSignature::default()); assert_eq!(p.search_mode, SearchMode::PathAwareGlob, "this is the element override"); assert!(p.attributes.is_empty()); assert!(!p.is_nil()); Ok(())} #[test]fn glob_from_defaults_is_overridden_by_element_glob() -> crate::Result { let defaults = gix_pathspec::Defaults { search_mode: SearchMode::PathAwareGlob, ..Default::default() }; let p = gix_pathspec::parse(":(literal)*override".as_bytes(), defaults)?; assert_eq!(p.path(), "*override"); assert_eq!(p.signature, MagicSignature::default()); assert_eq!(p.search_mode, SearchMode::Literal, "this is the element override"); assert!(p.attributes.is_empty()); assert!(!p.is_nil()); Ok(())} #[test]fn empty_signatures() { let inputs = vec![ (".", pat_with_path(".")), ("some/path", pat_with_path("some/path")), (":some/path", pat_with_path("some/path")), (":()some/path", pat_with_path("some/path")), ("::some/path", pat_with_path("some/path")), (":::some/path", pat_with_path(":some/path")), (":():some/path", pat_with_path(":some/path")), ]; check_valid_inputs(inputs);} #[test]fn whitespace_in_pathspec() { let inputs = vec![ (" some/path", pat_with_path(" some/path")), ("some/ path", pat_with_path("some/ path")), ("some/path ", pat_with_path("some/path ")), (": some/path", pat_with_path(" some/path")), (": !some/path", pat_with_path(" !some/path")), (": :some/path", pat_with_path(" :some/path")), (": ()some/path", pat_with_path(" ()some/path")), ( ":! some/path", pat_with_path_and_sig(" some/path", MagicSignature::EXCLUDE), ), ( ":!!some/path", pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE), ), ]; check_valid_inputs(inputs);} #[test]fn short_signatures() { let inputs = vec![ (":/some/path", pat_with_path_and_sig("some/path", MagicSignature::TOP)), ( ":^some/path", pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE), ), ( ":!some/path", pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE), ), ( ":/!some/path", pat_with_path_and_sig("some/path", MagicSignature::TOP | MagicSignature::EXCLUDE), ), ( ":!/^/:some/path", pat_with_path_and_sig("some/path", MagicSignature::TOP | MagicSignature::EXCLUDE), ), ]; check_valid_inputs(inputs);} #[test]fn trailing_slash_is_turned_into_magic_signature_and_removed() { check_valid_inputs([ ("a/b/", pat_with_path_and_sig("a/b", MagicSignature::MUST_BE_DIR)), ("a/", pat_with_path_and_sig("a", MagicSignature::MUST_BE_DIR)), ]);} #[test]fn signatures_and_searchmodes() { let inputs = vec![ (":(top)", pat_with_sig(MagicSignature::TOP)), (":(icase)", pat_with_sig(MagicSignature::ICASE)), (":(attr)", pat_with_path("")), (":(exclude)", pat_with_sig(MagicSignature::EXCLUDE)), (":(literal)", pat_with_search_mode(SearchMode::Literal)), (":(glob)", pat_with_search_mode(SearchMode::PathAwareGlob)), ( ":(top,exclude)", pat_with_sig(MagicSignature::TOP | MagicSignature::EXCLUDE), ), ( ":(icase,literal)", pat("", MagicSignature::ICASE, SearchMode::Literal, vec![]), ), ( ":!(literal)some/*path", pat("some/*path", MagicSignature::EXCLUDE, SearchMode::Literal, vec![]), ), ( ":(top,literal,icase,attr,exclude)some/path", pat( "some/path", MagicSignature::TOP | MagicSignature::EXCLUDE | MagicSignature::ICASE, SearchMode::Literal, vec![], ), ), ( ":(top,glob,icase,attr,exclude)some/path", pat( "some/path", MagicSignature::TOP | MagicSignature::EXCLUDE | MagicSignature::ICASE, SearchMode::PathAwareGlob, vec![], ), ), ]; check_valid_inputs(inputs);} #[test]fn attributes_in_signature() { let inputs = vec![ (":(attr:someAttr)", pat_with_attrs(vec![("someAttr", State::Set)])), ( ":(attr:!someAttr)", pat_with_attrs(vec![("someAttr", State::Unspecified)]), ), (":(attr:-someAttr)", pat_with_attrs(vec![("someAttr", State::Unset)])), ( ":(attr:someAttr=value)", pat_with_attrs(vec![("someAttr", State::Value("value".into()))]), ), ( ":(attr:a=one b=)", pat_with_attrs(vec![("a", State::Value("one".into())), ("b", State::Value("".into()))]), ), ( ":(attr:a= b=two)", pat_with_attrs(vec![("a", State::Value("".into())), ("b", State::Value("two".into()))]), ), ( ":(attr:a=one b=two)", pat_with_attrs(vec![ ("a", State::Value("one".into())), ("b", State::Value("two".into())), ]), ), ( ":(attr:a=one b=two)", pat_with_attrs(vec![ ("a", State::Value("one".into())), ("b", State::Value("two".into())), ]), ), ( ":(attr:someAttr anotherAttr)", pat_with_attrs(vec![("someAttr", State::Set), ("anotherAttr", State::Set)]), ), ]; check_valid_inputs(inputs);} #[test]fn attributes_with_escape_chars_in_state_values() { let inputs = vec![ ( r":(attr:v=one\-)", pat_with_attrs(vec![("v", State::Value(r"one-".into()))]), ), ( r":(attr:v=one\_)", pat_with_attrs(vec![("v", State::Value(r"one_".into()))]), ), ( r":(attr:v=one\,)", pat_with_attrs(vec![("v", State::Value(r"one,".into()))]), ), ( r":(attr:v=one\,two\,three)", pat_with_attrs(vec![("v", State::Value(r"one,two,three".into()))]), ), ( r":(attr:a=\d b= c=\d)", pat_with_attrs(vec![ ("a", State::Value(r"d".into())), ("b", State::Value(r"".into())), ("c", State::Value(r"d".into())), ]), ), ]; check_valid_inputs(inputs);} fn pat_with_path(path: &str) -> NormalizedPattern { pat_with_path_and_sig(path, MagicSignature::empty())} fn pat_with_path_and_sig(path: &str, signature: MagicSignature) -> NormalizedPattern { pat(path, signature, SearchMode::ShellGlob, vec![])} fn pat_with_sig(signature: MagicSignature) -> NormalizedPattern { pat("", signature, SearchMode::ShellGlob, vec![])} fn pat_with_attrs(attrs: Vec<(&'static str, State)>) -> NormalizedPattern { pat("", MagicSignature::empty(), SearchMode::ShellGlob, attrs)} fn pat_with_search_mode(search_mode: SearchMode) -> NormalizedPattern { pat("", MagicSignature::empty(), search_mode, vec![])} fn pat( path: &str, signature: MagicSignature, search_mode: SearchMode, attributes: Vec<(&str, State)>,) -> NormalizedPattern { NormalizedPattern { path: path.into(), signature, search_mode, attributes: attributes .into_iter() .map(|(attr, state)| (attr.into(), state)) .collect(), }}
use gix_attributes::State;
use gix_pathspec::{MagicSignature, SearchMode};
use crate::parse::{NormalizedPattern, check_against_baseline, check_valid_inputs};
#[test]
fn repeated_matcher_keywords() {
let input = vec![
(":(glob,glob)", pat_with_search_mode(SearchMode::PathAwareGlob)),
(":(literal,literal)", pat_with_search_mode(SearchMode::Literal)),
(":(top,top)", pat_with_sig(MagicSignature::TOP)),
(":(icase,icase)", pat_with_sig(MagicSignature::ICASE)),
(":(attr,attr)", pat_with_attrs(vec![])),
(":!^(exclude,exclude)", pat_with_sig(MagicSignature::EXCLUDE)),
];
check_valid_inputs(input);
}
#[test]
fn glob_negations_are_always_literal() {
check_valid_inputs([("!a", pat_with_path("!a")), (r"\!a", pat_with_path(r"\!a"))]);
}
#[test]
fn literal_default_prevents_parsing() {
let pattern = gix_pathspec::parse(
":".as_bytes(),
gix_pathspec::Defaults {
signature: MagicSignature::EXCLUDE,
search_mode: SearchMode::PathAwareGlob,
literal: true,
},
)
.expect("valid");
assert!(!pattern.is_nil());
assert_eq!(pattern.path(), ":");
assert!(matches!(pattern.search_mode, SearchMode::Literal));
let input = ":(literal)f[o][o]";
let pattern = gix_pathspec::parse(
input.as_bytes(),
gix_pathspec::Defaults {
signature: MagicSignature::TOP,
search_mode: SearchMode::Literal,
literal: true,
},
)
.expect("valid");
assert_eq!(pattern.path(), input, "no parsing happens at all");
assert!(matches!(pattern.search_mode, SearchMode::Literal));
let pattern = gix_pathspec::parse(
input.as_bytes(),
gix_pathspec::Defaults {
signature: MagicSignature::TOP,
search_mode: SearchMode::Literal,
literal: false,
},
)
.expect("valid");
assert_eq!(pattern.path(), "f[o][o]", "in literal default mode, we still parse");
assert!(matches!(pattern.search_mode, SearchMode::Literal));
}
#[test]
fn there_is_no_pathspec_pathspec() {
check_against_baseline(":");
let pattern = gix_pathspec::parse(":".as_bytes(), Default::default()).expect("valid");
assert!(pattern.is_nil());
let actual: NormalizedPattern = pattern.into();
assert_eq!(actual, pat_with_path(""));
let pattern = gix_pathspec::parse(
":".as_bytes(),
gix_pathspec::Defaults {
signature: MagicSignature::EXCLUDE,
search_mode: SearchMode::PathAwareGlob,
literal: false,
},
)
.expect("valid");
assert!(pattern.is_nil());
}
#[test]
fn defaults_are_used() -> crate::Result {
let defaults = gix_pathspec::Defaults {
signature: MagicSignature::EXCLUDE,
search_mode: SearchMode::Literal,
literal: false,
};
let p = gix_pathspec::parse(".".as_bytes(), defaults)?;
assert_eq!(p.path(), ".");
assert_eq!(p.signature, defaults.signature);
assert_eq!(p.search_mode, defaults.search_mode);
assert!(p.attributes.is_empty());
assert!(!p.is_nil());
Ok(())
}
#[test]
fn literal_from_defaults_is_overridden_by_element_glob() -> crate::Result {
let defaults = gix_pathspec::Defaults {
search_mode: SearchMode::Literal,
..Default::default()
};
let p = gix_pathspec::parse(":(glob)*override".as_bytes(), defaults)?;
assert_eq!(p.path(), "*override");
assert_eq!(p.signature, MagicSignature::default());
assert_eq!(p.search_mode, SearchMode::PathAwareGlob, "this is the element override");
assert!(p.attributes.is_empty());
assert!(!p.is_nil());
Ok(())
}
#[test]
fn glob_from_defaults_is_overridden_by_element_glob() -> crate::Result {
let defaults = gix_pathspec::Defaults {
search_mode: SearchMode::PathAwareGlob,
..Default::default()
};
let p = gix_pathspec::parse(":(literal)*override".as_bytes(), defaults)?;
assert_eq!(p.path(), "*override");
assert_eq!(p.signature, MagicSignature::default());
assert_eq!(p.search_mode, SearchMode::Literal, "this is the element override");
assert!(p.attributes.is_empty());
assert!(!p.is_nil());
Ok(())
}
#[test]
fn empty_signatures() {
let inputs = vec![
(".", pat_with_path(".")),
("some/path", pat_with_path("some/path")),
(":some/path", pat_with_path("some/path")),
(":()some/path", pat_with_path("some/path")),
("::some/path", pat_with_path("some/path")),
(":::some/path", pat_with_path(":some/path")),
(":():some/path", pat_with_path(":some/path")),
];
check_valid_inputs(inputs);
}
#[test]
fn whitespace_in_pathspec() {
let inputs = vec![
(" some/path", pat_with_path(" some/path")),
("some/ path", pat_with_path("some/ path")),
("some/path ", pat_with_path("some/path ")),
(": some/path", pat_with_path(" some/path")),
(": !some/path", pat_with_path(" !some/path")),
(": :some/path", pat_with_path(" :some/path")),
(": ()some/path", pat_with_path(" ()some/path")),
(
":! some/path",
pat_with_path_and_sig(" some/path", MagicSignature::EXCLUDE),
),
(
":!!some/path",
pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE),
),
];
check_valid_inputs(inputs);
}
#[test]
fn short_signatures() {
let inputs = vec![
(":/some/path", pat_with_path_and_sig("some/path", MagicSignature::TOP)),
(
":^some/path",
pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE),
),
(
":!some/path",
pat_with_path_and_sig("some/path", MagicSignature::EXCLUDE),
),
(
":/!some/path",
pat_with_path_and_sig("some/path", MagicSignature::TOP | MagicSignature::EXCLUDE),
),
(
":!/^/:some/path",
pat_with_path_and_sig("some/path", MagicSignature::TOP | MagicSignature::EXCLUDE),
),
];
check_valid_inputs(inputs);
}
#[test]
fn trailing_slash_is_turned_into_magic_signature_and_removed() {
check_valid_inputs([
("a/b/", pat_with_path_and_sig("a/b", MagicSignature::MUST_BE_DIR)),
("a/", pat_with_path_and_sig("a", MagicSignature::MUST_BE_DIR)),
]);
}
#[test]
fn signatures_and_searchmodes() {
let inputs = vec![
(":(top)", pat_with_sig(MagicSignature::TOP)),
(":(icase)", pat_with_sig(MagicSignature::ICASE)),
(":(attr)", pat_with_path("")),
(":(exclude)", pat_with_sig(MagicSignature::EXCLUDE)),
(":(literal)", pat_with_search_mode(SearchMode::Literal)),
(":(glob)", pat_with_search_mode(SearchMode::PathAwareGlob)),
(
":(top,exclude)",
pat_with_sig(MagicSignature::TOP | MagicSignature::EXCLUDE),
),
(
":(icase,literal)",
pat("", MagicSignature::ICASE, SearchMode::Literal, vec![]),
),
(
":!(literal)some/*path",
pat("some/*path", MagicSignature::EXCLUDE, SearchMode::Literal, vec![]),
),
(
":(top,literal,icase,attr,exclude)some/path",
pat(
"some/path",
MagicSignature::TOP | MagicSignature::EXCLUDE | MagicSignature::ICASE,
SearchMode::Literal,
vec![],
),
),
(
":(top,glob,icase,attr,exclude)some/path",
pat(
"some/path",
MagicSignature::TOP | MagicSignature::EXCLUDE | MagicSignature::ICASE,
SearchMode::PathAwareGlob,
vec![],
),
),
];
check_valid_inputs(inputs);
}
#[test]
fn attributes_in_signature() {
let inputs = vec![
(":(attr:someAttr)", pat_with_attrs(vec![("someAttr", State::Set)])),
(
":(attr:!someAttr)",
pat_with_attrs(vec![("someAttr", State::Unspecified)]),
),
(":(attr:-someAttr)", pat_with_attrs(vec![("someAttr", State::Unset)])),
(
":(attr:someAttr=value)",
pat_with_attrs(vec![("someAttr", State::Value("value".into()))]),
),
(
":(attr:a=one b=)",
pat_with_attrs(vec![("a", State::Value("one".into())), ("b", State::Value("".into()))]),
),
(
":(attr:a= b=two)",
pat_with_attrs(vec![("a", State::Value("".into())), ("b", State::Value("two".into()))]),
),
(
":(attr:a=one b=two)",
pat_with_attrs(vec![
("a", State::Value("one".into())),
("b", State::Value("two".into())),
]),
),
(
":(attr:a=one b=two)",
pat_with_attrs(vec![
("a", State::Value("one".into())),
("b", State::Value("two".into())),
]),
),
(
":(attr:someAttr anotherAttr)",
pat_with_attrs(vec![("someAttr", State::Set), ("anotherAttr", State::Set)]),
),
];
check_valid_inputs(inputs);
}
#[test]
fn attributes_with_escape_chars_in_state_values() {
let inputs = vec![
(
r":(attr:v=one\-)",
pat_with_attrs(vec![("v", State::Value(r"one-".into()))]),
),
(
r":(attr:v=one\_)",
pat_with_attrs(vec![("v", State::Value(r"one_".into()))]),
),
(
r":(attr:v=one\,)",
pat_with_attrs(vec![("v", State::Value(r"one,".into()))]),
),
(
r":(attr:v=one\,two\,three)",
pat_with_attrs(vec![("v", State::Value(r"one,two,three".into()))]),
),
(
r":(attr:a=\d b= c=\d)",
pat_with_attrs(vec![
("a", State::Value(r"d".into())),
("b", State::Value(r"".into())),
("c", State::Value(r"d".into())),
]),
),
];
check_valid_inputs(inputs);
}
fn pat_with_path(path: &str) -> NormalizedPattern {
pat_with_path_and_sig(path, MagicSignature::empty())
}
fn pat_with_path_and_sig(path: &str, signature: MagicSignature) -> NormalizedPattern {
pat(path, signature, SearchMode::ShellGlob, vec![])
}
fn pat_with_sig(signature: MagicSignature) -> NormalizedPattern {
pat("", signature, SearchMode::ShellGlob, vec![])
}
fn pat_with_attrs(attrs: Vec<(&'static str, State)>) -> NormalizedPattern {
pat("", MagicSignature::empty(), SearchMode::ShellGlob, attrs)
}
fn pat_with_search_mode(search_mode: SearchMode) -> NormalizedPattern {
pat("", MagicSignature::empty(), search_mode, vec![])
}
fn pat(
path: &str,
signature: MagicSignature,
search_mode: SearchMode,
attributes: Vec<(&str, State)>,
) -> NormalizedPattern {
NormalizedPattern {
path: path.into(),
signature,
search_mode,
attributes: attributes
.into_iter()
.map(|(attr, state)| (attr.into(), state))
.collect(),
}
}