536 lines · 16.0 KB · Rust
RawDownload
use bstr::BString;use gix_attributes::{StateRef, parse, state::ValueRef};use gix_glob::pattern::Mode;use gix_testtools::fixture_bytes; #[test]fn byte_order_marks_are_no_patterns() {    assert_eq!(        line("\u{feff}hello"),        (pattern(r"hello", Mode::NO_SUB_DIR, None), vec![], 1)    );    assert_eq!(        line("\u{feff}\"hello\""),        (pattern(r"hello", Mode::NO_SUB_DIR, None), vec![], 1)    );} #[test]fn line_numbers_are_counted_correctly() {    let input = fixture_bytes("attributes/various.txt");    assert_eq!(        try_lines(&String::from_utf8(input).unwrap()).unwrap(),        vec![            (pattern(r"*.[oa]", Mode::NO_SUB_DIR, Some(0)), vec![set("c")], 2),            (                pattern(r"*.html", Mode::NO_SUB_DIR | Mode::ENDS_WITH, Some(0)),                vec![set("a"), value("b", "c")],                5            ),            (pattern(r"!foo.html", Mode::NO_SUB_DIR, None), vec![set("x")], 8),            (pattern(r"#a/path", Mode::empty(), None), vec![unset("a")], 10),            (                pattern(r"*", Mode::ABSOLUTE | Mode::NO_SUB_DIR | Mode::ENDS_WITH, Some(0)),                vec![unspecified("b")],                11            ),        ]    );} #[test]fn line_endings_can_be_windows_or_unix() {    assert_eq!(        try_lines("unix\nwindows\r\nlast").unwrap(),        vec![            (pattern(r"unix", Mode::NO_SUB_DIR, None), vec![], 1),            (pattern(r"windows", Mode::NO_SUB_DIR, None), vec![], 2),            (pattern(r"last", Mode::NO_SUB_DIR, None), vec![], 3)        ]    );} #[test]fn comment_lines_are_ignored_as_well_as_empty_ones() {    assert!(gix_attributes::parse(b"# hello world").next().is_none());    assert!(gix_attributes::parse(b"# \"hello world\"").next().is_none());    assert!(        gix_attributes::parse(b" \t\r# \"hello world\"").next().is_none(),        "also behind leading whitespace"    );    assert!(gix_attributes::parse(b"\n\r\n\t\t   \n").next().is_none());} #[test]fn leading_whitespace_is_ignored() {    assert_eq!(line(" \r\tp"), (pattern(r"p", Mode::NO_SUB_DIR, None), vec![], 1));    assert_eq!(line(" \r\t\"p\""), (pattern(r"p", Mode::NO_SUB_DIR, None), vec![], 1));} #[test]fn quotes_separate_attributes_even_without_whitespace() {    assert_eq!(        line(r#""path"a b"#),        (pattern(r"path", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1)    );} #[test]fn comment_can_be_escaped_like_gitignore_or_quoted() {    assert_eq!(        line(r"\#hello"),        (pattern(r"#hello", Mode::NO_SUB_DIR, None), vec![], 1),        "undocumented, but definitely works"    );    assert_eq!(        line("\"# hello\""),        (pattern(r"# hello", Mode::NO_SUB_DIR, None), vec![], 1)    );} #[test]fn exclamation_marks_must_be_escaped_or_error_unlike_gitignore() {    assert_eq!(        line(r"\!hello"),        (pattern(r"!hello", Mode::NO_SUB_DIR, None), vec![], 1)    );    assert!(matches!(        try_line(r"!hello"),        Err(parse::Error::PatternNegation { line_number: 1, .. })    ));    assert!(lenient_lines(r#"!hello"#).is_empty());    assert!(        matches!(            try_line(r#""!hello""#),            Err(parse::Error::PatternNegation { line_number: 1, .. }),        ),        "even in quotes they trigger…"    );    assert!(lenient_lines(r#""!hello""#).is_empty());    assert_eq!(        line(r#""\\!hello""#),        (pattern(r"!hello", Mode::NO_SUB_DIR, None), vec![], 1),        "…and must be double-escaped, once to get through quote, then to get through parse ignore line"    );} #[test]fn broken_quoting_falls_back_to_the_raw_text() {    assert_eq!(        line(r#""\!hello""#),        (pattern(r#""\!hello""#, Mode::NO_SUB_DIR, Some(1)), vec![], 1),        "an invalid escape leaves the quotes in place, so the leading `!` no longer negates, \         and the backslash goes on to escape it for the matcher"    );    assert_eq!(        line(r#""abc"#),        (pattern(r#""abc"#, Mode::NO_SUB_DIR, None), vec![], 1),        "so does a quote that is never closed"    );    assert_eq!(        line(r#""abc def"#),        (pattern(r#""abc"#, Mode::NO_SUB_DIR, None), vec![set("def")], 1),        "the raw text is then split on blanks like any unquoted pattern"    );} #[test]fn custom_macros_can_be_differentiated() {    let output = line(r#"[attr]foo bar -baz"#);    match output.0 {        parse::Kind::Pattern(_) => unreachable!(),        parse::Kind::Macro(name) => {            assert_eq!(                (name.as_str(), output.1, output.2),                (r"foo", vec![set("bar"), unset("baz")], 1)            );        }    }     let output = line(r#""[attr]foo" bar -baz"#);    match output.0 {        parse::Kind::Pattern(_) => unreachable!(),        parse::Kind::Macro(name) => {            assert_eq!(                (name.as_str(), output.1, output.2),                (r"foo", vec![set("bar"), unset("baz")], 1),                "it works after unquoting even, making it harder to denote a file name with [attr] prefix"            );        }    }} #[test]fn parsing_continues_even_in_the_face_of_invalid_lines_when_using_leniency() {    assert_eq!(        lenient_lines("[attr]-bad\np good\n[attr]-bad\np good2"),        vec![            (pattern(r"p", Mode::NO_SUB_DIR, None), vec![set("good")], 2),            (pattern(r"p", Mode::NO_SUB_DIR, None), vec![set("good2")], 4),        ]    );} #[test]fn macros_can_be_empty() {    let output = line(r#"[attr]macro"#);    match output.0 {        parse::Kind::Pattern(_) => unreachable!(),        parse::Kind::Macro(name) => {            assert_eq!((name.as_str(), output.1, output.2), (r"macro", vec![], 1));        }    }} #[test]fn the_macro_prefix_without_a_name_is_a_pattern() {    for input in ["[attr]", "\"[attr]\""] {        let output = line(input);        assert!(            matches!(output.0, parse::Kind::Pattern(_)),            "{input:?} is an ordinary pattern in Git"        );    }} #[test]fn custom_macros_must_be_valid_attribute_names() {    assert!(matches!(        try_line(r"[attr]-prefixdash"),        Err(parse::Error::MacroName { line_number: 1, .. })    ));    assert!(lenient_lines(r"[attr]-prefixdash").is_empty());    assert!(matches!(        try_line(r"[attr]!exclamation"),        Err(parse::Error::MacroName { line_number: 1, .. })    ));    assert!(matches!(        try_line(r"[attr]assignment=value"),        Err(parse::Error::MacroName { line_number: 1, .. })    ));    assert!(matches!(        try_line(r"[attr]你好"),        Err(parse::Error::MacroName { line_number: 1, .. })    ));    assert!(lenient_lines(r"[attr]你好").is_empty());} #[test]fn attribute_names_must_not_begin_with_dash_and_must_be_ascii_only() {    assert!(matches!(        try_line(r"p !-a"),        Err(parse::Error::AttributeName { line_number: 1, .. })    ));    assert!(lenient_lines(r"p !-a").is_empty());    assert!(        matches!(            try_line(r#"p !!a"#),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "exclamation marks aren't allowed either"    );    assert!(lenient_lines(r#"p !!a"#).is_empty());    assert!(        matches!(            try_line(r#"p 你好"#),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "nor is utf-8 encoded characters - gitoxide could consider to relax this when established"    );    assert!(lenient_lines(r#"p 你好"#).is_empty());} #[test]fn attribute_names_must_not_be_empty() {    assert!(        matches!(            try_line(r"p text =lf"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "a blank in front of the equals sign leaves the assignment without a name"    );    assert!(        matches!(            try_line(r"p ="),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "an assignment that is nothing but an equals sign has no name either"    );    assert!(        matches!(            try_line(r"p -"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "prefixes need a name to apply to"    );    assert!(        matches!(            try_line(r"p !"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "the unspecified prefix needs one as well"    );    assert!(        gix_attributes::NameRef::try_from(bstr::BStr::new(b"")).is_err(),        "names can't be created empty either, which `attr_name_valid()` rejects via `namelen <= 0`"    );} #[test]fn attribute_names_must_not_use_the_reserved_builtin_prefix() {    assert!(        matches!(            try_line(r"p builtin_objectmode"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "Git reserves 'builtin_' for built-in attributes and drops lines that assign to it"    );    assert!(lenient_lines(r"p builtin_objectmode").is_empty());    assert!(        matches!(            try_line(r"p -builtin_objectmode"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "the prefix is checked after '-' and '!' are stripped, just like in `parse_attr()`"    );    assert!(        matches!(            try_line(r"[attr]builtin_macro -text"),            Err(parse::Error::MacroName { line_number: 1, .. })        ),        "macro names are checked against the reserved namespace as well"    );    assert_eq!(        line(r"p builtin"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("builtin")], 1),        "only the exact 'builtin_' prefix is reserved…"    );    assert_eq!(        line(r"p x_builtin_y"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("x_builtin_y")], 1),        "…and only at the beginning of the name"    );} #[test]fn attributes_are_parsed_behind_various_whitespace_characters() {    assert_eq!(        line(r#"p a b"#),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1),        "behind space"    );    assert_eq!(        line(r#""p" a b"#),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1),        "behind space"    );    assert_eq!(        line("p\ta\tb"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1),        "behind tab"    );    assert_eq!(        line("\"p\"\ta\tb"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1),        "behind tab"    );    assert_eq!(        line("p \t a \t b"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1),        "behind a mix of space and tab"    );    assert_eq!(        line("\"p\" \t a \t b"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a"), set("b")], 1),        "behind a mix of space and tab"    );} #[test]fn only_ascii_blanks_separate_attributes() {    assert_eq!(        line("p text=auto\u{a0}eol=lf"),        (            pattern("p", Mode::NO_SUB_DIR, None),            vec![value("text", "auto\u{a0}eol=lf")],            1        ),        "a non-breaking space belongs to the value it appears in"    );    assert!(        matches!(            try_line("p text\u{a0}eol=lf"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "in a name it makes the whole name invalid"    );    assert!(        matches!(            try_line("p a\u{b}b"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "a vertical tab is part of the name, not a separator"    );    assert!(        matches!(            try_line("p a\u{c}b"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "a form feed is part of the name, not a separator"    );    assert!(        matches!(            try_line("p a\u{2028}b"),            Err(parse::Error::AttributeName { line_number: 1, .. })        ),        "vertical tabs, form feeds and unicode line separators aren't blanks either"    );} #[test]fn attributes_come_in_different_flavors_due_to_prefixes() {    assert_eq!(        line(r#"p set -unset !unspecified -set"#),        (            pattern("p", Mode::NO_SUB_DIR, None),            vec![set("set"), unset("unset"), unspecified("unspecified"), unset("set")],            1        ),        "the parser doesn't care about double-mentions either"    );} #[test]fn attributes_can_have_values() {    assert_eq!(        line(r#"p a=one b=2 c=你好 "#),        (            pattern("p", Mode::NO_SUB_DIR, None),            vec![value("a", "one"), value("b", "2"), value("c", "你好")],            1        ),        "only non-whitespace ascii values are allowed, no escaping or anything fancy is possible there"    );} #[test]fn attributes_can_have_illformed_utf8() {    assert_eq!(        byte_line(b"p a=one b=\xC3\x28\x41 c=d "),        (            pattern("p", Mode::NO_SUB_DIR, None),            vec![value("a", "one"), byte_value("b", b"\xC3\x28\x41"), value("c", "d")],            1        ),        "illformed UTF8 is fully supported"    );} #[test]fn attributes_see_state_adjustments_over_value_assignments() {    assert_eq!(        line(r#"p set -unset=a !unspecified=b"#),        (            pattern("p", Mode::NO_SUB_DIR, None),            vec![set("set"), unset("unset"), unspecified("unspecified")],            1        )    );} #[test]fn whitespace_around_patterns_can_be_quoted() {    assert_eq!(        line("\" p \" a \r\t"),        (pattern(" p ", Mode::NO_SUB_DIR, None), vec![set("a")], 1),    );} #[test]fn trailing_whitespace_in_attributes_is_ignored() {    assert_eq!(        line("p a \r\t"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a")], 1),    );    assert_eq!(        line("\"p\" a \r\t"),        (pattern("p", Mode::NO_SUB_DIR, None), vec![set("a")], 1),    );} type ExpandedAttribute<'a> = (parse::Kind, Vec<(BString, gix_attributes::StateRef<'a>)>, usize); fn set(attr: &str) -> (BString, StateRef<'_>) {    (attr.into(), StateRef::Set)} fn unset(attr: &str) -> (BString, StateRef<'_>) {    (attr.into(), StateRef::Unset)} fn unspecified(attr: &str) -> (BString, StateRef<'_>) {    (attr.into(), StateRef::Unspecified)} fn value<'b>(attr: &str, value: &'b str) -> (BString, StateRef<'b>) {    (attr.into(), StateRef::Value(value.into()))} fn byte_value<'b>(attr: &str, value: &'b [u8]) -> (BString, StateRef<'b>) {    (attr.into(), StateRef::Value(ValueRef::from_bytes(value)))} fn pattern(name: &str, flags: gix_glob::pattern::Mode, first_wildcard_pos: Option<usize>) -> parse::Kind {    parse::Kind::Pattern(gix_glob::Pattern {        text: name.into(),        mode: flags,        first_wildcard_pos,    })} fn try_line(input: &str) -> Result<ExpandedAttribute<'_>, parse::Error> {    let mut lines = gix_attributes::parse(input.as_bytes());    let res = expand(lines.next().unwrap())?;    assert!(lines.next().is_none(), "expected only one line");    Ok(res)} fn line(input: &str) -> ExpandedAttribute<'_> {    try_line(input).unwrap()}

Lines 1–500 of 536.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t