Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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   * CLI tool with utilities to manage Behat integration in Moodle
  19   *
  20   * All CLI utilities uses $CFG->behat_dataroot and $CFG->prefix_dataroot as
  21   * $CFG->dataroot and $CFG->prefix
  22   *
  23   * @package    tool_behat
  24   * @copyright  2012 David MonllaĆ³
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  
  28  
  29  if (isset($_SERVER['REMOTE_ADDR'])) {
  30      die(); // No access from web!.
  31  }
  32  
  33  // Basic functions.
  34  require_once (__DIR__ . '/../../../../lib/clilib.php');
  35  require_once (__DIR__ . '/../../../../lib/behat/lib.php');
  36  
  37  // CLI options.
  38  list($options, $unrecognized) = cli_get_params(
  39      array(
  40          'help'        => false,
  41          'install'     => false,
  42          'parallel'    => 0,
  43          'run'         => 0,
  44          'drop'        => false,
  45          'enable'      => false,
  46          'disable'     => false,
  47          'diag'        => false,
  48          'tags'        => '',
  49          'updatesteps' => false,
  50          'optimize-runs' => '',
  51          'add-core-features-to-theme' => false,
  52      ),
  53      array(
  54          'h' => 'help',
  55          'o' => 'optimize-runs',
  56          'a' => 'add-core-features-to-theme',
  57      )
  58  );
  59  
  60  if ($options['install'] or $options['drop']) {
  61      define('CACHE_DISABLE_ALL', true);
  62  }
  63  
  64  // Checking util_single_run.php CLI script usage.
  65  $help = "
  66  Behat utilities to manage the test environment
  67  
  68  Usage:
  69    php util_single_run.php [--install|--drop|--enable|--disable|--diag|--updatesteps|--help]
  70  
  71  Options:
  72  --install        Installs the test environment for acceptance tests
  73  --drop           Drops the database tables and the dataroot contents
  74  --enable         Enables test environment and updates tests list
  75  --disable        Disables test environment
  76  --diag           Get behat test environment status code
  77  --updatesteps    Update feature step file.
  78  
  79  -o, --optimize-runs Split features with specified tags in all parallel runs.
  80  -a, --add-core-features-to-theme Add all core features to specified theme's
  81  
  82  -h, --help Print out this help
  83  
  84  Example from Moodle root directory:
  85  \$ php admin/tool/behat/cli/util_single_run.php --enable
  86  
  87  More info in http://docs.moodle.org/dev/Acceptance_testing#Running_tests
  88  ";
  89  
  90  if (!empty($options['help'])) {
  91      echo $help;
  92      exit(0);
  93  }
  94  
  95  // Describe this script.
  96  define('BEHAT_UTIL', true);
  97  define('CLI_SCRIPT', true);
  98  define('NO_OUTPUT_BUFFERING', true);
  99  define('IGNORE_COMPONENT_CACHE', true);
 100  
 101  // Set run value, to be used by setup for configuring proper CFG variables.
 102  if ($options['run']) {
 103      define('BEHAT_CURRENT_RUN', $options['run']);
 104  }
 105  
 106  // Only load CFG from config.php, stop ASAP in lib/setup.php.
 107  define('ABORT_AFTER_CONFIG', true);
 108  require_once(__DIR__ . '/../../../../config.php');
 109  
 110  // Remove error handling overrides done in config.php.
 111  $CFG->debug = (E_ALL | E_STRICT);
 112  $CFG->debugdisplay = 1;
 113  error_reporting($CFG->debug);
 114  ini_set('display_errors', '1');
 115  ini_set('log_errors', '1');
 116  
 117  // Finish moodle init.
 118  define('ABORT_AFTER_CONFIG_CANCEL', true);
 119  require("$CFG->dirroot/lib/setup.php");
 120  
 121  raise_memory_limit(MEMORY_HUGE);
 122  
 123  require_once($CFG->libdir.'/adminlib.php');
 124  require_once($CFG->libdir.'/upgradelib.php');
 125  require_once($CFG->libdir.'/clilib.php');
 126  require_once($CFG->libdir.'/installlib.php');
 127  require_once($CFG->libdir.'/testing/classes/test_lock.php');
 128  
 129  if ($unrecognized) {
 130      $unrecognized = implode(PHP_EOL . "  ", $unrecognized);
 131      cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
 132  }
 133  
 134  // Behat utilities.
 135  require_once($CFG->libdir . '/behat/classes/util.php');
 136  require_once($CFG->libdir . '/behat/classes/behat_command.php');
 137  require_once($CFG->libdir . '/behat/classes/behat_config_manager.php');
 138  
 139  // Ensure run option is <= parallel run installed.
 140  $run = 0;
 141  $parallel = 0;
 142  if ($options['run']) {
 143      $run = $options['run'];
 144      // If parallel option is not passed, then try get it form config.
 145      if (!$options['parallel']) {
 146          $parallel = behat_config_manager::get_behat_run_config_value('parallel');
 147      } else {
 148          $parallel = $options['parallel'];
 149      }
 150  
 151      if (empty($parallel) || $run > $parallel) {
 152          echo "Parallel runs can't be more then ".$parallel.PHP_EOL;
 153          exit(1);
 154      }
 155      $CFG->behatrunprocess = $run;
 156  }
 157  
 158  // Run command (only one per time).
 159  if ($options['install']) {
 160      behat_util::install_site();
 161  
 162      // This is only displayed once for parallel install.
 163      if (empty($run)) {
 164          mtrace("Acceptance tests site installed");
 165      }
 166  
 167      // Note: Do not build the themes here. This is done during the 'enable' stage.
 168  
 169  } else if ($options['drop']) {
 170      // Ensure no tests are running.
 171      test_lock::acquire('behat');
 172      behat_util::drop_site();
 173      // This is only displayed once for parallel install.
 174      if (empty($run)) {
 175          mtrace("Acceptance tests site dropped");
 176      }
 177  
 178  } else if ($options['enable']) {
 179      if (!empty($parallel)) {
 180          // Save parallel site info for enable and install options.
 181          behat_config_manager::set_behat_run_config_value('behatsiteenabled', 1);
 182      }
 183  
 184      // Enable test mode.
 185      behat_util::start_test_mode($options['add-core-features-to-theme'], $options['optimize-runs'], $parallel, $run);
 186  
 187      // Themes are only built in the 'enable' command.
 188      behat_util::build_themes();
 189      mtrace("Testing environment themes built");
 190  
 191      // This is only displayed once for parallel install.
 192      if (empty($run)) {
 193          // Notify user that 2.5 profile has been converted to 3.5.
 194          if (behat_config_manager::$autoprofileconversion) {
 195              mtrace("2.5 behat profile detected, automatically converted to current 3.x format");
 196          }
 197  
 198          $runtestscommand = behat_command::get_behat_command(true, !empty($run));
 199  
 200          $runtestscommand .= ' --config ' . behat_config_manager::get_behat_cli_config_filepath();
 201          mtrace("Acceptance tests environment enabled on $CFG->behat_wwwroot, to run the tests use: " . PHP_EOL .
 202              $runtestscommand);
 203      }
 204  
 205  } else if ($options['disable']) {
 206      behat_util::stop_test_mode($run);
 207      // This is only displayed once for parallel install.
 208      if (empty($run)) {
 209          mtrace("Acceptance tests environment disabled");
 210      }
 211  
 212  } else if ($options['diag']) {
 213      $code = behat_util::get_behat_status();
 214      exit($code);
 215  
 216  } else if ($options['updatesteps']) {
 217      if (defined('BEHAT_FEATURE_STEP_FILE') && BEHAT_FEATURE_STEP_FILE) {
 218          $behatstepfile = BEHAT_FEATURE_STEP_FILE;
 219      } else {
 220          echo "BEHAT_FEATURE_STEP_FILE is not set, please ensure you set this to writable file" . PHP_EOL;
 221          exit(1);
 222      }
 223  
 224      // Run behat command to get steps in feature files.
 225      $featurestepscmd = behat_command::get_behat_command(true);
 226      $featurestepscmd .= ' --config ' . behat_config_manager::get_behat_cli_config_filepath();
 227      $featurestepscmd .= ' --dry-run --format=moodle_stepcount';
 228      $processes = cli_execute_parallel(array($featurestepscmd), __DIR__ . "/../../../../");
 229      $status = print_update_step_output(array_pop($processes), $behatstepfile);
 230  
 231      exit($status);
 232  } else {
 233      echo $help;
 234      exit(1);
 235  }
 236  
 237  exit(0);
 238  
 239  /**
 240   * Print update progress as dots for updating feature file step list.
 241   *
 242   * @param Process $process process executing update step command.
 243   * @param string $featurestepfile feature step file in which steps will be saved.
 244   * @return int exitcode.
 245   */
 246  function print_update_step_output($process, $featurestepfile) {
 247      $printedlength = 0;
 248  
 249      echo "Updating steps feature file for parallel behat runs" . PHP_EOL;
 250  
 251      // Show progress while running command.
 252      while ($process->isRunning()) {
 253          usleep(10000);
 254          $op = $process->getIncrementalOutput();
 255          if (trim($op)) {
 256              echo ".";
 257              $printedlength++;
 258              if ($printedlength > 70) {
 259                  $printedlength = 0;
 260                  echo PHP_EOL;
 261              }
 262          }
 263      }
 264  
 265      // If any error then exit.
 266      $exitcode = $process->getExitCode();
 267      // Output err.
 268      if ($exitcode != 0) {
 269          echo $process->getErrorOutput();
 270          exit($exitcode);
 271      }
 272  
 273      // Extract features with step info and save it in file.
 274      $featuresteps = $process->getOutput();
 275      $featuresteps = explode(PHP_EOL, $featuresteps);
 276  
 277      $realroot = realpath(__DIR__.'/../../../../').'/';
 278      foreach ($featuresteps as $featurestep) {
 279          if (trim($featurestep)) {
 280              $step = explode("::", $featurestep);
 281              $step[0] = str_replace($realroot, '', $step[0]);
 282              $steps[$step[0]] = $step[1];
 283          }
 284      }
 285  
 286      if ($existing = @json_decode(file_get_contents($featurestepfile), true)) {
 287          $steps = array_merge($existing, $steps);
 288      }
 289      arsort($steps);
 290  
 291      if (!@file_put_contents($featurestepfile, json_encode($steps, JSON_PRETTY_PRINT))) {
 292          behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $featurestepfile . ' can not be created');
 293          $exitcode = -1;
 294      }
 295  
 296      echo PHP_EOL. "Updated step count in " . $featurestepfile . PHP_EOL;
 297  
 298      return $exitcode;
 299  }