Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 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       * Returns the behat config file path used by the steps definition list
 140       *
 141       * @return string
 142       */
 143      public static function get_steps_list_config_filepath() {
 144          global $USER;
 145  
 146          // We don't cygwin-it as it is called using exec() which uses cmd.exe.
 147          $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id;
 148          make_writable_directory($userdir);
 149  
 150          return $userdir . '/behat.yml';
 151      }
 152  
 153      /**
 154       * Returns the behat config file path used by the behat cli command.
 155       *
 156       * @param int $runprocess Runprocess.
 157       * @return string
 158       */
 159      public static function get_behat_cli_config_filepath($runprocess = 0) {
 160          global $CFG;
 161  
 162          if ($runprocess) {
 163              if (isset($CFG->behat_parallel_run[$runprocess - 1 ]['behat_dataroot'])) {
 164                  $command = $CFG->behat_parallel_run[$runprocess - 1]['behat_dataroot'];
 165              } else {
 166                  $command = $CFG->behat_dataroot . $runprocess;
 167              }
 168          } else {
 169              $command = $CFG->behat_dataroot;
 170          }
 171          $command .= DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
 172  
 173          // Cygwin uses linux-style directory separators.
 174          if (testing_is_cygwin()) {
 175              $command = str_replace('\\', '/', $command);
 176          }
 177  
 178          return $command;
 179      }
 180  
 181      /**
 182       * Returns the path to the parallel run file which specifies if parallel test environment is enabled
 183       * and how many parallel runs to execute.
 184       *
 185       * @return string
 186       */
 187      public final static function get_behat_run_config_file_path() {
 188          return behat_command::get_parent_behat_dir() . '/run_environment.json';
 189      }
 190  
 191      /**
 192       * Get config for parallel run.
 193       *
 194       * @param string $key Key to store
 195       * @return string|int|array value which is stored.
 196       */
 197      public final static function get_behat_run_config_value($key) {
 198          $parallelrunconfigfile = self::get_behat_run_config_file_path();
 199  
 200          if (file_exists($parallelrunconfigfile)) {
 201              if ($parallelrunconfigs = @json_decode(file_get_contents($parallelrunconfigfile), true)) {
 202                  if (isset($parallelrunconfigs[$key])) {
 203                      return $parallelrunconfigs[$key];
 204                  }
 205              }
 206          }
 207  
 208          return false;
 209      }
 210  
 211      /**
 212       * Save/update config for parallel run.
 213       *
 214       * @param string $key Key to store
 215       * @param string|int|array $value to store.
 216       */
 217      public final static function set_behat_run_config_value($key, $value) {
 218          $parallelrunconfigs = array();
 219          $parallelrunconfigfile = self::get_behat_run_config_file_path();
 220  
 221          // Get any existing config first.
 222          if (file_exists($parallelrunconfigfile)) {
 223              $parallelrunconfigs = @json_decode(file_get_contents($parallelrunconfigfile), true);
 224          }
 225          $parallelrunconfigs[$key] = $value;
 226  
 227          @file_put_contents($parallelrunconfigfile, json_encode($parallelrunconfigs, JSON_PRETTY_PRINT));
 228      }
 229  
 230      /**
 231       * Drops parallel site links.
 232       *
 233       * @return bool true on success else false.
 234       */
 235      public final static function drop_parallel_site_links() {
 236          global $CFG;
 237  
 238          // Get parallel test runs.
 239          $parallelrun = self::get_behat_run_config_value('parallel');
 240  
 241          if (empty($parallelrun)) {
 242              return false;
 243          }
 244  
 245          // If parallel run then remove links and original file.
 246          clearstatcache();
 247          for ($i = 1; $i <= $parallelrun; $i++) {
 248              // Don't delete links for specified sites, as they should be accessible.
 249              if (!empty($CFG->behat_parallel_run['behat_wwwroot'][$i - 1]['behat_wwwroot'])) {
 250                  continue;
 251              }
 252              $link = $CFG->dirroot . '/' . BEHAT_PARALLEL_SITE_NAME . $i;
 253              if (file_exists($link) && is_link($link)) {
 254                  @unlink($link);
 255              }
 256          }
 257          return true;
 258      }
 259  
 260      /**
 261       * Create parallel site links.
 262       *
 263       * @param int $fromrun first run
 264       * @param int $torun last run.
 265       * @return bool true for sucess, else false.
 266       */
 267      public final static function create_parallel_site_links($fromrun, $torun) {
 268          global $CFG;
 269  
 270          // Create site symlink if necessary.
 271          clearstatcache();
 272          for ($i = $fromrun; $i <= $torun; $i++) {
 273              // Don't create links for specified sites, as they should be accessible.
 274              if (!empty($CFG->behat_parallel_run['behat_wwwroot'][$i - 1]['behat_wwwroot'])) {
 275                  continue;
 276              }
 277              $link = $CFG->dirroot.'/'.BEHAT_PARALLEL_SITE_NAME.$i;
 278              clearstatcache();
 279              if (file_exists($link)) {
 280                  if (!is_link($link) || !is_dir($link)) {
 281                      echo "File exists at link location ($link) but is not a link or directory!" . PHP_EOL;
 282                      return false;
 283                  }
 284              } else if (!symlink($CFG->dirroot, $link)) {
 285                  // Try create link in case it's not already present.
 286                  echo "Unable to create behat site symlink ($link)" . PHP_EOL;
 287                  return false;
 288              }
 289          }
 290          return true;
 291      }
 292  }