ca40f8643986007f1fef72afd9a2614027dc68ea
[moodle.git] / lib / tests / upgradelib_test.php
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/>.
17 /**
18  * Unit tests for the lib/upgradelib.php library.
19  *
20  * @package   core
21  * @category  phpunit
22  * @copyright 2013 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir.'/upgradelib.php');
32 /**
33  * Tests various classes and functions in upgradelib.php library.
34  */
35 class core_upgradelib_testcase extends advanced_testcase {
37     /**
38      * Test the {@link upgrade_stale_php_files_present() function
39      */
40     public function test_upgrade_stale_php_files_present() {
41         // Just call the function, must return bool false always
42         // if there aren't any old files in the codebase.
43         $this->assertFalse(upgrade_stale_php_files_present());
44     }
46     /**
47      * Test the {@link upgrade_grade_item_fix_sortorder() function with
48      * faked duplicate sortorder data.
49      */
50     public function test_upgrade_grade_item_fix_sortorder() {
51         global $DB;
53         $this->resetAfterTest(true);
55         // The purpose of this test is to make sure that after upgrade script
56         // there is no duplicates in the field grade_items.sortorder (for each course)
57         // and the result of query "SELECT id FROM grade_items WHERE courseid=? ORDER BY sortorder, id" does not change.
58         $sequencesql = 'SELECT id FROM {grade_items} WHERE courseid=? ORDER BY sortorder, id';
60         // Each set is used for filling the db with fake data and will be representing the result of query:
61         // "SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id".
62         $testsets = array(
63             // Items that need no action.
64             array(1,2,3),
65             array(5,6,7),
66             array(7,6,1,3,2,5),
67             // Items with sortorder duplicates
68             array(1,2,2,3,3,4,5),
69             // Only one sortorder duplicate.
70             array(1,1),
71             array(3,3),
72             // Non-sequential sortorders with one or multiple duplicates.
73             array(3,3,7,5,6,6,9,10,8,3),
74             array(7,7,3),
75             array(3,4,5,3,5,4,7,1)
76         );
77         $origsequences = array();
79         // Generate the data and remember the initial sequence or items.
80         foreach ($testsets as $testset) {
81             $course = $this->getDataGenerator()->create_course();
82             foreach ($testset as $sortorder) {
83                 $this->insert_fake_grade_item_sortorder($course->id, $sortorder);
84             }
85             $DB->get_records('grade_items');
86             $origsequences[$course->id] = $DB->get_fieldset_sql($sequencesql, array($course->id));
87         }
89         $duplicatedetectionsql = "SELECT courseid, sortorder
90                                     FROM {grade_items}
91                                 GROUP BY courseid, sortorder
92                                   HAVING COUNT(id) > 1";
94         // Verify there are duplicates before we start the fix.
95         $dupes = $DB->record_exists_sql($duplicatedetectionsql);
96         $this->assertTrue($dupes);
98         // Do the work.
99         upgrade_grade_item_fix_sortorder();
101         // Verify that no duplicates are left in the database.
102         $dupes = $DB->record_exists_sql($duplicatedetectionsql);
103         $this->assertFalse($dupes);
105         // Verify that sequences are exactly the same as they were before upgrade script.
106         $idx = 0;
107         foreach ($origsequences as $courseid => $origsequence) {
108             if (count(($testsets[$idx])) == count(array_unique($testsets[$idx]))) {
109                 // If there were no duplicates for this course verify that sortorders are not modified.
110                 $newsortorders = $DB->get_fieldset_sql("SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id", array($courseid));
111                 $this->assertEquals($testsets[$idx], $newsortorders);
112             }
113             $newsequence = $DB->get_fieldset_sql($sequencesql, array($courseid));
114             $this->assertEquals($origsequence, $newsequence,
115                     "Sequences do not match for test set $idx : ".join(',', $testsets[$idx]));
116             $idx++;
117         }
118     }
120     /**
121      * Populate some fake grade items into the database with specified
122      * sortorder and course id.
123      *
124      * NOTE: This function doesn't make much attempt to respect the
125      * gradebook internals, its simply used to fake some data for
126      * testing the upgradelib function. Please don't use it for other
127      * purposes.
128      *
129      * @param int $courseid id of course
130      * @param int $sortorder numeric sorting order of item
131      * @return stdClass grade item object from the database.
132      */
133     private function insert_fake_grade_item_sortorder($courseid, $sortorder) {
134         global $DB, $CFG;
135         require_once($CFG->libdir.'/gradelib.php');
137         $item = new stdClass();
138         $item->courseid = $courseid;
139         $item->sortorder = $sortorder;
140         $item->gradetype = GRADE_TYPE_VALUE;
141         $item->grademin = 30;
142         $item->grademax = 110;
143         $item->itemnumber = 1;
144         $item->iteminfo = '';
145         $item->timecreated = time();
146         $item->timemodified = time();
148         $item->id = $DB->insert_record('grade_items', $item);
150         return $DB->get_record('grade_items', array('id' => $item->id));
151     }
153     public function test_upgrade_fix_missing_root_folders() {
154         global $DB, $SITE;
156         $this->resetAfterTest(true);
158         // Setup some broken data...
159         // Create two resources (and associated file areas).
160         $this->setAdminUser();
161         $resource1 = $this->getDataGenerator()->get_plugin_generator('mod_resource')
162             ->create_instance(array('course' => $SITE->id));
163         $resource2 = $this->getDataGenerator()->get_plugin_generator('mod_resource')
164             ->create_instance(array('course' => $SITE->id));
166         // Delete the folder record of resource1 to simulate broken data.
167         $context = context_module::instance($resource1->cmid);
168         $selectargs = array('contextid' => $context->id,
169                             'component' => 'mod_resource',
170                             'filearea' => 'content',
171                             'itemid' => 0);
173         // Verify file records exist.
174         $areafilecount = $DB->count_records('files', $selectargs);
175         $this->assertNotEmpty($areafilecount);
177         // Delete the folder record.
178         $folderrecord = $selectargs;
179         $folderrecord['filepath'] = '/';
180         $folderrecord['filename'] = '.';
182         // Get previous folder record.
183         $oldrecord = $DB->get_record('files', $folderrecord);
184         $DB->delete_records('files', $folderrecord);
186         // Verify the folder record has been removed.
187         $newareafilecount = $DB->count_records('files', $selectargs);
188         $this->assertSame($newareafilecount, $areafilecount - 1);
190         $this->assertFalse($DB->record_exists('files', $folderrecord));
192         // Run the upgrade step!
193         upgrade_fix_missing_root_folders();
195         // Verify the folder record has been restored.
196         $newareafilecount = $DB->count_records('files', $selectargs);
197         $this->assertSame($newareafilecount, $areafilecount);
199         $newrecord = $DB->get_record('files', $folderrecord, '*', MUST_EXIST);
200         // Verify the hash is correctly created.
201         $this->assertSame($oldrecord->pathnamehash, $newrecord->pathnamehash);
202     }
204     public function test_upgrade_fix_missing_root_folders_draft() {
205         global $DB, $SITE;
207         $this->resetAfterTest(true);
209         $user = $this->getDataGenerator()->create_user();
210         $usercontext = context_user::instance($user->id);
211         $this->setUser($user);
212         $resource1 = $this->getDataGenerator()->get_plugin_generator('mod_resource')
213             ->create_instance(array('course' => $SITE->id));
214         $context = context_module::instance($resource1->cmid);
215         $draftitemid = 0;
216         file_prepare_draft_area($draftitemid, $context->id, 'mod_resource', 'content', 0);
218         $queryparams = array(
219             'component' => 'user',
220             'contextid' => $usercontext->id,
221             'filearea' => 'draft',
222             'itemid' => $draftitemid,
223         );
225         // Make sure there are two records in files for the draft file area and one of them has filename '.'.
226         $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
227         $this->assertEquals(2, count($records));
228         $this->assertTrue(in_array('.', $records));
229         $originalhash = $DB->get_field('files', 'pathnamehash', $queryparams + array('filename' => '.'));
231         // Delete record with filename '.' and make sure it does not exist any more.
232         $DB->delete_records('files', $queryparams + array('filename' => '.'));
234         $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
235         $this->assertEquals(1, count($records));
236         $this->assertFalse(in_array('.', $records));
238         // Run upgrade script and make sure the record is restored.
239         upgrade_fix_missing_root_folders_draft();
241         $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
242         $this->assertEquals(2, count($records));
243         $this->assertTrue(in_array('.', $records));
244         $newhash = $DB->get_field('files', 'pathnamehash', $queryparams + array('filename' => '.'));
245         $this->assertEquals($originalhash, $newhash);
246     }
248     /**
249      * Tests the upgrade of an individual course-module or section from the
250      * old to new availability system. (This test does not use the database
251      * so it can run any time.)
252      */
253     public function test_upgrade_availability_item() {
254         global $CFG;
255         $this->resetAfterTest();
257         // This function is in the other upgradelib.
258         require_once($CFG->libdir . '/db/upgradelib.php');
260         // Groupmembersonly (or nothing). Show option on but ignored.
261         // Note: This $CFG option doesn't exist any more but we are testing the
262         // upgrade function so it did exist then...
263         $CFG->enablegroupmembersonly = 0;
264         $this->assertNull(
265                 upgrade_availability_item(1, 0, 0, 0, 1, array(), array()));
266         $CFG->enablegroupmembersonly = 1;
267         $this->assertNull(
268                 upgrade_availability_item(0, 0, 0, 0, 1, array(), array()));
269         $this->assertEquals(
270                 '{"op":"&","showc":[false],"c":[{"type":"group"}]}',
271                 upgrade_availability_item(1, 0, 0, 0, 1, array(), array()));
272         $this->assertEquals(
273                 '{"op":"&","showc":[false],"c":[{"type":"grouping","id":4}]}',
274                 upgrade_availability_item(1, 4, 0, 0, 1, array(), array()));
276         // Dates (with show/hide options - until date always hides).
277         $this->assertEquals(
278                 '{"op":"&","showc":[true],"c":[{"type":"date","d":">=","t":996}]}',
279                 upgrade_availability_item(0, 0, 996, 0, 1, array(), array()));
280         $this->assertEquals(
281                 '{"op":"&","showc":[false],"c":[{"type":"date","d":">=","t":997}]}',
282                 upgrade_availability_item(0, 0, 997, 0, 0, array(), array()));
283         $this->assertEquals(
284                 '{"op":"&","showc":[false],"c":[{"type":"date","d":"<","t":998}]}',
285                 upgrade_availability_item(0, 0, 0, 998, 1, array(), array()));
286         $this->assertEquals(
287                 '{"op":"&","showc":[true,false],"c":[' .
288                 '{"type":"date","d":">=","t":995},{"type":"date","d":"<","t":999}]}',
289                 upgrade_availability_item(0, 0, 995, 999, 1, array(), array()));
291         // Grade (show option works as normal).
292         $availrec = (object)array(
293                 'sourcecmid' => null, 'requiredcompletion' => null,
294                 'gradeitemid' => 13, 'grademin' => null, 'grademax' => null);
295         $this->assertEquals(
296                 '{"op":"&","showc":[true],"c":[{"type":"grade","id":13}]}',
297                 upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
298         $availrec->grademin = 4.1;
299         $this->assertEquals(
300                 '{"op":"&","showc":[false],"c":[{"type":"grade","id":13,"min":4.10000}]}',
301                 upgrade_availability_item(0, 0, 0, 0, 0, array($availrec), array()));
302         $availrec->grademax = 9.9;
303         $this->assertEquals(
304                 '{"op":"&","showc":[true],"c":[{"type":"grade","id":13,"min":4.10000,"max":9.90000}]}',
305                 upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
306         $availrec->grademin = null;
307         $this->assertEquals(
308                 '{"op":"&","showc":[true],"c":[{"type":"grade","id":13,"max":9.90000}]}',
309                 upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
311         // Completion (show option normal).
312         $availrec->grademax = null;
313         $availrec->gradeitemid = null;
314         $availrec->sourcecmid = 666;
315         $availrec->requiredcompletion = 1;
316         $this->assertEquals(
317                 '{"op":"&","showc":[true],"c":[{"type":"completion","cm":666,"e":1}]}',
318                 upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
319         $this->assertEquals(
320                 '{"op":"&","showc":[false],"c":[{"type":"completion","cm":666,"e":1}]}',
321                 upgrade_availability_item(0, 0, 0, 0, 0, array($availrec), array()));
323         // Profile conditions (custom/standard field, values/not, show option normal).
324         $fieldrec = (object)array('userfield' => 'email', 'operator' => 'isempty',
325                 'value' => '', 'shortname' => null);
326         $this->assertEquals(
327                 '{"op":"&","showc":[true],"c":[{"type":"profile","op":"isempty","sf":"email"}]}',
328                 upgrade_availability_item(0, 0, 0, 0, 1, array(), array($fieldrec)));
329         $fieldrec->value = '@';
330         $fieldrec->operator = 'contains';
331         $this->assertEquals(
332                 '{"op":"&","showc":[true],"c":[{"type":"profile","op":"contains","sf":"email","v":"@"}]}',
333                 upgrade_availability_item(0, 0, 0, 0, 1, array(), array($fieldrec)));
334         $fieldrec->operator = 'isnotempty';
335         $fieldrec->userfield = null;
336         $fieldrec->shortname = 'frogtype';
337         $this->assertEquals(
338                 '{"op":"&","showc":[false],"c":[{"type":"profile","op":"isnotempty","cf":"frogtype"}]}',
339                 upgrade_availability_item(0, 0, 0, 0, 0, array(), array($fieldrec)));
341         // Everything at once.
342         $this->assertEquals('{"op":"&","showc":[false,true,false,true,true,true],' .
343                 '"c":[{"type":"grouping","id":13},' .
344                 '{"type":"date","d":">=","t":990},' .
345                 '{"type":"date","d":"<","t":991},' .
346                 '{"type":"grade","id":665,"min":70.00000},' .
347                 '{"type":"completion","cm":42,"e":2},' .
348                 '{"type":"profile","op":"isempty","sf":"email"}]}',
349                 upgrade_availability_item(1, 13, 990, 991, 1, array(
350                     (object)array('sourcecmid' => null, 'gradeitemid' => 665, 'grademin' => 70),
351                     (object)array('sourcecmid' => 42, 'gradeitemid' => null, 'requiredcompletion' => 2)
352                 ), array(
353                     (object)array('userfield' => 'email', 'shortname' => null, 'operator' => 'isempty'),
354                 )));
355     }
357     /**
358      * Test upgrade minmaxgrade step.
359      */
360     public function test_upgrade_minmaxgrade() {
361         global $CFG, $DB;
362         require_once($CFG->libdir . '/gradelib.php');
363         $initialminmax = $CFG->grade_minmaxtouse;
364         $this->resetAfterTest();
366         $c1 = $this->getDataGenerator()->create_course();
367         $c2 = $this->getDataGenerator()->create_course();
368         $c3 = $this->getDataGenerator()->create_course();
369         $u1 = $this->getDataGenerator()->create_user();
370         $a1 = $this->getDataGenerator()->create_module('assign', array('course' => $c1, 'grade' => 100));
371         $a2 = $this->getDataGenerator()->create_module('assign', array('course' => $c2, 'grade' => 100));
372         $a3 = $this->getDataGenerator()->create_module('assign', array('course' => $c3, 'grade' => 100));
374         $cm1 = get_coursemodule_from_instance('assign', $a1->id);
375         $ctx1 = context_module::instance($cm1->id);
376         $assign1 = new assign($ctx1, $cm1, $c1);
378         $cm2 = get_coursemodule_from_instance('assign', $a2->id);
379         $ctx2 = context_module::instance($cm2->id);
380         $assign2 = new assign($ctx2, $cm2, $c2);
382         $cm3 = get_coursemodule_from_instance('assign', $a3->id);
383         $ctx3 = context_module::instance($cm3->id);
384         $assign3 = new assign($ctx3, $cm3, $c3);
386         // Give a grade to the student.
387         $ug = $assign1->get_user_grade($u1->id, true);
388         $ug->grade = 10;
389         $assign1->update_grade($ug);
391         $ug = $assign2->get_user_grade($u1->id, true);
392         $ug->grade = 20;
393         $assign2->update_grade($ug);
395         $ug = $assign3->get_user_grade($u1->id, true);
396         $ug->grade = 30;
397         $assign3->update_grade($ug);
400         // Run the upgrade.
401         upgrade_minmaxgrade();
403         // Nothing has happened.
404         $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c1->id)));
405         $this->assertSame(false, grade_get_setting($c1->id, 'minmaxtouse', false, true));
406         $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c1->id)));
407         $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c2->id)));
408         $this->assertSame(false, grade_get_setting($c2->id, 'minmaxtouse', false, true));
409         $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c2->id)));
410         $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
411         $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
412         $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
414         // Create inconsistency in c1 and c2.
415         $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a1->id,
416                 'courseid' => $c1->id, 'itemnumber' => 0);
417         $gi = grade_item::fetch($giparams);
418         $gi->grademin = 5;
419         $gi->update();
421         $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a2->id,
422                 'courseid' => $c2->id, 'itemnumber' => 0);
423         $gi = grade_item::fetch($giparams);
424         $gi->grademax = 50;
425         $gi->update();
428         // C1 and C2 should be updated, but the course setting should not be set.
429         $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
431         // Run the upgrade.
432         upgrade_minmaxgrade();
434         // C1 and C2 were partially updated.
435         $this->assertTrue($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c1->id)));
436         $this->assertSame(false, grade_get_setting($c1->id, 'minmaxtouse', false, true));
437         $this->assertTrue($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c1->id)));
438         $this->assertTrue($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c2->id)));
439         $this->assertSame(false, grade_get_setting($c2->id, 'minmaxtouse', false, true));
440         $this->assertTrue($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c2->id)));
442         // Nothing has happened for C3.
443         $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
444         $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
445         $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
448         // Course setting should not be set on a course that has the setting already.
449         $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
450         grade_set_setting($c1->id, 'minmaxtouse', -1); // Sets different value than constant to check that it remained the same.
452         // Run the upgrade.
453         upgrade_minmaxgrade();
455         // C2 was updated.
456         $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c2->id, 'minmaxtouse', false, true));
458         // Nothing has happened for C1.
459         $this->assertSame('-1', grade_get_setting($c1->id, 'minmaxtouse', false, true));
461         // Nothing has happened for C3.
462         $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
463         $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
464         $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
467         // Final check, this time we'll unset the default config.
468         unset($CFG->grade_minmaxtouse);
469         grade_set_setting($c1->id, 'minmaxtouse', null);
471         // Run the upgrade.
472         upgrade_minmaxgrade();
474         // C1 was updated.
475         $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c1->id, 'minmaxtouse', false, true));
477         // Nothing has happened for C3.
478         $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
479         $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
480         $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
482         // Restore value.
483         $CFG->grade_minmaxtouse = $initialminmax;
484     }
486     public function test_upgrade_extra_credit_weightoverride() {
487         global $DB, $CFG;
489         $this->resetAfterTest(true);
491         $c = array();
492         $a = array();
493         $gi = array();
494         for ($i=0; $i<5; $i++) {
495             $c[$i] = $this->getDataGenerator()->create_course();
496             $a[$i] = array();
497             $gi[$i] = array();
498             for ($j=0;$j<3;$j++) {
499                 $a[$i][$j] = $this->getDataGenerator()->create_module('assign', array('course' => $c[$i], 'grade' => 100));
500                 $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a[$i][$j]->id,
501                     'courseid' => $c[$i]->id, 'itemnumber' => 0);
502                 $gi[$i][$j] = grade_item::fetch($giparams);
503             }
504         }
506         // Case 1: Course $c[0] has aggregation method different from natural.
507         $coursecategory = grade_category::fetch_course_category($c[0]->id);
508         $coursecategory->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN;
509         $coursecategory->update();
510         $gi[0][1]->aggregationcoef = 1;
511         $gi[0][1]->update();
512         $gi[0][2]->weightoverride = 1;
513         $gi[0][2]->update();
515         // Case 2: Course $c[1] has neither extra credits nor overrides
517         // Case 3: Course $c[2] has extra credits but no overrides
518         $gi[2][1]->aggregationcoef = 1;
519         $gi[2][1]->update();
521         // Case 4: Course $c[3] has no extra credits and has overrides
522         $gi[3][2]->weightoverride = 1;
523         $gi[3][2]->update();
525         // Case 5: Course $c[4] has both extra credits and overrides
526         $gi[4][1]->aggregationcoef = 1;
527         $gi[4][1]->update();
528         $gi[4][2]->weightoverride = 1;
529         $gi[4][2]->update();
531         // Run the upgrade script and make sure only course $c[4] was marked as needed to be fixed.
532         upgrade_extra_credit_weightoverride();
534         $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[0]->id}));
535         $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[1]->id}));
536         $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[2]->id}));
537         $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[3]->id}));
538         $this->assertEquals(20150619, $CFG->{'gradebook_calculations_freeze_' . $c[4]->id});
540         set_config('gradebook_calculations_freeze_' . $c[4]->id, null);
542         // Run the upgrade script for a single course only.
543         upgrade_extra_credit_weightoverride($c[0]->id);
544         $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[0]->id}));
545         upgrade_extra_credit_weightoverride($c[4]->id);
546         $this->assertEquals(20150619, $CFG->{'gradebook_calculations_freeze_' . $c[4]->id});
547     }
549     /**
550      * Test the upgrade function for flagging courses with calculated grade item problems.
551      */
552     public function test_upgrade_calculated_grade_items_freeze() {
553         global $DB, $CFG;
554         $this->resetAfterTest();
556         // Create a user.
557         $user = $this->getDataGenerator()->create_user();
559         // Create a couple of courses.
560         $course1 = $this->getDataGenerator()->create_course();
561         $course2 = $this->getDataGenerator()->create_course();
562         $course3 = $this->getDataGenerator()->create_course();
564         // Enrol the user in the courses.
565         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
566         $maninstance1 = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'), '*', MUST_EXIST);
567         $maninstance2 = $DB->get_record('enrol', array('courseid' => $course2->id, 'enrol' => 'manual'), '*', MUST_EXIST);
568         $maninstance3 = $DB->get_record('enrol', array('courseid' => $course3->id, 'enrol' => 'manual'), '*', MUST_EXIST);
569         $manual = enrol_get_plugin('manual');
570         $manual->enrol_user($maninstance1, $user->id, $studentrole->id);
571         $manual->enrol_user($maninstance2, $user->id, $studentrole->id);
572         $manual->enrol_user($maninstance3, $user->id, $studentrole->id);
574         // To create the data we need we freeze the grade book to use the old behaviour.
575         set_config('gradebook_calculations_freeze_' . $course1->id, 20150627);
576         set_config('gradebook_calculations_freeze_' . $course2->id, 20150627);
577         set_config('gradebook_calculations_freeze_' . $course3->id, 20150627);
578         $CFG->grade_minmaxtouse = 2;
580         // Creating a category for a grade item.
581         $gradecategory = new grade_category();
582         $gradecategory->fullname = 'calculated grade category';
583         $gradecategory->courseid = $course1->id;
584         $gradecategory->insert();
585         $gradecategoryid = $gradecategory->id;
587         // This is a manual grade item.
588         $gradeitem = new grade_item();
589         $gradeitem->itemname = 'grade item one';
590         $gradeitem->itemtype = 'manual';
591         $gradeitem->categoryid = $gradecategoryid;
592         $gradeitem->courseid = $course1->id;
593         $gradeitem->idnumber = 'gi1';
594         $gradeitem->insert();
596         // Changing the category into a calculated grade category.
597         $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id));
598         $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2';
599         $gradecategoryitem->update();
601         // Setting a grade for the student.
602         $grade = $gradeitem->get_grade($user->id, true);
603         $grade->finalgrade = 50;
604         $grade->update();
605         // Creating all the grade_grade items.
606         grade_regrade_final_grades($course1->id);
607         // Updating the grade category to a new grade max and min.
608         $gradecategoryitem->grademax = 50;
609         $gradecategoryitem->grademin = 5;
610         $gradecategoryitem->update();
612         // Different manual grade item for course 2. We are creating a course with a calculated grade item that has a grade max of
613         // 50. The grade_grade will have a rawgrademax of 100 regardless.
614         $gradeitem = new grade_item();
615         $gradeitem->itemname = 'grade item one';
616         $gradeitem->itemtype = 'manual';
617         $gradeitem->courseid = $course2->id;
618         $gradeitem->idnumber = 'gi1';
619         $gradeitem->grademax = 25;
620         $gradeitem->insert();
622         // Calculated grade item for course 2.
623         $calculatedgradeitem = new grade_item();
624         $calculatedgradeitem->itemname = 'calculated grade';
625         $calculatedgradeitem->itemtype = 'manual';
626         $calculatedgradeitem->courseid = $course2->id;
627         $calculatedgradeitem->calculation = '=##gi' . $gradeitem->id . '##*2';
628         $calculatedgradeitem->grademax = 50;
629         $calculatedgradeitem->insert();
631         // Assigning a grade for the user.
632         $grade = $gradeitem->get_grade($user->id, true);
633         $grade->finalgrade = 10;
634         $grade->update();
636         // Setting all of the grade_grade items.
637         grade_regrade_final_grades($course2->id);
639         // Different manual grade item for course 3. We are creating a course with a calculated grade item that has a grade max of
640         // 50. The grade_grade will have a rawgrademax of 100 regardless.
641         $gradeitem = new grade_item();
642         $gradeitem->itemname = 'grade item one';
643         $gradeitem->itemtype = 'manual';
644         $gradeitem->courseid = $course3->id;
645         $gradeitem->idnumber = 'gi1';
646         $gradeitem->grademax = 25;
647         $gradeitem->insert();
649         // Calculated grade item for course 2.
650         $calculatedgradeitem = new grade_item();
651         $calculatedgradeitem->itemname = 'calculated grade';
652         $calculatedgradeitem->itemtype = 'manual';
653         $calculatedgradeitem->courseid = $course3->id;
654         $calculatedgradeitem->calculation = '=##gi' . $gradeitem->id . '##*2';
655         $calculatedgradeitem->grademax = 50;
656         $calculatedgradeitem->insert();
658         // Assigning a grade for the user.
659         $grade = $gradeitem->get_grade($user->id, true);
660         $grade->finalgrade = 10;
661         $grade->update();
663         // Setting all of the grade_grade items.
664         grade_regrade_final_grades($course3->id);
665         // Need to do this first before changing the other courses, otherwise they will be flagged too early.
666         set_config('gradebook_calculations_freeze_' . $course3->id, null);
667         upgrade_calculated_grade_items($course3->id);
668         $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course3->id});
670         // Change the setting back to null.
671         set_config('gradebook_calculations_freeze_' . $course1->id, null);
672         set_config('gradebook_calculations_freeze_' . $course2->id, null);
673         // Run the upgrade.
674         upgrade_calculated_grade_items();
675         // The setting should be set again after the upgrade.
676         $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course1->id});
677         $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course2->id});
678     }
680     function test_upgrade_calculated_grade_items_regrade() {
681         global $DB, $CFG;
682         $this->resetAfterTest();
684         // Create a user.
685         $user = $this->getDataGenerator()->create_user();
687         // Create a course.
688         $course = $this->getDataGenerator()->create_course();
690         // Enrol the user in the course.
691         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
692         $maninstance1 = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
693         $manual = enrol_get_plugin('manual');
694         $manual->enrol_user($maninstance1, $user->id, $studentrole->id);
696         set_config('upgrade_calculatedgradeitemsonlyregrade', 1);
698         // Creating a category for a grade item.
699         $gradecategory = new grade_category();
700         $gradecategory->fullname = 'calculated grade category';
701         $gradecategory->courseid = $course->id;
702         $gradecategory->insert();
703         $gradecategoryid = $gradecategory->id;
705         // This is a manual grade item.
706         $gradeitem = new grade_item();
707         $gradeitem->itemname = 'grade item one';
708         $gradeitem->itemtype = 'manual';
709         $gradeitem->categoryid = $gradecategoryid;
710         $gradeitem->courseid = $course->id;
711         $gradeitem->idnumber = 'gi1';
712         $gradeitem->insert();
714         // Changing the category into a calculated grade category.
715         $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id));
716         $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2';
717         $gradecategoryitem->grademax = 50;
718         $gradecategoryitem->grademin = 15;
719         $gradecategoryitem->update();
721         // Setting a grade for the student.
722         $grade = $gradeitem->get_grade($user->id, true);
723         $grade->finalgrade = 50;
724         $grade->update();
726         grade_regrade_final_grades($course->id);
727         $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
728         $grade->rawgrademax = 100;
729         $grade->rawgrademin = 0;
730         $grade->update();
731         $this->assertNotEquals($gradecategoryitem->grademax, $grade->rawgrademax);
732         $this->assertNotEquals($gradecategoryitem->grademin, $grade->rawgrademin);
734         // This is the function that we are testing. If we comment out this line, then the test fails because the grade items
735         // are not flagged for regrading.
736         upgrade_calculated_grade_items();
737         grade_regrade_final_grades($course->id);
739         $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
741         $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax);
742         $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin);
743     }
745     public function test_upgrade_course_tags() {
746         global $DB;
747         $this->resetAfterTest();
749         // Running upgrade script when there are no tags.
750         upgrade_course_tags();
751         $this->assertFalse($DB->record_exists('tag_instance', array()));
753         // No course entries.
754         $DB->insert_record('tag_instance', array('itemid' => 123, 'tagid' => 101, 'tiuserid' => 0,
755             'itemtype' => 'post', 'component' => 'core', 'contextid' => 1));
756         $DB->insert_record('tag_instance', array('itemid' => 333, 'tagid' => 103, 'tiuserid' => 1002,
757             'itemtype' => 'post', 'component' => 'core', 'contextid' => 1));
759         upgrade_course_tags();
760         $records = array_values($DB->get_records('tag_instance', array(), 'id', '*'));
761         $this->assertEquals(2, count($records));
762         $this->assertEquals(123, $records[0]->itemid);
763         $this->assertEquals(333, $records[1]->itemid);
765         // Imagine we have tags 101, 102, 103, ... and courses 1, 2, 3, ... and users 1001, 1002, ... .
766         $keys = array('itemid', 'tagid', 'tiuserid');
767         $valuesets = array(
768             array(1, 101, 0),
769             array(1, 102, 0),
771             array(2, 102, 0),
772             array(2, 103, 1001),
774             array(3, 103, 0),
775             array(3, 103, 1001),
777             array(3, 104, 1006),
778             array(3, 104, 1001),
779             array(3, 104, 1002),
780         );
782         foreach ($valuesets as $values) {
783             $DB->insert_record('tag_instance', array_combine($keys, $values) +
784                     array('itemtype' => 'course', 'component' => 'core', 'contextid' => 1));
785         }
787         upgrade_course_tags();
788         // There are 8 records in 'tag_instance' table and 7 of them do not have tiuserid (except for one 'post').
789         $records = array_values($DB->get_records('tag_instance', array(), 'id', '*'));
790         $this->assertEquals(8, count($records));
791         $this->assertEquals(7, $DB->count_records('tag_instance', array('tiuserid' => 0)));
792         // Course 1 is mapped to tags 101 and 102.
793         $this->assertEquals(array(101, 102), array_values($DB->get_fieldset_select('tag_instance', 'tagid',
794                 'itemtype = ? AND itemid = ? ORDER BY tagid', array('course', 1))));
795         // Course 2 is mapped to tags 102 and 103.
796         $this->assertEquals(array(102, 103), array_values($DB->get_fieldset_select('tag_instance', 'tagid',
797                 'itemtype = ? AND itemid = ? ORDER BY tagid', array('course', 2))));
798         // Course 1 is mapped to tags 101 and 102.
799         $this->assertEquals(array(103, 104), array_values($DB->get_fieldset_select('tag_instance', 'tagid',
800                 'itemtype = ? AND itemid = ? ORDER BY tagid', array('course', 3))));
801     }