Page MenuHomePhorge

No OneTemporary

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index af7e0195..acbcf472 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,248 +1,250 @@
<?php
/**
* This file is automatically generated. Use 'arc liberate' to rebuild it.
* @generated
* @phutil-library-version 2
*/
phutil_register_library_map(array(
'__library_version__' => 2,
'class' =>
array(
'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
'ArcanistApacheLicenseLinter' => 'lint/linter/ArcanistApacheLicenseLinter.php',
'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/__tests__/ArcanistApacheLicenseLinterTestCase.php',
'ArcanistBaseCommitParser' => 'parser/ArcanistBaseCommitParser.php',
'ArcanistBaseCommitParserTestCase' => 'parser/__tests__/ArcanistBaseCommitParserTestCase.php',
'ArcanistBaseUnitTestEngine' => 'unit/engine/ArcanistBaseUnitTestEngine.php',
'ArcanistBaseWorkflow' => 'workflow/ArcanistBaseWorkflow.php',
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
'ArcanistBundle' => 'parser/ArcanistBundle.php',
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
'ArcanistCallConduitWorkflow' => 'workflow/ArcanistCallConduitWorkflow.php',
'ArcanistCapabilityNotSupportedException' => 'workflow/exception/ArcanistCapabilityNotSupportedException.php',
'ArcanistChooseInvalidRevisionException' => 'exception/ArcanistChooseInvalidRevisionException.php',
'ArcanistChooseNoRevisionsException' => 'exception/ArcanistChooseNoRevisionsException.php',
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
'ArcanistCloseWorkflow' => 'workflow/ArcanistCloseWorkflow.php',
'ArcanistCommentRemover' => 'parser/ArcanistCommentRemover.php',
'ArcanistCommentRemoverTestCase' => 'parser/__tests__/ArcanistCommentRemoverTestCase.php',
'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php',
'ArcanistConduitLinter' => 'lint/linter/ArcanistConduitLinter.php',
'ArcanistConfiguration' => 'configuration/ArcanistConfiguration.php',
'ArcanistCoverWorkflow' => 'workflow/ArcanistCoverWorkflow.php',
'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php',
'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php',
'ArcanistDiffHunk' => 'parser/diff/ArcanistDiffHunk.php',
'ArcanistDiffParser' => 'parser/ArcanistDiffParser.php',
'ArcanistDiffParserTestCase' => 'parser/__tests__/ArcanistDiffParserTestCase.php',
'ArcanistDiffUtils' => 'difference/ArcanistDiffUtils.php',
'ArcanistDiffUtilsTestCase' => 'difference/__tests__/ArcanistDiffUtilsTestCase.php',
'ArcanistDiffWorkflow' => 'workflow/ArcanistDiffWorkflow.php',
'ArcanistDifferentialCommitMessage' => 'differential/ArcanistDifferentialCommitMessage.php',
'ArcanistDifferentialCommitMessageParserException' => 'differential/ArcanistDifferentialCommitMessageParserException.php',
'ArcanistDifferentialRevisionHash' => 'differential/constants/ArcanistDifferentialRevisionHash.php',
'ArcanistDifferentialRevisionStatus' => 'differential/constants/ArcanistDifferentialRevisionStatus.php',
'ArcanistDownloadWorkflow' => 'workflow/ArcanistDownloadWorkflow.php',
'ArcanistEventType' => 'events/constant/ArcanistEventType.php',
'ArcanistExportWorkflow' => 'workflow/ArcanistExportWorkflow.php',
'ArcanistFilenameLinter' => 'lint/linter/ArcanistFilenameLinter.php',
'ArcanistFlagWorkflow' => 'workflow/ArcanistFlagWorkflow.php',
'ArcanistGeneratedLinter' => 'lint/linter/ArcanistGeneratedLinter.php',
'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php',
'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php',
'ArcanistGitHookPreReceiveWorkflow' => 'workflow/ArcanistGitHookPreReceiveWorkflow.php',
'ArcanistHelpWorkflow' => 'workflow/ArcanistHelpWorkflow.php',
'ArcanistHgClientChannel' => 'hgdaemon/ArcanistHgClientChannel.php',
'ArcanistHgProxyClient' => 'hgdaemon/ArcanistHgProxyClient.php',
'ArcanistHgProxyServer' => 'hgdaemon/ArcanistHgProxyServer.php',
'ArcanistHgServerChannel' => 'hgdaemon/ArcanistHgServerChannel.php',
'ArcanistHookAPI' => 'repository/hookapi/ArcanistHookAPI.php',
'ArcanistInlinesWorkflow' => 'workflow/ArcanistInlinesWorkflow.php',
'ArcanistInstallCertificateWorkflow' => 'workflow/ArcanistInstallCertificateWorkflow.php',
'ArcanistJSHintLinter' => 'lint/linter/ArcanistJSHintLinter.php',
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
'ArcanistLicenseLinter' => 'lint/linter/ArcanistLicenseLinter.php',
'ArcanistLintConsoleRenderer' => 'lint/renderer/ArcanistLintConsoleRenderer.php',
'ArcanistLintEngine' => 'lint/engine/ArcanistLintEngine.php',
'ArcanistLintJSONRenderer' => 'lint/renderer/ArcanistLintJSONRenderer.php',
'ArcanistLintLikeCompilerRenderer' => 'lint/renderer/ArcanistLintLikeCompilerRenderer.php',
'ArcanistLintMessage' => 'lint/ArcanistLintMessage.php',
'ArcanistLintPatcher' => 'lint/ArcanistLintPatcher.php',
'ArcanistLintRenderer' => 'lint/renderer/ArcanistLintRenderer.php',
'ArcanistLintResult' => 'lint/ArcanistLintResult.php',
'ArcanistLintSeverity' => 'lint/ArcanistLintSeverity.php',
'ArcanistLintSummaryRenderer' => 'lint/renderer/ArcanistLintSummaryRenderer.php',
'ArcanistLintWorkflow' => 'workflow/ArcanistLintWorkflow.php',
'ArcanistLinter' => 'lint/linter/ArcanistLinter.php',
'ArcanistLinterTestCase' => 'lint/linter/__tests__/ArcanistLinterTestCase.php',
'ArcanistListWorkflow' => 'workflow/ArcanistListWorkflow.php',
'ArcanistMarkCommittedWorkflow' => 'workflow/ArcanistMarkCommittedWorkflow.php',
'ArcanistMercurialAPI' => 'repository/api/ArcanistMercurialAPI.php',
'ArcanistMercurialParser' => 'repository/parser/ArcanistMercurialParser.php',
'ArcanistMercurialParserTestCase' => 'repository/parser/__tests__/ArcanistMercurialParserTestCase.php',
'ArcanistNoEffectException' => 'exception/usage/ArcanistNoEffectException.php',
'ArcanistNoEngineException' => 'exception/usage/ArcanistNoEngineException.php',
'ArcanistNoLintLinter' => 'lint/linter/ArcanistNoLintLinter.php',
'ArcanistNoLintTestCaseMisnamed' => 'lint/linter/__tests__/ArcanistNoLintTestCase.php',
'ArcanistPEP8Linter' => 'lint/linter/ArcanistPEP8Linter.php',
'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php',
'ArcanistPatchWorkflow' => 'workflow/ArcanistPatchWorkflow.php',
'ArcanistPhpcsLinter' => 'lint/linter/ArcanistPhpcsLinter.php',
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
'ArcanistPhutilTestCase' => 'unit/engine/phutil/ArcanistPhutilTestCase.php',
'ArcanistPhutilTestCaseTestCase' => 'unit/engine/phutil/testcase/ArcanistPhutilTestCaseTestCase.php',
'ArcanistPhutilTestSkippedException' => 'unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php',
'ArcanistPhutilTestTerminatedException' => 'unit/engine/phutil/testcase/ArcanistPhutilTestTerminatedException.php',
'ArcanistPyFlakesLinter' => 'lint/linter/ArcanistPyFlakesLinter.php',
'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php',
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php',
'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php',
'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php',
'ArcanistSetConfigWorkflow' => 'workflow/ArcanistSetConfigWorkflow.php',
'ArcanistSettings' => 'configuration/ArcanistSettings.php',
'ArcanistShellCompleteWorkflow' => 'workflow/ArcanistShellCompleteWorkflow.php',
'ArcanistSingleLintEngine' => 'lint/engine/ArcanistSingleLintEngine.php',
'ArcanistSpellingDefaultData' => 'lint/linter/spelling/ArcanistSpellingDefaultData.php',
'ArcanistSpellingLinter' => 'lint/linter/ArcanistSpellingLinter.php',
'ArcanistSpellingLinterTestCase' => 'lint/linter/__tests__/ArcanistSpellingLinterTestCase.php',
'ArcanistSubversionAPI' => 'repository/api/ArcanistSubversionAPI.php',
'ArcanistSubversionHookAPI' => 'repository/hookapi/ArcanistSubversionHookAPI.php',
'ArcanistSvnHookPreCommitWorkflow' => 'workflow/ArcanistSvnHookPreCommitWorkflow.php',
'ArcanistTasksWorkflow' => 'workflow/ArcanistTasksWorkflow.php',
'ArcanistTestCase' => 'infrastructure/testing/ArcanistTestCase.php',
'ArcanistTextLinter' => 'lint/linter/ArcanistTextLinter.php',
'ArcanistTextLinterTestCase' => 'lint/linter/__tests__/ArcanistTextLinterTestCase.php',
'ArcanistTodoWorkflow' => 'workflow/ArcanistTodoWorkflow.php',
'ArcanistUncommittedChangesException' => 'exception/usage/ArcanistUncommittedChangesException.php',
'ArcanistUnitTestResult' => 'unit/ArcanistUnitTestResult.php',
'ArcanistUnitWorkflow' => 'workflow/ArcanistUnitWorkflow.php',
'ArcanistUpgradeWorkflow' => 'workflow/ArcanistUpgradeWorkflow.php',
'ArcanistUploadWorkflow' => 'workflow/ArcanistUploadWorkflow.php',
'ArcanistUsageException' => 'exception/ArcanistUsageException.php',
'ArcanistUserAbortException' => 'exception/usage/ArcanistUserAbortException.php',
'ArcanistWhichWorkflow' => 'workflow/ArcanistWhichWorkflow.php',
'ArcanistWorkingCopyIdentity' => 'workingcopyidentity/ArcanistWorkingCopyIdentity.php',
'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/ArcanistXHPASTLintNamingHook.php',
'ArcanistXHPASTLintNamingHookTestCase' => 'lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php',
'ArcanistXHPASTLinter' => 'lint/linter/ArcanistXHPASTLinter.php',
'ArcanistXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistXHPASTLinterTestCase.php',
'ComprehensiveLintEngine' => 'lint/engine/ComprehensiveLintEngine.php',
'ExampleLintEngine' => 'lint/engine/ExampleLintEngine.php',
'NoseTestEngine' => 'unit/engine/NoseTestEngine.php',
+ 'PHPUnitTestEngineTestCase' => 'unit/engine/__tests__/PHPUnitTestEngineTestCase.php',
'PhpunitTestEngine' => 'unit/engine/PhpunitTestEngine.php',
'PhutilLintEngine' => 'lint/engine/PhutilLintEngine.php',
'PhutilUnitTestEngine' => 'unit/engine/PhutilUnitTestEngine.php',
'PhutilUnitTestEngineTestCase' => 'unit/engine/__tests__/PhutilUnitTestEngineTestCase.php',
'UnitTestableArcanistLintEngine' => 'lint/engine/UnitTestableArcanistLintEngine.php',
),
'function' =>
array(
),
'xmap' =>
array(
'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistAnoidWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter',
'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistBaseCommitParserTestCase' => 'ArcanistTestCase',
'ArcanistBranchWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistBundleTestCase' => 'ArcanistTestCase',
'ArcanistCallConduitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCapabilityNotSupportedException' => 'Exception',
'ArcanistChooseInvalidRevisionException' => 'Exception',
'ArcanistChooseNoRevisionsException' => 'Exception',
'ArcanistCloseRevisionWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCloseWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCommentRemoverTestCase' => 'ArcanistTestCase',
'ArcanistCommitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistConduitLinter' => 'ArcanistLinter',
'ArcanistCoverWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistDiffParserTestCase' => 'ArcanistTestCase',
'ArcanistDiffUtilsTestCase' => 'ArcanistTestCase',
'ArcanistDiffWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistDifferentialCommitMessageParserException' => 'Exception',
'ArcanistDownloadWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistEventType' => 'PhutilEventType',
'ArcanistExportWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistFilenameLinter' => 'ArcanistLinter',
'ArcanistFlagWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistGeneratedLinter' => 'ArcanistLinter',
'ArcanistGetConfigWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistGitAPI' => 'ArcanistRepositoryAPI',
'ArcanistGitHookPreReceiveWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistHelpWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistHgClientChannel' => 'PhutilProtocolChannel',
'ArcanistHgServerChannel' => 'PhutilProtocolChannel',
'ArcanistInlinesWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistInstallCertificateWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistJSHintLinter' => 'ArcanistLinter',
'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLicenseLinter' => 'ArcanistLinter',
'ArcanistLintConsoleRenderer' => 'ArcanistLintRenderer',
'ArcanistLintJSONRenderer' => 'ArcanistLintRenderer',
'ArcanistLintLikeCompilerRenderer' => 'ArcanistLintRenderer',
'ArcanistLintSummaryRenderer' => 'ArcanistLintRenderer',
'ArcanistLintWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLinterTestCase' => 'ArcanistTestCase',
'ArcanistListWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistMarkCommittedWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistMercurialAPI' => 'ArcanistRepositoryAPI',
'ArcanistMercurialParserTestCase' => 'ArcanistTestCase',
'ArcanistNoEffectException' => 'ArcanistUsageException',
'ArcanistNoEngineException' => 'ArcanistUsageException',
'ArcanistNoLintLinter' => 'ArcanistLinter',
'ArcanistNoLintTestCaseMisnamed' => 'ArcanistLinterTestCase',
'ArcanistPEP8Linter' => 'ArcanistLinter',
'ArcanistPasteWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistPatchWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistPhpcsLinter' => 'ArcanistLinter',
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
'ArcanistPhutilTestCaseTestCase' => 'ArcanistPhutilTestCase',
'ArcanistPhutilTestSkippedException' => 'Exception',
'ArcanistPhutilTestTerminatedException' => 'Exception',
'ArcanistPyFlakesLinter' => 'ArcanistLinter',
'ArcanistPyLintLinter' => 'ArcanistLinter',
'ArcanistRubyLinter' => 'ArcanistLinter',
'ArcanistRubyLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistScriptAndRegexLinter' => 'ArcanistLinter',
'ArcanistSetConfigWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistShellCompleteWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistSingleLintEngine' => 'ArcanistLintEngine',
'ArcanistSpellingLinter' => 'ArcanistLinter',
'ArcanistSpellingLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI',
'ArcanistSubversionHookAPI' => 'ArcanistHookAPI',
'ArcanistSvnHookPreCommitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistTasksWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistTestCase' => 'ArcanistPhutilTestCase',
'ArcanistTextLinter' => 'ArcanistLinter',
'ArcanistTextLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistTodoWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUncommittedChangesException' => 'ArcanistUsageException',
'ArcanistUnitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUpgradeWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUploadWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUsageException' => 'Exception',
'ArcanistUserAbortException' => 'ArcanistUsageException',
'ArcanistWhichWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistXHPASTLintNamingHookTestCase' => 'ArcanistTestCase',
'ArcanistXHPASTLinter' => 'ArcanistLinter',
'ArcanistXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
'ComprehensiveLintEngine' => 'ArcanistLintEngine',
'ExampleLintEngine' => 'ArcanistLintEngine',
'NoseTestEngine' => 'ArcanistBaseUnitTestEngine',
+ 'PHPUnitTestEngineTestCase' => 'ArcanistTestCase',
'PhpunitTestEngine' => 'ArcanistBaseUnitTestEngine',
'PhutilLintEngine' => 'ArcanistLintEngine',
'PhutilUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
'PhutilUnitTestEngineTestCase' => 'ArcanistTestCase',
'UnitTestableArcanistLintEngine' => 'ArcanistLintEngine',
),
));
diff --git a/src/unit/engine/PhpunitTestEngine.php b/src/unit/engine/PhpunitTestEngine.php
index baa7a201..d76423f5 100644
--- a/src/unit/engine/PhpunitTestEngine.php
+++ b/src/unit/engine/PhpunitTestEngine.php
@@ -1,341 +1,426 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* PHPUnit wrapper
*
* To use, set unit_engine in .arcconfig, or use --engine flag
* with arc unit. Currently supports only class & test files
* (no directory support).
* To use custom phpunit configuration, set phpunit_config in
* .arcconfig (e.g. app/phpunit.xml.dist).
*
* @group unitrun
*/
final class PhpunitTestEngine extends ArcanistBaseUnitTestEngine {
private $configFile;
private $affectedTests;
private $projectRoot;
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('No tests to run.');
}
$this->prepareConfigFile();
$futures = array();
$tmpfiles = array();
foreach ($this->affectedTests as $class_path => $test_path) {
$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;
$futures[$test_path] = new ExecFuture('phpunit %C --log-json %s %C %s',
$config, $json_tmp, $clover, $test_path);
$tmpfiles[$test_path] = array(
'json' => $json_tmp,
'clover' => $clover_tmp,
);
}
$results = array();
foreach (Futures($futures)->limit(4) as $test => $future) {
list($err, $stdout, $stderr) = $future->resolve();
$results[] = $this->parseTestResults($test_path,
$tmpfiles[$test]['json'],
$tmpfiles[$test]['clover']);
}
return array_mergev($results);
}
/**
* We need this non-sense to make json generated by phpunit
* valid.
*
* @param string $json_tmp Path to JSON report
*
* @return array JSON decoded array
*/
private function getJsonReport($json_tmp) {
$json = Filesystem::readFile($json_tmp);
if (empty($json)) {
throw new Exception('JSON report file is empty, '
. 'it probably means that phpunit failed to run tests. '
. 'Try running arc unit with --trace option and then run '
. 'generated phpunit command yourself, you might get the '
. 'answer.'
);
}
$json = str_replace('}{"', '},{"', $json);
$json = '[' . $json . ']';
$json = json_decode($json);
if (!is_array($json)) {
throw new Exception('JSON could not be decoded');
}
return $json;
}
/**
* Parse test results from phpunit json report
*
* @param string $path Path to test
* @param string $json_path Path to phpunit json report
* @param string $clover_tmp Path to phpunit clover report
*
* @return array
*/
private function parseTestResults($path, $json_tmp, $clover_tmp) {
$test_results = Filesystem::readFile($json_tmp);
$report = $this->getJsonReport($json_tmp);
// coverage is for all testcases in the executed $path
$coverage = array();
if ($this->getEnableCoverage() !== false) {
$coverage = $this->readCoverage($clover_tmp);
}
$results = array();
foreach ($report as $event) {
if ('test' != $event->event) {
continue;
}
$status = ArcanistUnitTestResult::RESULT_PASS;
$user_data = '';
if ('fail' == $event->status) {
$status = ArcanistUnitTestResult::RESULT_FAIL;
$user_data .= $event->message . "\n";
foreach ($event->trace as $trace) {
$user_data .= sprintf("\n%s:%s", $trace->file, $trace->line);
}
} else if ('error' == $event->status) {
if ('Skipped Test' == $event->message) {
$status = ArcanistUnitTestResult::RESULT_SKIP;
$user_data .= $event->message;
} else if ('Incomplete Test' == $event->message) {
$status = ArcanistUnitTestResult::RESULT_SKIP;
$user_data .= $event->message;
} else {
$status = ArcanistUnitTestResult::RESULT_BROKEN;
$user_data .= $event->message;
foreach ($event->trace as $trace) {
$user_data .= sprintf("\n%s:%s", $trace->file, $trace->line);
}
}
}
$name = preg_replace('/ \(.*\)/', '', $event->test);
$result = new ArcanistUnitTestResult();
$result->setName($name);
$result->setResult($status);
$result->setDuration($event->time);
$result->setCoverage($coverage);
$result->setUserData($user_data);
$results[] = $result;
}
return $results;
}
/**
* Red the coverage from phpunit generated clover report
*
* @param string $path Path to report
*
* @return array
*/
private function readCoverage($path) {
$test_results = Filesystem::readFile($path);
if (empty($test_results)) {
throw new Exception('Clover coverage XML report file is empty, '
. 'it probably means that phpunit failed to run tests. '
. 'Try running arc unit with --trace option and then run '
. 'generated phpunit command yourself, you might get the '
. 'answer.'
);
}
$coverage_dom = new DOMDocument();
$coverage_dom->loadXML($test_results);
$reports = array();
$files = $coverage_dom->getElementsByTagName('file');
foreach ($files as $file) {
$class_path = $file->getAttribute('name');
if (empty($this->affectedTests[$class_path])) {
continue;
}
$test_path = $this->affectedTests[$file->getAttribute('name')];
// get total line count in file
$line_count = count(file($class_path));
$coverage = '';
$start_line = 1;
$lines = $file->getElementsByTagName('line');
for ($ii = 0; $ii < $lines->length; $ii++) {
$line = $lines->item($ii);
for (; $start_line < $line->getAttribute('num'); $start_line++) {
$coverage .= 'N';
}
if ($line->getAttribute('type') != 'stmt') {
$coverage .= 'N';
} else {
if ((int) $line->getAttribute('count') == 0) {
$coverage .= 'U';
}
else if ((int) $line->getAttribute('count') > 0) {
$coverage .= 'C';
}
}
$start_line++;
}
for (; $start_line <= $line_count; $start_line++) {
$coverage .= 'N';
}
$len = strlen($this->projectRoot . DIRECTORY_SEPARATOR);
$class_path = substr($class_path, $len);
$reports[$class_path] = $coverage;
}
return $reports;
}
/**
- * Some nasty guessing here.
+ * Search for test cases for a given file in a large number of "reasonable"
+ * locations. See @{method:getSearchLocationsForTests} for specifics.
*
- * Walk up to the project root trying to find
- * [Tt]ests directory and replicate the structure there.
+ * 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 (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:
*
- * Assume that the class path is
- * /www/project/module/package/subpackage/FooBar.php
- * and a project root is /www/project it will look for it by these paths:
- * /www/project/module/package/subpackage/[Tt]ests/FooBarTest.php
- * /www/project/module/package/[Tt]ests/subpackage/FooBarTest.php
- * /www/project/module/[Tt]ests/package/subpackage/FooBarTest.php
- * /www/project/Tt]ests/module/package/subpackage/FooBarTest.php
+ * /a/b/c/
*
- * TODO: Add support for finding tests based on PSR-1 naming conventions:
- * /www/project/src/Something/Foo/Bar.php tests should be detected in
- * /www/project/tests/Something/Foo/BarTest.php
+ * We then look in all parent directories for a directory named "tests/"
+ * (or "Tests/"):
*
- * TODO: Add support for finding tests in testsuite folders from
- * phpunit.xml configuration.
+ * /a/b/c/tests/
+ * /a/b/tests/
+ * /a/tests/
+ * /tests/
+ *
+ * We also try to replace each directory component with "tests/":
*
- * @param string $path
+ * /a/b/tests/
+ * /a/tests/c/
+ * /tests/b/c/
*
- * @return string|boolean
+ * 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.
*/
- private function findTestFile($path) {
- $expected_file = substr(basename($path), 0, -4) . 'Test.php';
- $expected_dir = null;
- $dirname = dirname($path);
- foreach (Filesystem::walkToRoot($dirname) as $dir) {
- $expected_dir = DIRECTORY_SEPARATOR
- . substr($dirname, strlen($dir) + 1)
- . $expected_dir;
- $look_for = $dir . DIRECTORY_SEPARATOR
- . '%s' . $expected_dir . $expected_file;
-
- if (Filesystem::pathExists(sprintf($look_for, 'Tests'))) {
- return sprintf($look_for, 'Tests');
- } else if (Filesystem::pathExists(sprintf($look_for, 'Tests'))) {
- return sprintf($look_for, 'Tests');
+ 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;
}
+ }
- if ($dir == $this->projectRoot) {
- break;
+ // 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 false;
+ 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;
if ($config = $this->getWorkingCopy()->getConfig('phpunit_config')) {
if (Filesystem::pathExists($project_root . $config)) {
$this->configFile = $project_root . $config;
} else {
throw new Exception('PHPUnit configuration file was not ' .
'found in ' . $project_root . $config);
}
}
}
}
diff --git a/src/unit/engine/__tests__/PHPUnitTestEngineTestCase.php b/src/unit/engine/__tests__/PHPUnitTestEngineTestCase.php
new file mode 100644
index 00000000..882122a8
--- /dev/null
+++ b/src/unit/engine/__tests__/PHPUnitTestEngineTestCase.php
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Tests for @{class:PHPUnitTestEngine}.
+ *
+ * @group testcase
+ */
+final class PHPUnitTestEngineTestCase extends ArcanistTestCase {
+
+ public function testSearchLocations() {
+
+ $path = '/path/to/some/file/X.php';
+
+ $this->assertEqual(
+ array(
+ '/path/to/some/file/',
+ '/path/to/some/file/tests/',
+ '/path/to/some/file/Tests/',
+ '/path/to/some/tests/',
+ '/path/to/some/Tests/',
+ '/path/to/tests/',
+ '/path/to/Tests/',
+ '/path/tests/',
+ '/path/Tests/',
+ '/tests/',
+ '/Tests/',
+ '/path/to/tests/file/',
+ '/path/to/Tests/file/',
+ '/path/tests/some/file/',
+ '/path/Tests/some/file/',
+ '/tests/to/some/file/',
+ '/Tests/to/some/file/',
+ '/path/to/some/tests/file/',
+ '/path/to/some/Tests/file/',
+ '/path/to/tests/some/file/',
+ '/path/to/Tests/some/file/',
+ '/path/tests/to/some/file/',
+ '/path/Tests/to/some/file/',
+ '/tests/path/to/some/file/',
+ '/Tests/path/to/some/file/',
+ ),
+ PhpunitTestEngine::getSearchLocationsForTests($path));
+ }
+
+}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 12:59 (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1124829
Default Alt Text
(32 KB)

Event Timeline