Commit be30af0e authored by Eloy Lafuente's avatar Eloy Lafuente
Browse files

MDL-71036 phpunit: Remove custom autoloader

Custom autoloaders are deprecated with PHPUnit 9 and will be removed
with PHPUnit 10.

Since PHPUnit 8.5 custom autoloaders don't do much because that
version removed the ability to launch unit tests by class name
and that's exactly the reason we had a custom autoloader (to map
class names to files within our tests). See MDL-67673 about
when direct use of classes was deprecated (8.5), now removed (9.5).

So, as far as it's unused, removing it now, test still can be
selectively using any of:

- a relative path to file (although there are some restrictions comming
  with PHPUnit 9, see https://github.com/sebastianbergmann/phpunit/issues/4105
- using --filter, to point to any classname[::method]
- using --testsuite to run a complete suite
- using --config to point to custom components.

Also, commented out the lib/ajax/tests directory because it doesn't
exist / is empty and PHPUnit 9 emits error when a configured test
directory does not exist. See
https://github.com/sebastianbergmann/phpunit/issues/4493.

Alternative was to completely remove the configuration line, but
decided to keep it around in case some day we want to add some
test there.
parent 8a3663b1
......@@ -50,7 +50,6 @@ if (!defined('IGNORE_COMPONENT_CACHE')) {
require_once(__DIR__.'/bootstraplib.php');
require_once(__DIR__.'/../testing/lib.php');
require_once(__DIR__.'/classes/autoloader.php');
if (isset($_SERVER['REMOTE_ADDR'])) {
phpunit_bootstrap_error(1, 'Unit tests can be executed only from command line!');
......
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* PHPUnit autoloader for Moodle.
*
* @package core
* @category phpunit
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Class phpunit_autoloader.
*
* Please notice that phpunit testcases obey frankenstyle naming rules,
* that is full component prefix + _testcase postfix. The files are expected
* in tests directory inside each component. There are some extra tests
* directories which require both classname and file path.
*
* Examples:
*
* vendor/bin/phpunit core_component_testcase
* vendor/bin/phpunit lib/tests/component_test.php
* vendor/bin/phpunit core_component_testcase lib/tests/component_test.php
*
* @package core
* @category phpunit
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class phpunit_autoloader implements \PHPUnit\Runner\TestSuiteLoader {
public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass {
global $CFG;
// Let's guess what user entered on the commandline...
if ($suiteClassFile) {
// This means they either entered the class+path or path only.
if (strpos($suiteClassName, '/') !== false) {
// Class names can not contain slashes,
// user entered only path without testcase class name.
return $this->guess_class_from_path($suiteClassFile);
}
if (strpos($suiteClassName, '\\') !== false and strpos($suiteClassFile, $suiteClassName.'.php') !== false) {
// This must be backslashed windows path.
return $this->guess_class_from_path($suiteClassFile);
}
}
if (class_exists($suiteClassName, false)) {
$class = new ReflectionClass($suiteClassName);
return $class;
}
if ($suiteClassFile) {
PHPUnit\Util\Fileloader::checkAndLoad($suiteClassFile);
if (class_exists($suiteClassName, false)) {
$class = new ReflectionClass($suiteClassName);
return $class;
}
throw new PHPUnit\Framework\Exception(
sprintf("Class '%s' could not be found in '%s'.", $suiteClassName, $suiteClassFile)
);
}
/*
* Try standard testcase naming rules based on frankenstyle component:
* 1/ test classes should use standard frankenstyle class names plus suffix "_testcase"
* 2/ test classes should be stored in files with suffix "_test"
*/
$parts = explode('_', $suiteClassName);
$suffix = end($parts);
$component = '';
if ($suffix === 'testcase') {
unset($parts[key($parts)]);
while($parts) {
if (!$component) {
$component = array_shift($parts);
} else {
$component = $component . '_' . array_shift($parts);
}
// Try standard plugin and core subsystem locations.
if ($fulldir = core_component::get_component_directory($component)) {
$testfile = implode('_', $parts);
$fullpath = "{$fulldir}/tests/{$testfile}_test.php";
if (is_readable($fullpath)) {
include_once($fullpath);
if (class_exists($suiteClassName, false)) {
$class = new ReflectionClass($suiteClassName);
return $class;
}
}
}
}
// The last option is testsuite directories in main phpunit.xml file.
$xmlfile = "$CFG->dirroot/phpunit.xml";
if (is_readable($xmlfile) and $xml = file_get_contents($xmlfile)) {
$dom = new DOMDocument();
$dom->loadXML($xml);
$nodes = $dom->getElementsByTagName('testsuite');
foreach ($nodes as $node) {
/** @var DOMNode $node */
$suitename = trim($node->attributes->getNamedItem('name')->nodeValue);
if (strpos($suitename, 'core') !== 0 or strpos($suitename, ' ') !== false) {
continue;
}
// This is a nasty hack: testsuit names are sometimes used as prefix for testcases
// in non-standard core subsystem locations.
if (strpos($suiteClassName, $suitename) !== 0) {
continue;
}
foreach ($node->childNodes as $dirnode) {
/** @var DOMNode $dirnode */
$dir = trim($dirnode->textContent);
if (!$dir) {
continue;
}
$dir = $CFG->dirroot.'/'.$dir;
$parts = explode('_', $suitename);
$prefix = '';
while ($parts) {
if ($prefix) {
$prefix = $prefix.'_'.array_shift($parts);
} else {
$prefix = array_shift($parts);
}
$filename = substr($suiteClassName, strlen($prefix)+1);
$filename = preg_replace('/testcase$/', 'test', $filename);
if (is_readable("$dir/$filename.php")) {
include_once("$dir/$filename.php");
if (class_exists($suiteClassName, false)) {
$class = new ReflectionClass($suiteClassName);
return $class;
}
}
}
}
}
}
}
throw new PHPUnit\Framework\Exception(
sprintf("Class '%s' could not be found in '%s'.", $suiteClassName, $suiteClassFile)
);
}
protected function guess_class_from_path($file) {
// Somebody is using just the file name, we need to look inside the file and guess the testcase
// class name. Let's throw fatal error if there are more testcases in one file.
$classes = get_declared_classes();
PHPUnit\Util\Fileloader::checkAndLoad($file);
$includePathFilename = stream_resolve_include_path($file);
$loadedClasses = array_diff(get_declared_classes(), $classes);
$candidates = array();
foreach ($loadedClasses as $loadedClass) {
$class = new ReflectionClass($loadedClass);
if ($class->isSubclassOf('PHPUnit\Framework\TestCase') and !$class->isAbstract()) {
if (realpath($includePathFilename) === realpath($class->getFileName())) {
$candidates[] = $loadedClass;
}
}
}
if (count($candidates) == 0) {
throw new PHPUnit\Framework\Exception(
sprintf("File '%s' does not contain any test cases.", $file)
);
}
if (count($candidates) > 1) {
throw new PHPUnit\Framework\Exception(
sprintf("File '%s' contains multiple test cases: ".implode(', ', $candidates), $file)
);
}
$classname = reset($candidates);
return new ReflectionClass($classname);
}
public function reload(ReflectionClass $aClass): ReflectionClass {
return $aClass;
}
}
......@@ -16,7 +16,6 @@
stopOnSkipped="false"
beStrictAboutTestsThatDoNotTestAnything="false"
beStrictAboutOutputDuringTests="true"
testSuiteLoaderClass="phpunit_autoloader"
>
<php>
......@@ -45,7 +44,7 @@
</testsuite>
<testsuite name="core_testsuite">
<directory suffix="_test.php">lib/tests</directory>
<directory suffix="_test.php">lib/ajax/tests</directory>
<!-- <directory suffix="_test.php">lib/ajax/tests</directory> -->
</testsuite>
<testsuite name="core_favourites_testsuite">
<directory suffix="_test.php">favourites/tests</directory>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment