[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/behat/ -> lib.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   * Behat basic functions
  19   *
  20   * It does not include MOODLE_INTERNAL because is part of the bootstrap.
  21   *
  22   * This script should not be usually included, neither any of its functions
  23   * used, within mooodle code at all. It's for exclusive use of behat and
  24   * moodle setup.php. For places requiring a different/special behavior
  25   * needing to check if are being run as part of behat tests, use:
  26   *     if (defined('BEHAT_SITE_RUNNING')) { ...
  27   *
  28   * @package    core
  29   * @category   test
  30   * @copyright  2012 David Monllaó
  31   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  
  34  require_once (__DIR__ . '/../testing/lib.php');
  35  
  36  define('BEHAT_EXITCODE_CONFIG', 250);
  37  define('BEHAT_EXITCODE_REQUIREMENT', 251);
  38  define('BEHAT_EXITCODE_PERMISSIONS', 252);
  39  define('BEHAT_EXITCODE_REINSTALL', 253);
  40  define('BEHAT_EXITCODE_INSTALL', 254);
  41  define('BEHAT_EXITCODE_INSTALLED', 256);
  42  
  43  /**
  44   * The behat test site fullname and shortname.
  45   */
  46  define('BEHAT_PARALLEL_SITE_NAME', "behatrun");
  47  
  48  /**
  49   * Exits with an error code
  50   *
  51   * @param  mixed $errorcode
  52   * @param  string $text
  53   * @return void Stops execution with error code
  54   */
  55  function behat_error($errorcode, $text = '') {
  56  
  57      // Adding error prefixes.
  58      switch ($errorcode) {
  59          case BEHAT_EXITCODE_CONFIG:
  60              $text = 'Behat config error: ' . $text;
  61              break;
  62          case BEHAT_EXITCODE_REQUIREMENT:
  63              $text = 'Behat requirement not satisfied: ' . $text;
  64              break;
  65          case BEHAT_EXITCODE_PERMISSIONS:
  66              $text = 'Behat permissions problem: ' . $text . ', check the permissions';
  67              break;
  68          case BEHAT_EXITCODE_REINSTALL:
  69              $path = testing_cli_argument_path('/admin/tool/behat/cli/init.php');
  70              $text = "Reinstall Behat: ".$text.", use:\n php ".$path;
  71              break;
  72          case BEHAT_EXITCODE_INSTALL:
  73              $path = testing_cli_argument_path('/admin/tool/behat/cli/init.php');
  74              $text = "Install Behat before enabling it, use:\n php ".$path;
  75              break;
  76          case BEHAT_EXITCODE_INSTALLED:
  77              $text = "The Behat site is already installed";
  78              break;
  79          default:
  80              $text = 'Unknown error ' . $errorcode . ' ' . $text;
  81              break;
  82      }
  83  
  84      testing_error($errorcode, $text);
  85  }
  86  
  87  /**
  88   * PHP errors handler to use when running behat tests.
  89   *
  90   * Adds specific CSS classes to identify
  91   * the messages.
  92   *
  93   * @param int $errno
  94   * @param string $errstr
  95   * @param string $errfile
  96   * @param int $errline
  97   * @param array $errcontext
  98   * @return bool
  99   */
 100  function behat_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
 101  
 102      // If is preceded by an @ we don't show it.
 103      if (!error_reporting()) {
 104          return true;
 105      }
 106  
 107      // This error handler receives E_ALL | E_STRICT, running the behat test site the debug level is
 108      // set to DEVELOPER and will always include E_NOTICE,E_USER_NOTICE... as part of E_ALL, if the current
 109      // error_reporting() value does not include one of those levels is because it has been forced through
 110      // the moodle code (see fix_utf8() for example) in that cases we respect the forced error level value.
 111      $respect = array(E_NOTICE, E_USER_NOTICE, E_STRICT, E_WARNING, E_USER_WARNING);
 112      foreach ($respect as $respectable) {
 113  
 114          // If the current value does not include this kind of errors and the reported error is
 115          // at that level don't print anything.
 116          if ($errno == $respectable && !(error_reporting() & $respectable)) {
 117              return true;
 118          }
 119      }
 120  
 121      // Using the default one in case there is a fatal catchable error.
 122      default_error_handler($errno, $errstr, $errfile, $errline, $errcontext);
 123  
 124      switch ($errno) {
 125          case E_USER_ERROR:
 126              $errnostr = 'Fatal error';
 127              break;
 128          case E_WARNING:
 129          case E_USER_WARNING:
 130              $errnostr = 'Warning';
 131              break;
 132          case E_NOTICE:
 133          case E_USER_NOTICE:
 134          case E_STRICT:
 135              $errnostr = 'Notice';
 136              break;
 137          case E_RECOVERABLE_ERROR:
 138              $errnostr = 'Catchable';
 139              break;
 140          default:
 141              $errnostr = 'Unknown error type';
 142      }
 143  
 144      // Wrapping the output.
 145      echo '<div class="phpdebugmessage" data-rel="phpdebugmessage">' . PHP_EOL;
 146      echo "$errnostr: $errstr in $errfile on line $errline" . PHP_EOL;
 147      echo '</div>';
 148  
 149      // Also use the internal error handler so we keep the usual behaviour.
 150      return false;
 151  }
 152  
 153  /**
 154   * Restrict the config.php settings allowed.
 155   *
 156   * When running the behat features the config.php
 157   * settings should not affect the results.
 158   *
 159   * @return void
 160   */
 161  function behat_clean_init_config() {
 162      global $CFG;
 163  
 164      $allowed = array_flip(array(
 165          'wwwroot', 'dataroot', 'dirroot', 'admin', 'directorypermissions', 'filepermissions',
 166          'umaskpermissions', 'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix',
 167          'dboptions', 'proxyhost', 'proxyport', 'proxytype', 'proxyuser', 'proxypassword',
 168          'proxybypass', 'theme', 'pathtogs', 'pathtodu', 'aspellpath', 'pathtodot', 'skiplangupgrade',
 169          'altcacheconfigpath', 'pathtounoconv'
 170      ));
 171  
 172      // Add extra allowed settings.
 173      if (!empty($CFG->behat_extraallowedsettings)) {
 174          $allowed = array_merge($allowed, array_flip($CFG->behat_extraallowedsettings));
 175      }
 176  
 177      // Also allowing behat_ prefixed attributes.
 178      foreach ($CFG as $key => $value) {
 179          if (!isset($allowed[$key]) && strpos($key, 'behat_') !== 0) {
 180              unset($CFG->{$key});
 181          }
 182      }
 183  }
 184  
 185  /**
 186   * Checks that the behat config vars are properly set.
 187   *
 188   * @return void Stops execution with error code if something goes wrong.
 189   */
 190  function behat_check_config_vars() {
 191      global $CFG;
 192  
 193      // Verify prefix value.
 194      if (empty($CFG->behat_prefix)) {
 195          behat_error(BEHAT_EXITCODE_CONFIG,
 196              'Define $CFG->behat_prefix in config.php');
 197      }
 198      if (!empty($CFG->prefix) and $CFG->behat_prefix == $CFG->prefix) {
 199          behat_error(BEHAT_EXITCODE_CONFIG,
 200              '$CFG->behat_prefix in config.php must be different from $CFG->prefix');
 201      }
 202      if (!empty($CFG->phpunit_prefix) and $CFG->behat_prefix == $CFG->phpunit_prefix) {
 203          behat_error(BEHAT_EXITCODE_CONFIG,
 204              '$CFG->behat_prefix in config.php must be different from $CFG->phpunit_prefix');
 205      }
 206  
 207      // Verify behat wwwroot value.
 208      if (empty($CFG->behat_wwwroot)) {
 209          behat_error(BEHAT_EXITCODE_CONFIG,
 210              'Define $CFG->behat_wwwroot in config.php');
 211      }
 212      if (!empty($CFG->wwwroot) and $CFG->behat_wwwroot == $CFG->wwwroot) {
 213          behat_error(BEHAT_EXITCODE_CONFIG,
 214              '$CFG->behat_wwwroot in config.php must be different from $CFG->wwwroot');
 215      }
 216  
 217      // Verify behat dataroot value.
 218      if (empty($CFG->behat_dataroot)) {
 219          behat_error(BEHAT_EXITCODE_CONFIG,
 220              'Define $CFG->behat_dataroot in config.php');
 221      }
 222      clearstatcache();
 223      if (!file_exists($CFG->behat_dataroot)) {
 224          $permissions = isset($CFG->directorypermissions) ? $CFG->directorypermissions : 02777;
 225          umask(0);
 226          if (!mkdir($CFG->behat_dataroot, $permissions, true)) {
 227              behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory can not be created');
 228          }
 229      }
 230      $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
 231      if (empty($CFG->behat_dataroot) or !is_dir($CFG->behat_dataroot) or !is_writable($CFG->behat_dataroot)) {
 232          behat_error(BEHAT_EXITCODE_CONFIG,
 233              '$CFG->behat_dataroot in config.php must point to an existing writable directory');
 234      }
 235      if (!empty($CFG->dataroot) and $CFG->behat_dataroot == realpath($CFG->dataroot)) {
 236          behat_error(BEHAT_EXITCODE_CONFIG,
 237              '$CFG->behat_dataroot in config.php must be different from $CFG->dataroot');
 238      }
 239      if (!empty($CFG->phpunit_dataroot) and $CFG->behat_dataroot == realpath($CFG->phpunit_dataroot)) {
 240          behat_error(BEHAT_EXITCODE_CONFIG,
 241              '$CFG->behat_dataroot in config.php must be different from $CFG->phpunit_dataroot');
 242      }
 243  }
 244  
 245  /**
 246   * Should we switch to the test site data?
 247   * @return bool
 248   */
 249  function behat_is_test_site() {
 250      global $CFG;
 251  
 252      if (defined('BEHAT_UTIL')) {
 253          // This is the admin tool that installs/drops the test site install.
 254          return true;
 255      }
 256      if (defined('BEHAT_TEST')) {
 257          // This is the main vendor/bin/behat script.
 258          return true;
 259      }
 260      if (empty($CFG->behat_wwwroot)) {
 261          return false;
 262      }
 263      if (isset($_SERVER['REMOTE_ADDR']) and behat_is_requested_url($CFG->behat_wwwroot)) {
 264          // Something is accessing the web server like a real browser.
 265          return true;
 266      }
 267  
 268      return false;
 269  }
 270  
 271  /**
 272   * Fix variables for parallel behat testing.
 273   * - behat_wwwroot = behat_wwwroot{behatrunprocess}
 274   * - behat_dataroot = behat_dataroot{behatrunprocess}
 275   * - behat_prefix = behat_prefix.{behatrunprocess}_ (For oracle it will be firstletter of prefix and behatrunprocess)
 276   **/
 277  function behat_update_vars_for_process() {
 278      global $CFG;
 279  
 280      $allowedconfigoverride = array('dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'behat_prefix',
 281          'behat_wwwroot', 'behat_dataroot');
 282      $behatrunprocess = behat_get_run_process();
 283      $CFG->behatrunprocess = $behatrunprocess;
 284  
 285      if ($behatrunprocess) {
 286          if (empty($CFG->behat_parallel_run[$behatrunprocess - 1]['behat_wwwroot'])) {
 287              // Set www root for run process.
 288              if (isset($CFG->behat_wwwroot) &&
 289                  !preg_match("#/" . BEHAT_PARALLEL_SITE_NAME . $behatrunprocess . "\$#", $CFG->behat_wwwroot)) {
 290                  $CFG->behat_wwwroot .= "/" . BEHAT_PARALLEL_SITE_NAME . $behatrunprocess;
 291              }
 292          }
 293  
 294          if (empty($CFG->behat_parallel_run[$behatrunprocess - 1]['behat_dataroot'])) {
 295              // Set behat_dataroot.
 296              if (!preg_match("#" . $behatrunprocess . "\$#", $CFG->behat_dataroot)) {
 297                  $CFG->behat_dataroot .= $behatrunprocess;
 298              }
 299          }
 300  
 301          // Set behat_prefix for db, just suffix run process number, to avoid max length exceed.
 302          // For oracle only 2 letter prefix is possible.
 303          // NOTE: This will not work for parallel process > 9.
 304          if ($CFG->dbtype === 'oci') {
 305              $CFG->behat_prefix = substr($CFG->behat_prefix, 0, 1);
 306              $CFG->behat_prefix .= "{$behatrunprocess}";
 307          } else {
 308              $CFG->behat_prefix .= "{$behatrunprocess}_";
 309          }
 310  
 311          if (!empty($CFG->behat_parallel_run[$behatrunprocess - 1])) {
 312              // Override allowed config vars.
 313              foreach ($allowedconfigoverride as $config) {
 314                  if (isset($CFG->behat_parallel_run[$behatrunprocess - 1][$config])) {
 315                      $CFG->$config = $CFG->behat_parallel_run[$behatrunprocess - 1][$config];
 316                  }
 317              }
 318          }
 319      }
 320  }
 321  
 322  /**
 323   * Checks if the URL requested by the user matches the provided argument
 324   *
 325   * @param string $url
 326   * @return bool Returns true if it matches.
 327   */
 328  function behat_is_requested_url($url) {
 329  
 330      $parsedurl = parse_url($url . '/');
 331      $parsedurl['port'] = isset($parsedurl['port']) ? $parsedurl['port'] : 80;
 332      $parsedurl['path'] = rtrim($parsedurl['path'], '/');
 333  
 334      // Removing the port.
 335      $pos = strpos($_SERVER['HTTP_HOST'], ':');
 336      if ($pos !== false) {
 337          $requestedhost = substr($_SERVER['HTTP_HOST'], 0, $pos);
 338      } else {
 339          $requestedhost = $_SERVER['HTTP_HOST'];
 340      }
 341  
 342      // The path should also match.
 343      if (empty($parsedurl['path'])) {
 344          $matchespath = true;
 345      } else if (strpos($_SERVER['SCRIPT_NAME'], $parsedurl['path']) === 0) {
 346          $matchespath = true;
 347      }
 348  
 349      // The host and the port should match
 350      if ($parsedurl['host'] == $requestedhost && $parsedurl['port'] == $_SERVER['SERVER_PORT'] && !empty($matchespath)) {
 351          return true;
 352      }
 353  
 354      return false;
 355  }
 356  
 357  /**
 358   * Get behat run process from either $_SERVER or command config.
 359   *
 360   * @return bool|int false if single run, else run process number.
 361   */
 362  function behat_get_run_process() {
 363      global $argv, $CFG;
 364      $behatrunprocess = false;
 365  
 366      // Get behat run process, if set.
 367      if (defined('BEHAT_CURRENT_RUN') && BEHAT_CURRENT_RUN) {
 368          $behatrunprocess = BEHAT_CURRENT_RUN;
 369      } else if (!empty($_SERVER['REMOTE_ADDR'])) {
 370          // Try get it from config if present.
 371          if (!empty($CFG->behat_parallel_run)) {
 372              foreach ($CFG->behat_parallel_run as $run => $behatconfig) {
 373                  if (isset($behatconfig['behat_wwwroot']) && behat_is_requested_url($behatconfig['behat_wwwroot'])) {
 374                      $behatrunprocess = $run + 1; // We start process from 1.
 375                      break;
 376                  }
 377              }
 378          }
 379          // Check if parallel site prefix is used.
 380          if (empty($behatrunprocess) && preg_match('#/' . BEHAT_PARALLEL_SITE_NAME . '(.+?)/#', $_SERVER['REQUEST_URI'])) {
 381              $dirrootrealpath = str_replace("\\", "/", realpath($CFG->dirroot));
 382              $serverrealpath = str_replace("\\", "/", realpath($_SERVER['SCRIPT_FILENAME']));
 383              $afterpath = str_replace($dirrootrealpath.'/', '', $serverrealpath);
 384              if (!$behatrunprocess = preg_filter("#.*/" . BEHAT_PARALLEL_SITE_NAME . "(.+?)/$afterpath#", '$1',
 385                  $_SERVER['SCRIPT_FILENAME'])) {
 386                  throw new Exception("Unable to determine behat process [afterpath=" . $afterpath .
 387                      ", scriptfilename=" . $_SERVER['SCRIPT_FILENAME'] . "]!");
 388              }
 389          }
 390      } else if (defined('BEHAT_TEST') || defined('BEHAT_UTIL')) {
 391          if ($match = preg_filter('#--run=(.+)#', '$1', $argv)) {
 392              $behatrunprocess = reset($match);
 393          } else if ($k = array_search('--config', $argv)) {
 394              $behatconfig = str_replace("\\", "/", $argv[$k + 1]);
 395              // Try get it from config if present.
 396              if (!empty($CFG->behat_parallel_run)) {
 397                  foreach ($CFG->behat_parallel_run as $run => $parallelconfig) {
 398                      if (!empty($parallelconfig['behat_dataroot']) &&
 399                          $parallelconfig['behat_dataroot'] . '/behat/behat.yml' == $behatconfig) {
 400  
 401                          $behatrunprocess = $run + 1; // We start process from 1.
 402                          break;
 403                      }
 404                  }
 405              }
 406              // Check if default behat datroot increment was done.
 407              if (empty($behatrunprocess)) {
 408                  $behatdataroot = str_replace("\\", "/", $CFG->behat_dataroot);
 409                  $behatrunprocess = preg_filter("#^{$behatdataroot}" . "(.+?)[/|\\\]behat[/|\\\]behat\.yml#", '$1',
 410                      $behatconfig);
 411              }
 412          }
 413      }
 414  
 415      return $behatrunprocess;
 416  }
 417  
 418  /**
 419   * Execute commands in parallel.
 420   *
 421   * @param array $cmds list of commands to be executed.
 422   * @param string $cwd absolute path of working directory.
 423   * @return array list of processes.
 424   */
 425  function cli_execute_parallel($cmds, $cwd = null) {
 426      require_once(__DIR__ . "/../../vendor/autoload.php");
 427  
 428      $processes = array();
 429  
 430      // Create child process.
 431      foreach ($cmds as $name => $cmd) {
 432          $process = new Symfony\Component\Process\Process($cmd);
 433  
 434          $process->setWorkingDirectory($cwd);
 435          $process->setTimeout(null);
 436          $processes[$name] = $process;
 437          $processes[$name]->start();
 438  
 439          // If error creating process then exit.
 440          if ($processes[$name]->getStatus() !== 'started') {
 441              echo "Error starting process: $name";
 442              foreach ($processes[$name] as $process) {
 443                  if ($process) {
 444                      $process->signal(SIGKILL);
 445                  }
 446              }
 447              exit(1);
 448          }
 449      }
 450      return $processes;
 451  }


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