[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/course/tests/ -> courselib_test.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   * Course related unit tests
  19   *
  20   * @package    core
  21   * @category   phpunit
  22   * @copyright  2012 Petr Skoda {@link http://skodak.org}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->dirroot . '/course/lib.php');
  30  require_once($CFG->dirroot . '/course/tests/fixtures/course_capability_assignment.php');
  31  require_once($CFG->dirroot . '/enrol/imsenterprise/tests/imsenterprise_test.php');
  32  require_once($CFG->dirroot . '/tag/lib.php');
  33  
  34  class core_course_courselib_testcase extends advanced_testcase {
  35  
  36      /**
  37       * Tidy up open files that may be left open.
  38       */
  39      protected function tearDown() {
  40          gc_collect_cycles();
  41      }
  42  
  43      /**
  44       * Set forum specific test values for calling create_module().
  45       *
  46       * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference.
  47       */
  48      private function forum_create_set_values(&$moduleinfo) {
  49          // Completion specific to forum - optional.
  50          $moduleinfo->completionposts = 3;
  51          $moduleinfo->completiondiscussions = 1;
  52          $moduleinfo->completionreplies = 2;
  53  
  54          // Specific values to the Forum module.
  55          $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE;
  56          $moduleinfo->type = 'single';
  57          $moduleinfo->trackingtype = FORUM_TRACKING_FORCED;
  58          $moduleinfo->maxbytes = 10240;
  59          $moduleinfo->maxattachments = 2;
  60  
  61          // Post threshold for blocking - specific to forum.
  62          $moduleinfo->blockperiod = 60*60*24;
  63          $moduleinfo->blockafter = 10;
  64          $moduleinfo->warnafter = 5;
  65      }
  66  
  67      /**
  68       * Execute test asserts on the saved DB data by create_module($forum).
  69       *
  70       * @param object $moduleinfo - the specific forum values that were used to create a forum.
  71       * @param object $dbmodinstance - the DB values of the created forum.
  72       */
  73      private function forum_create_run_asserts($moduleinfo, $dbmodinstance) {
  74          // Compare values specific to forums.
  75          $this->assertEquals($moduleinfo->forcesubscribe, $dbmodinstance->forcesubscribe);
  76          $this->assertEquals($moduleinfo->type, $dbmodinstance->type);
  77          $this->assertEquals($moduleinfo->assessed, $dbmodinstance->assessed);
  78          $this->assertEquals($moduleinfo->completionposts, $dbmodinstance->completionposts);
  79          $this->assertEquals($moduleinfo->completiondiscussions, $dbmodinstance->completiondiscussions);
  80          $this->assertEquals($moduleinfo->completionreplies, $dbmodinstance->completionreplies);
  81          $this->assertEquals($moduleinfo->scale, $dbmodinstance->scale);
  82          $this->assertEquals($moduleinfo->assesstimestart, $dbmodinstance->assesstimestart);
  83          $this->assertEquals($moduleinfo->assesstimefinish, $dbmodinstance->assesstimefinish);
  84          $this->assertEquals($moduleinfo->rsstype, $dbmodinstance->rsstype);
  85          $this->assertEquals($moduleinfo->rssarticles, $dbmodinstance->rssarticles);
  86          $this->assertEquals($moduleinfo->trackingtype, $dbmodinstance->trackingtype);
  87          $this->assertEquals($moduleinfo->maxbytes, $dbmodinstance->maxbytes);
  88          $this->assertEquals($moduleinfo->maxattachments, $dbmodinstance->maxattachments);
  89          $this->assertEquals($moduleinfo->blockperiod, $dbmodinstance->blockperiod);
  90          $this->assertEquals($moduleinfo->blockafter, $dbmodinstance->blockafter);
  91          $this->assertEquals($moduleinfo->warnafter, $dbmodinstance->warnafter);
  92      }
  93  
  94      /**
  95       * Set assign module specific test values for calling create_module().
  96       *
  97       * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference.
  98       */
  99      private function assign_create_set_values(&$moduleinfo) {
 100          // Specific values to the Assign module.
 101          $moduleinfo->alwaysshowdescription = true;
 102          $moduleinfo->submissiondrafts = true;
 103          $moduleinfo->requiresubmissionstatement = true;
 104          $moduleinfo->sendnotifications = true;
 105          $moduleinfo->sendlatenotifications = true;
 106          $moduleinfo->duedate = time() + (7 * 24 * 3600);
 107          $moduleinfo->cutoffdate = time() + (7 * 24 * 3600);
 108          $moduleinfo->allowsubmissionsfromdate = time();
 109          $moduleinfo->teamsubmission = true;
 110          $moduleinfo->requireallteammemberssubmit = true;
 111          $moduleinfo->teamsubmissiongroupingid = true;
 112          $moduleinfo->blindmarking = true;
 113          $moduleinfo->markingworkflow = true;
 114          $moduleinfo->markingallocation = true;
 115          $moduleinfo->assignsubmission_onlinetext_enabled = true;
 116          $moduleinfo->assignsubmission_file_enabled = true;
 117          $moduleinfo->assignsubmission_file_maxfiles = 1;
 118          $moduleinfo->assignsubmission_file_maxsizebytes = 1000000;
 119          $moduleinfo->assignsubmission_comments_enabled = true;
 120          $moduleinfo->assignfeedback_comments_enabled = true;
 121          $moduleinfo->assignfeedback_offline_enabled = true;
 122          $moduleinfo->assignfeedback_file_enabled = true;
 123  
 124          // Advanced grading.
 125          $gradingmethods = grading_manager::available_methods();
 126          $moduleinfo->advancedgradingmethod_submissions = current(array_keys($gradingmethods));
 127      }
 128  
 129      /**
 130       * Execute test asserts on the saved DB data by create_module($assign).
 131       *
 132       * @param object $moduleinfo - the specific assign module values that were used to create an assign module.
 133       * @param object $dbmodinstance - the DB values of the created assign module.
 134       */
 135      private function assign_create_run_asserts($moduleinfo, $dbmodinstance) {
 136          global $DB;
 137  
 138          $this->assertEquals($moduleinfo->alwaysshowdescription, $dbmodinstance->alwaysshowdescription);
 139          $this->assertEquals($moduleinfo->submissiondrafts, $dbmodinstance->submissiondrafts);
 140          $this->assertEquals($moduleinfo->requiresubmissionstatement, $dbmodinstance->requiresubmissionstatement);
 141          $this->assertEquals($moduleinfo->sendnotifications, $dbmodinstance->sendnotifications);
 142          $this->assertEquals($moduleinfo->duedate, $dbmodinstance->duedate);
 143          $this->assertEquals($moduleinfo->cutoffdate, $dbmodinstance->cutoffdate);
 144          $this->assertEquals($moduleinfo->allowsubmissionsfromdate, $dbmodinstance->allowsubmissionsfromdate);
 145          $this->assertEquals($moduleinfo->teamsubmission, $dbmodinstance->teamsubmission);
 146          $this->assertEquals($moduleinfo->requireallteammemberssubmit, $dbmodinstance->requireallteammemberssubmit);
 147          $this->assertEquals($moduleinfo->teamsubmissiongroupingid, $dbmodinstance->teamsubmissiongroupingid);
 148          $this->assertEquals($moduleinfo->blindmarking, $dbmodinstance->blindmarking);
 149          $this->assertEquals($moduleinfo->markingworkflow, $dbmodinstance->markingworkflow);
 150          $this->assertEquals($moduleinfo->markingallocation, $dbmodinstance->markingallocation);
 151          // The goal not being to fully test assign_add_instance() we'll stop here for the assign tests - to avoid too many DB queries.
 152  
 153          // Advanced grading.
 154          $cm = get_coursemodule_from_instance('assign', $dbmodinstance->id);
 155          $contextmodule = context_module::instance($cm->id);
 156          $advancedgradingmethod = $DB->get_record('grading_areas',
 157              array('contextid' => $contextmodule->id,
 158                  'activemethod' => $moduleinfo->advancedgradingmethod_submissions));
 159          $this->assertEquals($moduleinfo->advancedgradingmethod_submissions, $advancedgradingmethod);
 160      }
 161  
 162      /**
 163       * Run some asserts test for a specific module for the function create_module().
 164       *
 165       * The function has been created (and is called) for $this->test_create_module().
 166       * Note that the call to MODULE_create_set_values and MODULE_create_run_asserts are done after the common set values/run asserts.
 167       * So if you want, you can overwrite the default values/asserts in the respective functions.
 168       * @param string $modulename Name of the module ('forum', 'assign', 'book'...).
 169       */
 170      private function create_specific_module_test($modulename) {
 171          global $DB, $CFG;
 172  
 173          $this->resetAfterTest(true);
 174  
 175          $this->setAdminUser();
 176  
 177          // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard.
 178          require_once($CFG->dirroot.'/mod/'. $modulename .'/lib.php');
 179  
 180          // Enable avaibility.
 181          // If not enabled all conditional fields will be ignored.
 182          set_config('enableavailability', 1);
 183  
 184          // Enable course completion.
 185          // If not enabled all completion settings will be ignored.
 186          set_config('enablecompletion', COMPLETION_ENABLED);
 187  
 188          // Enable forum RSS feeds.
 189          set_config('enablerssfeeds', 1);
 190          set_config('forum_enablerssfeeds', 1);
 191  
 192          $course = $this->getDataGenerator()->create_course(array('numsections'=>1, 'enablecompletion' => COMPLETION_ENABLED),
 193             array('createsections'=>true));
 194  
 195          $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
 196  
 197          // Create assign module instance for test.
 198          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 199          $params['course'] = $course->id;
 200          $instance = $generator->create_instance($params);
 201          $assigncm = get_coursemodule_from_instance('assign', $instance->id);
 202  
 203          // Module test values.
 204          $moduleinfo = new stdClass();
 205  
 206          // Always mandatory generic values to any module.
 207          $moduleinfo->modulename = $modulename;
 208          $moduleinfo->section = 1; // This is the section number in the course. Not the section id in the database.
 209          $moduleinfo->course = $course->id;
 210          $moduleinfo->groupingid = $grouping->id;
 211          $moduleinfo->visible = true;
 212  
 213          // Sometimes optional generic values for some modules.
 214          $moduleinfo->name = 'My test module';
 215          $moduleinfo->showdescription = 1; // standard boolean
 216          require_once($CFG->libdir . '/gradelib.php');
 217          $gradecats = grade_get_categories_menu($moduleinfo->course, false);
 218          $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats
 219          $moduleinfo->gradecat = $gradecatid;
 220          $moduleinfo->groupmode = VISIBLEGROUPS;
 221          $moduleinfo->cmidnumber = 'idnumber_XXX';
 222  
 223          // Completion common to all module.
 224          $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC;
 225          $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED;
 226          $moduleinfo->completiongradeitemnumber = 1;
 227          $moduleinfo->completionexpected = time() + (7 * 24 * 3600);
 228  
 229          // Conditional activity.
 230          $moduleinfo->availability = '{"op":"&","showc":[true,true],"c":[' .
 231                  '{"type":"date","d":">=","t":' . time() . '},' .
 232                  '{"type":"date","d":"<","t":' . (time() + (7 * 24 * 3600)) . '}' .
 233                  ']}';
 234          $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself.
 235          $moduleinfo->conditiongradegroup = array(array('conditiongradeitemid' => $coursegradeitem->id, 'conditiongrademin' => 10, 'conditiongrademax' => 80));
 236          $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => OP_CONTAINS, 'conditionfieldvalue' => '@'));
 237          $moduleinfo->conditioncompletiongroup = array(array('conditionsourcecmid' => $assigncm->id, 'conditionrequiredcompletion' => COMPLETION_COMPLETE)); // "conditionsourcecmid == 0" => none
 238  
 239          // Grading and Advanced grading.
 240          require_once($CFG->dirroot . '/rating/lib.php');
 241          $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE;
 242          $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number.
 243          $moduleinfo->assesstimestart = time();
 244          $moduleinfo->assesstimefinish = time() + (7 * 24 * 3600);
 245  
 246          // RSS.
 247          $moduleinfo->rsstype = 2;
 248          $moduleinfo->rssarticles = 10;
 249  
 250          // Optional intro editor (depends of module).
 251          $draftid_editor = 0;
 252          file_prepare_draft_area($draftid_editor, null, null, null, null);
 253          $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor);
 254  
 255          // Following is the advanced grading method area called 'submissions' for the 'assign' module.
 256          if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
 257              $moduleinfo->grade = 100;
 258          }
 259  
 260          // Plagiarism form values.
 261          // No plagiarism plugin installed by default. Use this space to make your own test.
 262  
 263          // Values specific to the module.
 264          $modulesetvalues = $modulename.'_create_set_values';
 265          $this->$modulesetvalues($moduleinfo);
 266  
 267          // Create the module.
 268          $result = create_module($moduleinfo);
 269  
 270          // Retrieve the module info.
 271          $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance));
 272          $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance);
 273          // We passed the course section number to create_courses but $dbcm contain the section id.
 274          // We need to retrieve the db course section number.
 275          $section = $DB->get_record('course_sections', array('course' => $dbcm->course, 'id' => $dbcm->section));
 276          // Retrieve the grade item.
 277          $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course,
 278              'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename));
 279  
 280          // Compare the values common to all module instances.
 281          $this->assertEquals($moduleinfo->modulename, $dbcm->modname);
 282          $this->assertEquals($moduleinfo->section, $section->section);
 283          $this->assertEquals($moduleinfo->course, $dbcm->course);
 284          $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid);
 285          $this->assertEquals($moduleinfo->visible, $dbcm->visible);
 286          $this->assertEquals($moduleinfo->completion, $dbcm->completion);
 287          $this->assertEquals($moduleinfo->completionview, $dbcm->completionview);
 288          $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber);
 289          $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected);
 290          $this->assertEquals($moduleinfo->availability, $dbcm->availability);
 291          $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription);
 292          $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode);
 293          $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber);
 294          $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid);
 295  
 296          // Optional grade testing.
 297          if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
 298              $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade);
 299          }
 300  
 301          // Some optional (but quite common) to some module.
 302          $this->assertEquals($moduleinfo->name, $dbmodinstance->name);
 303          $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro);
 304          $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat);
 305  
 306          // Test specific to the module.
 307          $modulerunasserts = $modulename.'_create_run_asserts';
 308          $this->$modulerunasserts($moduleinfo, $dbmodinstance);
 309          return $moduleinfo;
 310      }
 311  
 312      /**
 313       * Test create_module() for multiple modules defined in the $modules array (first declaration of the function).
 314       */
 315      public function test_create_module() {
 316          // Add the module name you want to test here.
 317          // Create the match MODULENAME_create_set_values() and MODULENAME_create_run_asserts().
 318          $modules = array('forum', 'assign');
 319          // Run all tests.
 320          foreach ($modules as $modulename) {
 321              $this->create_specific_module_test($modulename);
 322          }
 323      }
 324  
 325      /**
 326       * Test update_module() for multiple modules defined in the $modules array (first declaration of the function).
 327       */
 328      public function test_update_module() {
 329          // Add the module name you want to test here.
 330          // Create the match MODULENAME_update_set_values() and MODULENAME_update_run_asserts().
 331          $modules = array('forum');
 332          // Run all tests.
 333          foreach ($modules as $modulename) {
 334              $this->update_specific_module_test($modulename);
 335          }
 336      }
 337  
 338      /**
 339       * Set forum specific test values for calling update_module().
 340       *
 341       * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference.
 342       */
 343      private function forum_update_set_values(&$moduleinfo) {
 344          // Completion specific to forum - optional.
 345          $moduleinfo->completionposts = 3;
 346          $moduleinfo->completiondiscussions = 1;
 347          $moduleinfo->completionreplies = 2;
 348  
 349          // Specific values to the Forum module.
 350          $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE;
 351          $moduleinfo->type = 'single';
 352          $moduleinfo->trackingtype = FORUM_TRACKING_FORCED;
 353          $moduleinfo->maxbytes = 10240;
 354          $moduleinfo->maxattachments = 2;
 355  
 356          // Post threshold for blocking - specific to forum.
 357          $moduleinfo->blockperiod = 60*60*24;
 358          $moduleinfo->blockafter = 10;
 359          $moduleinfo->warnafter = 5;
 360      }
 361  
 362      /**
 363       * Execute test asserts on the saved DB data by update_module($forum).
 364       *
 365       * @param object $moduleinfo - the specific forum values that were used to update a forum.
 366       * @param object $dbmodinstance - the DB values of the updated forum.
 367       */
 368      private function forum_update_run_asserts($moduleinfo, $dbmodinstance) {
 369          // Compare values specific to forums.
 370          $this->assertEquals($moduleinfo->forcesubscribe, $dbmodinstance->forcesubscribe);
 371          $this->assertEquals($moduleinfo->type, $dbmodinstance->type);
 372          $this->assertEquals($moduleinfo->assessed, $dbmodinstance->assessed);
 373          $this->assertEquals($moduleinfo->completionposts, $dbmodinstance->completionposts);
 374          $this->assertEquals($moduleinfo->completiondiscussions, $dbmodinstance->completiondiscussions);
 375          $this->assertEquals($moduleinfo->completionreplies, $dbmodinstance->completionreplies);
 376          $this->assertEquals($moduleinfo->scale, $dbmodinstance->scale);
 377          $this->assertEquals($moduleinfo->assesstimestart, $dbmodinstance->assesstimestart);
 378          $this->assertEquals($moduleinfo->assesstimefinish, $dbmodinstance->assesstimefinish);
 379          $this->assertEquals($moduleinfo->rsstype, $dbmodinstance->rsstype);
 380          $this->assertEquals($moduleinfo->rssarticles, $dbmodinstance->rssarticles);
 381          $this->assertEquals($moduleinfo->trackingtype, $dbmodinstance->trackingtype);
 382          $this->assertEquals($moduleinfo->maxbytes, $dbmodinstance->maxbytes);
 383          $this->assertEquals($moduleinfo->maxattachments, $dbmodinstance->maxattachments);
 384          $this->assertEquals($moduleinfo->blockperiod, $dbmodinstance->blockperiod);
 385          $this->assertEquals($moduleinfo->blockafter, $dbmodinstance->blockafter);
 386          $this->assertEquals($moduleinfo->warnafter, $dbmodinstance->warnafter);
 387      }
 388  
 389  
 390  
 391      /**
 392       * Test a specific type of module.
 393       *
 394       * @param string $modulename - the module name to test
 395       */
 396      private function update_specific_module_test($modulename) {
 397          global $DB, $CFG;
 398  
 399          $this->resetAfterTest(true);
 400  
 401          $this->setAdminUser();
 402  
 403          // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard.
 404          require_once($CFG->dirroot.'/mod/'. $modulename .'/lib.php');
 405  
 406          // Enable avaibility.
 407          // If not enabled all conditional fields will be ignored.
 408          set_config('enableavailability', 1);
 409  
 410          // Enable course completion.
 411          // If not enabled all completion settings will be ignored.
 412          set_config('enablecompletion', COMPLETION_ENABLED);
 413  
 414          // Enable forum RSS feeds.
 415          set_config('enablerssfeeds', 1);
 416          set_config('forum_enablerssfeeds', 1);
 417  
 418          $course = $this->getDataGenerator()->create_course(array('numsections'=>1, 'enablecompletion' => COMPLETION_ENABLED),
 419             array('createsections'=>true));
 420  
 421          $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
 422  
 423          // Create assign module instance for testing gradeitem.
 424          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 425          $params['course'] = $course->id;
 426          $instance = $generator->create_instance($params);
 427          $assigncm = get_coursemodule_from_instance('assign', $instance->id);
 428  
 429          // Create the test forum to update.
 430          $initvalues = new stdClass();
 431          $initvalues->introformat = FORMAT_HTML;
 432          $initvalues->course = $course->id;
 433          $forum = self::getDataGenerator()->create_module('forum', $initvalues);
 434  
 435          // Retrieve course module.
 436          $cm = get_coursemodule_from_instance('forum', $forum->id);
 437  
 438          // Module test values.
 439          $moduleinfo = new stdClass();
 440  
 441          // Always mandatory generic values to any module.
 442          $moduleinfo->coursemodule = $cm->id;
 443          $moduleinfo->modulename = $modulename;
 444          $moduleinfo->course = $course->id;
 445          $moduleinfo->groupingid = $grouping->id;
 446          $moduleinfo->visible = true;
 447  
 448          // Sometimes optional generic values for some modules.
 449          $moduleinfo->name = 'My test module';
 450          $moduleinfo->showdescription = 1; // standard boolean
 451          require_once($CFG->libdir . '/gradelib.php');
 452          $gradecats = grade_get_categories_menu($moduleinfo->course, false);
 453          $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats
 454          $moduleinfo->gradecat = $gradecatid;
 455          $moduleinfo->groupmode = VISIBLEGROUPS;
 456          $moduleinfo->cmidnumber = 'idnumber_XXX';
 457  
 458          // Completion common to all module.
 459          $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC;
 460          $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED;
 461          $moduleinfo->completiongradeitemnumber = 1;
 462          $moduleinfo->completionexpected = time() + (7 * 24 * 3600);
 463          $moduleinfo->completionunlocked = 1;
 464  
 465          // Conditional activity.
 466          $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself.
 467          $moduleinfo->availability = json_encode(\core_availability\tree::get_root_json(
 468                  array(\availability_date\condition::get_json('>=', time()),
 469                  \availability_date\condition::get_json('<', time() + (7 * 24 * 3600)),
 470                  \availability_grade\condition::get_json($coursegradeitem->id, 10, 80),
 471                  \availability_profile\condition::get_json(false, 'email', 'contains', '@'),
 472                  \availability_completion\condition::get_json($assigncm->id, COMPLETION_COMPLETE)), '&'));
 473  
 474          // Grading and Advanced grading.
 475          require_once($CFG->dirroot . '/rating/lib.php');
 476          $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE;
 477          $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number.
 478          $moduleinfo->assesstimestart = time();
 479          $moduleinfo->assesstimefinish = time() + (7 * 24 * 3600);
 480  
 481          // RSS.
 482          $moduleinfo->rsstype = 2;
 483          $moduleinfo->rssarticles = 10;
 484  
 485          // Optional intro editor (depends of module).
 486          $draftid_editor = 0;
 487          file_prepare_draft_area($draftid_editor, null, null, null, null);
 488          $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor);
 489  
 490          // Following is the advanced grading method area called 'submissions' for the 'assign' module.
 491          if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
 492              $moduleinfo->grade = 100;
 493          }
 494          // Plagiarism form values.
 495          // No plagiarism plugin installed by default. Use this space to make your own test.
 496  
 497          // Values specific to the module.
 498          $modulesetvalues = $modulename.'_update_set_values';
 499          $this->$modulesetvalues($moduleinfo);
 500  
 501          // Create the module.
 502          $result = update_module($moduleinfo);
 503  
 504          // Retrieve the module info.
 505          $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance));
 506          $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance);
 507          // Retrieve the grade item.
 508          $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course,
 509              'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename));
 510  
 511          // Compare the values common to all module instances.
 512          $this->assertEquals($moduleinfo->modulename, $dbcm->modname);
 513          $this->assertEquals($moduleinfo->course, $dbcm->course);
 514          $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid);
 515          $this->assertEquals($moduleinfo->visible, $dbcm->visible);
 516          $this->assertEquals($moduleinfo->completion, $dbcm->completion);
 517          $this->assertEquals($moduleinfo->completionview, $dbcm->completionview);
 518          $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber);
 519          $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected);
 520          $this->assertEquals($moduleinfo->availability, $dbcm->availability);
 521          $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription);
 522          $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode);
 523          $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber);
 524          $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid);
 525  
 526          // Optional grade testing.
 527          if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
 528              $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade);
 529          }
 530  
 531          // Some optional (but quite common) to some module.
 532          $this->assertEquals($moduleinfo->name, $dbmodinstance->name);
 533          $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro);
 534          $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat);
 535  
 536          // Test specific to the module.
 537          $modulerunasserts = $modulename.'_update_run_asserts';
 538          $this->$modulerunasserts($moduleinfo, $dbmodinstance);
 539          return $moduleinfo;
 540     }
 541  
 542  
 543      public function test_create_course() {
 544          global $DB;
 545          $this->resetAfterTest(true);
 546          $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
 547  
 548          $course = new stdClass();
 549          $course->fullname = 'Apu loves Unit Təsts';
 550          $course->shortname = 'Spread the lÅ­ve';
 551          $course->idnumber = '123';
 552          $course->summary = 'Awesome!';
 553          $course->summaryformat = FORMAT_PLAIN;
 554          $course->format = 'topics';
 555          $course->newsitems = 0;
 556          $course->numsections = 5;
 557          $course->category = $defaultcategory;
 558          $original = (array) $course;
 559  
 560          $created = create_course($course);
 561          $context = context_course::instance($created->id);
 562  
 563          // Compare original and created.
 564          $this->assertEquals($original, array_intersect_key((array) $created, $original));
 565  
 566          // Ensure default section is created.
 567          $sectioncreated = $DB->record_exists('course_sections', array('course' => $created->id, 'section' => 0));
 568          $this->assertTrue($sectioncreated);
 569  
 570          // Ensure blocks have been associated to the course.
 571          $blockcount = $DB->count_records('block_instances', array('parentcontextid' => $context->id));
 572          $this->assertGreaterThan(0, $blockcount);
 573  
 574          // Ensure that the shortname isn't duplicated.
 575          try {
 576              $created = create_course($course);
 577              $this->fail('Exception expected');
 578          } catch (moodle_exception $e) {
 579              $this->assertSame(get_string('shortnametaken', 'error', $course->shortname), $e->getMessage());
 580          }
 581  
 582          // Ensure that the idnumber isn't duplicated.
 583          $course->shortname .= '1';
 584          try {
 585              $created = create_course($course);
 586              $this->fail('Exception expected');
 587          } catch (moodle_exception $e) {
 588              $this->assertSame(get_string('courseidnumbertaken', 'error', $course->idnumber), $e->getMessage());
 589          }
 590      }
 591  
 592      public function test_create_course_with_generator() {
 593          global $DB;
 594          $this->resetAfterTest(true);
 595          $course = $this->getDataGenerator()->create_course();
 596  
 597          // Ensure default section is created.
 598          $sectioncreated = $DB->record_exists('course_sections', array('course' => $course->id, 'section' => 0));
 599          $this->assertTrue($sectioncreated);
 600      }
 601  
 602      public function test_create_course_sections() {
 603          global $DB;
 604          $this->resetAfterTest(true);
 605  
 606          $course = $this->getDataGenerator()->create_course(
 607                  array('shortname' => 'GrowingCourse',
 608                      'fullname' => 'Growing Course',
 609                      'numsections' => 5),
 610                  array('createsections' => true));
 611  
 612          // Ensure all 6 (0-5) sections were created and course content cache works properly
 613          $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
 614          $this->assertEquals(range(0, $course->numsections), $sectionscreated);
 615  
 616          // this will do nothing, section already exists
 617          $this->assertFalse(course_create_sections_if_missing($course, $course->numsections));
 618  
 619          // this will create new section
 620          $this->assertTrue(course_create_sections_if_missing($course, $course->numsections + 1));
 621  
 622          // Ensure all 7 (0-6) sections were created and modinfo/sectioninfo cache works properly
 623          $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
 624          $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated);
 625      }
 626  
 627      public function test_update_course() {
 628          global $DB;
 629  
 630          $this->resetAfterTest();
 631  
 632          $defaultcategory = $DB->get_field_select('course_categories', 'MIN(id)', 'parent = 0');
 633  
 634          $course = new stdClass();
 635          $course->fullname = 'Apu loves Unit Təsts';
 636          $course->shortname = 'test1';
 637          $course->idnumber = '1';
 638          $course->summary = 'Awesome!';
 639          $course->summaryformat = FORMAT_PLAIN;
 640          $course->format = 'topics';
 641          $course->newsitems = 0;
 642          $course->numsections = 5;
 643          $course->category = $defaultcategory;
 644  
 645          $created = create_course($course);
 646          // Ensure the checks only work on idnumber/shortname that are not already ours.
 647          update_course($created);
 648  
 649          $course->shortname = 'test2';
 650          $course->idnumber = '2';
 651  
 652          $created2 = create_course($course);
 653  
 654          // Test duplicate idnumber.
 655          $created2->idnumber = '1';
 656          try {
 657              update_course($created2);
 658              $this->fail('Expected exception when trying to update a course with duplicate idnumber');
 659          } catch (moodle_exception $e) {
 660              $this->assertEquals(get_string('courseidnumbertaken', 'error', $created2->idnumber), $e->getMessage());
 661          }
 662  
 663          // Test duplicate shortname.
 664          $created2->idnumber = '2';
 665          $created2->shortname = 'test1';
 666          try {
 667              update_course($created2);
 668              $this->fail('Expected exception when trying to update a course with a duplicate shortname');
 669          } catch (moodle_exception $e) {
 670              $this->assertEquals(get_string('shortnametaken', 'error', $created2->shortname), $e->getMessage());
 671          }
 672      }
 673  
 674      public function test_course_add_cm_to_section() {
 675          global $DB;
 676          $this->resetAfterTest(true);
 677  
 678          // Create course with 1 section.
 679          $course = $this->getDataGenerator()->create_course(
 680                  array('shortname' => 'GrowingCourse',
 681                      'fullname' => 'Growing Course',
 682                      'numsections' => 1),
 683                  array('createsections' => true));
 684  
 685          // Trash modinfo.
 686          rebuild_course_cache($course->id, true);
 687  
 688          // Create some cms for testing.
 689          $cmids = array();
 690          for ($i=0; $i<4; $i++) {
 691              $cmids[$i] = $DB->insert_record('course_modules', array('course' => $course->id));
 692          }
 693  
 694          // Add it to section that exists.
 695          course_add_cm_to_section($course, $cmids[0], 1);
 696  
 697          // Check it got added to sequence.
 698          $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
 699          $this->assertEquals($cmids[0], $sequence);
 700  
 701          // Add a second, this time using courseid variant of parameters.
 702          $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
 703          course_add_cm_to_section($course->id, $cmids[1], 1);
 704          $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
 705          $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence);
 706  
 707          // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly).
 708          $this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id)));
 709          $this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id));
 710  
 711          // Add one to section that doesn't exist (this might rebuild modinfo).
 712          course_add_cm_to_section($course, $cmids[2], 2);
 713          $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id)));
 714          $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2));
 715          $this->assertEquals($cmids[2], $sequence);
 716  
 717          // Add using the 'before' option.
 718          course_add_cm_to_section($course, $cmids[3], 2, $cmids[2]);
 719          $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id)));
 720          $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2));
 721          $this->assertEquals($cmids[3] . ',' . $cmids[2], $sequence);
 722      }
 723  
 724      public function test_reorder_sections() {
 725          global $DB;
 726          $this->resetAfterTest(true);
 727  
 728          $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
 729          $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
 730          $oldsections = array();
 731          $sections = array();
 732          foreach ($DB->get_records('course_sections', array('course'=>$course->id), 'id') as $section) {
 733              $oldsections[$section->section] = $section->id;
 734              $sections[$section->id] = $section->section;
 735          }
 736          ksort($oldsections);
 737  
 738          $neworder = reorder_sections($sections, 2, 4);
 739          $neworder = array_keys($neworder);
 740          $this->assertEquals($oldsections[0], $neworder[0]);
 741          $this->assertEquals($oldsections[1], $neworder[1]);
 742          $this->assertEquals($oldsections[2], $neworder[4]);
 743          $this->assertEquals($oldsections[3], $neworder[2]);
 744          $this->assertEquals($oldsections[4], $neworder[3]);
 745          $this->assertEquals($oldsections[5], $neworder[5]);
 746          $this->assertEquals($oldsections[6], $neworder[6]);
 747  
 748          $neworder = reorder_sections($sections, 4, 2);
 749          $neworder = array_keys($neworder);
 750          $this->assertEquals($oldsections[0], $neworder[0]);
 751          $this->assertEquals($oldsections[1], $neworder[1]);
 752          $this->assertEquals($oldsections[2], $neworder[3]);
 753          $this->assertEquals($oldsections[3], $neworder[4]);
 754          $this->assertEquals($oldsections[4], $neworder[2]);
 755          $this->assertEquals($oldsections[5], $neworder[5]);
 756          $this->assertEquals($oldsections[6], $neworder[6]);
 757  
 758          $neworder = reorder_sections(1, 2, 4);
 759          $this->assertFalse($neworder);
 760      }
 761  
 762      public function test_move_section_down() {
 763          global $DB;
 764          $this->resetAfterTest(true);
 765  
 766          $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
 767          $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
 768          $oldsections = array();
 769          foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
 770              $oldsections[$section->section] = $section->id;
 771          }
 772          ksort($oldsections);
 773  
 774          // Test move section down..
 775          move_section_to($course, 2, 4);
 776          $sections = array();
 777          foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
 778              $sections[$section->section] = $section->id;
 779          }
 780          ksort($sections);
 781  
 782          $this->assertEquals($oldsections[0], $sections[0]);
 783          $this->assertEquals($oldsections[1], $sections[1]);
 784          $this->assertEquals($oldsections[2], $sections[4]);
 785          $this->assertEquals($oldsections[3], $sections[2]);
 786          $this->assertEquals($oldsections[4], $sections[3]);
 787          $this->assertEquals($oldsections[5], $sections[5]);
 788          $this->assertEquals($oldsections[6], $sections[6]);
 789      }
 790  
 791      public function test_move_section_up() {
 792          global $DB;
 793          $this->resetAfterTest(true);
 794  
 795          $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
 796          $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
 797          $oldsections = array();
 798          foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
 799              $oldsections[$section->section] = $section->id;
 800          }
 801          ksort($oldsections);
 802  
 803          // Test move section up..
 804          move_section_to($course, 6, 4);
 805          $sections = array();
 806          foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
 807              $sections[$section->section] = $section->id;
 808          }
 809          ksort($sections);
 810  
 811          $this->assertEquals($oldsections[0], $sections[0]);
 812          $this->assertEquals($oldsections[1], $sections[1]);
 813          $this->assertEquals($oldsections[2], $sections[2]);
 814          $this->assertEquals($oldsections[3], $sections[3]);
 815          $this->assertEquals($oldsections[4], $sections[5]);
 816          $this->assertEquals($oldsections[5], $sections[6]);
 817          $this->assertEquals($oldsections[6], $sections[4]);
 818      }
 819  
 820      public function test_move_section_marker() {
 821          global $DB;
 822          $this->resetAfterTest(true);
 823  
 824          $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
 825          $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
 826  
 827          // Set course marker to the section we are going to move..
 828          course_set_marker($course->id, 2);
 829          // Verify that the course marker is set correctly.
 830          $course = $DB->get_record('course', array('id' => $course->id));
 831          $this->assertEquals(2, $course->marker);
 832  
 833          // Test move the marked section down..
 834          move_section_to($course, 2, 4);
 835  
 836          // Verify that the coruse marker has been moved along with the section..
 837          $course = $DB->get_record('course', array('id' => $course->id));
 838          $this->assertEquals(4, $course->marker);
 839  
 840          // Test move the marked section up..
 841          move_section_to($course, 4, 3);
 842  
 843          // Verify that the course marker has been moved along with the section..
 844          $course = $DB->get_record('course', array('id' => $course->id));
 845          $this->assertEquals(3, $course->marker);
 846  
 847          // Test moving a non-marked section above the marked section..
 848          move_section_to($course, 4, 2);
 849  
 850          // Verify that the course marker has been moved down to accomodate..
 851          $course = $DB->get_record('course', array('id' => $course->id));
 852          $this->assertEquals(4, $course->marker);
 853  
 854          // Test moving a non-marked section below the marked section..
 855          move_section_to($course, 3, 6);
 856  
 857          // Verify that the course marker has been up to accomodate..
 858          $course = $DB->get_record('course', array('id' => $course->id));
 859          $this->assertEquals(3, $course->marker);
 860      }
 861  
 862      public function test_get_course_display_name_for_list() {
 863          global $CFG;
 864          $this->resetAfterTest(true);
 865  
 866          $course = $this->getDataGenerator()->create_course(array('shortname' => 'FROG101', 'fullname' => 'Introduction to pond life'));
 867  
 868          $CFG->courselistshortnames = 0;
 869          $this->assertEquals('Introduction to pond life', get_course_display_name_for_list($course));
 870  
 871          $CFG->courselistshortnames = 1;
 872          $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course));
 873      }
 874  
 875      public function test_move_module_in_course() {
 876          global $DB;
 877  
 878          $this->resetAfterTest(true);
 879          // Setup fixture
 880          $course = $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections' => true));
 881          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 882  
 883          $cms = get_fast_modinfo($course)->get_cms();
 884          $cm = reset($cms);
 885  
 886          $newsection = get_fast_modinfo($course)->get_section_info(3);
 887          $oldsectionid = $cm->section;
 888  
 889          // Perform the move
 890          moveto_module($cm, $newsection);
 891  
 892          $cms = get_fast_modinfo($course)->get_cms();
 893          $cm = reset($cms);
 894  
 895          // Check that the cached modinfo contains the correct section info
 896          $modinfo = get_fast_modinfo($course);
 897          $this->assertTrue(empty($modinfo->sections[0]));
 898          $this->assertFalse(empty($modinfo->sections[3]));
 899  
 900          // Check that the old section's sequence no longer contains this ID
 901          $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid));
 902          $oldsequences = explode(',', $newsection->sequence);
 903          $this->assertFalse(in_array($cm->id, $oldsequences));
 904  
 905          // Check that the new section's sequence now contains this ID
 906          $newsection = $DB->get_record('course_sections', array('id' => $newsection->id));
 907          $newsequences = explode(',', $newsection->sequence);
 908          $this->assertTrue(in_array($cm->id, $newsequences));
 909  
 910          // Check that the section number has been changed in the cm
 911          $this->assertEquals($newsection->id, $cm->section);
 912  
 913  
 914          // Perform a second move as some issues were only seen on the second move
 915          $newsection = get_fast_modinfo($course)->get_section_info(2);
 916          $oldsectionid = $cm->section;
 917          moveto_module($cm, $newsection);
 918  
 919          $cms = get_fast_modinfo($course)->get_cms();
 920          $cm = reset($cms);
 921  
 922          // Check that the cached modinfo contains the correct section info
 923          $modinfo = get_fast_modinfo($course);
 924          $this->assertTrue(empty($modinfo->sections[0]));
 925          $this->assertFalse(empty($modinfo->sections[2]));
 926  
 927          // Check that the old section's sequence no longer contains this ID
 928          $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid));
 929          $oldsequences = explode(',', $newsection->sequence);
 930          $this->assertFalse(in_array($cm->id, $oldsequences));
 931  
 932          // Check that the new section's sequence now contains this ID
 933          $newsection = $DB->get_record('course_sections', array('id' => $newsection->id));
 934          $newsequences = explode(',', $newsection->sequence);
 935          $this->assertTrue(in_array($cm->id, $newsequences));
 936      }
 937  
 938      public function test_module_visibility() {
 939          $this->setAdminUser();
 940          $this->resetAfterTest(true);
 941  
 942          // Create course and modules.
 943          $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
 944          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
 945          $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
 946          $modules = compact('forum', 'assign');
 947  
 948          // Hiding the modules.
 949          foreach ($modules as $mod) {
 950              set_coursemodule_visible($mod->cmid, 0);
 951              $this->check_module_visibility($mod, 0, 0);
 952          }
 953  
 954          // Showing the modules.
 955          foreach ($modules as $mod) {
 956              set_coursemodule_visible($mod->cmid, 1);
 957              $this->check_module_visibility($mod, 1, 1);
 958          }
 959      }
 960  
 961      public function test_section_visibility_events() {
 962          $this->setAdminUser();
 963          $this->resetAfterTest(true);
 964  
 965          $course = $this->getDataGenerator()->create_course(array('numsections' => 1), array('createsections' => true));
 966          $sectionnumber = 1;
 967          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
 968              array('section' => $sectionnumber));
 969          $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
 970              'course' => $course->id), array('section' => $sectionnumber));
 971          $sink = $this->redirectEvents();
 972          set_section_visible($course->id, $sectionnumber, 0);
 973          $events = $sink->get_events();
 974  
 975          // Extract the number of events related to what we are testing, other events
 976          // such as course_section_updated could have been triggered.
 977          $count = 0;
 978          foreach ($events as $event) {
 979              if ($event instanceof \core\event\course_module_updated) {
 980                  $count++;
 981              }
 982          }
 983          $this->assertSame(2, $count);
 984          $sink->close();
 985      }
 986  
 987      public function test_section_visibility() {
 988          $this->setAdminUser();
 989          $this->resetAfterTest(true);
 990  
 991          // Create course.
 992          $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
 993  
 994          $sink = $this->redirectEvents();
 995  
 996          // Testing an empty section.
 997          $sectionnumber = 1;
 998          set_section_visible($course->id, $sectionnumber, 0);
 999          $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1000          $this->assertEquals($section_info->visible, 0);
1001          set_section_visible($course->id, $sectionnumber, 1);
1002          $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1003          $this->assertEquals($section_info->visible, 1);
1004  
1005          // Checking that an event was fired.
1006          $events = $sink->get_events();
1007          $this->assertInstanceOf('\core\event\course_section_updated', $events[0]);
1008          $sink->close();
1009  
1010          // Testing a section with visible modules.
1011          $sectionnumber = 2;
1012          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1013                  array('section' => $sectionnumber));
1014          $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
1015                  'course' => $course->id), array('section' => $sectionnumber));
1016          $modules = compact('forum', 'assign');
1017          set_section_visible($course->id, $sectionnumber, 0);
1018          $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1019          $this->assertEquals($section_info->visible, 0);
1020          foreach ($modules as $mod) {
1021              $this->check_module_visibility($mod, 0, 1);
1022          }
1023          set_section_visible($course->id, $sectionnumber, 1);
1024          $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1025          $this->assertEquals($section_info->visible, 1);
1026          foreach ($modules as $mod) {
1027              $this->check_module_visibility($mod, 1, 1);
1028          }
1029  
1030          // Testing a section with hidden modules, which should stay hidden.
1031          $sectionnumber = 3;
1032          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1033                  array('section' => $sectionnumber));
1034          $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
1035                  'course' => $course->id), array('section' => $sectionnumber));
1036          $modules = compact('forum', 'assign');
1037          foreach ($modules as $mod) {
1038              set_coursemodule_visible($mod->cmid, 0);
1039              $this->check_module_visibility($mod, 0, 0);
1040          }
1041          set_section_visible($course->id, $sectionnumber, 0);
1042          $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1043          $this->assertEquals($section_info->visible, 0);
1044          foreach ($modules as $mod) {
1045              $this->check_module_visibility($mod, 0, 0);
1046          }
1047          set_section_visible($course->id, $sectionnumber, 1);
1048          $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1049          $this->assertEquals($section_info->visible, 1);
1050          foreach ($modules as $mod) {
1051              $this->check_module_visibility($mod, 0, 0);
1052          }
1053      }
1054  
1055      /**
1056       * Helper function to assert that a module has correctly been made visible, or hidden.
1057       *
1058       * @param stdClass $mod module information
1059       * @param int $visibility the current state of the module
1060       * @param int $visibleold the current state of the visibleold property
1061       * @return void
1062       */
1063      public function check_module_visibility($mod, $visibility, $visibleold) {
1064          global $DB;
1065          $cm = get_fast_modinfo($mod->course)->get_cm($mod->cmid);
1066          $this->assertEquals($visibility, $cm->visible);
1067          $this->assertEquals($visibleold, $cm->visibleold);
1068  
1069          // Check the module grade items.
1070          $grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $cm->modname,
1071                  'iteminstance' => $cm->instance, 'courseid' => $cm->course));
1072          if ($grade_items) {
1073              foreach ($grade_items as $grade_item) {
1074                  if ($visibility) {
1075                      $this->assertFalse($grade_item->is_hidden(), "$cm->modname grade_item not visible");
1076                  } else {
1077                      $this->assertTrue($grade_item->is_hidden(), "$cm->modname grade_item not hidden");
1078                  }
1079              }
1080          }
1081  
1082          // Check the events visibility.
1083          if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $cm->modname))) {
1084              foreach ($events as $event) {
1085                  $calevent = new calendar_event($event);
1086                  $this->assertEquals($visibility, $calevent->visible, "$cm->modname calendar_event visibility");
1087              }
1088          }
1089      }
1090  
1091      public function test_course_page_type_list() {
1092          global $DB;
1093          $this->resetAfterTest(true);
1094  
1095          // Create a category.
1096          $category = new stdClass();
1097          $category->name = 'Test Category';
1098  
1099          $testcategory = $this->getDataGenerator()->create_category($category);
1100  
1101          // Create a course.
1102          $course = new stdClass();
1103          $course->fullname = 'Apu loves Unit Təsts';
1104          $course->shortname = 'Spread the lÅ­ve';
1105          $course->idnumber = '123';
1106          $course->summary = 'Awesome!';
1107          $course->summaryformat = FORMAT_PLAIN;
1108          $course->format = 'topics';
1109          $course->newsitems = 0;
1110          $course->numsections = 5;
1111          $course->category = $testcategory->id;
1112  
1113          $testcourse = $this->getDataGenerator()->create_course($course);
1114  
1115          // Create contexts.
1116          $coursecontext = context_course::instance($testcourse->id);
1117          $parentcontext = $coursecontext->get_parent_context(); // Not actually used.
1118          $pagetype = 'page-course-x'; // Not used either.
1119          $pagetypelist = course_page_type_list($pagetype, $parentcontext, $coursecontext);
1120  
1121          // Page type lists for normal courses.
1122          $testpagetypelist1 = array();
1123          $testpagetypelist1['*'] = 'Any page';
1124          $testpagetypelist1['course-*'] = 'Any course page';
1125          $testpagetypelist1['course-view-*'] = 'Any type of course main page';
1126  
1127          $this->assertEquals($testpagetypelist1, $pagetypelist);
1128  
1129          // Get the context for the front page course.
1130          $sitecoursecontext = context_course::instance(SITEID);
1131          $pagetypelist = course_page_type_list($pagetype, $parentcontext, $sitecoursecontext);
1132  
1133          // Page type list for the front page course.
1134          $testpagetypelist2 = array('*' => 'Any page');
1135          $this->assertEquals($testpagetypelist2, $pagetypelist);
1136  
1137          // Make sure that providing no current context to the function doesn't result in an error.
1138          // Calls made from generate_page_type_patterns() may provide null values.
1139          $pagetypelist = course_page_type_list($pagetype, null, null);
1140          $this->assertEquals($pagetypelist, $testpagetypelist1);
1141      }
1142  
1143      public function test_compare_activities_by_time_desc() {
1144  
1145          // Let's create some test data.
1146          $activitiesivities = array();
1147          $x = new stdClass();
1148          $x->timestamp = null;
1149          $activities[] = $x;
1150  
1151          $x = new stdClass();
1152          $x->timestamp = 1;
1153          $activities[] = $x;
1154  
1155          $x = new stdClass();
1156          $x->timestamp = 3;
1157          $activities[] = $x;
1158  
1159          $x = new stdClass();
1160          $x->timestamp = 0;
1161          $activities[] = $x;
1162  
1163          $x = new stdClass();
1164          $x->timestamp = 5;
1165          $activities[] = $x;
1166  
1167          $x = new stdClass();
1168          $activities[] = $x;
1169  
1170          $x = new stdClass();
1171          $x->timestamp = 5;
1172          $activities[] = $x;
1173  
1174          // Do the sorting.
1175          usort($activities, 'compare_activities_by_time_desc');
1176  
1177          // Let's check the result.
1178          $last = 10;
1179          foreach($activities as $activity) {
1180              if (empty($activity->timestamp)) {
1181                  $activity->timestamp = 0;
1182              }
1183              $this->assertLessThanOrEqual($last, $activity->timestamp);
1184          }
1185      }
1186  
1187      public function test_compare_activities_by_time_asc() {
1188  
1189          // Let's create some test data.
1190          $activities = array();
1191          $x = new stdClass();
1192          $x->timestamp = null;
1193          $activities[] = $x;
1194  
1195          $x = new stdClass();
1196          $x->timestamp = 1;
1197          $activities[] = $x;
1198  
1199          $x = new stdClass();
1200          $x->timestamp = 3;
1201          $activities[] = $x;
1202  
1203          $x = new stdClass();
1204          $x->timestamp = 0;
1205          $activities[] = $x;
1206  
1207          $x = new stdClass();
1208          $x->timestamp = 5;
1209          $activities[] = $x;
1210  
1211          $x = new stdClass();
1212          $activities[] = $x;
1213  
1214          $x = new stdClass();
1215          $x->timestamp = 5;
1216          $activities[] = $x;
1217  
1218          // Do the sorting.
1219          usort($activities, 'compare_activities_by_time_asc');
1220  
1221          // Let's check the result.
1222          $last = 0;
1223          foreach($activities as $activity) {
1224              if (empty($activity->timestamp)) {
1225                  $activity->timestamp = 0;
1226              }
1227              $this->assertGreaterThanOrEqual($last, $activity->timestamp);
1228          }
1229      }
1230  
1231      /**
1232       * Tests moving a module between hidden/visible sections and
1233       * verifies that the course/module visiblity seettings are
1234       * retained.
1235       */
1236      public function test_moveto_module_between_hidden_sections() {
1237          global $DB;
1238  
1239          $this->resetAfterTest(true);
1240  
1241          $course = $this->getDataGenerator()->create_course(array('numsections' => 4), array('createsections' => true));
1242          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1243          $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
1244          $quiz= $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
1245  
1246          // Set the page as hidden
1247          set_coursemodule_visible($page->cmid, 0);
1248  
1249          // Set sections 3 as hidden.
1250          set_section_visible($course->id, 3, 0);
1251  
1252          $modinfo = get_fast_modinfo($course);
1253  
1254          $hiddensection = $modinfo->get_section_info(3);
1255          // New section is definitely not visible:
1256          $this->assertEquals($hiddensection->visible, 0);
1257  
1258          $forumcm = $modinfo->cms[$forum->cmid];
1259          $pagecm = $modinfo->cms[$page->cmid];
1260  
1261          // Move the forum and the page to a hidden section, make sure moveto_module returns 0 as new visibility state.
1262          $this->assertEquals(0, moveto_module($forumcm, $hiddensection));
1263          $this->assertEquals(0, moveto_module($pagecm, $hiddensection));
1264  
1265          $modinfo = get_fast_modinfo($course);
1266  
1267          // Verify that forum and page have been moved to the hidden section and quiz has not.
1268          $this->assertContains($forum->cmid, $modinfo->sections[3]);
1269          $this->assertContains($page->cmid, $modinfo->sections[3]);
1270          $this->assertNotContains($quiz->cmid, $modinfo->sections[3]);
1271  
1272          // Verify that forum has been made invisible.
1273          $forumcm = $modinfo->cms[$forum->cmid];
1274          $this->assertEquals($forumcm->visible, 0);
1275          // Verify that old state has been retained.
1276          $this->assertEquals($forumcm->visibleold, 1);
1277  
1278          // Verify that page has stayed invisible.
1279          $pagecm = $modinfo->cms[$page->cmid];
1280          $this->assertEquals($pagecm->visible, 0);
1281          // Verify that old state has been retained.
1282          $this->assertEquals($pagecm->visibleold, 0);
1283  
1284          // Verify that quiz has been unaffected.
1285          $quizcm = $modinfo->cms[$quiz->cmid];
1286          $this->assertEquals($quizcm->visible, 1);
1287  
1288          // Move forum and page back to visible section.
1289          // Make sure the visibility is restored to the original value (visible for forum and hidden for page).
1290          $visiblesection = $modinfo->get_section_info(2);
1291          $this->assertEquals(1, moveto_module($forumcm, $visiblesection));
1292          $this->assertEquals(0, moveto_module($pagecm, $visiblesection));
1293  
1294          $modinfo = get_fast_modinfo($course);
1295  
1296          // Double check that forum has been made visible.
1297          $forumcm = $modinfo->cms[$forum->cmid];
1298          $this->assertEquals($forumcm->visible, 1);
1299  
1300          // Double check that page has stayed invisible.
1301          $pagecm = $modinfo->cms[$page->cmid];
1302          $this->assertEquals($pagecm->visible, 0);
1303  
1304          // Move the page in the same section (this is what mod duplicate does).
1305          // Visibility of page remains 0.
1306          $this->assertEquals(0, moveto_module($pagecm, $visiblesection, $forumcm));
1307  
1308          // Double check that the the page is still hidden.
1309          $modinfo = get_fast_modinfo($course);
1310          $pagecm = $modinfo->cms[$page->cmid];
1311          $this->assertEquals($pagecm->visible, 0);
1312      }
1313  
1314      /**
1315       * Tests moving a module around in the same section. moveto_module()
1316       * is called this way in modduplicate.
1317       */
1318      public function test_moveto_module_in_same_section() {
1319          global $DB;
1320  
1321          $this->resetAfterTest(true);
1322  
1323          $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
1324          $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
1325          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1326  
1327          // Simulate inconsistent visible/visibleold values (MDL-38713).
1328          $cm = $DB->get_record('course_modules', array('id' => $page->cmid), '*', MUST_EXIST);
1329          $cm->visible = 0;
1330          $cm->visibleold = 1;
1331          $DB->update_record('course_modules', $cm);
1332  
1333          $modinfo = get_fast_modinfo($course);
1334          $forumcm = $modinfo->cms[$forum->cmid];
1335          $pagecm = $modinfo->cms[$page->cmid];
1336  
1337          // Verify that page is hidden.
1338          $this->assertEquals($pagecm->visible, 0);
1339  
1340          // Verify section 0 is where all mods added.
1341          $section = $modinfo->get_section_info(0);
1342          $this->assertEquals($section->id, $forumcm->section);
1343          $this->assertEquals($section->id, $pagecm->section);
1344  
1345  
1346          // Move the page inside the hidden section. Make sure it is hidden.
1347          $this->assertEquals(0, moveto_module($pagecm, $section, $forumcm));
1348  
1349          // Double check that the the page is still hidden.
1350          $modinfo = get_fast_modinfo($course);
1351          $pagecm = $modinfo->cms[$page->cmid];
1352          $this->assertEquals($pagecm->visible, 0);
1353      }
1354  
1355      public function test_course_delete_module() {
1356          global $DB;
1357          $this->resetAfterTest(true);
1358          $this->setAdminUser();
1359  
1360          // Create course and modules.
1361          $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
1362  
1363          // Generate an assignment with due date (will generate a course event).
1364          $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
1365  
1366          // Get the module context.
1367          $modcontext = context_module::instance($assign->cmid);
1368  
1369          // Verify context exists.
1370          $this->assertInstanceOf('context_module', $modcontext);
1371  
1372          // Add some tags to this assignment.
1373          tag_set('assign', $assign->id, array('Tag 1', 'Tag 2', 'Tag 3'), 'mod_assign', $modcontext->id);
1374  
1375          // Confirm the tag instances were added.
1376          $this->assertEquals(3, $DB->count_records('tag_instance', array('component' => 'mod_assign', 'contextid' =>
1377              $modcontext->id)));
1378  
1379          // Verify event assignment event has been generated.
1380          $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
1381          $this->assertEquals(1, $eventcount);
1382  
1383          // Run delete..
1384          course_delete_module($assign->cmid);
1385  
1386          // Verify the context has been removed.
1387          $this->assertFalse(context_module::instance($assign->cmid, IGNORE_MISSING));
1388  
1389          // Verify the course_module record has been deleted.
1390          $cmcount = $DB->count_records('course_modules', array('id' => $assign->cmid));
1391          $this->assertEmpty($cmcount);
1392  
1393          // Verify event assignment events have been removed.
1394          $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
1395          $this->assertEmpty($eventcount);
1396  
1397          // Verify the tag instances were deleted.
1398          $this->assertEquals(0, $DB->count_records('tag_instance', array('component' => 'mod_assign', 'contextid' =>
1399              $modcontext->id)));
1400      }
1401  
1402      /**
1403       * Test that triggering a course_created event works as expected.
1404       */
1405      public function test_course_created_event() {
1406          global $DB;
1407  
1408          $this->resetAfterTest();
1409  
1410          // Catch the events.
1411          $sink = $this->redirectEvents();
1412  
1413          // Create the course with an id number which is used later when generating a course via the imsenterprise plugin.
1414          $data = new stdClass();
1415          $data->idnumber = 'idnumber';
1416          $course = $this->getDataGenerator()->create_course($data);
1417          // Get course from DB for comparison.
1418          $course = $DB->get_record('course', array('id' => $course->id));
1419  
1420          // Capture the event.
1421          $events = $sink->get_events();
1422          $sink->close();
1423  
1424          // Validate the event.
1425          $event = $events[0];
1426          $this->assertInstanceOf('\core\event\course_created', $event);
1427          $this->assertEquals('course', $event->objecttable);
1428          $this->assertEquals($course->id, $event->objectid);
1429          $this->assertEquals(context_course::instance($course->id), $event->get_context());
1430          $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
1431          $this->assertEquals('course_created', $event->get_legacy_eventname());
1432          $this->assertEventLegacyData($course, $event);
1433          $expectedlog = array(SITEID, 'course', 'new', 'view.php?id=' . $course->id, $course->fullname . ' (ID ' . $course->id . ')');
1434          $this->assertEventLegacyLogData($expectedlog, $event);
1435  
1436          // Now we want to trigger creating a course via the imsenterprise.
1437          // Delete the course we created earlier, as we want the imsenterprise plugin to create this.
1438          // We do not want print out any of the text this function generates while doing this, which is why
1439          // we are using ob_start() and ob_end_clean().
1440          ob_start();
1441          delete_course($course);
1442          ob_end_clean();
1443  
1444          // Create the XML file we want to use.
1445          $imstestcase = new enrol_imsenterprise_testcase();
1446          $imstestcase->imsplugin = enrol_get_plugin('imsenterprise');
1447          $imstestcase->set_test_config();
1448          $imstestcase->set_xml_file(false, array($course));
1449  
1450          // Capture the event.
1451          $sink = $this->redirectEvents();
1452          $imstestcase->imsplugin->cron();
1453          $events = $sink->get_events();
1454          $sink->close();
1455          $event = $events[0];
1456  
1457          // Validate the event triggered is \core\event\course_created. There is no need to validate the other values
1458          // as they have already been validated in the previous steps. Here we only want to make sure that when the
1459          // imsenterprise plugin creates a course an event is triggered.
1460          $this->assertInstanceOf('\core\event\course_created', $event);
1461          $this->assertEventContextNotUsed($event);
1462      }
1463  
1464      /**
1465       * Test that triggering a course_updated event works as expected.
1466       */
1467      public function test_course_updated_event() {
1468          global $DB;
1469  
1470          $this->resetAfterTest();
1471  
1472          // Create a course.
1473          $course = $this->getDataGenerator()->create_course();
1474  
1475          // Create a category we are going to move this course to.
1476          $category = $this->getDataGenerator()->create_category();
1477  
1478          // Create a hidden category we are going to move this course to.
1479          $categoryhidden = $this->getDataGenerator()->create_category(array('visible' => 0));
1480  
1481          // Update course and catch course_updated event.
1482          $sink = $this->redirectEvents();
1483          update_course($course);
1484          $events = $sink->get_events();
1485          $sink->close();
1486  
1487          // Get updated course information from the DB.
1488          $updatedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1489          // Validate event.
1490          $event = array_shift($events);
1491          $this->assertInstanceOf('\core\event\course_updated', $event);
1492          $this->assertEquals('course', $event->objecttable);
1493          $this->assertEquals($updatedcourse->id, $event->objectid);
1494          $this->assertEquals(context_course::instance($course->id), $event->get_context());
1495          $url = new moodle_url('/course/edit.php', array('id' => $event->objectid));
1496          $this->assertEquals($url, $event->get_url());
1497          $this->assertEquals($updatedcourse, $event->get_record_snapshot('course', $event->objectid));
1498          $this->assertEquals('course_updated', $event->get_legacy_eventname());
1499          $this->assertEventLegacyData($updatedcourse, $event);
1500          $expectedlog = array($updatedcourse->id, 'course', 'update', 'edit.php?id=' . $course->id, $course->id);
1501          $this->assertEventLegacyLogData($expectedlog, $event);
1502  
1503          // Move course and catch course_updated event.
1504          $sink = $this->redirectEvents();
1505          move_courses(array($course->id), $category->id);
1506          $events = $sink->get_events();
1507          $sink->close();
1508  
1509          // Return the moved course information from the DB.
1510          $movedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1511          // Validate event.
1512          $event = array_shift($events);
1513          $this->assertInstanceOf('\core\event\course_updated', $event);
1514          $this->assertEquals('course', $event->objecttable);
1515          $this->assertEquals($movedcourse->id, $event->objectid);
1516          $this->assertEquals(context_course::instance($course->id), $event->get_context());
1517          $this->assertEquals($movedcourse, $event->get_record_snapshot('course', $movedcourse->id));
1518          $this->assertEquals('course_updated', $event->get_legacy_eventname());
1519          $this->assertEventLegacyData($movedcourse, $event);
1520          $expectedlog = array($movedcourse->id, 'course', 'move', 'edit.php?id=' . $movedcourse->id, $movedcourse->id);
1521          $this->assertEventLegacyLogData($expectedlog, $event);
1522  
1523          // Move course to hidden category and catch course_updated event.
1524          $sink = $this->redirectEvents();
1525          move_courses(array($course->id), $categoryhidden->id);
1526          $events = $sink->get_events();
1527          $sink->close();
1528  
1529          // Return the moved course information from the DB.
1530          $movedcoursehidden = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1531          // Validate event.
1532          $event = array_shift($events);
1533          $this->assertInstanceOf('\core\event\course_updated', $event);
1534          $this->assertEquals('course', $event->objecttable);
1535          $this->assertEquals($movedcoursehidden->id, $event->objectid);
1536          $this->assertEquals(context_course::instance($course->id), $event->get_context());
1537          $this->assertEquals($movedcoursehidden, $event->get_record_snapshot('course', $movedcoursehidden->id));
1538          $this->assertEquals('course_updated', $event->get_legacy_eventname());
1539          $this->assertEventLegacyData($movedcoursehidden, $event);
1540          $expectedlog = array($movedcoursehidden->id, 'course', 'move', 'edit.php?id=' . $movedcoursehidden->id, $movedcoursehidden->id);
1541          $this->assertEventLegacyLogData($expectedlog, $event);
1542          $this->assertEventContextNotUsed($event);
1543      }
1544  
1545      /**
1546       * Test that triggering a course_deleted event works as expected.
1547       */
1548      public function test_course_deleted_event() {
1549          $this->resetAfterTest();
1550  
1551          // Create the course.
1552          $course = $this->getDataGenerator()->create_course();
1553  
1554          // Save the course context before we delete the course.
1555          $coursecontext = context_course::instance($course->id);
1556  
1557          // Catch the update event.
1558          $sink = $this->redirectEvents();
1559  
1560          // Call delete_course() which will trigger the course_deleted event and the course_content_deleted
1561          // event. This function prints out data to the screen, which we do not want during a PHPUnit test,
1562          // so use ob_start and ob_end_clean to prevent this.
1563          ob_start();
1564          delete_course($course);
1565          ob_end_clean();
1566  
1567          // Capture the event.
1568          $events = $sink->get_events();
1569          $sink->close();
1570  
1571          // Validate the event.
1572          $event = $events[1];
1573          $this->assertInstanceOf('\core\event\course_deleted', $event);
1574          $this->assertEquals('course', $event->objecttable);
1575          $this->assertEquals($course->id, $event->objectid);
1576          $this->assertEquals($coursecontext->id, $event->contextid);
1577          $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
1578          $this->assertEquals('course_deleted', $event->get_legacy_eventname());
1579          $eventdata = $event->get_data();
1580          $this->assertSame($course->idnumber, $eventdata['other']['idnumber']);
1581          $this->assertSame($course->fullname, $eventdata['other']['fullname']);
1582          $this->assertSame($course->shortname, $eventdata['other']['shortname']);
1583  
1584          // The legacy data also passed the context in the course object and substitutes timemodified with the current date.
1585          $expectedlegacy = clone($course);
1586          $expectedlegacy->context = $coursecontext;
1587          $expectedlegacy->timemodified = $event->timecreated;
1588          $this->assertEventLegacyData($expectedlegacy, $event);
1589  
1590          // Validate legacy log data.
1591          $expectedlog = array(SITEID, 'course', 'delete', 'view.php?id=' . $course->id, $course->fullname . '(ID ' . $course->id . ')');
1592          $this->assertEventLegacyLogData($expectedlog, $event);
1593          $this->assertEventContextNotUsed($event);
1594      }
1595  
1596      /**
1597       * Test that triggering a course_content_deleted event works as expected.
1598       */
1599      public function test_course_content_deleted_event() {
1600          global $DB;
1601  
1602          $this->resetAfterTest();
1603  
1604          // Create the course.
1605          $course = $this->getDataGenerator()->create_course();
1606  
1607          // Get the course from the DB. The data generator adds some extra properties, such as
1608          // numsections, to the course object which will fail the assertions later on.
1609          $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1610  
1611          // Save the course context before we delete the course.
1612          $coursecontext = context_course::instance($course->id);
1613  
1614          // Catch the update event.
1615          $sink = $this->redirectEvents();
1616  
1617          remove_course_contents($course->id, false);
1618  
1619          // Capture the event.
1620          $events = $sink->get_events();
1621          $sink->close();
1622  
1623          // Validate the event.
1624          $event = $events[0];
1625          $this->assertInstanceOf('\core\event\course_content_deleted', $event);
1626          $this->assertEquals('course', $event->objecttable);
1627          $this->assertEquals($course->id, $event->objectid);
1628          $this->assertEquals($coursecontext->id, $event->contextid);
1629          $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
1630          $this->assertEquals('course_content_removed', $event->get_legacy_eventname());
1631          // The legacy data also passed the context and options in the course object.
1632          $course->context = $coursecontext;
1633          $course->options = array();
1634          $this->assertEventLegacyData($course, $event);
1635          $this->assertEventContextNotUsed($event);
1636      }
1637  
1638      /**
1639       * Test that triggering a course_category_deleted event works as expected.
1640       */
1641      public function test_course_category_deleted_event() {
1642          $this->resetAfterTest();
1643  
1644          // Create a category.
1645          $category = $this->getDataGenerator()->create_category();
1646  
1647          // Save the context before it is deleted.
1648          $categorycontext = context_coursecat::instance($category->id);
1649  
1650          // Catch the update event.
1651          $sink = $this->redirectEvents();
1652  
1653          // Delete the category.
1654          $category->delete_full();
1655  
1656          // Capture the event.
1657          $events = $sink->get_events();
1658          $sink->close();
1659  
1660          // Validate the event.
1661          $event = $events[0];
1662          $this->assertInstanceOf('\core\event\course_category_deleted', $event);
1663          $this->assertEquals('course_categories', $event->objecttable);
1664          $this->assertEquals($category->id, $event->objectid);
1665          $this->assertEquals($categorycontext->id, $event->contextid);
1666          $this->assertEquals('course_category_deleted', $event->get_legacy_eventname());
1667          $this->assertEquals(null, $event->get_url());
1668          $this->assertEventLegacyData($category, $event);
1669          $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category->name . '(ID ' . $category->id . ')');
1670          $this->assertEventLegacyLogData($expectedlog, $event);
1671  
1672          // Create two categories.
1673          $category = $this->getDataGenerator()->create_category();
1674          $category2 = $this->getDataGenerator()->create_category();
1675  
1676          // Save the context before it is moved and then deleted.
1677          $category2context = context_coursecat::instance($category2->id);
1678  
1679          // Catch the update event.
1680          $sink = $this->redirectEvents();
1681  
1682          // Move the category.
1683          $category2->delete_move($category->id);
1684  
1685          // Capture the event.
1686          $events = $sink->get_events();
1687          $sink->close();
1688  
1689          // Validate the event.
1690          $event = $events[0];
1691          $this->assertInstanceOf('\core\event\course_category_deleted', $event);
1692          $this->assertEquals('course_categories', $event->objecttable);
1693          $this->assertEquals($category2->id, $event->objectid);
1694          $this->assertEquals($category2context->id, $event->contextid);
1695          $this->assertEquals('course_category_deleted', $event->get_legacy_eventname());
1696          $this->assertEventLegacyData($category2, $event);
1697          $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category2->name . '(ID ' . $category2->id . ')');
1698          $this->assertEventLegacyLogData($expectedlog, $event);
1699          $this->assertEventContextNotUsed($event);
1700      }
1701  
1702      /**
1703       * Test that triggering a course_restored event works as expected.
1704       */
1705      public function test_course_restored_event() {
1706          global $CFG;
1707  
1708          // Get the necessary files to perform backup and restore.
1709          require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1710          require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1711  
1712          $this->resetAfterTest();
1713  
1714          // Set to admin user.
1715          $this->setAdminUser();
1716  
1717          // The user id is going to be 2 since we are the admin user.
1718          $userid = 2;
1719  
1720          // Create a course.
1721          $course = $this->getDataGenerator()->create_course();
1722  
1723          // Create backup file and save it to the backup location.
1724          $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1725              backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid);
1726          $bc->execute_plan();
1727          $results = $bc->get_results();
1728          $file = $results['backup_destination'];
1729          $fp = get_file_packer();
1730          $filepath = $CFG->dataroot . '/temp/backup/test-restore-course-event';
1731          $file->extract_to_pathname($fp, $filepath);
1732          $bc->destroy();
1733          unset($bc);
1734  
1735          // Now we want to catch the restore course event.
1736          $sink = $this->redirectEvents();
1737  
1738          // Now restore the course to trigger the event.
1739          $rc = new restore_controller('test-restore-course-event', $course->id, backup::INTERACTIVE_NO,
1740              backup::MODE_GENERAL, $userid, backup::TARGET_NEW_COURSE);
1741          $rc->execute_precheck();
1742          $rc->execute_plan();
1743  
1744          // Capture the event.
1745          $events = $sink->get_events();
1746          $sink->close();
1747  
1748          // Validate the event.
1749          $event = $events[0];
1750          $this->assertInstanceOf('\core\event\course_restored', $event);
1751          $this->assertEquals('course', $event->objecttable);
1752          $this->assertEquals($rc->get_courseid(), $event->objectid);
1753          $this->assertEquals(context_course::instance($rc->get_courseid())->id, $event->contextid);
1754          $this->assertEquals('course_restored', $event->get_legacy_eventname());
1755          $legacydata = (object) array(
1756              'courseid' => $rc->get_courseid(),
1757              'userid' => $rc->get_userid(),
1758              'type' => $rc->get_type(),
1759              'target' => $rc->get_target(),
1760              'mode' => $rc->get_mode(),
1761              'operation' => $rc->get_operation(),
1762              'samesite' => $rc->is_samesite()
1763          );
1764          $url = new moodle_url('/course/view.php', array('id' => $event->objectid));
1765          $this->assertEquals($url, $event->get_url());
1766          $this->assertEventLegacyData($legacydata, $event);
1767          $this->assertEventContextNotUsed($event);
1768  
1769          // Destroy the resource controller since we are done using it.
1770          $rc->destroy();
1771          unset($rc);
1772      }
1773  
1774      /**
1775       * Test that triggering a course_section_updated event works as expected.
1776       */
1777      public function test_course_section_updated_event() {
1778          global $DB;
1779  
1780          $this->resetAfterTest();
1781  
1782          // Create the course with sections.
1783          $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true));
1784          $sections = $DB->get_records('course_sections', array('course' => $course->id));
1785  
1786          $coursecontext = context_course::instance($course->id);
1787  
1788          $section = array_pop($sections);
1789          $section->name = 'Test section';
1790          $section->summary = 'Test section summary';
1791          $DB->update_record('course_sections', $section);
1792  
1793          // Trigger an event for course section update.
1794          $event = \core\event\course_section_updated::create(
1795                  array(
1796                      'objectid' => $section->id,
1797                      'courseid' => $course->id,
1798                      'context' => context_course::instance($course->id),
1799                      'other' => array(
1800                          'sectionnum' => $section->section
1801                      )
1802                  )
1803              );
1804          $event->add_record_snapshot('course_sections', $section);
1805          // Trigger and catch event.
1806          $sink = $this->redirectEvents();
1807          $event->trigger();
1808          $events = $sink->get_events();
1809          $sink->close();
1810  
1811          // Validate the event.
1812          $event = $events[0];
1813          $this->assertInstanceOf('\core\event\course_section_updated', $event);
1814          $this->assertEquals('course_sections', $event->objecttable);
1815          $this->assertEquals($section->id, $event->objectid);
1816          $this->assertEquals($course->id, $event->courseid);
1817          $this->assertEquals($coursecontext->id, $event->contextid);
1818          $this->assertEquals($section->section, $event->other['sectionnum']);
1819          $expecteddesc = "The user with id '{$event->userid}' updated section number '{$event->other['sectionnum']}' for the course with id '{$event->courseid}'";
1820          $this->assertEquals($expecteddesc, $event->get_description());
1821          $url = new moodle_url('/course/editsection.php', array('id' => $event->objectid));
1822          $this->assertEquals($url, $event->get_url());
1823          $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid));
1824          $id = $section->id;
1825          $sectionnum = $section->section;
1826          $expectedlegacydata = array($course->id, "course", "editsection", 'editsection.php?id=' . $id, $sectionnum);
1827          $this->assertEventLegacyLogData($expectedlegacydata, $event);
1828          $this->assertEventContextNotUsed($event);
1829      }
1830  
1831      public function test_course_integrity_check() {
1832          global $DB;
1833  
1834          $this->resetAfterTest(true);
1835          $course = $this->getDataGenerator()->create_course(array('numsections' => 1),
1836             array('createsections'=>true));
1837  
1838          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1839                  array('section' => 0));
1840          $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
1841                  array('section' => 0));
1842          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id),
1843                  array('section' => 0));
1844          $correctseq = join(',', array($forum->cmid, $page->cmid, $quiz->cmid));
1845  
1846          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1847          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1848          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1849          $this->assertEquals($correctseq, $section0->sequence);
1850          $this->assertEmpty($section1->sequence);
1851          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1852          $this->assertEquals($section0->id, $cms[$page->cmid]->section);
1853          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1854          $this->assertEmpty(course_integrity_check($course->id));
1855  
1856          // Now let's make manual change in DB and let course_integrity_check() fix it:
1857  
1858          // 1. Module appears twice in one section.
1859          $DB->update_record('course_sections', array('id' => $section0->id, 'sequence' => $section0->sequence. ','. $page->cmid));
1860          $this->assertEquals(
1861                  array('Failed integrity check for course ['. $course->id.
1862                  ']. Sequence for course section ['. $section0->id. '] is "'.
1863                  $section0->sequence. ','. $page->cmid. '", must be "'.
1864                  $section0->sequence. '"'),
1865                  course_integrity_check($course->id));
1866          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1867          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1868          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1869          $this->assertEquals($correctseq, $section0->sequence);
1870          $this->assertEmpty($section1->sequence);
1871          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1872          $this->assertEquals($section0->id, $cms[$page->cmid]->section);
1873          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1874  
1875          // 2. Module appears in two sections (last section wins).
1876          $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''. $page->cmid));
1877          // First message about double mentioning in sequence, second message about wrong section field for $page.
1878          $this->assertEquals(array(
1879              'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1880              '] must be removed from sequence of section ['. $section0->id.
1881              '] because it is also present in sequence of section ['. $section1->id. ']',
1882              'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1883              '] points to section ['. $section0->id. '] instead of ['. $section1->id. ']'),
1884                  course_integrity_check($course->id));
1885          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1886          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1887          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1888          $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1889          $this->assertEquals(''. $page->cmid, $section1->sequence);
1890          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1891          $this->assertEquals($section1->id, $cms[$page->cmid]->section);
1892          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1893  
1894          // 3. Module id is not present in course_section.sequence (integrity check with $fullcheck = false).
1895          $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''));
1896          $this->assertEmpty(course_integrity_check($course->id)); // Not an error!
1897          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1898          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1899          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1900          $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1901          $this->assertEmpty($section1->sequence);
1902          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1903          $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed.
1904          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1905  
1906          // 4. Module id is not present in course_section.sequence (integrity check with $fullcheck = true).
1907          $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['.
1908                  $page->cmid. '] is missing from sequence of section ['. $section1->id. ']'),
1909                  course_integrity_check($course->id, null, null, true)); // Error!
1910          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1911          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1912          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1913          $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1914          $this->assertEquals(''. $page->cmid, $section1->sequence);  // Yay, module added to section.
1915          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1916          $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed.
1917          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1918  
1919          // 5. Module id is not present in course_section.sequence and it's section is invalid (integrity check with $fullcheck = true).
1920          $DB->update_record('course_modules', array('id' => $page->cmid, 'section' => 8765));
1921          $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''));
1922          $this->assertEquals(array(
1923              'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1924              '] is missing from sequence of section ['. $section0->id. ']',
1925              'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1926              '] points to section [8765] instead of ['. $section0->id. ']'),
1927                  course_integrity_check($course->id, null, null, true));
1928          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1929          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1930          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1931          $this->assertEquals($forum->cmid. ','. $quiz->cmid. ','. $page->cmid, $section0->sequence); // Module added to section.
1932          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1933          $this->assertEquals($section0->id, $cms[$page->cmid]->section); // Section changed to section0.
1934          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1935  
1936          // 6. Module is deleted from course_modules but not deleted in sequence (integrity check with $fullcheck = true).
1937          $DB->delete_records('course_modules', array('id' => $page->cmid));
1938          $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['.
1939                  $page->cmid. '] does not exist but is present in the sequence of section ['. $section0->id. ']'),
1940                  course_integrity_check($course->id, null, null, true));
1941          $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1942          $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1943          $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1944          $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1945          $this->assertEmpty($section1->sequence);
1946          $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1947          $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1948          $this->assertEquals(2, count($cms));
1949      }
1950  
1951      /**
1952       * Tests for event related to course module creation.
1953       */
1954      public function test_course_module_created_event() {
1955          global $USER, $DB;
1956          $this->resetAfterTest();
1957  
1958          // Create an assign module.
1959          $sink = $this->redirectEvents();
1960          $modinfo = $this->create_specific_module_test('assign');
1961          $events = $sink->get_events();
1962          $event = array_pop($events);
1963  
1964          $cm = get_coursemodule_from_id('assign', $modinfo->coursemodule, 0, false, MUST_EXIST);
1965          $mod = $DB->get_record('assign', array('id' => $modinfo->instance), '*', MUST_EXIST);
1966  
1967          // Validate event data.
1968          $this->assertInstanceOf('\core\event\course_module_created', $event);
1969          $this->assertEquals($cm->id, $event->objectid);
1970          $this->assertEquals($USER->id, $event->userid);
1971          $this->assertEquals('course_modules', $event->objecttable);
1972          $url = new moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1973          $this->assertEquals($url, $event->get_url());
1974  
1975          // Test legacy data.
1976          $this->assertSame('mod_created', $event->get_legacy_eventname());
1977          $eventdata = new stdClass();
1978          $eventdata->modulename = 'assign';
1979          $eventdata->name       = $mod->name;
1980          $eventdata->cmid       = $cm->id;
1981          $eventdata->courseid   = $cm->course;
1982          $eventdata->userid     = $USER->id;
1983          $this->assertEventLegacyData($eventdata, $event);
1984  
1985          $arr = array(
1986              array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance"),
1987              array($cm->course, "assign", "add", "view.php?id=$cm->id", $cm->instance, $cm->id)
1988          );
1989          $this->assertEventLegacyLogData($arr, $event);
1990          $this->assertEventContextNotUsed($event);
1991  
1992          // Let us see if duplicating an activity results in a nice course module created event.
1993          $sink->clear();
1994          $course = get_course($mod->course);
1995          $newcm = duplicate_module($course, $cm);
1996          $events = $sink->get_events();
1997          $event = array_pop($events);
1998          $sink->close();
1999  
2000          // Validate event data.
2001          $this->assertInstanceOf('\core\event\course_module_created', $event);
2002          $this->assertEquals($newcm->id, $event->objectid);
2003          $this->assertEquals($USER->id, $event->userid);
2004          $this->assertEquals($course->id, $event->courseid);
2005          $url = new moodle_url('/mod/assign/view.php', array('id' => $newcm->id));
2006          $this->assertEquals($url, $event->get_url());
2007      }
2008  
2009      /**
2010       * Tests for event validations related to course module creation.
2011       */
2012      public function test_course_module_created_event_exceptions() {
2013  
2014          $this->resetAfterTest();
2015  
2016          // Generate data.
2017          $modinfo = $this->create_specific_module_test('assign');
2018          $context = context_module::instance($modinfo->coursemodule);
2019  
2020          // Test not setting instanceid.
2021          try {
2022              $event = \core\event\course_module_created::create(array(
2023                  'courseid' => $modinfo->course,
2024                  'context'  => $context,
2025                  'objectid' => $modinfo->coursemodule,
2026                  'other'    => array(
2027                      'modulename' => 'assign',
2028                      'name'       => 'My assignment',
2029                  )
2030              ));
2031              $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
2032                      other['instanceid']");
2033          } catch (coding_exception $e) {
2034              $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage());
2035          }
2036  
2037          // Test not setting modulename.
2038          try {
2039              $event = \core\event\course_module_created::create(array(
2040                  'courseid' => $modinfo->course,
2041                  'context'  => $context,
2042                  'objectid' => $modinfo->coursemodule,
2043                  'other'    => array(
2044                      'instanceid' => $modinfo->instance,
2045                      'name'       => 'My assignment',
2046                  )
2047              ));
2048              $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
2049                      other['modulename']");
2050          } catch (coding_exception $e) {
2051              $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage());
2052          }
2053  
2054          // Test not setting name.
2055  
2056          try {
2057              $event = \core\event\course_module_created::create(array(
2058                  'courseid' => $modinfo->course,
2059                  'context'  => $context,
2060                  'objectid' => $modinfo->coursemodule,
2061                  'other'    => array(
2062                      'modulename' => 'assign',
2063                      'instanceid' => $modinfo->instance,
2064                  )
2065              ));
2066              $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
2067                      other['name']");
2068          } catch (coding_exception $e) {
2069              $this->assertContains("The 'name' value must be set in other.", $e->getMessage());
2070          }
2071  
2072      }
2073  
2074      /**
2075       * Tests for event related to course module updates.
2076       */
2077      public function test_course_module_updated_event() {
2078          global $USER, $DB;
2079          $this->resetAfterTest();
2080  
2081          // Update a forum module.
2082          $sink = $this->redirectEvents();
2083          $modinfo = $this->update_specific_module_test('forum');
2084          $events = $sink->get_events();
2085          $event = array_pop($events);
2086          $sink->close();
2087  
2088          $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
2089          $mod = $DB->get_record('forum', array('id' => $cm->instance), '*', MUST_EXIST);
2090  
2091          // Validate event data.
2092          $this->assertInstanceOf('\core\event\course_module_updated', $event);
2093          $this->assertEquals($cm->id, $event->objectid);
2094          $this->assertEquals($USER->id, $event->userid);
2095          $this->assertEquals('course_modules', $event->objecttable);
2096          $url = new moodle_url('/mod/forum/view.php', array('id' => $cm->id));
2097          $this->assertEquals($url, $event->get_url());
2098  
2099          // Test legacy data.
2100          $this->assertSame('mod_updated', $event->get_legacy_eventname());
2101          $eventdata = new stdClass();
2102          $eventdata->modulename = 'forum';
2103          $eventdata->name       = $mod->name;
2104          $eventdata->cmid       = $cm->id;
2105          $eventdata->courseid   = $cm->course;
2106          $eventdata->userid     = $USER->id;
2107          $this->assertEventLegacyData($eventdata, $event);
2108  
2109          $arr = array(
2110              array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$cm->id", "forum $cm->instance"),
2111              array($cm->course, "forum", "update", "view.php?id=$cm->id", $cm->instance, $cm->id)
2112          );
2113          $this->assertEventLegacyLogData($arr, $event);
2114          $this->assertEventContextNotUsed($event);
2115      }
2116  
2117      /**
2118       * Tests for create_from_cm method.
2119       */
2120      public function test_course_module_create_from_cm() {
2121          $this->resetAfterTest();
2122          $this->setAdminUser();
2123  
2124          // Create course and modules.
2125          $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
2126  
2127          // Generate an assignment.
2128          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
2129  
2130          // Get the module context.
2131          $modcontext = context_module::instance($assign->cmid);
2132  
2133          // Get course module.
2134          $cm = get_coursemodule_from_id(null, $assign->cmid, $course->id, false, MUST_EXIST);
2135  
2136          // Create an event from course module.
2137          $event = \core\event\course_module_updated::create_from_cm($cm, $modcontext);
2138  
2139          // Trigger the events.
2140          $sink = $this->redirectEvents();
2141          $event->trigger();
2142          $events = $sink->get_events();
2143          $event2 = array_pop($events);
2144  
2145          // Test event data.
2146          $this->assertInstanceOf('\core\event\course_module_updated', $event);
2147          $this->assertEquals($cm->id, $event2->objectid);
2148          $this->assertEquals($modcontext, $event2->get_context());
2149          $this->assertEquals($cm->modname, $event2->other['modulename']);
2150          $this->assertEquals($cm->instance, $event2->other['instanceid']);
2151          $this->assertEquals($cm->name, $event2->other['name']);
2152          $this->assertEventContextNotUsed($event2);
2153          $this->assertSame('mod_updated', $event2->get_legacy_eventname());
2154          $arr = array(
2155              array($cm->course, "course", "update mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance"),
2156              array($cm->course, "assign", "update", "view.php?id=$cm->id", $cm->instance, $cm->id)
2157          );
2158          $this->assertEventLegacyLogData($arr, $event);
2159      }
2160  
2161      /**
2162       * Tests for event validations related to course module update.
2163       */
2164      public function test_course_module_updated_event_exceptions() {
2165  
2166          $this->resetAfterTest();
2167  
2168          // Generate data.
2169          $modinfo = $this->create_specific_module_test('assign');
2170          $context = context_module::instance($modinfo->coursemodule);
2171  
2172          // Test not setting instanceid.
2173          try {
2174              $event = \core\event\course_module_updated::create(array(
2175                  'courseid' => $modinfo->course,
2176                  'context'  => $context,
2177                  'objectid' => $modinfo->coursemodule,
2178                  'other'    => array(
2179                      'modulename' => 'assign',
2180                      'name'       => 'My assignment',
2181                  )
2182              ));
2183              $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
2184                      other['instanceid']");
2185          } catch (coding_exception $e) {
2186              $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage());
2187          }
2188  
2189          // Test not setting modulename.
2190          try {
2191              $event = \core\event\course_module_updated::create(array(
2192                  'courseid' => $modinfo->course,
2193                  'context'  => $context,
2194                  'objectid' => $modinfo->coursemodule,
2195                  'other'    => array(
2196                      'instanceid' => $modinfo->instance,
2197                      'name'       => 'My assignment',
2198                  )
2199              ));
2200              $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
2201                      other['modulename']");
2202          } catch (coding_exception $e) {
2203              $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage());
2204          }
2205  
2206          // Test not setting name.
2207  
2208          try {
2209              $event = \core\event\course_module_updated::create(array(
2210                  'courseid' => $modinfo->course,
2211                  'context'  => $context,
2212                  'objectid' => $modinfo->coursemodule,
2213                  'other'    => array(
2214                      'modulename' => 'assign',
2215                      'instanceid' => $modinfo->instance,
2216                  )
2217              ));
2218              $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
2219                      other['name']");
2220          } catch (coding_exception $e) {
2221              $this->assertContains("The 'name' value must be set in other.", $e->getMessage());
2222          }
2223  
2224      }
2225  
2226      /**
2227       * Tests for event related to course module delete.
2228       */
2229      public function test_course_module_deleted_event() {
2230          global $USER, $DB;
2231          $this->resetAfterTest();
2232  
2233          // Create and delete a module.
2234          $sink = $this->redirectEvents();
2235          $modinfo = $this->create_specific_module_test('forum');
2236          $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
2237          course_delete_module($modinfo->coursemodule);
2238          $events = $sink->get_events();
2239          $event = array_pop($events); // delete module event.;
2240          $sink->close();
2241  
2242          // Validate event data.
2243          $this->assertInstanceOf('\core\event\course_module_deleted', $event);
2244          $this->assertEquals($cm->id, $event->objectid);
2245          $this->assertEquals($USER->id, $event->userid);
2246          $this->assertEquals('course_modules', $event->objecttable);
2247          $this->assertEquals(null, $event->get_url());
2248          $this->assertEquals($cm, $event->get_record_snapshot('course_modules', $cm->id));
2249  
2250          // Test legacy data.
2251          $this->assertSame('mod_deleted', $event->get_legacy_eventname());
2252          $eventdata = new stdClass();
2253          $eventdata->modulename = 'forum';
2254          $eventdata->cmid       = $cm->id;
2255          $eventdata->courseid   = $cm->course;
2256          $eventdata->userid     = $USER->id;
2257          $this->assertEventLegacyData($eventdata, $event);
2258  
2259          $arr = array($cm->course, 'course', "delete mod", "view.php?id=$cm->course", "forum $cm->instance", $cm->id);
2260          $this->assertEventLegacyLogData($arr, $event);
2261  
2262      }
2263  
2264      /**
2265       * Tests for event validations related to course module deletion.
2266       */
2267      public function test_course_module_deleted_event_exceptions() {
2268  
2269          $this->resetAfterTest();
2270  
2271          // Generate data.
2272          $modinfo = $this->create_specific_module_test('assign');
2273          $context = context_module::instance($modinfo->coursemodule);
2274  
2275          // Test not setting instanceid.
2276          try {
2277              $event = \core\event\course_module_deleted::create(array(
2278                  'courseid' => $modinfo->course,
2279                  'context'  => $context,
2280                  'objectid' => $modinfo->coursemodule,
2281                  'other'    => array(
2282                      'modulename' => 'assign',
2283                      'name'       => 'My assignment',
2284                  )
2285              ));
2286              $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without
2287                      other['instanceid']");
2288          } catch (coding_exception $e) {
2289              $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage());
2290          }
2291  
2292          // Test not setting modulename.
2293          try {
2294              $event = \core\event\course_module_deleted::create(array(
2295                  'courseid' => $modinfo->course,
2296                  'context'  => $context,
2297                  'objectid' => $modinfo->coursemodule,
2298                  'other'    => array(
2299                      'instanceid' => $modinfo->instance,
2300                      'name'       => 'My assignment',
2301                  )
2302              ));
2303              $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without
2304                      other['modulename']");
2305          } catch (coding_exception $e) {
2306              $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage());
2307          }
2308      }
2309  
2310      /**
2311       * Returns a user object and its assigned new role.
2312       *
2313       * @param testing_data_generator $generator
2314       * @param $contextid
2315       * @return array The user object and the role ID
2316       */
2317      protected function get_user_objects(testing_data_generator $generator, $contextid) {
2318          global $USER;
2319  
2320          if (empty($USER->id)) {
2321              $user  = $generator->create_user();
2322              $this->setUser($user);
2323          }
2324          $roleid = create_role('Test role', 'testrole', 'Test role description');
2325          if (!is_array($contextid)) {
2326              $contextid = array($contextid);
2327          }
2328          foreach ($contextid as $cid) {
2329              $assignid = role_assign($roleid, $user->id, $cid);
2330          }
2331          return array($user, $roleid);
2332      }
2333  
2334      /**
2335       * Test course move after course.
2336       */
2337      public function test_course_change_sortorder_after_course() {
2338          global $DB;
2339  
2340          $this->resetAfterTest(true);
2341  
2342          $generator = $this->getDataGenerator();
2343          $category = $generator->create_category();
2344          $course3 = $generator->create_course(array('category' => $category->id));
2345          $course2 = $generator->create_course(array('category' => $category->id));
2346          $course1 = $generator->create_course(array('category' => $category->id));
2347          $context = $category->get_context();
2348  
2349          list($user, $roleid) = $this->get_user_objects($generator, $context->id);
2350          $caps = course_capability_assignment::allow('moodle/category:manage', $roleid, $context->id);
2351  
2352          $courses = $category->get_courses();
2353          $this->assertInternalType('array', $courses);
2354          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
2355          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2356          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2357  
2358          // Test moving down.
2359          $this->assertTrue(course_change_sortorder_after_course($course1->id, $course3->id));
2360          $courses = $category->get_courses();
2361          $this->assertInternalType('array', $courses);
2362          $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses));
2363          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2364          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2365  
2366          // Test moving up.
2367          $this->assertTrue(course_change_sortorder_after_course($course1->id, $course2->id));
2368          $courses = $category->get_courses();
2369          $this->assertInternalType('array', $courses);
2370          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
2371          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2372          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2373  
2374          // Test moving to the top.
2375          $this->assertTrue(course_change_sortorder_after_course($course1->id, 0));
2376          $courses = $category->get_courses();
2377          $this->assertInternalType('array', $courses);
2378          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
2379          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2380          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2381      }
2382  
2383      /**
2384       * Tests changing the visibility of a course.
2385       */
2386      public function test_course_change_visibility() {
2387          global $DB;
2388  
2389          $this->resetAfterTest(true);
2390  
2391          $generator = $this->getDataGenerator();
2392          $category = $generator->create_category();
2393          $course = $generator->create_course(array('category' => $category->id));
2394  
2395          $this->assertEquals('1', $course->visible);
2396          $this->assertEquals('1', $course->visibleold);
2397  
2398          $this->assertTrue(course_change_visibility($course->id, false));
2399          $course = $DB->get_record('course', array('id' => $course->id));
2400          $this->assertEquals('0', $course->visible);
2401          $this->assertEquals('0', $course->visibleold);
2402  
2403          $this->assertTrue(course_change_visibility($course->id, true));
2404          $course = $DB->get_record('course', array('id' => $course->id));
2405          $this->assertEquals('1', $course->visible);
2406          $this->assertEquals('1', $course->visibleold);
2407      }
2408  
2409      /**
2410       * Tests moving the course up and down by one.
2411       */
2412      public function test_course_change_sortorder_by_one() {
2413          global $DB;
2414  
2415          $this->resetAfterTest(true);
2416  
2417          $generator = $this->getDataGenerator();
2418          $category = $generator->create_category();
2419          $course3 = $generator->create_course(array('category' => $category->id));
2420          $course2 = $generator->create_course(array('category' => $category->id));
2421          $course1 = $generator->create_course(array('category' => $category->id));
2422  
2423          $courses = $category->get_courses();
2424          $this->assertInternalType('array', $courses);
2425          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
2426          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2427          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2428  
2429          // Test moving down.
2430          $course1 = get_course($course1->id);
2431          $this->assertTrue(course_change_sortorder_by_one($course1, false));
2432          $courses = $category->get_courses();
2433          $this->assertInternalType('array', $courses);
2434          $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
2435          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2436          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2437  
2438          // Test moving up.
2439          $course1 = get_course($course1->id);
2440          $this->assertTrue(course_change_sortorder_by_one($course1, true));
2441          $courses = $category->get_courses();
2442          $this->assertInternalType('array', $courses);
2443          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
2444          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2445          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2446  
2447          // Test moving the top course up one.
2448          $course1 = get_course($course1->id);
2449          $this->assertFalse(course_change_sortorder_by_one($course1, true));
2450          // Check nothing changed.
2451          $courses = $category->get_courses();
2452          $this->assertInternalType('array', $courses);
2453          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
2454          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2455          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2456  
2457          // Test moving the bottom course up down.
2458          $course3 = get_course($course3->id);
2459          $this->assertFalse(course_change_sortorder_by_one($course3, false));
2460          // Check nothing changed.
2461          $courses = $category->get_courses();
2462          $this->assertInternalType('array', $courses);
2463          $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
2464          $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
2465          $this->assertEquals(array_keys($dbcourses), array_keys($courses));
2466      }
2467  
2468      public function test_view_resources_list() {
2469          $this->resetAfterTest();
2470  
2471          $course = self::getDataGenerator()->create_course();
2472          $coursecontext = context_course::instance($course->id);
2473  
2474          $event = \core\event\course_resources_list_viewed::create(array('context' => context_course::instance($course->id)));
2475          $event->set_legacy_logdata(array('book', 'page', 'resource'));
2476          $sink = $this->redirectEvents();
2477          $event->trigger();
2478          $events = $sink->get_events();
2479          $sink->close();
2480  
2481          // Validate the event.
2482          $event = $events[0];
2483          $this->assertInstanceOf('\core\event\course_resources_list_viewed', $event);
2484          $this->assertEquals(null, $event->objecttable);
2485          $this->assertEquals(null, $event->objectid);
2486          $this->assertEquals($course->id, $event->courseid);
2487          $this->assertEquals($coursecontext->id, $event->contextid);
2488          $expectedlegacydata = array(
2489              array($course->id, "book", "view all", 'index.php?id=' . $course->id, ''),
2490              array($course->id, "page", "view all", 'index.php?id=' . $course->id, ''),
2491              array($course->id, "resource", "view all", 'index.php?id=' . $course->id, ''),
2492          );
2493          $this->assertEventLegacyLogData($expectedlegacydata, $event);
2494          $this->assertEventContextNotUsed($event);
2495      }
2496  
2497      /**
2498       * Test duplicate_module()
2499       */
2500      public function test_duplicate_module() {
2501          $this->setAdminUser();
2502          $this->resetAfterTest();
2503          $course = self::getDataGenerator()->create_course();
2504          $res = self::getDataGenerator()->create_module('resource', array('course' => $course));
2505          $cm = get_coursemodule_from_id('resource', $res->cmid, 0, false, MUST_EXIST);
2506  
2507          $newcm = duplicate_module($course, $cm);
2508  
2509          // Make sure they are the same, except obvious id changes.
2510          foreach ($cm as $prop => $value) {
2511              if ($prop == 'id' || $prop == 'url' || $prop == 'instance' || $prop == 'added') {
2512                  // Ignore obviously different properties.
2513                  continue;
2514              }
2515              $this->assertEquals($value, $newcm->$prop);
2516          }
2517      }
2518  
2519      /**
2520       * Tests that when creating or updating a module, if the availability settings
2521       * are present but set to an empty tree, availability is set to null in
2522       * database.
2523       */
2524      public function test_empty_availability_settings() {
2525          global $DB;
2526          $this->setAdminUser();
2527          $this->resetAfterTest();
2528  
2529          // Enable availability.
2530          set_config('enableavailability', 1);
2531  
2532          // Test add.
2533          $emptyavailability = json_encode(\core_availability\tree::get_root_json(array()));
2534          $course = self::getDataGenerator()->create_course();
2535          $label = self::getDataGenerator()->create_module('label', array(
2536                  'course' => $course, 'availability' => $emptyavailability));
2537          $this->assertNull($DB->get_field('course_modules', 'availability',
2538                  array('id' => $label->cmid)));
2539  
2540          // Test update.
2541          $formdata = $DB->get_record('course_modules', array('id' => $label->cmid));
2542          unset($formdata->availability);
2543          $formdata->availabilityconditionsjson = $emptyavailability;
2544          $formdata->modulename = 'label';
2545          $formdata->coursemodule = $label->cmid;
2546          $draftid = 0;
2547          file_prepare_draft_area($draftid, context_module::instance($label->cmid)->id,
2548                  'mod_label', 'intro', 0);
2549          $formdata->introeditor = array(
2550              'itemid' => $draftid,
2551              'text' => '<p>Yo</p>',
2552              'format' => FORMAT_HTML);
2553          update_module($formdata);
2554          $this->assertNull($DB->get_field('course_modules', 'availability',
2555                  array('id' => $label->cmid)));
2556      }
2557  }


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