~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to course/tests/courselib_test.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
 
28
28
global $CFG;
29
29
require_once($CFG->dirroot.'/course/lib.php');
 
30
require_once($CFG->dirroot.'/course/tests/fixtures/course_capability_assignment.php');
30
31
 
31
 
class courselib_testcase extends advanced_testcase {
 
32
class core_course_courselib_testcase extends advanced_testcase {
32
33
 
33
34
    /**
34
35
     * Set forum specific test values for calling create_module().
44
45
        // Specific values to the Forum module.
45
46
        $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE;
46
47
        $moduleinfo->type = 'single';
47
 
        $moduleinfo->trackingtype = FORUM_TRACKING_ON;
 
48
        $moduleinfo->trackingtype = FORUM_TRACKING_FORCED;
48
49
        $moduleinfo->maxbytes = 10240;
49
50
        $moduleinfo->maxattachments = 2;
50
51
 
100
101
        $moduleinfo->requireallteammemberssubmit = true;
101
102
        $moduleinfo->teamsubmissiongroupingid = true;
102
103
        $moduleinfo->blindmarking = true;
 
104
        $moduleinfo->markingworkflow = true;
 
105
        $moduleinfo->markingallocation = true;
103
106
        $moduleinfo->assignsubmission_onlinetext_enabled = true;
104
107
        $moduleinfo->assignsubmission_file_enabled = true;
105
108
        $moduleinfo->assignsubmission_file_maxfiles = 1;
134
137
        $this->assertEquals($moduleinfo->requireallteammemberssubmit, $dbmodinstance->requireallteammemberssubmit);
135
138
        $this->assertEquals($moduleinfo->teamsubmissiongroupingid, $dbmodinstance->teamsubmissiongroupingid);
136
139
        $this->assertEquals($moduleinfo->blindmarking, $dbmodinstance->blindmarking);
 
140
        $this->assertEquals($moduleinfo->markingworkflow, $dbmodinstance->markingworkflow);
 
141
        $this->assertEquals($moduleinfo->markingallocation, $dbmodinstance->markingallocation);
137
142
        // The goal not being to fully test assign_add_instance() we'll stop here for the assign tests - to avoid too many DB queries.
138
143
 
139
144
        // Advanced grading.
140
 
        $contextmodule = context_module::instance($dbmodinstance->id);
 
145
        $cm = get_coursemodule_from_instance('assign', $dbmodinstance->id);
 
146
        $contextmodule = context_module::instance($cm->id);
141
147
        $advancedgradingmethod = $DB->get_record('grading_areas',
142
148
            array('contextid' => $contextmodule->id,
143
149
                'activemethod' => $moduleinfo->advancedgradingmethod_submissions));
313
319
        // Test specific to the module.
314
320
        $modulerunasserts = $modulename.'_create_run_asserts';
315
321
        $this->$modulerunasserts($moduleinfo, $dbmodinstance);
 
322
        return $moduleinfo;
316
323
    }
317
324
 
318
325
    /**
355
362
        // Specific values to the Forum module.
356
363
        $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE;
357
364
        $moduleinfo->type = 'single';
358
 
        $moduleinfo->trackingtype = FORUM_TRACKING_ON;
 
365
        $moduleinfo->trackingtype = FORUM_TRACKING_FORCED;
359
366
        $moduleinfo->maxbytes = 10240;
360
367
        $moduleinfo->maxattachments = 2;
361
368
 
565
572
        // Test specific to the module.
566
573
        $modulerunasserts = $modulename.'_update_run_asserts';
567
574
        $this->$modulerunasserts($moduleinfo, $dbmodinstance);
 
575
        return $moduleinfo;
568
576
   }
569
577
 
570
578
 
598
606
        // Ensure blocks have been associated to the course.
599
607
        $blockcount = $DB->count_records('block_instances', array('parentcontextid' => $context->id));
600
608
        $this->assertGreaterThan(0, $blockcount);
 
609
 
 
610
        // Ensure that the shortname isn't duplicated.
 
611
        try {
 
612
            $created = create_course($course);
 
613
            $this->fail('Exception expected');
 
614
        } catch (moodle_exception $e) {
 
615
            $this->assertSame(get_string('shortnametaken', 'error', $course->shortname), $e->getMessage());
 
616
        }
 
617
 
 
618
        // Ensure that the idnumber isn't duplicated.
 
619
        $course->shortname .= '1';
 
620
        try {
 
621
            $created = create_course($course);
 
622
            $this->fail('Exception expected');
 
623
        } catch (moodle_exception $e) {
 
624
            $this->assertSame(get_string('courseidnumbertaken', 'error', $course->idnumber), $e->getMessage());
 
625
        }
601
626
    }
602
627
 
603
628
    public function test_create_course_with_generator() {
620
645
                    'numsections' => 5),
621
646
                array('createsections' => true));
622
647
 
623
 
        // Ensure all 6 (0-5) sections were created and modinfo/sectioninfo cache works properly
 
648
        // Ensure all 6 (0-5) sections were created and course content cache works properly
624
649
        $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
625
650
        $this->assertEquals(range(0, $course->numsections), $sectionscreated);
626
651
 
639
664
        global $DB;
640
665
 
641
666
        $this->resetAfterTest();
642
 
        $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
 
667
 
 
668
        $defaultcategory = $DB->get_field_select('course_categories', 'MIN(id)', 'parent = 0');
643
669
 
644
670
        $course = new stdClass();
645
671
        $course->fullname = 'Apu loves Unit Təsts';
667
693
            update_course($created2);
668
694
            $this->fail('Expected exception when trying to update a course with duplicate idnumber');
669
695
        } catch (moodle_exception $e) {
670
 
            $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 
696
            $this->assertEquals(get_string('courseidnumbertaken', 'error', $created2->idnumber), $e->getMessage());
671
697
        }
672
698
 
673
699
        // Test duplicate shortname.
709
735
        $this->assertEquals($cmids[0], $sequence);
710
736
 
711
737
        // Add a second, this time using courseid variant of parameters.
 
738
        $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
712
739
        course_add_cm_to_section($course->id, $cmids[1], 1);
713
740
        $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
714
741
        $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence);
715
742
 
716
 
        // Check modinfo was not rebuilt (important for performance if calling
717
 
        // repeatedly).
718
 
        $this->assertNull($DB->get_field('course', 'modinfo', array('id' => $course->id)));
 
743
        // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly).
 
744
        $this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id)));
 
745
        $this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id));
719
746
 
720
747
        // Add one to section that doesn't exist (this might rebuild modinfo).
721
748
        course_add_cm_to_section($course, $cmids[2], 2);
898
925
        // Perform the move
899
926
        moveto_module($cm, $newsection);
900
927
 
901
 
        // reset of get_fast_modinfo is usually called the code calling moveto_module so call it here
902
 
        get_fast_modinfo(0, 0, true);
903
928
        $cms = get_fast_modinfo($course)->get_cms();
904
929
        $cm = reset($cms);
905
930
 
925
950
        // Perform a second move as some issues were only seen on the second move
926
951
        $newsection = get_fast_modinfo($course)->get_section_info(2);
927
952
        $oldsectionid = $cm->section;
928
 
        $result = moveto_module($cm, $newsection);
929
 
        $this->assertTrue($result);
 
953
        moveto_module($cm, $newsection);
930
954
 
931
 
        // reset of get_fast_modinfo is usually called the code calling moveto_module so call it here
932
 
        get_fast_modinfo(0, 0, true);
933
955
        $cms = get_fast_modinfo($course)->get_cms();
934
956
        $cm = reset($cms);
935
957
 
972
994
        }
973
995
    }
974
996
 
 
997
    public function test_section_visibility_events() {
 
998
        $this->setAdminUser();
 
999
        $this->resetAfterTest(true);
 
1000
 
 
1001
        $course = $this->getDataGenerator()->create_course(array('numsections' => 1), array('createsections' => true));
 
1002
        $sectionnumber = 1;
 
1003
        $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
 
1004
            array('section' => $sectionnumber));
 
1005
        $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
 
1006
            'course' => $course->id), array('section' => $sectionnumber));
 
1007
        $sink = $this->redirectEvents();
 
1008
        set_section_visible($course->id, $sectionnumber, 0);
 
1009
        $events = $sink->get_events();
 
1010
 
 
1011
        // Extract the number of events related to what we are testing, other events
 
1012
        // such as course_section_updated could have been triggered.
 
1013
        $count = 0;
 
1014
        foreach ($events as $event) {
 
1015
            if ($event instanceof \core\event\course_module_updated) {
 
1016
                $count++;
 
1017
            }
 
1018
        }
 
1019
        $this->assertSame(2, $count);
 
1020
        $sink->close();
 
1021
    }
 
1022
 
975
1023
    public function test_section_visibility() {
976
1024
        $this->setAdminUser();
977
1025
        $this->resetAfterTest(true);
1239
1287
        $forumcm = $modinfo->cms[$forum->cmid];
1240
1288
        $pagecm = $modinfo->cms[$page->cmid];
1241
1289
 
1242
 
        // Move the forum and the page to a hidden section.
1243
 
        moveto_module($forumcm, $hiddensection);
1244
 
        moveto_module($pagecm, $hiddensection);
1245
 
 
1246
 
        // Reset modinfo cache.
1247
 
        get_fast_modinfo(0, 0, true);
 
1290
        // Move the forum and the page to a hidden section, make sure moveto_module returns 0 as new visibility state.
 
1291
        $this->assertEquals(0, moveto_module($forumcm, $hiddensection));
 
1292
        $this->assertEquals(0, moveto_module($pagecm, $hiddensection));
1248
1293
 
1249
1294
        $modinfo = get_fast_modinfo($course);
1250
1295
 
1270
1315
        $this->assertEquals($quizcm->visible, 1);
1271
1316
 
1272
1317
        // Move forum and page back to visible section.
 
1318
        // Make sure the visibility is restored to the original value (visible for forum and hidden for page).
1273
1319
        $visiblesection = $modinfo->get_section_info(2);
1274
 
        moveto_module($forumcm, $visiblesection);
1275
 
        moveto_module($pagecm, $visiblesection);
 
1320
        $this->assertEquals(1, moveto_module($forumcm, $visiblesection));
 
1321
        $this->assertEquals(0, moveto_module($pagecm, $visiblesection));
1276
1322
 
1277
 
        // Reset modinfo cache.
1278
 
        get_fast_modinfo(0, 0, true);
1279
1323
        $modinfo = get_fast_modinfo($course);
1280
1324
 
1281
 
        // Verify that forum has been made visible.
 
1325
        // Double check that forum has been made visible.
1282
1326
        $forumcm = $modinfo->cms[$forum->cmid];
1283
1327
        $this->assertEquals($forumcm->visible, 1);
1284
1328
 
1285
 
        // Verify that page has stayed invisible.
 
1329
        // Double check that page has stayed invisible.
1286
1330
        $pagecm = $modinfo->cms[$page->cmid];
1287
1331
        $this->assertEquals($pagecm->visible, 0);
1288
1332
 
1289
 
        // Move the page in the same section (this is what mod duplicate does_
1290
 
        moveto_module($pagecm, $visiblesection, $forumcm);
1291
 
 
1292
 
        // Reset modinfo cache.
1293
 
        get_fast_modinfo(0, 0, true);
1294
 
 
1295
 
        // Verify that the the page is still hidden
 
1333
        // Move the page in the same section (this is what mod duplicate does).
 
1334
        // Visibility of page remains 0.
 
1335
        $this->assertEquals(0, moveto_module($pagecm, $visiblesection, $forumcm));
 
1336
 
 
1337
        // Double check that the the page is still hidden.
1296
1338
        $modinfo = get_fast_modinfo($course);
1297
1339
        $pagecm = $modinfo->cms[$page->cmid];
1298
1340
        $this->assertEquals($pagecm->visible, 0);
1330
1372
        $this->assertEquals($section->id, $pagecm->section);
1331
1373
 
1332
1374
 
1333
 
        // Move the forum and the page to a hidden section.
1334
 
        moveto_module($pagecm, $section, $forumcm);
1335
 
 
1336
 
        // Reset modinfo cache.
1337
 
        get_fast_modinfo(0, 0, true);
1338
 
 
1339
 
        // Verify that the the page is still hidden
 
1375
        // Move the page inside the hidden section. Make sure it is hidden.
 
1376
        $this->assertEquals(0, moveto_module($pagecm, $section, $forumcm));
 
1377
 
 
1378
        // Double check that the the page is still hidden.
1340
1379
        $modinfo = get_fast_modinfo($course);
1341
1380
        $pagecm = $modinfo->cms[$page->cmid];
1342
1381
        $this->assertEquals($pagecm->visible, 0);
1343
1382
    }
 
1383
 
 
1384
    public function test_course_delete_module() {
 
1385
        global $DB;
 
1386
        $this->resetAfterTest(true);
 
1387
        $this->setAdminUser();
 
1388
 
 
1389
        // Create course and modules.
 
1390
        $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
 
1391
 
 
1392
        // Generate an assignment with due date (will generate a course event).
 
1393
        $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
 
1394
 
 
1395
        $cm = get_coursemodule_from_instance('assign', $assign->id);
 
1396
 
 
1397
        // Verify context exists.
 
1398
        $this->assertInstanceOf('context_module', context_module::instance($cm->id, IGNORE_MISSING));
 
1399
 
 
1400
        // Verify event assignment event has been generated.
 
1401
        $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
 
1402
        $this->assertEquals(1, $eventcount);
 
1403
 
 
1404
        // Run delete..
 
1405
        course_delete_module($cm->id);
 
1406
 
 
1407
        // Verify the context has been removed.
 
1408
        $this->assertFalse(context_module::instance($cm->id, IGNORE_MISSING));
 
1409
 
 
1410
        // Verify the course_module record has been deleted.
 
1411
        $cmcount = $DB->count_records('course_modules', array('id' => $cm->id));
 
1412
        $this->assertEmpty($cmcount);
 
1413
 
 
1414
        // Verify event assignment events have been removed.
 
1415
        $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
 
1416
        $this->assertEmpty($eventcount);
 
1417
    }
 
1418
 
 
1419
    /**
 
1420
     * Test that triggering a course_created event works as expected.
 
1421
     */
 
1422
    public function test_course_created_event() {
 
1423
        global $DB;
 
1424
 
 
1425
        $this->resetAfterTest();
 
1426
 
 
1427
        // Catch the events.
 
1428
        $sink = $this->redirectEvents();
 
1429
 
 
1430
        // Create the course.
 
1431
        $course = $this->getDataGenerator()->create_course();
 
1432
        // Get course from DB for comparison.
 
1433
        $course = $DB->get_record('course', array('id' => $course->id));
 
1434
 
 
1435
        // Capture the event.
 
1436
        $events = $sink->get_events();
 
1437
        $sink->close();
 
1438
 
 
1439
        // Validate the event.
 
1440
        $event = $events[0];
 
1441
        $this->assertInstanceOf('\core\event\course_created', $event);
 
1442
        $this->assertEquals('course', $event->objecttable);
 
1443
        $this->assertEquals($course->id, $event->objectid);
 
1444
        $this->assertEquals(context_course::instance($course->id), $event->get_context());
 
1445
        $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
 
1446
        $this->assertEquals('course_created', $event->get_legacy_eventname());
 
1447
        $this->assertEventLegacyData($course, $event);
 
1448
        $expectedlog = array(SITEID, 'course', 'new', 'view.php?id=' . $course->id, $course->fullname . ' (ID ' . $course->id . ')');
 
1449
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1450
    }
 
1451
 
 
1452
    /**
 
1453
     * Test that triggering a course_updated event works as expected.
 
1454
     */
 
1455
    public function test_course_updated_event() {
 
1456
        global $DB;
 
1457
 
 
1458
        $this->resetAfterTest();
 
1459
 
 
1460
        // Create a course.
 
1461
        $course = $this->getDataGenerator()->create_course();
 
1462
 
 
1463
        // Create a category we are going to move this course to.
 
1464
        $category = $this->getDataGenerator()->create_category();
 
1465
 
 
1466
        // Create a hidden category we are going to move this course to.
 
1467
        $categoryhidden = $this->getDataGenerator()->create_category(array('visible' => 0));
 
1468
 
 
1469
        // Update course and catch course_updated event.
 
1470
        $sink = $this->redirectEvents();
 
1471
        update_course($course);
 
1472
        $events = $sink->get_events();
 
1473
        $sink->close();
 
1474
 
 
1475
        // Get updated course information from the DB.
 
1476
        $updatedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
 
1477
        // Validate event.
 
1478
        $event = array_shift($events);
 
1479
        $this->assertInstanceOf('\core\event\course_updated', $event);
 
1480
        $this->assertEquals('course', $event->objecttable);
 
1481
        $this->assertEquals($updatedcourse->id, $event->objectid);
 
1482
        $this->assertEquals(context_course::instance($course->id), $event->get_context());
 
1483
        $this->assertEquals($updatedcourse, $event->get_record_snapshot('course', $event->objectid));
 
1484
        $this->assertEquals('course_updated', $event->get_legacy_eventname());
 
1485
        $this->assertEventLegacyData($updatedcourse, $event);
 
1486
        $expectedlog = array($updatedcourse->id, 'course', 'update', 'edit.php?id=' . $course->id, $course->id);
 
1487
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1488
 
 
1489
        // Move course and catch course_updated event.
 
1490
        $sink = $this->redirectEvents();
 
1491
        move_courses(array($course->id), $category->id);
 
1492
        $events = $sink->get_events();
 
1493
        $sink->close();
 
1494
 
 
1495
        // Return the moved course information from the DB.
 
1496
        $movedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
 
1497
        // Validate event.
 
1498
        $event = array_shift($events);
 
1499
        $this->assertInstanceOf('\core\event\course_updated', $event);
 
1500
        $this->assertEquals('course', $event->objecttable);
 
1501
        $this->assertEquals($movedcourse->id, $event->objectid);
 
1502
        $this->assertEquals(context_course::instance($course->id), $event->get_context());
 
1503
        $this->assertEquals($movedcourse, $event->get_record_snapshot('course', $movedcourse->id));
 
1504
        $this->assertEquals('course_updated', $event->get_legacy_eventname());
 
1505
        $this->assertEventLegacyData($movedcourse, $event);
 
1506
        $expectedlog = array($movedcourse->id, 'course', 'move', 'edit.php?id=' . $movedcourse->id, $movedcourse->id);
 
1507
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1508
 
 
1509
        // Move course to hidden category and catch course_updated event.
 
1510
        $sink = $this->redirectEvents();
 
1511
        move_courses(array($course->id), $categoryhidden->id);
 
1512
        $events = $sink->get_events();
 
1513
        $sink->close();
 
1514
 
 
1515
        // Return the moved course information from the DB.
 
1516
        $movedcoursehidden = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
 
1517
        // Validate event.
 
1518
        $event = array_shift($events);
 
1519
        $this->assertInstanceOf('\core\event\course_updated', $event);
 
1520
        $this->assertEquals('course', $event->objecttable);
 
1521
        $this->assertEquals($movedcoursehidden->id, $event->objectid);
 
1522
        $this->assertEquals(context_course::instance($course->id), $event->get_context());
 
1523
        $this->assertEquals($movedcoursehidden, $event->get_record_snapshot('course', $movedcoursehidden->id));
 
1524
        $this->assertEquals('course_updated', $event->get_legacy_eventname());
 
1525
        $this->assertEventLegacyData($movedcoursehidden, $event);
 
1526
        $expectedlog = array($movedcoursehidden->id, 'course', 'move', 'edit.php?id=' . $movedcoursehidden->id, $movedcoursehidden->id);
 
1527
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1528
    }
 
1529
 
 
1530
    /**
 
1531
     * Test that triggering a course_deleted event works as expected.
 
1532
     */
 
1533
    public function test_course_deleted_event() {
 
1534
        $this->resetAfterTest();
 
1535
 
 
1536
        // Create the course.
 
1537
        $course = $this->getDataGenerator()->create_course();
 
1538
 
 
1539
        // Save the course context before we delete the course.
 
1540
        $coursecontext = context_course::instance($course->id);
 
1541
 
 
1542
        // Catch the update event.
 
1543
        $sink = $this->redirectEvents();
 
1544
 
 
1545
        // Call delete_course() which will trigger the course_deleted event and the course_content_deleted
 
1546
        // event. This function prints out data to the screen, which we do not want during a PHPUnit test,
 
1547
        // so use ob_start and ob_end_clean to prevent this.
 
1548
        ob_start();
 
1549
        delete_course($course);
 
1550
        ob_end_clean();
 
1551
 
 
1552
        // Capture the event.
 
1553
        $events = $sink->get_events();
 
1554
        $sink->close();
 
1555
 
 
1556
        // Validate the event.
 
1557
        $event = $events[1];
 
1558
        $this->assertInstanceOf('\core\event\course_deleted', $event);
 
1559
        $this->assertEquals('course', $event->objecttable);
 
1560
        $this->assertEquals($course->id, $event->objectid);
 
1561
        $this->assertEquals($coursecontext->id, $event->contextid);
 
1562
        $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
 
1563
        $this->assertEquals('course_deleted', $event->get_legacy_eventname());
 
1564
        $eventdata = $event->get_data();
 
1565
        $this->assertSame($course->idnumber, $eventdata['other']['idnumber']);
 
1566
        $this->assertSame($course->fullname, $eventdata['other']['fullname']);
 
1567
        $this->assertSame($course->shortname, $eventdata['other']['shortname']);
 
1568
        // The legacy data also passed the context in the course object.
 
1569
        $course->context = $coursecontext;
 
1570
        $this->assertEventLegacyData($course, $event);
 
1571
        $expectedlog = array(SITEID, 'course', 'delete', 'view.php?id=' . $course->id, $course->fullname . '(ID ' . $course->id . ')');
 
1572
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1573
    }
 
1574
 
 
1575
    /**
 
1576
     * Test that triggering a course_content_deleted event works as expected.
 
1577
     */
 
1578
    public function test_course_content_deleted_event() {
 
1579
        global $DB;
 
1580
 
 
1581
        $this->resetAfterTest();
 
1582
 
 
1583
        // Create the course.
 
1584
        $course = $this->getDataGenerator()->create_course();
 
1585
 
 
1586
        // Get the course from the DB. The data generator adds some extra properties, such as
 
1587
        // numsections, to the course object which will fail the assertions later on.
 
1588
        $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
 
1589
 
 
1590
        // Save the course context before we delete the course.
 
1591
        $coursecontext = context_course::instance($course->id);
 
1592
 
 
1593
        // Catch the update event.
 
1594
        $sink = $this->redirectEvents();
 
1595
 
 
1596
        remove_course_contents($course->id, false);
 
1597
 
 
1598
        // Capture the event.
 
1599
        $events = $sink->get_events();
 
1600
        $sink->close();
 
1601
 
 
1602
        // Validate the event.
 
1603
        $event = $events[0];
 
1604
        $this->assertInstanceOf('\core\event\course_content_deleted', $event);
 
1605
        $this->assertEquals('course', $event->objecttable);
 
1606
        $this->assertEquals($course->id, $event->objectid);
 
1607
        $this->assertEquals($coursecontext->id, $event->contextid);
 
1608
        $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
 
1609
        $this->assertEquals('course_content_removed', $event->get_legacy_eventname());
 
1610
        // The legacy data also passed the context and options in the course object.
 
1611
        $course->context = $coursecontext;
 
1612
        $course->options = array();
 
1613
        $this->assertEventLegacyData($course, $event);
 
1614
    }
 
1615
 
 
1616
    /**
 
1617
     * Test that triggering a course_category_deleted event works as expected.
 
1618
     */
 
1619
    public function test_course_category_deleted_event() {
 
1620
        $this->resetAfterTest();
 
1621
 
 
1622
        // Create a category.
 
1623
        $category = $this->getDataGenerator()->create_category();
 
1624
 
 
1625
        // Save the context before it is deleted.
 
1626
        $categorycontext = context_coursecat::instance($category->id);
 
1627
 
 
1628
        // Catch the update event.
 
1629
        $sink = $this->redirectEvents();
 
1630
 
 
1631
        // Delete the category.
 
1632
        $category->delete_full();
 
1633
 
 
1634
        // Capture the event.
 
1635
        $events = $sink->get_events();
 
1636
        $sink->close();
 
1637
 
 
1638
        // Validate the event.
 
1639
        $event = $events[0];
 
1640
        $this->assertInstanceOf('\core\event\course_category_deleted', $event);
 
1641
        $this->assertEquals('course_categories', $event->objecttable);
 
1642
        $this->assertEquals($category->id, $event->objectid);
 
1643
        $this->assertEquals($categorycontext->id, $event->contextid);
 
1644
        $this->assertEquals('course_category_deleted', $event->get_legacy_eventname());
 
1645
        $this->assertEventLegacyData($category, $event);
 
1646
        $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category->name . '(ID ' . $category->id . ')');
 
1647
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1648
 
 
1649
        // Create two categories.
 
1650
        $category = $this->getDataGenerator()->create_category();
 
1651
        $category2 = $this->getDataGenerator()->create_category();
 
1652
 
 
1653
        // Save the context before it is moved and then deleted.
 
1654
        $category2context = context_coursecat::instance($category2->id);
 
1655
 
 
1656
        // Catch the update event.
 
1657
        $sink = $this->redirectEvents();
 
1658
 
 
1659
        // Move the category.
 
1660
        $category2->delete_move($category->id);
 
1661
 
 
1662
        // Capture the event.
 
1663
        $events = $sink->get_events();
 
1664
        $sink->close();
 
1665
 
 
1666
        // Validate the event.
 
1667
        $event = $events[0];
 
1668
        $this->assertInstanceOf('\core\event\course_category_deleted', $event);
 
1669
        $this->assertEquals('course_categories', $event->objecttable);
 
1670
        $this->assertEquals($category2->id, $event->objectid);
 
1671
        $this->assertEquals($category2context->id, $event->contextid);
 
1672
        $this->assertEquals('course_category_deleted', $event->get_legacy_eventname());
 
1673
        $this->assertEventLegacyData($category2, $event);
 
1674
        $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category2->name . '(ID ' . $category2->id . ')');
 
1675
        $this->assertEventLegacyLogData($expectedlog, $event);
 
1676
    }
 
1677
 
 
1678
    /**
 
1679
     * Test that triggering a course_restored event works as expected.
 
1680
     */
 
1681
    public function test_course_restored_event() {
 
1682
        global $CFG;
 
1683
 
 
1684
        // Get the necessary files to perform backup and restore.
 
1685
        require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
 
1686
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
 
1687
 
 
1688
        $this->resetAfterTest();
 
1689
 
 
1690
        // Set to admin user.
 
1691
        $this->setAdminUser();
 
1692
 
 
1693
        // The user id is going to be 2 since we are the admin user.
 
1694
        $userid = 2;
 
1695
 
 
1696
        // Create a course.
 
1697
        $course = $this->getDataGenerator()->create_course();
 
1698
 
 
1699
        // Create backup file and save it to the backup location.
 
1700
        $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
 
1701
            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid);
 
1702
        $bc->execute_plan();
 
1703
        $results = $bc->get_results();
 
1704
        $file = $results['backup_destination'];
 
1705
        $fp = get_file_packer();
 
1706
        $filepath = $CFG->dataroot . '/temp/backup/test-restore-course-event';
 
1707
        $file->extract_to_pathname($fp, $filepath);
 
1708
        $bc->destroy();
 
1709
        unset($bc);
 
1710
 
 
1711
        // Now we want to catch the restore course event.
 
1712
        $sink = $this->redirectEvents();
 
1713
 
 
1714
        // Now restore the course to trigger the event.
 
1715
        $rc = new restore_controller('test-restore-course-event', $course->id, backup::INTERACTIVE_NO,
 
1716
            backup::MODE_GENERAL, $userid, backup::TARGET_NEW_COURSE);
 
1717
        $rc->execute_precheck();
 
1718
        $rc->execute_plan();
 
1719
 
 
1720
        // Capture the event.
 
1721
        $events = $sink->get_events();
 
1722
        $sink->close();
 
1723
 
 
1724
        // Validate the event.
 
1725
        $event = $events[0];
 
1726
        $this->assertInstanceOf('\core\event\course_restored', $event);
 
1727
        $this->assertEquals('course', $event->objecttable);
 
1728
        $this->assertEquals($rc->get_courseid(), $event->objectid);
 
1729
        $this->assertEquals(context_course::instance($rc->get_courseid())->id, $event->contextid);
 
1730
        $this->assertEquals('course_restored', $event->get_legacy_eventname());
 
1731
        $legacydata = (object) array(
 
1732
            'courseid' => $rc->get_courseid(),
 
1733
            'userid' => $rc->get_userid(),
 
1734
            'type' => $rc->get_type(),
 
1735
            'target' => $rc->get_target(),
 
1736
            'mode' => $rc->get_mode(),
 
1737
            'operation' => $rc->get_operation(),
 
1738
            'samesite' => $rc->is_samesite()
 
1739
        );
 
1740
        $this->assertEventLegacyData($legacydata, $event);
 
1741
 
 
1742
        // Destroy the resource controller since we are done using it.
 
1743
        $rc->destroy();
 
1744
        unset($rc);
 
1745
 
 
1746
        // Clear the time limit, otherwise PHPUnit complains.
 
1747
        set_time_limit(0);
 
1748
    }
 
1749
 
 
1750
    /**
 
1751
     * Test that triggering a course_section_updated event works as expected.
 
1752
     */
 
1753
    public function test_course_section_updated_event() {
 
1754
        global $DB;
 
1755
 
 
1756
        $this->resetAfterTest();
 
1757
 
 
1758
        // Create the course with sections.
 
1759
        $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true));
 
1760
        $sections = $DB->get_records('course_sections', array('course' => $course->id));
 
1761
 
 
1762
        $coursecontext = context_course::instance($course->id);
 
1763
 
 
1764
        $section = array_pop($sections);
 
1765
        $section->name = 'Test section';
 
1766
        $section->summary = 'Test section summary';
 
1767
        $DB->update_record('course_sections', $section);
 
1768
 
 
1769
        // Trigger an event for course section update.
 
1770
        $event = \core\event\course_section_updated::create(
 
1771
                array(
 
1772
                    'objectid' => $section->id,
 
1773
                    'courseid' => $course->id,
 
1774
                    'context' => context_course::instance($course->id)
 
1775
                )
 
1776
            );
 
1777
        $event->add_record_snapshot('course_sections', $section);
 
1778
        // Trigger and catch event.
 
1779
        $sink = $this->redirectEvents();
 
1780
        $event->trigger();
 
1781
        $events = $sink->get_events();
 
1782
        $sink->close();
 
1783
 
 
1784
        // Validate the event.
 
1785
        $event = $events[0];
 
1786
        $this->assertInstanceOf('\core\event\course_section_updated', $event);
 
1787
        $this->assertEquals('course_sections', $event->objecttable);
 
1788
        $this->assertEquals($section->id, $event->objectid);
 
1789
        $this->assertEquals($course->id, $event->courseid);
 
1790
        $this->assertEquals($coursecontext->id, $event->contextid);
 
1791
        $expecteddesc = 'Course ' . $event->courseid . ' section ' . $event->other['sectionnum'] . ' updated by user ' . $event->userid;
 
1792
        $this->assertEquals($expecteddesc, $event->get_description());
 
1793
        $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid));
 
1794
        $id = $section->id;
 
1795
        $sectionnum = $section->section;
 
1796
        $expectedlegacydata = array($course->id, "course", "editsection", 'editsection.php?id=' . $id, $sectionnum);
 
1797
        $this->assertEventLegacyLogData($expectedlegacydata, $event);
 
1798
    }
 
1799
 
 
1800
    public function test_course_integrity_check() {
 
1801
        global $DB;
 
1802
 
 
1803
        $this->resetAfterTest(true);
 
1804
        $course = $this->getDataGenerator()->create_course(array('numsections' => 1),
 
1805
           array('createsections'=>true));
 
1806
 
 
1807
        $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
 
1808
                array('section' => 0));
 
1809
        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
 
1810
                array('section' => 0));
 
1811
        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id),
 
1812
                array('section' => 0));
 
1813
        $correctseq = join(',', array($forum->cmid, $page->cmid, $quiz->cmid));
 
1814
 
 
1815
        $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
 
1816
        $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
 
1817
        $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
 
1818
        $this->assertEquals($correctseq, $section0->sequence);
 
1819
        $this->assertEmpty($section1->sequence);
 
1820
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1821
        $this->assertEquals($section0->id, $cms[$page->cmid]->section);
 
1822
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1823
        $this->assertEmpty(course_integrity_check($course->id));
 
1824
 
 
1825
        // Now let's make manual change in DB and let course_integrity_check() fix it:
 
1826
 
 
1827
        // 1. Module appears twice in one section.
 
1828
        $DB->update_record('course_sections', array('id' => $section0->id, 'sequence' => $section0->sequence. ','. $page->cmid));
 
1829
        $this->assertEquals(
 
1830
                array('Failed integrity check for course ['. $course->id.
 
1831
                ']. Sequence for course section ['. $section0->id. '] is "'.
 
1832
                $section0->sequence. ','. $page->cmid. '", must be "'.
 
1833
                $section0->sequence. '"'),
 
1834
                course_integrity_check($course->id));
 
1835
        $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
 
1836
        $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
 
1837
        $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
 
1838
        $this->assertEquals($correctseq, $section0->sequence);
 
1839
        $this->assertEmpty($section1->sequence);
 
1840
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1841
        $this->assertEquals($section0->id, $cms[$page->cmid]->section);
 
1842
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1843
 
 
1844
        // 2. Module appears in two sections (last section wins).
 
1845
        $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''. $page->cmid));
 
1846
        // First message about double mentioning in sequence, second message about wrong section field for $page.
 
1847
        $this->assertEquals(array(
 
1848
            'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
 
1849
            '] must be removed from sequence of section ['. $section0->id.
 
1850
            '] because it is also present in sequence of section ['. $section1->id. ']',
 
1851
            'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
 
1852
            '] points to section ['. $section0->id. '] instead of ['. $section1->id. ']'),
 
1853
                course_integrity_check($course->id));
 
1854
        $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
 
1855
        $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
 
1856
        $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
 
1857
        $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
 
1858
        $this->assertEquals(''. $page->cmid, $section1->sequence);
 
1859
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1860
        $this->assertEquals($section1->id, $cms[$page->cmid]->section);
 
1861
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1862
 
 
1863
        // 3. Module id is not present in course_section.sequence (integrity check with $fullcheck = false).
 
1864
        $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''));
 
1865
        $this->assertEmpty(course_integrity_check($course->id)); // Not an error!
 
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($forum->cmid. ','. $quiz->cmid, $section0->sequence);
 
1870
        $this->assertEmpty($section1->sequence);
 
1871
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1872
        $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed.
 
1873
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1874
 
 
1875
        // 4. Module id is not present in course_section.sequence (integrity check with $fullcheck = true).
 
1876
        $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['.
 
1877
                $page->cmid. '] is missing from sequence of section ['. $section1->id. ']'),
 
1878
                course_integrity_check($course->id, null, null, true)); // Error!
 
1879
        $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
 
1880
        $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
 
1881
        $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
 
1882
        $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
 
1883
        $this->assertEquals(''. $page->cmid, $section1->sequence);  // Yay, module added to section.
 
1884
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1885
        $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed.
 
1886
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1887
 
 
1888
        // 5. Module id is not present in course_section.sequence and it's section is invalid (integrity check with $fullcheck = true).
 
1889
        $DB->update_record('course_modules', array('id' => $page->cmid, 'section' => 8765));
 
1890
        $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''));
 
1891
        $this->assertEquals(array(
 
1892
            'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
 
1893
            '] is missing from sequence of section ['. $section0->id. ']',
 
1894
            'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
 
1895
            '] points to section [8765] instead of ['. $section0->id. ']'),
 
1896
                course_integrity_check($course->id, null, null, true));
 
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. ','. $page->cmid, $section0->sequence); // Module added to section.
 
1901
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1902
        $this->assertEquals($section0->id, $cms[$page->cmid]->section); // Section changed to section0.
 
1903
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1904
 
 
1905
        // 6. Module is deleted from course_modules but not deleted in sequence (integrity check with $fullcheck = true).
 
1906
        $DB->delete_records('course_modules', array('id' => $page->cmid));
 
1907
        $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['.
 
1908
                $page->cmid. '] does not exist but is present in the sequence of section ['. $section0->id. ']'),
 
1909
                course_integrity_check($course->id, null, null, true));
 
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->assertEmpty($section1->sequence);
 
1915
        $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
 
1916
        $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
 
1917
        $this->assertEquals(2, count($cms));
 
1918
    }
 
1919
 
 
1920
    /**
 
1921
     * Tests for event related to course module creation.
 
1922
     */
 
1923
    public function test_course_module_created_event() {
 
1924
        global $USER, $DB;
 
1925
        $this->resetAfterTest();
 
1926
 
 
1927
        // Create an assign module.
 
1928
        $sink = $this->redirectEvents();
 
1929
        $modinfo = $this->create_specific_module_test('assign');
 
1930
        $events = $sink->get_events();
 
1931
        $event = array_pop($events);
 
1932
        $sink->close();
 
1933
 
 
1934
        $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
 
1935
        $mod = $DB->get_record('assign', array('id' => $modinfo->instance), '*', MUST_EXIST);
 
1936
 
 
1937
        // Validate event data.
 
1938
        $this->assertInstanceOf('\core\event\course_module_created', $event);
 
1939
        $this->assertEquals($cm->id, $event->objectid);
 
1940
        $this->assertEquals($USER->id, $event->userid);
 
1941
        $this->assertEquals('course_modules', $event->objecttable);
 
1942
        $url = new moodle_url('/mod/assign/view.php', array('id' => $cm->id));
 
1943
        $this->assertEquals($url, $event->get_url());
 
1944
 
 
1945
        // Test legacy data.
 
1946
        $this->assertSame('mod_created', $event->get_legacy_eventname());
 
1947
        $eventdata = new stdClass();
 
1948
        $eventdata->modulename = 'assign';
 
1949
        $eventdata->name       = $mod->name;
 
1950
        $eventdata->cmid       = $cm->id;
 
1951
        $eventdata->courseid   = $cm->course;
 
1952
        $eventdata->userid     = $USER->id;
 
1953
        $this->assertEventLegacyData($eventdata, $event);
 
1954
 
 
1955
        $arr = array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance");
 
1956
        $this->assertEventLegacyLogData($arr, $event);
 
1957
 
 
1958
    }
 
1959
 
 
1960
    /**
 
1961
     * Tests for event validations related to course module creation.
 
1962
     */
 
1963
    public function test_course_module_created_event_exceptions() {
 
1964
 
 
1965
        $this->resetAfterTest();
 
1966
 
 
1967
        // Generate data.
 
1968
        $modinfo = $this->create_specific_module_test('assign');
 
1969
        $context = context_module::instance($modinfo->coursemodule);
 
1970
 
 
1971
        // Test not setting instanceid.
 
1972
        try {
 
1973
            $event = \core\event\course_module_created::create(array(
 
1974
                'courseid' => $modinfo->course,
 
1975
                'context'  => $context,
 
1976
                'objectid' => $modinfo->coursemodule,
 
1977
                'other'    => array(
 
1978
                    'modulename' => 'assign',
 
1979
                    'name'       => 'My assignment',
 
1980
                )
 
1981
            ));
 
1982
            $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
 
1983
                    other['instanceid']");
 
1984
        } catch (coding_exception $e) {
 
1985
            $this->assertContains("Field other['instanceid'] cannot be empty", $e->getMessage());
 
1986
        }
 
1987
 
 
1988
        // Test not setting modulename.
 
1989
        try {
 
1990
            $event = \core\event\course_module_created::create(array(
 
1991
                'courseid' => $modinfo->course,
 
1992
                'context'  => $context,
 
1993
                'objectid' => $modinfo->coursemodule,
 
1994
                'other'    => array(
 
1995
                    'instanceid' => $modinfo->instance,
 
1996
                    'name'       => 'My assignment',
 
1997
                )
 
1998
            ));
 
1999
            $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
 
2000
                    other['modulename']");
 
2001
        } catch (coding_exception $e) {
 
2002
            $this->assertContains("Field other['modulename'] cannot be empty", $e->getMessage());
 
2003
        }
 
2004
 
 
2005
        // Test not setting name.
 
2006
 
 
2007
        try {
 
2008
            $event = \core\event\course_module_created::create(array(
 
2009
                'courseid' => $modinfo->course,
 
2010
                'context'  => $context,
 
2011
                'objectid' => $modinfo->coursemodule,
 
2012
                'other'    => array(
 
2013
                    'modulename' => 'assign',
 
2014
                    'instanceid' => $modinfo->instance,
 
2015
                )
 
2016
            ));
 
2017
            $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
 
2018
                    other['name']");
 
2019
        } catch (coding_exception $e) {
 
2020
            $this->assertContains("Field other['name'] cannot be empty", $e->getMessage());
 
2021
        }
 
2022
 
 
2023
    }
 
2024
 
 
2025
    /**
 
2026
     * Tests for event related to course module updates.
 
2027
     */
 
2028
    public function test_course_module_updated_event() {
 
2029
        global $USER, $DB;
 
2030
        $this->resetAfterTest();
 
2031
 
 
2032
        // Update a forum module.
 
2033
        $sink = $this->redirectEvents();
 
2034
        $modinfo = $this->update_specific_module_test('forum');
 
2035
        $events = $sink->get_events();
 
2036
        $event = array_pop($events);
 
2037
        $sink->close();
 
2038
 
 
2039
        $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
 
2040
        $mod = $DB->get_record('forum', array('id' => $cm->instance), '*', MUST_EXIST);
 
2041
 
 
2042
        // Validate event data.
 
2043
        $this->assertInstanceOf('\core\event\course_module_updated', $event);
 
2044
        $this->assertEquals($cm->id, $event->objectid);
 
2045
        $this->assertEquals($USER->id, $event->userid);
 
2046
        $this->assertEquals('course_modules', $event->objecttable);
 
2047
        $url = new moodle_url('/mod/forum/view.php', array('id' => $cm->id));
 
2048
        $this->assertEquals($url, $event->get_url());
 
2049
 
 
2050
        // Test legacy data.
 
2051
        $this->assertSame('mod_updated', $event->get_legacy_eventname());
 
2052
        $eventdata = new stdClass();
 
2053
        $eventdata->modulename = 'forum';
 
2054
        $eventdata->name       = $mod->name;
 
2055
        $eventdata->cmid       = $cm->id;
 
2056
        $eventdata->courseid   = $cm->course;
 
2057
        $eventdata->userid     = $USER->id;
 
2058
        $this->assertEventLegacyData($eventdata, $event);
 
2059
 
 
2060
        $arr = array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$cm->id", "forum $cm->instance");
 
2061
        $this->assertEventLegacyLogData($arr, $event);
 
2062
 
 
2063
    }
 
2064
 
 
2065
    /**
 
2066
     * Tests for create_from_cm method.
 
2067
     */
 
2068
    public function test_course_module_create_from_cm() {
 
2069
        $this->resetAfterTest();
 
2070
        $this->setAdminUser();
 
2071
 
 
2072
        // Create course and modules.
 
2073
        $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
 
2074
 
 
2075
        // Generate an assignment.
 
2076
        $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
 
2077
 
 
2078
        // Get the module context.
 
2079
        $modcontext = context_module::instance($assign->cmid);
 
2080
 
 
2081
        // Get course module.
 
2082
        $cm = get_coursemodule_from_id(null, $assign->cmid, $course->id, false, MUST_EXIST);
 
2083
 
 
2084
        // Create an event from course module.
 
2085
        $event = \core\event\course_module_updated::create_from_cm($cm, $modcontext);
 
2086
 
 
2087
        // Trigger the events.
 
2088
        $sink = $this->redirectEvents();
 
2089
        $event->trigger();
 
2090
        $events = $sink->get_events();
 
2091
        $event2 = array_pop($events);
 
2092
 
 
2093
        // Test event data.
 
2094
        $this->assertInstanceOf('\core\event\course_module_updated', $event);
 
2095
        $this->assertEquals($cm->id, $event2->objectid);
 
2096
        $this->assertEquals($modcontext, $event2->get_context());
 
2097
        $this->assertEquals($cm->modname, $event2->other['modulename']);
 
2098
        $this->assertEquals($cm->instance, $event2->other['instanceid']);
 
2099
        $this->assertEquals($cm->name, $event2->other['name']);
 
2100
        $this->assertSame('mod_updated', $event2->get_legacy_eventname());
 
2101
        $arr = array($cm->course, "course", "update mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance");
 
2102
        $this->assertEventLegacyLogData($arr, $event);
 
2103
    }
 
2104
 
 
2105
    /**
 
2106
     * Tests for event validations related to course module update.
 
2107
     */
 
2108
    public function test_course_module_updated_event_exceptions() {
 
2109
 
 
2110
        $this->resetAfterTest();
 
2111
 
 
2112
        // Generate data.
 
2113
        $modinfo = $this->create_specific_module_test('assign');
 
2114
        $context = context_module::instance($modinfo->coursemodule);
 
2115
 
 
2116
        // Test not setting instanceid.
 
2117
        try {
 
2118
            $event = \core\event\course_module_updated::create(array(
 
2119
                'courseid' => $modinfo->course,
 
2120
                'context'  => $context,
 
2121
                'objectid' => $modinfo->coursemodule,
 
2122
                'other'    => array(
 
2123
                    'modulename' => 'assign',
 
2124
                    'name'       => 'My assignment',
 
2125
                )
 
2126
            ));
 
2127
            $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
 
2128
                    other['instanceid']");
 
2129
        } catch (coding_exception $e) {
 
2130
            $this->assertContains("Field other['instanceid'] cannot be empty", $e->getMessage());
 
2131
        }
 
2132
 
 
2133
        // Test not setting modulename.
 
2134
        try {
 
2135
            $event = \core\event\course_module_updated::create(array(
 
2136
                'courseid' => $modinfo->course,
 
2137
                'context'  => $context,
 
2138
                'objectid' => $modinfo->coursemodule,
 
2139
                'other'    => array(
 
2140
                    'instanceid' => $modinfo->instance,
 
2141
                    'name'       => 'My assignment',
 
2142
                )
 
2143
            ));
 
2144
            $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
 
2145
                    other['modulename']");
 
2146
        } catch (coding_exception $e) {
 
2147
            $this->assertContains("Field other['modulename'] cannot be empty", $e->getMessage());
 
2148
        }
 
2149
 
 
2150
        // Test not setting name.
 
2151
 
 
2152
        try {
 
2153
            $event = \core\event\course_module_updated::create(array(
 
2154
                'courseid' => $modinfo->course,
 
2155
                'context'  => $context,
 
2156
                'objectid' => $modinfo->coursemodule,
 
2157
                'other'    => array(
 
2158
                    'modulename' => 'assign',
 
2159
                    'instanceid' => $modinfo->instance,
 
2160
                )
 
2161
            ));
 
2162
            $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
 
2163
                    other['name']");
 
2164
        } catch (coding_exception $e) {
 
2165
            $this->assertContains("Field other['name'] cannot be empty", $e->getMessage());
 
2166
        }
 
2167
 
 
2168
    }
 
2169
 
 
2170
    /**
 
2171
     * Tests for event related to course module delete.
 
2172
     */
 
2173
    public function test_course_module_deleted_event() {
 
2174
        global $USER, $DB;
 
2175
        $this->resetAfterTest();
 
2176
 
 
2177
        // Create and delete a module.
 
2178
        $sink = $this->redirectEvents();
 
2179
        $modinfo = $this->create_specific_module_test('forum');
 
2180
        $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
 
2181
        course_delete_module($modinfo->coursemodule);
 
2182
        $events = $sink->get_events();
 
2183
        $event = array_pop($events); // delete module event.;
 
2184
        $sink->close();
 
2185
 
 
2186
        // Validate event data.
 
2187
        $this->assertInstanceOf('\core\event\course_module_deleted', $event);
 
2188
        $this->assertEquals($cm->id, $event->objectid);
 
2189
        $this->assertEquals($USER->id, $event->userid);
 
2190
        $this->assertEquals('course_modules', $event->objecttable);
 
2191
        $this->assertEquals(null, $event->get_url());
 
2192
        $this->assertEquals($cm, $event->get_record_snapshot('course_modules', $cm->id));
 
2193
 
 
2194
        // Test legacy data.
 
2195
        $this->assertSame('mod_deleted', $event->get_legacy_eventname());
 
2196
        $eventdata = new stdClass();
 
2197
        $eventdata->modulename = 'forum';
 
2198
        $eventdata->cmid       = $cm->id;
 
2199
        $eventdata->courseid   = $cm->course;
 
2200
        $eventdata->userid     = $USER->id;
 
2201
        $this->assertEventLegacyData($eventdata, $event);
 
2202
 
 
2203
        $arr = array($cm->course, 'course', "delete mod", "view.php?id=$cm->course", "forum $cm->instance", $cm->id);
 
2204
        $this->assertEventLegacyLogData($arr, $event);
 
2205
 
 
2206
    }
 
2207
 
 
2208
    /**
 
2209
     * Tests for event validations related to course module deletion.
 
2210
     */
 
2211
    public function test_course_module_deleted_event_exceptions() {
 
2212
 
 
2213
        $this->resetAfterTest();
 
2214
 
 
2215
        // Generate data.
 
2216
        $modinfo = $this->create_specific_module_test('assign');
 
2217
        $context = context_module::instance($modinfo->coursemodule);
 
2218
 
 
2219
        // Test not setting instanceid.
 
2220
        try {
 
2221
            $event = \core\event\course_module_deleted::create(array(
 
2222
                'courseid' => $modinfo->course,
 
2223
                'context'  => $context,
 
2224
                'objectid' => $modinfo->coursemodule,
 
2225
                'other'    => array(
 
2226
                    'modulename' => 'assign',
 
2227
                    'name'       => 'My assignment',
 
2228
                )
 
2229
            ));
 
2230
            $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without
 
2231
                    other['instanceid']");
 
2232
        } catch (coding_exception $e) {
 
2233
            $this->assertContains("Field other['instanceid'] cannot be empty", $e->getMessage());
 
2234
        }
 
2235
 
 
2236
        // Test not setting modulename.
 
2237
        try {
 
2238
            $event = \core\event\course_module_deleted::create(array(
 
2239
                'courseid' => $modinfo->course,
 
2240
                'context'  => $context,
 
2241
                'objectid' => $modinfo->coursemodule,
 
2242
                'other'    => array(
 
2243
                    'instanceid' => $modinfo->instance,
 
2244
                    'name'       => 'My assignment',
 
2245
                )
 
2246
            ));
 
2247
            $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without
 
2248
                    other['modulename']");
 
2249
        } catch (coding_exception $e) {
 
2250
            $this->assertContains("Field other['modulename'] cannot be empty", $e->getMessage());
 
2251
        }
 
2252
    }
 
2253
 
 
2254
    /**
 
2255
     * Returns a user object and its assigned new role.
 
2256
     *
 
2257
     * @param testing_data_generator $generator
 
2258
     * @param $contextid
 
2259
     * @return array The user object and the role ID
 
2260
     */
 
2261
    protected function get_user_objects(testing_data_generator $generator, $contextid) {
 
2262
        global $USER;
 
2263
 
 
2264
        if (empty($USER->id)) {
 
2265
            $user  = $generator->create_user();
 
2266
            $this->setUser($user);
 
2267
        }
 
2268
        $roleid = create_role('Test role', 'testrole', 'Test role description');
 
2269
        if (!is_array($contextid)) {
 
2270
            $contextid = array($contextid);
 
2271
        }
 
2272
        foreach ($contextid as $cid) {
 
2273
            $assignid = role_assign($roleid, $user->id, $cid);
 
2274
        }
 
2275
        return array($user, $roleid);
 
2276
    }
 
2277
 
 
2278
    /**
 
2279
     * Test course move after course.
 
2280
     */
 
2281
    public function test_course_change_sortorder_after_course() {
 
2282
        global $DB;
 
2283
 
 
2284
        $this->resetAfterTest(true);
 
2285
 
 
2286
        $generator = $this->getDataGenerator();
 
2287
        $category = $generator->create_category();
 
2288
        $course3 = $generator->create_course(array('category' => $category->id));
 
2289
        $course2 = $generator->create_course(array('category' => $category->id));
 
2290
        $course1 = $generator->create_course(array('category' => $category->id));
 
2291
        $context = $category->get_context();
 
2292
 
 
2293
        list($user, $roleid) = $this->get_user_objects($generator, $context->id);
 
2294
        $caps = course_capability_assignment::allow('moodle/category:manage', $roleid, $context->id);
 
2295
 
 
2296
        $courses = $category->get_courses();
 
2297
        $this->assertInternalType('array', $courses);
 
2298
        $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 
2299
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2300
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2301
 
 
2302
        // Test moving down.
 
2303
        $this->assertTrue(course_change_sortorder_after_course($course1->id, $course3->id));
 
2304
        $courses = $category->get_courses();
 
2305
        $this->assertInternalType('array', $courses);
 
2306
        $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses));
 
2307
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2308
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2309
 
 
2310
        // Test moving up.
 
2311
        $this->assertTrue(course_change_sortorder_after_course($course1->id, $course2->id));
 
2312
        $courses = $category->get_courses();
 
2313
        $this->assertInternalType('array', $courses);
 
2314
        $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses));
 
2315
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2316
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2317
 
 
2318
        // Test moving to the top.
 
2319
        $this->assertTrue(course_change_sortorder_after_course($course1->id, 0));
 
2320
        $courses = $category->get_courses();
 
2321
        $this->assertInternalType('array', $courses);
 
2322
        $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 
2323
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2324
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2325
    }
 
2326
 
 
2327
    /**
 
2328
     * Tests changing the visibility of a course.
 
2329
     */
 
2330
    public function test_course_change_visibility() {
 
2331
        global $DB;
 
2332
 
 
2333
        $this->resetAfterTest(true);
 
2334
 
 
2335
        $generator = $this->getDataGenerator();
 
2336
        $category = $generator->create_category();
 
2337
        $course = $generator->create_course(array('category' => $category->id));
 
2338
 
 
2339
        $this->assertEquals('1', $course->visible);
 
2340
        $this->assertEquals('1', $course->visibleold);
 
2341
 
 
2342
        $this->assertTrue(course_change_visibility($course->id, false));
 
2343
        $course = $DB->get_record('course', array('id' => $course->id));
 
2344
        $this->assertEquals('0', $course->visible);
 
2345
        $this->assertEquals('0', $course->visibleold);
 
2346
 
 
2347
        $this->assertTrue(course_change_visibility($course->id, true));
 
2348
        $course = $DB->get_record('course', array('id' => $course->id));
 
2349
        $this->assertEquals('1', $course->visible);
 
2350
        $this->assertEquals('1', $course->visibleold);
 
2351
    }
 
2352
 
 
2353
    /**
 
2354
     * Tests moving the course up and down by one.
 
2355
     */
 
2356
    public function test_course_change_sortorder_by_one() {
 
2357
        global $DB;
 
2358
 
 
2359
        $this->resetAfterTest(true);
 
2360
 
 
2361
        $generator = $this->getDataGenerator();
 
2362
        $category = $generator->create_category();
 
2363
        $course3 = $generator->create_course(array('category' => $category->id));
 
2364
        $course2 = $generator->create_course(array('category' => $category->id));
 
2365
        $course1 = $generator->create_course(array('category' => $category->id));
 
2366
 
 
2367
        $courses = $category->get_courses();
 
2368
        $this->assertInternalType('array', $courses);
 
2369
        $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 
2370
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2371
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2372
 
 
2373
        // Test moving down.
 
2374
        $course1 = get_course($course1->id);
 
2375
        $this->assertTrue(course_change_sortorder_by_one($course1, false));
 
2376
        $courses = $category->get_courses();
 
2377
        $this->assertInternalType('array', $courses);
 
2378
        $this->assertEquals(array($course2->id, $course1->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
        // Test moving up.
 
2383
        $course1 = get_course($course1->id);
 
2384
        $this->assertTrue(course_change_sortorder_by_one($course1, true));
 
2385
        $courses = $category->get_courses();
 
2386
        $this->assertInternalType('array', $courses);
 
2387
        $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 
2388
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2389
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2390
 
 
2391
        // Test moving the top course up one.
 
2392
        $course1 = get_course($course1->id);
 
2393
        $this->assertFalse(course_change_sortorder_by_one($course1, true));
 
2394
        // Check nothing changed.
 
2395
        $courses = $category->get_courses();
 
2396
        $this->assertInternalType('array', $courses);
 
2397
        $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 
2398
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2399
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2400
 
 
2401
        // Test moving the bottom course up down.
 
2402
        $course3 = get_course($course3->id);
 
2403
        $this->assertFalse(course_change_sortorder_by_one($course3, false));
 
2404
        // Check nothing changed.
 
2405
        $courses = $category->get_courses();
 
2406
        $this->assertInternalType('array', $courses);
 
2407
        $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses));
 
2408
        $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id');
 
2409
        $this->assertEquals(array_keys($dbcourses), array_keys($courses));
 
2410
    }
1344
2411
}