Byronreview1825e853 months agoHistory
1240 lines · 38.9 KB · Rust
RawDownload
use super::*; #[test]#[cfg(target_pointer_width = "64")]fn input_size_is_limited_by_span_representation() {    assert!(ensure_supported_input_size(u32::MAX as usize).is_ok());    let actual = u32::MAX as usize + 1;    let err = ensure_supported_input_size(actual).expect_err("inputs above the span limit must be rejected");    assert_eq!(err.line_number(), 1);    assert!(err.remaining_data().is_empty());    assert_eq!(        err.to_string(),        format!(            "Configuration input is {actual} bytes large, but at most {} bytes are supported",            u32::MAX        )    );} /// Extend basic types to get their owned versions, without leaking this into production code.trait ToOwnedIn {    type Owned;    fn to_owned_in(self, backing: &[u8]) -> Self::Owned;} impl ToOwnedIn for Span {    type Owned = bstr::BString;     fn to_owned_in(self, backing: &[u8]) -> Self::Owned {        self.to_bstring_in(backing)    }} impl ToOwnedIn for crate::parse::MaybeDecoded {    type Owned = bstr::BString;     fn to_owned_in(self, backing: &[u8]) -> Self::Owned {        self.value_in(backing).to_owned()    }} impl ToOwnedIn for Event {    type Owned = crate::parse::tests::util::OwnedEvent;     fn to_owned_in(self, backing: &[u8]) -> Self::Owned {        crate::parse::tests::util::own_event(&self, backing)    }} impl ToOwnedIn for Comment {    type Owned = crate::parse::tests::util::OwnedComment;     fn to_owned_in(self, backing: &[u8]) -> Self::Owned {        crate::parse::tests::util::OwnedComment {            tag: self.tag,            text: self.text.to_bstring_in(backing),        }    }} impl ToOwnedIn for crate::parse::section::HeaderData {    type Owned = crate::parse::tests::util::OwnedHeader;     fn to_owned_in(self, backing: &[u8]) -> Self::Owned {        crate::parse::tests::util::own_header(&self, backing)    }} impl ToOwnedIn for crate::parse::SectionData {    type Owned = crate::parse::tests::util::OwnedSection;     fn to_owned_in(self, backing: &[u8]) -> Self::Owned {        crate::parse::tests::util::OwnedSection {            header: self.header.to_owned_in(backing),            events: self                .events                .into_iter()                .map(|event| event.to_owned_in(backing))                .collect(),        }    }} trait ParsePeekExt<'a, T> {    fn parse_peek(self, input: &'a [u8]) -> Result<(&'a [u8], T), ()>;    fn parse(self, input: &'a [u8]) -> Result<T, ()>;} impl<'a, T, F> ParsePeekExt<'a, T> for Fwhere    F: FnOnce(&mut &'a [u8]) -> Result<T, ()>,{    fn parse_peek(self, mut input: &'a [u8]) -> Result<(&'a [u8], T), ()> {        let value = self(&mut input)?;        Ok((input, value))    }     fn parse(self, input: &'a [u8]) -> Result<T, ()> {        let (remaining, value) = self.parse_peek(input)?;        if remaining.is_empty() { Ok(value) } else { Err(()) }    }} /// Adapt a parser that produces spans into a separate backing allocation for convenient assertions.////// [`Self::parse_peek()`] copies `input` into a temporary backing vector, invokes the parser with/// that backing and a cursor into it, and resolves the result into its test-only owned equivalent/// before dropping the backing. The returned remainder still refers to the original `input`, with/// its start determined from the number of bytes consumed in the backing.trait ParsePeekWithBackingExt<'a, T> {    fn parse_peek(self, input: &'a [u8]) -> Result<(&'a [u8], <T as ToOwnedIn>::Owned), ()>    where        T: ToOwnedIn;} impl<'a, T, F> ParsePeekWithBackingExt<'a, T> for Fwhere    F: for<'backing, 'input> FnOnce(&'backing [u8], &'input mut &'backing [u8]) -> Result<T, ()>,    T: ToOwnedIn,{    fn parse_peek(self, input: &'a [u8]) -> Result<(&'a [u8], T::Owned), ()> {        let backing = input.to_vec();        let mut current = &backing[..];        let value = self(&backing, &mut current)?;        let consumed = input.len() - current.len();        Ok((&input[consumed..], value.to_owned_in(&backing)))    }} mod config {    use super::from_bytes;    use crate::parse::tests::util::{        OwnedEvent as Event, comment_event, name_event, newline_custom_event, newline_event, own_event, value_event,        whitespace_event,    };     #[test]    fn key_value_before_first_section_is_accepted() {        let mut events = Vec::new();        let backing = b"a = b\n";        from_bytes(backing, &mut |event| events.push(event)).unwrap();        let events: Vec<_> = events.iter().map(|event| own_event(event, backing)).collect();        assert_eq!(            events,            vec![                name_event("a"),                whitespace_event(" "),                Event::KeyValueSeparator,                whitespace_event(" "),                value_event("b"),                newline_event(),            ],            "Git accepts this and reports `a=b`, as git_parse_source() parses alphabetic keys even before any section"        );    }     #[test]    fn comments_do_not_consume_the_carriage_return_of_crlf() {        let mut events = Vec::new();        let backing = b"; comment\r\n";        from_bytes(backing, &mut |event| events.push(event)).expect("valid comment");        let events: Vec<_> = events.iter().map(|event| own_event(event, backing)).collect();        assert_eq!(            events,            vec![comment_event(';', " comment"), newline_custom_event("\r\n")],            "the complete CRLF sequence must belong to the newline event"        );         let mut events = Vec::new();        let backing = b"; comment\r";        from_bytes(backing, &mut |event| events.push(event)).expect("a bare carriage return is comment text");        let events: Vec<_> = events.iter().map(|event| own_event(event, backing)).collect();        assert_eq!(            events,            vec![comment_event(';', " comment\r")],            "a carriage return without a following line feed must remain part of the comment"        );    }} mod section_headers {    use super::{ParsePeekWithBackingExt, section_header};    use crate::parse::tests::util::{fully_consumed, section_header as parsed_section_header};     #[test]    fn no_subsection() {        assert_eq!(            section_header.parse_peek(b"[hello]").unwrap(),            fully_consumed(parsed_section_header("hello", None)),        );    }     #[test]    fn empty_section_name_without_quoted_subsection_is_rejected() {        assert!(section_header.parse_peek(b"[]").is_err());    }     #[test]    fn modern_subsection() {        assert_eq!(            section_header.parse_peek(br#"[hello "world"]"#).unwrap(),            fully_consumed(parsed_section_header("hello", (" ", "world"))),        );    }     #[test]    fn empty_section_name_with_quoted_subsection_is_accepted() {        assert_eq!(            section_header.parse_peek(br#"[ "core"]"#).unwrap(),            fully_consumed(crate::parse::tests::util::OwnedHeader {                name: "".into(),                separator: Some(" ".into()),                subsection_name: Some("core".into()),            }),            "Git accepts this as an empty section name with `core` as subsection, yielding keys like `.core.bare`; gix does this too for compatibility"        );    }     #[test]    fn quoted_section_name_without_leading_space_is_rejected() {        assert!(            section_header.parse_peek(br#"["core"]"#).is_err(),            "Git rejects this as a bad config line"        );    }     #[test]    fn escaped_subsection() {        assert_eq!(            section_header.parse_peek(br#"[hello "foo\\bar\""]"#).unwrap(),            fully_consumed(parsed_section_header("hello", (" ", r#"foo\bar""#))),        );    }     #[test]    fn deprecated_subsection() {        assert_eq!(            section_header.parse_peek(br#"[hello.world]"#).unwrap(),            fully_consumed(parsed_section_header("hello", (".", "world")))        );        assert_eq!(            section_header.parse_peek(br#"[Hello.World]"#).unwrap(),            fully_consumed(parsed_section_header("Hello", (".", "World")))        );    }     #[test]    fn empty_legacy_subsection_name() {        assert_eq!(            section_header.parse_peek(br#"[hello-world.]"#).unwrap(),            fully_consumed(parsed_section_header("hello-world", (".", "")))        );    }     #[test]    fn empty_modern_subsection_name() {        assert_eq!(            section_header.parse_peek(br#"[hello ""]"#).unwrap(),            fully_consumed(parsed_section_header("hello", (" ", "")))        );    }     #[test]    fn backslashes_in_subsections_do_not_escape_newlines_or_tabs() {        assert_eq!(            section_header.parse_peek(br#"[hello "single \ \\ \t \n \0"]"#).unwrap(),            fully_consumed(parsed_section_header("hello", (" ", r"single  \ t n 0")))        );    }     #[test]    fn newline_in_header() {        assert!(section_header.parse_peek(b"[hello\n]").is_err());    }     #[test]    fn newline_in_sub_section() {        assert!(section_header.parse_peek(b"[hello \"hello\n\"]").is_err());    }     #[test]    fn null_byt_in_sub_section() {        assert_eq!(            section_header.parse_peek(b"[hello \"hello\0\"]").unwrap(),            fully_consumed(parsed_section_header("hello", (" ", "hello\0"))),            "Git accepts this because get_extended_base_var() only rejects newline in quoted subsections"        );    }     #[test]    fn escaped_newline_in_sub_section() {        assert!(section_header.parse_peek(b"[hello \"hello\\\n\"]").is_err());    }     #[test]    fn eof_after_escape_in_sub_section() {        assert!(section_header.parse_peek(br#"[hello "hello\"#).is_err());    }     #[test]    fn missing_closing_bracket_after_quoted_subsection() {        assert!(section_header.parse_peek(br#"[hello "world""#).is_err());    }     #[test]    fn whitespace_before_closing_bracket_after_quoted_subsection() {        assert!(            section_header.parse_peek(br#"[hello "world" ]"#).is_err(),            "yes, Git fails here, too!"        );    }     #[test]    fn null_byte_in_header() {        assert!(section_header.parse_peek(b"[hello\0]").is_err());    }     #[test]    fn invalid_characters_in_section() {        assert!(section_header.parse_peek(b"[$]").is_err());    }    #[test]    fn invalid_characters_in_legacy_sub_section() {        assert!(section_header.parse_peek(b"[hello.$]").is_err());        assert!(section_header.parse_peek(b"[hello. world]").is_err());    }     #[test]    fn right_brace_in_subsection_name() {        assert_eq!(            section_header.parse_peek(br#"[hello "]"]"#).unwrap(),            fully_consumed(parsed_section_header("hello", (" ", "]")))        );    }} mod sub_section {    use bstr::ByteSlice;     use super::{ParsePeekWithBackingExt, quoted_sub_section};     #[test]    fn simple() {        let actual = quoted_sub_section.parse_peek(br#"name""#).unwrap().1;        assert_eq!(actual.as_bstr(), "name");    }     #[test]    fn escapes_are_unescaped() {        let actual = quoted_sub_section.parse_peek(br#"\x\t\n\0\\\"""#).unwrap().1;        assert_eq!(actual.as_bstr(), r#"xtn0\""#);    }} mod config_name {    use super::{ParsePeekExt, config_name};    use crate::parse::tests::util::fully_consumed;     #[test]    fn just_name() {        assert_eq!(config_name.parse_peek(b"name").unwrap(), fully_consumed("name".into()));    }     #[test]    fn must_start_with_alphabetic() {        assert!(config_name.parse_peek(b"4aaa").is_err());        assert!(config_name.parse_peek(b"-aaa").is_err());    }     #[test]    fn only_a_subset_of_characters_is_allowed() {        assert!(config_name.parse(b"Name$_").is_err());        assert!(config_name.parse(b"other#").is_err());    }     #[test]    fn cannot_be_empty() {        assert!(config_name.parse_peek(b"").is_err());    }} mod section {    use crate::parse::{        Event as RawEvent,        error::ParseNode,        tests::util::{            OwnedEvent as Event, OwnedSection as SectionData, comment_event, fully_consumed, name_event,            newline_custom_event, newline_event, own_event, own_header, section_header as parsed_section_header,            value_done_event, value_event, value_not_done_event, whitespace_event,        },    };     fn section<'a>(input: &'a [u8], node: &mut ParseNode) -> Result<(&'a [u8], SectionData), ()> {        let backing = input.to_vec();        let mut current = &backing[..];        let mut header = None;        let mut events = Vec::new();        super::section(&backing, &mut current, node, &mut |e| match &header {            None => {                header = Some(e);            }            Some(_) => events.push(e),        })        .map(|_| {            let consumed = input.len() - current.len();            (                &input[consumed..],                SectionData {                    header: match header.expect("header set") {                        RawEvent::SectionHeader(header) => own_header(&header, &backing),                        _ => unreachable!("unexpected"),                    },                    events: events.iter().map(|event| own_event(event, &backing)).collect(),                },            )        })    }     #[test]    fn empty_value_with_windows_newlines() {        let mut node = ParseNode::SectionHeader;        assert_eq!(            section(b"[a] k = \r\n", &mut node).unwrap(),            fully_consumed(SectionData {                header: parsed_section_header("a", None),                events: vec![                    whitespace_event(" "),                    name_event("k"),                    whitespace_event(" "),                    Event::KeyValueSeparator,                    whitespace_event(" "),                    value_event(""),                    newline_custom_event("\r\n")                ]            }),        );    }     #[test]    fn simple_value_with_windows_newlines() {        let mut node = ParseNode::SectionHeader;        assert_eq!(            section(b"[a] k = v\r\n", &mut node).unwrap(),            fully_consumed(SectionData {                header: parsed_section_header("a", None),                events: vec![                    whitespace_event(" "),                    name_event("k"),                    whitespace_event(" "),                    Event::KeyValueSeparator,                    whitespace_event(" "),                    value_event("v"),                    newline_custom_event("\r\n")                ]            }),        );        assert_eq!(            section(b"[a] k = \r\n", &mut node).unwrap(),            fully_consumed(SectionData {                header: parsed_section_header("a", None),                events: vec![                    whitespace_event(" "),                    name_event("k"),                    whitespace_event(" "),                    Event::KeyValueSeparator,                    whitespace_event(" "),                    value_event(""),                    newline_custom_event("\r\n")                ]            }),        );    }     #[test]    fn empty_section() {        let mut node = ParseNode::SectionHeader;        assert_eq!(            section(b"[test]", &mut node).unwrap(),            fully_consumed(SectionData {                header: parsed_section_header("test", None),                events: Default::default()            }),        );    }     #[test]    fn simple_section() {        let mut node = ParseNode::SectionHeader;        let section_data = br#"[hello]            a = b            c            d = "lol""#;        assert_eq!(            section(section_data, &mut node).unwrap(),            fully_consumed(SectionData {                header: parsed_section_header("hello", None),                events: vec![                    newline_event(),                    whitespace_event("            "),                    name_event("a"),

Lines 1–500 of 1240.