1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-22 14:52:40 +01:00

Add coverage reports to py.test runner

Summary: Of note this now forces the pytest-cov plugin to be installed unless they turn it off.

Test Plan:
```
../arcanist/bin/arc unit

COVERAGE REPORT
     97%     changes/models/test.py
    100%     tests/changes/api/serializer/models/test_testcase.py
    100%     changes/api/serializer/models/testcase.py
 UNIT OKAY  No unit test failures.
Updated an existing Differential revision:
        Revision URI: https://tails.corp.dropbox.com/D43387

Included changes:
  M       changes/api/serializer/models/testcase.py
  M       changes/models/test.py
  M       tests/changes/api/serializer/models/test_testcase.py
```

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: epriestley, Korvin

Differential Revision: https://secure.phabricator.com/D8667
This commit is contained in:
David Cramer 2014-04-02 11:31:38 -07:00 committed by epriestley
parent 11e2c1688f
commit b8e4261455

View file

@ -8,16 +8,128 @@
final class PytestTestEngine extends ArcanistBaseUnitTestEngine {
public function run() {
$working_copy = $this->getWorkingCopy();
$this->project_root = $working_copy->getProjectRoot();
$junit_tmp = new TempFile();
$cover_tmp = new TempFile();
$cmd_line = csprintf('py.test --junitxml=%s',
$future = $this->buildTestFuture($junit_tmp, $cover_tmp);
$future->resolvex();
$future = new ExecFuture('coverage xml -o %s', $cover_tmp);
$future->setCWD($this->project_root);
$future->resolvex();
return $this->parseTestResults($junit_tmp, $cover_tmp);
}
public function buildTestFuture($junit_tmp, $cover_tmp) {
$paths = $this->getPaths();
$cmd_line = csprintf('py.test --junitxml %s',
$junit_tmp);
$future = new ExecFuture('%C', $cmd_line);
list($stdout, $stderr) = $future->resolvex();
if ($this->getEnableCoverage() !== false) {
$cmd_line = csprintf('coverage run --source %s -m %C',
$this->project_root, $cmd_line);
}
return new ExecFuture('%C', $cmd_line);
}
public function parseTestResults($junit_tmp, $cover_tmp) {
$parser = new ArcanistXUnitTestResultParser();
$results = $parser->parseTestResults(
Filesystem::readFile($junit_tmp));
return $parser->parseTestResults(Filesystem::readFile($junit_tmp));
if ($this->getEnableCoverage() !== false) {
$coverage_report = $this->readCoverage($cover_tmp);
foreach ($results as $result) {
$result->setCoverage($coverage_report);
}
}
return $results;
}
public function readCoverage($path) {
$coverage_data = Filesystem::readFile($path);
if (empty($coverage_data)) {
return array();
}
$coverage_dom = new DOMDocument();
$coverage_dom->loadXML($coverage_data);
$paths = $this->getPaths();
$reports = array();
$classes = $coverage_dom->getElementsByTagName("class");
foreach ($classes as $class) {
// filename is actually python module path with ".py" at the end,
// e.g.: tornado.web.py
$relative_path = explode(".", $class->getAttribute("filename"));
array_pop($relative_path);
$relative_path = implode("/", $relative_path);
// first we check if the path is a directory (a Python package), if it is
// set relative and absolute paths to have __init__.py at the end.
$absolute_path = Filesystem::resolvePath($relative_path);
if (is_dir($absolute_path)) {
$relative_path .= "/__init__.py";
$absolute_path .= "/__init__.py";
}
// then we check if the path with ".py" at the end is file (a Python
// submodule), if it is - set relative and absolute paths to have
// ".py" at the end.
if (is_file($absolute_path.".py")) {
$relative_path .= ".py";
$absolute_path .= ".py";
}
if (!file_exists($absolute_path)) {
continue;
}
if (!in_array($relative_path, $paths)) {
continue;
}
// get total line count in file
$line_count = count(file($absolute_path));
$coverage = "";
$start_line = 1;
$lines = $class->getElementsByTagName("line");
for ($ii = 0; $ii < $lines->length; $ii++) {
$line = $lines->item($ii);
$next_line = intval($line->getAttribute("number"));
for ($start_line; $start_line < $next_line; $start_line++) {
$coverage .= "N";
}
if (intval($line->getAttribute("hits")) == 0) {
$coverage .= "U";
}
else if (intval($line->getAttribute("hits")) > 0) {
$coverage .= "C";
}
$start_line++;
}
if ($start_line < $line_count) {
foreach (range($start_line, $line_count) as $line_num) {
$coverage .= "N";
}
}
$reports[$relative_path] = $coverage;
}
return $reports;
}
}