1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 16:22:42 +01:00

Print a more useful error message if any PHPUnit tests crash

Summary:
Before, if PHPUnit crashed (syntax error, undefined constant, etc), you would get a relatively unhelpful error message:

    firehed@Eric-Sterns-Mac-Pro ~/dev/php-lcd: arc unit
    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.
    (Run with --trace for a full exception trace.)

This now checks the json and code coverage reports and tries to pull a useful error message:

  firehed@Eric-Sterns-Mac-Pro ~/dev/php-lcd: arc unit
  Exception
  The test '/Users/firehed/dev/php-lcd/tests/PlateButtonsTest.php' crashed with the following output:

  Fatal error: Undefined class constant 'EFT' in /Users/firehed/dev/php-lcd/tests/PlateButtonsTest.php on line 25

  Call Stack:
  	0.0002     233104   1. {main}() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/composer/bin/phpunit:0
  	0.0039     564872   2. PHPUnit_TextUI_Command::main() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/composer/bin/phpunit:63
  	0.0039     565496   3. PHPUnit_TextUI_Command->run() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php:129
  	0.0247    2280168   4. PHPUnit_TextUI_TestRunner->doRun() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php:176
  	0.0293    2730760   5. PHPUnit_Framework_TestSuite->run() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php:349
  	0.1211    3996832   6. PHPUnit_Framework_TestSuite->runTest() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php:745
  	0.1211    3996832   7. PHPUnit_Framework_TestCase->run() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php:775
  	0.1211    3996832   8. PHPUnit_Framework_TestResult->run() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php:783
  	0.1233    3999752   9. PHPUnit_Framework_TestCase->runBare() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php:648
  	0.1236    4016432  10. PHPUnit_Framework_TestCase->runTest() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php:838
  	0.1237    4017240  11. ReflectionMethod->invokeArgs() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php:983
  	0.1237    4017520  12. Firehed\PlateButtonsTest->testSingleButtonOnButtonDown() /Users/firehed/dev/php-lcd/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php:983

  (Run with --trace for a full exception trace.)

Or if nothing was written to `stderr`, you may get something like this:

  firehed@Eric-Sterns-Mac-Pro ~/dev/php-lcd: arc unit --no-coverage
  Exception
  Test Firehed\PlateButtonsTest::testSingleButtonOnButtonDown did not finish
  (Run with --trace for a full exception trace.)

Test Plan:
`arc unit` for arcanist itself
`arc unit` before and after the change on a project where:
* all tests pass
* some tests are failing
* a test causes a fatal error (undefined constant, bad method call, etc)
* a test file is invalid (syntax error)

No regressions that I could find, and all crashes now display a more useful error.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: epriestley

CC: aurelijus, epriestley, aran

Differential Revision: https://secure.phabricator.com/D7848
This commit is contained in:
Eric Stern 2014-01-02 12:02:22 -08:00 committed by epriestley
parent 35c01eee7b
commit 739881a3f4
4 changed files with 39 additions and 13 deletions

View file

@ -14,7 +14,6 @@ final class ArcanistUnitTestResult {
const RESULT_UNSOUND = 'unsound'; const RESULT_UNSOUND = 'unsound';
const RESULT_POSTPONED = 'postponed'; const RESULT_POSTPONED = 'postponed';
private $namespace;
private $name; private $name;
private $link; private $link;
private $result; private $result;

View file

@ -8,6 +8,7 @@ abstract class ArcanistBaseTestResultParser {
protected $enableCoverage; protected $enableCoverage;
protected $projectRoot; protected $projectRoot;
protected $coverageFile; protected $coverageFile;
protected $stderr;
public function setEnableCoverage($enable_coverage) { public function setEnableCoverage($enable_coverage) {
$this->enableCoverage = $enable_coverage; $this->enableCoverage = $enable_coverage;
@ -33,6 +34,11 @@ abstract class ArcanistBaseTestResultParser {
return $this; return $this;
} }
public function setStderr($stderr) {
$this->stderr = $stderr;
return $this;
}
/** /**
* Parse test results from provided input and return an array * Parse test results from provided input and return an array
* of ArcanistUnitTestResult * of ArcanistUnitTestResult

View file

@ -23,6 +23,14 @@ final class PhpunitResultParser extends ArcanistBaseTestResultParser {
*/ */
public function parseTestResults($path, $test_results) { public function parseTestResults($path, $test_results) {
if (!$test_results) {
$result = id(new ArcanistUnitTestResult())
->setName($path)
->setUserData($this->stderr)
->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
return array($result);
}
$report = $this->getJsonReport($test_results); $report = $this->getJsonReport($test_results);
// coverage is for all testcases in the executed $path // coverage is for all testcases in the executed $path
@ -33,8 +41,14 @@ final class PhpunitResultParser extends ArcanistBaseTestResultParser {
$results = array(); $results = array();
foreach ($report as $event) { foreach ($report as $event) {
if ('test' != $event->event) { switch ($event->event) {
continue; case 'test':
break;
case 'testStart':
$lastTestFinished = false;
// fall through
default:
continue 2; // switch + loop
} }
$status = ArcanistUnitTestResult::RESULT_PASS; $status = ArcanistUnitTestResult::RESULT_PASS;
@ -72,8 +86,15 @@ final class PhpunitResultParser extends ArcanistBaseTestResultParser {
$result->setUserData($user_data); $result->setUserData($user_data);
$results[] = $result; $results[] = $result;
$lastTestFinished = true;
} }
if (!$lastTestFinished) {
$results[] = id(new ArcanistUnitTestResult())
->setName($event->test) // use last event
->setUserData($this->stderr)
->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
}
return $results; return $results;
} }
@ -85,12 +106,7 @@ final class PhpunitResultParser extends ArcanistBaseTestResultParser {
private function readCoverage() { private function readCoverage() {
$test_results = Filesystem::readFile($this->coverageFile); $test_results = Filesystem::readFile($this->coverageFile);
if (empty($test_results)) { if (empty($test_results)) {
throw new Exception('Clover coverage XML report file is empty, ' return array();
. '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 = new DOMDocument();

View file

@ -71,8 +71,10 @@ final class PhpunitTestEngine extends ArcanistBaseUnitTestEngine {
$config = $this->configFile ? csprintf('-c %s', $this->configFile) : null; $config = $this->configFile ? csprintf('-c %s', $this->configFile) : null;
$futures[$test_path] = new ExecFuture('%C %C --log-json %s %C %s', $stderr = "-d display_errors=stderr";
$this->phpunitBinary, $config, $json_tmp, $clover, $test_path);
$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( $tmpfiles[$test_path] = array(
'json' => $json_tmp, 'json' => $json_tmp,
'clover' => $clover_tmp, 'clover' => $clover_tmp,
@ -89,7 +91,8 @@ final class PhpunitTestEngine extends ArcanistBaseUnitTestEngine {
$results[] = $this->parseTestResults( $results[] = $this->parseTestResults(
$test, $test,
$tmpfiles[$test]['json'], $tmpfiles[$test]['json'],
$tmpfiles[$test]['clover']); $tmpfiles[$test]['clover'],
$stderr);
} }
return array_mergev($results); return array_mergev($results);
@ -101,16 +104,18 @@ final class PhpunitTestEngine extends ArcanistBaseUnitTestEngine {
* @param string $path Path to test * @param string $path Path to test
* @param string $json_tmp Path to phpunit json report * @param string $json_tmp Path to phpunit json report
* @param string $clover_tmp Path to phpunit clover report * @param string $clover_tmp Path to phpunit clover report
* @param string $stderr Data written to stderr
* *
* @return array * @return array
*/ */
private function parseTestResults($path, $json_tmp, $clover_tmp) { private function parseTestResults($path, $json_tmp, $clover_tmp, $stderr) {
$test_results = Filesystem::readFile($json_tmp); $test_results = Filesystem::readFile($json_tmp);
return id(new PhpunitResultParser()) return id(new PhpunitResultParser())
->setEnableCoverage($this->getEnableCoverage()) ->setEnableCoverage($this->getEnableCoverage())
->setProjectRoot($this->projectRoot) ->setProjectRoot($this->projectRoot)
->setCoverageFile($clover_tmp) ->setCoverageFile($clover_tmp)
->setAffectedTests($this->affectedTests) ->setAffectedTests($this->affectedTests)
->setStderr($stderr)
->parseTestResults($path, $test_results); ->parseTestResults($path, $test_results);
} }