private function parseOutput($output)
 {
     $results = array();
     $json = json_decode($output);
     foreach ($json as $feature) {
         if (property_exists($feature, 'elements')) {
             foreach ($feature->elements as $element) {
                 $passed = true;
                 $result = new ArcanistUnitTestResult();
                 $result->setName($feature->description);
                 foreach ($element->steps as $step) {
                     switch ($step->result->status) {
                         case 'passed':
                             break;
                         case 'failed':
                             $passed = false;
                             $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
                             $result->setUserData($step->result->error_message);
                             break;
                     }
                 }
                 if ($passed) {
                     $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
                 }
                 $results[] = $result;
             }
         }
     }
     return $results;
 }
 private function parseOutput($output)
 {
     $results = array();
     $lines = explode(PHP_EOL, $output);
     foreach ($lines as $index => $line) {
         preg_match('/^(not ok|ok)\\s+\\d+\\s+-?(.*)/', $line, $matches);
         if (count($matches) < 3) {
             continue;
         }
         $result = new ArcanistUnitTestResult();
         $result->setName(trim($matches[2]));
         switch (trim($matches[1])) {
             case 'ok':
                 $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
                 break;
             case 'not ok':
                 $exception_message = trim($lines[$index + 1]);
                 $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
                 $result->setUserData($exception_message);
                 break;
             default:
                 continue;
         }
         $results[] = $result;
     }
     return $results;
 }
 private function runTests()
 {
     $root = $this->getWorkingCopy()->getProjectRoot();
     $script = $this->getConfiguredScript();
     $path = $this->getConfiguredTestResultPath();
     foreach (glob($root . DIRECTORY_SEPARATOR . $path . "/*.xml") as $filename) {
         // Remove existing files so we cannot report old results
         $this->unlink($filename);
     }
     // Provide changed paths to process
     putenv("ARCANIST_DIFF_PATHS=" . implode(PATH_SEPARATOR, $this->getPaths()));
     $future = new ExecFuture('%C %s', $script, $path);
     $future->setCWD($root);
     $err = null;
     try {
         $future->resolvex();
     } catch (CommandException $exc) {
         $err = $exc;
     }
     $results = $this->parseTestResults($root . DIRECTORY_SEPARATOR . $path);
     if ($err) {
         $result = new ArcanistUnitTestResult();
         $result->setName('Unit Test Script');
         $result->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
         $result->setUserData("ERROR: Command failed with code {$err->getError()}\nCOMMAND: `{$err->getCommand()}`");
         $results[] = $result;
     }
     return $results;
 }
 private function checkNonEmptyTestPlan()
 {
     $result = new ArcanistUnitTestResult();
     $result->setName("Test Plan");
     $lines = join(' ', $this->getMessage());
     $testPlanExists = preg_match('/\\sTest Plan:/', $lines);
     if (!$testPlanExists) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         print 'Test Plan not found!';
         return array($result);
     }
     $platforms = $this->getPlatformTestsFromLines($this->getMessage());
     if ($platforms) {
         print "Found tests for the following platforms:\n";
         foreach ($platforms as $platform => $tests) {
             print "{$platform}:\n    ";
             print join("\n    ", $tests);
             print "\n";
         }
         $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
         print 'Test Plan found!';
         return array($result);
     }
     $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
     print "No tests found to run on CI! Check your repo's README for instructions\n";
     return array($result);
 }
Exemplo n.º 5
0
 private function runCommand($command)
 {
     exec($command, $output, $return_code);
     $result = new ArcanistUnitTestResult();
     $result->setName($command);
     $result->setResult($return_code == 0 ? ArcanistUnitTestResult::RESULT_PASS : ArcanistUnitTestResult::RESULT_FAIL);
     return array($result);
 }
 public function run()
 {
     // For a call to `arc call-conduit differential.updateunitresults` to
     // succeed we need at least one entry here.
     $result = new ArcanistUnitTestResult();
     $result->setName("dummy_placeholder_entry");
     $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
     return array($result);
 }
Exemplo n.º 7
0
 public function run()
 {
     $working_copy = $this->getWorkingCopy();
     $this->project_root = $working_copy->getProjectRoot();
     // We only want to report results for tests that actually ran, so
     // we'll compare the test result files' timestamps to the start time
     // of the test run. This will probably break if multiple test runs
     // are happening in parallel, but if that's happening then we can't
     // count on the results files being intact anyway.
     $start_time = time();
     $maven_top_dirs = $this->findTopLevelMavenDirectories();
     // We'll figure out if any of the modified files we're testing are in
     // Maven directories. We won't want to run a bunch of Java tests for
     // changes to CSS files or whatever.
     $modified_paths = $this->getModifiedPaths();
     $maven_failed = false;
     foreach ($maven_top_dirs as $dir) {
         $dir_with_trailing_slash = $dir . '/';
         foreach ($modified_paths as $path) {
             if ($dir_with_trailing_slash === substr($path, 0, strlen($dir_with_trailing_slash))) {
                 $future = new ExecFuture('mvn test');
                 $future->setCWD($dir);
                 list($status, $stdout, $stderr) = $future->resolve();
                 if ($status) {
                     // Maven exits with a nonzero status if there were test failures
                     // or if there was a compilation error.
                     $maven_failed = true;
                     break 2;
                 }
                 break;
             }
         }
     }
     $testResults = $this->parseTestResultsSince($start_time);
     if ($maven_failed) {
         // If there wasn't a test failure, then synthesize one to represent
         // the failure of the test run as a whole, since it probably means the
         // code failed to compile.
         $found_failure = false;
         foreach ($testResults as $testResult) {
             if ($testResult->getResult() === ArcanistUnitTestResult::RESULT_FAIL || $testResult->getResult() === ArcanistUnitTestResult::RESULT_BROKEN) {
                 $found_failure = true;
                 break;
             }
         }
         if (!$found_failure) {
             $testResult = new ArcanistUnitTestResult();
             $testResult->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
             $testResult->setName('mvn test');
             $testResults[] = $testResult;
         }
     }
     return $testResults;
 }
 public function run()
 {
     // Here we create a new unit test "jenkins_async_test" and promise we'll
     // update the results later.
     // Jenkins updates the results using `arc call-conduit
     // differential.updateunitresults` call. If you change the name here, also
     // make sure to change the name in Jenkins script that updates the test
     // result -- they have to be the same.
     $result = new ArcanistUnitTestResult();
     $result->setName("jenkins_async_test");
     $result->setResult(ArcanistUnitTestResult::RESULT_POSTPONED);
     return array($result);
 }
Exemplo n.º 9
0
 public function run()
 {
     $working_copy = $this->getWorkingCopy();
     $this->projectRoot = $working_copy->getProjectRoot();
     $future = new ExecFuture('npm run coverage');
     $future->setCWD($this->projectRoot);
     list($err, $stdout, $stderr) = $future->resolve();
     $result = new ArcanistUnitTestResult();
     $result->setName("Node test engine");
     $result->setUserData($stdout);
     if ($err) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
     } else {
         $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
     }
     return array($result);
 }
Exemplo n.º 10
0
 private function parseOutput($output)
 {
     $results = array();
     $json = json_decode($output, true);
     foreach ($json['examples'] as $example) {
         $result = new ArcanistUnitTestResult();
         $result->setName($example['full_description']);
         if (array_key_exists('run_time', $example)) {
             $result->setDuration($example['run_time']);
         }
         switch ($example['status']) {
             case 'passed':
                 $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
                 break;
             case 'failed':
                 $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
                 $result->setUserData($example['exception']['message']);
                 break;
             case 'pending':
                 $result->setResult(ArcanistUnitTestResult::RESULT_SKIP);
                 break;
         }
         $results[] = $result;
     }
     return $results;
 }
Exemplo n.º 11
0
 /**
  * Runs the test suite.
  */
 public function run()
 {
     $results = array();
     $command = '(mkdir -p build && cd build && cmake ..)';
     $command .= '&& make -C build all';
     $command .= '&& make -C build test';
     // Execute the test command & time it.
     $timeStart = microtime(true);
     $future = new ExecFuture($command);
     do {
         $future->read();
         sleep(0.5);
     } while (!$future->isReady());
     list($error, $stdout, $stderr) = $future->resolve();
     $timeEnd = microtime(true);
     // Create a unit test result structure.
     $result = new ArcanistUnitTestResult();
     $result->setNamespace('DerpVision');
     $result->setName('Core');
     $result->setDuration($timeEnd - $timeStart);
     if ($error == 0) {
         $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
     } else {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         $result->setUserData($stdout . $stderr);
     }
     $results[] = $result;
     return $results;
 }
 public function run()
 {
     // If we are running asynchronously, mark all tests as postponed
     // and return those results.  Otherwise, run the tests and collect
     // the actual results.
     if ($this->getEnableAsyncTests()) {
         $results = array();
         $result = new ArcanistUnitTestResult();
         $result->setName("jcommon_build");
         $result->setResult(ArcanistUnitTestResult::RESULT_POSTPONED);
         $results[] = $result;
         return $results;
     } else {
         $server = new FacebookBuildServer();
         $server->startProjectBuilds(false);
         return array();
     }
 }
Exemplo n.º 13
0
 public function runJs()
 {
     // First, check to see if karma is on $PATH:
     list($err, $stdout, $_) = exec_manual("which karma");
     if ($err != 0) {
         $result = new ArcanistUnitTestResult();
         $result->setName("Karma not found. Skipping js tests...");
         $result->setResult(ArcanistUnitTestResult::RESULT_SKIP);
         $result->setDuration(0);
         return array($result);
     }
     // Karma IS on the path.
     $old_dir = getcwd();
     $project_root = $this->getWorkingCopy()->getProjectRoot();
     chdir($project_root . '/client/js');
     exec_manual("karma start karma-conf-oneshot.js");
     chdir($old_dir);
     // Read from the text-results.xml file.
     $xml = file_get_contents($project_root . '/client/test-results.xml');
     $doc = new SimpleXMLElement($xml);
     // Destroy the test-results.xml file.
     unlink($project_root . '/client/test-results.xml');
     // Extract all the test cases.
     $results = array();
     foreach ($doc->testsuite as $suite) {
         $suite_name = $suite['name'];
         foreach ($suite->testcase as $case) {
             $case_name = $case['name'];
             $time = $case['time'];
             $fixture_name = substr($case['classname'], strlen($suite_name) + 1);
             // Did we fail?
             $failure = (string) $case->failure;
             // Convert each to a ArcanistUnitTestResult
             $result = new ArcanistUnitTestResult();
             $result->setName($fixture_name . ' ' . $case_name);
             $result->setResult($failure ? ArcanistUnitTestResult::RESULT_FAIL : ArcanistUnitTestResult::RESULT_PASS);
             $result->setUserData($failure);
             $result->setDuration($time);
             $results[] = $result;
         }
     }
     return $results;
 }
 public function testCoverageMerges()
 {
     $cases = array(array('coverage' => array(), 'expect' => null), array('coverage' => array('UUUNCNC'), 'expect' => 'UUUNCNC'), array('coverage' => array('UUCUUU', 'UUUUCU'), 'expect' => 'UUCUCU'), array('coverage' => array('UUCCCU', 'UUUCCCNNNC'), 'expect' => 'UUCCCCNNNC'));
     foreach ($cases as $case) {
         $input = $case['coverage'];
         $expect = $case['expect'];
         $actual = ArcanistUnitTestResult::mergeCoverage($input);
         $this->assertEqual($expect, $actual);
     }
 }
 private function checkNonEmptyTestPlan()
 {
     $result = new ArcanistUnitTestResult();
     $result->setName("Test Plan");
     $lines = join(' ', $this->getMessage());
     $testPlanExists = preg_match('/\\sTest Plan:/', $lines);
     if (!$testPlanExists) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         print 'Test Plan not found!';
         return array($result);
     }
     $testPlanEmpty = preg_match('/\\sTest Plan:\\s*?Reviewers:/', $lines);
     if ($testPlanEmpty) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         print 'Test Plan cannot be empty!';
         return array($result);
     }
     $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
     print 'Test Plan found!';
     return array($result);
 }
Exemplo n.º 16
0
 private function parseTestResults($test, $err, $stdout, $stderr)
 {
     $result = new ArcanistUnitTestResult();
     $result->setName($test);
     $result->setUserData($stdout . $stderr);
     $result->setResult($err == 0 ? ArcanistUnitTestResult::RESULT_PASS : ArcanistUnitTestResult::RESULT_FAIL);
     if (preg_match("/# ELAPSED: (\\d+)ms/", $stderr, $M)) {
         $result->setDuration($M[1] / 1000);
     }
     return $result;
 }
 public function renderUnitResult(ArcanistUnitTestResult $result)
 {
     $result_code = $result->getResult();
     $duration = '';
     if ($result_code == ArcanistUnitTestResult::RESULT_PASS) {
         $duration = ' ' . $this->formatTestDuration($result->getDuration());
     }
     $return = sprintf("  %s %s\n", $this->getFormattedResult($result->getResult()) . $duration, $result->getName());
     if ($result_code != ArcanistUnitTestResult::RESULT_PASS) {
         $return .= $result->getUserData() . "\n";
     }
     return $return;
 }
 /**
  * Parse test results from phpunit json report
  *
  * @param string $path Path to test
  * @param string $test_results String containing phpunit json report
  *
  * @return array
  */
 public function parseTestResults($path, $test_results)
 {
     $report = $this->getJsonReport($test_results);
     // coverage is for all testcases in the executed $path
     $coverage = array();
     if ($this->enableCoverage !== false) {
         $coverage = $this->readCoverage();
     }
     $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 (strpos($event->message, 'Skipped Test') !== false) {
                     $status = ArcanistUnitTestResult::RESULT_SKIP;
                     $user_data .= $event->message;
                 } else {
                     if (strpos($event->message, 'Incomplete Test') !== false) {
                         $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;
 }
Exemplo n.º 19
0
 private function checkNonEmptyRevertPlan()
 {
     $result = new ArcanistUnitTestResult();
     $lines = implode(' ', $this->getMessage());
     $revert_plan_exists = preg_match('/\\sRevert Plan:/', $lines);
     if (!$revert_plan_exists) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         $result->setName('Revert Plan not found!
         (See http://t.uber.com/revert for more info)');
         return array($result);
     }
     $revert_plan_empty = preg_match('/\\sRevert Plan:\\s*?$/', $lines);
     if ($revert_plan_empty) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         $result->setName('Revert Plan cannot be empty!
         (See http://t.uber.com/revert for more info)');
         return array($result);
     }
     $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
     $result->setName('Revert Plan found!');
     return array($result);
 }
Exemplo n.º 20
0
 public function run()
 {
     $results = array();
     $build_start = microtime(true);
     $config_manager = $this->getConfigurationManager();
     if ($this->getEnableCoverage() !== false) {
         $command = $config_manager->getConfigFromAnySource('unit.engine.tap.cover');
     } else {
         $command = $config_manager->getConfigFromAnySource('unit.engine.tap.command');
     }
     $timeout = $config_manager->getConfigFromAnySource('unit.engine.tap.timeout');
     if (!$timeout) {
         $timeout = 15;
     }
     $future = new ExecFuture('%C', $command);
     $future->setTimeout($timeout);
     $result = new ArcanistUnitTestResult();
     $result->setName($command ? $command : 'unknown');
     try {
         list($stdout, $stderr) = $future->resolvex();
         $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
         if ($this->getEnableCoverage() !== false) {
             $coverage = $this->readCoverage('coverage/cobertura-coverage.xml');
             $result->setCoverage($coverage);
         }
     } catch (CommandException $exc) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
         if ($future->getWasKilledByTimeout()) {
             print "Process stdout:\n" . $exc->getStdout() . "\nProcess stderr:\n" . $exc->getStderr() . "\nExceeded timeout of {$timeout} secs.\nMake unit tests faster.";
         } else {
             $result->setUserdata($exc->getStdout() . $exc->getStderr());
         }
     }
     $result->setDuration(microtime(true) - $build_start);
     $results[] = $result;
     return $results;
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $author_phid = $request->getUser()->getPHID();
     $rendering_reference = $request->getStr('ref');
     $parts = explode('/', $rendering_reference);
     if (count($parts) == 2) {
         list($id, $vs) = $parts;
     } else {
         $id = $parts[0];
         $vs = 0;
     }
     $id = (int) $id;
     $vs = (int) $vs;
     $changeset = id(new DifferentialChangeset())->load($id);
     if (!$changeset) {
         return new Aphront404Response();
     }
     $view = $request->getStr('view');
     if ($view) {
         $changeset->attachHunks($changeset->loadHunks());
         $phid = idx($changeset->getMetadata(), "{$view}:binary-phid");
         if ($phid) {
             return id(new AphrontRedirectResponse())->setURI("/file/info/{$phid}/");
         }
         switch ($view) {
             case 'new':
                 return $this->buildRawFileResponse($changeset, $is_new = true);
             case 'old':
                 if ($vs && $vs != -1) {
                     $vs_changeset = id(new DifferentialChangeset())->load($vs);
                     if ($vs_changeset) {
                         $vs_changeset->attachHunks($vs_changeset->loadHunks());
                         return $this->buildRawFileResponse($vs_changeset, $is_new = true);
                     }
                 }
                 return $this->buildRawFileResponse($changeset, $is_new = false);
             default:
                 return new Aphront400Response();
         }
     }
     if ($vs && $vs != -1) {
         $vs_changeset = id(new DifferentialChangeset())->load($vs);
         if (!$vs_changeset) {
             return new Aphront404Response();
         }
     }
     if (!$vs) {
         $right = $changeset;
         $left = null;
         $right_source = $right->getID();
         $right_new = true;
         $left_source = $right->getID();
         $left_new = false;
         $render_cache_key = $right->getID();
     } else {
         if ($vs == -1) {
             $right = null;
             $left = $changeset;
             $right_source = $left->getID();
             $right_new = false;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         } else {
             $right = $changeset;
             $left = $vs_changeset;
             $right_source = $right->getID();
             $right_new = true;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         }
     }
     if ($left) {
         $left->attachHunks($left->loadHunks());
     }
     if ($right) {
         $right->attachHunks($right->loadHunks());
     }
     if ($left) {
         $left_data = $left->makeNewFile();
         if ($right) {
             $right_data = $right->makeNewFile();
         } else {
             $right_data = $left->makeOldFile();
         }
         $engine = new PhabricatorDifferenceEngine();
         $synthetic = $engine->generateChangesetFromFileContent($left_data, $right_data);
         $choice = clone nonempty($left, $right);
         $choice->attachHunks($synthetic->getHunks());
         $changeset = $choice;
     }
     $coverage = null;
     if ($right && $right->getDiffID()) {
         $unit = id(new DifferentialDiffProperty())->loadOneWhere('diffID = %d AND name = %s', $right->getDiffID(), 'arc:unit');
         if ($unit) {
             $coverage = array();
             foreach ($unit->getData() as $result) {
                 $result_coverage = idx($result, 'coverage');
                 if (!$result_coverage) {
                     continue;
                 }
                 $file_coverage = idx($result_coverage, $right->getFileName());
                 if (!$file_coverage) {
                     continue;
                 }
                 $coverage[] = $file_coverage;
             }
             $coverage = ArcanistUnitTestResult::mergeCoverage($coverage);
         }
     }
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec);
     $parser = new DifferentialChangesetParser();
     $parser->setCoverage($coverage);
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($rendering_reference);
     $parser->setRenderCacheKey($render_cache_key);
     $parser->setRightSideCommentMapping($right_source, $right_new);
     $parser->setLeftSideCommentMapping($left_source, $left_new);
     $parser->setWhitespaceMode($request->getStr('whitespace'));
     if ($left && $right) {
         $parser->setOriginals($left, $right);
     }
     // Load both left-side and right-side inline comments.
     $inlines = $this->loadInlineComments(array($left_source, $right_source), $author_phid);
     if ($left_new) {
         $inlines = array_merge($inlines, $this->buildLintInlineComments($left));
     }
     if ($right_new) {
         $inlines = array_merge($inlines, $this->buildLintInlineComments($right));
     }
     $phids = array();
     foreach ($inlines as $inline) {
         $parser->parseInlineComment($inline);
         if ($inline->getAuthorPHID()) {
             $phids[$inline->getAuthorPHID()] = true;
         }
     }
     $phids = array_keys($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $parser->setHandles($handles);
     $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
     $parser->setMarkupEngine($engine);
     if ($request->isAjax()) {
         // TODO: This is sort of lazy, the effect is just to not render "Edit"
         // and "Reply" links on the "standalone view".
         $parser->setUser($request->getUser());
     }
     $output = $parser->render($range_s, $range_e, $mask);
     $mcov = $parser->renderModifiedCoverage();
     if ($request->isAjax()) {
         $coverage = array('differential-mcoverage-' . md5($changeset->getFilename()) => $mcov);
         return id(new PhabricatorChangesetResponse())->setRenderedChangeset($output)->setCoverage($coverage);
     }
     Javelin::initBehavior('differential-show-more', array('uri' => '/differential/changeset/', 'whitespace' => $request->getStr('whitespace')));
     Javelin::initBehavior('differential-comment-jump', array());
     $detail = new DifferentialChangesetDetailView();
     $detail->setChangeset($changeset);
     $detail->appendChild($output);
     $detail->setVsChangesetID($left_source);
     $output = id(new DifferentialPrimaryPaneView())->setLineWidthFromChangesets(array($changeset))->appendChild('<div class="differential-review-stage" ' . 'id="differential-review-stage">' . $detail->render() . '</div>');
     return $this->buildStandardPageResponse(array($output), array('title' => 'Changeset View'));
 }
 private function loadCoverage(DifferentialChangeset $changeset)
 {
     $target_phids = $changeset->getDiff()->getBuildTargetPHIDs();
     if (!$target_phids) {
         return null;
     }
     $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere('buildTargetPHID IN (%Ls)', $target_phids);
     if (!$unit) {
         return null;
     }
     $coverage = array();
     foreach ($unit as $message) {
         $test_coverage = $message->getProperty('coverage');
         if ($test_coverage === null) {
             continue;
         }
         $coverage_data = idx($test_coverage, $changeset->getFileName());
         if (!strlen($coverage_data)) {
             continue;
         }
         $coverage[] = $coverage_data;
     }
     if (!$coverage) {
         return null;
     }
     return ArcanistUnitTestResult::mergeCoverage($coverage);
 }
Exemplo n.º 23
0
 public function loadCoverageMap(PhabricatorUser $viewer)
 {
     $target_phids = $this->getBuildTargetPHIDs();
     if (!$target_phids) {
         return array();
     }
     $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere('buildTargetPHID IN (%Ls)', $target_phids);
     $map = array();
     foreach ($unit as $message) {
         $coverage = $message->getProperty('coverage', array());
         foreach ($coverage as $path => $coverage_data) {
             $map[$path][] = $coverage_data;
         }
     }
     foreach ($map as $path => $coverage_items) {
         $map[$path] = ArcanistUnitTestResult::mergeCoverage($coverage_items);
     }
     return $map;
 }
 public function render()
 {
     $this->requireResource('differential-core-view-css');
     $this->requireResource('differential-table-of-contents-css');
     $rows = array();
     $coverage = array();
     if ($this->unitTestData) {
         $coverage_by_file = array();
         foreach ($this->unitTestData as $result) {
             $test_coverage = idx($result, 'coverage');
             if (!$test_coverage) {
                 continue;
             }
             foreach ($test_coverage as $file => $results) {
                 $coverage_by_file[$file][] = $results;
             }
         }
         foreach ($coverage_by_file as $file => $coverages) {
             $coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages);
         }
     }
     $changesets = $this->changesets;
     $paths = array();
     foreach ($changesets as $id => $changeset) {
         $type = $changeset->getChangeType();
         $ftype = $changeset->getFileType();
         $ref = idx($this->references, $id);
         $display_file = $changeset->getDisplayFilename();
         $meta = null;
         if (DifferentialChangeType::isOldLocationChangeType($type)) {
             $away = $changeset->getAwayPaths();
             if (count($away) > 1) {
                 $meta = array();
                 if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
                     $meta[] = pht('Deleted after being copied to multiple locations:');
                 } else {
                     $meta[] = pht('Copied to multiple locations:');
                 }
                 foreach ($away as $path) {
                     $meta[] = $path;
                 }
                 $meta = phutil_implode_html(phutil_tag('br'), $meta);
             } else {
                 if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
                     $display_file = $this->renderRename($display_file, reset($away), "→");
                 } else {
                     $meta = pht('Copied to %s', reset($away));
                 }
             }
         } else {
             if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
                 $old_file = $changeset->getOldFile();
                 $display_file = $this->renderRename($display_file, $old_file, "←");
             } else {
                 if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
                     $meta = pht('Copied from %s', $changeset->getOldFile());
                 }
             }
         }
         $link = $this->renderChangesetLink($changeset, $ref, $display_file);
         $line_count = $changeset->getAffectedLineCount();
         if ($line_count == 0) {
             $lines = '';
         } else {
             $lines = ' ' . pht('(%d line(s))', $line_count);
         }
         $char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
         $chartitle = DifferentialChangeType::getFullNameForChangeType($type);
         $desc = DifferentialChangeType::getShortNameForFileType($ftype);
         if ($desc) {
             $desc = '(' . $desc . ')';
         }
         $pchar = $changeset->getOldProperties() === $changeset->getNewProperties() ? '' : phutil_tag('span', array('title' => pht('Properties Changed')), 'M');
         $fname = $changeset->getFilename();
         $cov = $this->renderCoverage($coverage, $fname);
         if ($cov === null) {
             $mcov = $cov = phutil_tag('em', array(), '-');
         } else {
             $mcov = phutil_tag('div', array('id' => 'differential-mcoverage-' . md5($fname), 'class' => 'differential-mcoverage-loading'), isset($this->visibleChangesets[$id]) ? pht('Loading...') : pht('?'));
         }
         if ($meta) {
             $meta = phutil_tag('div', array('class' => 'differential-toc-meta'), $meta);
         }
         if ($this->diff && $this->repository) {
             $paths[] = $changeset->getAbsoluteRepositoryPath($this->repository, $this->diff);
         }
         $rows[] = array($char, $pchar, $desc, array($link, $lines, $meta), $cov, $mcov);
     }
     $editor_link = null;
     if ($paths && $this->user) {
         $editor_link = $this->user->loadEditorLink($paths, 1, $this->repository->getCallsign());
         if ($editor_link) {
             $editor_link = phutil_tag('a', array('href' => $editor_link, 'class' => 'button differential-toc-edit-all'), pht('Open All in Editor'));
         }
     }
     $reveal_link = javelin_tag('a', array('sigil' => 'differential-reveal-all', 'mustcapture' => true, 'class' => 'button differential-toc-reveal-all'), pht('Show All Context'));
     $buttons = phutil_tag('div', array('class' => 'differential-toc-buttons grouped'), array($editor_link, $reveal_link));
     $table = id(new AphrontTableView($rows));
     $table->setHeaders(array('', '', '', pht('Path'), pht('Coverage (All)'), pht('Coverage (Touched)')));
     $table->setColumnClasses(array('differential-toc-char center', 'differential-toc-prop center', 'differential-toc-ftype center', 'differential-toc-file wide', 'differential-toc-cov', 'differential-toc-cov'));
     $table->setDeviceVisibility(array(true, true, true, true, false, false));
     $anchor = id(new PhabricatorAnchorView())->setAnchorName('toc')->setNavigationMarker(true);
     return id(new PHUIObjectBoxView())->setHeaderText(pht('Table of Contents'))->appendChild($anchor)->appendChild($table)->appendChild($buttons);
 }
Exemplo n.º 25
0
 function generateValgrindTestResults()
 {
     $this->terminateProcess();
     if ($this->coverage) {
         return $this->generateCoverageResults();
     }
     if (!$this->valgrind) {
         return array();
     }
     $definite_leaks = array();
     $possible_leaks = array();
     $errors = array();
     $descriptors = array();
     // valgrind seems to use an interesting definition of valid XML.
     // Tolerate having multiple documents in one file.
     // Confluence of weird bugs; hhvm has very low preg_match limits
     // so we have to grovel around to make sure that we read this
     // stuff in properly :-/
     $documents = array();
     $in_doc = false;
     $doc = null;
     foreach (file($this->vg_log . '.xml') as $line) {
         if ($in_doc) {
             $doc[] = $line;
             if (preg_match(',</valgrindoutput>,', $line)) {
                 $documents[] = implode("\n", $doc);
                 $doc = null;
             }
         } else {
             if (preg_match(',<valgrindoutput>,', $line)) {
                 $doc = array($line);
                 $in_doc = true;
             }
         }
     }
     libxml_use_internal_errors(true);
     foreach ($documents as $data) {
         libxml_clear_errors();
         $vg = @simplexml_load_string($data);
         if (is_object($vg)) {
             foreach ($vg->error as $err) {
                 $render = $this->renderVGResult($err);
                 switch ($err->kind) {
                     case 'Leak_DefinitelyLost':
                         $definite_leaks[] = $render;
                         break;
                     case 'Leak_PossiblyLost':
                         $possible_leaks[] = $render;
                         break;
                     default:
                         $errors[] = $render;
                 }
             }
             // These look like fd leak records, but they're not documented
             // as such.  These go away if we turn off track-fds
             foreach ($vg->stack as $stack) {
                 // Suppressing this for now: posix_spawn seems to confuse
                 // some valgrind's, particularly the version we run on travis,
                 // as it records open descriptors from the exec'ing child
                 // $descriptors[] = $this->renderVGStack($stack);
             }
         } else {
             $why = 'failed to parse xml';
             $lines = explode("\n", $data);
             foreach (libxml_get_errors() as $err) {
                 $slice = array_slice($lines, $err->line - 3, 6);
                 $slice = implode("\n", $slice);
                 $why .= sprintf("\n%s (line %d col %d) %s", $err->message, $err->line, $err->column, $slice);
             }
             printf("parsing valgrind output: %s\n", $why);
         }
     }
     $results = array();
     $res = new ArcanistUnitTestResult();
     $res->setName('valgrind possible leaks');
     $res->setUserData(implode("\n\n", $possible_leaks));
     $res->setResult(count($possible_leaks) ? ArcanistUnitTestResult::RESULT_SKIP : ArcanistUnitTestResult::RESULT_PASS);
     $results[] = $res;
     $res = new ArcanistUnitTestResult();
     $res->setName('descriptor leaks');
     $res->setUserData(implode("\n\n", $descriptors));
     $res->setResult(count($descriptors) ? ArcanistUnitTestResult::RESULT_FAIL : ArcanistUnitTestResult::RESULT_PASS);
     $results[] = $res;
     $res = new ArcanistUnitTestResult();
     $res->setName('valgrind leaks');
     $res->setUserData(implode("\n\n", $definite_leaks));
     $leak_res = count($definite_leaks) ? ArcanistUnitTestResult::RESULT_FAIL : ArcanistUnitTestResult::RESULT_PASS;
     if ($leak_res == ArcanistUnitTestResult::RESULT_FAIL && getenv('TRAVIS') == 'true') {
         // Travis has false positives at this time, downgrade
         $leak_res = ArcanistUnitTestResult::RESULT_SKIP;
     }
     $res->setResult($leak_res);
     $results[] = $res;
     $res = new ArcanistUnitTestResult();
     $res->setName('valgrind errors');
     $res->setUserData(implode("\n\n", $errors));
     $res->setResult(count($errors) ? ArcanistUnitTestResult::RESULT_FAIL : ArcanistUnitTestResult::RESULT_PASS);
     $results[] = $res;
     return $results;
 }
 public function render()
 {
     require_celerity_resource('differential-core-view-css');
     require_celerity_resource('differential-table-of-contents-css');
     $rows = array();
     $coverage = array();
     if ($this->unitTestData) {
         $coverage_by_file = array();
         foreach ($this->unitTestData as $result) {
             $test_coverage = idx($result, 'coverage');
             if (!$test_coverage) {
                 continue;
             }
             foreach ($test_coverage as $file => $results) {
                 $coverage_by_file[$file][] = $results;
             }
         }
         foreach ($coverage_by_file as $file => $coverages) {
             $coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages);
         }
     }
     $changesets = $this->changesets;
     $paths = array();
     foreach ($changesets as $id => $changeset) {
         $type = $changeset->getChangeType();
         $ftype = $changeset->getFileType();
         $ref = idx($this->references, $id);
         $link = $this->renderChangesetLink($changeset, $ref);
         if (DifferentialChangeType::isOldLocationChangeType($type)) {
             $away = $changeset->getAwayPaths();
             if (count($away) > 1) {
                 $meta = array();
                 if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
                     $meta[] = 'Deleted after being copied to multiple locations:';
                 } else {
                     $meta[] = 'Copied to multiple locations:';
                 }
                 foreach ($away as $path) {
                     $meta[] = phutil_escape_html($path);
                 }
                 $meta = implode('<br />', $meta);
             } else {
                 if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
                     $meta = 'Moved to ' . phutil_escape_html(reset($away));
                 } else {
                     $meta = 'Copied to ' . phutil_escape_html(reset($away));
                 }
             }
         } else {
             if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
                 $meta = 'Moved from ' . phutil_escape_html($changeset->getOldFile());
             } else {
                 if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
                     $meta = 'Copied from ' . phutil_escape_html($changeset->getOldFile());
                 } else {
                     $meta = null;
                 }
             }
         }
         $line_count = $changeset->getAffectedLineCount();
         if ($line_count == 0) {
             $lines = null;
         } else {
             $lines = ' ' . pht('(%d line(s))', $line_count);
         }
         $char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
         $chartitle = DifferentialChangeType::getFullNameForChangeType($type);
         $desc = DifferentialChangeType::getShortNameForFileType($ftype);
         if ($desc) {
             $desc = '(' . $desc . ')';
         }
         $pchar = $changeset->getOldProperties() === $changeset->getNewProperties() ? null : '<span title="Properties Changed">M</span>';
         $fname = $changeset->getFilename();
         $cov = $this->renderCoverage($coverage, $fname);
         if ($cov === null) {
             $mcov = $cov = '<em>-</em>';
         } else {
             $mcov = phutil_render_tag('div', array('id' => 'differential-mcoverage-' . md5($fname), 'class' => 'differential-mcoverage-loading'), isset($this->visibleChangesets[$id]) ? 'Loading...' : '?');
         }
         $rows[] = '<tr>' . phutil_render_tag('td', array('class' => 'differential-toc-char', 'title' => $chartitle), $char) . '<td class="differential-toc-prop">' . $pchar . '</td>' . '<td class="differential-toc-ftype">' . $desc . '</td>' . '<td class="differential-toc-file">' . $link . $lines . '</td>' . '<td class="differential-toc-cov">' . $cov . '</td>' . '<td class="differential-toc-mcov">' . $mcov . '</td>' . '</tr>';
         if ($meta) {
             $rows[] = '<tr>' . '<td colspan="3"></td>' . '<td class="differential-toc-meta">' . $meta . '</td>' . '</tr>';
         }
         if ($this->diff && $this->repository) {
             $paths[] = $changeset->getAbsoluteRepositoryPath($this->repository, $this->diff);
         }
     }
     $editor_link = null;
     if ($paths && $this->user) {
         $editor_link = $this->user->loadEditorLink(implode(' ', $paths), 1, $this->repository->getCallsign());
         if ($editor_link) {
             $editor_link = phutil_render_tag('a', array('href' => $editor_link, 'class' => 'button differential-toc-edit-all'), 'Open All in Editor');
         }
     }
     $reveal_link = javelin_render_tag('a', array('sigil' => 'differential-reveal-all', 'mustcapture' => true, 'class' => 'button differential-toc-reveal-all'), 'Show All Context');
     return '<div id="differential-review-toc" ' . 'class="differential-toc differential-panel">' . $editor_link . $reveal_link . '<h1>Table of Contents</h1>' . '<table>' . '<tr>' . '<th></th>' . '<th></th>' . '<th></th>' . '<th>Path</th>' . '<th class="differential-toc-cov">Coverage (All)</th>' . '<th class="differential-toc-mcov">Coverage (Touched)</th>' . '</tr>' . implode("\n", $rows) . '</table>' . '</div>';
 }
 /**
  * Parse test results from provided input and return an array
  * of @{class:ArcanistUnitTestResult}.
  *
  * @param string $test_results String containing test results
  *
  * @return array ArcanistUnitTestResult
  */
 public function parseTestResults($test_results)
 {
     if (!strlen($test_results)) {
         throw new Exception(pht('%s argument to %s must not be empty', 'test_results', 'parseTestResults()'));
     }
     // xunit xsd: https://gist.github.com/959290
     $xunit_dom = new DOMDocument();
     $load_success = @$xunit_dom->loadXML($test_results);
     if (!$load_success) {
         $input_start = id(new PhutilUTF8StringTruncator())->setMaximumGlyphs(150)->truncateString($test_results);
         throw new Exception(sprintf("%s\n\n%s", pht('Failed to load XUnit report; Input starts with:'), $input_start));
     }
     $results = array();
     $testcases = $xunit_dom->getElementsByTagName('testcase');
     foreach ($testcases as $testcase) {
         $classname = $testcase->getAttribute('classname');
         $name = $testcase->getAttribute('name');
         $time = $testcase->getAttribute('time');
         $status = ArcanistUnitTestResult::RESULT_PASS;
         $user_data = '';
         // A skipped test is a test which was ignored using framework
         // mechanisms (e.g. @skip decorator)
         $skipped = $testcase->getElementsByTagName('skipped');
         if ($skipped->length > 0) {
             $status = ArcanistUnitTestResult::RESULT_SKIP;
             $messages = array();
             for ($ii = 0; $ii < $skipped->length; $ii++) {
                 $messages[] = trim($skipped->item($ii)->nodeValue, " \n");
             }
             $user_data .= implode("\n", $messages);
         }
         // Failure is a test which the code has explicitly failed by using
         // the mechanisms for that purpose. e.g., via an assertEquals
         $failures = $testcase->getElementsByTagName('failure');
         if ($failures->length > 0) {
             $status = ArcanistUnitTestResult::RESULT_FAIL;
             $messages = array();
             for ($ii = 0; $ii < $failures->length; $ii++) {
                 $messages[] = trim($failures->item($ii)->nodeValue, " \n");
             }
             $user_data .= implode("\n", $messages) . "\n";
         }
         // An errored test is one that had an unanticipated problem. e.g., an
         // unchecked throwable, or a problem with an implementation of the test.
         $errors = $testcase->getElementsByTagName('error');
         if ($errors->length > 0) {
             $status = ArcanistUnitTestResult::RESULT_BROKEN;
             $messages = array();
             for ($ii = 0; $ii < $errors->length; $ii++) {
                 $messages[] = trim($errors->item($ii)->nodeValue, " \n");
             }
             $user_data .= implode("\n", $messages) . "\n";
         }
         $result = new ArcanistUnitTestResult();
         $result->setName($classname . '.' . $name);
         $result->setResult($status);
         $result->setDuration((double) $time);
         $result->setUserData($user_data);
         $results[] = $result;
     }
     return $results;
 }
Exemplo n.º 28
0
 /**
  * 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;
 }
Exemplo n.º 29
0
 /**
  * Parse test results from Go test report
  * (e.g. `go test -v`)
  *
  * @param string $path Path to test
  * @param string $test_results String containing Go test output
  *
  * @return array
  */
 public function parseTestResults($path, $test_results)
 {
     $test_results = explode("\n", $test_results);
     $results = array();
     // We'll get our full test case name at the end and add it back in
     $test_case_name = '';
     // Temp store for test case results (in case we run multiple test cases)
     $test_case_results = array();
     foreach ($test_results as $i => $line) {
         if (strncmp($line, '--- PASS', 8) === 0) {
             // We have a passing test
             $meta = array();
             preg_match('/^--- PASS: (?P<test_name>.+) \\((?P<time>.+) seconds\\).*/', $line, $meta);
             $result = new ArcanistUnitTestResult();
             // For now set name without test case, we'll add it later
             $result->setName($meta['test_name']);
             $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
             $result->setDuration($meta['time']);
             $test_case_results[] = $result;
             continue;
         }
         if (strncmp($line, '--- FAIL', 8) === 0) {
             // We have a failing test
             $reason = trim($test_results[$i + 1]);
             $meta = array();
             preg_match('/^--- FAIL: (?P<test_name>.+) \\((?P<time>.+) seconds\\).*/', $line, $meta);
             $result = new ArcanistUnitTestResult();
             $result->setName($meta['test_name']);
             $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
             $result->setDuration($meta['time']);
             $result->setUserData($reason . "\n");
             $test_case_results[] = $result;
             continue;
         }
         if (strncmp($line, 'ok', 2) === 0) {
             $meta = array();
             preg_match('/^ok[\\s\\t]+(?P<test_name>\\w.*)[\\s\\t]+(?P<time>.*)s.*/', $line, $meta);
             $test_case_name = str_replace('/', '::', $meta['test_name']);
             // Our test case passed
             // check to make sure we were in verbose (-v) mode
             if (empty($test_case_results)) {
                 // We weren't in verbose mode
                 // create one successful result for the whole test case
                 $test_name = 'Go::TestCase::' . $test_case_name;
                 $result = new ArcanistUnitTestResult();
                 $result->setName($test_name);
                 $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
                 $result->setDuration($meta['time']);
                 $results[] = $result;
             } else {
                 $test_case_results = $this->fixNames($test_case_results, $test_case_name);
                 $results = array_merge($results, $test_case_results);
                 $test_case_results = array();
             }
             continue;
         }
         if (strncmp($line, "FAIL\t", 5) === 0) {
             $meta = array();
             preg_match('/^FAIL[\\s\\t]+(?P<test_name>\\w.*)[\\s\\t]+.*/', $line, $meta);
             $test_case_name = str_replace('/', '::', $meta['test_name']);
             $test_case_results = $this->fixNames($test_case_results, $test_case_name);
             $results = array_merge($results, $test_case_results);
             $test_case_results = array();
             continue;
         }
     }
     return $results;
 }
Exemplo n.º 30
0
 public function run()
 {
     $working_copy = $this->getWorkingCopy();
     $paths = $this->getArgument('paths');
     $rev = $this->getArgument('rev');
     $everything = $this->getArgument('everything');
     if ($everything && $paths) {
         throw new ArcanistUsageException(pht('You can not specify paths with %s. The %s flag runs every test.', '--everything', '--everything'));
     }
     if ($everything) {
         $paths = iterator_to_array($this->getRepositoryApi()->getAllFiles());
     } else {
         $paths = $this->selectPathsForWorkflow($paths, $rev);
     }
     $this->engine = $this->newUnitTestEngine($this->getArgument('engine'));
     if ($everything) {
         $this->engine->setRunAllTests(true);
     } else {
         $this->engine->setPaths($paths);
     }
     $this->engine->setArguments($this->getPassthruArgumentsAsMap('unit'));
     $renderer = new ArcanistUnitConsoleRenderer();
     $this->engine->setRenderer($renderer);
     $enable_coverage = null;
     // Means "default".
     if ($this->getArgument('coverage') || $this->getArgument('detailed-coverage')) {
         $enable_coverage = true;
     } else {
         if ($this->getArgument('no-coverage')) {
             $enable_coverage = false;
         }
     }
     $this->engine->setEnableCoverage($enable_coverage);
     // Enable possible async tests only for 'arc diff' not 'arc unit'
     if ($this->getParentWorkflow()) {
         $this->engine->setEnableAsyncTests(true);
     } else {
         $this->engine->setEnableAsyncTests(false);
     }
     $results = $this->engine->run();
     $this->validateUnitEngineResults($this->engine, $results);
     $this->testResults = $results;
     $console = PhutilConsole::getConsole();
     $output_format = $this->getOutputFormat();
     if ($output_format !== 'full') {
         $console->disableOut();
     }
     $unresolved = array();
     $coverage = array();
     foreach ($results as $result) {
         $result_code = $result->getResult();
         if ($this->engine->shouldEchoTestResults()) {
             $console->writeOut('%s', $renderer->renderUnitResult($result));
         }
         if ($result_code != ArcanistUnitTestResult::RESULT_PASS) {
             $unresolved[] = $result;
         }
         if ($result->getCoverage()) {
             foreach ($result->getCoverage() as $file => $report) {
                 $coverage[$file][] = $report;
             }
         }
     }
     if ($coverage) {
         $file_coverage = array_fill_keys($paths, 0);
         $file_reports = array();
         foreach ($coverage as $file => $reports) {
             $report = ArcanistUnitTestResult::mergeCoverage($reports);
             $cov = substr_count($report, 'C');
             $uncov = substr_count($report, 'U');
             if ($cov + $uncov) {
                 $coverage = $cov / ($cov + $uncov);
             } else {
                 $coverage = 0;
             }
             $file_coverage[$file] = $coverage;
             $file_reports[$file] = $report;
         }
         $console->writeOut("\n__%s__\n", pht('COVERAGE REPORT'));
         asort($file_coverage);
         foreach ($file_coverage as $file => $coverage) {
             $console->writeOut("    **%s%%**     %s\n", sprintf('% 3d', (int) (100 * $coverage)), $file);
             $full_path = $working_copy->getProjectRoot() . '/' . $file;
             if ($this->getArgument('detailed-coverage') && Filesystem::pathExists($full_path) && is_file($full_path) && array_key_exists($file, $file_reports)) {
                 $console->writeOut('%s', $this->renderDetailedCoverageReport(Filesystem::readFile($full_path), $file_reports[$file]));
             }
         }
     }
     $this->unresolvedTests = $unresolved;
     $overall_result = self::RESULT_OKAY;
     foreach ($results as $result) {
         $result_code = $result->getResult();
         if ($result_code == ArcanistUnitTestResult::RESULT_FAIL || $result_code == ArcanistUnitTestResult::RESULT_BROKEN) {
             $overall_result = self::RESULT_FAIL;
             break;
         } else {
             if ($result_code == ArcanistUnitTestResult::RESULT_UNSOUND) {
                 $overall_result = self::RESULT_UNSOUND;
             }
         }
     }
     if ($output_format !== 'full') {
         $console->enableOut();
     }
     $data = array_values(mpull($results, 'toDictionary'));
     switch ($output_format) {
         case 'ugly':
             $console->writeOut('%s', json_encode($data));
             break;
         case 'json':
             $json = new PhutilJSON();
             $console->writeOut('%s', $json->encodeFormatted($data));
             break;
         case 'full':
             // already printed
             break;
         case 'none':
             // do nothing
             break;
     }
     return $overall_result;
 }