Page MenuHomePhorge

No OneTemporary

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

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)

Event Timeline