Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • Differences Between: [Versions 28 and 29] [Versions 28 and 30] [Versions 28 and 31] [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

       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__ . '/../lib.php');
      29  require_once (__DIR__ . '/behat_command.php');
      30  require_once (__DIR__ . '/../../testing/classes/tests_finder.php');
      31  
      32  /**
      33   * Behat configuration manager
      34   *
      35   * Creates/updates Behat config files getting tests
      36   * and steps from Moodle codebase
      37   *
      38   * @package    core
      39   * @category   test
      40   * @copyright  2012 David MonllaĆ³
      41   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      42   */
      43  class behat_config_manager {
      44  
      45      /**
      46       * Updates a config file
      47       *
      48       * The tests runner and the steps definitions list uses different
      49       * config files to avoid problems with concurrent executions.
      50       *
      51       * The steps definitions list can be filtered by component so it's
      52       * behat.yml is different from the $CFG->dirroot one.
      53       *
      54       * @param  string $component Restricts the obtained steps definitions to the specified component
      55       * @param  string $testsrunner If the config file will be used to run tests
      56       * @return void
      57       */
      58      public static function update_config_file($component = '', $testsrunner = true) {
      59          global $CFG;
      60  
      61          // Behat must have a separate behat.yml to have access to the whole set of features and steps definitions.
      62          if ($testsrunner === true) {
      63              $configfilepath = behat_command::get_behat_dir() . '/behat.yml';
      64          } else {
      65              // Alternative for steps definitions filtering, one for each user.
      66              $configfilepath = self::get_steps_list_config_filepath();
      67          }
      68  
      69          // Gets all the components with features.
      70          $features = array();
      71          $components = tests_finder::get_components_with_tests('features');
      72          if ($components) {
      73              foreach ($components as $componentname => $path) {
      74                  $path = self::clean_path($path) . self::get_behat_tests_path();
      75                  if (empty($featurespaths[$path]) && file_exists($path)) {
      76  
      77                      // Standarizes separator (some dirs. comes with OS-dependant separator).
      78                      $uniquekey = str_replace('\\', '/', $path);
      79                      $featurespaths[$uniquekey] = $path;
      80                  }
      81              }
      82              $features = array_values($featurespaths);
      83          }
      84  
      85          // Optionally include features from additional directories.
      86          if (!empty($CFG->behat_additionalfeatures)) {
      87              $features = array_merge($features, array_map("realpath", $CFG->behat_additionalfeatures));
      88          }
      89  
      90          // Gets all the components with steps definitions.
      91          $stepsdefinitions = array();
      92          $steps = self::get_components_steps_definitions();
      93          if ($steps) {
      94              foreach ($steps as $key => $filepath) {
      95                  if ($component == '' || $component === $key) {
      96                      $stepsdefinitions[$key] = $filepath;
      97                  }
      98              }
      99          }
     100  
     101          // We don't want the deprecated steps definitions here.
     102          if (!$testsrunner) {
     103              unset($stepsdefinitions['behat_deprecated']);
     104          }
     105  
     106          // Behat config file specifing the main context class,
     107          // the required Behat extensions and Moodle test wwwroot.
     108          $contents = self::get_config_file_contents($features, $stepsdefinitions);
     109  
     110          // Stores the file.
     111          if (!file_put_contents($configfilepath, $contents)) {
     112              behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $configfilepath . ' can not be created');
     113          }
     114  
     115      }
     116  
     117      /**
     118       * Gets the list of Moodle steps definitions
     119       *
     120       * Class name as a key and the filepath as value
     121       *
     122       * Externalized from update_config_file() to use
     123       * it from the steps definitions web interface
     124       *
     125       * @return array
     126       */
     127      public static function get_components_steps_definitions() {
     128  
     129          $components = tests_finder::get_components_with_tests('stepsdefinitions');
     130          if (!$components) {
     131              return false;
     132          }
     133  
     134          $stepsdefinitions = array();
     135          foreach ($components as $componentname => $componentpath) {
     136              $componentpath = self::clean_path($componentpath);
     137  
     138              if (!file_exists($componentpath . self::get_behat_tests_path())) {
     139                  continue;
     140              }
     141              $diriterator = new DirectoryIterator($componentpath . self::get_behat_tests_path());
     142              $regite = new RegexIterator($diriterator, '|behat_.*\.php$|');
     143  
     144              // All behat_*.php inside behat_config_manager::get_behat_tests_path() are added as steps definitions files.
     145              foreach ($regite as $file) {
     146                  $key = $file->getBasename('.php');
     147                  $stepsdefinitions[$key] = $file->getPathname();
     148              }
     149          }
     150  
     151          return $stepsdefinitions;
     152      }
     153  
     154      /**
     155       * Returns the behat config file path used by the steps definition list
     156       *
     157       * @return string
     158       */
     159      public static function get_steps_list_config_filepath() {
     160          global $USER;
     161  
     162          // We don't cygwin-it as it is called using exec() which uses cmd.exe.
     163          $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id;
     164          make_writable_directory($userdir);
     165  
     166          return $userdir . '/behat.yml';
     167      }
     168  
     169      /**
     170       * Returns the behat config file path used by the behat cli command.
     171       *
     172       * @return string
     173       */
     174      public static function get_behat_cli_config_filepath() {
     175          global $CFG;
     176  
     177          $command = $CFG->behat_dataroot . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
     178  
     179          // Cygwin uses linux-style directory separators.
     180          if (testing_is_cygwin()) {
     181              $command = str_replace('\\', '/', $command);
     182          }
     183  
     184          return $command;
     185      }
     186  
     187      /**
     188       * Behat config file specifing the main context class,
     189       * the required Behat extensions and Moodle test wwwroot.
     190       *
     191       * @param array $features The system feature files
     192       * @param array $stepsdefinitions The system steps definitions
     193       * @return string
     194       */
     195      protected static function get_config_file_contents($features, $stepsdefinitions) {
     196          global $CFG;
     197  
     198          // We require here when we are sure behat dependencies are available.
     199          require_once($CFG->dirroot . '/vendor/autoload.php');
     200  
     201          // It is possible that it has no value as we don't require a full behat setup to list the step definitions.
     202          if (empty($CFG->behat_wwwroot)) {
     203              $CFG->behat_wwwroot = 'http://itwillnotbeused.com';
     204          }
     205  
     206          $basedir = $CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat';
     207          $config = array(
     208              'default' => array(
     209                  'paths' => array(
     210                      'features' => $basedir . DIRECTORY_SEPARATOR . 'features',
     211                      'bootstrap' => $basedir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'bootstrap',
     212                  ),
     213                  'context' => array(
     214                      'class' => 'behat_init_context'
     215                  ),
     216                  'extensions' => array(
     217                      'Behat\MinkExtension\Extension' => array(
     218                          'base_url' => $CFG->behat_wwwroot,
     219                          'goutte' => null,
     220                          'selenium2' => null
     221                      ),
     222                      'Moodle\BehatExtension\Extension' => array(
     223                          'formatters' => array(
     224                              'moodle_progress' => 'Moodle\BehatExtension\Formatter\MoodleProgressFormatter',
     225                              'moodle_list' => 'Moodle\BehatExtension\Formatter\MoodleListFormatter'
     226                          ),
     227                          'features' => $features,
     228                          'steps_definitions' => $stepsdefinitions
     229                      )
     230                  ),
     231                  'formatter' => array(
     232                      'name' => 'moodle_progress'
     233                  )
     234              )
     235          );
     236  
     237          // In case user defined overrides respect them over our default ones.
     238          if (!empty($CFG->behat_config)) {
     239              $config = self::merge_config($config, $CFG->behat_config);
     240          }
     241  
     242          return Symfony\Component\Yaml\Yaml::dump($config, 10, 2);
     243      }
     244  
     245      /**
     246       * Overrides default config with local config values
     247       *
     248       * array_merge does not merge completely the array's values
     249       *
     250       * @param mixed $config The node of the default config
     251       * @param mixed $localconfig The node of the local config
     252       * @return mixed The merge result
     253       */
     254      protected static function merge_config($config, $localconfig) {
     255  
     256          if (!is_array($config) && !is_array($localconfig)) {
     257              return $localconfig;
     258          }
     259  
     260          // Local overrides also deeper default values.
     261          if (is_array($config) && !is_array($localconfig)) {
     262              return $localconfig;
     263          }
     264  
     265          foreach ($localconfig as $key => $value) {
     266  
     267              // If defaults are not as deep as local values let locals override.
     268              if (!is_array($config)) {
     269                  unset($config);
     270              }
     271  
     272              // Add the param if it doesn't exists or merge branches.
     273              if (empty($config[$key])) {
     274                  $config[$key] = $value;
     275              } else {
     276                  $config[$key] = self::merge_config($config[$key], $localconfig[$key]);
     277              }
     278          }
     279  
     280          return $config;
     281      }
     282  
     283      /**
     284       * Cleans the path returned by get_components_with_tests() to standarize it
     285       *
     286       * @see tests_finder::get_all_directories_with_tests() it returns the path including /tests/
     287       * @param string $path
     288       * @return string The string without the last /tests part
     289       */
     290      protected final static function clean_path($path) {
     291  
     292          $path = rtrim($path, DIRECTORY_SEPARATOR);
     293  
     294          $parttoremove = DIRECTORY_SEPARATOR . 'tests';
     295  
     296          $substr = substr($path, strlen($path) - strlen($parttoremove));
     297          if ($substr == $parttoremove) {
     298              $path = substr($path, 0, strlen($path) - strlen($parttoremove));
     299          }
     300  
     301          return rtrim($path, DIRECTORY_SEPARATOR);
     302      }
     303  
     304      /**
     305       * The relative path where components stores their behat tests
     306       *
     307       * @return string
     308       */
     309      protected final static function get_behat_tests_path() {
     310          return DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'behat';
     311      }
     312  
     313  }
    

    Search This Site: