use expect_test::{expect, Expect}; use oyster_parser::ParseTree; fn check(s: &str, expect: Expect) { let actual = ParseTree::from(s); expect.assert_debug_eq(&actual); } #[test] fn empty() { check( "", expect![[r#" Tree { kind: Program, children: [], } "#]], ); } #[test] fn word() { check( "word", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 4, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn word_with_escape() { check( r"hello\#world", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 5, }, Leaf { kind: EscapedChar, len: 2, }, Leaf { kind: PlainText, len: 5, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn unicode() { check( "谚语", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn simple_command() { check( "echo hi", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 4, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 2, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn semicolon() { check( "hello; hi", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 5, }, ], }, ], }, Leaf { kind: Semicolon, len: 1, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 2, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn comment() { let source = r"# a # b"; check( source, expect![[r#" Tree { kind: Program, children: [ Leaf { kind: Comment, len: 3, }, Leaf { kind: Newlines, len: 1, }, Leaf { kind: Comment, len: 3, }, ], } "#]], ); } #[test] fn inline_comment() { check( "whoami # hello", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, Leaf { kind: Comment, len: 7, }, ], }, ], }, ], } "#]], ); } #[test] fn pipeline() { check( "whoami | cat", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, ], }, Leaf { kind: Pipe, len: 1, }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 3, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn multiline_pipeline() { let source = r"whoami | wc -l"; check( source, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, ], }, Leaf { kind: Pipe, len: 1, }, Leaf { kind: Newlines, len: 1, }, Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 2, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 2, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn comment_in_pipeline() { let source = r"whoami | # comment wc -l"; check( source, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, ], }, Leaf { kind: Pipe, len: 1, }, Leaf { kind: Whitespace, len: 1, }, Leaf { kind: Comment, len: 9, }, Leaf { kind: Newlines, len: 1, }, Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 2, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 2, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn reject_leading_pipe() { let source = r"whoami | cat"; check( source, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, ], }, ], }, Leaf { kind: Newlines, len: 1, }, Error { kind: UnexpectedPipe, len: 1, }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 3, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn reject_trailing_pipe() { check( "whoami |", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, ], }, Leaf { kind: Pipe, len: 1, }, Error { kind: UnexpectedEof, len: 0, }, ], }, ], } "#]], ); } #[test] fn reject_pipe_semicolon() { let source = "whoami | ; cat"; check( source, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, ], }, Leaf { kind: Pipe, len: 1, }, Leaf { kind: Whitespace, len: 1, }, Error { kind: UnexpectedSemicolon, len: 1, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 3, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn reject_double_pipe() { check( "whoami | | cat", expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, Leaf { kind: Whitespace, len: 1, }, ], }, Leaf { kind: Pipe, len: 1, }, Leaf { kind: Whitespace, len: 1, }, Error { kind: UnexpectedPipe, len: 1, }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 3, }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn double_quote_string() { check( r#""hello world""#, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Tree { kind: DQuotedString, children: [ Leaf { kind: DoubleQuote, len: 1, }, Leaf { kind: PlainText, len: 11, }, Leaf { kind: DoubleQuote, len: 1, }, ], }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn escaped_char_in_double_quotes() { check( r#""hello \" world""#, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Tree { kind: DQuotedString, children: [ Leaf { kind: DoubleQuote, len: 1, }, Leaf { kind: PlainText, len: 6, }, Leaf { kind: EscapedChar, len: 2, }, Leaf { kind: PlainText, len: 6, }, Leaf { kind: DoubleQuote, len: 1, }, ], }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn unterminated_double_quotes() { check( r#""hello world"#, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Tree { kind: DQuotedString, children: [ Leaf { kind: DoubleQuote, len: 1, }, Leaf { kind: PlainText, len: 11, }, Error { kind: UnexpectedEof, len: 0, }, ], }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn command_substitution() { check( r#"echo (whoami)"#, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 4, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Word, children: [ Tree { kind: CommandSubstitution, children: [ Leaf { kind: OpeningParenthesis, len: 1, }, Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, ], }, ], }, Leaf { kind: ClosingParenthesis, len: 1, }, ], }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn quoted_command_substitution() { let source = r#"echo "(whoami) ""#; check( source, expect![[r#" Tree { kind: Program, children: [ Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 4, }, ], }, Leaf { kind: Whitespace, len: 1, }, Tree { kind: Word, children: [ Tree { kind: DQuotedString, children: [ Leaf { kind: DoubleQuote, len: 1, }, Tree { kind: CommandSubstitution, children: [ Leaf { kind: OpeningParenthesis, len: 1, }, Tree { kind: Pipeline, children: [ Tree { kind: Command, children: [ Tree { kind: Word, children: [ Leaf { kind: PlainText, len: 6, }, ], }, ], }, ], }, Leaf { kind: ClosingParenthesis, len: 1, }, ], }, Leaf { kind: PlainText, len: 9, }, Leaf { kind: DoubleQuote, len: 1, }, ], }, ], }, ], }, ], }, ], } "#]], ); } #[test] fn empty_pipeline() { check( ";", expect![[r#" Tree { kind: Program, children: [ Leaf { kind: Semicolon, len: 1, }, ], } "#]], ); }