PageRenderTime 39ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/phpunit/classes/autoloader.php

https://gitlab.com/unofficial-mirrors/moodle
PHP | 203 lines | 147 code | 11 blank | 45 comment | 9 complexity | 46d49fbd73c666e2f95d5774ecc80fed MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * PHPUnit autoloader for Moodle.
  18. *
  19. * @package core
  20. * @category phpunit
  21. * @copyright 2013 Petr Skoda {@link http://skodak.org}
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. /**
  25. * Class phpunit_autoloader.
  26. *
  27. * Please notice that phpunit testcases obey frankenstyle naming rules,
  28. * that is full component prefix + _testcase postfix. The files are expected
  29. * in tests directory inside each component. There are some extra tests
  30. * directories which require both classname and file path.
  31. *
  32. * Examples:
  33. *
  34. * vendor/bin/phpunit core_component_testcase
  35. * vendor/bin/phpunit lib/tests/component_test.php
  36. * vendor/bin/phpunit core_component_testcase lib/tests/component_test.php
  37. *
  38. * @package core
  39. * @category phpunit
  40. * @copyright 2013 Petr Skoda {@link http://skodak.org}
  41. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42. */
  43. class phpunit_autoloader implements \PHPUnit\Runner\TestSuiteLoader {
  44. public function load($suiteClassName, $suiteClassFile = '') {
  45. global $CFG;
  46. // Let's guess what user entered on the commandline...
  47. if ($suiteClassFile) {
  48. // This means they either entered the class+path or path only.
  49. if (strpos($suiteClassName, '/') !== false) {
  50. // Class names can not contain slashes,
  51. // user entered only path without testcase class name.
  52. return $this->guess_class_from_path($suiteClassFile);
  53. }
  54. if (strpos($suiteClassName, '\\') !== false and strpos($suiteClassFile, $suiteClassName.'.php') !== false) {
  55. // This must be backslashed windows path.
  56. return $this->guess_class_from_path($suiteClassFile);
  57. }
  58. }
  59. if (class_exists($suiteClassName, false)) {
  60. $class = new ReflectionClass($suiteClassName);
  61. return $class;
  62. }
  63. if ($suiteClassFile) {
  64. PHPUnit\Util\Fileloader::checkAndLoad($suiteClassFile);
  65. if (class_exists($suiteClassName, false)) {
  66. $class = new ReflectionClass($suiteClassName);
  67. return $class;
  68. }
  69. throw new PHPUnit\Framework\Exception(
  70. sprintf("Class '%s' could not be found in '%s'.", $suiteClassName, $suiteClassFile)
  71. );
  72. }
  73. /*
  74. * Try standard testcase naming rules based on frankenstyle component:
  75. * 1/ test classes should use standard frankenstyle class names plus suffix "_testcase"
  76. * 2/ test classes should be stored in files with suffix "_test"
  77. */
  78. $parts = explode('_', $suiteClassName);
  79. $suffix = end($parts);
  80. $component = '';
  81. if ($suffix === 'testcase') {
  82. unset($parts[key($parts)]);
  83. while($parts) {
  84. if (!$component) {
  85. $component = array_shift($parts);
  86. } else {
  87. $component = $component . '_' . array_shift($parts);
  88. }
  89. // Try standard plugin and core subsystem locations.
  90. if ($fulldir = core_component::get_component_directory($component)) {
  91. $testfile = implode('_', $parts);
  92. $fullpath = "{$fulldir}/tests/{$testfile}_test.php";
  93. if (is_readable($fullpath)) {
  94. include_once($fullpath);
  95. if (class_exists($suiteClassName, false)) {
  96. $class = new ReflectionClass($suiteClassName);
  97. return $class;
  98. }
  99. }
  100. }
  101. }
  102. // The last option is testsuite directories in main phpunit.xml file.
  103. $xmlfile = "$CFG->dirroot/phpunit.xml";
  104. if (is_readable($xmlfile) and $xml = file_get_contents($xmlfile)) {
  105. $dom = new DOMDocument();
  106. $dom->loadXML($xml);
  107. $nodes = $dom->getElementsByTagName('testsuite');
  108. foreach ($nodes as $node) {
  109. /** @var DOMNode $node */
  110. $suitename = trim($node->attributes->getNamedItem('name')->nodeValue);
  111. if (strpos($suitename, 'core') !== 0 or strpos($suitename, ' ') !== false) {
  112. continue;
  113. }
  114. // This is a nasty hack: testsuit names are sometimes used as prefix for testcases
  115. // in non-standard core subsystem locations.
  116. if (strpos($suiteClassName, $suitename) !== 0) {
  117. continue;
  118. }
  119. foreach ($node->childNodes as $dirnode) {
  120. /** @var DOMNode $dirnode */
  121. $dir = trim($dirnode->textContent);
  122. if (!$dir) {
  123. continue;
  124. }
  125. $dir = $CFG->dirroot.'/'.$dir;
  126. $parts = explode('_', $suitename);
  127. $prefix = '';
  128. while ($parts) {
  129. if ($prefix) {
  130. $prefix = $prefix.'_'.array_shift($parts);
  131. } else {
  132. $prefix = array_shift($parts);
  133. }
  134. $filename = substr($suiteClassName, strlen($prefix)+1);
  135. $filename = preg_replace('/testcase$/', 'test', $filename);
  136. if (is_readable("$dir/$filename.php")) {
  137. include_once("$dir/$filename.php");
  138. if (class_exists($suiteClassName, false)) {
  139. $class = new ReflectionClass($suiteClassName);
  140. return $class;
  141. }
  142. }
  143. }
  144. }
  145. }
  146. }
  147. }
  148. throw new PHPUnit\Framework\Exception(
  149. sprintf("Class '%s' could not be found in '%s'.", $suiteClassName, $suiteClassFile)
  150. );
  151. }
  152. protected function guess_class_from_path($file) {
  153. // Somebody is using just the file name, we need to look inside the file and guess the testcase
  154. // class name. Let's throw fatal error if there are more testcases in one file.
  155. $classes = get_declared_classes();
  156. PHPUnit\Util\Fileloader::checkAndLoad($file);
  157. $includePathFilename = stream_resolve_include_path($file);
  158. $loadedClasses = array_diff(get_declared_classes(), $classes);
  159. $candidates = array();
  160. foreach ($loadedClasses as $loadedClass) {
  161. $class = new ReflectionClass($loadedClass);
  162. if ($class->isSubclassOf('PHPUnit\Framework\TestCase') and !$class->isAbstract()) {
  163. if (realpath($includePathFilename) === realpath($class->getFileName())) {
  164. $candidates[] = $loadedClass;
  165. }
  166. }
  167. }
  168. if (count($candidates) == 0) {
  169. throw new PHPUnit\Framework\Exception(
  170. sprintf("File '%s' does not contain any test cases.", $file)
  171. );
  172. }
  173. if (count($candidates) > 1) {
  174. throw new PHPUnit\Framework\Exception(
  175. sprintf("File '%s' contains multiple test cases: ".implode(', ', $candidates), $file)
  176. );
  177. }
  178. $classname = reset($candidates);
  179. return new ReflectionClass($classname);
  180. }
  181. public function reload(ReflectionClass $aClass) {
  182. return $aClass;
  183. }
  184. }