Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 403]

   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  /**
  18   * Utils to set Behat config
  19   *
  20   * @package    core
  21   * @category   test
  22   * @copyright  2012 David MonllaĆ³
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once (__DIR__ . '/behat_config_util.php');
  29  
  30  /**
  31   * Behat configuration manager
  32   *
  33   * Creates/updates Behat config files getting tests
  34   * and steps from Moodle codebase
  35   *
  36   * @package    core
  37   * @category   test
  38   * @copyright  2012 David MonllaĆ³
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class behat_config_manager {
  42  
  43      /**
  44       * @var bool Keep track of the automatic profile conversion. So we can notify user.
  45       */
  46      public static $autoprofileconversion = false;
  47  
  48      /**
  49       * @var behat_config_util keep object of behat_config_util for use.
  50       */
  51      public static $behatconfigutil = null;
  52  
  53      /**
  54       * Returns behat_config_util.
  55       *
  56       * @return behat_config_util
  57       */
  58      private static function get_behat_config_util() {
  59          if (!self::$behatconfigutil) {
  60              self::$behatconfigutil = new behat_config_util();
  61          }
  62  
  63          return self::$behatconfigutil;
  64      }
  65  
  66      /**
  67       * Updates a config file
  68       *
  69       * The tests runner and the steps definitions list uses different
  70       * config files to avoid problems with concurrent executions.
  71       *
  72       * The steps definitions list can be filtered by component so it's
  73       * behat.yml is different from the $CFG->dirroot one.
  74       *
  75       * @param  string $component Restricts the obtained steps definitions to the specified component
  76       * @param  string $testsrunner If the config file will be used to run tests
  77       * @param  string $tags features files including tags.
  78       * @param  bool   $themesuitewithallfeatures if only theme specific features need to be included in the suite.
  79       * @param  int    $parallelruns number of parallel runs.
  80       * @param  int    $run current run for which config needs to be updated.
  81       * @return void
  82       */
  83      public static function update_config_file($component = '', $testsrunner = true, $tags = '',
  84          $themesuitewithallfeatures = false, $parallelruns = 0, $run = 0) {
  85  
  86          global $CFG;
  87  
  88          // Behat must have a separate behat.yml to have access to the whole set of features and steps definitions.
  89          if ($testsrunner === true) {
  90              $configfilepath = behat_command::get_behat_dir($run) . '/behat.yml';
  91          } else {
  92              // Alternative for steps definitions filtering, one for each user.
  93              $configfilepath = self::get_steps_list_config_filepath();
  94          }
  95  
  96          $behatconfigutil = self::get_behat_config_util();
  97          $behatconfigutil->set_theme_suite_to_include_core_features($themesuitewithallfeatures);
  98          $behatconfigutil->set_tag_for_feature_filter($tags);
  99  
 100          // Gets all the components with features, if running the tests otherwise not required.
 101          $features = array();
 102          if ($testsrunner) {
 103              $features = $behatconfigutil->get_components_features();
 104          }
 105  
 106          // Gets all the components with steps definitions.
 107          $stepsdefinitions = $behatconfigutil->get_components_contexts($component);
 108          if (!$testsrunner) {
 109              // Exclude deprecated steps definitions from the available steps list.
 110              foreach (array_keys($stepsdefinitions) as $key) {
 111                  if (preg_match('/_deprecated$/', $key)) {
 112                      unset($stepsdefinitions[$key]);
 113                  }
 114              }
 115          }
 116  
 117          // Get current run.
 118          if (empty($run) && ($run !== false) && !empty($CFG->behatrunprocess)) {
 119              $run = $CFG->behatrunprocess;
 120          }
 121  
 122          // Get number of parallel runs if not passed.
 123          if (empty($parallelruns) && ($parallelruns !== false)) {
 124              $parallelruns = self::get_behat_run_config_value('parallel');
 125          }
 126  
 127          // Behat config file specifing the main context class,
 128          // the required Behat extensions and Moodle test wwwroot.
 129          $contents = $behatconfigutil->get_config_file_contents($features, $stepsdefinitions, $tags, $parallelruns, $run);
 130  
 131          // Stores the file.
 132          if (!file_put_contents($configfilepath, $contents)) {
 133              behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $configfilepath . ' can not be created');
 134          }
 135  
 136      }
 137  
 138      /**
 139       * @deprecated since 3.2 - please use behat_config_util.php
 140       */
 141      public static function get_features_with_tags() {
 142          throw new coding_exception('get_features_with_tags() can not be used anymore. ' .
 143              'Please use behat_config_util instead.');
 144      }
 145  
 146      /**
 147       * @deprecated since 3.2 - please use behat_config_util.php
 148       */
 149      public static function get_components_steps_definitions() {
 150          throw new coding_exception('get_components_steps_definitions() can not be used anymore. ' .
 151              'Please use behat_config_util instead.');
 152      }
 153  
 154      /**
 155       * Returns the behat config file path used by the steps definition list
 156       *
 157       * @return string
 158       */
 159      public static function get_steps_list_config_filepath() {
 160          global $USER;
 161  
 162          // We don't cygwin-it as it is called using exec() which uses cmd.exe.
 163          $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id;
 164          make_writable_directory($userdir);
 165  
 166          return $userdir . '/behat.yml';
 167      }
 168  
 169      /**
 170       * Returns the behat config file path used by the behat cli command.
 171       *
 172       * @param int $runprocess Runprocess.
 173       * @return string
 174       */
 175      public static function get_behat_cli_config_filepath($runprocess = 0) {
 176          global $CFG;
 177  
 178          if ($runprocess) {
 179              if (isset($CFG->behat_parallel_run[$runprocess - 1 ]['behat_dataroot'])) {
 180                  $command = $CFG->behat_parallel_run[$runprocess - 1]['behat_dataroot'];
 181              } else {
 182                  $command = $CFG->behat_dataroot . $runprocess;
 183              }
 184          } else {
 185              $command = $CFG->behat_dataroot;
 186          }
 187          $command .= DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
 188  
 189          // Cygwin uses linux-style directory separators.
 190          if (testing_is_cygwin()) {
 191              $command = str_replace('\\', '/', $command);
 192          }
 193  
 194          return $command;
 195      }
 196  
 197      /**
 198       * Returns the path to the parallel run file which specifies if parallel test environment is enabled
 199       * and how many parallel runs to execute.
 200       *
 201       * @return string
 202       */
 203      public final static function get_behat_run_config_file_path() {
 204          return behat_command::get_parent_behat_dir() . '/run_environment.json';
 205      }
 206  
 207      /**
 208       * Get config for parallel run.
 209       *
 210       * @param string $key Key to store
 211       * @return string|int|array value which is stored.
 212       */
 213      public final static function get_behat_run_config_value($key) {
 214          $parallelrunconfigfile = self::get_behat_run_config_file_path();
 215  
 216          if (file_exists($parallelrunconfigfile)) {
 217              if ($parallelrunconfigs = @json_decode(file_get_contents($parallelrunconfigfile), true)) {
 218                  if (isset($parallelrunconfigs[$key])) {
 219                      return $parallelrunconfigs[$key];
 220                  }
 221              }
 222          }
 223  
 224          return false;
 225      }
 226  
 227      /**
 228       * Save/update config for parallel run.
 229       *
 230       * @param string $key Key to store
 231       * @param string|int|array $value to store.
 232       */
 233      public final static function set_behat_run_config_value($key, $value) {
 234          $parallelrunconfigs = array();
 235          $parallelrunconfigfile = self::get_behat_run_config_file_path();
 236  
 237          // Get any existing config first.
 238          if (file_exists($parallelrunconfigfile)) {
 239              $parallelrunconfigs = @json_decode(file_get_contents($parallelrunconfigfile), true);
 240          }
 241          $parallelrunconfigs[$key] = $value;
 242  
 243          @file_put_contents($parallelrunconfigfile, json_encode($parallelrunconfigs, JSON_PRETTY_PRINT));
 244      }
 245  
 246      /**
 247       * Drops parallel site links.
 248       *
 249       * @return bool true on success else false.
 250       */
 251      public final static function drop_parallel_site_links() {
 252          global $CFG;
 253  
 254          // Get parallel test runs.
 255          $parallelrun = self::get_behat_run_config_value('parallel');
 256  
 257          if (empty($parallelrun)) {
 258              return false;
 259          }
 260  
 261          // If parallel run then remove links and original file.
 262          clearstatcache();
 263          for ($i = 1; $i <= $parallelrun; $i++) {
 264              // Don't delete links for specified sites, as they should be accessible.
 265              if (!empty($CFG->behat_parallel_run['behat_wwwroot'][$i - 1]['behat_wwwroot'])) {
 266                  continue;
 267              }
 268              $link = $CFG->dirroot . '/' . BEHAT_PARALLEL_SITE_NAME . $i;
 269              if (file_exists($link) && is_link($link)) {
 270                  @unlink($link);
 271              }
 272          }
 273          return true;
 274      }
 275  
 276      /**
 277       * Create parallel site links.
 278       *
 279       * @param int $fromrun first run
 280       * @param int $torun last run.
 281       * @return bool true for sucess, else false.
 282       */
 283      public final static function create_parallel_site_links($fromrun, $torun) {
 284          global $CFG;
 285  
 286          // Create site symlink if necessary.
 287          clearstatcache();
 288          for ($i = $fromrun; $i <= $torun; $i++) {
 289              // Don't create links for specified sites, as they should be accessible.
 290              if (!empty($CFG->behat_parallel_run['behat_wwwroot'][$i - 1]['behat_wwwroot'])) {
 291                  continue;
 292              }
 293              $link = $CFG->dirroot.'/'.BEHAT_PARALLEL_SITE_NAME.$i;
 294              clearstatcache();
 295              if (file_exists($link)) {
 296                  if (!is_link($link) || !is_dir($link)) {
 297                      echo "File exists at link location ($link) but is not a link or directory!" . PHP_EOL;
 298                      return false;
 299                  }
 300              } else if (!symlink($CFG->dirroot, $link)) {
 301                  // Try create link in case it's not already present.
 302                  echo "Unable to create behat site symlink ($link)" . PHP_EOL;
 303                  return false;
 304              }
 305          }
 306          return true;
 307      }
 308  
 309      /**
 310       * @deprecated since 3.2 - please use behat_config_util.php
 311       */
 312      protected static function get_config_file_contents() {
 313          throw new coding_exception('get_config_file_contents() can not be used anymore. ' .
 314              'Please use behat_config_util instead.');
 315      }
 316  
 317      /**
 318       * @deprecated since 3.2 - please use behat_config_util.php
 319       */
 320      protected static function merge_behat_config() {
 321          throw new coding_exception('merge_behat_config() can not be used anymore. ' .
 322              'Please use behat_config_util instead.');
 323      }
 324  
 325      /**
 326       * @deprecated since 3.2 - please use behat_config_util.php
 327       */
 328      protected static function get_behat_profile() {
 329          throw new coding_exception('get_behat_profile() can not be used anymore. ' .
 330              'Please use behat_config_util instead.');
 331      }
 332  
 333      /**
 334       * @deprecated since 3.2 - please use behat_config_util.php
 335       */
 336      protected static function profile_guided_allocate() {
 337          throw new coding_exception('profile_guided_allocate() can not be used anymore. ' .
 338              'Please use behat_config_util instead.');
 339      }
 340  
 341      /**
 342       * @deprecated since 3.2 - please use behat_config_util.php
 343       */
 344      protected static function merge_config() {
 345          throw new coding_exception('merge_config() can not be used anymore. ' .
 346              'Please use behat_config_util instead.');
 347      }
 348  
 349      /**
 350       * @deprecated since 3.2 - please use behat_config_util.php
 351       */
 352      protected final static function clean_path() {
 353          throw new coding_exception('clean_path() can not be used anymore. ' .
 354              'Please use behat_config_util instead.');
 355      }
 356  
 357      /**
 358       * @deprecated since 3.2 - please use behat_config_util.php
 359       */
 360      protected final static function get_behat_tests_path() {
 361          throw new coding_exception('get_behat_tests_path() can not be used anymore. ' .
 362              'Please use behat_config_util instead.');
 363      }
 364  
 365  }