(define-module (test-parser) #:use-module (geesh parser) #:use-module (srfi srfi-64) #:use-module (tests automake)) ;;; Commentary: ;;; ;;; Tests for the parser module. ;;; ;;; Code: (define (parse str) (call-with-input-string str read-sh)) (test-begin "reader") ;; Commands and lists (test-equal "Parses simple command" '( "echo" "foo") (parse "echo foo")) (test-equal "Parses command lists" '(( "echo" "foo") ( "echo" "bar")) (parse "echo foo; echo bar")) (test-equal "Parses asynchronous command lists" '(( ( "echo" "foo")) ( ( "echo" "bar"))) (parse "echo foo& echo bar&")) (test-equal "Parses mixed command lists" '(( ( "echo" "foo")) ( "echo" "bar")) (parse "echo foo& echo bar")) (test-equal "Parses commands with assignments" '( (("FOO" "bar")) "echo" ( "FOO")) (parse "FOO=bar echo $FOO")) (test-equal "Parses commands with default redirects" '( ((> 1 "bar")) ( "echo" "foo")) (parse "echo foo > bar")) (test-equal "Parses commands with specific redirects" '( ((< 5 "bar")) ( "echo" "foo")) (parse "echo foo 5< bar")) (test-equal "Parses commands with dup redirects" '( ((>& 1 3)) ( "exec")) (parse "exec >&3")) (test-equal "Parses commands with close redirects" '( ((<& 3 -)) ( "exec")) (parse "exec 3<&-")) (test-equal "Parses assignments" '( (("FOO" "bar"))) (parse "FOO=bar")) ;; Boolean expressions (test-equal "Parses disjunctions" '( ( "echo" "foo") ( "echo" "bar")) (parse "echo foo || echo bar")) (test-equal "Parses conjunctions" '( ( "echo" "foo") ( "echo" "bar")) (parse "echo foo && echo bar")) (test-equal "Parses conjunction than disjunction" '( ( ( "echo" "foo") ( "echo" "bar")) ( "echo" "baz")) (parse "echo foo && echo bar || echo baz")) (test-equal "Parses disjunction than conjunction" '( ( ( "echo" "foo") ( "echo" "bar")) ( "echo" "baz")) (parse "echo foo || echo bar && echo baz")) ;; Pipelines (test-equal "Parses pipelines" '( (( "cat" "foo.txt") ( "grep" "bar"))) (parse "cat foo.txt | grep bar")) ;; Brace groups and subshells (test-equal "Parses brace groups" '( ( "echo" "foo") ( "echo" "bar")) (parse "{ echo foo echo bar }")) (test-equal "Parses subshells" '( ( ( "echo" "foo") ( "echo" "bar"))) (parse "(echo foo; echo bar)")) ;; For loops (test-equal "Parses for loops over parameters without seperator" '( ("x" ( "@")) ( "echo" ( "x"))) (parse "for x do echo $x done")) (test-equal "Parses for loops over parameters with seperator" '( ("x" ( "@")) ( "echo" ( "x"))) (parse "for x; do echo $x done")) (test-equal "Parses for loops over parameters with \"in\"" '( ("x" ( "@")) ( "echo" ( "x"))) (parse "for x in; do echo $x done")) (test-equal "Parses for loops over word lists" '( ("x" ("foo" "bar" "baz")) ( "echo" ( "x"))) (parse "for x in foo bar baz; do echo $x done")) ;; Case statements (test-equal "Parses case statements with final seperator" '( ( "foo") (("bar") ( "echo" "bar"))) (parse "case $foo in bar) echo bar ;; esac")) (test-equal "Parses case statements without final seperator" '( ( "foo") (("bar") ( "echo" "bar"))) (parse "case $foo in bar) echo bar esac")) (test-equal "Parses empty case statements" '( ( "foo")) (parse "case $foo in esac")) (test-equal "Parses case statements with empty case item" '( ( "foo") (("bar") #f)) (parse "case $foo in bar) esac")) (test-equal "Parses case statements with multiple case items" '( ( "foo") (("bar") ( "echo" "bar")) (("baz") ( "echo" "baz"))) (parse "case $foo in bar) echo bar ;; baz) echo baz esac")) (test-equal "Parses case statements with compound patterns" '( ( "foo") (("bar" "baz") ( "echo" ( "bar or baz")))) (parse "case $foo in bar | baz) echo 'bar or baz' ;; esac")) ;; If statements (test-equal "Parses one-branch if statements" '( (( "[" ( "foo") "=" "bar" "]") ( "echo" "bar"))) (parse "if [ $foo = bar ] then echo bar fi")) (test-equal "Parses two-branch if statements" '( (( "[" ( "foo") "=" "bar" "]") ( "echo" "bar")) ( ( "echo" "baz"))) (parse "if [ $foo = bar ] then echo bar else echo baz fi")) (test-equal "Parses multi-branch if statements" '( (( "[" ( "foo") "=" "bar" "]") ( "echo" "bar")) (( "[" ( "foo") "=" "baz" "]") ( "echo" "baz")) ( ( "echo" "quux"))) (parse "if [ $foo = bar ] then echo bar elif [ $foo = baz ] then echo baz else echo quux fi")) ;; While and until loops (test-equal "Parses while loops" '( ( "is-foo-time") ( "foo")) (parse "while is-foo-time do foo done")) (test-equal "Parses until loops" '( ( "is-no-longer-foo-time") ( "foo")) (parse "until is-no-longer-foo-time do foo done")) ;; Functions (test-equal "Parses functions" '( ("foo") ( "echo" "foo")) (parse "foo() { echo foo }")) ;; Nested commands (test-equal "Parses bracketed command substitions" '( "echo" ( ( "foo")) ( ( "bar"))) (parse "echo $(foo) $(bar)")) (test-equal "Parses nested bracketed command substitions" '( "echo" ( ( "foo" ( ( "bar"))))) (parse "echo $(foo $(bar))")) (test-equal "Parses empty bracketed command substitions" '( "echo" ( #f)) (parse "echo $()")) (test-equal "Parses multiline bracketed command substitions" '( "echo" ( (( "foo") ( "bar")))) (parse "echo $(foo bar)")) (test-equal "Parses backquoted command substitions" '( "echo" ( ( "foo")) ( ( "bar"))) (parse "echo `foo` `bar`")) (test-equal "Parses nested backquoted command substitions" '( "echo" ( ( "foo" ( ( "bar"))))) (parse "echo `foo \\`bar\\``")) (test-equal "Parses empty backquoted command substitions" '( "echo" ( #f)) (parse "echo ``")) (test-equal "Parses multiline backquoted command substitions" '( "echo" ( (( "foo") ( "bar")))) (parse "echo `foo bar`")) ;; Other tests (test-assert "Returns EOF on EOF" (eof-object? (parse ""))) (test-equal "Parses one statement at a time" '(( "echo" "foo") ( "echo" "bar")) (call-with-input-string "echo foo echo bar" (lambda (port) (list (read-sh port) (read-sh port))))) (test-end)