MDL-63366 core_phpunit: Add ability to specify coverage filters

This commit is contained in:
Andrew Nicols
2019-03-01 10:30:31 +08:00
parent 89d1238962
commit 0970aa10af
3 changed files with 217 additions and 16 deletions
+119 -16
View File
@@ -24,6 +24,7 @@
*/
require_once(__DIR__.'/../../testing/classes/util.php');
require_once(__DIR__ . "/coverage_info.php");
/**
* Collection of utility methods.
@@ -489,29 +490,62 @@ class phpunit_util extends testing_util {
<testsuite name="@component@_testsuite">
<directory suffix="_test.php">@dir@</directory>
</testsuite>';
$filtertemplate = '
<testsuite name="@component@_testsuite">
<directory suffix="_test.php">@dir@</directory>
</testsuite>';
$data = file_get_contents("$CFG->dirroot/phpunit.xml.dist");
$suites = '';
$whitelists = [];
$excludelists = [];
$subsystems = core_component::get_core_subsystems();
$subsystems['core'] = $CFG->dirroot . '/lib';
foreach ($subsystems as $subsystem => $fulldir) {
if (empty($fulldir)) {
continue;
}
if (!file_exists("{$fulldir}/tests/")) {
// There are no tests - skip this directory.
continue;
}
$dir = substr($fulldir, strlen($CFG->dirroot) + 1);
if ($coverageinfo = self::get_coverage_info($fulldir)) {
$whitelists = array_merge($whitelists, $coverageinfo->get_whitelists($dir));
$excludelists = array_merge($excludelists, $coverageinfo->get_excludelists($dir));
}
}
$plugintypes = core_component::get_plugin_types();
ksort($plugintypes);
foreach ($plugintypes as $type=>$unused) {
foreach (array_keys($plugintypes) as $type) {
$plugs = core_component::get_plugin_list($type);
ksort($plugs);
foreach ($plugs as $plug=>$fullplug) {
if (!file_exists("$fullplug/tests/")) {
foreach ($plugs as $plug => $plugindir) {
if (!file_exists("{$plugindir}/tests/")) {
// There are no tests - skip this directory.
continue;
}
$dir = substr($fullplug, strlen($CFG->dirroot)+1);
$dir .= '/tests';
$component = $type.'_'.$plug;
$dir = substr($plugindir, strlen($CFG->dirroot) + 1);
$testdir = "{$dir}/tests";
$component = "{$type}_{$plug}";
$suite = str_replace('@component@', $component, $template);
$suite = str_replace('@dir@', $dir, $suite);
$suite = str_replace('@dir@', $testdir, $suite);
$suites .= $suite;
if ($coverageinfo = self::get_coverage_info($plugindir)) {
$whitelists = array_merge($whitelists, $coverageinfo->get_whitelists($dir));
$excludelists = array_merge($excludelists, $coverageinfo->get_excludelists($dir));
}
}
}
// Start a sequence between 100000 and 199000 to ensure each call to init produces
// different ids in the database. This reduces the risk that hard coded values will
// end up being placed in phpunit or behat test code.
@@ -523,6 +557,9 @@ class phpunit_util extends testing_util {
'<const name="PHPUNIT_SEQUENCE_START" value="' . $sequencestart . '"/>',
$data);
$filters = self::get_filter_config($whitelists, $excludelists);
$data = str_replace('<!--@filterlist@-->', $filters, $data);
$result = false;
if (is_writable($CFG->dirroot)) {
if ($result = file_put_contents("$CFG->dirroot/phpunit.xml", $data)) {
@@ -551,19 +588,18 @@ class phpunit_util extends testing_util {
global $CFG;
$template = '
<testsuites>
<testsuite name="@component@_testsuite">
<directory suffix="_test.php">.</directory>
</testsuite>
</testsuites>
<filter>
<testsuites>
<testsuite name="@component@_testsuite">
<directory suffix="_test.php">.</directory>
</testsuite>
</testsuites>';
$filterdefault = '
<whitelist processUncoveredFilesFromWhitelist="false">
<directory suffix=".php">.</directory>
<exclude>
<directory suffix="_test.php">.</directory>
</exclude>
</whitelist>
</filter>';
</whitelist>';
// Start a sequence between 100000 and 199000 to ensure each call to init produces
// different ids in the database. This reduces the risk that hard coded values will
@@ -583,8 +619,17 @@ class phpunit_util extends testing_util {
$ctemplate = $template;
$ctemplate = str_replace('@component@', $cname, $ctemplate);
// Apply it to the file template
$fcontents = str_replace('<!--@component_suite@-->', $ctemplate, $ftemplate);
// Check for filter configurations.
if ($coverageinfo = self::get_coverage_info($cpath)) {
$filters = self::get_filter_config($coverageinfo->get_whitelists(''), $coverageinfo->get_excludelists(''));
} else {
$filters = $filterdefault;
}
$fcontents = str_replace('<!--@filterlist@-->', $filters, $fcontents);
// Apply it to the file template.
$fcontents = str_replace(
'<const name="PHPUNIT_SEQUENCE_START" value=""/>',
'<const name="PHPUNIT_SEQUENCE_START" value="' . $sequencestart . '"/>',
@@ -881,4 +926,62 @@ class phpunit_util extends testing_util {
$method->setAccessible(true);
return $method->invokeArgs($object, $params);
}
/**
* Pad the supplied string with $level levels of indentation.
*
* @param string $string The string to pad
* @param int $level The number of levels of indentation to pad
* @return string
*/
protected static function pad(string $string, int $level) : string {
return str_repeat(" ", $level * 4) . "{$string}\n";
}
/**
* Get the filter config for the supplied whitelist and excludelist configuration.
*
* @param array[] $whitelists The list of files/folders in the whitelist.
* @param array[] $excludelists The list of files/folders in the excludelist.
* @return string
*/
protected static function get_filter_config(array $whitelists, array $excludelists) : string {
$filters = '';
if (!empty($whitelists)) {
$filters .= self::pad("<whitelist>", 2);
foreach ($whitelists as $line) {
$filters .= self::pad($line, 3);
}
if (!empty($excludelists)) {
$filters .= self::pad("<exclude>", 3);
foreach ($excludelists as $line) {
$filters .= self::pad($line, 4);
}
$filters .= self::pad("</exclude>", 3);
}
$filters .= self::pad("</whitelist>", 2);
}
return $filters;
}
/**
* Get the phpunit_coverage_info for the specified plugin or subsystem directory.
*
* @param string $fulldir The directory to find the coverage info file in.
* @return phpunit_coverage_info
*/
protected static function get_coverage_info(string $fulldir): ?phpunit_coverage_info {
$coverageconfig = "{$fulldir}/tests/coverage.php";
if (file_exists($coverageconfig)) {
$coverageinfo = require($coverageconfig);
if (!$coverageinfo instanceof phpunit_coverage_info) {
throw new \coding_exception("{$coverageconfig} does not return a phpunit_coverage_info");
}
return $coverageinfo;
}
return null;
}
}