X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/tests/parser/ParserTestPrinter.php diff --git a/tests/parser/ParserTestPrinter.php b/tests/parser/ParserTestPrinter.php new file mode 100644 index 00000000..94d226c1 --- /dev/null +++ b/tests/parser/ParserTestPrinter.php @@ -0,0 +1,328 @@ +term = $term; + $options += [ + 'showDiffs' => true, + 'showProgress' => true, + 'showFailure' => true, + 'showOutput' => false, + 'useDwdiff' => false, + 'markWhitespace' => false, + ]; + $this->showDiffs = $options['showDiffs']; + $this->showProgress = $options['showProgress']; + $this->showFailure = $options['showFailure']; + $this->showOutput = $options['showOutput']; + $this->useDwdiff = $options['useDwdiff']; + $this->markWhitespace = $options['markWhitespace']; + } + + public function start() { + $this->total = 0; + $this->success = 0; + $this->skipped = 0; + } + + public function startTest( $test ) { + if ( $this->showProgress ) { + $this->showTesting( $test['desc'] ); + } + } + + private function showTesting( $desc ) { + print "Running test $desc... "; + } + + /** + * Show "Reading tests from ..." + * + * @param string $path + */ + public function startSuite( $path ) { + print $this->term->color( 1 ) . + "Running parser tests from \"$path\"..." . + $this->term->reset() . + "\n"; + } + + public function endSuite( $path ) { + print "\n"; + } + + public function record( $test, ParserTestResult $result ) { + $this->total++; + $this->success += ( $result->isSuccess() ? 1 : 0 ); + + if ( $result->isSuccess() ) { + $this->showSuccess( $result ); + } else { + $this->showFailure( $result ); + } + } + + /** + * Print a happy success message. + * + * @param ParserTestResult $testResult + * @return bool + */ + private function showSuccess( ParserTestResult $testResult ) { + if ( $this->showProgress ) { + print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n"; + } + } + + /** + * Print a failure message and provide some explanatory output + * about what went wrong if so configured. + * + * @param ParserTestResult $testResult + * @return bool + */ + private function showFailure( ParserTestResult $testResult ) { + if ( $this->showFailure ) { + if ( !$this->showProgress ) { + # In quiet mode we didn't show the 'Testing' message before the + # test, in case it succeeded. Show it now: + $this->showTesting( $testResult->getDescription() ); + } + + print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n"; + + if ( $this->showOutput ) { + print "--- Expected ---\n{$testResult->expected}\n"; + print "--- Actual ---\n{$testResult->actual}\n"; + } + + if ( $this->showDiffs ) { + print $this->quickDiff( $testResult->expected, $testResult->actual ); + if ( !$this->wellFormed( $testResult->actual ) ) { + print "XML error: $this->xmlError\n"; + } + } + } + + return false; + } + + /** + * Run given strings through a diff and return the (colorized) output. + * Requires writable /tmp directory and a 'diff' command in the PATH. + * + * @param string $input + * @param string $output + * @param string $inFileTail Tailing for the input file name + * @param string $outFileTail Tailing for the output file name + * @return string + */ + private function quickDiff( $input, $output, + $inFileTail = 'expected', $outFileTail = 'actual' + ) { + if ( $this->markWhitespace ) { + $pairs = [ + "\n" => '¶', + ' ' => '·', + "\t" => '→' + ]; + $input = strtr( $input, $pairs ); + $output = strtr( $output, $pairs ); + } + + # Windows, or at least the fc utility, is retarded + $slash = wfIsWindows() ? '\\' : '/'; + $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand(); + + $infile = "$prefix-$inFileTail"; + $this->dumpToFile( $input, $infile ); + + $outfile = "$prefix-$outFileTail"; + $this->dumpToFile( $output, $outfile ); + + $shellInfile = wfEscapeShellArg( $infile ); + $shellOutfile = wfEscapeShellArg( $outfile ); + + global $wgDiff3; + // we assume that people with diff3 also have usual diff + if ( $this->useDwdiff ) { + $shellCommand = 'dwdiff -Pc'; + } else { + $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au'; + } + + $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" ); + + unlink( $infile ); + unlink( $outfile ); + + if ( $this->useDwdiff ) { + return $diff; + } else { + return $this->colorDiff( $diff ); + } + } + + /** + * Write the given string to a file, adding a final newline. + * + * @param string $data + * @param string $filename + */ + private function dumpToFile( $data, $filename ) { + $file = fopen( $filename, "wt" ); + fwrite( $file, $data . "\n" ); + fclose( $file ); + } + + /** + * Colorize unified diff output if set for ANSI color output. + * Subtractions are colored blue, additions red. + * + * @param string $text + * @return string + */ + private function colorDiff( $text ) { + return preg_replace( + [ '/^(-.*)$/m', '/^(\+.*)$/m' ], + [ $this->term->color( 34 ) . '$1' . $this->term->reset(), + $this->term->color( 31 ) . '$1' . $this->term->reset() ], + $text ); + } + + private function wellFormed( $text ) { + $html = + Sanitizer::hackDocType() . + '' . + $text . + ''; + + $parser = xml_parser_create( "UTF-8" ); + + # case folding violates XML standard, turn it off + xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); + + if ( !xml_parse( $parser, $html, true ) ) { + $err = xml_error_string( xml_get_error_code( $parser ) ); + $position = xml_get_current_byte_index( $parser ); + $fragment = $this->extractFragment( $html, $position ); + $this->xmlError = "$err at byte $position:\n$fragment"; + xml_parser_free( $parser ); + + return false; + } + + xml_parser_free( $parser ); + + return true; + } + + private function extractFragment( $text, $position ) { + $start = max( 0, $position - 10 ); + $before = $position - $start; + $fragment = '...' . + $this->term->color( 34 ) . + substr( $text, $start, $before ) . + $this->term->color( 0 ) . + $this->term->color( 31 ) . + $this->term->color( 1 ) . + substr( $text, $position, 1 ) . + $this->term->color( 0 ) . + $this->term->color( 34 ) . + substr( $text, $position + 1, 9 ) . + $this->term->color( 0 ) . + '...'; + $display = str_replace( "\n", ' ', $fragment ); + $caret = ' ' . + str_repeat( ' ', $before ) . + $this->term->color( 31 ) . + '^' . + $this->term->color( 0 ); + + return "$display\n$caret"; + } + + /** + * Show a warning to the user + * @param string $message + */ + public function warning( $message ) { + echo "$message\n"; + } + + /** + * Mark a test skipped + * @param string $test + * @param string $subtest + */ + public function skipped( $test, $subtest ) { + if ( $this->showProgress ) { + print $this->term->color( '1;33' ) . 'SKIPPED' . $this->term->reset() . "\n"; + } + $this->skipped++; + } + + public function report() { + if ( $this->total > 0 ) { + $this->reportPercentage( $this->success, $this->total ); + } else { + print $this->term->color( 31 ) . "No tests found." . $this->term->reset() . "\n"; + } + } + + private function reportPercentage( $success, $total ) { + $ratio = wfPercent( 100 * $success / $total ); + print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)"; + if ( $this->skipped ) { + print ", skipped {$this->skipped}"; + } + print "... "; + + if ( $success == $total ) { + print $this->term->color( 32 ) . "ALL TESTS PASSED!"; + } else { + $failed = $total - $success; + print $this->term->color( 31 ) . "$failed tests failed!"; + } + + print $this->term->reset() . "\n"; + + return ( $success == $total ); + } +}