Commit Diff


commit - 8e4033faa2a65945f3eef825447e1685d6b31def
commit + e6eae0468089573a77abb012145073dd8d1c4402
blob - aa59087be78be413b07f5af0ba3b78b914b8266a
blob + 7e552eddfecbe0b782dec4fa11b58fe32d9d2124
--- exercise
+++ exercise
@@ -8,9 +8,9 @@ package require Tcl 8.4
 
 
 # config options
-set opts(-p)  {*.t}             ;# pattern for test executable files
-set opts(-ps) {*.tests}         ;# pattern for test suite executable files
-set opts(-pd) {*}               ;# pattern for test search directories
+set opts(-d) {*}                ;# pattern for test search directories
+set opts(-p) {*.t}              ;# pattern for test executable files
+set opts(-s) {*.tests}          ;# pattern for test suite executable files
 
 
 # executing tests
@@ -20,7 +20,7 @@ proc execute_test {countervar path} {
     upvar $countervar counters
 
     # Should the test result be integrated as a nested test suite?
-    set suite [string match $opts(-ps) [file tail $path]]
+    set suite [string match $opts(-s) [file tail $path]]
 
     # Call files in current dir with "./".
     set exe $path
@@ -140,9 +140,9 @@ proc find_files_iter {resultvar dirsvar paths} {
             if {[dict exists $dirs $normpath]} continue
             dict set dirs $normpath {}
                 
-            set subs   [glob -type d -nocomplain -directory $path $opts(-pd)]
-            set suites [glob -type f -nocomplain -directory $path $opts(-ps)]
+            set subs   [glob -type d -nocomplain -directory $path $opts(-d)]
             set tests  [glob -type f -nocomplain -directory $path $opts(-p)]
+            set suites [glob -type f -nocomplain -directory $path $opts(-s)]
 
             find_files_iter result dirs [lsort $subs]
             lappend result {*}[lsort $suites]
@@ -264,11 +264,70 @@ proc check_suite_counters {counters} {
 
 # main routine
 
+proc getopt {argvvar optstring optvar argvar} {
+    upvar $argvvar argv $optvar opt $argvar arg
+
+    set hd [lindex $argv 0]
+    set opt [string index $hd 1]
+    if {$opt eq "" || [string index $hd 0] ne "-"} {	;# no option argument
+        return 0
+    }
+    if {$hd eq "--"} {
+        set argv [lreplace $argv 0 0]           ;# remove hd
+        return 0
+    }
+
+    # consume option character
+    set hd [string replace $hd 1 1]
+    if {$hd ne "-"} {
+        set argv [lreplace $argv 0 0 $hd]       ;# replace hd
+    } else {
+        set argv [lreplace $argv 0 0]           ;# consume exhausted hd
+    }
+
+    set idx [string first $opt $optstring]
+    if {$idx == -1 || $opt eq ":"} {
+        puts stderr "$::argv0: unknown option -- $opt"
+        set opt "?"
+        return 1
+    }
+
+    # handle option arguments
+    if {[string index $optstring [expr $idx + 1]] ne ":"} {
+        return 1                                ;# no argument expected
+    }
+    if {$hd ne "-"} {
+        set arg [string replace $hd 0 0]        ;# rest of hd -> optarg
+        set argv [lreplace $argv 0 0]           ;# consume hd
+    } elseif {[llength $argv] > 0} {
+        set arg [lindex $argv 0]                ;# next arg -> optarg
+        set argv [lreplace $argv 0 0]           ;# consume arg
+    } else {
+        puts stderr "$::argv0: option requires an argument -- $opt"
+        if {[string index $optstring 0] eq ":"} {
+            set opt ":"
+        } else {
+            set opt "?"
+        }
+    }
+
+    return 1
+}
+
 proc main {argc argv} {
+    global opts
     set counters {run 0  ok 0  fail 0  error 0}
 
-    # TODO: accept opts on command line
-    # TODO: command-line --help
+    # handle command line options
+    set u {[-d pat] [-p pat] [-s pat] [path ...]}
+    while {[getopt argv {d:p:s:} opt arg]} {
+        if {$opt eq "?"} {
+            puts stderr "usage: [file tail $::argv0] $u"
+            exit 1
+        }
+        set opts(-$opt) $arg
+    }
+    set argc [llength $argv]
 
     set paths [expr {$argc == 0 ? "." : $argv}]
     set files [find_files $paths]
blob - 4d54c5639a0cffb8b75e634b9b9ad9880cf37c33
blob + 463ab25fde319dbe79fc192598eb3e0ace506e8a
--- exercise.1
+++ exercise.1
@@ -21,19 +21,38 @@ If given a directory argument,
 .Nm
 searches for tests therein.
 If given a file argument, it executes it as a single test (or test suite).
+To be recognized as tests, files must match the pattern
+.Pa *.t .
+To be run as test suites (see below), files must match the pattern
+.Pa *.tests .
 .Pp
-Tests are represented as executable files that are expected to exit 0 on
+The options are as follows:
+.Pp
+.Bl -tag
+.It Fl d Ar pattern
+When searching for tests,
+only descend into directories that match the given pattern.
+Defaults to
+.Dq *
+(all directories).
+.It Fl p Ar pattern
+Process (only) files matching the given pattern as tests.
+Defaults to
+.Dq *.t .
+.It Fl s Ar pattern
+Process (only) files matching the given pattern as test suites.
+Defaults to
+.Dq *.tests .
+.El
+.
+.Ss Test Files
+Tests are represented simply as executable files that are expected to exit 0 on
 success.
 Any output to standard error and non-zero exits are interpreted as negative
 results (test failures).
 Standard output is ignored.
 As a special case, exit status 127 is interpreted as an error during the test,
 implying an inconclusive result.
-.Pp
-To be recognized as tests, files must match the pattern
-.Pa *.t .
-To be run as test suites (see below), files must match the pattern
-.Pa *.tests .
 .
 .Ss Test Suites
 Multiple tests can be combined into one executable.