use super::ast::*; use crate::parse_input; use crate::{Config, into_spans}; use proptest::prelude::*; use proptest::proptest; proptest! { #[test] fn proptest_from_segments(original in Segments::arb(Token::arb())) { let stringified = original.to_string(); let parsed = parse_input(&stringified).unwrap(); let stringified_again = parsed.to_string(); assert_eq!(stringified, stringified_again, "parsed: `{parsed:#?}`"); } #[test] fn proptest_from_random_text(original in ".*") { let parsed = parse_input(&original).unwrap(); let stringified_again = parsed.to_string(); assert_eq!(original, stringified_again, "parsed: `{parsed:#?}`"); } #[test] fn proptest_into_spans(original in Segments::arb(Token::arb())) { let stringified = original.to_string(); let spans = into_spans(original, Config {collapse_space: false}); let spans_concatenated = spans.into_iter().map(|i| i.text).collect::(); assert_eq!(stringified, spans_concatenated); } } impl AnyString<'static> { #[cfg(test)] fn arb() -> impl Strategy { let prefix = "\\w*"; let ty = any::(); let contents = "\\w*"; let num_hashtags = 0usize..3; let suffix = "\\w*"; (prefix, ty, contents, num_hashtags, suffix).prop_map( |(prefix, ty, contents, num_hashtags, suffix)| Self { prefix: prefix.into(), ty, contents: contents.into(), num_hashtags, suffix: suffix.into(), }, ) } } impl PathSegment<'static> { #[cfg(test)] fn arb() -> impl Strategy { (any::(), "\\w*").prop_map(|(leading_separator, segment)| Self { leading_separator, segment: segment.into(), }) } } impl FileLocation<'static> { #[cfg(test)] fn arb() -> impl Strategy { use proptest::option::*; ("[0-9]{0,4}", of("[0-9]{0,4}")).prop_map(|(line, offset)| Self { line: line.into(), offset: offset.map(Into::into), }) } } impl Space<'static> { #[cfg(test)] fn arb() -> impl Strategy { " *".prop_map(|spaces| Self(spaces.into())) } } impl FileName<'static> { #[cfg(test)] fn arb() -> impl Strategy { use proptest::option::*; ( any::(), "\\w*", of(".{0,3}"), of(FileLocation::arb()), ) .prop_map( |(leading_separator, segment, ext_excluding_dot, location)| Self { leading_separator, segment: segment.into(), ext_excluding_dot: ext_excluding_dot.map(Into::into), location, }, ) } } impl Path<'static> { #[cfg(test)] fn arb() -> impl Strategy { use proptest::{char::*, collection::*, option::*}; ( of(range('A', 'Z')), vec(PathSegment::arb(), 0..3), FileName::arb(), ) .prop_map(|(drive_excluding_colon, segments, filename)| Self { drive_excluding_colon, segments, filename, }) } } impl Number<'static> { #[cfg(test)] fn arb() -> impl Strategy { prop_oneof![ any::().prop_map(|number| Self(number.to_string().into())), any::().prop_map(|number| Self(number.to_string().into())) ] } } impl Atom<'static> { #[cfg(test)] fn arb() -> impl Strategy { "[a-zA-Z]+".prop_map(|i| Self::Text(i.into())) } } impl Token<'static> { #[cfg(test)] fn arb() -> impl Strategy { let leaf = prop_oneof![ Just(Self::True), Just(Self::False), Just(Self::None), Path::arb().prop_map(Self::Path), AnyString::arb().prop_map(Self::String), Number::arb().prop_map(Self::Number), Atom::arb().prop_map(Self::Atom), ]; leaf.prop_recursive(4, 64, 16, |token| { Delimited::arb(token).prop_map(Self::Delimited).boxed() }) } } impl Delimited<'static> { #[cfg(test)] fn arb(token: impl Strategy>) -> impl Strategy { use proptest::option::*; ( of((Atom::arb(), Space::arb())), any::(), Segments::arb(token), ) .prop_map(|(prefix, delimiter, contents)| Self { prefix, delimiter, contents, }) } } impl Segment<'static> { #[cfg(test)] fn arb(token: impl Strategy>) -> impl Strategy { (Space::arb(), token).prop_map(|(leading_space, token)| Self { leading_space, token, }) } } impl Segments<'static> { #[cfg(test)] fn arb(token: impl Strategy>) -> impl Strategy { use proptest::collection::*; (vec(Segment::arb(token), 1..10), Space::arb()).prop_map(|(segments, trailing_space)| { Self { segments, trailing_space, } }) } }