Commit 50b4ea93db986b2a9498a31023a1d4a9865b63b5
- Diff rendering mode:
- inline
- side by side
ext/Tapir/Parser.pir
(131 / 0)
|   | |||
| 1 | # Copyright (C) 2009, Jonathan "Duke" Leto | ||
| 2 | |||
| 3 | =head1 AUTHOR | ||
| 4 | |||
| 5 | Written and maintained by Jonathan "Duke" Leto C<< jonathan@leto.net >>. | ||
| 6 | |||
| 7 | =cut | ||
| 8 | |||
| 9 | .namespace [ 'Tapir'; 'Parser' ] | ||
| 10 | |||
| 11 | .sub parse_tapstream :method | ||
| 12 | .param string tap | ||
| 13 | .param int exit_code :optional | ||
| 14 | .local string curr_line | ||
| 15 | .local pmc plan, pass, fail, skip, todo | ||
| 16 | .local int i, curr_test, reported_test | ||
| 17 | .local pmc tap_lines, parts, klass, stream | ||
| 18 | |||
| 19 | i = 0 | ||
| 20 | curr_test = 1 | ||
| 21 | fail = new 'Integer' | ||
| 22 | skip = new 'Integer' | ||
| 23 | todo = new 'Integer' | ||
| 24 | pass = new 'Integer' | ||
| 25 | plan = new 'Integer' | ||
| 26 | tap_lines = new 'ResizablePMCArray' | ||
| 27 | parts = new 'ResizablePMCArray' | ||
| 28 | |||
| 29 | split tap_lines, "\n", tap | ||
| 30 | $I0 = tap_lines | ||
| 31 | |||
| 32 | .local string plan_line | ||
| 33 | plan_line = tap_lines[0] | ||
| 34 | plan = self.'parse_plan'(plan_line) | ||
| 35 | |||
| 36 | .local string prefix | ||
| 37 | loop: | ||
| 38 | if i >= $I0 goto done | ||
| 39 | curr_line = tap_lines[i] | ||
| 40 | |||
| 41 | split parts, "ok ", curr_line | ||
| 42 | |||
| 43 | prefix = parts[0] | ||
| 44 | reported_test = parts[1] | ||
| 45 | |||
| 46 | if prefix == 'not ' goto fail_or_todo | ||
| 47 | |||
| 48 | if reported_test == curr_test goto pass_or_skip | ||
| 49 | |||
| 50 | # it was an unrecognized line | ||
| 51 | inc i | ||
| 52 | goto loop | ||
| 53 | pass_or_skip: | ||
| 54 | split parts, "# ", curr_line | ||
| 55 | $S0 = parts[1] | ||
| 56 | $S0 = substr $S0, 0, 4 | ||
| 57 | downcase $S0 | ||
| 58 | if $S0 != "skip" goto passz | ||
| 59 | # it is a SKIP test! | ||
| 60 | inc skip | ||
| 61 | inc i | ||
| 62 | inc curr_test | ||
| 63 | goto loop | ||
| 64 | fail_or_todo: | ||
| 65 | split parts, "# ", curr_line | ||
| 66 | $S0 = parts[1] | ||
| 67 | $S0 = substr $S0, 0, 4 | ||
| 68 | downcase $S0 | ||
| 69 | if $S0 != "todo" goto failz | ||
| 70 | # it is a TODO test! | ||
| 71 | inc todo | ||
| 72 | inc curr_test | ||
| 73 | inc i | ||
| 74 | goto loop | ||
| 75 | failz: | ||
| 76 | inc fail | ||
| 77 | inc curr_test | ||
| 78 | inc i | ||
| 79 | goto loop | ||
| 80 | passz: | ||
| 81 | inc pass | ||
| 82 | inc i | ||
| 83 | inc curr_test | ||
| 84 | goto loop | ||
| 85 | |||
| 86 | done: | ||
| 87 | stream = new [ 'Tapir'; 'Stream' ] | ||
| 88 | stream.'set_pass'(pass) | ||
| 89 | stream.'set_fail'(fail) | ||
| 90 | stream.'set_todo'(todo) | ||
| 91 | stream.'set_skip'(skip) | ||
| 92 | stream.'set_plan'(plan) | ||
| 93 | stream.'set_exit_code'(exit_code) | ||
| 94 | .return (stream) | ||
| 95 | .end | ||
| 96 | |||
| 97 | # parse_plan returns the expected number of test given a TAP stream as a string | ||
| 98 | |||
| 99 | .sub parse_plan :method | ||
| 100 | .param string plan_line | ||
| 101 | .local pmc plan_parts | ||
| 102 | # yes, a numeric | ||
| 103 | .local num num_expected_tests | ||
| 104 | |||
| 105 | $I0 = length plan_line | ||
| 106 | if $I0 < 4 goto plan_error | ||
| 107 | |||
| 108 | # this needs to take into account TAP Versions | ||
| 109 | $S0 = substr plan_line, 0, 3 | ||
| 110 | unless $S0 == "1.." goto plan_error | ||
| 111 | |||
| 112 | plan_parts = new 'FixedPMCArray' | ||
| 113 | plan_parts = 2 | ||
| 114 | |||
| 115 | split plan_parts, "..", plan_line | ||
| 116 | num_expected_tests = plan_parts[1] | ||
| 117 | |||
| 118 | $I1 = num_expected_tests | ||
| 119 | unless $I1 == num_expected_tests goto plan_error | ||
| 120 | .return (num_expected_tests) | ||
| 121 | plan_error: | ||
| 122 | # this indicates an invalid plan | ||
| 123 | .return (-1) | ||
| 124 | .end | ||
| 125 | |||
| 126 | |||
| 127 | # Local Variables: | ||
| 128 | # mode: pir | ||
| 129 | # fill-column: 100 | ||
| 130 | # End: | ||
| 131 | # vim: expandtab shiftwidth=4 ft=pir: |
ext/Tapir/Stream.pir
(128 / 0)
|   | |||
| 1 | # Copyright (C) 2009, Jonathan "Duke" Leto <jonathan@leto.net> | ||
| 2 | |||
| 3 | .namespace [ 'Tapir'; 'Stream' ] | ||
| 4 | |||
| 5 | # 06:28:33 <@chromatic> :load executes only when loading from bytecode. | ||
| 6 | # 06:28:48 <@chromatic> :init executes right after compilation. | ||
| 7 | # 06:29:17 <@chromatic> The effects of :init should be frozen into PBC. | ||
| 8 | # 06:37:26 <@chromatic> :anon :init trips the "Do something special with bytecode" magic. | ||
| 9 | |||
| 10 | .sub _initialize :load :anon | ||
| 11 | .local pmc klass | ||
| 12 | |||
| 13 | klass = newclass [ 'Tapir'; 'Stream' ] | ||
| 14 | klass.'add_attribute'('pass') | ||
| 15 | klass.'add_attribute'('fail') | ||
| 16 | klass.'add_attribute'('skip') | ||
| 17 | klass.'add_attribute'('todo') | ||
| 18 | klass.'add_attribute'('plan') | ||
| 19 | klass.'add_attribute'('exit_code') | ||
| 20 | .end | ||
| 21 | |||
| 22 | .sub is_pass :method | ||
| 23 | .local pmc fail | ||
| 24 | fail = getattribute self, "fail" | ||
| 25 | if fail goto failz | ||
| 26 | |||
| 27 | .local pmc exit_code | ||
| 28 | exit_code = self."get_exit_code"() | ||
| 29 | if exit_code goto failz | ||
| 30 | |||
| 31 | .local pmc skip, pass, todo, plan | ||
| 32 | skip = self."get_skip"() | ||
| 33 | pass = self."get_pass"() | ||
| 34 | todo = self."get_todo"() | ||
| 35 | plan = self."get_plan"() | ||
| 36 | $P0 = pass + todo | ||
| 37 | $P0 += skip | ||
| 38 | |||
| 39 | $I1 = plan == $P0 | ||
| 40 | .return( $I1 ) | ||
| 41 | failz: | ||
| 42 | .return( 0 ) | ||
| 43 | .end | ||
| 44 | |||
| 45 | .sub set_exit_code :method | ||
| 46 | .param pmc exit_code | ||
| 47 | setattribute self, "exit_code", exit_code | ||
| 48 | .end | ||
| 49 | |||
| 50 | .sub set_pass :method | ||
| 51 | .param pmc pass | ||
| 52 | setattribute self, "pass", pass | ||
| 53 | .end | ||
| 54 | |||
| 55 | .sub set_fail :method | ||
| 56 | .param pmc fail | ||
| 57 | setattribute self, "fail", fail | ||
| 58 | .end | ||
| 59 | |||
| 60 | .sub set_todo :method | ||
| 61 | .param pmc todo | ||
| 62 | setattribute self, "todo", todo | ||
| 63 | .end | ||
| 64 | |||
| 65 | .sub set_skip :method | ||
| 66 | .param pmc skip | ||
| 67 | setattribute self, "skip", skip | ||
| 68 | .end | ||
| 69 | |||
| 70 | .sub set_plan :method | ||
| 71 | .param pmc plan | ||
| 72 | setattribute self, "plan", plan | ||
| 73 | .end | ||
| 74 | |||
| 75 | .sub get_exit_code :method | ||
| 76 | .local pmc exit_code | ||
| 77 | exit_code = getattribute self, "exit_code" | ||
| 78 | .return( exit_code ) | ||
| 79 | .end | ||
| 80 | |||
| 81 | .sub get_pass :method | ||
| 82 | .local pmc pass | ||
| 83 | pass = getattribute self, "pass" | ||
| 84 | .return( pass ) | ||
| 85 | .end | ||
| 86 | |||
| 87 | .sub get_fail :method | ||
| 88 | .local pmc fail | ||
| 89 | fail = getattribute self, "fail" | ||
| 90 | .return( fail ) | ||
| 91 | .end | ||
| 92 | |||
| 93 | .sub get_todo :method | ||
| 94 | .local pmc todo | ||
| 95 | todo = getattribute self, "todo" | ||
| 96 | .return( todo ) | ||
| 97 | .end | ||
| 98 | |||
| 99 | .sub get_skip :method | ||
| 100 | .local pmc skip | ||
| 101 | skip = getattribute self, "skip" | ||
| 102 | .return( skip ) | ||
| 103 | .end | ||
| 104 | |||
| 105 | .sub get_plan :method | ||
| 106 | .local pmc plan | ||
| 107 | plan = getattribute self, "plan" | ||
| 108 | .return( plan ) | ||
| 109 | .end | ||
| 110 | |||
| 111 | .sub total :method | ||
| 112 | .local pmc skip, pass, fail, todo | ||
| 113 | skip = getattribute self, "skip" | ||
| 114 | pass = getattribute self, "pass" | ||
| 115 | fail = getattribute self, "fail" | ||
| 116 | todo = getattribute self, "todo" | ||
| 117 | $P0 = pass + fail | ||
| 118 | $P0 += todo | ||
| 119 | $P0 += skip | ||
| 120 | .return( $P0 ) | ||
| 121 | .end | ||
| 122 | |||
| 123 | |||
| 124 | # Local Variables: | ||
| 125 | # mode: pir | ||
| 126 | # fill-column: 100 | ||
| 127 | # End: | ||
| 128 | # vim: expandtab shiftwidth=4 ft=pir: |
src/Makefile.in
(5 / 1)
|   | |||
| 67 | 67 | "src/*.c" "src/*$(O)" src/plumage$(EXE) plumage$(EXE) Makefile | |
| 68 | 68 | ||
| 69 | 69 | test: FORCE all | |
| 70 | $(PARROT_NQP) t/harness t/*.t | ||
| 70 | $(PARROT) t/harness.pir --exec=$(PARROT_NQP) t/*.t | ||
| 71 | |||
| 72 | testv: FORCE all | ||
| 73 | $(PARROT) t/harness.pir --verbose --exec=$(PARROT_NQP) t/*.t | ||
| 74 | |||
| 71 | 75 | ||
| 72 | 76 | # Local variables: | |
| 73 | 77 | # mode: makefile |
t/harness.pir
(265 / 0)
|   | |||
| 1 | # Copyright (C) 2009, Jonathan "Duke" Leto <jonathan@leto.net> | ||
| 2 | |||
| 3 | |||
| 4 | .sub version | ||
| 5 | say "Tapir version 0.01" | ||
| 6 | exit 0 | ||
| 7 | .end | ||
| 8 | |||
| 9 | .sub help | ||
| 10 | say <<"HELP" | ||
| 11 | |||
| 12 | Tapir is a TAP test harness. There are different ways to run it, depending on | ||
| 13 | your preferences and build, but this should always work: | ||
| 14 | |||
| 15 | parrot t/harness.pir t/*.t | ||
| 16 | |||
| 17 | If you have created binary "fakecutable" (this requires a working compiler in | ||
| 18 | your PATH) then you can use Tapir like this: | ||
| 19 | |||
| 20 | ./tapir t/*.t | ||
| 21 | |||
| 22 | Currently supported arguments: | ||
| 23 | -v Print the output of each test file | ||
| 24 | --verbose | ||
| 25 | |||
| 26 | --version Print out the current Tapir version | ||
| 27 | |||
| 28 | -e | ||
| 29 | --exec=program Use a given program to execute test scripts | ||
| 30 | i.e. ./tapir --exec=perl t/*.t to run Perl tests | ||
| 31 | -h | ||
| 32 | --help This message | ||
| 33 | |||
| 34 | HELP | ||
| 35 | exit 0 | ||
| 36 | .end | ||
| 37 | |||
| 38 | .sub _parse_opts | ||
| 39 | .param pmc argv | ||
| 40 | .local pmc getopts, opts | ||
| 41 | load_bytecode "Getopt/Obj.pbc" | ||
| 42 | getopts = new 'Getopt::Obj' | ||
| 43 | getopts."notOptStop"(1) | ||
| 44 | push getopts, "exec|e:s" | ||
| 45 | push getopts, "verbose|v" | ||
| 46 | push getopts, "version" | ||
| 47 | push getopts, "help|h" | ||
| 48 | opts = getopts."get_options"(argv) | ||
| 49 | .return(opts) | ||
| 50 | .end | ||
| 51 | |||
| 52 | .sub _find_max_file_length | ||
| 53 | .param pmc files | ||
| 54 | .local int numfiles | ||
| 55 | .local int maxlength | ||
| 56 | numfiles = files | ||
| 57 | maxlength = 0 | ||
| 58 | $I0 = -1 | ||
| 59 | loop_top: | ||
| 60 | inc $I0 | ||
| 61 | if $I0 > numfiles goto loop_bottom | ||
| 62 | $S0 = files[$I0] | ||
| 63 | $I1 = length $S0 | ||
| 64 | if $I1 <= maxlength goto loop_top | ||
| 65 | maxlength = $I1 | ||
| 66 | goto loop_top | ||
| 67 | loop_bottom: | ||
| 68 | .return(maxlength) | ||
| 69 | .end | ||
| 70 | |||
| 71 | .sub _print_elipses | ||
| 72 | .param string filename | ||
| 73 | .param int maxlength | ||
| 74 | .local int namelength | ||
| 75 | .local int lengthdiff | ||
| 76 | namelength = length filename | ||
| 77 | lengthdiff = maxlength - namelength | ||
| 78 | $I0 = lengthdiff + 2 | ||
| 79 | $S0 = repeat ".", $I0 | ||
| 80 | print " " | ||
| 81 | print $S0 | ||
| 82 | print " " | ||
| 83 | .end | ||
| 84 | |||
| 85 | .sub main :main | ||
| 86 | .param pmc argv | ||
| 87 | .local pmc opts | ||
| 88 | .local string exec, verbose | ||
| 89 | .local int argc | ||
| 90 | .local num start_time, end_time | ||
| 91 | |||
| 92 | start_time = time | ||
| 93 | $S0 = shift argv # get rid of harness.pir in the args list | ||
| 94 | |||
| 95 | argc = elements argv | ||
| 96 | if argc > 0 goto load_libs | ||
| 97 | help() | ||
| 98 | |||
| 99 | load_libs: | ||
| 100 | load_bytecode 'ext/Tapir/Parser.pbc' | ||
| 101 | load_bytecode 'ext/Tapir/Stream.pbc' | ||
| 102 | |||
| 103 | |||
| 104 | # parse command line args | ||
| 105 | opts = _parse_opts(argv) | ||
| 106 | exec = opts["exec"] | ||
| 107 | $S1 = opts["version"] | ||
| 108 | $S2 = opts["help"] | ||
| 109 | verbose = opts["verbose"] | ||
| 110 | |||
| 111 | unless $S2 goto check_version | ||
| 112 | help() | ||
| 113 | |||
| 114 | check_version: | ||
| 115 | unless $S1 goto make_parser | ||
| 116 | version() | ||
| 117 | |||
| 118 | make_parser: | ||
| 119 | .local pmc tapir, klass | ||
| 120 | klass = newclass [ 'Tapir'; 'Parser' ] | ||
| 121 | tapir = klass.'new'() | ||
| 122 | |||
| 123 | .local pmc stream, qx_data | ||
| 124 | .local int i | ||
| 125 | .local string file | ||
| 126 | .local string output | ||
| 127 | .local int success, exit_code | ||
| 128 | .local int total_files, failing_files, failing_tests, tests | ||
| 129 | .local int namelength | ||
| 130 | |||
| 131 | namelength = _find_max_file_length(argv) | ||
| 132 | i = 0 | ||
| 133 | failing_files = 0 | ||
| 134 | failing_tests = 0 | ||
| 135 | total_files = 0 | ||
| 136 | tests = 0 | ||
| 137 | loop: | ||
| 138 | file = argv[i] | ||
| 139 | unless file goto done | ||
| 140 | inc total_files | ||
| 141 | print file | ||
| 142 | _print_elipses(file, namelength) | ||
| 143 | |||
| 144 | # we assume the test is PIR unless given an --exec flag | ||
| 145 | # how to do proper shebang-line detection? | ||
| 146 | .local string exec_cmd | ||
| 147 | exec_cmd = 'parrot' | ||
| 148 | unless exec goto run_cmd | ||
| 149 | exec_cmd = exec | ||
| 150 | run_cmd: | ||
| 151 | qx_data = qx(exec_cmd,file) | ||
| 152 | output = qx_data[0] | ||
| 153 | exit_code = qx_data[1] | ||
| 154 | unless verbose goto parse | ||
| 155 | print output | ||
| 156 | parse: | ||
| 157 | stream = tapir.'parse_tapstream'(output, exit_code) | ||
| 158 | success = stream.'is_pass'() | ||
| 159 | unless success goto fail | ||
| 160 | print "passed " | ||
| 161 | |||
| 162 | $I0 = stream.'total'() # includes todo tests | ||
| 163 | print $I0 | ||
| 164 | tests += $I0 | ||
| 165 | say " tests" | ||
| 166 | |||
| 167 | unless exit_code goto redo | ||
| 168 | # all tests passed but file had non-zero exit code | ||
| 169 | inc failing_files | ||
| 170 | |||
| 171 | goto redo | ||
| 172 | fail: | ||
| 173 | print "failed " | ||
| 174 | $I0 = stream.'get_fail'() | ||
| 175 | print $I0 | ||
| 176 | inc failing_files | ||
| 177 | inc failing_tests | ||
| 178 | $S1 = stream.'total'() | ||
| 179 | $S0 = "/" . $S1 | ||
| 180 | print $S0 | ||
| 181 | print " tests" | ||
| 182 | $I1 = stream.'get_exit_code'() | ||
| 183 | unless $I1 goto newline | ||
| 184 | print ", exit code = " | ||
| 185 | say $I1 | ||
| 186 | goto redo | ||
| 187 | newline: | ||
| 188 | print "\n" | ||
| 189 | redo: | ||
| 190 | inc i | ||
| 191 | goto loop | ||
| 192 | |||
| 193 | done: | ||
| 194 | if failing_files goto print_fail | ||
| 195 | print "PASSED " | ||
| 196 | print tests | ||
| 197 | print " test(s) in " | ||
| 198 | print total_files | ||
| 199 | print " files" | ||
| 200 | goto over | ||
| 201 | print_fail: | ||
| 202 | print "FAILED " | ||
| 203 | print failing_tests | ||
| 204 | print " test(s) in " | ||
| 205 | print failing_files | ||
| 206 | print "/" | ||
| 207 | print total_files | ||
| 208 | print " files" | ||
| 209 | over: | ||
| 210 | end_time = time | ||
| 211 | $N1 = end_time - start_time | ||
| 212 | print " (" | ||
| 213 | $P0 = new 'FixedPMCArray' | ||
| 214 | $P0 = 1 | ||
| 215 | $P0[0] = $N1 | ||
| 216 | $S1 = sprintf "%.4f", $P0 | ||
| 217 | print $S1 | ||
| 218 | say " seconds)" | ||
| 219 | $I0 = failing_files != 0 | ||
| 220 | exit $I0 | ||
| 221 | .end | ||
| 222 | |||
| 223 | .sub 'qx' | ||
| 224 | .param pmc command_and_args :slurpy | ||
| 225 | |||
| 226 | .local string cmd | ||
| 227 | cmd = join ' ', command_and_args | ||
| 228 | |||
| 229 | .local pmc pipe | ||
| 230 | pipe = open cmd, 'rp' | ||
| 231 | unless pipe goto pipe_open_error | ||
| 232 | |||
| 233 | .local pmc output | ||
| 234 | pipe.'encoding'('utf8') | ||
| 235 | output = pipe.'readall'() | ||
| 236 | pipe.'close'() | ||
| 237 | |||
| 238 | .local pmc exit_status | ||
| 239 | $I0 = pipe.'exit_status'() | ||
| 240 | exit_status = box $I0 | ||
| 241 | |||
| 242 | find_dynamic_lex $P0, '$!' | ||
| 243 | if null $P0 goto skip_exit_status | ||
| 244 | store_dynamic_lex '$!', exit_status | ||
| 245 | skip_exit_status: | ||
| 246 | |||
| 247 | # hack | ||
| 248 | $P0 = new 'FixedPMCArray' | ||
| 249 | $P0 = 2 | ||
| 250 | $P0[0] = output | ||
| 251 | $P0[1] = exit_status | ||
| 252 | .return ($P0) | ||
| 253 | |||
| 254 | pipe_open_error: | ||
| 255 | $S0 = 'Unable to execute "' | ||
| 256 | $S0 .= cmd | ||
| 257 | $S0 .= '"' | ||
| 258 | die $S0 | ||
| 259 | .end | ||
| 260 | |||
| 261 | # Local Variables: | ||
| 262 | # mode: pir | ||
| 263 | # fill-column: 100 | ||
| 264 | # End: | ||
| 265 | # vim: expandtab shiftwidth=4 ft=pir: |

