Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15282703
D11672.id31085.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
55 KB
Referenced Files
None
Subscribers
None
D11672.id31085.diff
View Options
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
@@ -25,6 +25,7 @@
'ArcanistCSSLintLinter' => 'lint/linter/ArcanistCSSLintLinter.php',
'ArcanistCSSLintLinterTestCase' => 'lint/linter/__tests__/ArcanistCSSLintLinterTestCase.php',
'ArcanistCSharpLinter' => 'lint/linter/ArcanistCSharpLinter.php',
+ 'ArcanistCSharpToolsTestEngine' => 'unit/engine/ArcanistCSharpToolsTestEngine.php',
'ArcanistCallConduitWorkflow' => 'workflow/ArcanistCallConduitWorkflow.php',
'ArcanistCapabilityNotSupportedException' => 'workflow/exception/ArcanistCapabilityNotSupportedException.php',
'ArcanistCheckstyleXMLLintRenderer' => 'lint/renderer/ArcanistCheckstyleXMLLintRenderer.php',
@@ -128,6 +129,7 @@
'ArcanistNoLintLinter' => 'lint/linter/ArcanistNoLintLinter.php',
'ArcanistNoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistNoLintLinterTestCase.php',
'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php',
+ 'ArcanistNoseTestEngine' => 'unit/engine/ArcanistNoseTestEngine.php',
'ArcanistPEP8Linter' => 'lint/linter/ArcanistPEP8Linter.php',
'ArcanistPEP8LinterTestCase' => 'lint/linter/__tests__/ArcanistPEP8LinterTestCase.php',
'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php',
@@ -136,6 +138,8 @@
'ArcanistPhpLinterTestCase' => 'lint/linter/__tests__/ArcanistPhpLinterTestCase.php',
'ArcanistPhpcsLinter' => 'lint/linter/ArcanistPhpcsLinter.php',
'ArcanistPhpcsLinterTestCase' => 'lint/linter/__tests__/ArcanistPhpcsLinterTestCase.php',
+ 'ArcanistPhpunitTestEngine' => 'unit/engine/ArcanistPhpunitTestEngine.php',
+ 'ArcanistPhpunitTestEngineTestCase' => 'unit/engine/__tests__/ArcanistPhpunitTestEngineTestCase.php',
'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php',
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
@@ -151,6 +155,7 @@
'ArcanistPyFlakesLinterTestCase' => 'lint/linter/__tests__/ArcanistPyFlakesLinterTestCase.php',
'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php',
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
+ 'ArcanistPytestTestEngine' => 'unit/engine/__tests__/ArcanistPytestTestEngine.php',
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
@@ -200,16 +205,16 @@
'ArcanistXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistXHPASTLinterTestCase.php',
'ArcanistXMLLinter' => 'lint/linter/ArcanistXMLLinter.php',
'ArcanistXMLLinterTestCase' => 'lint/linter/__tests__/ArcanistXMLLinterTestCase.php',
+ 'ArcanistXUnitTestEngine' => 'unit/engine/ArcanistXUnitTestEngine.php',
'ArcanistXUnitTestResultParser' => 'unit/parser/ArcanistXUnitTestResultParser.php',
+ 'ArcanistXUnitTestResultParserTestCase' => 'unit/parser/__tests__/ArcanistXUnitTestResultParserTestCase.php',
'CSharpToolsTestEngine' => 'unit/engine/CSharpToolsTestEngine.php',
'NoseTestEngine' => 'unit/engine/NoseTestEngine.php',
'PhpunitTestEngine' => 'unit/engine/PhpunitTestEngine.php',
- 'PhpunitTestEngineTestCase' => 'unit/engine/__tests__/PhpunitTestEngineTestCase.php',
'PhutilUnitTestEngine' => 'unit/engine/PhutilUnitTestEngine.php',
'PhutilUnitTestEngineTestCase' => 'unit/engine/__tests__/PhutilUnitTestEngineTestCase.php',
'PytestTestEngine' => 'unit/engine/PytestTestEngine.php',
'XUnitTestEngine' => 'unit/engine/XUnitTestEngine.php',
- 'XUnitTestResultParserTestCase' => 'unit/parser/__tests__/XUnitTestResultParserTestCase.php',
),
'function' => array(),
'xmap' => array(
@@ -227,6 +232,7 @@
'ArcanistCSSLintLinter' => 'ArcanistExternalLinter',
'ArcanistCSSLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistCSharpLinter' => 'ArcanistLinter',
+ 'ArcanistCSharpToolsTestEngine' => 'ArcanistXUnitTestEngine',
'ArcanistCallConduitWorkflow' => 'ArcanistWorkflow',
'ArcanistCapabilityNotSupportedException' => 'Exception',
'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer',
@@ -309,6 +315,7 @@
'ArcanistNoLintLinter' => 'ArcanistLinter',
'ArcanistNoLintLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer',
+ 'ArcanistNoseTestEngine' => 'ArcanistUnitTestEngine',
'ArcanistPEP8Linter' => 'ArcanistExternalLinter',
'ArcanistPEP8LinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistPasteWorkflow' => 'ArcanistWorkflow',
@@ -317,6 +324,8 @@
'ArcanistPhpLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistPhpcsLinter' => 'ArcanistExternalLinter',
'ArcanistPhpcsLinterTestCase' => 'ArcanistExternalLinterTestCase',
+ 'ArcanistPhpunitTestEngine' => 'ArcanistUnitTestEngine',
+ 'ArcanistPhpunitTestEngineTestCase' => 'ArcanistTestCase',
'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser',
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
@@ -331,6 +340,7 @@
'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistPyLintLinter' => 'ArcanistExternalLinter',
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
+ 'ArcanistPytestTestEngine' => 'ArcanistUnitTestEngine',
'ArcanistRepositoryAPIMiscTestCase' => 'ArcanistTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'ArcanistTestCase',
'ArcanistRevertWorkflow' => 'ArcanistWorkflow',
@@ -371,14 +381,14 @@
'ArcanistXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistXMLLinter' => 'ArcanistLinter',
'ArcanistXMLLinterTestCase' => 'ArcanistLinterTestCase',
- 'CSharpToolsTestEngine' => 'XUnitTestEngine',
- 'NoseTestEngine' => 'ArcanistUnitTestEngine',
- 'PhpunitTestEngine' => 'ArcanistUnitTestEngine',
- 'PhpunitTestEngineTestCase' => 'ArcanistTestCase',
+ 'ArcanistXUnitTestEngine' => 'ArcanistUnitTestEngine',
+ 'ArcanistXUnitTestResultParserTestCase' => 'ArcanistTestCase',
+ 'CSharpToolsTestEngine' => 'ArcanistCSharpToolsTestEngine',
+ 'NoseTestEngine' => 'ArcanistNoseTestEngine',
+ 'PhpunitTestEngine' => 'ArcanistPhpunitTestEngine',
'PhutilUnitTestEngine' => 'ArcanistUnitTestEngine',
'PhutilUnitTestEngineTestCase' => 'ArcanistTestCase',
- 'PytestTestEngine' => 'ArcanistUnitTestEngine',
- 'XUnitTestEngine' => 'ArcanistUnitTestEngine',
- 'XUnitTestResultParserTestCase' => 'ArcanistTestCase',
+ 'PytestTestEngine' => 'ArcanistPytestTestEngine',
+ 'XUnitTestEngine' => 'ArcanistXUnitTestEngine',
),
));
diff --git a/src/unit/engine/CSharpToolsTestEngine.php b/src/unit/engine/ArcanistCSharpToolsTestEngine.php
copy from src/unit/engine/CSharpToolsTestEngine.php
copy to src/unit/engine/ArcanistCSharpToolsTestEngine.php
--- a/src/unit/engine/CSharpToolsTestEngine.php
+++ b/src/unit/engine/ArcanistCSharpToolsTestEngine.php
@@ -3,11 +3,13 @@
/**
* Uses cscover (http://github.com/hach-que/cstools) to report code coverage.
*
- * This engine inherits from `XUnitTestEngine`, where xUnit is used to actually
- * run the unit tests and this class provides a thin layer on top to collect
- * code coverage data with a third-party tool.
+ * This engine inherits from @{class:ArcanistXUnitTestEngine}, where xUnit is
+ * used to actually run the unit tests and this class provides a thin layer on
+ * top to collect code coverage data with a third-party tool.
+ *
+ * @todo Should be final but isn't because of @{class:CSharpToolsTestEngine}.
*/
-final class CSharpToolsTestEngine extends XUnitTestEngine {
+class ArcanistCSharpToolsTestEngine extends ArcanistXUnitTestEngine {
private $cscoverHintPath;
private $coverEngine;
diff --git a/src/unit/engine/NoseTestEngine.php b/src/unit/engine/ArcanistNoseTestEngine.php
copy from src/unit/engine/NoseTestEngine.php
copy to src/unit/engine/ArcanistNoseTestEngine.php
--- a/src/unit/engine/NoseTestEngine.php
+++ b/src/unit/engine/ArcanistNoseTestEngine.php
@@ -4,8 +4,10 @@
* Very basic 'nose' unit test engine wrapper.
*
* Requires nose 1.1.3 for code coverage.
+ *
+ * @todo Should be final but isn't because of @{class:NoseTestEngine}.
*/
-final class NoseTestEngine extends ArcanistUnitTestEngine {
+class ArcanistNoseTestEngine extends ArcanistUnitTestEngine {
public function run() {
$paths = $this->getPaths();
diff --git a/src/unit/engine/PhpunitTestEngine.php b/src/unit/engine/ArcanistPhpunitTestEngine.php
copy from src/unit/engine/PhpunitTestEngine.php
copy to src/unit/engine/ArcanistPhpunitTestEngine.php
--- a/src/unit/engine/PhpunitTestEngine.php
+++ b/src/unit/engine/ArcanistPhpunitTestEngine.php
@@ -2,8 +2,10 @@
/**
* PHPUnit wrapper.
+ *
+ * @todo Should be final but isn't because of @{class:PhpunitTestEngine}.
*/
-final class PhpunitTestEngine extends ArcanistUnitTestEngine {
+class ArcanistPhpunitTestEngine extends ArcanistUnitTestEngine {
private $configFile;
private $phpunitBinary = 'phpunit';
diff --git a/src/unit/engine/XUnitTestEngine.php b/src/unit/engine/ArcanistXUnitTestEngine.php
copy from src/unit/engine/XUnitTestEngine.php
copy to src/unit/engine/ArcanistXUnitTestEngine.php
--- a/src/unit/engine/XUnitTestEngine.php
+++ b/src/unit/engine/ArcanistXUnitTestEngine.php
@@ -9,7 +9,7 @@
*
* @concrete-extensible
*/
-class XUnitTestEngine extends ArcanistUnitTestEngine {
+class ArcanistXUnitTestEngine extends ArcanistUnitTestEngine {
protected $runtimeEngine;
protected $buildEngine;
diff --git a/src/unit/engine/CSharpToolsTestEngine.php b/src/unit/engine/CSharpToolsTestEngine.php
--- a/src/unit/engine/CSharpToolsTestEngine.php
+++ b/src/unit/engine/CSharpToolsTestEngine.php
@@ -1,287 +1,17 @@
<?php
/**
- * Uses cscover (http://github.com/hach-que/cstools) to report code coverage.
- *
- * This engine inherits from `XUnitTestEngine`, where xUnit is used to actually
- * run the unit tests and this class provides a thin layer on top to collect
- * code coverage data with a third-party tool.
+ * @deprecated
*/
-final class CSharpToolsTestEngine extends XUnitTestEngine {
-
- private $cscoverHintPath;
- private $coverEngine;
- private $cachedResults;
- private $matchRegex;
- private $excludedFiles;
-
- /**
- * Overridden version of `loadEnvironment` to support a different set of
- * configuration values and to pull in the cstools config for code coverage.
- */
- protected function loadEnvironment() {
- $config = $this->getConfigurationManager();
- $this->cscoverHintPath = $config->getConfigFromAnySource(
- 'unit.csharp.cscover.binary');
- $this->matchRegex = $config->getConfigFromAnySource(
- 'unit.csharp.coverage.match');
- $this->excludedFiles = $config->getConfigFromAnySource(
- 'unit.csharp.coverage.excluded');
-
- parent::loadEnvironment();
-
- if ($this->getEnableCoverage() === false) {
- return;
- }
-
- // Determine coverage path.
- if ($this->cscoverHintPath === null) {
- throw new Exception(
- pht(
- "Unable to locate %s. Configure it with the '%s' option in %s.",
- 'cscover',
- 'unit.csharp.coverage.binary',
- '.arcconfig'));
- }
- $cscover = $this->projectRoot.DIRECTORY_SEPARATOR.$this->cscoverHintPath;
- if (file_exists($cscover)) {
- $this->coverEngine = Filesystem::resolvePath($cscover);
- } else {
- throw new Exception(
- pht(
- 'Unable to locate %s coverage runner (have you built yet?)',
- 'cscover'));
- }
- }
-
- /**
- * Returns whether the specified assembly should be instrumented for
- * code coverage reporting. Checks the excluded file list and the
- * matching regex if they are configured.
- *
- * @return boolean Whether the assembly should be instrumented.
- */
- private function assemblyShouldBeInstrumented($file) {
- if ($this->excludedFiles !== null) {
- if (array_key_exists((string)$file, $this->excludedFiles)) {
- return false;
- }
- }
- if ($this->matchRegex !== null) {
- if (preg_match($this->matchRegex, $file) === 1) {
- return true;
- } else {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Overridden version of `buildTestFuture` so that the unit test can be run
- * via `cscover`, which instruments assemblies and reports on code coverage.
- *
- * @param string Name of the test assembly.
- * @return array The future, output filename and coverage filename
- * stored in an array.
- */
- protected function buildTestFuture($test_assembly) {
- if ($this->getEnableCoverage() === false) {
- return parent::buildTestFuture($test_assembly);
- }
-
- // FIXME: Can't use TempFile here as xUnit doesn't like
- // UNIX-style full paths. It sees the leading / as the
- // start of an option flag, even when quoted.
- $xunit_temp = Filesystem::readRandomCharacters(10).'.results.xml';
- if (file_exists($xunit_temp)) {
- unlink($xunit_temp);
- }
- $cover_temp = new TempFile();
- $cover_temp->setPreserveFile(true);
- $xunit_cmd = $this->runtimeEngine;
- $xunit_args = null;
- if ($xunit_cmd === '') {
- $xunit_cmd = $this->testEngine;
- $xunit_args = csprintf(
- '%s /xml %s',
- $test_assembly,
- $xunit_temp);
- } else {
- $xunit_args = csprintf(
- '%s %s /xml %s',
- $this->testEngine,
- $test_assembly,
- $xunit_temp);
- }
- $assembly_dir = dirname($test_assembly);
- $assemblies_to_instrument = array();
- foreach (Filesystem::listDirectory($assembly_dir) as $file) {
- if (substr($file, -4) == '.dll' || substr($file, -4) == '.exe') {
- if ($this->assemblyShouldBeInstrumented($file)) {
- $assemblies_to_instrument[] = $assembly_dir.DIRECTORY_SEPARATOR.$file;
- }
- }
- }
- if (count($assemblies_to_instrument) === 0) {
- return parent::buildTestFuture($test_assembly);
- }
- $future = new ExecFuture(
- '%C -o %s -c %s -a %s -w %s %Ls',
- trim($this->runtimeEngine.' '.$this->coverEngine),
- $cover_temp,
- $xunit_cmd,
- $xunit_args,
- $assembly_dir,
- $assemblies_to_instrument);
- $future->setCWD(Filesystem::resolvePath($this->projectRoot));
- return array(
- $future,
- $assembly_dir.DIRECTORY_SEPARATOR.$xunit_temp,
- $cover_temp,
- );
- }
-
- /**
- * Returns coverage results for the unit tests.
- *
- * @param string The name of the coverage file if one was provided by
- * `buildTestFuture`.
- * @return array Code coverage results, or null.
- */
- protected function parseCoverageResult($cover_file) {
- if ($this->getEnableCoverage() === false) {
- return parent::parseCoverageResult($cover_file);
- }
- return $this->readCoverage($cover_file);
- }
-
- /**
- * Retrieves the cached results for a coverage result file. The coverage
- * result file is XML and can be large depending on what has been instrumented
- * so we cache it in case it's requested again.
- *
- * @param string The name of the coverage file.
- * @return array Code coverage results, or null if not cached.
- */
- private function getCachedResultsIfPossible($cover_file) {
- if ($this->cachedResults == null) {
- $this->cachedResults = array();
- }
- if (array_key_exists((string)$cover_file, $this->cachedResults)) {
- return $this->cachedResults[(string)$cover_file];
- }
- return null;
- }
-
- /**
- * Stores the code coverage results in the cache.
- *
- * @param string The name of the coverage file.
- * @param array The results to cache.
- */
- private function addCachedResults($cover_file, array $results) {
- if ($this->cachedResults == null) {
- $this->cachedResults = array();
- }
- $this->cachedResults[(string)$cover_file] = $results;
- }
-
- /**
- * Processes a set of XML tags as code coverage results. We parse
- * the `instrumented` and `executed` tags with this method so that
- * we can access the data multiple times without a performance hit.
- *
- * @param array The array of XML tags to parse.
- * @return array A PHP array containing the data.
- */
- private function processTags($tags) {
- $results = array();
- foreach ($tags as $tag) {
- $results[] = array(
- 'file' => $tag->getAttribute('file'),
- 'start' => $tag->getAttribute('start'),
- 'end' => $tag->getAttribute('end'),
- );
- }
- return $results;
- }
-
- /**
- * Reads the code coverage results from the cscover results file.
- *
- * @param string The path to the code coverage file.
- * @return array The code coverage results.
- */
- public function readCoverage($cover_file) {
- $cached = $this->getCachedResultsIfPossible($cover_file);
- if ($cached !== null) {
- return $cached;
- }
-
- $coverage_dom = new DOMDocument();
- $coverage_dom->loadXML(Filesystem::readFile($cover_file));
-
- $modified = $this->getPaths();
- $files = array();
- $reports = array();
- $instrumented = array();
- $executed = array();
-
- $instrumented = $this->processTags(
- $coverage_dom->getElementsByTagName('instrumented'));
- $executed = $this->processTags(
- $coverage_dom->getElementsByTagName('executed'));
-
- foreach ($instrumented as $instrument) {
- $absolute_file = $instrument['file'];
- $relative_file = substr($absolute_file, strlen($this->projectRoot) + 1);
- if (!in_array($relative_file, $files)) {
- $files[] = $relative_file;
- }
- }
-
- foreach ($files as $file) {
- $absolute_file = Filesystem::resolvePath(
- $this->projectRoot.DIRECTORY_SEPARATOR.$file);
-
- // get total line count in file
- $line_count = count(file($absolute_file));
-
- $coverage = array();
- for ($i = 0; $i < $line_count; $i++) {
- $coverage[$i] = 'N';
- }
-
- foreach ($instrumented as $instrument) {
- if ($instrument['file'] !== $absolute_file) {
- continue;
- }
- for (
- $i = $instrument['start'];
- $i <= $instrument['end'];
- $i++) {
- $coverage[$i - 1] = 'U';
- }
- }
-
- foreach ($executed as $execute) {
- if ($execute['file'] !== $absolute_file) {
- continue;
- }
- for (
- $i = $execute['start'];
- $i <= $execute['end'];
- $i++) {
- $coverage[$i - 1] = 'C';
- }
- }
-
- $reports[$file] = implode($coverage);
- }
-
- $this->addCachedResults($cover_file, $reports);
- return $reports;
+final class CSharpToolsTestEngine extends ArcanistCSharpToolsTestEngine {
+
+ public function run() {
+ phutil_deprecated(
+ __CLASS__,
+ pht(
+ 'You should use `%s` instead.',
+ 'ArcanistCSharpToolsTestEngine'));
+ parent::run();
}
}
diff --git a/src/unit/engine/NoseTestEngine.php b/src/unit/engine/NoseTestEngine.php
--- a/src/unit/engine/NoseTestEngine.php
+++ b/src/unit/engine/NoseTestEngine.php
@@ -1,166 +1,17 @@
<?php
/**
- * Very basic 'nose' unit test engine wrapper.
- *
- * Requires nose 1.1.3 for code coverage.
+ * @deprecated
*/
-final class NoseTestEngine extends ArcanistUnitTestEngine {
+final class NoseTestEngine extends ArcanistNoseTestEngine {
public function run() {
- $paths = $this->getPaths();
-
- $affected_tests = array();
- foreach ($paths as $path) {
- $absolute_path = Filesystem::resolvePath($path);
-
- if (is_dir($absolute_path)) {
- $absolute_test_path = Filesystem::resolvePath('tests/'.$path);
- if (is_readable($absolute_test_path)) {
- $affected_tests[] = $absolute_test_path;
- }
- }
-
- if (is_readable($absolute_path)) {
- $filename = basename($path);
- $directory = dirname($path);
-
- // assumes directory layout: tests/<package>/test_<module>.py
- $relative_test_path = 'tests/'.$directory.'/test_'.$filename;
- $absolute_test_path = Filesystem::resolvePath($relative_test_path);
-
- if (is_readable($absolute_test_path)) {
- $affected_tests[] = $absolute_test_path;
- }
- }
- }
-
- return $this->runTests($affected_tests, './');
- }
-
- public function runTests($test_paths, $source_path) {
- if (empty($test_paths)) {
- return array();
- }
-
- $futures = array();
- $tmpfiles = array();
- foreach ($test_paths as $test_path) {
- $xunit_tmp = new TempFile();
- $cover_tmp = new TempFile();
-
- $future = $this->buildTestFuture($test_path, $xunit_tmp, $cover_tmp);
-
- $futures[$test_path] = $future;
- $tmpfiles[$test_path] = array(
- 'xunit' => $xunit_tmp,
- 'cover' => $cover_tmp,
- );
- }
-
- $results = array();
- $futures = id(new FutureIterator($futures))
- ->limit(4);
- foreach ($futures as $test_path => $future) {
- try {
- list($stdout, $stderr) = $future->resolvex();
- } catch(CommandException $exc) {
- if ($exc->getError() > 1) {
- // 'nose' returns 1 when tests are failing/broken.
- throw $exc;
- }
- }
-
- $xunit_tmp = $tmpfiles[$test_path]['xunit'];
- $cover_tmp = $tmpfiles[$test_path]['cover'];
-
- $this->parser = new ArcanistXUnitTestResultParser();
- $results[] = $this->parseTestResults(
- $source_path,
- $xunit_tmp,
- $cover_tmp);
- }
-
- return array_mergev($results);
- }
-
- public function buildTestFuture($path, $xunit_tmp, $cover_tmp) {
- $cmd_line = csprintf(
- 'nosetests --with-xunit --xunit-file=%s',
- $xunit_tmp);
-
- if ($this->getEnableCoverage() !== false) {
- $cmd_line .= csprintf(
- ' --with-coverage --cover-xml --cover-xml-file=%s',
- $cover_tmp);
- }
-
- return new ExecFuture('%C %s', $cmd_line, $path);
- }
-
- public function parseTestResults($source_path, $xunit_tmp, $cover_tmp) {
- $results = $this->parser->parseTestResults(
- Filesystem::readFile($xunit_tmp));
-
- // coverage is for all testcases in the executed $path
- if ($this->getEnableCoverage() !== false) {
- $coverage = $this->readCoverage($cover_tmp, $source_path);
- foreach ($results as $result) {
- $result->setCoverage($coverage);
- }
- }
-
- return $results;
- }
-
- public function readCoverage($cover_file, $source_path) {
- $coverage_dom = new DOMDocument();
- $coverage_dom->loadXML(Filesystem::readFile($cover_file));
-
- $reports = array();
- $classes = $coverage_dom->getElementsByTagName('class');
-
- foreach ($classes as $class) {
- $path = $class->getAttribute('filename');
- $root = $this->getWorkingCopy()->getProjectRoot();
-
- if (!Filesystem::isDescendant($path, $root)) {
- continue;
- }
-
- // get total line count in file
- $line_count = count(phutil_split_lines(Filesystem::readFile($path)));
-
- $coverage = '';
- $start_line = 1;
- $lines = $class->getElementsByTagName('line');
- for ($ii = 0; $ii < $lines->length; $ii++) {
- $line = $lines->item($ii);
-
- $next_line = intval($line->getAttribute('number'));
- for ($start_line; $start_line < $next_line; $start_line++) {
- $coverage .= 'N';
- }
-
- if (intval($line->getAttribute('hits')) == 0) {
- $coverage .= 'U';
- } else if (intval($line->getAttribute('hits')) > 0) {
- $coverage .= 'C';
- }
-
- $start_line++;
- }
-
- if ($start_line < $line_count) {
- foreach (range($start_line, $line_count) as $line_num) {
- $coverage .= 'N';
- }
- }
-
- $reports[$path] = $coverage;
- }
-
- return $reports;
+ phutil_deprecated(
+ __CLASS__,
+ pht(
+ 'You should use `%s` instead.',
+ 'ArcanistNoseTestEngine'));
+ parent::run();
}
}
diff --git a/src/unit/engine/PhpunitTestEngine.php b/src/unit/engine/PhpunitTestEngine.php
--- a/src/unit/engine/PhpunitTestEngine.php
+++ b/src/unit/engine/PhpunitTestEngine.php
@@ -1,280 +1,17 @@
<?php
/**
- * PHPUnit wrapper.
+ * @deprecated
*/
-final class PhpunitTestEngine extends ArcanistUnitTestEngine {
-
- private $configFile;
- private $phpunitBinary = 'phpunit';
- private $affectedTests;
- private $projectRoot;
+final class PhpunitTestEngine extends ArcanistPhpunitTestEngine {
public function run() {
- $this->projectRoot = $this->getWorkingCopy()->getProjectRoot();
- $this->affectedTests = array();
- foreach ($this->getPaths() as $path) {
-
- $path = Filesystem::resolvePath($path, $this->projectRoot);
-
- // TODO: add support for directories
- // Users can call phpunit on the directory themselves
- if (is_dir($path)) {
- continue;
- }
-
- // Not sure if it would make sense to go further if
- // it is not a .php file
- if (substr($path, -4) != '.php') {
- continue;
- }
-
- if (substr($path, -8) == 'Test.php') {
- // Looks like a valid test file name.
- $this->affectedTests[$path] = $path;
- continue;
- }
-
- if ($test = $this->findTestFile($path)) {
- $this->affectedTests[$path] = $test;
- }
-
- }
-
- if (empty($this->affectedTests)) {
- throw new ArcanistNoEffectException(pht('No tests to run.'));
- }
-
- $this->prepareConfigFile();
- $futures = array();
- $tmpfiles = array();
- foreach ($this->affectedTests as $class_path => $test_path) {
- if (!Filesystem::pathExists($test_path)) {
- continue;
- }
- $json_tmp = new TempFile();
- $clover_tmp = null;
- $clover = null;
- if ($this->getEnableCoverage() !== false) {
- $clover_tmp = new TempFile();
- $clover = csprintf('--coverage-clover %s', $clover_tmp);
- }
-
- $config = $this->configFile ? csprintf('-c %s', $this->configFile) : null;
-
- $stderr = '-d display_errors=stderr';
-
- $futures[$test_path] = new ExecFuture('%C %C %C --log-json %s %C %s',
- $this->phpunitBinary, $config, $stderr, $json_tmp, $clover, $test_path);
- $tmpfiles[$test_path] = array(
- 'json' => $json_tmp,
- 'clover' => $clover_tmp,
- );
- }
-
- $results = array();
- $futures = id(new FutureIterator($futures))
- ->limit(4);
- foreach ($futures as $test => $future) {
-
- list($err, $stdout, $stderr) = $future->resolve();
-
- $results[] = $this->parseTestResults(
- $test,
- $tmpfiles[$test]['json'],
- $tmpfiles[$test]['clover'],
- $stderr);
- }
-
- return array_mergev($results);
- }
-
- /**
- * Parse test results from phpunit json report.
- *
- * @param string $path Path to test
- * @param string $json_tmp Path to phpunit json report
- * @param string $clover_tmp Path to phpunit clover report
- * @param string $stderr Data written to stderr
- *
- * @return array
- */
- private function parseTestResults($path, $json_tmp, $clover_tmp, $stderr) {
- $test_results = Filesystem::readFile($json_tmp);
- return id(new ArcanistPhpunitTestResultParser())
- ->setEnableCoverage($this->getEnableCoverage())
- ->setProjectRoot($this->projectRoot)
- ->setCoverageFile($clover_tmp)
- ->setAffectedTests($this->affectedTests)
- ->setStderr($stderr)
- ->parseTestResults($path, $test_results);
- }
-
-
- /**
- * Search for test cases for a given file in a large number of "reasonable"
- * locations. See @{method:getSearchLocationsForTests} for specifics.
- *
- * TODO: Add support for finding tests in testsuite folders from
- * phpunit.xml configuration.
- *
- * @param string PHP file to locate test cases for.
- * @return string|null Path to test cases, or null.
- */
- private function findTestFile($path) {
- $root = $this->projectRoot;
- $path = Filesystem::resolvePath($path, $root);
-
- $file = basename($path);
- $possible_files = array(
- $file,
- substr($file, 0, -4).'Test.php',
- );
-
- $search = self::getSearchLocationsForTests($path);
-
- foreach ($search as $search_path) {
- foreach ($possible_files as $possible_file) {
- $full_path = $search_path.$possible_file;
- if (!Filesystem::pathExists($full_path)) {
- // If the file doesn't exist, it's clearly a miss.
- continue;
- }
- if (!Filesystem::isDescendant($full_path, $root)) {
- // Don't look above the project root.
- continue;
- }
- if (0 == strcasecmp(Filesystem::resolvePath($full_path), $path)) {
- // Don't return the original file.
- continue;
- }
- return $full_path;
- }
- }
-
- return null;
- }
-
-
- /**
- * Get places to look for PHP Unit tests that cover a given file. For some
- * file "/a/b/c/X.php", we look in the same directory:
- *
- * /a/b/c/
- *
- * We then look in all parent directories for a directory named "tests/"
- * (or "Tests/"):
- *
- * /a/b/c/tests/
- * /a/b/tests/
- * /a/tests/
- * /tests/
- *
- * We also try to replace each directory component with "tests/":
- *
- * /a/b/tests/
- * /a/tests/c/
- * /tests/b/c/
- *
- * We also try to add "tests/" at each directory level:
- *
- * /a/b/c/tests/
- * /a/b/tests/c/
- * /a/tests/b/c/
- * /tests/a/b/c/
- *
- * This finds tests with a layout like:
- *
- * docs/
- * src/
- * tests/
- *
- * ...or similar. This list will be further pruned by the caller; it is
- * intentionally filesystem-agnostic to be unit testable.
- *
- * @param string PHP file to locate test cases for.
- * @return list<string> List of directories to search for tests in.
- */
- public static function getSearchLocationsForTests($path) {
- $file = basename($path);
- $dir = dirname($path);
-
- $test_dir_names = array('tests', 'Tests');
-
- $try_directories = array();
-
- // Try in the current directory.
- $try_directories[] = array($dir);
-
- // Try in a tests/ directory anywhere in the ancestry.
- foreach (Filesystem::walkToRoot($dir) as $parent_dir) {
- if ($parent_dir == '/') {
- // We'll restore this later.
- $parent_dir = '';
- }
- foreach ($test_dir_names as $test_dir_name) {
- $try_directories[] = array($parent_dir, $test_dir_name);
- }
- }
-
- // Try replacing each directory component with 'tests/'.
- $parts = trim($dir, DIRECTORY_SEPARATOR);
- $parts = explode(DIRECTORY_SEPARATOR, $parts);
- foreach (array_reverse(array_keys($parts)) as $key) {
- foreach ($test_dir_names as $test_dir_name) {
- $try = $parts;
- $try[$key] = $test_dir_name;
- array_unshift($try, '');
- $try_directories[] = $try;
- }
- }
-
- // Try adding 'tests/' at each level.
- foreach (array_reverse(array_keys($parts)) as $key) {
- foreach ($test_dir_names as $test_dir_name) {
- $try = $parts;
- $try[$key] = $test_dir_name.DIRECTORY_SEPARATOR.$try[$key];
- array_unshift($try, '');
- $try_directories[] = $try;
- }
- }
-
- $results = array();
- foreach ($try_directories as $parts) {
- $results[implode(DIRECTORY_SEPARATOR, $parts).DIRECTORY_SEPARATOR] = true;
- }
-
- return array_keys($results);
- }
-
- /**
- * Tries to find and update phpunit configuration file based on
- * `phpunit_config` option in `.arcconfig`.
- */
- private function prepareConfigFile() {
- $project_root = $this->projectRoot.DIRECTORY_SEPARATOR;
- $config = $this->getConfigurationManager()->getConfigFromAnySource(
- 'phpunit_config');
-
- if ($config) {
- if (Filesystem::pathExists($project_root.$config)) {
- $this->configFile = $project_root.$config;
- } else {
- throw new Exception(
- pht(
- 'PHPUnit configuration file was not found in %s',
- $project_root.$config));
- }
- }
- $bin = $this->getConfigurationManager()->getConfigFromAnySource(
- 'unit.phpunit.binary');
- if ($bin) {
- if (Filesystem::binaryExists($bin)) {
- $this->phpunitBinary = $bin;
- } else {
- $this->phpunitBinary = Filesystem::resolvePath($bin, $project_root);
- }
- }
+ phutil_deprecated(
+ __CLASS__,
+ pht(
+ 'You should use `%s` instead.',
+ 'ArcanistPhpunitTestEngine'));
+ parent::run();
}
}
diff --git a/src/unit/engine/PytestTestEngine.php b/src/unit/engine/PytestTestEngine.php
--- a/src/unit/engine/PytestTestEngine.php
+++ b/src/unit/engine/PytestTestEngine.php
@@ -1,143 +1,17 @@
<?php
/**
- * Very basic 'py.test' unit test engine wrapper.
+ * @deprecated
*/
-final class PytestTestEngine extends ArcanistUnitTestEngine {
+final class PytestTestEngine extends ArcanistPytestTestEngine {
public function run() {
- $working_copy = $this->getWorkingCopy();
- $this->project_root = $working_copy->getProjectRoot();
-
- $junit_tmp = new TempFile();
- $cover_tmp = new TempFile();
-
- $future = $this->buildTestFuture($junit_tmp, $cover_tmp);
- list($err, $stdout, $stderr) = $future->resolve();
-
- if (!Filesystem::pathExists($junit_tmp)) {
- throw new CommandException(
- pht('Command failed with error #%s!', $err),
- $future->getCommand(),
- $err,
- $stdout,
- $stderr);
- }
-
- $future = new ExecFuture('coverage xml -o %s', $cover_tmp);
- $future->setCWD($this->project_root);
- $future->resolvex();
-
- return $this->parseTestResults($junit_tmp, $cover_tmp);
- }
-
- public function buildTestFuture($junit_tmp, $cover_tmp) {
- $paths = $this->getPaths();
-
- $cmd_line = csprintf('py.test --junit-xml=%s', $junit_tmp);
-
- if ($this->getEnableCoverage() !== false) {
- $cmd_line = csprintf(
- 'coverage run --source %s -m %C',
- $this->project_root,
- $cmd_line);
- }
-
- return new ExecFuture('%C', $cmd_line);
- }
-
- public function parseTestResults($junit_tmp, $cover_tmp) {
- $parser = new ArcanistXUnitTestResultParser();
- $results = $parser->parseTestResults(
- Filesystem::readFile($junit_tmp));
-
- if ($this->getEnableCoverage() !== false) {
- $coverage_report = $this->readCoverage($cover_tmp);
- foreach ($results as $result) {
- $result->setCoverage($coverage_report);
- }
- }
-
- return $results;
- }
-
- public function readCoverage($path) {
- $coverage_data = Filesystem::readFile($path);
- if (empty($coverage_data)) {
- return array();
- }
-
- $coverage_dom = new DOMDocument();
- $coverage_dom->loadXML($coverage_data);
-
- $paths = $this->getPaths();
- $reports = array();
- $classes = $coverage_dom->getElementsByTagName('class');
-
- foreach ($classes as $class) {
- // filename is actually python module path with ".py" at the end,
- // e.g.: tornado.web.py
- $relative_path = explode('.', $class->getAttribute('filename'));
- array_pop($relative_path);
- $relative_path = implode('/', $relative_path);
-
- // first we check if the path is a directory (a Python package), if it is
- // set relative and absolute paths to have __init__.py at the end.
- $absolute_path = Filesystem::resolvePath($relative_path);
- if (is_dir($absolute_path)) {
- $relative_path .= '/__init__.py';
- $absolute_path .= '/__init__.py';
- }
-
- // then we check if the path with ".py" at the end is file (a Python
- // submodule), if it is - set relative and absolute paths to have
- // ".py" at the end.
- if (is_file($absolute_path.'.py')) {
- $relative_path .= '.py';
- $absolute_path .= '.py';
- }
-
- if (!file_exists($absolute_path)) {
- continue;
- }
-
- if (!in_array($relative_path, $paths)) {
- continue;
- }
-
- // get total line count in file
- $line_count = count(file($absolute_path));
-
- $coverage = '';
- $start_line = 1;
- $lines = $class->getElementsByTagName('line');
- for ($ii = 0; $ii < $lines->length; $ii++) {
- $line = $lines->item($ii);
-
- $next_line = intval($line->getAttribute('number'));
- for ($start_line; $start_line < $next_line; $start_line++) {
- $coverage .= 'N';
- }
-
- if (intval($line->getAttribute('hits')) == 0) {
- $coverage .= 'U';
- } else if (intval($line->getAttribute('hits')) > 0) {
- $coverage .= 'C';
- }
-
- $start_line++;
- }
-
- if ($start_line < $line_count) {
- foreach (range($start_line, $line_count) as $line_num) {
- $coverage .= 'N';
- }
- }
-
- $reports[$relative_path] = $coverage;
- }
-
- return $reports;
+ phutil_deprecated(
+ __CLASS__,
+ pht(
+ 'You should use `%s` instead.',
+ 'ArcanistPytestTestEngine'));
+ parent::run();
}
}
diff --git a/src/unit/engine/XUnitTestEngine.php b/src/unit/engine/XUnitTestEngine.php
--- a/src/unit/engine/XUnitTestEngine.php
+++ b/src/unit/engine/XUnitTestEngine.php
@@ -1,466 +1,18 @@
<?php
/**
- * Uses xUnit (http://xunit.codeplex.com/) to test C# code.
- *
- * Assumes that when modifying a file with a path like `SomeAssembly/MyFile.cs`,
- * that the test assembly that verifies the functionality of `SomeAssembly` is
- * located at `SomeAssembly.Tests`.
- *
* @concrete-extensible
+ * @deprecated
*/
-class XUnitTestEngine extends ArcanistUnitTestEngine {
+class XUnitTestEngine extends ArcanistXUnitTestEngine {
- protected $runtimeEngine;
- protected $buildEngine;
- protected $testEngine;
- protected $projectRoot;
- protected $xunitHintPath;
- protected $discoveryRules;
-
- /**
- * This test engine supports running all tests.
- */
- protected function supportsRunAllTests() {
- return true;
- }
-
- /**
- * Determines what executables and test paths to use. Between platforms this
- * also changes whether the test engine is run under .NET or Mono. It also
- * ensures that all of the required binaries are available for the tests to
- * run successfully.
- *
- * @return void
- */
- protected function loadEnvironment() {
- $this->projectRoot = $this->getWorkingCopy()->getProjectRoot();
-
- // Determine build engine.
- if (Filesystem::binaryExists('msbuild')) {
- $this->buildEngine = 'msbuild';
- } else if (Filesystem::binaryExists('xbuild')) {
- $this->buildEngine = 'xbuild';
- } else {
- throw new Exception(
- pht(
- 'Unable to find %s or %s in %s!',
- 'msbuild',
- 'xbuild',
- 'PATH'));
- }
-
- // Determine runtime engine (.NET or Mono).
- if (phutil_is_windows()) {
- $this->runtimeEngine = '';
- } else if (Filesystem::binaryExists('mono')) {
- $this->runtimeEngine = Filesystem::resolveBinary('mono');
- } else {
- throw new Exception(
- pht('Unable to find Mono and you are not on Windows!'));
- }
-
- // Read the discovery rules.
- $this->discoveryRules =
- $this->getConfigurationManager()->getConfigFromAnySource(
- 'unit.csharp.discovery');
- if ($this->discoveryRules === null) {
- throw new Exception(
- pht(
- 'You must configure discovery rules to map C# files '.
- 'back to test projects (`%s` in %s).',
- 'unit.csharp.discovery',
- '.arcconfig'));
- }
-
- // Determine xUnit test runner path.
- if ($this->xunitHintPath === null) {
- $this->xunitHintPath =
- $this->getConfigurationManager()->getConfigFromAnySource(
- 'unit.csharp.xunit.binary');
- }
- $xunit = $this->projectRoot.DIRECTORY_SEPARATOR.$this->xunitHintPath;
- if (file_exists($xunit) && $this->xunitHintPath !== null) {
- $this->testEngine = Filesystem::resolvePath($xunit);
- } else if (Filesystem::binaryExists('xunit.console.clr4.exe')) {
- $this->testEngine = 'xunit.console.clr4.exe';
- } else {
- throw new Exception(
- pht(
- "Unable to locate xUnit console runner. Configure ".
- "it with the `%s' option in %s.",
- 'unit.csharp.xunit.binary',
- '.arcconfig'));
- }
- }
-
- /**
- * Main entry point for the test engine. Determines what assemblies to build
- * and test based on the files that have changed.
- *
- * @return array Array of test results.
- */
public function run() {
-
- $this->loadEnvironment();
-
- if ($this->getRunAllTests()) {
- $paths = id(new FileFinder($this->projectRoot))->find();
- } else {
- $paths = $this->getPaths();
- }
-
- return $this->runAllTests($this->mapPathsToResults($paths));
- }
-
- /**
- * Applies the discovery rules to the set of paths specified.
- *
- * @param array Array of paths.
- * @return array Array of paths to test projects and assemblies.
- */
- public function mapPathsToResults(array $paths) {
- $results = array();
- foreach ($this->discoveryRules as $regex => $targets) {
- $regex = str_replace('/', '\\/', $regex);
- foreach ($paths as $path) {
- if (preg_match('/'.$regex.'/', $path) === 1) {
- foreach ($targets as $target) {
- // Index 0 is the test project (.csproj file)
- // Index 1 is the output assembly (.dll file)
- $project = preg_replace('/'.$regex.'/', $target[0], $path);
- $project = $this->projectRoot.DIRECTORY_SEPARATOR.$project;
- $assembly = preg_replace('/'.$regex.'/', $target[1], $path);
- $assembly = $this->projectRoot.DIRECTORY_SEPARATOR.$assembly;
- if (file_exists($project)) {
- $project = Filesystem::resolvePath($project);
- $assembly = Filesystem::resolvePath($assembly);
-
- // Check to ensure uniqueness.
- $exists = false;
- foreach ($results as $existing) {
- if ($existing['assembly'] === $assembly) {
- $exists = true;
- break;
- }
- }
-
- if (!$exists) {
- $results[] = array(
- 'project' => $project,
- 'assembly' => $assembly,
- );
- }
- }
- }
- }
- }
- }
- return $results;
- }
-
- /**
- * Builds and runs the specified test assemblies.
- *
- * @param array Array of paths to test project files.
- * @return array Array of test results.
- */
- public function runAllTests(array $test_projects) {
- if (empty($test_projects)) {
- return array();
- }
-
- $results = array();
- $results[] = $this->generateProjects();
- if ($this->resultsContainFailures($results)) {
- return array_mergev($results);
- }
- $results[] = $this->buildProjects($test_projects);
- if ($this->resultsContainFailures($results)) {
- return array_mergev($results);
- }
- $results[] = $this->testAssemblies($test_projects);
-
- return array_mergev($results);
- }
-
- /**
- * Determine whether or not a current set of results contains any failures.
- * This is needed since we build the assemblies as part of the unit tests, but
- * we can't run any of the unit tests if the build fails.
- *
- * @param array Array of results to check.
- * @return bool If there are any failures in the results.
- */
- private function resultsContainFailures(array $results) {
- $results = array_mergev($results);
- foreach ($results as $result) {
- if ($result->getResult() != ArcanistUnitTestResult::RESULT_PASS) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * If the `Build` directory exists, we assume that this is a multi-platform
- * project that requires generation of C# project files. Because we want to
- * test that the generation and subsequent build is whole, we need to
- * regenerate any projects in case the developer has added files through an
- * IDE and then forgotten to add them to the respective `.definitions` file.
- * By regenerating the projects we ensure that any missing definition entries
- * will cause the build to fail.
- *
- * @return array Array of test results.
- */
- private function generateProjects() {
- // No "Build" directory; so skip generation of projects.
- if (!is_dir(Filesystem::resolvePath($this->projectRoot.'/Build'))) {
- return array();
- }
-
- // No "Protobuild.exe" file; so skip generation of projects.
- if (!is_file(Filesystem::resolvePath(
- $this->projectRoot.'/Protobuild.exe'))) {
-
- return array();
- }
-
- // Work out what platform the user is building for already.
- $platform = phutil_is_windows() ? 'Windows' : 'Linux';
- $files = Filesystem::listDirectory($this->projectRoot);
- foreach ($files as $file) {
- if (strtolower(substr($file, -4)) == '.sln') {
- $parts = explode('.', $file);
- $platform = $parts[count($parts) - 2];
- break;
- }
- }
-
- $regenerate_start = microtime(true);
- $regenerate_future = new ExecFuture(
- '%C Protobuild.exe --resync %s',
- $this->runtimeEngine,
- $platform);
- $regenerate_future->setCWD(Filesystem::resolvePath(
- $this->projectRoot));
- $results = array();
- $result = new ArcanistUnitTestResult();
- $result->setName(pht('(regenerate projects for %s)', $platform));
-
- try {
- $regenerate_future->resolvex();
- $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
- } catch(CommandException $exc) {
- if ($exc->getError() > 1) {
- throw $exc;
- }
- $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
- $result->setUserdata($exc->getStdout());
- }
-
- $result->setDuration(microtime(true) - $regenerate_start);
- $results[] = $result;
- return $results;
- }
-
- /**
- * Build the projects relevant for the specified test assemblies and return
- * the results of the builds as test results. This build also passes the
- * "SkipTestsOnBuild" parameter when building the projects, so that MSBuild
- * conditionals can be used to prevent any tests running as part of the
- * build itself (since the unit tester is about to run each of the tests
- * individually).
- *
- * @param array Array of test assemblies.
- * @return array Array of test results.
- */
- private function buildProjects(array $test_assemblies) {
- $build_futures = array();
- $build_failed = false;
- $build_start = microtime(true);
- $results = array();
- foreach ($test_assemblies as $test_assembly) {
- $build_future = new ExecFuture(
- '%C %s',
- $this->buildEngine,
- '/p:SkipTestsOnBuild=True');
- $build_future->setCWD(Filesystem::resolvePath(
- dirname($test_assembly['project'])));
- $build_futures[$test_assembly['project']] = $build_future;
- }
- $iterator = id(new FutureIterator($build_futures))->limit(1);
- foreach ($iterator as $test_assembly => $future) {
- $result = new ArcanistUnitTestResult();
- $result->setName('(build) '.$test_assembly);
-
- try {
- $future->resolvex();
- $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
- } catch(CommandException $exc) {
- if ($exc->getError() > 1) {
- throw $exc;
- }
- $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
- $result->setUserdata($exc->getStdout());
- $build_failed = true;
- }
-
- $result->setDuration(microtime(true) - $build_start);
- $results[] = $result;
- }
- return $results;
- }
-
- /**
- * Build the future for running a unit test. This can be overridden to enable
- * support for code coverage via another tool.
- *
- * @param string Name of the test assembly.
- * @return array The future, output filename and coverage filename
- * stored in an array.
- */
- protected function buildTestFuture($test_assembly) {
- // FIXME: Can't use TempFile here as xUnit doesn't like
- // UNIX-style full paths. It sees the leading / as the
- // start of an option flag, even when quoted.
- $xunit_temp = Filesystem::readRandomCharacters(10).'.results.xml';
- if (file_exists($xunit_temp)) {
- unlink($xunit_temp);
- }
- $future = new ExecFuture(
- '%C %s /xml %s',
- trim($this->runtimeEngine.' '.$this->testEngine),
- $test_assembly,
- $xunit_temp);
- $folder = Filesystem::resolvePath($this->projectRoot);
- $future->setCWD($folder);
- $combined = $folder.'/'.$xunit_temp;
- if (phutil_is_windows()) {
- $combined = $folder.'\\'.$xunit_temp;
- }
- return array($future, $combined, null);
- }
-
- /**
- * Run the xUnit test runner on each of the assemblies and parse the
- * resulting XML.
- *
- * @param array Array of test assemblies.
- * @return array Array of test results.
- */
- private function testAssemblies(array $test_assemblies) {
- $results = array();
-
- // Build the futures for running the tests.
- $futures = array();
- $outputs = array();
- $coverages = array();
- foreach ($test_assemblies as $test_assembly) {
- list($future_r, $xunit_temp, $coverage) =
- $this->buildTestFuture($test_assembly['assembly']);
- $futures[$test_assembly['assembly']] = $future_r;
- $outputs[$test_assembly['assembly']] = $xunit_temp;
- $coverages[$test_assembly['assembly']] = $coverage;
- }
-
- // Run all of the tests.
- $futures = id(new FutureIterator($futures))
- ->limit(8);
- foreach ($futures as $test_assembly => $future) {
- list($err, $stdout, $stderr) = $future->resolve();
-
- if (file_exists($outputs[$test_assembly])) {
- $result = $this->parseTestResult(
- $outputs[$test_assembly],
- $coverages[$test_assembly]);
- $results[] = $result;
- unlink($outputs[$test_assembly]);
- } else {
- // FIXME: There's a bug in Mono which causes a segmentation fault
- // when xUnit.NET runs; this causes the XML file to not appear
- // (depending on when the segmentation fault occurs). See
- // https://bugzilla.xamarin.com/show_bug.cgi?id=16379
- // for more information.
-
- // Since it's not possible for the user to correct this error, we
- // ignore the fact the tests didn't run here.
- }
- }
-
- return array_mergev($results);
- }
-
- /**
- * Returns null for this implementation as xUnit does not support code
- * coverage directly. Override this method in another class to provide code
- * coverage information (also see @{class:CSharpToolsUnitEngine}).
- *
- * @param string The name of the coverage file if one was provided by
- * `buildTestFuture`.
- * @return array Code coverage results, or null.
- */
- protected function parseCoverageResult($coverage) {
- return null;
- }
-
- /**
- * Parses the test results from xUnit.
- *
- * @param string The name of the xUnit results file.
- * @param string The name of the coverage file if one was provided by
- * `buildTestFuture`. This is passed through to
- * `parseCoverageResult`.
- * @return array Test results.
- */
- private function parseTestResult($xunit_tmp, $coverage) {
- $xunit_dom = new DOMDocument();
- $xunit_dom->loadXML(Filesystem::readFile($xunit_tmp));
-
- $results = array();
- $tests = $xunit_dom->getElementsByTagName('test');
- foreach ($tests as $test) {
- $name = $test->getAttribute('name');
- $time = $test->getAttribute('time');
- $status = ArcanistUnitTestResult::RESULT_UNSOUND;
- switch ($test->getAttribute('result')) {
- case 'Pass':
- $status = ArcanistUnitTestResult::RESULT_PASS;
- break;
- case 'Fail':
- $status = ArcanistUnitTestResult::RESULT_FAIL;
- break;
- case 'Skip':
- $status = ArcanistUnitTestResult::RESULT_SKIP;
- break;
- }
- $userdata = '';
- $reason = $test->getElementsByTagName('reason');
- $failure = $test->getElementsByTagName('failure');
- if ($reason->length > 0 || $failure->length > 0) {
- $node = ($reason->length > 0) ? $reason : $failure;
- $message = $node->item(0)->getElementsByTagName('message');
- if ($message->length > 0) {
- $userdata = $message->item(0)->nodeValue;
- }
- $stacktrace = $node->item(0)->getElementsByTagName('stack-trace');
- if ($stacktrace->length > 0) {
- $userdata .= "\n".$stacktrace->item(0)->nodeValue;
- }
- }
-
- $result = new ArcanistUnitTestResult();
- $result->setName($name);
- $result->setResult($status);
- $result->setDuration($time);
- $result->setUserData($userdata);
- if ($coverage != null) {
- $result->setCoverage($this->parseCoverageResult($coverage));
- }
- $results[] = $result;
- }
-
- return $results;
+ phutil_deprecated(
+ __CLASS__,
+ pht(
+ 'You should use `%s` instead.',
+ 'ArcanistXUnitTestEngine'));
+ parent::run();
}
}
diff --git a/src/unit/engine/__tests__/PhpunitTestEngineTestCase.php b/src/unit/engine/__tests__/ArcanistPhpunitTestEngineTestCase.php
rename from src/unit/engine/__tests__/PhpunitTestEngineTestCase.php
rename to src/unit/engine/__tests__/ArcanistPhpunitTestEngineTestCase.php
--- a/src/unit/engine/__tests__/PhpunitTestEngineTestCase.php
+++ b/src/unit/engine/__tests__/ArcanistPhpunitTestEngineTestCase.php
@@ -1,9 +1,9 @@
<?php
/**
- * Tests for @{class:PhpunitTestEngine}.
+ * Tests for @{class:ArcanistPhpunitTestEngine}.
*/
-final class PhpunitTestEngineTestCase extends ArcanistTestCase {
+final class ArcanistPhpunitTestEngineTestCase extends ArcanistTestCase {
public function testSearchLocations() {
$path = '/path/to/some/file/X.php';
@@ -36,7 +36,7 @@
'/tests/path/to/some/file/',
'/Tests/path/to/some/file/',
),
- PhpunitTestEngine::getSearchLocationsForTests($path));
+ ArcanistPhpunitTestEngine::getSearchLocationsForTests($path));
}
}
diff --git a/src/unit/engine/PytestTestEngine.php b/src/unit/engine/__tests__/ArcanistPytestTestEngine.php
copy from src/unit/engine/PytestTestEngine.php
copy to src/unit/engine/__tests__/ArcanistPytestTestEngine.php
--- a/src/unit/engine/PytestTestEngine.php
+++ b/src/unit/engine/__tests__/ArcanistPytestTestEngine.php
@@ -2,8 +2,10 @@
/**
* Very basic 'py.test' unit test engine wrapper.
+ *
+ * @todo Should be final but isn't because of @{class:PytestTestEngine}.
*/
-final class PytestTestEngine extends ArcanistUnitTestEngine {
+class ArcanistPytestTestEngine extends ArcanistUnitTestEngine {
public function run() {
$working_copy = $this->getWorkingCopy();
diff --git a/src/unit/parser/ArcanistPhpunitTestResultParser.php b/src/unit/parser/ArcanistPhpunitTestResultParser.php
--- a/src/unit/parser/ArcanistPhpunitTestResultParser.php
+++ b/src/unit/parser/ArcanistPhpunitTestResultParser.php
@@ -4,7 +4,7 @@
* PHPUnit Result Parsing utility
*
* For an example on how to integrate with your test engine, see
- * @{class:PhpunitTestEngine}.
+ * @{class:ArcanistPhpunitTestEngine}.
*/
final class ArcanistPhpunitTestResultParser extends ArcanistTestResultParser {
diff --git a/src/unit/parser/__tests__/XUnitTestResultParserTestCase.php b/src/unit/parser/__tests__/ArcanistXUnitTestResultParserTestCase.php
rename from src/unit/parser/__tests__/XUnitTestResultParserTestCase.php
rename to src/unit/parser/__tests__/ArcanistXUnitTestResultParserTestCase.php
--- a/src/unit/parser/__tests__/XUnitTestResultParserTestCase.php
+++ b/src/unit/parser/__tests__/ArcanistXUnitTestResultParserTestCase.php
@@ -3,7 +3,7 @@
/**
* Test for @{class:ArcanistXUnitTestResultParser}.
*/
-final class XUnitTestResultParserTestCase extends ArcanistTestCase {
+final class ArcanistXUnitTestResultParserTestCase extends ArcanistTestCase {
public function testAcceptsNoTestsInput() {
$stubbed_results = Filesystem::readFile(
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 5, 1:38 AM (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7223293
Default Alt Text
D11672.id31085.diff (55 KB)
Attached To
Mode
D11672: Rename ArcanistUnitTestEngine subclasses
Attached
Detach File
Event Timeline
Log In to Comment