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 $slowThreshold Test execution time at which a test should be considered slow (milliseconds) * @return bool */ protected function isSlow($time, $slowThreshold) { return $time >= $slowThreshold; } /** * 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->reportLength); } /** * 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->slowThreshold); } /** * Renders slow test report body. */ protected function renderBody() { $slowTests = $this->slow; $length = $this->getReportLength($slowTests); for ($i = 1; $i <= $length; ++$i) { $label = key($slowTests); $time = array_shift($slowTests); 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", $hidden == 1 ? 'is' : 'are', $hidden); } } /** * Populate options into class internals. * * @param array $options */ protected function loadOptions(array $options) { $this->slowThreshold = isset($options['slowThreshold']) ? $options['slowThreshold'] : 500; $this->reportLength = 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): * * * @slowThreshold 5000 * public function testLongRunningProcess() {} * * * @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->slowThreshold; } }