PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/behat/classes/behat_config_manager.php

https://bitbucket.org/kudutest1/moodlegit
PHP | 279 lines | 193 code | 21 blank | 65 comment | 15 complexity | deaf8c182a193e0f65e564fdd075641f 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. * Utils to set Behat config
  18. *
  19. * @package core
  20. * @category test
  21. * @copyright 2012 David MonllaĆ³
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. require_once(__DIR__ . '/../lib.php');
  26. require_once(__DIR__ . '/behat_command.php');
  27. require_once(__DIR__ . '/../../testing/classes/tests_finder.php');
  28. /**
  29. * Behat configuration manager
  30. *
  31. * Creates/updates Behat config files getting tests
  32. * and steps from Moodle codebase
  33. *
  34. * @package core
  35. * @category test
  36. * @copyright 2012 David MonllaĆ³
  37. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38. */
  39. class behat_config_manager {
  40. /**
  41. * Updates a config file
  42. *
  43. * The tests runner and the steps definitions list uses different
  44. * config files to avoid problems with concurrent executions.
  45. *
  46. * The steps definitions list can be filtered by component so it's
  47. * behat.yml is different from the $CFG->dirroot one.
  48. *
  49. * @param string $component Restricts the obtained steps definitions to the specified component
  50. * @param string $testsrunner If the config file will be used to run tests
  51. * @return void
  52. */
  53. public static function update_config_file($component = '', $testsrunner = true) {
  54. global $CFG;
  55. // Behat must have a separate behat.yml to have access to the whole set of features and steps definitions.
  56. if ($testsrunner === true) {
  57. $configfilepath = behat_command::get_behat_dir() . '/behat.yml';
  58. } else {
  59. // Alternative for steps definitions filtering, one for each user.
  60. $configfilepath = self::get_steps_list_config_filepath();
  61. }
  62. // Gets all the components with features.
  63. $features = array();
  64. $components = tests_finder::get_components_with_tests('features');
  65. if ($components) {
  66. foreach ($components as $componentname => $path) {
  67. $path = self::clean_path($path) . self::get_behat_tests_path();
  68. if (empty($featurespaths[$path]) && file_exists($path)) {
  69. // Standarizes separator (some dirs. comes with OS-dependant separator).
  70. $uniquekey = str_replace('\\', '/', $path);
  71. $featurespaths[$uniquekey] = $path;
  72. }
  73. }
  74. $features = array_values($featurespaths);
  75. }
  76. // Gets all the components with steps definitions.
  77. $stepsdefinitions = array();
  78. $steps = self::get_components_steps_definitions();
  79. if ($steps) {
  80. foreach ($steps as $key => $filepath) {
  81. if ($component == '' || $component === $key) {
  82. $stepsdefinitions[$key] = $filepath;
  83. }
  84. }
  85. }
  86. // Behat config file specifing the main context class,
  87. // the required Behat extensions and Moodle test wwwroot.
  88. $contents = self::get_config_file_contents($features, $stepsdefinitions);
  89. // Stores the file.
  90. if (!file_put_contents($configfilepath, $contents)) {
  91. behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $configfilepath . ' can not be created');
  92. }
  93. }
  94. /**
  95. * Gets the list of Moodle steps definitions
  96. *
  97. * Class name as a key and the filepath as value
  98. *
  99. * Externalized from update_config_file() to use
  100. * it from the steps definitions web interface
  101. *
  102. * @return array
  103. */
  104. public static function get_components_steps_definitions() {
  105. $components = tests_finder::get_components_with_tests('stepsdefinitions');
  106. if (!$components) {
  107. return false;
  108. }
  109. $stepsdefinitions = array();
  110. foreach ($components as $componentname => $componentpath) {
  111. $componentpath = self::clean_path($componentpath);
  112. if (!file_exists($componentpath . self::get_behat_tests_path())) {
  113. continue;
  114. }
  115. $diriterator = new DirectoryIterator($componentpath . self::get_behat_tests_path());
  116. $regite = new RegexIterator($diriterator, '|behat_.*\.php$|');
  117. // All behat_*.php inside behat_config_manager::get_behat_tests_path() are added as steps definitions files.
  118. foreach ($regite as $file) {
  119. $key = $file->getBasename('.php');
  120. $stepsdefinitions[$key] = $file->getPathname();
  121. }
  122. }
  123. return $stepsdefinitions;
  124. }
  125. /**
  126. * Returns the behat config file path used by the steps definition list
  127. *
  128. * Note this can only be called from web-based scripts so it will return the
  129. * production dataroot not behat_dataroot. With this the steps definitions
  130. * list is accessible without having to install the behat test site.
  131. *
  132. * @return string
  133. */
  134. public static function get_steps_list_config_filepath() {
  135. global $USER;
  136. $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id;
  137. make_writable_directory($userdir);
  138. return $userdir . '/behat.yml';
  139. }
  140. /**
  141. * Behat config file specifing the main context class,
  142. * the required Behat extensions and Moodle test wwwroot.
  143. *
  144. * @param array $features The system feature files
  145. * @param array $stepsdefinitions The system steps definitions
  146. * @return string
  147. */
  148. protected static function get_config_file_contents($features, $stepsdefinitions) {
  149. global $CFG;
  150. // We require here when we are sure behat dependencies are available.
  151. require_once($CFG->dirroot . '/vendor/autoload.php');
  152. $basedir = $CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat';
  153. $config = array(
  154. 'default' => array(
  155. 'paths' => array(
  156. 'features' => $basedir . DIRECTORY_SEPARATOR . 'features',
  157. 'bootstrap' => $basedir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'bootstrap',
  158. ),
  159. 'context' => array(
  160. 'class' => 'behat_init_context'
  161. ),
  162. 'extensions' => array(
  163. 'Behat\MinkExtension\Extension' => array(
  164. 'base_url' => $CFG->behat_wwwroot,
  165. 'goutte' => null,
  166. 'selenium2' => null
  167. ),
  168. 'Moodle\BehatExtension\Extension' => array(
  169. 'features' => $features,
  170. 'steps_definitions' => $stepsdefinitions
  171. )
  172. ),
  173. 'formatter' => array(
  174. 'name' => 'progress'
  175. )
  176. )
  177. );
  178. // In case user defined overrides respect them over our default ones.
  179. if (!empty($CFG->behat_config)) {
  180. $config = self::merge_config($config, $CFG->behat_config);
  181. }
  182. return Symfony\Component\Yaml\Yaml::dump($config, 10, 2);
  183. }
  184. /**
  185. * Overrides default config with local config values
  186. *
  187. * array_merge does not merge completely the array's values
  188. *
  189. * @param mixed $config The node of the default config
  190. * @param mixed $localconfig The node of the local config
  191. * @return mixed The merge result
  192. */
  193. protected static function merge_config($config, $localconfig) {
  194. if (!is_array($config) && !is_array($localconfig)) {
  195. return $localconfig;
  196. }
  197. // Local overrides also deeper default values.
  198. if (is_array($config) && !is_array($localconfig)) {
  199. return $localconfig;
  200. }
  201. foreach ($localconfig as $key => $value) {
  202. // If defaults are not as deep as local values let locals override.
  203. if (!is_array($config)) {
  204. unset($config);
  205. }
  206. // Add the param if it doesn't exists or merge branches.
  207. if (empty($config[$key])) {
  208. $config[$key] = $value;
  209. } else {
  210. $config[$key] = self::merge_config($config[$key], $localconfig[$key]);
  211. }
  212. }
  213. return $config;
  214. }
  215. /**
  216. * Cleans the path returned by get_components_with_tests() to standarize it
  217. *
  218. * @see tests_finder::get_all_directories_with_tests() it returns the path including /tests/
  219. * @param string $path
  220. * @return string The string without the last /tests part
  221. */
  222. protected final static function clean_path($path) {
  223. $path = rtrim($path, DIRECTORY_SEPARATOR);
  224. $parttoremove = DIRECTORY_SEPARATOR . 'tests';
  225. $substr = substr($path, strlen($path) - strlen($parttoremove));
  226. if ($substr == $parttoremove) {
  227. $path = substr($path, 0, strlen($path) - strlen($parttoremove));
  228. }
  229. return rtrim($path, DIRECTORY_SEPARATOR);
  230. }
  231. /**
  232. * The relative path where components stores their behat tests
  233. *
  234. * @return string
  235. */
  236. protected final static function get_behat_tests_path() {
  237. return DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'behat';
  238. }
  239. }