diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9cf2a511..15337967 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -139,6 +139,8 @@ phutil_register_library_map(array( 'ArcanistTextLinterTestCase' => 'lint/linter/__tests__/ArcanistTextLinterTestCase.php', 'ArcanistTodoWorkflow' => 'workflow/ArcanistTodoWorkflow.php', 'ArcanistUncommittedChangesException' => 'exception/usage/ArcanistUncommittedChangesException.php', + 'ArcanistUnitConsoleRenderer' => 'unit/renderer/ArcanistUnitConsoleRenderer.php', + 'ArcanistUnitRenderer' => 'unit/renderer/ArcanistUnitRenderer.php', 'ArcanistUnitTestResult' => 'unit/ArcanistUnitTestResult.php', 'ArcanistUnitWorkflow' => 'workflow/ArcanistUnitWorkflow.php', 'ArcanistUpgradeWorkflow' => 'workflow/ArcanistUpgradeWorkflow.php', @@ -271,6 +273,7 @@ phutil_register_library_map(array( 'ArcanistTextLinterTestCase' => 'ArcanistArcanistLinterTestCase', 'ArcanistTodoWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistUncommittedChangesException' => 'ArcanistUsageException', + 'ArcanistUnitConsoleRenderer' => 'ArcanistUnitRenderer', 'ArcanistUnitWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistUpgradeWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistUploadWorkflow' => 'ArcanistBaseWorkflow', diff --git a/src/unit/ArcanistUnitTestResult.php b/src/unit/ArcanistUnitTestResult.php index 2690d6ee..6372efcc 100644 --- a/src/unit/ArcanistUnitTestResult.php +++ b/src/unit/ArcanistUnitTestResult.php @@ -50,18 +50,6 @@ final class ArcanistUnitTestResult { return $this->result; } - public function getConsoleFormattedResult() { - static $status_codes = array( - self::RESULT_PASS => '** PASS **', - self::RESULT_FAIL => '** FAIL **', - self::RESULT_SKIP => '** SKIP **', - self::RESULT_BROKEN => '** BROKEN **', - self::RESULT_UNSOUND => '** UNSOUND **', - self::RESULT_POSTPONED => '** POSTPONED **', - ); - return phutil_console_format($status_codes[$this->result]); - } - public function setDuration($duration) { $this->duration = $duration; return $this; diff --git a/src/unit/engine/ArcanistBaseUnitTestEngine.php b/src/unit/engine/ArcanistBaseUnitTestEngine.php index e6ea50f0..802cc15d 100644 --- a/src/unit/engine/ArcanistBaseUnitTestEngine.php +++ b/src/unit/engine/ArcanistBaseUnitTestEngine.php @@ -14,6 +14,7 @@ abstract class ArcanistBaseUnitTestEngine { private $enableAsyncTests; private $enableCoverage; private $runAllTests; + protected $renderer; public function setRunAllTests($run_all_tests) { @@ -86,6 +87,11 @@ abstract class ArcanistBaseUnitTestEngine { return $this->enableCoverage; } + public function setRenderer(ArcanistUnitRenderer $renderer) { + $this->renderer = $renderer; + return $this; + } + abstract public function run(); /** diff --git a/src/unit/engine/PhutilUnitTestEngine.php b/src/unit/engine/PhutilUnitTestEngine.php index 9f1cfcbb..2179d5ba 100644 --- a/src/unit/engine/PhutilUnitTestEngine.php +++ b/src/unit/engine/PhutilUnitTestEngine.php @@ -45,6 +45,9 @@ final class PhutilUnitTestEngine extends ArcanistBaseUnitTestEngine { if ($this->getPaths()) { $test_case->setPaths($this->getPaths()); } + if ($this->renderer) { + $test_case->setRenderer($this->renderer); + } $results[] = $test_case->run(); } @@ -169,4 +172,8 @@ final class PhutilUnitTestEngine extends ArcanistBaseUnitTestEngine { return $run_tests; } + public function shouldEchoTestResults() { + return !$this->renderer; + } + } diff --git a/src/unit/engine/phutil/ArcanistPhutilTestCase.php b/src/unit/engine/phutil/ArcanistPhutilTestCase.php index 2c9126a5..3f7d4507 100644 --- a/src/unit/engine/phutil/ArcanistPhutilTestCase.php +++ b/src/unit/engine/phutil/ArcanistPhutilTestCase.php @@ -20,6 +20,7 @@ abstract class ArcanistPhutilTestCase { private $coverage = array(); private $projectRoot; private $paths; + private $renderer; /* -( Making Test Assertions )--------------------------------------------- */ @@ -372,6 +373,10 @@ abstract class ArcanistPhutilTestCase { $result->setDuration(microtime(true) - $this->testStartTime); $result->setUserData($reason); $this->results[] = $result; + + if ($this->renderer) { + echo $this->renderer->renderUnitResult($result); + } } @@ -530,4 +535,9 @@ abstract class ArcanistPhutilTestCase { return null; } + public function setRenderer(ArcanistUnitRenderer $renderer) { + $this->renderer = $renderer; + return $this; + } + } diff --git a/src/unit/renderer/ArcanistUnitConsoleRenderer.php b/src/unit/renderer/ArcanistUnitConsoleRenderer.php new file mode 100644 index 00000000..974a7979 --- /dev/null +++ b/src/unit/renderer/ArcanistUnitConsoleRenderer.php @@ -0,0 +1,85 @@ +getResult(); + + $duration = ''; + if ($result_code == ArcanistUnitTestResult::RESULT_PASS) { + $duration = ' '.$this->formatTestDuration($result->getDuration()); + } + $return = sprintf( + " %s %s\n", + $this->getFormattedResult($result->getResult()).$duration, + $result->getName()); + + if ($result_code != ArcanistUnitTestResult::RESULT_PASS) { + $return .= $result->getUserData()."\n"; + } + + return $return; + } + + public function renderPostponedResult($count) { + return sprintf( + "%s %s\n", + $this->getFormattedResult(ArcanistUnitTestResult::RESULT_POSTPONED), + pht('%d test(s)', $count)); + } + + private function getFormattedResult($result) { + static $status_codes = array( + ArcanistUnitTestResult::RESULT_PASS => '** PASS **', + ArcanistUnitTestResult::RESULT_FAIL => '** FAIL **', + ArcanistUnitTestResult::RESULT_SKIP => '** SKIP **', + ArcanistUnitTestResult::RESULT_BROKEN => '** BROKEN **', + ArcanistUnitTestResult::RESULT_UNSOUND => '** UNSOUND **', + ArcanistUnitTestResult::RESULT_POSTPONED => + '** POSTPONED **', + ); + return phutil_console_format($status_codes[$result]); + } + + private 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 = $this->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 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'; + } + +} diff --git a/src/unit/renderer/ArcanistUnitRenderer.php b/src/unit/renderer/ArcanistUnitRenderer.php new file mode 100644 index 00000000..c4fcd954 --- /dev/null +++ b/src/unit/renderer/ArcanistUnitRenderer.php @@ -0,0 +1,11 @@ +engine->setArguments($this->getPassthruArgumentsAsMap('unit')); + $renderer = new ArcanistUnitConsoleRenderer(); + $this->engine->setRenderer($renderer); + $enable_coverage = null; // Means "default". if ($this->getArgument('coverage') || $this->getArgument('detailed-coverage')) { @@ -175,19 +178,9 @@ EOTEXT $unresolved[] = $result; } else { if ($this->engine->shouldEchoTestResults()) { - $duration = ''; - if ($result_code == ArcanistUnitTestResult::RESULT_PASS) { - $duration = ' '.self::formatTestDuration($result->getDuration()); - } - $console->writeOut( - " %s %s\n", - $result->getConsoleFormattedResult().$duration, - $result->getName()); + $console->writeOut('%s', $renderer->renderUnitResult($result)); } if ($result_code != ArcanistUnitTestResult::RESULT_PASS) { - if ($this->engine->shouldEchoTestResults()) { - $console->writeOut("%s\n", $result->getUserData()); - } $unresolved[] = $result; } } @@ -198,12 +191,9 @@ EOTEXT } } if ($postponed_count) { - $postponed = id(new ArcanistUnitTestResult()) - ->setResult(ArcanistUnitTestResult::RESULT_POSTPONED); $console->writeOut( - "%s %s\n", - $postponed->getConsoleFormattedResult(), - pht('%d test(s)', $postponed_count)); + '%s', + $renderer->renderPostponedResult($postponed_count)); } if ($coverage) { @@ -286,44 +276,6 @@ EOTEXT return $this->testResults; } - 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'; - } - private function renderDetailedCoverageReport($data, $report) { $data = explode("\n", $data);