Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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          // We don't want the deprecated steps definitions here.
 109          if (!$testsrunner) {
 110              unset($stepsdefinitions['behat_deprecated']);
 111          }
 112  
 113          // Get current run.
 114          if (empty($run) && ($run !== false) && !empty($CFG->behatrunprocess)) {
 115              $run = $CFG->behatrunprocess;
 116          }
 117  
 118          // Get number of parallel runs if not passed.
 119          if (empty($parallelruns) && ($parallelruns !== false)) {
 120              $parallelruns = self::get_behat_run_config_value('parallel');
 121          }
 122  
 123          // Behat config file specifing the main context class,
 124          // the required Behat extensions and Moodle test wwwroot.
 125          $contents = $behatconfigutil->get_config_file_contents($features, $stepsdefinitions, $tags, $parallelruns, $run);
 126  
 127          // Stores the file.
 128          if (!file_put_contents($configfilepath, $contents)) {
 129              behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $configfilepath . ' can not be created');
 130          }
 131  
 132      }
 133  
 134      /**
 135       * @deprecated since 3.2 - please use behat_config_util.php
 136       */
 137      public static function get_features_with_tags() {
 138          throw new coding_exception('get_features_with_tags() can not be used anymore. ' .
 139              'Please use behat_config_util instead.');
 140      }
 141  
 142      /**
 143       * @deprecated since 3.2 - please use behat_config_util.php
 144       */
 145      public static function get_components_steps_definitions() {
 146          throw new coding_exception('get_components_steps_definitions() can not be used anymore. ' .
 147              'Please use behat_config_util instead.');
 148      }
 149  
 150      /**
 151       * Returns the behat config file path used by the steps definition list
 152       *
 153       * @return string
 154       */
 155      public static function get_steps_list_config_filepath() {
 156          global $USER;
 157  
 158          // We don't cygwin-it as it is called using exec() which uses cmd.exe.
 159          $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id;
 160          make_writable_directory($userdir);
 161  
 162          return $userdir . '/behat.yml';
 163      }
 164  
 165      /**
 166       * Returns the behat config file path used by the behat cli command.
 167       *
 168       * @param int $runprocess Runprocess.
 169       * @return string
 170       */
 171      public static function get_behat_cli_config_filepath($runprocess = 0) {
 172          global $CFG;
 173  
 174          if ($runprocess) {
 175              if (isset($CFG->behat_parallel_run[$runprocess - 1 ]['behat_dataroot'])) {
 176                  $command = $CFG->behat_parallel_run[$runprocess - 1]['behat_dataroot'];
 177              } else {
 178                  $command = $CFG->behat_dataroot . $runprocess;
 179              }
 180          } else {
 181              $command = $CFG->behat_dataroot;
 182          }
 183          $command .= DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
 184  
 185          // Cygwin uses linux-style directory separators.
 186          if (testing_is_cygwin()) {
 187              $command = str_replace('\\', '/', $command);
 188          }
 189  
 190          return $command;
 191      }
 192  
 193      /**
 194       * Returns the path to the parallel run file which specifies if parallel test environment is enabled
 195       * and how many parallel runs to execute.
 196       *
 197       * @return string
 198       */
 199      public final static function get_behat_run_config_file_path() {
 200          return behat_command::get_parent_behat_dir() . '/run_environment.json';
 201      }
 202  
 203      /**
 204       * Get config for parallel run.
 205       *
 206       * @param string $key Key to store
 207       * @return string|int|array value which is stored.
 208       */
 209      public final static function get_behat_run_config_value($key) {
 210          $parallelrunconfigfile = self::get_behat_run_config_file_path();
 211  
 212          if (file_exists($parallelrunconfigfile)) {
 213              if ($parallelrunconfigs = @json_decode(file_get_contents($parallelrunconfigfile), true)) {
 214                  if (isset($parallelrunconfigs[$key])) {
 215                      return $parallelrunconfigs[$key];
 216                  }
 217              }
 218          }
 219  
 220          return false;
 221      }
 222  
 223      /**
 224       * Save/update config for parallel run.
 225       *
 226       * @param string $key Key to store
 227       * @param string|int|array $value to store.
 228       */
 229      public final static function set_behat_run_config_value($key, $value) {
 230          $parallelrunconfigs = array();
 231          $parallelrunconfigfile = self::get_behat_run_config_file_path();
 232  
 233          // Get any existing config first.
 234          if (file_exists($parallelrunconfigfile)) {
 235              $parallelrunconfigs = @json_decode(file_get_contents($parallelrunconfigfile), true);
 236          }
 237          $parallelrunconfigs[$key] = $value;
 238  
 239          @file_put_contents($parallelrunconfigfile, json_encode($parallelrunconfigs, JSON_PRETTY_PRINT));
 240      }
 241  
 242      /**
 243       * Drops parallel site links.
 244       *
 245       * @return bool true on success else false.
 246       */
 247      public final static function drop_parallel_site_links() {
 248          global $CFG;
 249  
 250          // Get parallel test runs.
 251          $parallelrun = self::get_behat_run_config_value('parallel');
 252  
 253          if (empty($parallelrun)) {
 254              return false;
 255          }
 256  
 257          // If parallel run then remove links and original file.
 258          clearstatcache();
 259          for ($i = 1; $i <= $parallelrun; $i++) {
 260              // Don't delete links for specified sites, as they should be accessible.
 261              if (!empty($CFG->behat_parallel_run['behat_wwwroot'][$i - 1]['behat_wwwroot'])) {
 262                  continue;
 263              }
 264              $link = $CFG->dirroot . '/' . BEHAT_PARALLEL_SITE_NAME . $i;
 265              if (file_exists($link) && is_link($link)) {
 266                  @unlink($link);
 267              }
 268          }
 269          return true;
 270      }
 271  
 272      /**
 273       * Create parallel site links.
 274       *
 275       * @param int $fromrun first run
 276       * @param int $torun last run.
 277       * @return bool true for sucess, else false.
 278       */
 279      public final static function create_parallel_site_links($fromrun, $torun) {
 280          global $CFG;
 281  
 282          // Create site symlink if necessary.
 283          clearstatcache();
 284          for ($i = $fromrun; $i <= $torun; $i++) {
 285              // Don't create links for specified sites, as they should be accessible.
 286              if (!empty($CFG->behat_parallel_run['behat_wwwroot'][$i - 1]['behat_wwwroot'])) {
 287                  continue;
 288              }
 289              $link = $CFG->dirroot.'/'.BEHAT_PARALLEL_SITE_NAME.$i;
 290              clearstatcache();
 291              if (file_exists($link)) {
 292                  if (!is_link($link) || !is_dir($link)) {
 293                      echo "File exists at link location ($link) but is not a link or directory!" . PHP_EOL;
 294                      return false;
 295                  }
 296              } else if (!symlink($CFG->dirroot, $link)) {
 297                  // Try create link in case it's not already present.
 298                  echo "Unable to create behat site symlink ($link)" . PHP_EOL;
 299                  return false;
 300              }
 301          }
 302          return true;
 303      }
 304  
 305      /**
 306       * @deprecated since 3.2 - please use behat_config_util.php
 307       */
 308      protected static function get_config_file_contents() {
 309          throw new coding_exception('get_config_file_contents() can not be used anymore. ' .
 310              'Please use behat_config_util instead.');
 311      }
 312  
 313      /**
 314       * @deprecated since 3.2 - please use behat_config_util.php
 315       */
 316      protected static function merge_behat_config() {
 317          throw new coding_exception('merge_behat_config() can not be used anymore. ' .
 318              'Please use behat_config_util instead.');
 319      }
 320  
 321      /**
 322       * @deprecated since 3.2 - please use behat_config_util.php
 323       */
 324      protected static function get_behat_profile() {
 325          throw new coding_exception('get_behat_profile() can not be used anymore. ' .
 326              'Please use behat_config_util instead.');
 327      }
 328  
 329      /**
 330       * @deprecated since 3.2 - please use behat_config_util.php
 331       */
 332      protected static function profile_guided_allocate() {
 333          throw new coding_exception('profile_guided_allocate() can not be used anymore. ' .
 334              'Please use behat_config_util instead.');
 335      }
 336  
 337      /**
 338       * @deprecated since 3.2 - please use behat_config_util.php
 339       */
 340      protected static function merge_config() {
 341          throw new coding_exception('merge_config() can not be used anymore. ' .
 342              'Please use behat_config_util instead.');
 343      }
 344  
 345      /**
 346       * @deprecated since 3.2 - please use behat_config_util.php
 347       */
 348      protected final static function clean_path() {
 349          throw new coding_exception('clean_path() can not be used anymore. ' .
 350              'Please use behat_config_util instead.');
 351      }
 352  
 353      /**
 354       * @deprecated since 3.2 - please use behat_config_util.php
 355       */
 356      protected final static function get_behat_tests_path() {
 357          throw new coding_exception('get_behat_tests_path() can not be used anymore. ' .
 358              'Please use behat_config_util instead.');
 359      }
 360  
 361  }