Page MenuHomePhabricator

D19711.diff
No OneTemporary

D19711.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -165,7 +165,7 @@
'ArcanistDeclarationParenthesesXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistDeclarationParenthesesXHPASTLinterRuleTestCase.php',
'ArcanistDefaultParametersXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDefaultParametersXHPASTLinterRule.php',
'ArcanistDefaultParametersXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistDefaultParametersXHPASTLinterRuleTestCase.php',
- 'ArcanistDefaultUnitFormatter' => 'unit/formatter/ArcanistDefaultUnitFormatter.php',
+ 'ArcanistDefaultUnitSink' => 'unit/sink/ArcanistDefaultUnitSink.php',
'ArcanistDefaultsConfigurationSource' => 'config/source/ArcanistDefaultsConfigurationSource.php',
'ArcanistDeprecationXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php',
'ArcanistDeprecationXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistDeprecationXHPASTLinterRuleTestCase.php',
@@ -279,7 +279,7 @@
'ArcanistJSONLintRenderer' => 'lint/renderer/ArcanistJSONLintRenderer.php',
'ArcanistJSONLinter' => 'lint/linter/ArcanistJSONLinter.php',
'ArcanistJSONLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLinterTestCase.php',
- 'ArcanistJSONUnitFormatter' => 'unit/formatter/ArcanistJSONUnitFormatter.php',
+ 'ArcanistJSONUnitSink' => 'unit/sink/ArcanistJSONUnitSink.php',
'ArcanistJscsLinter' => 'lint/linter/ArcanistJscsLinter.php',
'ArcanistJscsLinterTestCase' => 'lint/linter/__tests__/ArcanistJscsLinterTestCase.php',
'ArcanistKeywordCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistKeywordCasingXHPASTLinterRule.php',
@@ -473,9 +473,9 @@
'ArcanistUnexpectedReturnValueXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistUnexpectedReturnValueXHPASTLinterRuleTestCase.php',
'ArcanistUnitConsoleRenderer' => 'unit/renderer/ArcanistUnitConsoleRenderer.php',
'ArcanistUnitEngine' => 'unit/engine/ArcanistUnitEngine.php',
- 'ArcanistUnitFormatter' => 'unit/formatter/ArcanistUnitFormatter.php',
'ArcanistUnitOverseer' => 'unit/overseer/ArcanistUnitOverseer.php',
'ArcanistUnitRenderer' => 'unit/renderer/ArcanistUnitRenderer.php',
+ 'ArcanistUnitSink' => 'unit/sink/ArcanistUnitSink.php',
'ArcanistUnitTestResult' => 'unit/ArcanistUnitTestResult.php',
'ArcanistUnitTestResultTestCase' => 'unit/__tests__/ArcanistUnitTestResultTestCase.php',
'ArcanistUnitTestableLintEngine' => 'lint/engine/ArcanistUnitTestableLintEngine.php',
@@ -1276,7 +1276,7 @@
'ArcanistDeclarationParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistDefaultParametersXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDefaultParametersXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
- 'ArcanistDefaultUnitFormatter' => 'ArcanistUnitFormatter',
+ 'ArcanistDefaultUnitSink' => 'ArcanistUnitSink',
'ArcanistDefaultsConfigurationSource' => 'ArcanistDictionaryConfigurationSource',
'ArcanistDeprecationXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDeprecationXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
@@ -1390,7 +1390,7 @@
'ArcanistJSONLintRenderer' => 'ArcanistLintRenderer',
'ArcanistJSONLinter' => 'ArcanistLinter',
'ArcanistJSONLinterTestCase' => 'ArcanistLinterTestCase',
- 'ArcanistJSONUnitFormatter' => 'ArcanistUnitFormatter',
+ 'ArcanistJSONUnitSink' => 'ArcanistUnitSink',
'ArcanistJscsLinter' => 'ArcanistExternalLinter',
'ArcanistJscsLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistKeywordCasingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@@ -1584,9 +1584,9 @@
'ArcanistUnexpectedReturnValueXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistUnitConsoleRenderer' => 'ArcanistUnitRenderer',
'ArcanistUnitEngine' => 'Phobject',
- 'ArcanistUnitFormatter' => 'Phobject',
'ArcanistUnitOverseer' => 'Phobject',
'ArcanistUnitRenderer' => 'Phobject',
+ 'ArcanistUnitSink' => 'Phobject',
'ArcanistUnitTestResult' => 'Phobject',
'ArcanistUnitTestResultTestCase' => 'PhutilTestCase',
'ArcanistUnitTestableLintEngine' => 'ArcanistLintEngine',
diff --git a/src/unit/ArcanistUnitTestResult.php b/src/unit/ArcanistUnitTestResult.php
--- a/src/unit/ArcanistUnitTestResult.php
+++ b/src/unit/ArcanistUnitTestResult.php
@@ -175,13 +175,8 @@
}
public static function getAllResultCodes() {
- return array(
- self::RESULT_PASS,
- self::RESULT_FAIL,
- self::RESULT_SKIP,
- self::RESULT_BROKEN,
- self::RESULT_UNSOUND,
- );
+ $map = self::getResultCodeSpecs();
+ return array_keys($map);
}
public static function getResultCodeName($result_code) {
@@ -205,31 +200,56 @@
return idx($specs, $result_code);
}
+ public function getANSIColor() {
+ $spec = $this->getResultMap();
+ return idx($spec, 'color.ansi', 'red');
+ }
+
+ public function getResultLabel() {
+ $spec = $this->getResultMap();
+ return idx($spec, 'label', $this->getResult());
+ }
+
+ private function getResultMap() {
+ $map = self::getResultCodeSpecs();
+ return idx($map, $this->getResult(), array());
+ }
+
private static function getResultCodeSpecs() {
return array(
self::RESULT_PASS => array(
'name' => pht('Pass'),
+ 'label' => pht('PASS'),
+ 'color.ansi' => 'green',
'description' => pht(
'The test passed.'),
),
self::RESULT_FAIL => array(
'name' => pht('Fail'),
+ 'label' => pht('FAIL'),
+ 'color.ansi' => 'red',
'description' => pht(
'The test failed.'),
),
self::RESULT_SKIP => array(
'name' => pht('Skip'),
+ 'label' => pht('SKIP'),
+ 'color.ansi' => 'cyan',
'description' => pht(
'The test was not executed.'),
),
self::RESULT_BROKEN => array(
'name' => pht('Broken'),
+ 'label' => pht('BROKEN'),
+ 'color.ansi' => 'red',
'description' => pht(
'The test failed in an abnormal or severe way. For example, the '.
'harness crashed instead of reporting a failure.'),
),
self::RESULT_UNSOUND => array(
'name' => pht('Unsound'),
+ 'label' => pht('UNSOUND'),
+ 'color.ansi' => 'yellow',
'description' => pht(
'The test failed, but this change is probably not what broke it. '.
'For example, it might have already been failing.'),
@@ -237,5 +257,15 @@
);
}
+ public function getDisplayName() {
+ $name = $this->getName();
+
+ $namespace = $this->getNamespace();
+ if (strlen($namespace)) {
+ $name = $namespace.'::'.$name;
+ }
+
+ return $name;
+ }
}
diff --git a/src/unit/engine/ArcanistUnitEngine.php b/src/unit/engine/ArcanistUnitEngine.php
--- a/src/unit/engine/ArcanistUnitEngine.php
+++ b/src/unit/engine/ArcanistUnitEngine.php
@@ -56,14 +56,7 @@
abstract public function runTests();
final protected function didRunTests(array $tests) {
- assert_instances_of($tests, 'ArcanistUnitTestResult');
-
- // TOOLSETS: Pass this stuff to result output so it can print progress or
- // stream results.
-
- foreach ($tests as $test) {
- echo "Ran Test: ".$test->getNamespace().'::'.$test->getName()."\n";
- }
+ return $this->getOverseer()->didRunTests($tests);
}
}
\ No newline at end of file
diff --git a/src/unit/formatter/ArcanistDefaultUnitFormatter.php b/src/unit/formatter/ArcanistDefaultUnitFormatter.php
deleted file mode 100644
--- a/src/unit/formatter/ArcanistDefaultUnitFormatter.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-final class ArcanistDefaultUnitFormatter
- extends ArcanistUnitFormatter {
-
- const FORMATTER_KEY = 'default';
-
-
-}
diff --git a/src/unit/formatter/ArcanistJSONUnitFormatter.php b/src/unit/formatter/ArcanistJSONUnitFormatter.php
deleted file mode 100644
--- a/src/unit/formatter/ArcanistJSONUnitFormatter.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-final class ArcanistJSONUnitFormatter
- extends ArcanistUnitFormatter {
-
- const FORMATTER_KEY = 'json';
-
-
-}
diff --git a/src/unit/formatter/ArcanistUnitFormatter.php b/src/unit/formatter/ArcanistUnitFormatter.php
deleted file mode 100644
--- a/src/unit/formatter/ArcanistUnitFormatter.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-abstract class ArcanistUnitFormatter
- extends Phobject {
-
- final public function getUnitFormatterKey() {
- return $this->getPhobjectClassConstant('FORMATTER_KEY');
- }
-
- public static function getAllUnitFormatters() {
- return id(new PhutilClassMapQuery())
- ->setAncestorClass(__CLASS__)
- ->setUniqueMethod('getUnitFormatterKey')
- ->execute();
- }
-
-}
diff --git a/src/unit/overseer/ArcanistUnitOverseer.php b/src/unit/overseer/ArcanistUnitOverseer.php
--- a/src/unit/overseer/ArcanistUnitOverseer.php
+++ b/src/unit/overseer/ArcanistUnitOverseer.php
@@ -5,9 +5,9 @@
private $directory;
private $paths = array();
- private $formatter;
+ private $sinks = array();
- public function setPaths($paths) {
+ public function setPaths(array $paths) {
$this->paths = $paths;
return $this;
}
@@ -16,13 +16,14 @@
return $this->paths;
}
- public function setFormatter(ArcanistUnitFormatter $formatter) {
- $this->formatter = $formatter;
+ public function setSinks(array $sinks) {
+ assert_instances_of($sinks, 'ArcanistUnitSink');
+ $this->sinks = $sinks;
return $this;
}
- public function getFormatter() {
- return $this->formatter;
+ public function getSinks() {
+ return $this->sinks;
}
public function setDirectory($directory) {
@@ -50,9 +51,27 @@
}
}
+ $this->didCompleteTests($results);
+
return $results;
}
+ public function didRunTests(array $tests) {
+ assert_instances_of($tests, 'ArcanistUnitTestResult');
+
+ foreach ($this->getSinks() as $sink) {
+ $sink->sinkPartialResults($tests);
+ }
+ }
+
+ private function didCompleteTests(array $tests) {
+ assert_instances_of($tests, 'ArcanistUnitTestResult');
+
+ foreach ($this->getSinks() as $sink) {
+ $sink->sinkFinalResults($tests);
+ }
+ }
+
private function loadEngines() {
$root = $this->getDirectory();
diff --git a/src/unit/sink/ArcanistDefaultUnitSink.php b/src/unit/sink/ArcanistDefaultUnitSink.php
new file mode 100644
--- /dev/null
+++ b/src/unit/sink/ArcanistDefaultUnitSink.php
@@ -0,0 +1,169 @@
+<?php
+
+final class ArcanistDefaultUnitSink
+ extends ArcanistUnitSink {
+
+ const SINKKEY = 'default';
+
+ private $lastUpdateTime;
+ private $buffer = array();
+
+ public function sinkPartialResults(array $results) {
+
+ // We want to show the user both regular progress reports and make sure
+ // that important results aren't scrolled off screen. We'll print a summary
+ // at the end so it's not critical that users can never miss important
+ // results, but they may (for example) want to ^C early if tests fail and
+ // their fate is sealed.
+
+
+ $failed = array();
+ foreach ($results as $result) {
+ $result_code = $result->getResult();
+ switch ($result_code) {
+ case ArcanistUnitTestResult::RESULT_PASS:
+ case ArcanistUnitTestResult::RESULT_SKIP:
+ break;
+ default:
+ $failed[] = $result;
+ break;
+ }
+ }
+
+ $now = microtime(true);
+
+ if (!$failed) {
+ if ($this->lastUpdateTime) {
+ $delay = 1;
+ if (($now - $this->lastUpdateTime) < $delay) {
+ $this->buffer[] = $results;
+ return;
+ }
+ }
+ }
+
+ $this->buffer[] = $results;
+ $results = array_mergev($this->buffer);
+ $this->buffer = array();
+
+ $pass_count = 0;
+ $skip_count = 0;
+ $failed = array();
+ foreach ($results as $result) {
+ $result_code = $result->getResult();
+ switch ($result_code) {
+ case ArcanistUnitTestResult::RESULT_PASS:
+ $pass_count++;
+ break;
+ case ArcanistUnitTestResult::RESULT_SKIP:
+ $skip_count++;
+ break;
+ default:
+ $failed[] = $result;
+ break;
+ }
+ }
+
+ if ($pass_count) {
+ echo tsprintf(
+ "%s\n",
+ pht('%s tests passed.', $pass_count));
+ }
+
+ if ($skip_count) {
+ echo tsprintf(
+ "%s\n",
+ pht('%s tests skipped.', $skip_count));
+ }
+
+ foreach ($failed as $result) {
+ echo $this->getDisplayForTest($result, false);
+ }
+
+ $this->lastUpdateTime = $now;
+
+ return $this;
+ }
+
+ public function sinkFinalResults(array $results) {
+
+ $passed = array();
+ $skipped = array();
+ $failed = array();
+ foreach ($results as $result) {
+ $result_code = $result->getResult();
+ switch ($result_code) {
+ case ArcanistUnitTestResult::RESULT_PASS:
+ $passed[] = $result;
+ break;
+ case ArcanistUnitTestResult::RESULT_SKIP:
+ $skipped[] = $result;
+ break;
+ default:
+ $failed[] = $result;
+ break;
+ }
+ }
+
+ echo tsprintf(
+ "%s\n",
+ pht('RESULT SUMMARY'));
+
+
+ if ($skipped) {
+ echo tsprintf(
+ "%s\n",
+ pht('SKIPPED TESTS'));
+
+ foreach ($skipped as $result) {
+ echo $this->getDisplayForTest($result);
+ }
+ }
+
+ if ($failed) {
+ echo tsprintf(
+ "%s\n",
+ pht('FAILED TESTS'));
+
+ foreach ($failed as $result) {
+ echo $this->getDisplayForTest($result);
+ }
+ }
+
+
+ echo tsprintf(
+ "**<bg:red> ~~~ %s </bg>**\n",
+ pht(
+ "%s PASSED * %s SKIPPED * %s FAILED/BROKEN/UNSTABLE",
+ phutil_count($passed),
+ phutil_count($skipped),
+ phutil_count($failed)));
+ }
+
+ private function getDisplayForTest(ArcanistUnitTestResult $result) {
+
+ $color = $result->getANSIColor();
+ $status = $result->getResultLabel();
+ $name = $result->getDisplayName();
+
+ // TOOLSETS: Restore timing information.
+ $timing = ' ';
+
+ $output = tsprintf(
+ "**<bg:".$color."> %s </bg>** %s %s\n",
+ $status,
+ $timing,
+ $name);
+
+ $user_data = $result->getUserData();
+ if (strlen($user_data)) {
+ $output = tsprintf(
+ "%s%B\n",
+ $output,
+ $user_data);
+ }
+
+ return $output;
+ }
+
+}
diff --git a/src/unit/sink/ArcanistJSONUnitSink.php b/src/unit/sink/ArcanistJSONUnitSink.php
new file mode 100644
--- /dev/null
+++ b/src/unit/sink/ArcanistJSONUnitSink.php
@@ -0,0 +1,9 @@
+<?php
+
+final class ArcanistJSONUnitSink
+ extends ArcanistUnitSink {
+
+ const SINKKEY = 'json';
+
+
+}
diff --git a/src/unit/sink/ArcanistUnitSink.php b/src/unit/sink/ArcanistUnitSink.php
new file mode 100644
--- /dev/null
+++ b/src/unit/sink/ArcanistUnitSink.php
@@ -0,0 +1,31 @@
+<?php
+
+abstract class ArcanistUnitSink
+ extends Phobject {
+
+ private $results;
+
+ final public function getUnitSinkKey() {
+ return $this->getPhobjectClassConstant('SINKKEY');
+ }
+
+ public static function getAllUnitSinks() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass(__CLASS__)
+ ->setUniqueMethod('getUnitSinkKey')
+ ->execute();
+ }
+
+ public function sinkPartialResults(array $results) {
+ return $this;
+ }
+
+ public function sinkFinalResults(array $results) {
+ return $this;
+ }
+
+ public function getOutput() {
+ return null;
+ }
+
+}
diff --git a/src/workflow/ArcanistUnitWorkflow.php b/src/workflow/ArcanistUnitWorkflow.php
--- a/src/workflow/ArcanistUnitWorkflow.php
+++ b/src/workflow/ArcanistUnitWorkflow.php
@@ -23,7 +23,7 @@
return array(
$this->newWorkflowArgument('commit')
->setParameter('commit'),
- $this->newWorkflowArgument('format')
+ $this->newWorkflowArgument('sink')
->setParameter('format'),
$this->newWorkflowArgument('everything'),
$this->newWorkflowArgument('paths')
@@ -51,32 +51,40 @@
// though it is "arc unit --everything", and ignoring the "--commit" flag
// and "paths" arguments.
- $formatter = $this->newUnitFormatter();
- $overseer->setFormatter($formatter);
+ $sinks = array();
+ $sinks[] = $this->newUnitSink();
+ $overseer->setSinks($sinks);
$overseer->execute();
+ foreach ($sinks as $sink) {
+ $result = $sink->getOutput();
+ if ($result !== null) {
+ echo $result;
+ }
+ }
+
return 0;
}
- private function newUnitFormatter() {
- $formatters = ArcanistUnitFormatter::getAllUnitFormatters();
- $format_key = $this->getArgument('format');
- if (!strlen($format_key)) {
- $format_key = ArcanistDefaultUnitFormatter::FORMATTER_KEY;
+ private function newUnitSink() {
+ $sinks = ArcanistUnitSink::getAllUnitSinks();
+ $sink_key = $this->getArgument('sink');
+ if (!strlen($sink_key)) {
+ $sink_key = ArcanistDefaultUnitSink::SINKKEY;
}
- $formatter = idx($formatters, $format_key);
- if (!$formatter) {
+ $sink = idx($sinks, $sink_key);
+ if (!$sink) {
throw new ArcanistUsageException(
pht(
- 'Unit test output format ("%s") is unknown. Supported formats '.
+ 'Unit test output sink ("%s") is unknown. Supported sinks '.
'are: %s.',
- $format_key,
- implode(', ', array_keys($formatters))));
+ $sink_key,
+ implode(', ', array_keys($sinks))));
}
- return $formatter;
+ return $sink;
}
}

File Metadata

Mime Type
text/plain
Expires
Mon, May 20, 3:42 PM (1 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6289920
Default Alt Text
D19711.diff (17 KB)

Event Timeline