Changeset View
Changeset View
Standalone View
Standalone View
src/unit/engine/PytestTestEngine.php
- This file was copied to src/unit/engine/__tests__/ArcanistPytestTestEngine.php.
<?php | <?php | ||||
/** | /** | ||||
* Very basic 'py.test' unit test engine wrapper. | * @deprecated | ||||
*/ | */ | ||||
final class PytestTestEngine extends ArcanistUnitTestEngine { | final class PytestTestEngine extends ArcanistPytestTestEngine { | ||||
public function run() { | public function run() { | ||||
$working_copy = $this->getWorkingCopy(); | phutil_deprecated( | ||||
$this->project_root = $working_copy->getProjectRoot(); | __CLASS__, | ||||
pht( | |||||
$junit_tmp = new TempFile(); | 'You should use `%s` instead.', | ||||
$cover_tmp = new TempFile(); | 'ArcanistPytestTestEngine')); | ||||
parent::run(); | |||||
$future = $this->buildTestFuture($junit_tmp, $cover_tmp); | |||||
list($err, $stdout, $stderr) = $future->resolve(); | |||||
if (!Filesystem::pathExists($junit_tmp)) { | |||||
throw new CommandException( | |||||
pht('Command failed with error #%s!', $err), | |||||
$future->getCommand(), | |||||
$err, | |||||
$stdout, | |||||
$stderr); | |||||
} | |||||
$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 --junit-xml=%s', $junit_tmp); | |||||
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)); | |||||
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; | |||||
} | } | ||||
} | } |