diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 731fb4ae3f..52717bfa53 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -31,4 +31,15 @@
+
+
+
+
+
+ 150
+
+
+
+
+
diff --git a/tests/phpunit/includes/speed-trap-listener.php b/tests/phpunit/includes/speed-trap-listener.php
new file mode 100644
index 0000000000..e7e3c1a61c
--- /dev/null
+++ b/tests/phpunit/includes/speed-trap-listener.php
@@ -0,0 +1,314 @@
+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 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;
+ }
+}