diff --git a/lib/phpunit/bootstrap.php b/lib/phpunit/bootstrap.php index 98d06dc7627bf..ed38405263f4c 100644 --- a/lib/phpunit/bootstrap.php +++ b/lib/phpunit/bootstrap.php @@ -33,6 +33,7 @@ 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!'); diff --git a/lib/phpunit/classes/autoloader.php b/lib/phpunit/classes/autoloader.php new file mode 100644 index 0000000000000..55654ae466bc7 --- /dev/null +++ b/lib/phpunit/classes/autoloader.php @@ -0,0 +1,155 @@ +. + +/** + * 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($suiteClassName, $suiteClassFile = '') { + + // 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); + } + 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; + } + } + } + } + } + + 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. + + PHPUnit_Util_Class::collectStart(); + PHPUnit_Util_Fileloader::checkAndLoad($file); + $includePathFilename = stream_resolve_include_path($file); + $loadedClasses = PHPUnit_Util_Class::collectEnd(); + + $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) + ); + } + + return $class; + } + + public function reload(ReflectionClass $aClass) { + return $aClass; + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 123db5a35ba59..e65fc38378c33 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,6 +15,7 @@ stopOnSkipped="false" strict="false" printerClass="Hint_ResultPrinter" + testSuiteLoaderClass="phpunit_autoloader" >