Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2895414
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
22 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index d5815b13..b7454cdc 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,207 +1,209 @@
<?php
/**
* This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
* @generated
*/
phutil_register_library_map(array(
'class' =>
array(
'ArcanistAliasWorkflow' => 'workflow/alias',
'ArcanistAmendWorkflow' => 'workflow/amend',
'ArcanistApacheLicenseLinter' => 'lint/linter/apachelicense',
'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/apachelicense/__tests__',
'ArcanistBaseUnitTestEngine' => 'unit/engine/base',
'ArcanistBaseWorkflow' => 'workflow/base',
'ArcanistBranchWorkflow' => 'workflow/branch',
'ArcanistBundle' => 'parser/bundle',
'ArcanistBundleTestCase' => 'parser/bundle/__tests__',
'ArcanistCallConduitWorkflow' => 'workflow/call-conduit',
'ArcanistCapabilityNotSupportedException' => 'workflow/exception/notsupported',
'ArcanistChooseInvalidRevisionException' => 'exception',
'ArcanistChooseNoRevisionsException' => 'exception',
'ArcanistCloseRevisionWorkflow' => 'workflow/close-revision',
'ArcanistCloseWorkflow' => 'workflow/close',
'ArcanistCommentRemover' => 'parser/commentremover',
'ArcanistCommentRemoverTestCase' => 'parser/commentremover/__tests__',
'ArcanistCommitWorkflow' => 'workflow/commit',
'ArcanistConduitLinter' => 'lint/linter/conduit',
'ArcanistConfiguration' => 'configuration',
'ArcanistCoverWorkflow' => 'workflow/cover',
'ArcanistDiffChange' => 'parser/diff/change',
'ArcanistDiffChangeType' => 'parser/diff/changetype',
'ArcanistDiffHunk' => 'parser/diff/hunk',
'ArcanistDiffParser' => 'parser/diff',
'ArcanistDiffParserTestCase' => 'parser/diff/__tests__',
'ArcanistDiffUtils' => 'difference',
'ArcanistDiffUtilsTestCase' => 'difference/__tests__',
'ArcanistDiffWorkflow' => 'workflow/diff',
'ArcanistDifferentialCommitMessage' => 'differential/commitmessage',
'ArcanistDifferentialCommitMessageParserException' => 'differential/commitmessage',
'ArcanistDifferentialRevisionHash' => 'differential/constants/revisionhash',
'ArcanistDifferentialRevisionStatus' => 'differential/constants/revisionstatus',
'ArcanistDownloadWorkflow' => 'workflow/download',
'ArcanistEventType' => 'events/constant/type',
'ArcanistExportWorkflow' => 'workflow/export',
'ArcanistFilenameLinter' => 'lint/linter/filename',
'ArcanistGeneratedLinter' => 'lint/linter/generated',
'ArcanistGetConfigWorkflow' => 'workflow/get-config',
'ArcanistGitAPI' => 'repository/api/git',
'ArcanistGitHookPreReceiveWorkflow' => 'workflow/git-hook-pre-receive',
'ArcanistHelpWorkflow' => 'workflow/help',
'ArcanistHookAPI' => 'repository/hookapi/base',
'ArcanistInstallCertificateWorkflow' => 'workflow/install-certificate',
'ArcanistJSHintLinter' => 'lint/linter/jshint',
'ArcanistLandWorkflow' => 'workflow/land',
'ArcanistLiberateLintEngine' => 'lint/engine/liberate',
'ArcanistLiberateWorkflow' => 'workflow/liberate',
'ArcanistLicenseLinter' => 'lint/linter/license',
'ArcanistLintEngine' => 'lint/engine/base',
'ArcanistLintJSONRenderer' => 'lint/renderer',
'ArcanistLintLikeCompilerRenderer' => 'lint/renderer',
'ArcanistLintMessage' => 'lint/message',
'ArcanistLintPatcher' => 'lint/patcher',
'ArcanistLintRenderer' => 'lint/renderer',
'ArcanistLintResult' => 'lint/result',
'ArcanistLintSeverity' => 'lint/severity',
'ArcanistLintSummaryRenderer' => 'lint/renderer',
'ArcanistLintWorkflow' => 'workflow/lint',
'ArcanistLinter' => 'lint/linter/base',
'ArcanistLinterTestCase' => 'lint/linter/base/test',
'ArcanistListWorkflow' => 'workflow/list',
'ArcanistMarkCommittedWorkflow' => 'workflow/mark-committed',
'ArcanistMercurialAPI' => 'repository/api/mercurial',
'ArcanistMercurialParser' => 'repository/parser/mercurial',
'ArcanistMercurialParserTestCase' => 'repository/parser/mercurial/__tests__',
'ArcanistNoEffectException' => 'exception/usage/noeffect',
'ArcanistNoEngineException' => 'exception/usage/noengine',
'ArcanistNoLintLinter' => 'lint/linter/nolint',
'ArcanistNoLintTestCaseMisnamed' => 'lint/linter/nolint/__tests__',
'ArcanistPEP8Linter' => 'lint/linter/pep8',
'ArcanistPasteWorkflow' => 'workflow/paste',
'ArcanistPatchWorkflow' => 'workflow/patch',
'ArcanistPhutilModuleLinter' => 'lint/linter/phutilmodule',
'ArcanistPhutilTestCase' => 'unit/engine/phutil/testcase',
'ArcanistPhutilTestSkippedException' => 'unit/engine/phutil/testcase/exception',
'ArcanistPhutilTestTerminatedException' => 'unit/engine/phutil/testcase/exception',
'ArcanistPyFlakesLinter' => 'lint/linter/pyflakes',
'ArcanistPyLintLinter' => 'lint/linter/pylint',
'ArcanistRepositoryAPI' => 'repository/api/base',
'ArcanistSetConfigWorkflow' => 'workflow/set-config',
'ArcanistShellCompleteWorkflow' => 'workflow/shell-complete',
'ArcanistSpellingDefaultData' => 'lint/linter/spelling',
'ArcanistSpellingLinter' => 'lint/linter/spelling',
'ArcanistSpellingLinterTestCase' => 'lint/linter/spelling/__tests__',
'ArcanistSubversionAPI' => 'repository/api/subversion',
'ArcanistSubversionHookAPI' => 'repository/hookapi/subversion',
'ArcanistSvnHookPreCommitWorkflow' => 'workflow/svn-hook-pre-commit',
'ArcanistTasksWorkflow' => 'workflow/tasks',
'ArcanistTextLinter' => 'lint/linter/text',
'ArcanistTextLinterTestCase' => 'lint/linter/text/__tests__',
'ArcanistUncommittedChangesException' => 'exception/usage/uncommittedchanges',
'ArcanistUnitTestResult' => 'unit/result',
'ArcanistUnitWorkflow' => 'workflow/unit',
'ArcanistUpgradeWorkflow' => 'workflow/upgrade',
'ArcanistUploadWorkflow' => 'workflow/upload',
'ArcanistUsageException' => 'exception/usage',
'ArcanistUserAbortException' => 'exception/usage/userabort',
'ArcanistWhichWorkflow' => 'workflow/which',
'ArcanistWorkingCopyIdentity' => 'workingcopyidentity',
'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/naminghook',
'ArcanistXHPASTLintNamingHookTestCase' => 'lint/linter/xhpast/naminghook/__tests__',
'ArcanistXHPASTLinter' => 'lint/linter/xhpast',
'ArcanistXHPASTLinterTestCase' => 'lint/linter/xhpast/__tests__',
'BranchInfo' => 'branch',
'ComprehensiveLintEngine' => 'lint/engine/comprehensive',
'ExampleLintEngine' => 'lint/engine/example',
'NoseTestEngine' => 'unit/engine/nose',
+ 'PhpunitTestEngine' => 'unit/engine/phpunit',
'PhutilLintEngine' => 'lint/engine/phutil',
'PhutilModuleRequirements' => 'parser/phutilmodule',
'PhutilUnitTestEngine' => 'unit/engine/phutil',
'PhutilUnitTestEngineTestCase' => 'unit/engine/phutil/__tests__',
'UnitTestableArcanistLintEngine' => 'lint/engine/test',
),
'function' =>
array(
),
'requires_class' =>
array(
'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter',
'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistBranchWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistBundleTestCase' => 'ArcanistPhutilTestCase',
'ArcanistCallConduitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCloseRevisionWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCloseWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCommentRemoverTestCase' => 'ArcanistPhutilTestCase',
'ArcanistCommitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistConduitLinter' => 'ArcanistLinter',
'ArcanistCoverWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistDiffParserTestCase' => 'ArcanistPhutilTestCase',
'ArcanistDiffUtilsTestCase' => 'ArcanistPhutilTestCase',
'ArcanistDiffWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistDownloadWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistEventType' => 'PhutilEventType',
'ArcanistExportWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistFilenameLinter' => 'ArcanistLinter',
'ArcanistGeneratedLinter' => 'ArcanistLinter',
'ArcanistGetConfigWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistGitAPI' => 'ArcanistRepositoryAPI',
'ArcanistGitHookPreReceiveWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistHelpWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistInstallCertificateWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistJSHintLinter' => 'ArcanistLinter',
'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLiberateLintEngine' => 'ArcanistLintEngine',
'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLicenseLinter' => 'ArcanistLinter',
'ArcanistLintWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLinterTestCase' => 'ArcanistPhutilTestCase',
'ArcanistListWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistMarkCommittedWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistMercurialAPI' => 'ArcanistRepositoryAPI',
'ArcanistMercurialParserTestCase' => 'ArcanistPhutilTestCase',
'ArcanistNoEffectException' => 'ArcanistUsageException',
'ArcanistNoEngineException' => 'ArcanistUsageException',
'ArcanistNoLintLinter' => 'ArcanistLinter',
'ArcanistNoLintTestCaseMisnamed' => 'ArcanistLinterTestCase',
'ArcanistPEP8Linter' => 'ArcanistLinter',
'ArcanistPasteWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistPatchWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistPhutilModuleLinter' => 'ArcanistLinter',
'ArcanistPyFlakesLinter' => 'ArcanistLinter',
'ArcanistPyLintLinter' => 'ArcanistLinter',
'ArcanistSetConfigWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistShellCompleteWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistSpellingLinter' => 'ArcanistLinter',
'ArcanistSpellingLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI',
'ArcanistSubversionHookAPI' => 'ArcanistHookAPI',
'ArcanistSvnHookPreCommitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistTasksWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistTextLinter' => 'ArcanistLinter',
'ArcanistTextLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistUncommittedChangesException' => 'ArcanistUsageException',
'ArcanistUnitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUpgradeWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUploadWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistUserAbortException' => 'ArcanistUsageException',
'ArcanistWhichWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistXHPASTLintNamingHookTestCase' => 'ArcanistPhutilTestCase',
'ArcanistXHPASTLinter' => 'ArcanistLinter',
'ArcanistXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
'ComprehensiveLintEngine' => 'ArcanistLintEngine',
'ExampleLintEngine' => 'ArcanistLintEngine',
'NoseTestEngine' => 'ArcanistBaseUnitTestEngine',
+ 'PhpunitTestEngine' => 'ArcanistBaseUnitTestEngine',
'PhutilLintEngine' => 'ArcanistLintEngine',
'PhutilUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
'PhutilUnitTestEngineTestCase' => 'ArcanistPhutilTestCase',
'UnitTestableArcanistLintEngine' => 'ArcanistLintEngine',
),
'requires_interface' =>
array(
),
));
diff --git a/src/unit/engine/phpunit/PhpunitTestEngine.php b/src/unit/engine/phpunit/PhpunitTestEngine.php
new file mode 100644
index 00000000..a9f1ba4b
--- /dev/null
+++ b/src/unit/engine/phpunit/PhpunitTestEngine.php
@@ -0,0 +1,336 @@
+<?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);
+
+ // 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 = substr($event->test, strlen($event->suite) + 2);
+ $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');
+ }
+
+ $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.
+ *
+ * Walk up to the project root trying to find
+ * [Tt]ests directory and replicate the structure there.
+ *
+ * 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
+ *
+ * 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
+ *
+ * TODO: Add support for finding tests in testsuite folders from
+ * phpunit.xml configuration.
+ *
+ * @param string $path
+ *
+ * @return string|boolean
+ */
+ 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');
+ }
+
+ if ($dir == $this->projectRoot) {
+ break;
+ }
+
+ }
+
+ return false;
+ }
+
+ /**
+ * 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/phpunit/__init__.php b/src/unit/engine/phpunit/__init__.php
new file mode 100644
index 00000000..a39a3d7a
--- /dev/null
+++ b/src/unit/engine/phpunit/__init__.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('arcanist', 'exception/usage/noeffect');
+phutil_require_module('arcanist', 'unit/engine/base');
+phutil_require_module('arcanist', 'unit/result');
+
+phutil_require_module('phutil', 'filesystem');
+phutil_require_module('phutil', 'filesystem/tempfile');
+phutil_require_module('phutil', 'future');
+phutil_require_module('phutil', 'future/exec');
+phutil_require_module('phutil', 'utils');
+phutil_require_module('phutil', 'xsprintf/csprintf');
+
+
+phutil_require_source('PhpunitTestEngine.php');
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jan 19 2025, 21:26 (6 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128919
Default Alt Text
(22 KB)
Attached To
Mode
rARC Arcanist
Attached
Detach File
Event Timeline
Log In to Comment