Page MenuHomePhabricator

D11672.id31082.diff
No OneTemporary

D11672.id31082.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
@@ -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;
@@ -37,20 +39,15 @@
// 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'));
+ "Unable to locate cscover. Configure it with ".
+ "the `unit.csharp.coverage.binary' option in .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'));
+ 'Unable to locate cscover coverage runner (have you built yet?)');
}
}
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();
@@ -75,19 +77,17 @@
$cover_tmp = $tmpfiles[$test_path]['cover'];
$this->parser = new ArcanistXUnitTestResultParser();
- $results[] = $this->parseTestResults(
- $source_path,
- $xunit_tmp,
- $cover_tmp);
+ $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);
+ $cmd_line = csprintf('nosetests --with-xunit --xunit-file=%s',
+ $xunit_tmp);
if ($this->getEnableCoverage() !== false) {
$cmd_line .= csprintf(
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';
@@ -42,7 +44,7 @@
}
if (empty($this->affectedTests)) {
- throw new ArcanistNoEffectException(pht('No tests to run.'));
+ throw new ArcanistNoEffectException('No tests to run.');
}
$this->prepareConfigFile();
@@ -260,10 +262,8 @@
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));
+ throw new Exception('PHPUnit configuration file was not '.
+ 'found in '.$project_root.$config);
}
}
$bin = $this->getConfigurationManager()->getConfigFromAnySource(
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;
@@ -42,12 +42,7 @@
} else if (Filesystem::binaryExists('xbuild')) {
$this->buildEngine = 'xbuild';
} else {
- throw new Exception(
- pht(
- 'Unable to find %s or %s in %s!',
- 'msbuild',
- 'xbuild',
- 'PATH'));
+ throw new Exception('Unable to find msbuild or xbuild in PATH!');
}
// Determine runtime engine (.NET or Mono).
@@ -56,8 +51,7 @@
} 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!'));
+ throw new Exception('Unable to find Mono and you are not on Windows!');
}
// Read the discovery rules.
@@ -66,11 +60,8 @@
'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'));
+ 'You must configure discovery rules to map C# files '.
+ 'back to test projects (`unit.csharp.discovery` in .arcconfig).');
}
// Determine xUnit test runner path.
@@ -86,11 +77,8 @@
$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'));
+ "Unable to locate xUnit console runner. Configure ".
+ "it with the `unit.csharp.xunit.binary' option in .arcconfig");
}
}
@@ -246,7 +234,7 @@
$this->projectRoot));
$results = array();
$result = new ArcanistUnitTestResult();
- $result->setName(pht('(regenerate projects for %s)', $platform));
+ $result->setName("(regenerate projects for $platform)");
try {
$regenerate_future->resolvex();
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();
@@ -17,7 +19,7 @@
if (!Filesystem::pathExists($junit_tmp)) {
throw new CommandException(
- pht('Command failed with error #%s!', $err),
+ "Command failed with error #{$err}!",
$future->getCommand(),
$err,
$stdout,
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

Mime Type
text/plain
Expires
Thu, May 16, 4:16 AM (2 w, 2 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/3l/zu/wbc3eeag3sjgq7cp
Default Alt Text
D11672.id31082.diff (60 KB)

Event Timeline