CodeView
Back to article:
<?php /** * @author Michael Heim * @link http://www.zeilenwechsel.de/ * @version 1.1.0 * @license http://www.zeilenwechsel.de/it/code/browse/komodo-phpunit-harness/prod/license.txt */ require_once( 'PHPUnit/Runner/Version.php' ); require_once( dirname( dirname( __file__ ) ) . '/Utils/Loader.php' ); PHPUnitXt_Utils_Loader::load( 'PHPUnitXt_Base_PrinterOptions' ); /** * Prints the test results in a configurable format. */ class PHPUnitXt_Base_Printer extends PHPUnit_TextUI_ResultPrinter implements PHPUnit_Framework_TestListener { /* @var PHPUnitXt_Base_PrinterOptions */ protected $options; protected $all_tests = array(); protected $all_reports = array(); /* @var array paths to be removed from a stack trace; for use with PHPUnit 3.6 */ protected $blacklist = array(); public function __construct() { parent::__construct(); $this->options = new PHPUnitXt_Base_PrinterOptions(); } /** * Accepts the paths which must be removed from a stack trace. For use with * PHPUnit 3.6. * * @param array $blacklist * @return PHPUnitXt_Base_Printer */ public function set_blacklist ( array $blacklist ) { $this->blacklist = $blacklist; return $this; } /** * @param PHPUnitXt_Base_PrinterOptions $options * @return void */ public function set_all_options ( PHPUnitXt_Base_PrinterOptions $options ) { $this->options = $options; } /** * @param string $name * @param mixed $value * @return void */ public function set_option ( $name, $value = true ) { $this->options->set( $name, $value ); } /** * Returns an array of all switches accepted by this printer. * * Names only, without leading '--'. * * @return array */ public function get_switch_names () { return $this->options->get_option_names(); } /** * @param array $defects * @param integer $count * @param string $type */ protected function printDefects( array $defects, $count, $type ) { if ( $count == 0 ) return; $i = 1; // Buffer and store the output for reordering if the defects are not grouped by type. $buffered = ( ! $this->options->get( 'in-groups' ) ); foreach ( $defects as $defect ) { $buffer = $this->print_extended_defect_info( $defect, $i++, $type, $buffered ); if ( $buffered ) { $testname = PHPUnit_Util_Test::describe( $defect->failedTest() ); $this->all_reports[$testname] = $buffer; } } } /** * @param PHPUnit_Framework_TestFailure $defect * @param integer $count * @param string $type * @param bool $buffered * @return void|string */ protected function print_extended_defect_info ( PHPUnit_Framework_TestFailure $defect, $count, $type, $buffered ) { if ( $buffered ) ob_start(); $this->printDefect( $defect, $count ); $this->write( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nCategory: $type\n" ); if ( $buffered ) return ob_get_clean(); } /** * @param string $buffer */ public function write( $buffer ) { // A bit of a hack to intercept the version string announcing PHPUnit before it's printed; // done for --hide-summary. Unclean, but all else is worse. $hide_summary = $this->options->get( 'hide-summary' ); if ( $hide_summary and $buffer === PHPUnit_Runner_Version::getVersionString() . "\n\n" ) return; print $buffer; if ( $this->autoFlush ) { $this->incrementalFlush(); } } /** * @param PHPUnit_Framework_Test $test */ public function startTest( PHPUnit_Framework_Test $test ) { $this->all_tests[] = PHPUnit_Util_Test::describe( $test ); parent::startTest( $test ); } /** * @param PHPUnit_Framework_TestResult $result */ public function printResult( PHPUnit_Framework_TestResult $result ) { $display_failed_tests_only = $this->options->get( 'hide-passed' ); $display_order_by_status = $this->options->get( 'in-groups' ); $show_summary = $this->options->get( 'show-summary' ); // Printing the test summary. if ( $show_summary ) { print 'Using PHP ' . phpversion() . '.'; if ( version_compare( PHPUnit_Runner_Version::id(), '3.5.0', '<' ) ) { $this->printHeader(0); // PHPUnit 3.4 } else { $this->printHeader(); // PHPUnit 3.5 } $this->write( "---\n" ); $this->printFooter( $result ); } // Printing the tests. // NB: If tests are to be ordered by execution and, therefore, failures can't be printed as groups, // failure output will be intercepted and written to an array first ($this->all_reports). if ( $result->errorCount() > 0 ) $this->printErrors( $result ); if ( $result->failureCount() > 0 ) $this->printFailures( $result ); if ( $result->skippedCount() > 0 ) $this->printSkipped( $result ); if ( $result->notImplementedCount() > 0 ) $this->printIncompletes( $result ); if ( $display_order_by_status ) { if ( count( $result->passed() ) > 0 and ! $display_failed_tests_only ) $this->print_passed_tests( $result ); } else { $testnames = $this->all_tests; foreach ( $testnames as $testname ) { if ( array_key_exists( $testname, $this->all_reports ) ) { print $this->all_reports[$testname]; } elseif ( ! $display_failed_tests_only ) { $this->print_passed_test( $testname ); } } } } /** * @param string $progress */ protected function writeProgress( $progress ) { $this->numTestsRun++; } /** * @param PHPUnit_Framework_TestCase $test * @return string the formatted test name */ protected function get_formatted_test_name( PHPUnit_Framework_TestCase $test ) { $test_name = PHPUnit_Util_Test::describe( $test ); return $this->format_test_name( $test_name ); } /** * @param string $test_name * @return string the formatted test name */ protected function format_test_name( $test_name ) { $no_formatting = $this->options->get( 'as-name' ); $remove_classnames = $this->options->get( 'hide-class' ); if ( $no_formatting ) { $formatted_name = ( $remove_classnames ? preg_replace( '%.+::%', '', $test_name ) : $test_name ); } else { if ( preg_match( '%^((?:.+::)?)(?:test)?(.+?)(?:test)?$%i', $test_name, $parts ) ) { $class = str_replace( '::', ' - ', $parts[1] ); $description = $parts[2]; } else { $class = ''; $description = $test_name; } $description = preg_replace( "/([a-z_\d])([A-Z])/", "$1 $2", $description ); if ( strpos( $description, '_' ) !== false ) { $description[strpos( $description, '_' )] = ':'; $description = str_replace( '_', ',', $description ); } $description = preg_replace_callback( '/ ([A-Z])([a-z])/', create_function( '$matches', 'return " " . strtolower( $matches[1] ) . $matches[2];' ), $description ); $formatted_name = ( $remove_classnames ? $description : $class . $description ); } return $formatted_name; } /** * @param PHPUnit_Framework_TestResult $result */ protected function print_passed_tests ( PHPUnit_Framework_TestResult $result ) { $passed = $result->passed(); foreach( $passed as $name => $unused ) { $this->print_passed_test( $name ); } } /** * @param string $name */ protected function print_passed_test ( $name ) { $displayed = $this->format_test_name( $name ); print "\n$displayed\n"; } /** * @param PHPUnit_Framework_TestFailure $defect */ protected function printDefectTrace( PHPUnit_Framework_TestFailure $defect ) { if ( version_compare( PHPUnit_Runner_Version::id(), '3.6.0', '<' ) ) { parent::printDefectTrace( $defect ); } else { // In PHPUnit 3.6, we need to filter the test harness files from the // stack trace ourselves. There is no way to access and customize the // built-in filter, as there used to be in PHP 3.5. // // (Actually, the built-in filter can be manipulated - see StackOverflow // http://goo.gl/HP2el. But it doesn't work with PHP 5.2.) ob_start(); parent::printDefectTrace( $defect ); $trace = ob_get_clean(); $lines = explode( "\n", $trace ); $filtered_stacktrace = array(); foreach( $lines as $line ) { $include = true; foreach( $this->blacklist as $blacklisted ) { $include = ( stripos( $line, $blacklisted ) !== 0 ); if ( ! $include ) break; } if ( $include ) $filtered_stacktrace[] = $line; } $this->write( implode( "\n", $filtered_stacktrace ) ); } } } ?>