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 @@ 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 @@ 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//test_.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 @@ 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 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 @@ 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 @@ 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 @@ 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(