#lang racket (require rackunit "spec-ast.rkt" "parser.rkt" "match.rkt") (require/expose "match.rkt" (find-switch invoke-cmd-fn match-command snoc)) (provide match-tests) (define add-function (lambda (#:start-date [start-date #f] #:due-date [due-date #f] #:note [note #f] #:edit-note [edit-note #f] #:url [url #f] . summary) `(add (start-date ,start-date) (due-date ,due-date) (note ,note) (edit-note ,edit-note) (url ,url) (summary ,summary)))) (define list-function (lambda (#:show-completed [show-completed #f] #:show-future [show-future #f]) `(list (show-completed ,show-completed) (show-future ,show-future)))) (define show-function (lambda (id id2 #:single-arg [single-arg #f] #:multi-arg [multi-arg #f]) `(show (id ,id) (id2 ,id2) (single-arg ,single-arg) (multi-arg ,multi-arg)))) (define complete-function (lambda (id) `(complete (id ,id)))) (define update-function (lambda (id #:summary [summary #f] #:start-date [start-date #f] #:due-date [due-date #f] #:note [note #f] #:edit-note [edit-note #f] #:url [url #f] #:incomplete [incomplete #f]) `(update (id ,id) (summary ,summary) (start-date ,start-date) (due-date ,due-date) (note ,note) (edit-note ,edit-note) (url ,url) (incomplete ,incomplete)))) (define test-command-spec (parse-spec `(tasks "Task management System" [add "Add a new task" "Long add help" summary #:once-each [("-s" "--start-date") #:start-date "Date on which task begins" date] [("-d" "--due-date") #:due-date "Task's due date" date] #:once-any [("-n" "--note") #:note "Name of file containing note for task" filename] [("-e" "--edit-note") #:edit-note "Bring up $EDITOR for task's note"] #:once-each [("-u" "--url") #:url "Associate a URL with the task" url] ,add-function] [list "List existing tasks" "Long LIST help" () #:once-each [("-c" "--show-completed") #:show-completed "Show completed as well as pending tasks"] [("-f" "--show-future") #:show-future "Show tasks not yet started as well as normal tasks"] ,list-function] [show "Show detailed information about a specific task" "long SHOW help" (id id2) #:multi [("-s" "--single-arg") #:single-arg "single arg multi switch" arg] [("-m" "--multi-arg") #:multi-arg "multiple arg multi switch" arg1 arg2] ,show-function] [complete "Mark task as completed" "long COMPLETE help" (id) ,complete-function] [update "Updates a task" "long UPDATE help" (id) #:once-each [("-m" "--summary") #:summary "Update summary" summary-text] [("-s" "--start-date") #:start-date "Update start date" start-date] [("-d" "--due-date") #:due-date "Update due date" due-date] #:once-any [("-n" "--note") #:note "Replace the task's note with the contents of a file" filename] [("-e" "--edit-note") #:edit-note "Load the task's note into $EDITOR"] #:once-each [("-u" "--url") #:url "Replace the task's URL" url] [("-i" "--incomplete") #:incomplete "Remove the task's \"completed\" marking"] ,update-function]))) (define match-spec-tests (test-suite "match-spec" (test-equal? "add" (match-spec test-command-spec '("add" "summary text" "-s" "2011-01-30" "-d" "2011-03-01" "-e" "-u" "http://www.example.com/")) '(add (start-date ("2011-01-30")) (due-date ("2011-03-01")) (note #f) (edit-note ()) (url ("http://www.example.com/")) (summary ("summary text")))) (test-exn "help request" exn:fc:help-request? (lambda () (match-spec test-command-spec '("help")))) (test-exn "help request for specific command" exn:fc:help-request? (lambda () (match-spec test-command-spec '("help" "add")))) (test-exn "help request for bad command" exn:fc:bad-command? (lambda () (match-spec test-command-spec '("help" "foo")))) (test-exn "switch instead of command" exn:fc:bad-command? (lambda () (match-spec test-command-spec '("--start-date" "2011-02-02")))) (test-exn "bad command name" exn:fc:bad-command? (lambda () (match-spec test-command-spec '("foo" "bar" "baz" "-s" "quux")))))) (define match-command-tests (let ([add-command (find-command test-command-spec "add")] [list-command (find-command test-command-spec "list")] [show-command (find-command test-command-spec "show")]) (test-suite "match-command" (test-equal? "add, only positional" (match-command add-command '("foo" "bar")) '(add (start-date #f) (due-date #f) (note #f) (edit-note #f) (url #f) (summary ("foo" "bar")))) (test-equal? "add, no positional args" (match-command add-command null) '(add (start-date #f) (due-date #f) (note #f) (edit-note #f) (url #f) (summary ()))) (test-equal? "once-each args" (match-command add-command '("-s" "today" "-d" "tomorrow")) `(add (start-date ("today")) (due-date ("tomorrow")) (note #f) (edit-note #f) (url #f) (summary ()))) (test-equal? "once-any args" (match-command add-command '("-n" "note-file.txt")) `(add (start-date #f) (due-date #f) (note ("note-file.txt")) (edit-note #f) (url #f) (summary ()))) (test-exn "duplicate once-each args (add)" exn:fc:duplicate-switch? (lambda () (match-command add-command '("task" "summary" "-s" "today" "-d" "tomorrow" "-d" "2011-03-15")))) (test-exn "multiple once-any args (add)" exn:fc:multiple-group-members? (lambda () (match-command add-command '("-n" "note-file.txt" "-e")))) (test-equal? "show, only positional" (match-command show-command '("3" "4")) '(show (id "3") (id2 "4") (single-arg #f) (multi-arg #f))) (test-equal? "show, full multi args" (match-command show-command '("arg1" "-s" "s1" "-m" "m1,1" "m1,2" "arg2" "-m" "m2,1" "m2,2" "-s" "s2")) '(show (id "arg1") (id2 "arg2") (single-arg (("s1" "s2"))) (multi-arg (("m1,1" "m2,1") ("m1,2" "m2,2"))))) (test-exn "list, extra positional" exn:fc:extra-argument? (lambda () (match-command list-command '("x" "y" "z")))) (test-exn "missing positional (show)" exn:fc:missing-argument? (lambda () (match-command show-command '("arg1" "-s" "foo")))) (test-exn "missing argument to switch (show)" exn:fc:missing-argument? (lambda () (match-command show-command '("arg1" "arg2" "-s")))) (test-exn "bogus switch (show)" exn:fc:bad-switch? (lambda () (match-command show-command '("arg1" "arg2" "-q"))))))) (define find-switch-tests (let ([add-flags (command-flags (find-command test-command-spec "add"))] [show-flags (command-flags (find-command test-command-spec "show"))]) (test-suite "find-switch" (test-equal? "optional flag, no group" (find-switch "-s" add-flags) (optional-flag (set "-s" "--start-date") '#:start-date '("Date on which task begins") '(date))) (test-equal? "multi flag" (find-switch "--single-arg" show-flags) (multi-flag (set "-s" "--single-arg") '#:single-arg '("single arg multi switch") '(arg))) (test-equal? "optional flag, in group" (find-switch "--note" add-flags) (let ([s (optional-flag (set "-n" "--note") '#:note '("Name of file containing note for task") '(filename))]) (cons s (group (list s (optional-flag (set "-e" "--edit-note") '#:edit-note '("Bring up $EDITOR for task's note") null)))))) (test-false "flag not found" (find-switch "--no-such-flag" add-flags))))) (define invoke-cmd-fn-tests (test-suite "invoke-cmd-fn" (test-equal? "add-function, no kw args" (invoke-cmd-fn add-function null '("x" "y" "z") (hasheq)) '(add (start-date #f) (due-date #f) (note #f) (edit-note #f) (url #f) (summary ("x" "y" "z")))) (test-equal? "add-function, kw args" (invoke-cmd-fn add-function null '("x" "y" "z") (hasheq '#:start-date '("s") '#:edit-note '("foo"))) '(add (start-date ("s")) (due-date #f) (note #f) (edit-note ("foo")) (url #f) (summary ("x" "y" "z")))) (test-equal? "show function w/ multi args" (invoke-cmd-fn show-function '("42" "56") null (hasheq '#:single-arg '(("x" "y")) '#:multi-arg '(("a" "c") ("b" "d")))) '(show (id "42") (id2 "56") (single-arg (("x" "y"))) (multi-arg (("a" "c") ("b" "d"))))))) (define snoc-tests (test-suite "snoc" (test-equal? "second arg empty" (snoc 'x null) '(x)) (test-equal? "second arg non-empty" (snoc 'z '(x y)) '(x y z)))) (define match-tests (test-suite "Test of command-line matcher" match-spec-tests match-command-tests find-switch-tests invoke-cmd-fn-tests snoc-tests))