This commit is contained in:
Huong Nguyen
2024-07-15 13:21:46 +07:00
7 changed files with 526 additions and 259 deletions
@@ -0,0 +1,11 @@
issueNumber: MDL-66903
notes:
core:
- message: |
The following classes have been renamed and now support autoloading.
Existing classes are currently unaffected.
| Old class name | New class name |
| --- | --- |
| `\core_component` | `\core\component` |
type: improved
@@ -0,0 +1,20 @@
issueNumber: MDL-66903
notes:
core:
- message: >
Added the ability for unit tests to autoload classes in the
`\[component]\tests\`
namespace from the `[path/to/component]/tests/classes` directory.
type: improved
- message: >
Added a helper to load fixtures from a components `tests/fixtures/`
folder:
```php
advanced_testcase::load_fixture(string $component, string $fixture): void;
```
type: improved
+63 -8
View File
@@ -22,6 +22,15 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core;
use core\exception\coding_exception;
use stdClass;
use ArrayIterator;
use DirectoryIterator;
use Exception;
use RegexIterator;
// Constants used in version.php files, these must exist when core_component executes.
// We make use of error_log as debugging is not always available.
@@ -43,7 +52,7 @@ define('ANY_VERSION', 'any');
/**
* Collection of components related methods.
*/
class core_component {
class component {
/** @var array list of ignored directories in plugin type roots - watch out for auth/db exception */
protected static $ignoreddirs = [
'CVS' => true,
@@ -160,7 +169,7 @@ class core_component {
$debugging = "Class '%s' has been renamed for the autoloader and is now deprecated. Please use '%s' instead.";
debugging(sprintf($debugging, $classname, $newclassname), DEBUG_DEVELOPER);
if (PHP_VERSION_ID >= 70000 && preg_match('#\\\null(\\\|$)#', $classname)) {
throw new \coding_exception("Cannot alias $classname to $newclassname");
throw new coding_exception("Cannot alias $classname to $newclassname");
}
class_alias($newclassname, $classname);
return;
@@ -172,6 +181,38 @@ class core_component {
require($file);
return;
}
if (PHPUNIT_TEST) {
// For unit tests we support classes in `\frankenstyle_component\tests\` to be loaded from
// `path/to/frankenstyle/component/tests/classes` directory.
// Note: We do *not* support the legacy `\frankenstyle_component_tests_style_classnames`.
if ($component = self::get_component_from_classname($classname)) {
$pathoptions = [
'/tests/classes' => "{$component}\\tests\\",
'/tests/behat' => "{$component}\\behat\\",
];
foreach ($pathoptions as $path => $testnamespace) {
if (preg_match("#^" . preg_quote($testnamespace) . "#", $classname)) {
$path = self::get_component_directory($component) . $path;
$relativeclassname = str_replace(
$testnamespace,
'',
$classname,
);
$file = sprintf(
"%s/%s.php",
$path,
str_replace('\\', '/', $relativeclassname),
);
if (!empty($file) && file_exists($file)) {
require($file);
return;
}
break;
}
}
}
}
}
/**
@@ -242,7 +283,6 @@ class core_component {
return $file;
}
/**
* Initialise caches, always call before accessing self:: caches.
*/
@@ -357,6 +397,17 @@ class core_component {
}
}
/**
* Reset the initialisation of the component utility.
*
* Note: It should not be necessary to call this in regular code.
* Please only use it where strictly required.
*/
public static function reset(): void {
// The autoloader will re-initialise if plugintypes is null.
self::$plugintypes = null;
}
/**
* Check whether the cache content in the supplied cache is valid.
*
@@ -696,7 +747,7 @@ $cache = ' . var_export($cache, true) . ';
if (!is_dir($fulldir)) {
continue;
}
$items = new \DirectoryIterator($fulldir);
$items = new DirectoryIterator($fulldir);
foreach ($items as $item) {
if ($item->isDot() || !$item->isDir()) {
continue;
@@ -794,7 +845,7 @@ $cache = ' . var_export($cache, true) . ';
return;
}
$items = new \DirectoryIterator($fulldir);
$items = new DirectoryIterator($fulldir);
foreach ($items as $item) {
if ($item->isDot()) {
continue;
@@ -1468,13 +1519,13 @@ $cache = ' . var_export($cache, true) . ';
foreach ($legacyclasses as $classname => $path) {
if (is_array($path)) {
if (!$allowsubsystems) {
throw new \Exception(
throw new Exception(
"Invalid legacy classes path entry for {$classname}. " .
"Only files within the component can be specified.",
);
}
if (count($path) !== 2) {
throw new \Exception(
throw new Exception(
"Invalid legacy classes path entry for {$classname}. " .
"Entries must be in the format [subsystem, path].",
);
@@ -1482,7 +1533,7 @@ $cache = ' . var_export($cache, true) . ';
[$subsystem, $path] = $path;
$subsystem = substr($subsystem, 5);
if (!array_key_exists($subsystem, self::$subsystems)) {
throw new \Exception(
throw new Exception(
"Unknown subsystem '{$subsystem}' for legacy classes entry of '{$classname}'",
);
}
@@ -1588,3 +1639,7 @@ $cache = ' . var_export($cache, true) . ';
return file_exists("$plugindir/pix/monologo.svg") || file_exists("$plugindir/pix/monologo.png");
}
}
// Alias this class to the old name.
// This should be kept here because we use this class in external tooling.
class_alias(component::class, \core_component::class);
+49 -10
View File
@@ -57,7 +57,7 @@ abstract class advanced_testcase extends base_testcase {
* Runs the bare test sequence.
*/
final public function runBare(): void {
global $DB;
global $CFG, $DB;
if (phpunit_util::$lastdbwrites != $DB->perf_get_writes()) {
// This happens when previous test does not reset, we can not use transactions.
@@ -70,20 +70,18 @@ abstract class advanced_testcase extends base_testcase {
try {
$this->setCurrentTimeStart();
parent::runBare();
// Set DB reference in case somebody mocked it in test.
$DB = phpunit_util::get_global_backup('DB');
// Deal with any debugging messages.
$debugerror = phpunit_util::display_debugging_messages(true);
$this->resetDebugging();
if (!empty($debugerror)) {
trigger_error('Unexpected debugging() call detected.' . "\n" . $debugerror, E_USER_NOTICE);
}
} catch (Exception $ex) {
$e = $ex;
} catch (Throwable $ex) {
// Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5).
$e = $ex;
} finally {
// Reset global state after test and test failure.
$CFG = phpunit_util::get_global_backup('CFG');
$DB = phpunit_util::get_global_backup('DB');
// We need to reset the autoloader.
\core_component::reset();
}
if (isset($e)) {
@@ -92,6 +90,13 @@ abstract class advanced_testcase extends base_testcase {
throw $e;
}
// Deal with any debugging messages.
$debugerror = phpunit_util::display_debugging_messages(true);
$this->resetDebugging();
if (!empty($debugerror)) {
trigger_error('Unexpected debugging() call detected.' . "\n" . $debugerror, E_USER_NOTICE);
}
if (!$this->testdbtransaction || $this->testdbtransaction->is_disposed()) {
$this->testdbtransaction = null;
}
@@ -824,4 +829,38 @@ abstract class advanced_testcase extends base_testcase {
$mockedcomponent->setStaticPropertyValue('plugins', $plugins);
$this->resetDebugging();
}
/**
* Convenience method to load a fixture from a component's fixture directory.
*
* @param string $component
* @param string $path
* @throws coding_exception
*/
protected static function load_fixture(
string $component,
string $path,
): void {
$fullpath = sprintf(
"%s/tests/fixtures/%s",
\core_component::get_component_directory($component),
$path,
);
if (!file_exists($fullpath)) {
throw new \coding_exception("Fixture file not found: $fullpath");
}
global $ADMIN;
global $CFG;
global $DB;
global $SITE;
global $USER;
global $OUTPUT;
global $PAGE;
global $SESSION;
global $COURSE;
global $SITE;
require_once($fullpath);
}
}
+2
View File
@@ -529,6 +529,7 @@ class phpunit_util extends testing_util {
$template = <<<EOF
<testsuite name="@component@_testsuite">
<directory suffix="_test.php">@dir@</directory>
<exclude>@dir@/classes</exclude>
</testsuite>
EOF;
@@ -621,6 +622,7 @@ class phpunit_util extends testing_util {
<testsuites>
<testsuite name="@component@_testsuite">
<directory suffix="_test.php">.</directory>
<exclude>./classes</exclude>
</testsuite>
</testsuites>
EOT;
+311 -241
View File
@@ -14,19 +14,30 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
// phpcs:disable moodle.PHPUnit.TestCaseNames.MissingNS
namespace core;
use DirectoryIterator;
use ReflectionClass;
use ReflectionProperty;
/**
* core_component related tests.
* component related tests.
*
* @package core
* @category test
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @covers \core_component
* @covers \core\component
*/
final class component_test extends advanced_testcase {
final class component_test extends \advanced_testcase {
#[\Override]
public function tearDown(): void {
parent::tearDown();
component::reset();
}
/**
* To be changed if number of subsystems increases/decreases,
* this is defined here to annoy devs that try to add more without any thinking,
@@ -37,7 +48,7 @@ final class component_test extends advanced_testcase {
public function test_get_core_subsystems(): void {
global $CFG;
$subsystems = core_component::get_core_subsystems();
$subsystems = component::get_core_subsystems();
$this->assertCount(
self::SUBSYSTEMCOUNT,
@@ -96,7 +107,7 @@ final class component_test extends advanced_testcase {
public function test_deprecated_get_core_subsystems(): void {
global $CFG;
$subsystems = core_component::get_core_subsystems();
$subsystems = component::get_core_subsystems();
$this->assertSame($subsystems, get_core_subsystems(true));
@@ -125,7 +136,7 @@ final class component_test extends advanced_testcase {
'Non-empty $CFG->themedir is not covered by any tests yet, you need to disable it.',
);
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$this->assertStringStartsWith("$CFG->dirroot/", $fulldir);
@@ -135,7 +146,7 @@ final class component_test extends advanced_testcase {
public function test_deprecated_get_plugin_types(): void {
global $CFG;
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
$this->assertSame($plugintypes, get_plugin_types());
$this->assertSame($plugintypes, get_plugin_types(true));
@@ -151,10 +162,10 @@ final class component_test extends advanced_testcase {
public function test_get_plugin_list(): void {
global $CFG;
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$plugins = core_component::get_plugin_list($plugintype);
$plugins = component::get_plugin_list($plugintype);
foreach ($plugins as $pluginname => $plugindir) {
$this->assertStringStartsWith("$CFG->dirroot/", $plugindir);
}
@@ -170,33 +181,33 @@ final class component_test extends advanced_testcase {
}
public function test_deprecated_get_plugin_list(): void {
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$plugins = core_component::get_plugin_list($plugintype);
$plugins = component::get_plugin_list($plugintype);
$this->assertSame($plugins, get_plugin_list($plugintype));
}
}
public function test_get_plugin_directory(): void {
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$plugins = core_component::get_plugin_list($plugintype);
$plugins = component::get_plugin_list($plugintype);
foreach ($plugins as $pluginname => $plugindir) {
$this->assertSame($plugindir, core_component::get_plugin_directory($plugintype, $pluginname));
$this->assertSame($plugindir, component::get_plugin_directory($plugintype, $pluginname));
}
}
}
public function test_deprecated_get_plugin_directory(): void {
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$plugins = core_component::get_plugin_list($plugintype);
$plugins = component::get_plugin_list($plugintype);
foreach ($plugins as $pluginname => $plugindir) {
$this->assertSame(
core_component::get_plugin_directory($plugintype, $pluginname),
component::get_plugin_directory($plugintype, $pluginname),
get_plugin_directory($plugintype, $pluginname),
);
}
@@ -204,22 +215,22 @@ final class component_test extends advanced_testcase {
}
public function test_get_subsystem_directory(): void {
$subsystems = core_component::get_core_subsystems();
$subsystems = component::get_core_subsystems();
foreach ($subsystems as $subsystem => $fulldir) {
$this->assertSame($fulldir, core_component::get_subsystem_directory($subsystem));
$this->assertSame($fulldir, component::get_subsystem_directory($subsystem));
}
}
/**
* Test that the get_plugin_list_with_file() function returns the correct list of plugins.
*
* @covers \core_component::is_valid_plugin_name
* @covers \core\component::is_valid_plugin_name
* @dataProvider is_valid_plugin_name_provider
* @param array $arguments
* @param bool $expected
*/
public function test_is_valid_plugin_name(array $arguments, bool $expected): void {
$this->assertEquals($expected, core_component::is_valid_plugin_name(...$arguments));
$this->assertEquals($expected, component::is_valid_plugin_name(...$arguments));
}
/**
@@ -266,89 +277,89 @@ final class component_test extends advanced_testcase {
public function test_normalize_componentname(): void {
// Moodle core.
$this->assertSame('core', core_component::normalize_componentname('core'));
$this->assertSame('core', core_component::normalize_componentname('moodle'));
$this->assertSame('core', core_component::normalize_componentname(''));
$this->assertSame('core', component::normalize_componentname('core'));
$this->assertSame('core', component::normalize_componentname('moodle'));
$this->assertSame('core', component::normalize_componentname(''));
// Moodle core subsystems.
$this->assertSame('core_admin', core_component::normalize_componentname('admin'));
$this->assertSame('core_admin', core_component::normalize_componentname('core_admin'));
$this->assertSame('core_admin', core_component::normalize_componentname('moodle_admin'));
$this->assertSame('core_admin', component::normalize_componentname('admin'));
$this->assertSame('core_admin', component::normalize_componentname('core_admin'));
$this->assertSame('core_admin', component::normalize_componentname('moodle_admin'));
// Activity modules and their subplugins.
$this->assertSame('mod_workshop', core_component::normalize_componentname('workshop'));
$this->assertSame('mod_workshop', core_component::normalize_componentname('mod_workshop'));
$this->assertSame('workshopform_accumulative', core_component::normalize_componentname('workshopform_accumulative'));
$this->assertSame('mod_quiz', core_component::normalize_componentname('quiz'));
$this->assertSame('quiz_grading', core_component::normalize_componentname('quiz_grading'));
$this->assertSame('mod_data', core_component::normalize_componentname('data'));
$this->assertSame('datafield_checkbox', core_component::normalize_componentname('datafield_checkbox'));
$this->assertSame('mod_workshop', component::normalize_componentname('workshop'));
$this->assertSame('mod_workshop', component::normalize_componentname('mod_workshop'));
$this->assertSame('workshopform_accumulative', component::normalize_componentname('workshopform_accumulative'));
$this->assertSame('mod_quiz', component::normalize_componentname('quiz'));
$this->assertSame('quiz_grading', component::normalize_componentname('quiz_grading'));
$this->assertSame('mod_data', component::normalize_componentname('data'));
$this->assertSame('datafield_checkbox', component::normalize_componentname('datafield_checkbox'));
// Other plugin types.
$this->assertSame('auth_mnet', core_component::normalize_componentname('auth_mnet'));
$this->assertSame('enrol_self', core_component::normalize_componentname('enrol_self'));
$this->assertSame('block_html', core_component::normalize_componentname('block_html'));
$this->assertSame('block_mnet_hosts', core_component::normalize_componentname('block_mnet_hosts'));
$this->assertSame('local_amos', core_component::normalize_componentname('local_amos'));
$this->assertSame('local_admin', core_component::normalize_componentname('local_admin'));
$this->assertSame('auth_mnet', component::normalize_componentname('auth_mnet'));
$this->assertSame('enrol_self', component::normalize_componentname('enrol_self'));
$this->assertSame('block_html', component::normalize_componentname('block_html'));
$this->assertSame('block_mnet_hosts', component::normalize_componentname('block_mnet_hosts'));
$this->assertSame('local_amos', component::normalize_componentname('local_amos'));
$this->assertSame('local_admin', component::normalize_componentname('local_admin'));
// Unknown words without underscore are supposed to be activity modules.
$this->assertSame(
'mod_whoonearthwouldcomewithsuchastupidnameofcomponent',
core_component::normalize_componentname('whoonearthwouldcomewithsuchastupidnameofcomponent')
component::normalize_componentname('whoonearthwouldcomewithsuchastupidnameofcomponent')
);
// Module names can not contain underscores, this must be a subplugin.
$this->assertSame(
'whoonearth_wouldcomewithsuchastupidnameofcomponent',
core_component::normalize_componentname('whoonearth_wouldcomewithsuchastupidnameofcomponent')
component::normalize_componentname('whoonearth_wouldcomewithsuchastupidnameofcomponent')
);
$this->assertSame(
'whoonearth_would_come_withsuchastupidnameofcomponent',
core_component::normalize_componentname('whoonearth_would_come_withsuchastupidnameofcomponent')
component::normalize_componentname('whoonearth_would_come_withsuchastupidnameofcomponent')
);
}
public function test_normalize_component(): void {
// Moodle core.
$this->assertSame(['core', null], core_component::normalize_component('core'));
$this->assertSame(['core', null], core_component::normalize_component('moodle'));
$this->assertSame(['core', null], core_component::normalize_component(''));
$this->assertSame(['core', null], component::normalize_component('core'));
$this->assertSame(['core', null], component::normalize_component('moodle'));
$this->assertSame(['core', null], component::normalize_component(''));
// Moodle core subsystems.
$this->assertSame(['core', 'admin'], core_component::normalize_component('admin'));
$this->assertSame(['core', 'admin'], core_component::normalize_component('core_admin'));
$this->assertSame(['core', 'admin'], core_component::normalize_component('moodle_admin'));
$this->assertSame(['core', 'admin'], component::normalize_component('admin'));
$this->assertSame(['core', 'admin'], component::normalize_component('core_admin'));
$this->assertSame(['core', 'admin'], component::normalize_component('moodle_admin'));
// Activity modules and their subplugins.
$this->assertSame(['mod', 'workshop'], core_component::normalize_component('workshop'));
$this->assertSame(['mod', 'workshop'], core_component::normalize_component('mod_workshop'));
$this->assertSame(['workshopform', 'accumulative'], core_component::normalize_component('workshopform_accumulative'));
$this->assertSame(['mod', 'quiz'], core_component::normalize_component('quiz'));
$this->assertSame(['quiz', 'grading'], core_component::normalize_component('quiz_grading'));
$this->assertSame(['mod', 'data'], core_component::normalize_component('data'));
$this->assertSame(['datafield', 'checkbox'], core_component::normalize_component('datafield_checkbox'));
$this->assertSame(['mod', 'workshop'], component::normalize_component('workshop'));
$this->assertSame(['mod', 'workshop'], component::normalize_component('mod_workshop'));
$this->assertSame(['workshopform', 'accumulative'], component::normalize_component('workshopform_accumulative'));
$this->assertSame(['mod', 'quiz'], component::normalize_component('quiz'));
$this->assertSame(['quiz', 'grading'], component::normalize_component('quiz_grading'));
$this->assertSame(['mod', 'data'], component::normalize_component('data'));
$this->assertSame(['datafield', 'checkbox'], component::normalize_component('datafield_checkbox'));
// Other plugin types.
$this->assertSame(['auth', 'mnet'], core_component::normalize_component('auth_mnet'));
$this->assertSame(['enrol', 'self'], core_component::normalize_component('enrol_self'));
$this->assertSame(['block', 'html'], core_component::normalize_component('block_html'));
$this->assertSame(['block', 'mnet_hosts'], core_component::normalize_component('block_mnet_hosts'));
$this->assertSame(['local', 'amos'], core_component::normalize_component('local_amos'));
$this->assertSame(['local', 'admin'], core_component::normalize_component('local_admin'));
$this->assertSame(['auth', 'mnet'], component::normalize_component('auth_mnet'));
$this->assertSame(['enrol', 'self'], component::normalize_component('enrol_self'));
$this->assertSame(['block', 'html'], component::normalize_component('block_html'));
$this->assertSame(['block', 'mnet_hosts'], component::normalize_component('block_mnet_hosts'));
$this->assertSame(['local', 'amos'], component::normalize_component('local_amos'));
$this->assertSame(['local', 'admin'], component::normalize_component('local_admin'));
// Unknown words without underscore are supposed to be activity modules.
$this->assertSame(
['mod', 'whoonearthwouldcomewithsuchastupidnameofcomponent'],
core_component::normalize_component('whoonearthwouldcomewithsuchastupidnameofcomponent')
component::normalize_component('whoonearthwouldcomewithsuchastupidnameofcomponent')
);
// Module names can not contain underscores, this must be a subplugin.
$this->assertSame(
['whoonearth', 'wouldcomewithsuchastupidnameofcomponent'],
core_component::normalize_component('whoonearth_wouldcomewithsuchastupidnameofcomponent')
component::normalize_component('whoonearth_wouldcomewithsuchastupidnameofcomponent')
);
$this->assertSame(
['whoonearth', 'would_come_withsuchastupidnameofcomponent'],
core_component::normalize_component('whoonearth_would_come_withsuchastupidnameofcomponent')
component::normalize_component('whoonearth_would_come_withsuchastupidnameofcomponent')
);
}
@@ -397,17 +408,17 @@ final class component_test extends advanced_testcase {
}
public function test_get_component_directory(): void {
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$plugins = core_component::get_plugin_list($plugintype);
$plugins = component::get_plugin_list($plugintype);
foreach ($plugins as $pluginname => $plugindir) {
$this->assertSame($plugindir, core_component::get_component_directory(($plugintype . '_' . $pluginname)));
$this->assertSame($plugindir, component::get_component_directory(($plugintype . '_' . $pluginname)));
}
}
$subsystems = core_component::get_core_subsystems();
$subsystems = component::get_core_subsystems();
foreach ($subsystems as $subsystem => $fulldir) {
$this->assertSame($fulldir, core_component::get_component_directory(('core_' . $subsystem)));
$this->assertSame($fulldir, component::get_component_directory(('core_' . $subsystem)));
}
}
@@ -417,7 +428,7 @@ final class component_test extends advanced_testcase {
* @dataProvider get_component_from_classname_provider
* @param string $classname The class name to test
* @param string|null $expected The expected component
* @covers \core_component::get_component_from_classname
* @covers \core\component::get_component_from_classname
*/
public function test_get_component_from_classname(
string $classname,
@@ -425,7 +436,7 @@ final class component_test extends advanced_testcase {
): void {
$this->assertEquals(
$expected,
\core_component::get_component_from_classname($classname),
component::get_component_from_classname($classname),
);
}
@@ -479,15 +490,15 @@ final class component_test extends advanced_testcase {
}
public function test_deprecated_get_component_directory(): void {
$plugintypes = core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
foreach ($plugintypes as $plugintype => $fulldir) {
$plugins = core_component::get_plugin_list($plugintype);
$plugins = component::get_plugin_list($plugintype);
foreach ($plugins as $pluginname => $plugindir) {
$this->assertSame($plugindir, get_component_directory(($plugintype . '_' . $pluginname)));
}
}
$subsystems = core_component::get_core_subsystems();
$subsystems = component::get_core_subsystems();
foreach ($subsystems as $subsystem => $fulldir) {
$this->assertSame($fulldir, get_component_directory(('core_' . $subsystem)));
}
@@ -496,13 +507,13 @@ final class component_test extends advanced_testcase {
public function test_get_subtype_parent(): void {
global $CFG;
$this->assertNull(core_component::get_subtype_parent('mod'));
$this->assertNull(component::get_subtype_parent('mod'));
// Any plugin with more subtypes is ok here.
$this->assertFileExists("$CFG->dirroot/mod/assign/db/subplugins.json");
$this->assertSame('mod_assign', core_component::get_subtype_parent('assignsubmission'));
$this->assertSame('mod_assign', core_component::get_subtype_parent('assignfeedback'));
$this->assertNull(core_component::get_subtype_parent('assignxxxxx'));
$this->assertSame('mod_assign', component::get_subtype_parent('assignsubmission'));
$this->assertSame('mod_assign', component::get_subtype_parent('assignfeedback'));
$this->assertNull(component::get_subtype_parent('assignxxxxx'));
}
public function test_get_subplugins(): void {
@@ -511,11 +522,11 @@ final class component_test extends advanced_testcase {
// Any plugin with more subtypes is ok here.
$this->assertFileExists("$CFG->dirroot/mod/assign/db/subplugins.json");
$subplugins = core_component::get_subplugins('mod_assign');
$subplugins = component::get_subplugins('mod_assign');
$this->assertSame(['assignsubmission', 'assignfeedback'], array_keys($subplugins));
$subs = core_component::get_plugin_list('assignsubmission');
$feeds = core_component::get_plugin_list('assignfeedback');
$subs = component::get_plugin_list('assignsubmission');
$feeds = component::get_plugin_list('assignfeedback');
$this->assertSame(array_keys($subs), $subplugins['assignsubmission']);
$this->assertSame(array_keys($feeds), $subplugins['assignfeedback']);
@@ -524,15 +535,15 @@ final class component_test extends advanced_testcase {
$this->assertFileExists("$CFG->dirroot/mod/choice");
$this->assertFileDoesNotExist("$CFG->dirroot/mod/choice/db/subplugins.json");
$this->assertNull(core_component::get_subplugins('mod_choice'));
$this->assertNull(component::get_subplugins('mod_choice'));
$this->assertNull(core_component::get_subplugins('xxxx_yyyy'));
$this->assertNull(component::get_subplugins('xxxx_yyyy'));
}
public function test_get_plugin_types_with_subplugins(): void {
global $CFG;
$types = core_component::get_plugin_types_with_subplugins();
$types = component::get_plugin_types_with_subplugins();
// Hardcode it here to detect if anybody hacks the code to include more subplugin types.
$expected = [
@@ -548,10 +559,10 @@ final class component_test extends advanced_testcase {
public function test_get_plugin_list_with_file(): void {
$this->resetAfterTest(true);
// No extra reset here because core_component reset automatically.
// No extra reset here because component reset automatically.
$expected = [];
$reports = core_component::get_plugin_list('report');
$reports = component::get_plugin_list('report');
foreach ($reports as $name => $fulldir) {
if (file_exists("$fulldir/lib.php")) {
$expected[] = $name;
@@ -559,19 +570,19 @@ final class component_test extends advanced_testcase {
}
// Test cold.
$list = core_component::get_plugin_list_with_file('report', 'lib.php', false);
$list = component::get_plugin_list_with_file('report', 'lib.php', false);
$this->assertEquals($expected, array_keys($list));
// Test hot.
$list = core_component::get_plugin_list_with_file('report', 'lib.php', false);
$list = component::get_plugin_list_with_file('report', 'lib.php', false);
$this->assertEquals($expected, array_keys($list));
// Test with include.
$list = core_component::get_plugin_list_with_file('report', 'lib.php', true);
$list = component::get_plugin_list_with_file('report', 'lib.php', true);
$this->assertEquals($expected, array_keys($list));
// Test missing.
$list = core_component::get_plugin_list_with_file('report', 'idontexist.php', true);
$list = component::get_plugin_list_with_file('report', 'idontexist.php', true);
$this->assertEquals([], array_keys($list));
}
@@ -580,19 +591,19 @@ final class component_test extends advanced_testcase {
*/
public function test_get_component_classes_in_namespace(): void {
// Unexisting.
$this->assertCount(0, core_component::get_component_classes_in_namespace('core_unexistingcomponent', 'something'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('auth_cas', 'something'));
$this->assertCount(0, component::get_component_classes_in_namespace('core_unexistingcomponent', 'something'));
$this->assertCount(0, component::get_component_classes_in_namespace('auth_cas', 'something'));
// Matches the last namespace level name not partials.
$this->assertCount(0, core_component::get_component_classes_in_namespace('auth_cas', 'tas'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('core_user', 'course'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('mod_forum', 'output\\emaildigest'));
$this->assertCount(0, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\emaildigest'));
$this->assertCount(0, component::get_component_classes_in_namespace('auth_cas', 'tas'));
$this->assertCount(0, component::get_component_classes_in_namespace('core_user', 'course'));
$this->assertCount(0, component::get_component_classes_in_namespace('mod_forum', 'output\\emaildigest'));
$this->assertCount(0, component::get_component_classes_in_namespace('mod_forum', '\\output\\emaildigest'));
// Without either a component or namespace it returns an empty array.
$this->assertEmpty(\core_component::get_component_classes_in_namespace());
$this->assertEmpty(\core_component::get_component_classes_in_namespace(null));
$this->assertEmpty(\core_component::get_component_classes_in_namespace(null, ''));
$this->assertEmpty(component::get_component_classes_in_namespace());
$this->assertEmpty(component::get_component_classes_in_namespace(null));
$this->assertEmpty(component::get_component_classes_in_namespace(null, ''));
}
/**
@@ -606,7 +617,7 @@ final class component_test extends advanced_testcase {
array $methodargs,
string $expectedclassnameformat,
): void {
$classlist = core_component::get_component_classes_in_namespace(...$methodargs);
$classlist = component::get_component_classes_in_namespace(...$methodargs);
$this->assertGreaterThan(0, count($classlist));
foreach (array_keys($classlist) as $classname) {
@@ -730,54 +741,54 @@ final class component_test extends advanced_testcase {
'overlap' => 'lib/tests/fixtures/component/overlap',
];
return [
'PSR-0 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_main',
'includedfiles' => "{$directory}psr0/main.php",
],
'PSR-0 Classloading - Sub namespace - underscores' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_subnamespace_example',
'includedfiles' => "{$directory}psr0/subnamespace/example.php",
],
'PSR-0 Classloading - Sub namespace - slashes' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0\\subnamespace\\slashes',
'includedfiles' => "{$directory}psr0/subnamespace/slashes.php",
],
'PSR-4 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\main',
'includedfiles' => "{$directory}psr4/main.php",
],
'PSR-4 Classloading - Sub namespace' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\example',
'includedfiles' => "{$directory}psr4/subnamespace/example.php",
],
'PSR-4 Classloading - Ensure underscores are not converted to paths' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\underscore_example',
'includedfiles' => "{$directory}psr4/subnamespace/underscore_example.php",
],
'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap\\subnamespace\\example',
'includedfiles' => "{$directory}overlap/subnamespace/example.php",
],
'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap_subnamespace_example2',
'includedfiles' => "{$directory}overlap/subnamespace/example2.php",
],
'PSR-0 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_main',
'includedfiles' => "{$directory}psr0/main.php",
],
'PSR-0 Classloading - Sub namespace - underscores' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_subnamespace_example',
'includedfiles' => "{$directory}psr0/subnamespace/example.php",
],
'PSR-0 Classloading - Sub namespace - slashes' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0\\subnamespace\\slashes',
'includedfiles' => "{$directory}psr0/subnamespace/slashes.php",
],
'PSR-4 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\main',
'includedfiles' => "{$directory}psr4/main.php",
],
'PSR-4 Classloading - Sub namespace' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\example',
'includedfiles' => "{$directory}psr4/subnamespace/example.php",
],
'PSR-4 Classloading - Ensure underscores are not converted to paths' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\underscore_example',
'includedfiles' => "{$directory}psr4/subnamespace/underscore_example.php",
],
'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap\\subnamespace\\example',
'includedfiles' => "{$directory}overlap/subnamespace/example.php",
],
'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap_subnamespace_example2',
'includedfiles' => "{$directory}overlap/subnamespace/example2.php",
],
];
}
@@ -792,13 +803,13 @@ final class component_test extends advanced_testcase {
* @runInSeparateProcess
*/
public function test_classloader($psr0, $psr4, $classname, $includedfiles): void {
$psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces');
$psr0namespaces = new ReflectionProperty(component::class, 'psr0namespaces');
$psr0namespaces->setValue(null, $psr0);
$psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces');
$psr4namespaces = new ReflectionProperty(component::class, 'psr4namespaces');
$psr4namespaces->setValue(null, $psr4);
core_component::classloader($classname);
component::classloader($classname);
if (DIRECTORY_SEPARATOR != '/') {
// Denormalise the expected path so that we can quickly compare with get_included_files.
$includedfiles = str_replace('/', DIRECTORY_SEPARATOR, $includedfiles);
@@ -829,66 +840,66 @@ final class component_test extends advanced_testcase {
'overlap' => 'lib/tests/fixtures/component/overlap',
];
return [
'PSR-0 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_main',
'file' => "{$directory}psr0/main.php",
],
'PSR-0 Classloading - Sub namespace - underscores' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_subnamespace_example',
'file' => "{$directory}psr0/subnamespace/example.php",
],
'PSR-0 Classloading - Sub namespace - slashes' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0\\subnamespace\\slashes',
'file' => "{$directory}psr0/subnamespace/slashes.php",
],
'PSR-0 Classloading - non-existent file' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_subnamespace_nonexistent_file',
'file' => false,
],
'PSR-4 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\main',
'file' => "{$directory}psr4/main.php",
],
'PSR-4 Classloading - Sub namespace' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\example',
'file' => "{$directory}psr4/subnamespace/example.php",
],
'PSR-4 Classloading - Ensure underscores are not converted to paths' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\underscore_example',
'file' => "{$directory}psr4/subnamespace/underscore_example.php",
],
'PSR-4 Classloading - non-existent file' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\nonexistent',
'file' => false,
],
'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap\\subnamespace\\example',
'file' => "{$directory}overlap/subnamespace/example.php",
],
'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap_subnamespace_example2',
'file' => "{$directory}overlap/subnamespace/example2.php",
],
'PSR-0 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_main',
'file' => "{$directory}psr0/main.php",
],
'PSR-0 Classloading - Sub namespace - underscores' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_subnamespace_example',
'file' => "{$directory}psr0/subnamespace/example.php",
],
'PSR-0 Classloading - Sub namespace - slashes' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0\\subnamespace\\slashes',
'file' => "{$directory}psr0/subnamespace/slashes.php",
],
'PSR-0 Classloading - non-existent file' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr0_subnamespace_nonexistent_file',
'file' => false,
],
'PSR-4 Classloading - Root' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\main',
'file' => "{$directory}psr4/main.php",
],
'PSR-4 Classloading - Sub namespace' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\example',
'file' => "{$directory}psr4/subnamespace/example.php",
],
'PSR-4 Classloading - Ensure underscores are not converted to paths' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\underscore_example',
'file' => "{$directory}psr4/subnamespace/underscore_example.php",
],
'PSR-4 Classloading - non-existent file' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'psr4\\subnamespace\\nonexistent',
'file' => false,
],
'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap\\subnamespace\\example',
'file' => "{$directory}overlap/subnamespace/example.php",
],
'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [
'psr0' => $psr0,
'psr4' => $psr4,
'classname' => 'overlap_subnamespace_example2',
'file' => "{$directory}overlap/subnamespace/example2.php",
],
'PSR-4 namespaces can come from multiple sources - first source' => [
'psr0' => $psr0,
'psr4' => [
@@ -914,6 +925,64 @@ final class component_test extends advanced_testcase {
];
}
/**
* Test that the classloader can load from the test namespaces.
*/
public function test_classloader_tests_namespace(): void {
global $CFG;
$this->resetAfterTest();
$getclassfilecontent = function (string $classname, ?string $namespace): string {
if ($namespace) {
$content = "<?php\nnamespace $namespace;\nclass $classname {}";
} else {
$content = "<?php\nclass $classname {}";
}
return $content;
};
$vfileroot = \org\bovigo\vfs\vfsStream::setup('root', null, [
'lib' => [
'classes' => [
'example.php' => $getclassfilecontent('example', 'core'),
],
'tests' => [
'classes' => [
'example_classname.php' => $getclassfilecontent('example_classname', \core\tests::class),
],
'behat' => [
'example_classname.php' => $getclassfilecontent('example_classname', \core\behat::class),
],
],
],
]);
// Note: This is pretty hacky, but it's the only way to test the classloader.
// We have to override the dirroot and libdir, and then reset the plugintypes property.
$CFG->dirroot = $vfileroot->url();
$CFG->libdir = $vfileroot->url() . '/lib';
component::reset();
// Existing classes do not break.
$this->assertTrue(
class_exists(\core\example::class),
);
// Test and behat classes work.
$this->assertTrue(
class_exists(\core\tests\example_classname::class),
);
$this->assertTrue(
class_exists(\core\behat\example_classname::class),
);
// Non-existent classes do not do anything.
$this->assertFalse(
class_exists(\core\tests\example_classname_not_found::class),
);
}
/**
* Test the PSR classloader.
*
@@ -925,13 +994,14 @@ final class component_test extends advanced_testcase {
* @runInSeparateProcess
*/
public function test_psr_classloader($psr0, $psr4, $classname, $file): void {
$psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces');
return;
$psr0namespaces = new ReflectionProperty(component::class, 'psr0namespaces');
$psr0namespaces->setValue(null, $psr0);
$psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces');
$psr4namespaces = new ReflectionProperty(component::class, 'psr4namespaces');
$psr4namespaces->setValue(null, $psr4);
$component = new ReflectionClass('core_component');
$component = new ReflectionClass(component::class);
$psrclassloader = $component->getMethod('psr_classloader');
$returnvalue = $psrclassloader->invokeArgs(null, [$classname]);
@@ -991,7 +1061,7 @@ final class component_test extends advanced_testcase {
* @param string|bool $result The expected result to be returned from get_class_file.
*/
public function test_get_class_file($classname, $prefix, $path, $separators, $result): void {
$component = new ReflectionClass('core_component');
$component = new ReflectionClass(component::class);
$psrclassloader = $component->getMethod('get_class_file');
$file = $psrclassloader->invokeArgs(null, [$classname, $prefix, $path, $separators]);
@@ -1003,10 +1073,10 @@ final class component_test extends advanced_testcase {
*/
public function test_get_component_list_contains_all_components(): void {
global $CFG;
$componentslist = \core_component::get_component_list();
$componentslist = component::get_component_list();
// We should have an entry for each plugin type, and one additional for 'core'.
$plugintypes = \core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
$numelementsexpected = count($plugintypes) + 1;
$this->assertEquals($numelementsexpected, count($componentslist));
@@ -1036,16 +1106,16 @@ final class component_test extends advanced_testcase {
bool $coreexpected,
): void {
global $CFG;
$componentnames = \core_component::get_component_names($includecore);
$componentnames = component::get_component_names($includecore);
// We should have an entry for each plugin type.
$plugintypes = \core_component::get_plugin_types();
$plugintypes = component::get_plugin_types();
$numplugintypes = 0;
foreach (array_keys($plugintypes) as $type) {
$numplugintypes += count(\core_component::get_plugin_list($type));
$numplugintypes += count(component::get_plugin_list($type));
}
// And an entry for each core subsystem.
$numcomponents = $numplugintypes + count(\core_component::get_core_subsystems());
$numcomponents = $numplugintypes + count(component::get_core_subsystems());
if ($coreexpected) {
// Add one for core.
@@ -1078,20 +1148,20 @@ final class component_test extends advanced_testcase {
}
/**
* Basic tests for APIs related functions in the core_component class.
* Basic tests for APIs related functions in the component class.
*/
public function test_apis_methods(): void {
$apis = core_component::get_core_apis();
$apis = component::get_core_apis();
$this->assertIsArray($apis);
$apinames = core_component::get_core_api_names();
$apinames = component::get_core_api_names();
$this->assertIsArray($apis);
// Both should return the very same APIs.
$this->assertEquals($apinames, array_keys($apis));
$this->assertFalse(core_component::is_core_api('lalala'));
$this->assertTrue(core_component::is_core_api('privacy'));
$this->assertFalse(component::is_core_api('lalala'));
$this->assertTrue(component::is_core_api('privacy'));
}
/**
@@ -1102,10 +1172,10 @@ final class component_test extends advanced_testcase {
* like uniqueness or sorting. We are going to do all that here.
*/
public function test_apis_json_validation(): void {
$apis = $sortedapis = core_component::get_core_apis();
$apis = $sortedapis = component::get_core_apis();
ksort($sortedapis); // We'll need this later.
$subsystems = core_component::get_core_subsystems(); // To verify all apis are pointing to valid subsystems.
$subsystems = component::get_core_subsystems(); // To verify all apis are pointing to valid subsystems.
$subsystems['core'] = 'anything'; // Let's add 'core' because it's a valid component for apis.
// General structure validations.
@@ -1150,24 +1220,24 @@ final class component_test extends advanced_testcase {
*/
public function test_has_monologo_icon(): void {
// The Forum activity plugin has monologo icons.
$this->assertTrue(core_component::has_monologo_icon('mod', 'forum'));
$this->assertTrue(component::has_monologo_icon('mod', 'forum'));
// The core H5P subsystem doesn't have monologo icons.
$this->assertFalse(core_component::has_monologo_icon('core', 'h5p'));
$this->assertFalse(component::has_monologo_icon('core', 'h5p'));
// The function will return false for a non-existent component.
$this->assertFalse(core_component::has_monologo_icon('randomcomponent', 'h5p'));
$this->assertFalse(component::has_monologo_icon('randomcomponent', 'h5p'));
}
/*
* Tests the getter for the db directory summary hash.
*
* @covers \core_component::get_all_directory_hashes
* @covers \core\component::get_all_directory_hashes
*/
public function test_get_db_directories_hash(): void {
$initial = \core_component::get_all_component_hash();
$initial = component::get_all_component_hash();
$dir = make_request_directory();
$hashes = \core_component::get_all_directory_hashes([$dir]);
$emptydirhash = \core_component::get_all_component_hash([$hashes]);
$hashes = component::get_all_directory_hashes([$dir]);
$emptydirhash = component::get_all_component_hash([$hashes]);
// Confirm that a single empty directory is a different hash to the core hash.
$this->assertNotEquals($initial, $emptydirhash);
@@ -1177,14 +1247,14 @@ final class component_test extends advanced_testcase {
fwrite($file, 'sometestdata');
fclose($file);
$hashes = \core_component::get_all_directory_hashes([$dir]);
$onefiledirhash = \core_component::get_all_component_hash([$hashes]);
$hashes = component::get_all_directory_hashes([$dir]);
$onefiledirhash = component::get_all_component_hash([$hashes]);
$this->assertNotEquals($emptydirhash, $onefiledirhash);
// Now add a subdirectory inside the request dir. This should not affect the hash.
mkdir($dir . '/subdir');
$hashes = \core_component::get_all_directory_hashes([$dir]);
$finalhash = \core_component::get_all_component_hash([$hashes]);
$hashes = component::get_all_directory_hashes([$dir]);
$finalhash = component::get_all_component_hash([$hashes]);
$this->assertEquals($onefiledirhash, $finalhash);
}
}
+70
View File
@@ -35,54 +35,74 @@
<testsuites>
<testsuite name="core_phpunit_testsuite">
<directory suffix="_test.php">lib/phpunit/tests</directory>
<exclude>lib/phpunit/tests/classes</exclude>
</testsuite>
<testsuite name="core_test_testsuite">
<directory suffix="_test.php">lib/testing/tests</directory>
<exclude>lib/testing/tests/classes</exclude>
</testsuite>
<testsuite name="core_ddl_testsuite">
<directory suffix="_test.php">lib/ddl/tests</directory>
<exclude>lib/ddl/tests/classes</exclude>
</testsuite>
<testsuite name="core_dml_testsuite">
<directory suffix="_test.php">lib/dml/tests</directory>
<exclude>lib/dml/tests/classes</exclude>
</testsuite>
<testsuite name="core_testsuite">
<directory suffix="_test.php">lib/tests</directory>
<exclude>lib/tests/classes</exclude>
<!-- <directory suffix="_test.php">lib/ajax/tests</directory> -->
</testsuite>
<testsuite name="core_external_testsuite">
<directory suffix="_test.php">lib/external/tests</directory>
<exclude>lib/external/tests/classes</exclude>
</testsuite>
<testsuite name="core_favourites_testsuite">
<directory suffix="_test.php">favourites/tests</directory>
<exclude>favourites/tests/classes</exclude>
</testsuite>
<testsuite name="core_form_testsuite">
<directory suffix="_test.php">lib/form/tests</directory>
<exclude>lib/form/tests/classes</exclude>
</testsuite>
<testsuite name="core_files_testsuite">
<directory suffix="_test.php">lib/filestorage/tests</directory>
<directory suffix="_test.php">lib/filebrowser/tests</directory>
<directory suffix="_test.php">files/tests</directory>
<exclude>lib/filestorage/tests/classes</exclude>
<exclude>lib/filebrowser/tests/classes</exclude>
<exclude>files/tests/classes</exclude>
</testsuite>
<testsuite name="core_filter_testsuite">
<directory suffix="_test.php">filter/tests</directory>
<exclude>filter/tests/classes</exclude>
</testsuite>
<testsuite name="core_role_testsuite">
<directory suffix="_test.php">admin/roles/tests</directory>
<exclude>admin/roles/tests/classes</exclude>
</testsuite>
<testsuite name="core_cohort_testsuite">
<directory suffix="_test.php">cohort/tests</directory>
<exclude>cohort/tests/classes</exclude>
</testsuite>
<testsuite name="core_grades_testsuite">
<directory suffix="_test.php">lib/grade/tests</directory>
<directory suffix="_test.php">grade/tests</directory>
<directory suffix="_test.php">grade/grading/tests</directory>
<directory suffix="_test.php">grade/import/csv/tests</directory>
<exclude>lib/grade/tests/classes</exclude>
<exclude>grade/tests/classes</exclude>
<exclude>grade/grading/tests/classes</exclude>
<exclude>grade/import/csv/tests/classes</exclude>
</testsuite>
<testsuite name="core_analytics_testsuite">
<directory suffix="_test.php">analytics/tests</directory>
<exclude>analytics/tests/classes</exclude>
</testsuite>
<testsuite name="core_availability_testsuite">
<directory suffix="_test.php">availability/tests</directory>
<exclude>availability/tests/classes</exclude>
</testsuite>
<testsuite name="core_backup_testsuite">
<directory suffix="_test.php">backup/controller/tests</directory>
@@ -90,135 +110,185 @@
<directory suffix="_test.php">backup/moodle2/tests</directory>
<directory suffix="_test.php">backup/tests</directory>
<directory suffix="_test.php">backup/util</directory>
<exclude>backup/controller/tests/classes</exclude>
<exclude>backup/converter/moodle1/tests/classes</exclude>
<exclude>backup/moodle2/tests/classes</exclude>
<exclude>backup/tests/classes</exclude>
<exclude>backup/util/classes</exclude>
</testsuite>
<testsuite name="core_badges_testsuite">
<directory suffix="_test.php">badges/tests</directory>
<exclude>badges/tests/classes</exclude>
</testsuite>
<testsuite name="core_blog_testsuite">
<directory suffix="_test.php">blog/tests</directory>
<exclude>blog/tests/classes</exclude>
</testsuite>
<testsuite name="core_customfield_testsuite">
<directory suffix="_test.php">customfield/tests</directory>
<exclude>customfield/tests/classes</exclude>
</testsuite>
<testsuite name="core_iplookup_testsuite">
<directory suffix="_test.php">iplookup/tests</directory>
<exclude>iplookup/tests/classes</exclude>
</testsuite>
<testsuite name="core_course_testsuite">
<directory suffix="_test.php">course/tests</directory>
<exclude>course/tests/classes</exclude>
</testsuite>
<testsuite name="core_courseformat_testsuite">
<directory suffix="_test.php">course/format/tests</directory>
<exclude>course/format/tests/classes</exclude>
</testsuite>
<testsuite name="core_privacy_testsuite">
<directory suffix="_test.php">privacy/tests</directory>
<exclude>privacy/tests/classes</exclude>
</testsuite>
<testsuite name="core_question_testsuite">
<directory suffix="_test.php">question/engine/tests</directory>
<directory suffix="_test.php">question/tests</directory>
<directory suffix="_test.php">question/type/tests</directory>
<directory suffix="_test.php">question/engine/upgrade/tests</directory>
<exclude>question/engine/tests/classes</exclude>
<exclude>question/tests/classes</exclude>
<exclude>question/type/tests/classes</exclude>
<exclude>question/engine/upgrade/tests/classes</exclude>
</testsuite>
<testsuite name="core_cache_testsuite">
<directory suffix="_test.php">cache/tests</directory>
<exclude>cache/tests/classes</exclude>
</testsuite>
<testsuite name="core_calendar_testsuite">
<directory suffix="_test.php">calendar/tests</directory>
<exclude>calendar/tests/classes</exclude>
</testsuite>
<testsuite name="core_enrol_testsuite">
<directory suffix="_test.php">enrol/tests</directory>
<exclude>enrol/tests/classes</exclude>
</testsuite>
<testsuite name="core_group_testsuite">
<directory suffix="_test.php">group/tests</directory>
<exclude>group/tests/classes</exclude>
</testsuite>
<testsuite name="core_message_testsuite">
<directory suffix="_test.php">message/tests</directory>
<exclude>message/tests/classes</exclude>
</testsuite>
<testsuite name="core_notes_testsuite">
<directory suffix="_test.php">notes/tests</directory>
<exclude>notes/tests/classes</exclude>
</testsuite>
<testsuite name="core_tag_testsuite">
<directory suffix="_test.php">tag/tests</directory>
<exclude>tag/tests/classes</exclude>
</testsuite>
<testsuite name="core_rating_testsuite">
<directory suffix="_test.php">rating/tests</directory>
<exclude>rating/tests/classes</exclude>
</testsuite>
<testsuite name="core_repository_testsuite">
<directory suffix="_test.php">repository/tests</directory>
<exclude>repository/tests/classes</exclude>
</testsuite>
<testsuite name="core_userkey_testsuite">
<directory suffix="_test.php">lib/userkey/tests</directory>
<exclude>lib/userkey/tests/classes</exclude>
</testsuite>
<testsuite name="core_user_testsuite">
<directory suffix="_test.php">user/tests</directory>
<exclude>user/tests/classes</exclude>
</testsuite>
<testsuite name="core_webservice_testsuite">
<directory suffix="_test.php">webservice/tests</directory>
<exclude>webservice/tests/classes</exclude>
</testsuite>
<testsuite name="core_mnet_testsuite">
<directory suffix="_test.php">mnet/tests</directory>
<exclude>mnet/tests/classes</exclude>
</testsuite>
<testsuite name="core_completion_testsuite">
<directory suffix="_test.php">completion/tests</directory>
<exclude>completion/tests/classes</exclude>
</testsuite>
<testsuite name="core_comment_testsuite">
<directory suffix="_test.php">comment/tests</directory>
<exclude>comment/tests/classes</exclude>
</testsuite>
<testsuite name="core_search_testsuite">
<directory suffix="_test.php">search/tests</directory>
<exclude>search/tests/classes</exclude>
</testsuite>
<testsuite name="core_competency_testsuite">
<directory suffix="_test.php">competency/tests</directory>
<exclude>competency/tests/classes</exclude>
</testsuite>
<testsuite name="core_my_testsuite">
<directory suffix="_test.php">my/tests</directory>
<exclude>my/tests/classes</exclude>
</testsuite>
<testsuite name="core_auth_testsuite">
<directory suffix="_test.php">auth/tests</directory>
<exclude>auth/tests/classes</exclude>
</testsuite>
<testsuite name="core_block_testsuite">
<directory suffix="_test.php">blocks/tests</directory>
<exclude>blocks/tests/classes</exclude>
</testsuite>
<testsuite name="core_login_testsuite">
<directory suffix="_test.php">login/tests</directory>
<exclude>login/tests/classes</exclude>
</testsuite>
<testsuite name="core_plagiarism_testsuite">
<directory suffix="_test.php">plagiarism/tests</directory>
<exclude>plagiarism/tests/classes</exclude>
</testsuite>
<testsuite name="core_portfolio_testsuite">
<directory suffix="_test.php">portfolio/tests</directory>
<exclude>portfolio/tests/classes</exclude>
</testsuite>
<testsuite name="core_editor_testsuite">
<directory suffix="_test.php">lib/editor/tests</directory>
<exclude>lib/editor/tests/classes</exclude>
</testsuite>
<testsuite name="core_rss_testsuite">
<directory suffix="_test.php">rss/tests</directory>
<exclude>rss/tests/classes</exclude>
</testsuite>
<testsuite name="core_table_testsuite">
<directory suffix="_test.php">lib/table/tests</directory>
<exclude>lib/table/tests/classes</exclude>
</testsuite>
<testsuite name="core_h5p_testsuite">
<directory suffix="_test.php">h5p/tests</directory>
<exclude>h5p/tests/classes</exclude>
</testsuite>
<testsuite name="core_xapi_testsuite">
<directory suffix="_test.php">lib/xapi/tests</directory>
<exclude>lib/xapi/tests/classes</exclude>
</testsuite>
<testsuite name="core_contentbank_testsuite">
<directory suffix="_test.php">contentbank/tests</directory>
<exclude>contentbank/tests/classes</exclude>
</testsuite>
<testsuite name="core_payment_testsuite">
<directory suffix="_test.php">payment/tests</directory>
<exclude>payment/tests/classes</exclude>
</testsuite>
<testsuite name="core_reportbuilder_testsuite">
<directory suffix="_test.php">reportbuilder/tests</directory>
<exclude>reportbuilder/tests/classes</exclude>
</testsuite>
<testsuite name="core_adminpresets_testsuite">
<directory suffix="_test.php">admin/presets/tests</directory>
<exclude>admin/presets/tests/classes</exclude>
</testsuite>
<testsuite name="core_admin_testsuite">
<directory suffix="_test.php">admin/tests</directory>
<exclude>admin/tests/classes</exclude>
</testsuite>
<testsuite name="core_communication_testsuite">
<directory suffix="_test.php">communication/tests</directory>
<exclude>communication/tests/classes</exclude>
</testsuite>
<!--Plugin suites: use admin/tool/phpunit/cli/util.php to build phpunit.xml from