Wordpress/tests/phpunit/includes/speed-trap-listener.php

307 lines
7.3 KiB
PHP

<?php
/**
* A PHPUnit TestListener that exposes your slowest running tests by outputting
* results directly to the console.
*/
class SpeedTrapListener implements PHPUnit_Framework_TestListener {
/**
* Internal tracking for test suites.
*
* Increments as more suites are run, then decremented as they finish. All
* suites have been run when returns to 0.
*
* @var integer
*/
protected $suites = 0;
/**
* Time in milliseconds at which a test will be considered "slow" and be
* reported by this listener.
*
* @var int
*/
protected $slow_threshold;
/**
* Number of tests to report on for slowness.
*
* @var int
*/
protected $report_length;
/**
* Collection of slow tests.
*
* @var array
*/
protected $slow = array();
/**
* Construct a new instance.
*
* @param array $options
*/
public function __construct( array $options = array() ) {
$this->loadOptions( $options );
}
/**
* An error occurred.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addError( PHPUnit_Framework_Test $test, Exception $e, $time ) {
}
/**
* A warning occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_Warning $e
* @param float $time
* @since Method available since Release 5.1.0
*/
public function addWarning( PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time ) {
}
/**
* A failure occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_AssertionFailedError $e
* @param float $time
*/
public function addFailure( PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time ) {
}
/**
* Incomplete test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addIncompleteTest( PHPUnit_Framework_Test $test, Exception $e, $time ) {
}
/**
* Risky test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
* @since Method available since Release 4.0.0
*/
public function addRiskyTest( PHPUnit_Framework_Test $test, Exception $e, $time ) {
}
/**
* Skipped test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addSkippedTest( PHPUnit_Framework_Test $test, Exception $e, $time ) {
}
/**
* A test started.
*
* @param PHPUnit_Framework_Test $test
*/
public function startTest( PHPUnit_Framework_Test $test ) {
}
/**
* A test ended.
*
* @param PHPUnit_Framework_Test $test
* @param float $time
*/
public function endTest( PHPUnit_Framework_Test $test, $time ) {
if ( ! $test instanceof PHPUnit_Framework_TestCase ) {
return;
}
$time = $this->toMilliseconds( $time );
$threshold = $this->getSlowThreshold( $test );
if ( $this->isSlow( $time, $threshold ) ) {
$this->addSlowTest( $test, $time );
}
}
/**
* A test suite started.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function startTestSuite( PHPUnit_Framework_TestSuite $suite ) {
$this->suites++;
}
/**
* A test suite ended.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function endTestSuite( PHPUnit_Framework_TestSuite $suite ) {
$this->suites--;
if ( 0 === $this->suites && $this->hasSlowTests() ) {
arsort( $this->slow ); // Sort longest running tests to the top
$this->renderHeader();
$this->renderBody();
$this->renderFooter();
}
}
/**
* Whether the given test execution time is considered slow.
*
* @param int $time Test execution time in milliseconds
* @param int $slow_threshold Test execution time at which a test should be considered slow (milliseconds)
* @return bool
*/
protected function isSlow( $time, $slow_threshold ) {
return $time >= $slow_threshold;
}
/**
* Stores a test as slow.
*
* @param PHPUnit_Framework_TestCase $test
* @param int $time Test execution time in milliseconds
*/
protected function addSlowTest( PHPUnit_Framework_TestCase $test, $time ) {
$label = $this->makeLabel( $test );
$this->slow[ $label ] = $time;
}
/**
* Whether at least one test has been considered slow.
*
* @return bool
*/
protected function hasSlowTests() {
return ! empty( $this->slow );
}
/**
* Convert PHPUnit's reported test time (microseconds) to milliseconds.
*
* @param float $time
* @return int
*/
protected function toMilliseconds( $time ) {
return (int) round( $time * 1000 );
}
/**
* Label for describing a test.
*
* @param PHPUnit_Framework_TestCase $test
* @return string
*/
protected function makeLabel( PHPUnit_Framework_TestCase $test ) {
return sprintf( '%s:%s', get_class( $test ), $test->getName() );
}
/**
* Calculate number of slow tests to report about.
*
* @return int
*/
protected function getReportLength() {
return min( count( $this->slow ), $this->report_length );
}
/**
* Find how many slow tests occurred that won't be shown due to list length.
*
* @return int Number of hidden slow tests
*/
protected function getHiddenCount() {
$total = count( $this->slow );
$showing = $this->getReportLength( $this->slow );
$hidden = 0;
if ( $total > $showing ) {
$hidden = $total - $showing;
}
return $hidden;
}
/**
* Renders slow test report header.
*/
protected function renderHeader() {
echo sprintf( "\n\nYou should really fix these slow tests (>%sms)...\n", $this->slow_threshold );
}
/**
* Renders slow test report body.
*/
protected function renderBody() {
$slow_tests = $this->slow;
$length = $this->getReportLength( $slow_tests );
for ( $i = 1; $i <= $length; ++$i ) {
$label = key( $slow_tests );
$time = array_shift( $slow_tests );
echo sprintf( " %s. %sms to run %s\n", $i, $time, $label );
}
}
/**
* Renders slow test report footer.
*/
protected function renderFooter() {
if ( $hidden = $this->getHiddenCount( $this->slow ) ) {
echo sprintf( '...and there %s %s more above your threshold hidden from view', 1 === $hidden ? 'is' : 'are', $hidden );
}
}
/**
* Populate options into class internals.
*
* @param array $options
*/
protected function loadOptions( array $options ) {
$this->slow_threshold = isset( $options['slowThreshold'] ) ? $options['slowThreshold'] : 500;
$this->report_length = isset( $options['reportLength'] ) ? $options['reportLength'] : 10;
}
/**
* Get slow test threshold for given test. A TestCase can override the
* suite-wide slow threshold by using the annotation @slowThreshold with
* the threshold value in milliseconds.
*
* The following test will only be considered slow when its execution time
* reaches 5000ms (5 seconds):
*
* <code>
*
* @slowThreshold 5000
* public function testLongRunningProcess() {}
* </code>
*
* @param PHPUnit_Framework_TestCase $test
* @return int
*/
protected function getSlowThreshold( PHPUnit_Framework_TestCase $test ) {
$ann = $test->getAnnotations();
return isset( $ann['method']['slowThreshold'][0] ) ? $ann['method']['slowThreshold'][0] : $this->slow_threshold;
}
}