[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/admin/tool/behat/cli/ -> util.php (source)

   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 parallel 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  define('BEHAT_UTIL', true);
  34  define('CLI_SCRIPT', true);
  35  define('NO_OUTPUT_BUFFERING', true);
  36  define('IGNORE_COMPONENT_CACHE', true);
  37  define('ABORT_AFTER_CONFIG', true);
  38  
  39  require_once(__DIR__ . '/../../../../config.php');
  40  require_once (__DIR__ . '/../../../../lib/clilib.php');
  41  require_once (__DIR__ . '/../../../../lib/behat/lib.php');
  42  require_once (__DIR__ . '/../../../../lib/behat/classes/behat_command.php');
  43  require_once (__DIR__ . '/../../../../lib/behat/classes/behat_config_manager.php');
  44  
  45  // CLI options.
  46  list($options, $unrecognized) = cli_get_params(
  47      array(
  48          'help'        => false,
  49          'install'     => false,
  50          'drop'        => false,
  51          'enable'      => false,
  52          'disable'     => false,
  53          'diag'        => false,
  54          'parallel'    => 0,
  55          'maxruns'     => false,
  56          'updatesteps' => false,
  57          'fromrun'     => 1,
  58          'torun'       => 0,
  59      ),
  60      array(
  61          'h' => 'help',
  62          'j' => 'parallel',
  63          'm' => 'maxruns'
  64      )
  65  );
  66  
  67  // Checking util.php CLI script usage.
  68  $help = "
  69  Behat utilities to manage the test environment
  70  
  71  Usage:
  72    php util.php [--install|--drop|--enable|--disable|--diag|--updatesteps|--help] [--parallel=value [--maxruns=value]]
  73  
  74  Options:
  75  --install      Installs the test environment for acceptance tests
  76  --drop         Drops the database tables and the dataroot contents
  77  --enable       Enables test environment and updates tests list
  78  --disable      Disables test environment
  79  --diag         Get behat test environment status code
  80  --updatesteps  Update feature step file.
  81  -j, --parallel Number of parallel behat run operation
  82  -m, --maxruns  Max parallel processes to be executed at one time.
  83  
  84  -h, --help     Print out this help
  85  
  86  Example from Moodle root directory:
  87  \$ php admin/tool/behat/cli/util.php --enable --parallel=4
  88  
  89  More info in http://docs.moodle.org/dev/Acceptance_testing#Running_tests
  90  ";
  91  
  92  if (!empty($options['help'])) {
  93      echo $help;
  94      exit(0);
  95  }
  96  
  97  $cwd = getcwd();
  98  
  99  // For drop option check if parallel site.
 100  if ((empty($options['parallel'])) && ($options['drop']) || $options['updatesteps']) {
 101      // Get parallel run info from first run.
 102      $options['parallel'] = behat_config_manager::get_parallel_test_runs($options['fromrun']);
 103  }
 104  
 105  // If not a parallel site then open single run.
 106  if (empty($options['parallel'])) {
 107      chdir(__DIR__);
 108      // Check if behat is initialised, if not exit.
 109      passthru("php util_single_run.php --diag", $status);
 110      if ($status) {
 111          exit ($status);
 112      }
 113      $cmd = commands_to_execute($options);
 114      $processes = cli_execute_parallel(array($cmd), __DIR__);
 115      $status = print_sequential_output($processes, false);
 116      chdir($cwd);
 117      exit($status);
 118  }
 119  
 120  // Default torun is maximum parallel runs.
 121  if (empty($options['torun'])) {
 122      $options['torun'] = $options['parallel'];
 123  }
 124  
 125  $status = false;
 126  $cmds = commands_to_execute($options);
 127  
 128  // Start executing commands either sequential/parallel for options provided.
 129  if ($options['diag'] || $options['enable'] || $options['disable']) {
 130      // Do it sequentially as it's fast and need to be displayed nicely.
 131      foreach (array_chunk($cmds, 1, true) as $cmd) {
 132          $processes = cli_execute_parallel($cmd, __DIR__);
 133          print_sequential_output($processes);
 134      }
 135  
 136  } else if ($options['drop']) {
 137      $processes = cli_execute_parallel($cmds, __DIR__);
 138      $exitcodes = print_combined_drop_output($processes);
 139      foreach ($exitcodes as $exitcode) {
 140          $status = (bool)$status || (bool)$exitcode;
 141      }
 142  
 143  } else if ($options['install']) {
 144      // This is intensive compared to behat itself so run them in chunk if option maxruns not set.
 145      if ($options['maxruns']) {
 146          foreach (array_chunk($cmds, $options['maxruns'], true) as $chunk) {
 147              $processes = cli_execute_parallel($chunk, __DIR__);
 148              $exitcodes = print_combined_install_output($processes);
 149              foreach ($exitcodes as $name => $exitcode) {
 150                  if ($exitcode != 0) {
 151                      echo "Failed process [[$name]]" . PHP_EOL;
 152                      echo $processes[$name]->getOutput();
 153                      echo PHP_EOL;
 154                      echo $processes[$name]->getErrorOutput();
 155                      echo PHP_EOL . PHP_EOL;
 156                  }
 157                  $status = (bool)$status || (bool)$exitcode;
 158              }
 159          }
 160      } else {
 161          $processes = cli_execute_parallel($cmds, __DIR__);
 162          $exitcodes = print_combined_install_output($processes);
 163          foreach ($exitcodes as $name => $exitcode) {
 164              if ($exitcode != 0) {
 165                  echo "Failed process [[$name]]" . PHP_EOL;
 166                  echo $processes[$name]->getOutput();
 167                  echo PHP_EOL;
 168                  echo $processes[$name]->getErrorOutput();
 169                  echo PHP_EOL . PHP_EOL;
 170              }
 171              $status = (bool)$status || (bool)$exitcode;
 172          }
 173      }
 174  
 175  } else if ($options['updatesteps']) {
 176      // Rewrite config file to ensure we have all the features covered.
 177      if (empty($options['parallel'])) {
 178          behat_config_manager::update_config_file();
 179      } else {
 180          // Update config file, ensuring we have up-to-date behat.yml.
 181          for ($i = $options['fromrun']; $i <= $options['torun']; $i++) {
 182              $CFG->behatrunprocess = $i;
 183              behat_config_manager::update_config_file();
 184          }
 185          unset($CFG->behatrunprocess);
 186      }
 187  
 188      // Do it sequentially as it's fast and need to be displayed nicely.
 189      foreach (array_chunk($cmds, 1, true) as $cmd) {
 190          $processes = cli_execute_parallel($cmd, __DIR__);
 191          print_sequential_output($processes);
 192      }
 193      exit(0);
 194  
 195  } else {
 196      // We should never reach here.
 197      echo $help;
 198      exit(1);
 199  }
 200  
 201  // Ensure we have success status to show following information.
 202  if ($status) {
 203      echo "Unknown failure $status" . PHP_EOL;
 204      exit((int)$status);
 205  }
 206  
 207  // Show command o/p (only one per time).
 208  if ($options['install']) {
 209      echo "Acceptance tests site installed for sites:".PHP_EOL;
 210  
 211      // Display all sites which are installed/drop/diabled.
 212      for ($i = $options['fromrun']; $i <= $options['torun']; $i++) {
 213          if (empty($CFG->behat_parallel_run[$i - 1]['behat_wwwroot'])) {
 214              echo $CFG->behat_wwwroot . "/" . BEHAT_PARALLEL_SITE_NAME . $i . PHP_EOL;
 215          } else {
 216              echo $CFG->behat_parallel_run[$i - 1]['behat_wwwroot'] . PHP_EOL;
 217          }
 218  
 219      }
 220  } else if ($options['drop']) {
 221      echo "Acceptance tests site dropped for " . $options['parallel'] . " parallel sites" . PHP_EOL;
 222  
 223  } else if ($options['enable']) {
 224      echo "Acceptance tests environment enabled on $CFG->behat_wwwroot, to run the tests use:" . PHP_EOL;
 225      echo behat_command::get_behat_command(true, true);
 226      echo PHP_EOL;
 227  
 228  } else if ($options['disable']) {
 229      echo "Acceptance tests environment disabled for " . $options['parallel'] . " parallel sites" . PHP_EOL;
 230  
 231  } else if ($options['diag']) {
 232      // Valid option, so nothing to do.
 233  } else {
 234      echo $help;
 235      chdir($cwd);
 236      exit(1);
 237  }
 238  
 239  chdir($cwd);
 240  exit(0);
 241  
 242  /**
 243   * Create commands to be executed for parallel run.
 244   *
 245   * @param array $options options provided by user.
 246   * @return array commands to be executed.
 247   */
 248  function commands_to_execute($options) {
 249      $removeoptions = array('maxruns', 'fromrun', 'torun');
 250      $cmds = array();
 251      $extraoptions = $options;
 252      $extra = "";
 253  
 254      // Remove extra options not in util_single_run.php.
 255      foreach ($removeoptions as $ro) {
 256          $extraoptions[$ro] = null;
 257          unset($extraoptions[$ro]);
 258      }
 259  
 260      foreach ($extraoptions as $option => $value) {
 261          if ($options[$option]) {
 262              $extra .= " --$option";
 263              if ($value) {
 264                  $extra .= "=$value";
 265              }
 266          }
 267      }
 268  
 269      if (empty($options['parallel'])) {
 270          $cmds = "php util_single_run.php " . $extra;
 271      } else {
 272          // Create commands which has to be executed for parallel site.
 273          for ($i = $options['fromrun']; $i <= $options['torun']; $i++) {
 274              $prefix = BEHAT_PARALLEL_SITE_NAME . $i;
 275              $cmds[$prefix] = "php util_single_run.php " . $extra . " --run=" . $i . " 2>&1";
 276          }
 277      }
 278      return $cmds;
 279  }
 280  
 281  /**
 282   * Print drop output merging each run.
 283   *
 284   * @param array $processes list of processes.
 285   * @return array exit codes of each process.
 286   */
 287  function print_combined_drop_output($processes) {
 288      $exitcodes = array();
 289      $maxdotsonline = 70;
 290      $remainingprintlen = $maxdotsonline;
 291      $progresscount = 0;
 292      echo "Dropping tables:" . PHP_EOL;
 293  
 294      while (count($exitcodes) != count($processes)) {
 295          usleep(10000);
 296          foreach ($processes as $name => $process) {
 297              if ($process->isRunning()) {
 298                  $op = $process->getIncrementalOutput();
 299                  if (trim($op)) {
 300                      $update = preg_filter('#^\s*([FS\.\-]+)(?:\s+\d+)?\s*$#', '$1', $op);
 301                      $strlentoprint = strlen($update);
 302  
 303                      // If not enough dots printed on line then just print.
 304                      if ($strlentoprint < $remainingprintlen) {
 305                          echo $update;
 306                          $remainingprintlen = $remainingprintlen - $strlentoprint;
 307                      } else if ($strlentoprint == $remainingprintlen) {
 308                          $progresscount += $maxdotsonline;
 309                          echo $update . " " . $progresscount . PHP_EOL;
 310                          $remainingprintlen = $maxdotsonline;
 311                      } else {
 312                          while ($part = substr($update, 0, $remainingprintlen) > 0) {
 313                              $progresscount += $maxdotsonline;
 314                              echo $part . " " . $progresscount . PHP_EOL;
 315                              $update = substr($update, $remainingprintlen);
 316                              $remainingprintlen = $maxdotsonline;
 317                          }
 318                      }
 319                  }
 320              } else {
 321                  // Process exited.
 322                  $process->clearOutput();
 323                  $exitcodes[$name] = $process->getExitCode();
 324              }
 325          }
 326      }
 327  
 328      echo PHP_EOL;
 329      return $exitcodes;
 330  }
 331  
 332  /**
 333   * Print install output merging each run.
 334   *
 335   * @param array $processes list of processes.
 336   * @return array exit codes of each process.
 337   */
 338  function print_combined_install_output($processes) {
 339      $exitcodes = array();
 340      $line = array();
 341  
 342      // Check what best we can do to accommodate  all parallel run o/p on single line.
 343      // Windows command line has length of 80 chars, so default we will try fit o/p in 80 chars.
 344      if (defined('BEHAT_MAX_CMD_LINE_OUTPUT') && BEHAT_MAX_CMD_LINE_OUTPUT) {
 345          $lengthofprocessline = (int)max(10, BEHAT_MAX_CMD_LINE_OUTPUT / count($processes));
 346      } else {
 347          $lengthofprocessline = (int)max(10, 80 / count($processes));
 348      }
 349  
 350      echo "Installing behat site for " . count($processes) . " parallel behat run" . PHP_EOL;
 351  
 352      // Show process name in first row.
 353      foreach ($processes as $name => $process) {
 354          // If we don't have enough space to show full run name then show runX.
 355          if ($lengthofprocessline < strlen($name + 2)) {
 356              $name = substr($name, -5);
 357          }
 358          // One extra padding as we are adding | separator for rest of the data.
 359          $line[$name] = str_pad('[' . $name . '] ', $lengthofprocessline + 1);
 360      }
 361      ksort($line);
 362      $tableheader = array_keys($line);
 363      echo implode("", $line) . PHP_EOL;
 364  
 365      // Now print o/p from each process.
 366      while (count($exitcodes) != count($processes)) {
 367          usleep(50000);
 368          $poutput = array();
 369          // Create child process.
 370          foreach ($processes as $name => $process) {
 371              if ($process->isRunning()) {
 372                  $output = $process->getIncrementalOutput();
 373                  if (trim($output)) {
 374                      $poutput[$name] = explode(PHP_EOL, $output);
 375                  }
 376              } else {
 377                  // Process exited.
 378                  $exitcodes[$name] = $process->getExitCode();
 379              }
 380          }
 381          ksort($poutput);
 382  
 383          // Get max depth of o/p before displaying.
 384          $maxdepth = 0;
 385          foreach ($poutput as $pout) {
 386              $pdepth = count($pout);
 387              $maxdepth = $pdepth >= $maxdepth ? $pdepth : $maxdepth;
 388          }
 389  
 390          // Iterate over each process to get line to print.
 391          for ($i = 0; $i <= $maxdepth; $i++) {
 392              $pline = "";
 393              foreach ($tableheader as $name) {
 394                  $po = empty($poutput[$name][$i]) ? "" : substr($poutput[$name][$i], 0, $lengthofprocessline - 1);
 395                  $po = str_pad($po, $lengthofprocessline);
 396                  $pline .= "|". $po;
 397              }
 398              if (trim(str_replace("|", "", $pline))) {
 399                  echo $pline . PHP_EOL;
 400              }
 401          }
 402          unset($poutput);
 403          $poutput = null;
 404  
 405      }
 406      echo PHP_EOL;
 407      return $exitcodes;
 408  }
 409  
 410  /**
 411   * Print install output merging showing one run at a time.
 412   * If any process fail then exit.
 413   *
 414   * @param array $processes list of processes.
 415   * @param bool $showprefix show prefix.
 416   * @return bool exitcode.
 417   */
 418  function print_sequential_output($processes, $showprefix = true) {
 419      $status = false;
 420      foreach ($processes as $name => $process) {
 421          $shownname = false;
 422          while ($process->isRunning()) {
 423              $op = $process->getIncrementalOutput();
 424              if (trim($op)) {
 425                  // Show name of the run once for sequential.
 426                  if ($showprefix && !$shownname) {
 427                      echo '[' . $name . '] ';
 428                      $shownname = true;
 429                  }
 430                  echo $op;
 431              }
 432          }
 433          // If any error then exit.
 434          $exitcode = $process->getExitCode();
 435          if ($exitcode != 0) {
 436              exit($exitcode);
 437          }
 438          $status = $status || (bool)$exitcode;
 439      }
 440      return $status;
 441  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1