1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-01 19:22:41 +01:00
phorge-arcanist/src/unit/engine/ArcanistConfigurationDrivenUnitTestEngine.php

221 lines
5.7 KiB
PHP
Raw Normal View History

<?php
final class ArcanistConfigurationDrivenUnitTestEngine
extends ArcanistUnitTestEngine {
protected function supportsRunAllTests() {
$engines = $this->buildTestEngines();
foreach ($engines as $engine) {
if ($engine->supportsRunAllTests()) {
return true;
}
}
return false;
}
public function buildTestEngines() {
$working_copy = $this->getWorkingCopy();
$config_path = $working_copy->getProjectPath('.arcunit');
if (!Filesystem::pathExists($config_path)) {
throw new ArcanistUsageException(
pht(
"Unable to find '%s' file to configure test engines. Create an ".
"'%s' file in the root directory of the working copy.",
'.arcunit',
'.arcunit'));
}
$data = Filesystem::readFile($config_path);
$config = null;
try {
$config = phutil_json_decode($data);
} catch (PhutilJSONParserException $ex) {
throw new PhutilProxyException(
pht(
"Expected '%s' file to be a valid JSON file, but ".
"failed to decode '%s'.",
'.arcunit',
$config_path),
$ex);
}
$test_engines = $this->loadAvailableTestEngines();
try {
PhutilTypeSpec::checkMap(
$config,
array(
'engines' => 'map<string, map<string, wild>>',
));
} catch (PhutilTypeCheckException $ex) {
throw new PhutilProxyException(
pht("Error in parsing '%s' file.", $config_path),
$ex);
}
$built_test_engines = array();
$all_paths = $this->getPaths();
foreach ($config['engines'] as $name => $spec) {
$type = idx($spec, 'type');
if ($type !== null) {
if (empty($test_engines[$type])) {
throw new ArcanistUsageException(
pht(
"Test engine '%s' specifies invalid type '%s'. ".
"Available test engines are: %s.",
$name,
$type,
implode(', ', array_keys($test_engines))));
}
$test_engine = clone $test_engines[$type];
} else {
// We'll raise an error below about the invalid "type" key.
// TODO: Can we just do the type check first, and simplify this a bit?
$test_engine = null;
}
try {
PhutilTypeSpec::checkMap(
$spec,
array(
'type' => 'string',
'include' => 'optional regex | list<regex>',
'exclude' => 'optional regex | list<regex>',
));
} catch (PhutilTypeCheckException $ex) {
throw new PhutilProxyException(
pht(
"Error in parsing '%s' file, for test engine '%s'.",
'.arcunit',
$name),
$ex);
}
if ($all_paths) {
$include = (array)idx($spec, 'include', array());
$exclude = (array)idx($spec, 'exclude', array());
$paths = $this->matchPaths(
$all_paths,
$include,
$exclude);
$test_engine->setPaths($paths);
}
$built_test_engines[] = $test_engine;
}
return $built_test_engines;
}
public function run() {
$renderer = $this->renderer;
$this->setRenderer(null);
$paths = $this->getPaths();
// If we are running with `--everything` then `$paths` will be `null`.
if (!$paths) {
$paths = array();
}
$engines = $this->buildTestEngines();
$all_results = array();
$exceptions = array();
foreach ($engines as $engine) {
$engine
->setWorkingCopy($this->getWorkingCopy())
->setEnableCoverage($this->getEnableCoverage())
->setConfigurationManager($this->getConfigurationManager())
->setRenderer($renderer);
// TODO: At some point, maybe we should emit a warning here if an engine
// doesn't support `--everything`, to reduce surprise when `--everything`
// does not really mean `--everything`.
if ($engine->supportsRunAllTests()) {
$engine->setRunAllTests($this->getRunAllTests());
}
try {
// TODO: Type check the results.
$results = $engine->run();
$all_results[] = $results;
foreach ($results as $result) {
// If the proxied engine renders its own test results then there
// is no need to render them again here.
if (!$engine->shouldEchoTestResults()) {
echo $renderer->renderUnitResult($result);
}
}
} catch (ArcanistNoEffectException $ex) {
$exceptions[] = $ex;
}
}
if (!$all_results) {
// If all engines throw an `ArcanistNoEffectException`, then we should
// preserve this behavior.
throw new ArcanistNoEffectException(pht('No tests to run.'));
}
return array_mergev($all_results);
}
public function shouldEchoTestResults() {
return false;
}
private function loadAvailableTestEngines() {
return id(new PhutilClassMapQuery())
->setAncestorClass('ArcanistUnitTestEngine')
->setUniqueMethod('getEngineConfigurationName', true)
->execute();
}
/**
* TODO: This is copied from @{class:ArcanistConfigurationDrivenLintEngine}.
*/
private function matchPaths(array $paths, array $include, array $exclude) {
$match = array();
foreach ($paths as $path) {
$keep = false;
if (!$include) {
$keep = true;
} else {
foreach ($include as $rule) {
if (preg_match($rule, $path)) {
$keep = true;
break;
}
}
}
if (!$keep) {
continue;
}
if ($exclude) {
foreach ($exclude as $rule) {
if (preg_match($rule, $path)) {
continue 2;
}
}
}
$match[] = $path;
}
return $match;
}
}