diff --git a/src/unit/engine/phutil/testcase/ArcanistPhutilTestCase.php b/src/unit/engine/phutil/testcase/ArcanistPhutilTestCase.php index 6f82dbfd..59cc7bc6 100644 --- a/src/unit/engine/phutil/testcase/ArcanistPhutilTestCase.php +++ b/src/unit/engine/phutil/testcase/ArcanistPhutilTestCase.php @@ -28,6 +28,7 @@ abstract class ArcanistPhutilTestCase { private $runningTest; + private $testStartTime; private $results = array(); @@ -162,6 +163,7 @@ abstract class ArcanistPhutilTestCase { $result = new ArcanistUnitTestResult(); $result->setName($this->runningTest); $result->setResult(ArcanistUnitTestResult::RESULT_FAIL); + $result->setDuration(microtime(true) - $this->testStartTime); $result->setUserData($reason); $this->results[] = $result; } @@ -179,6 +181,7 @@ abstract class ArcanistPhutilTestCase { $result = new ArcanistUnitTestResult(); $result->setName($this->runningTest); $result->setResult(ArcanistUnitTestResult::RESULT_PASS); + $result->setDuration(microtime(true) - $this->testStartTime); $result->setUserData($reason); $this->results[] = $result; } @@ -206,6 +209,7 @@ abstract class ArcanistPhutilTestCase { $name = $method->getName(); if (preg_match('/^test/', $name)) { $this->runningTest = $name; + $this->testStartTime = microtime(true); try { $this->willRunOneTest($name); diff --git a/src/unit/result/ArcanistUnitTestResult.php b/src/unit/result/ArcanistUnitTestResult.php index 426f44aa..e2d63366 100644 --- a/src/unit/result/ArcanistUnitTestResult.php +++ b/src/unit/result/ArcanistUnitTestResult.php @@ -33,6 +33,7 @@ class ArcanistUnitTestResult { private $namespace; private $name; private $result; + private $duration; private $userData; public function setName($name) { @@ -53,6 +54,15 @@ class ArcanistUnitTestResult { return $this->result; } + public function setDuration($duration) { + $this->duration = $duration; + return $this; + } + + public function getDuration() { + return $this->duration; + } + public function setUserData($user_data) { $this->userData = $user_data; return $this; diff --git a/src/workflow/unit/ArcanistUnitWorkflow.php b/src/workflow/unit/ArcanistUnitWorkflow.php index cc16239a..d6c14a0a 100644 --- a/src/workflow/unit/ArcanistUnitWorkflow.php +++ b/src/workflow/unit/ArcanistUnitWorkflow.php @@ -132,7 +132,11 @@ EOTEXT $unresolved[] = $result; } else { if ($this->engine->shouldEchoTestResults()) { - echo ' '.$status_codes[$result_code].' '.$result->getName()."\n"; + echo ' '.$status_codes[$result_code]; + if ($result_code == ArcanistUnitTestResult::RESULT_PASS) { + echo ' '.self::formatTestDuration($result->getDuration()); + } + echo ' '.$result->getName()."\n"; } if ($result_code != ArcanistUnitTestResult::RESULT_PASS) { if ($this->engine->shouldEchoTestResults()) { @@ -179,4 +183,41 @@ EOTEXT } } + private static function formatTestDuration($seconds) { + // Very carefully define inclusive upper bounds on acceptable unit test + // durations. Times are in milliseconds and are in increasing order. + $acceptableness = array( + 50 => "%s\xE2\x98\x85 ", + 200 => '%s ', + 500 => '%s ', + INF => '%s ', + ); + + $milliseconds = $seconds * 1000; + $duration = self::formatTime($seconds); + foreach ($acceptableness as $upper_bound => $formatting) { + if ($milliseconds <= $upper_bound) { + return phutil_console_format($formatting, $duration); + } + } + return phutil_console_format(end($acceptableness), $duration); + } + + private static function formatTime($seconds) { + if ($seconds >= 60) { + $minutes = floor($seconds / 60); + return sprintf('%dm%02ds', $minutes, round($seconds % 60)); + } + + if ($seconds >= 1) { + return sprintf('%4.1fs', $seconds); + } + + $milliseconds = $seconds * 1000; + if ($milliseconds >= 1) { + return sprintf('%3dms', round($milliseconds)); + } + + return ' <1ms'; + } }