MDL-51580 upgrade: clean < 2.7.0 upgrade steps
[moodle.git] / lib / tests / upgradelib_test.php
CommitLineData
6b7df0b5
EL
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
f5700877 18 * Unit tests for the lib/upgradelib.php library.
6b7df0b5
EL
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 */
25
26defined('MOODLE_INTERNAL') || die();
27
28global $CFG;
29require_once($CFG->libdir.'/upgradelib.php');
30
31
32/**
f5700877 33 * Tests various classes and functions in upgradelib.php library.
6b7df0b5 34 */
f5700877 35class core_upgradelib_testcase extends advanced_testcase {
6b7df0b5
EL
36
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 }
2a9d7a42
DP
45
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;
52
53 $this->resetAfterTest(true);
54
b35ef82f
MG
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';
59
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();
78
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 }
2a9d7a42
DP
88
89 $duplicatedetectionsql = "SELECT courseid, sortorder
90 FROM {grade_items}
91 GROUP BY courseid, sortorder
92 HAVING COUNT(id) > 1";
93
94 // Verify there are duplicates before we start the fix.
95 $dupes = $DB->record_exists_sql($duplicatedetectionsql);
96 $this->assertTrue($dupes);
97
98 // Do the work.
99 upgrade_grade_item_fix_sortorder();
100
101 // Verify that no duplicates are left in the database.
102 $dupes = $DB->record_exists_sql($duplicatedetectionsql);
103 $this->assertFalse($dupes);
104
b35ef82f
MG
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++;
2a9d7a42
DP
117 }
118 }
119
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');
136
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();
147
148 $item->id = $DB->insert_record('grade_items', $item);
149
150 return $DB->get_record('grade_items', array('id' => $item->id));
151 }
119cf17e
DP
152
153 public function test_upgrade_fix_missing_root_folders() {
154 global $DB, $SITE;
155
156 $this->resetAfterTest(true);
157
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));
165
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);
172
173 // Verify file records exist.
174 $areafilecount = $DB->count_records('files', $selectargs);
175 $this->assertNotEmpty($areafilecount);
176
177 // Delete the folder record.
178 $folderrecord = $selectargs;
179 $folderrecord['filepath'] = '/';
180 $folderrecord['filename'] = '.';
6c704b15
DP
181
182 // Get previous folder record.
183 $oldrecord = $DB->get_record('files', $folderrecord);
119cf17e
DP
184 $DB->delete_records('files', $folderrecord);
185
186 // Verify the folder record has been removed.
187 $newareafilecount = $DB->count_records('files', $selectargs);
188 $this->assertSame($newareafilecount, $areafilecount - 1);
189
190 $this->assertFalse($DB->record_exists('files', $folderrecord));
191
192 // Run the upgrade step!
193 upgrade_fix_missing_root_folders();
194
195 // Verify the folder record has been restored.
196 $newareafilecount = $DB->count_records('files', $selectargs);
197 $this->assertSame($newareafilecount, $areafilecount);
198
6c704b15
DP
199 $newrecord = $DB->get_record('files', $folderrecord, '*', MUST_EXIST);
200 // Verify the hash is correctly created.
201 $this->assertSame($oldrecord->pathnamehash, $newrecord->pathnamehash);
119cf17e 202 }
8e97006a 203
941de296
MG
204 public function test_upgrade_fix_missing_root_folders_draft() {
205 global $DB, $SITE;
206
207 $this->resetAfterTest(true);
208
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);
217
218 $queryparams = array(
219 'component' => 'user',
220 'contextid' => $usercontext->id,
221 'filearea' => 'draft',
222 'itemid' => $draftitemid,
223 );
224
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' => '.'));
230
231 // Delete record with filename '.' and make sure it does not exist any more.
232 $DB->delete_records('files', $queryparams + array('filename' => '.'));
233
234 $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
235 $this->assertEquals(1, count($records));
236 $this->assertFalse(in_array('.', $records));
237
238 // Run upgrade script and make sure the record is restored.
239 upgrade_fix_missing_root_folders_draft();
240
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 }
247
8e97006a 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();
256
257 // This function is in the other upgradelib.
258 require_once($CFG->libdir . '/db/upgradelib.php');
259
260 // Groupmembersonly (or nothing). Show option on but ignored.
061e6b28 261 // Note: This $CFG option doesn't exist any more but we are testing the
262 // upgrade function so it did exist then...
8e97006a 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()));
275
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()));
290
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()));
310
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()));
322
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)));
340
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 }
056aeae8
FM
356
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();
365
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));
373
374 $cm1 = get_coursemodule_from_instance('assign', $a1->id);
375 $ctx1 = context_module::instance($cm1->id);
376 $assign1 = new assign($ctx1, $cm1, $c1);
377
378 $cm2 = get_coursemodule_from_instance('assign', $a2->id);
379 $ctx2 = context_module::instance($cm2->id);
380 $assign2 = new assign($ctx2, $cm2, $c2);
381
382 $cm3 = get_coursemodule_from_instance('assign', $a3->id);
383 $ctx3 = context_module::instance($cm3->id);
384 $assign3 = new assign($ctx3, $cm3, $c3);
385
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);
390
391 $ug = $assign2->get_user_grade($u1->id, true);
392 $ug->grade = 20;
393 $assign2->update_grade($ug);
394
395 $ug = $assign3->get_user_grade($u1->id, true);
396 $ug->grade = 30;
397 $assign3->update_grade($ug);
398
399
400 // Run the upgrade.
401 upgrade_minmaxgrade();
402
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)));
413
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();
420
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();
426
427
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;
430
431 // Run the upgrade.
432 upgrade_minmaxgrade();
433
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)));
441
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)));
446
447
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.
451
452 // Run the upgrade.
453 upgrade_minmaxgrade();
454
455 // C2 was updated.
456 $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c2->id, 'minmaxtouse', false, true));
457
458 // Nothing has happened for C1.
459 $this->assertSame('-1', grade_get_setting($c1->id, 'minmaxtouse', false, true));
460
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)));
465
466
467 // Final check, this time we'll unset the default config.
468 unset($CFG->grade_minmaxtouse);
469 grade_set_setting($c1->id, 'minmaxtouse', null);
470
471 // Run the upgrade.
472 upgrade_minmaxgrade();
473
474 // C1 was updated.
475 $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c1->id, 'minmaxtouse', false, true));
476
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)));
481
482 // Restore value.
483 $CFG->grade_minmaxtouse = $initialminmax;
484 }
156d0486
MG
485
486 public function test_upgrade_extra_credit_weightoverride() {
487 global $DB, $CFG;
488
489 $this->resetAfterTest(true);
490
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 }
505
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();
514
515 // Case 2: Course $c[1] has neither extra credits nor overrides
516
517 // Case 3: Course $c[2] has extra credits but no overrides
518 $gi[2][1]->aggregationcoef = 1;
519 $gi[2][1]->update();
520
521 // Case 4: Course $c[3] has no extra credits and has overrides
522 $gi[3][2]->weightoverride = 1;
523 $gi[3][2]->update();
524
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();
530
531 // Run the upgrade script and make sure only course $c[4] was marked as needed to be fixed.
532 upgrade_extra_credit_weightoverride();
533
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});
539
540 set_config('gradebook_calculations_freeze_' . $c[4]->id, null);
541
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 }
716c0810
AG
548
549 /**
550 * Test the upgrade function for flagging courses with calculated grade item problems.
551 */
316c560c 552 public function test_upgrade_calculated_grade_items_freeze() {
716c0810
AG
553 global $DB, $CFG;
554 $this->resetAfterTest();
555
556 // Create a user.
557 $user = $this->getDataGenerator()->create_user();
558
559 // Create a couple of courses.
560 $course1 = $this->getDataGenerator()->create_course();
561 $course2 = $this->getDataGenerator()->create_course();
562 $course3 = $this->getDataGenerator()->create_course();
563
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);
573
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;
579
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;
586
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();
595
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();
600
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();
611
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();
621
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();
630
631 // Assigning a grade for the user.
632 $grade = $gradeitem->get_grade($user->id, true);
633 $grade->finalgrade = 10;
634 $grade->update();
635
636 // Setting all of the grade_grade items.
637 grade_regrade_final_grades($course2->id);
638
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();
648
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();
657
658 // Assigning a grade for the user.
659 $grade = $gradeitem->get_grade($user->id, true);
660 $grade->finalgrade = 10;
661 $grade->update();
662
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});
669
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 }
316c560c
AG
679
680 function test_upgrade_calculated_grade_items_regrade() {
681 global $DB, $CFG;
682 $this->resetAfterTest();
683
684 // Create a user.
685 $user = $this->getDataGenerator()->create_user();
686
687 // Create a course.
688 $course = $this->getDataGenerator()->create_course();
689
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);
695
696 set_config('upgrade_calculatedgradeitemsonlyregrade', 1);
697
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;
704
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();
713
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();
720
721 // Setting a grade for the student.
722 $grade = $gradeitem->get_grade($user->id, true);
723 $grade->finalgrade = 50;
724 $grade->update();
725
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);
733
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);
738
739 $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
740
741 $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax);
742 $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin);
743 }
0d1e5456
MG
744
745 public function test_upgrade_course_tags() {
746 global $DB;
747 $this->resetAfterTest();
748
749 // Running upgrade script when there are no tags.
750 upgrade_course_tags();
751 $this->assertFalse($DB->record_exists('tag_instance', array()));
752
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));
758
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);
764
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),
770
771 array(2, 102, 0),
772 array(2, 103, 1001),
773
774 array(3, 103, 0),
775 array(3, 103, 1001),
776
777 array(3, 104, 1006),
778 array(3, 104, 1001),
779 array(3, 104, 1002),
780 );
781
782 foreach ($valuesets as $values) {
783 $DB->insert_record('tag_instance', array_combine($keys, $values) +
784 array('itemtype' => 'course', 'component' => 'core', 'contextid' => 1));
785 }
786
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 }
6b7df0b5 802}