[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/tests/behat/ -> behat_data_generators.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   * Data generators for acceptance testing.
  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  // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
  27  
  28  require_once (__DIR__ . '/../../behat/behat_base.php');
  29  
  30  use Behat\Gherkin\Node\TableNode as TableNode;
  31  use Behat\Behat\Exception\PendingException as PendingException;
  32  
  33  /**
  34   * Class to set up quickly a Given environment.
  35   *
  36   * Acceptance tests are block-boxed, so this steps definitions should only
  37   * be used to set up the test environment as we are not replicating user steps.
  38   *
  39   * All data generators should be in lib/testing/generator/*, shared between phpunit
  40   * and behat and they should be called from here, if possible using the standard
  41   * 'create_$elementname($options)' and if it's not possible (data generators arguments will not be
  42   * always the same) or the element is not suitable to be a data generator, create a
  43   * 'process_$elementname($options)' method and use the data generator from there if possible.
  44   *
  45   * @todo      If the available elements list grows too much this class must be split into smaller pieces
  46   * @package   core
  47   * @category  test
  48   * @copyright 2012 David Monllaó
  49   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  50   */
  51  class behat_data_generators extends behat_base {
  52  
  53      /**
  54       * @var testing_data_generator
  55       */
  56      protected $datagenerator;
  57  
  58      /**
  59       * Each element specifies:
  60       * - The data generator sufix used.
  61       * - The required fields.
  62       * - The mapping between other elements references and database field names.
  63       * @var array
  64       */
  65      protected static $elements = array(
  66          'users' => array(
  67              'datagenerator' => 'user',
  68              'required' => array('username')
  69          ),
  70          'categories' => array(
  71              'datagenerator' => 'category',
  72              'required' => array('idnumber'),
  73              'switchids' => array('category' => 'parent')
  74          ),
  75          'courses' => array(
  76              'datagenerator' => 'course',
  77              'required' => array('shortname'),
  78              'switchids' => array('category' => 'category')
  79          ),
  80          'groups' => array(
  81              'datagenerator' => 'group',
  82              'required' => array('idnumber', 'course'),
  83              'switchids' => array('course' => 'courseid')
  84          ),
  85          'groupings' => array(
  86              'datagenerator' => 'grouping',
  87              'required' => array('idnumber', 'course'),
  88              'switchids' => array('course' => 'courseid')
  89          ),
  90          'course enrolments' => array(
  91              'datagenerator' => 'enrol_user',
  92              'required' => array('user', 'course', 'role'),
  93              'switchids' => array('user' => 'userid', 'course' => 'courseid', 'role' => 'roleid')
  94  
  95          ),
  96          'permission overrides' => array(
  97              'datagenerator' => 'permission_override',
  98              'required' => array('capability', 'permission', 'role', 'contextlevel', 'reference'),
  99              'switchids' => array('role' => 'roleid')
 100          ),
 101          'system role assigns' => array(
 102              'datagenerator' => 'system_role_assign',
 103              'required' => array('user', 'role'),
 104              'switchids' => array('user' => 'userid', 'role' => 'roleid')
 105          ),
 106          'role assigns' => array(
 107              'datagenerator' => 'role_assign',
 108              'required' => array('user', 'role', 'contextlevel', 'reference'),
 109              'switchids' => array('user' => 'userid', 'role' => 'roleid')
 110          ),
 111          'activities' => array(
 112              'datagenerator' => 'activity',
 113              'required' => array('activity', 'idnumber', 'course'),
 114              'switchids' => array('course' => 'course', 'gradecategory' => 'gradecat')
 115          ),
 116          'group members' => array(
 117              'datagenerator' => 'group_member',
 118              'required' => array('user', 'group'),
 119              'switchids' => array('user' => 'userid', 'group' => 'groupid')
 120          ),
 121          'grouping groups' => array(
 122              'datagenerator' => 'grouping_group',
 123              'required' => array('grouping', 'group'),
 124              'switchids' => array('grouping' => 'groupingid', 'group' => 'groupid')
 125          ),
 126          'cohorts' => array(
 127              'datagenerator' => 'cohort',
 128              'required' => array('idnumber')
 129          ),
 130          'cohort members' => array(
 131              'datagenerator' => 'cohort_member',
 132              'required' => array('user', 'cohort'),
 133              'switchids' => array('user' => 'userid', 'cohort' => 'cohortid')
 134          ),
 135          'roles' => array(
 136              'datagenerator' => 'role',
 137              'required' => array('shortname')
 138          ),
 139          'grade categories' => array(
 140              'datagenerator' => 'grade_category',
 141              'required' => array('fullname', 'course'),
 142              'switchids' => array('course' => 'courseid', 'gradecategory' => 'parent')
 143          ),
 144          'grade items' => array(
 145              'datagenerator' => 'grade_item',
 146              'required' => array('course'),
 147              'switchids' => array('scale' => 'scaleid', 'outcome' => 'outcomeid', 'course' => 'courseid',
 148                                   'gradecategory' => 'categoryid')
 149          ),
 150          'grade outcomes' => array(
 151              'datagenerator' => 'grade_outcome',
 152              'required' => array('shortname', 'scale'),
 153              'switchids' => array('course' => 'courseid', 'gradecategory' => 'categoryid', 'scale' => 'scaleid')
 154          ),
 155          'scales' => array(
 156              'datagenerator' => 'scale',
 157              'required' => array('name', 'scale'),
 158              'switchids' => array('course' => 'courseid')
 159          )
 160      );
 161  
 162      /**
 163       * Creates the specified element. More info about available elements in http://docs.moodle.org/dev/Acceptance_testing#Fixtures.
 164       *
 165       * @Given /^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/
 166       *
 167       * @throws Exception
 168       * @throws PendingException
 169       * @param string    $elementname The name of the entity to add
 170       * @param TableNode $data
 171       */
 172      public function the_following_exist($elementname, TableNode $data) {
 173  
 174          // Now that we need them require the data generators.
 175          require_once (__DIR__ . '/../../testing/generator/lib.php');
 176  
 177          if (empty(self::$elements[$elementname])) {
 178              throw new PendingException($elementname . ' data generator is not implemented');
 179          }
 180  
 181          $this->datagenerator = testing_util::get_data_generator();
 182  
 183          $elementdatagenerator = self::$elements[$elementname]['datagenerator'];
 184          $requiredfields = self::$elements[$elementname]['required'];
 185          if (!empty(self::$elements[$elementname]['switchids'])) {
 186              $switchids = self::$elements[$elementname]['switchids'];
 187          }
 188  
 189          foreach ($data->getHash() as $elementdata) {
 190  
 191              // Check if all the required fields are there.
 192              foreach ($requiredfields as $requiredfield) {
 193                  if (!isset($elementdata[$requiredfield])) {
 194                      throw new Exception($elementname . ' requires the field ' . $requiredfield . ' to be specified');
 195                  }
 196              }
 197  
 198              // Switch from human-friendly references to ids.
 199              if (isset($switchids)) {
 200                  foreach ($switchids as $element => $field) {
 201                      $methodname = 'get_' . $element . '_id';
 202  
 203                      // Not all the switch fields are required, default vars will be assigned by data generators.
 204                      if (isset($elementdata[$element])) {
 205                          // Temp $id var to avoid problems when $element == $field.
 206                          $id = $this->{$methodname}($elementdata[$element]);
 207                          unset($elementdata[$element]);
 208                          $elementdata[$field] = $id;
 209                      }
 210                  }
 211              }
 212  
 213              // Preprocess the entities that requires a special treatment.
 214              if (method_exists($this, 'preprocess_' . $elementdatagenerator)) {
 215                  $elementdata = $this->{'preprocess_' . $elementdatagenerator}($elementdata);
 216              }
 217  
 218              // Creates element.
 219              $methodname = 'create_' . $elementdatagenerator;
 220              if (method_exists($this->datagenerator, $methodname)) {
 221                  // Using data generators directly.
 222                  $this->datagenerator->{$methodname}($elementdata);
 223  
 224              } else if (method_exists($this, 'process_' . $elementdatagenerator)) {
 225                  // Using an alternative to the direct data generator call.
 226                  $this->{'process_' . $elementdatagenerator}($elementdata);
 227              } else {
 228                  throw new PendingException($elementname . ' data generator is not implemented');
 229              }
 230          }
 231  
 232      }
 233  
 234      /**
 235       * If password is not set it uses the username.
 236       * @param array $data
 237       * @return array
 238       */
 239      protected function preprocess_user($data) {
 240          if (!isset($data['password'])) {
 241              $data['password'] = $data['username'];
 242          }
 243          return $data;
 244      }
 245  
 246      /**
 247       * If contextlevel and reference are specified for cohort, transform them to the contextid.
 248       *
 249       * @param array $data
 250       * @return array
 251       */
 252      protected function preprocess_cohort($data) {
 253          if (isset($data['contextlevel'])) {
 254              if (!isset($data['reference'])) {
 255                  throw new Exception('If field contextlevel is specified, field reference must also be present');
 256              }
 257              $context = $this->get_context($data['contextlevel'], $data['reference']);
 258              unset($data['contextlevel']);
 259              unset($data['reference']);
 260              $data['contextid'] = $context->id;
 261          }
 262          return $data;
 263      }
 264  
 265      /**
 266       * Preprocesses the creation of a grade item. Converts gradetype text to a number.
 267       * @param array $data
 268       * @return array
 269       */
 270      protected function preprocess_grade_item($data) {
 271          global $CFG;
 272          require_once("$CFG->libdir/grade/constants.php");
 273  
 274          if (isset($data['gradetype'])) {
 275              $data['gradetype'] = constant("GRADE_TYPE_" . strtoupper($data['gradetype']));
 276          }
 277          return $data;
 278      }
 279  
 280      /**
 281       * Adapter to modules generator
 282       * @throws Exception Custom exception for test writers
 283       * @param array $data
 284       * @return void
 285       */
 286      protected function process_activity($data) {
 287          global $DB, $CFG;
 288  
 289          // The the_following_exists() method checks that the field exists.
 290          $activityname = $data['activity'];
 291          unset($data['activity']);
 292  
 293          // Convert scale name into scale id (negative number indicates using scale).
 294          if (isset($data['grade']) && strlen($data['grade']) && !is_number($data['grade'])) {
 295              $data['grade'] = - $this->get_scale_id($data['grade']);
 296              require_once("$CFG->libdir/grade/constants.php");
 297  
 298              if (!isset($data['gradetype'])) {
 299                  $data['gradetype'] = GRADE_TYPE_SCALE;
 300              }
 301          }
 302  
 303          // We split $data in the activity $record and the course module $options.
 304          $cmoptions = array();
 305          $cmcolumns = $DB->get_columns('course_modules');
 306          foreach ($cmcolumns as $key => $value) {
 307              if (isset($data[$key])) {
 308                  $cmoptions[$key] = $data[$key];
 309              }
 310          }
 311  
 312          // Custom exception.
 313          try {
 314              $this->datagenerator->create_module($activityname, $data, $cmoptions);
 315          } catch (coding_exception $e) {
 316              throw new Exception('\'' . $activityname . '\' activity can not be added using this step,' .
 317                  ' use the step \'I add a "ACTIVITY_OR_RESOURCE_NAME_STRING" to section "SECTION_NUMBER"\' instead');
 318          }
 319      }
 320  
 321      /**
 322       * Adapter to enrol_user() data generator.
 323       * @throws Exception
 324       * @param array $data
 325       * @return void
 326       */
 327      protected function process_enrol_user($data) {
 328          global $SITE;
 329  
 330          if (empty($data['roleid'])) {
 331              throw new Exception('\'course enrolments\' requires the field \'role\' to be specified');
 332          }
 333  
 334          if (!isset($data['userid'])) {
 335              throw new Exception('\'course enrolments\' requires the field \'user\' to be specified');
 336          }
 337  
 338          if (!isset($data['courseid'])) {
 339              throw new Exception('\'course enrolments\' requires the field \'course\' to be specified');
 340          }
 341  
 342          if (!isset($data['enrol'])) {
 343              $data['enrol'] = 'manual';
 344          }
 345  
 346          // If the provided course shortname is the site shortname we consider it a system role assign.
 347          if ($data['courseid'] == $SITE->id) {
 348              // Frontpage course assign.
 349              $context = context_course::instance($data['courseid']);
 350              role_assign($data['roleid'], $data['userid'], $context->id);
 351  
 352          } else {
 353              // Course assign.
 354              $this->datagenerator->enrol_user($data['userid'], $data['courseid'], $data['roleid'], $data['enrol']);
 355          }
 356  
 357      }
 358  
 359      /**
 360       * Allows/denies a capability at the specified context
 361       *
 362       * @throws Exception
 363       * @param array $data
 364       * @return void
 365       */
 366      protected function process_permission_override($data) {
 367  
 368          // Will throw an exception if it does not exist.
 369          $context = $this->get_context($data['contextlevel'], $data['reference']);
 370  
 371          switch ($data['permission']) {
 372              case get_string('allow', 'role'):
 373                  $permission = CAP_ALLOW;
 374                  break;
 375              case get_string('prevent', 'role'):
 376                  $permission = CAP_PREVENT;
 377                  break;
 378              case get_string('prohibit', 'role'):
 379                  $permission = CAP_PROHIBIT;
 380                  break;
 381              default:
 382                  throw new Exception('The \'' . $data['permission'] . '\' permission does not exist');
 383                  break;
 384          }
 385  
 386          if (is_null(get_capability_info($data['capability']))) {
 387              throw new Exception('The \'' . $data['capability'] . '\' capability does not exist');
 388          }
 389  
 390          role_change_permission($data['roleid'], $context, $data['capability'], $permission);
 391      }
 392  
 393      /**
 394       * Assigns a role to a user at system context
 395       *
 396       * Used by "system role assigns" can be deleted when
 397       * system role assign will be deprecated in favour of
 398       * "role assigns"
 399       *
 400       * @throws Exception
 401       * @param array $data
 402       * @return void
 403       */
 404      protected function process_system_role_assign($data) {
 405  
 406          if (empty($data['roleid'])) {
 407              throw new Exception('\'system role assigns\' requires the field \'role\' to be specified');
 408          }
 409  
 410          if (!isset($data['userid'])) {
 411              throw new Exception('\'system role assigns\' requires the field \'user\' to be specified');
 412          }
 413  
 414          $context = context_system::instance();
 415  
 416          $this->datagenerator->role_assign($data['roleid'], $data['userid'], $context->id);
 417      }
 418  
 419      /**
 420       * Assigns a role to a user at the specified context
 421       *
 422       * @throws Exception
 423       * @param array $data
 424       * @return void
 425       */
 426      protected function process_role_assign($data) {
 427  
 428          if (empty($data['roleid'])) {
 429              throw new Exception('\'role assigns\' requires the field \'role\' to be specified');
 430          }
 431  
 432          if (!isset($data['userid'])) {
 433              throw new Exception('\'role assigns\' requires the field \'user\' to be specified');
 434          }
 435  
 436          if (empty($data['contextlevel'])) {
 437              throw new Exception('\'role assigns\' requires the field \'contextlevel\' to be specified');
 438          }
 439  
 440          if (!isset($data['reference'])) {
 441              throw new Exception('\'role assigns\' requires the field \'reference\' to be specified');
 442          }
 443  
 444          // Getting the context id.
 445          $context = $this->get_context($data['contextlevel'], $data['reference']);
 446  
 447          $this->datagenerator->role_assign($data['roleid'], $data['userid'], $context->id);
 448      }
 449  
 450      /**
 451       * Creates a role.
 452       *
 453       * @param array $data
 454       * @return void
 455       */
 456      protected function process_role($data) {
 457  
 458          // We require the user to fill the role shortname.
 459          if (empty($data['shortname'])) {
 460              throw new Exception('\'role\' requires the field \'shortname\' to be specified');
 461          }
 462  
 463          $this->datagenerator->create_role($data);
 464      }
 465  
 466      /**
 467       * Adds members to cohorts
 468       *
 469       * @param array $data
 470       * @return void
 471       */
 472      protected function process_cohort_member($data) {
 473          cohort_add_member($data['cohortid'], $data['userid']);
 474      }
 475  
 476      /**
 477       * Gets the grade category id from the grade category fullname
 478       * @throws Exception
 479       * @param string $username
 480       * @return int
 481       */
 482      protected function get_gradecategory_id($fullname) {
 483          global $DB;
 484  
 485          if (!$id = $DB->get_field('grade_categories', 'id', array('fullname' => $fullname))) {
 486              throw new Exception('The specified grade category with fullname "' . $fullname . '" does not exist');
 487          }
 488          return $id;
 489      }
 490  
 491      /**
 492       * Gets the user id from it's username.
 493       * @throws Exception
 494       * @param string $username
 495       * @return int
 496       */
 497      protected function get_user_id($username) {
 498          global $DB;
 499  
 500          if (!$id = $DB->get_field('user', 'id', array('username' => $username))) {
 501              throw new Exception('The specified user with username "' . $username . '" does not exist');
 502          }
 503          return $id;
 504      }
 505  
 506      /**
 507       * Gets the role id from it's shortname.
 508       * @throws Exception
 509       * @param string $roleshortname
 510       * @return int
 511       */
 512      protected function get_role_id($roleshortname) {
 513          global $DB;
 514  
 515          if (!$id = $DB->get_field('role', 'id', array('shortname' => $roleshortname))) {
 516              throw new Exception('The specified role with shortname "' . $roleshortname . '" does not exist');
 517          }
 518  
 519          return $id;
 520      }
 521  
 522      /**
 523       * Gets the category id from it's idnumber.
 524       * @throws Exception
 525       * @param string $idnumber
 526       * @return int
 527       */
 528      protected function get_category_id($idnumber) {
 529          global $DB;
 530  
 531          // If no category was specified use the data generator one.
 532          if ($idnumber == false) {
 533              return null;
 534          }
 535  
 536          if (!$id = $DB->get_field('course_categories', 'id', array('idnumber' => $idnumber))) {
 537              throw new Exception('The specified category with idnumber "' . $idnumber . '" does not exist');
 538          }
 539  
 540          return $id;
 541      }
 542  
 543      /**
 544       * Gets the course id from it's shortname.
 545       * @throws Exception
 546       * @param string $shortname
 547       * @return int
 548       */
 549      protected function get_course_id($shortname) {
 550          global $DB;
 551  
 552          if (!$id = $DB->get_field('course', 'id', array('shortname' => $shortname))) {
 553              throw new Exception('The specified course with shortname "' . $shortname . '" does not exist');
 554          }
 555          return $id;
 556      }
 557  
 558      /**
 559       * Gets the group id from it's idnumber.
 560       * @throws Exception
 561       * @param string $idnumber
 562       * @return int
 563       */
 564      protected function get_group_id($idnumber) {
 565          global $DB;
 566  
 567          if (!$id = $DB->get_field('groups', 'id', array('idnumber' => $idnumber))) {
 568              throw new Exception('The specified group with idnumber "' . $idnumber . '" does not exist');
 569          }
 570          return $id;
 571      }
 572  
 573      /**
 574       * Gets the grouping id from it's idnumber.
 575       * @throws Exception
 576       * @param string $idnumber
 577       * @return int
 578       */
 579      protected function get_grouping_id($idnumber) {
 580          global $DB;
 581  
 582          if (!$id = $DB->get_field('groupings', 'id', array('idnumber' => $idnumber))) {
 583              throw new Exception('The specified grouping with idnumber "' . $idnumber . '" does not exist');
 584          }
 585          return $id;
 586      }
 587  
 588      /**
 589       * Gets the cohort id from it's idnumber.
 590       * @throws Exception
 591       * @param string $idnumber
 592       * @return int
 593       */
 594      protected function get_cohort_id($idnumber) {
 595          global $DB;
 596  
 597          if (!$id = $DB->get_field('cohort', 'id', array('idnumber' => $idnumber))) {
 598              throw new Exception('The specified cohort with idnumber "' . $idnumber . '" does not exist');
 599          }
 600          return $id;
 601      }
 602  
 603      /**
 604       * Gets the outcome item id from its shortname.
 605       * @throws Exception
 606       * @param string $shortname
 607       * @return int
 608       */
 609      protected function get_outcome_id($shortname) {
 610          global $DB;
 611  
 612          if (!$id = $DB->get_field('grade_outcomes', 'id', array('shortname' => $shortname))) {
 613              throw new Exception('The specified outcome with shortname "' . $shortname . '" does not exist');
 614          }
 615          return $id;
 616      }
 617  
 618      /**
 619       * Gets the course id from its name.
 620       * @throws Exception
 621       * @param string $name
 622       * @return int
 623       */
 624      protected function get_scale_id($name) {
 625          global $DB;
 626  
 627          if (!$id = $DB->get_field('scale', 'id', array('name' => $name))) {
 628              throw new Exception('The specified scale with name "' . $name . '" does not exist');
 629          }
 630          return $id;
 631      }
 632  
 633      /**
 634       * Gets the internal context id from the context reference.
 635       *
 636       * The context reference changes depending on the context
 637       * level, it can be the system, a user, a category, a course or
 638       * a module.
 639       *
 640       * @throws Exception
 641       * @param string $levelname The context level string introduced by the test writer
 642       * @param string $contextref The context reference introduced by the test writer
 643       * @return context
 644       */
 645      protected function get_context($levelname, $contextref) {
 646          global $DB;
 647  
 648          // Getting context levels and names (we will be using the English ones as it is the test site language).
 649          $contextlevels = context_helper::get_all_levels();
 650          $contextnames = array();
 651          foreach ($contextlevels as $level => $classname) {
 652              $contextnames[context_helper::get_level_name($level)] = $level;
 653          }
 654  
 655          if (empty($contextnames[$levelname])) {
 656              throw new Exception('The specified "' . $levelname . '" context level does not exist');
 657          }
 658          $contextlevel = $contextnames[$levelname];
 659  
 660          // Return it, we don't need to look for other internal ids.
 661          if ($contextlevel == CONTEXT_SYSTEM) {
 662              return context_system::instance();
 663          }
 664  
 665          switch ($contextlevel) {
 666  
 667              case CONTEXT_USER:
 668                  $instanceid = $DB->get_field('user', 'id', array('username' => $contextref));
 669                  break;
 670  
 671              case CONTEXT_COURSECAT:
 672                  $instanceid = $DB->get_field('course_categories', 'id', array('idnumber' => $contextref));
 673                  break;
 674  
 675              case CONTEXT_COURSE:
 676                  $instanceid = $DB->get_field('course', 'id', array('shortname' => $contextref));
 677                  break;
 678  
 679              case CONTEXT_MODULE:
 680                  $instanceid = $DB->get_field('course_modules', 'id', array('idnumber' => $contextref));
 681                  break;
 682  
 683              default:
 684                  break;
 685          }
 686  
 687          $contextclass = $contextlevels[$contextlevel];
 688          if (!$context = $contextclass::instance($instanceid, IGNORE_MISSING)) {
 689              throw new Exception('The specified "' . $contextref . '" context reference does not exist');
 690          }
 691  
 692          return $context;
 693      }
 694  
 695  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1