MDL-45390 grades: Courses letters using system bad boundaries
[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
6b7df0b5 31/**
f5700877 32 * Tests various classes and functions in upgradelib.php library.
6b7df0b5 33 */
f5700877 34class core_upgradelib_testcase extends advanced_testcase {
6b7df0b5
EL
35
36 /**
37 * Test the {@link upgrade_stale_php_files_present() function
38 */
39 public function test_upgrade_stale_php_files_present() {
40 // Just call the function, must return bool false always
41 // if there aren't any old files in the codebase.
42 $this->assertFalse(upgrade_stale_php_files_present());
43 }
2a9d7a42 44
2a9d7a42
DP
45 /**
46 * Populate some fake grade items into the database with specified
47 * sortorder and course id.
48 *
49 * NOTE: This function doesn't make much attempt to respect the
50 * gradebook internals, its simply used to fake some data for
51 * testing the upgradelib function. Please don't use it for other
52 * purposes.
53 *
54 * @param int $courseid id of course
55 * @param int $sortorder numeric sorting order of item
56 * @return stdClass grade item object from the database.
57 */
58 private function insert_fake_grade_item_sortorder($courseid, $sortorder) {
59 global $DB, $CFG;
60 require_once($CFG->libdir.'/gradelib.php');
61
62 $item = new stdClass();
63 $item->courseid = $courseid;
64 $item->sortorder = $sortorder;
65 $item->gradetype = GRADE_TYPE_VALUE;
66 $item->grademin = 30;
67 $item->grademax = 110;
68 $item->itemnumber = 1;
69 $item->iteminfo = '';
70 $item->timecreated = time();
71 $item->timemodified = time();
72
73 $item->id = $DB->insert_record('grade_items', $item);
74
75 return $DB->get_record('grade_items', array('id' => $item->id));
76 }
119cf17e 77
941de296
MG
78 public function test_upgrade_fix_missing_root_folders_draft() {
79 global $DB, $SITE;
80
81 $this->resetAfterTest(true);
82
83 $user = $this->getDataGenerator()->create_user();
84 $usercontext = context_user::instance($user->id);
85 $this->setUser($user);
86 $resource1 = $this->getDataGenerator()->get_plugin_generator('mod_resource')
87 ->create_instance(array('course' => $SITE->id));
88 $context = context_module::instance($resource1->cmid);
89 $draftitemid = 0;
90 file_prepare_draft_area($draftitemid, $context->id, 'mod_resource', 'content', 0);
91
92 $queryparams = array(
93 'component' => 'user',
94 'contextid' => $usercontext->id,
95 'filearea' => 'draft',
96 'itemid' => $draftitemid,
97 );
98
99 // Make sure there are two records in files for the draft file area and one of them has filename '.'.
100 $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
101 $this->assertEquals(2, count($records));
102 $this->assertTrue(in_array('.', $records));
103 $originalhash = $DB->get_field('files', 'pathnamehash', $queryparams + array('filename' => '.'));
104
105 // Delete record with filename '.' and make sure it does not exist any more.
106 $DB->delete_records('files', $queryparams + array('filename' => '.'));
107
108 $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
109 $this->assertEquals(1, count($records));
110 $this->assertFalse(in_array('.', $records));
111
112 // Run upgrade script and make sure the record is restored.
113 upgrade_fix_missing_root_folders_draft();
114
115 $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
116 $this->assertEquals(2, count($records));
117 $this->assertTrue(in_array('.', $records));
118 $newhash = $DB->get_field('files', 'pathnamehash', $queryparams + array('filename' => '.'));
119 $this->assertEquals($originalhash, $newhash);
120 }
121
056aeae8
FM
122 /**
123 * Test upgrade minmaxgrade step.
124 */
125 public function test_upgrade_minmaxgrade() {
126 global $CFG, $DB;
127 require_once($CFG->libdir . '/gradelib.php');
128 $initialminmax = $CFG->grade_minmaxtouse;
129 $this->resetAfterTest();
130
131 $c1 = $this->getDataGenerator()->create_course();
132 $c2 = $this->getDataGenerator()->create_course();
133 $c3 = $this->getDataGenerator()->create_course();
134 $u1 = $this->getDataGenerator()->create_user();
135 $a1 = $this->getDataGenerator()->create_module('assign', array('course' => $c1, 'grade' => 100));
136 $a2 = $this->getDataGenerator()->create_module('assign', array('course' => $c2, 'grade' => 100));
137 $a3 = $this->getDataGenerator()->create_module('assign', array('course' => $c3, 'grade' => 100));
138
139 $cm1 = get_coursemodule_from_instance('assign', $a1->id);
140 $ctx1 = context_module::instance($cm1->id);
141 $assign1 = new assign($ctx1, $cm1, $c1);
142
143 $cm2 = get_coursemodule_from_instance('assign', $a2->id);
144 $ctx2 = context_module::instance($cm2->id);
145 $assign2 = new assign($ctx2, $cm2, $c2);
146
147 $cm3 = get_coursemodule_from_instance('assign', $a3->id);
148 $ctx3 = context_module::instance($cm3->id);
149 $assign3 = new assign($ctx3, $cm3, $c3);
150
151 // Give a grade to the student.
152 $ug = $assign1->get_user_grade($u1->id, true);
153 $ug->grade = 10;
154 $assign1->update_grade($ug);
155
156 $ug = $assign2->get_user_grade($u1->id, true);
157 $ug->grade = 20;
158 $assign2->update_grade($ug);
159
160 $ug = $assign3->get_user_grade($u1->id, true);
161 $ug->grade = 30;
162 $assign3->update_grade($ug);
163
164
165 // Run the upgrade.
166 upgrade_minmaxgrade();
167
168 // Nothing has happened.
169 $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c1->id)));
170 $this->assertSame(false, grade_get_setting($c1->id, 'minmaxtouse', false, true));
171 $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c1->id)));
172 $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c2->id)));
173 $this->assertSame(false, grade_get_setting($c2->id, 'minmaxtouse', false, true));
174 $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c2->id)));
175 $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
176 $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
177 $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
178
179 // Create inconsistency in c1 and c2.
180 $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a1->id,
181 'courseid' => $c1->id, 'itemnumber' => 0);
182 $gi = grade_item::fetch($giparams);
183 $gi->grademin = 5;
184 $gi->update();
185
186 $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a2->id,
187 'courseid' => $c2->id, 'itemnumber' => 0);
188 $gi = grade_item::fetch($giparams);
189 $gi->grademax = 50;
190 $gi->update();
191
192
193 // C1 and C2 should be updated, but the course setting should not be set.
194 $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
195
196 // Run the upgrade.
197 upgrade_minmaxgrade();
198
199 // C1 and C2 were partially updated.
200 $this->assertTrue($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c1->id)));
201 $this->assertSame(false, grade_get_setting($c1->id, 'minmaxtouse', false, true));
202 $this->assertTrue($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c1->id)));
203 $this->assertTrue($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c2->id)));
204 $this->assertSame(false, grade_get_setting($c2->id, 'minmaxtouse', false, true));
205 $this->assertTrue($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c2->id)));
206
207 // Nothing has happened for C3.
208 $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
209 $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
210 $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
211
212
213 // Course setting should not be set on a course that has the setting already.
214 $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
215 grade_set_setting($c1->id, 'minmaxtouse', -1); // Sets different value than constant to check that it remained the same.
216
217 // Run the upgrade.
218 upgrade_minmaxgrade();
219
220 // C2 was updated.
221 $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c2->id, 'minmaxtouse', false, true));
222
223 // Nothing has happened for C1.
224 $this->assertSame('-1', grade_get_setting($c1->id, 'minmaxtouse', false, true));
225
226 // Nothing has happened for C3.
227 $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
228 $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
229 $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
230
231
232 // Final check, this time we'll unset the default config.
233 unset($CFG->grade_minmaxtouse);
234 grade_set_setting($c1->id, 'minmaxtouse', null);
235
236 // Run the upgrade.
237 upgrade_minmaxgrade();
238
239 // C1 was updated.
240 $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c1->id, 'minmaxtouse', false, true));
241
242 // Nothing has happened for C3.
243 $this->assertFalse($DB->record_exists('config', array('name' => 'show_min_max_grades_changed_' . $c3->id)));
244 $this->assertSame(false, grade_get_setting($c3->id, 'minmaxtouse', false, true));
245 $this->assertFalse($DB->record_exists('grade_items', array('needsupdate' => 1, 'courseid' => $c3->id)));
246
247 // Restore value.
248 $CFG->grade_minmaxtouse = $initialminmax;
249 }
156d0486
MG
250
251 public function test_upgrade_extra_credit_weightoverride() {
252 global $DB, $CFG;
253
254 $this->resetAfterTest(true);
255
1405f010
EL
256 require_once($CFG->libdir . '/db/upgradelib.php');
257
156d0486
MG
258 $c = array();
259 $a = array();
260 $gi = array();
261 for ($i=0; $i<5; $i++) {
262 $c[$i] = $this->getDataGenerator()->create_course();
263 $a[$i] = array();
264 $gi[$i] = array();
265 for ($j=0;$j<3;$j++) {
266 $a[$i][$j] = $this->getDataGenerator()->create_module('assign', array('course' => $c[$i], 'grade' => 100));
267 $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a[$i][$j]->id,
268 'courseid' => $c[$i]->id, 'itemnumber' => 0);
269 $gi[$i][$j] = grade_item::fetch($giparams);
270 }
271 }
272
273 // Case 1: Course $c[0] has aggregation method different from natural.
274 $coursecategory = grade_category::fetch_course_category($c[0]->id);
275 $coursecategory->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN;
276 $coursecategory->update();
277 $gi[0][1]->aggregationcoef = 1;
278 $gi[0][1]->update();
279 $gi[0][2]->weightoverride = 1;
280 $gi[0][2]->update();
281
282 // Case 2: Course $c[1] has neither extra credits nor overrides
283
284 // Case 3: Course $c[2] has extra credits but no overrides
285 $gi[2][1]->aggregationcoef = 1;
286 $gi[2][1]->update();
287
288 // Case 4: Course $c[3] has no extra credits and has overrides
289 $gi[3][2]->weightoverride = 1;
290 $gi[3][2]->update();
291
292 // Case 5: Course $c[4] has both extra credits and overrides
293 $gi[4][1]->aggregationcoef = 1;
294 $gi[4][1]->update();
295 $gi[4][2]->weightoverride = 1;
296 $gi[4][2]->update();
297
298 // Run the upgrade script and make sure only course $c[4] was marked as needed to be fixed.
299 upgrade_extra_credit_weightoverride();
300
301 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[0]->id}));
302 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[1]->id}));
303 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[2]->id}));
304 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[3]->id}));
305 $this->assertEquals(20150619, $CFG->{'gradebook_calculations_freeze_' . $c[4]->id});
306
307 set_config('gradebook_calculations_freeze_' . $c[4]->id, null);
308
309 // Run the upgrade script for a single course only.
310 upgrade_extra_credit_weightoverride($c[0]->id);
311 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $c[0]->id}));
312 upgrade_extra_credit_weightoverride($c[4]->id);
313 $this->assertEquals(20150619, $CFG->{'gradebook_calculations_freeze_' . $c[4]->id});
314 }
716c0810
AG
315
316 /**
317 * Test the upgrade function for flagging courses with calculated grade item problems.
318 */
316c560c 319 public function test_upgrade_calculated_grade_items_freeze() {
716c0810 320 global $DB, $CFG;
1405f010 321
716c0810
AG
322 $this->resetAfterTest();
323
1405f010
EL
324 require_once($CFG->libdir . '/db/upgradelib.php');
325
716c0810
AG
326 // Create a user.
327 $user = $this->getDataGenerator()->create_user();
328
329 // Create a couple of courses.
330 $course1 = $this->getDataGenerator()->create_course();
331 $course2 = $this->getDataGenerator()->create_course();
332 $course3 = $this->getDataGenerator()->create_course();
333
334 // Enrol the user in the courses.
335 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
336 $maninstance1 = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'), '*', MUST_EXIST);
337 $maninstance2 = $DB->get_record('enrol', array('courseid' => $course2->id, 'enrol' => 'manual'), '*', MUST_EXIST);
338 $maninstance3 = $DB->get_record('enrol', array('courseid' => $course3->id, 'enrol' => 'manual'), '*', MUST_EXIST);
339 $manual = enrol_get_plugin('manual');
340 $manual->enrol_user($maninstance1, $user->id, $studentrole->id);
341 $manual->enrol_user($maninstance2, $user->id, $studentrole->id);
342 $manual->enrol_user($maninstance3, $user->id, $studentrole->id);
343
344 // To create the data we need we freeze the grade book to use the old behaviour.
345 set_config('gradebook_calculations_freeze_' . $course1->id, 20150627);
346 set_config('gradebook_calculations_freeze_' . $course2->id, 20150627);
347 set_config('gradebook_calculations_freeze_' . $course3->id, 20150627);
348 $CFG->grade_minmaxtouse = 2;
349
350 // Creating a category for a grade item.
351 $gradecategory = new grade_category();
352 $gradecategory->fullname = 'calculated grade category';
353 $gradecategory->courseid = $course1->id;
354 $gradecategory->insert();
355 $gradecategoryid = $gradecategory->id;
356
357 // This is a manual grade item.
358 $gradeitem = new grade_item();
359 $gradeitem->itemname = 'grade item one';
360 $gradeitem->itemtype = 'manual';
361 $gradeitem->categoryid = $gradecategoryid;
362 $gradeitem->courseid = $course1->id;
363 $gradeitem->idnumber = 'gi1';
364 $gradeitem->insert();
365
366 // Changing the category into a calculated grade category.
367 $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id));
368 $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2';
369 $gradecategoryitem->update();
370
371 // Setting a grade for the student.
372 $grade = $gradeitem->get_grade($user->id, true);
373 $grade->finalgrade = 50;
374 $grade->update();
375 // Creating all the grade_grade items.
376 grade_regrade_final_grades($course1->id);
377 // Updating the grade category to a new grade max and min.
378 $gradecategoryitem->grademax = 50;
379 $gradecategoryitem->grademin = 5;
380 $gradecategoryitem->update();
381
382 // Different manual grade item for course 2. We are creating a course with a calculated grade item that has a grade max of
383 // 50. The grade_grade will have a rawgrademax of 100 regardless.
384 $gradeitem = new grade_item();
385 $gradeitem->itemname = 'grade item one';
386 $gradeitem->itemtype = 'manual';
387 $gradeitem->courseid = $course2->id;
388 $gradeitem->idnumber = 'gi1';
389 $gradeitem->grademax = 25;
390 $gradeitem->insert();
391
392 // Calculated grade item for course 2.
393 $calculatedgradeitem = new grade_item();
394 $calculatedgradeitem->itemname = 'calculated grade';
395 $calculatedgradeitem->itemtype = 'manual';
396 $calculatedgradeitem->courseid = $course2->id;
397 $calculatedgradeitem->calculation = '=##gi' . $gradeitem->id . '##*2';
398 $calculatedgradeitem->grademax = 50;
399 $calculatedgradeitem->insert();
400
401 // Assigning a grade for the user.
402 $grade = $gradeitem->get_grade($user->id, true);
403 $grade->finalgrade = 10;
404 $grade->update();
405
406 // Setting all of the grade_grade items.
407 grade_regrade_final_grades($course2->id);
408
409 // Different manual grade item for course 3. We are creating a course with a calculated grade item that has a grade max of
410 // 50. The grade_grade will have a rawgrademax of 100 regardless.
411 $gradeitem = new grade_item();
412 $gradeitem->itemname = 'grade item one';
413 $gradeitem->itemtype = 'manual';
414 $gradeitem->courseid = $course3->id;
415 $gradeitem->idnumber = 'gi1';
416 $gradeitem->grademax = 25;
417 $gradeitem->insert();
418
419 // Calculated grade item for course 2.
420 $calculatedgradeitem = new grade_item();
421 $calculatedgradeitem->itemname = 'calculated grade';
422 $calculatedgradeitem->itemtype = 'manual';
423 $calculatedgradeitem->courseid = $course3->id;
424 $calculatedgradeitem->calculation = '=##gi' . $gradeitem->id . '##*2';
425 $calculatedgradeitem->grademax = 50;
426 $calculatedgradeitem->insert();
427
428 // Assigning a grade for the user.
429 $grade = $gradeitem->get_grade($user->id, true);
430 $grade->finalgrade = 10;
431 $grade->update();
432
433 // Setting all of the grade_grade items.
434 grade_regrade_final_grades($course3->id);
435 // Need to do this first before changing the other courses, otherwise they will be flagged too early.
436 set_config('gradebook_calculations_freeze_' . $course3->id, null);
437 upgrade_calculated_grade_items($course3->id);
438 $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course3->id});
439
440 // Change the setting back to null.
441 set_config('gradebook_calculations_freeze_' . $course1->id, null);
442 set_config('gradebook_calculations_freeze_' . $course2->id, null);
443 // Run the upgrade.
444 upgrade_calculated_grade_items();
445 // The setting should be set again after the upgrade.
446 $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course1->id});
447 $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course2->id});
448 }
316c560c
AG
449
450 function test_upgrade_calculated_grade_items_regrade() {
451 global $DB, $CFG;
1405f010 452
316c560c
AG
453 $this->resetAfterTest();
454
1405f010
EL
455 require_once($CFG->libdir . '/db/upgradelib.php');
456
316c560c
AG
457 // Create a user.
458 $user = $this->getDataGenerator()->create_user();
459
460 // Create a course.
461 $course = $this->getDataGenerator()->create_course();
462
463 // Enrol the user in the course.
464 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
465 $maninstance1 = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
466 $manual = enrol_get_plugin('manual');
467 $manual->enrol_user($maninstance1, $user->id, $studentrole->id);
468
469 set_config('upgrade_calculatedgradeitemsonlyregrade', 1);
470
471 // Creating a category for a grade item.
472 $gradecategory = new grade_category();
473 $gradecategory->fullname = 'calculated grade category';
474 $gradecategory->courseid = $course->id;
475 $gradecategory->insert();
476 $gradecategoryid = $gradecategory->id;
477
478 // This is a manual grade item.
479 $gradeitem = new grade_item();
480 $gradeitem->itemname = 'grade item one';
481 $gradeitem->itemtype = 'manual';
482 $gradeitem->categoryid = $gradecategoryid;
483 $gradeitem->courseid = $course->id;
484 $gradeitem->idnumber = 'gi1';
485 $gradeitem->insert();
486
487 // Changing the category into a calculated grade category.
488 $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id));
489 $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2';
490 $gradecategoryitem->grademax = 50;
491 $gradecategoryitem->grademin = 15;
492 $gradecategoryitem->update();
493
494 // Setting a grade for the student.
495 $grade = $gradeitem->get_grade($user->id, true);
496 $grade->finalgrade = 50;
497 $grade->update();
498
499 grade_regrade_final_grades($course->id);
500 $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
501 $grade->rawgrademax = 100;
502 $grade->rawgrademin = 0;
503 $grade->update();
504 $this->assertNotEquals($gradecategoryitem->grademax, $grade->rawgrademax);
505 $this->assertNotEquals($gradecategoryitem->grademin, $grade->rawgrademin);
506
507 // This is the function that we are testing. If we comment out this line, then the test fails because the grade items
508 // are not flagged for regrading.
509 upgrade_calculated_grade_items();
510 grade_regrade_final_grades($course->id);
511
512 $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
513
514 $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax);
515 $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin);
516 }
0d1e5456
MG
517
518 public function test_upgrade_course_tags() {
1405f010
EL
519 global $DB, $CFG;
520
0d1e5456
MG
521 $this->resetAfterTest();
522
1405f010
EL
523 require_once($CFG->libdir . '/db/upgradelib.php');
524
0d1e5456
MG
525 // Running upgrade script when there are no tags.
526 upgrade_course_tags();
527 $this->assertFalse($DB->record_exists('tag_instance', array()));
528
529 // No course entries.
530 $DB->insert_record('tag_instance', array('itemid' => 123, 'tagid' => 101, 'tiuserid' => 0,
531 'itemtype' => 'post', 'component' => 'core', 'contextid' => 1));
532 $DB->insert_record('tag_instance', array('itemid' => 333, 'tagid' => 103, 'tiuserid' => 1002,
533 'itemtype' => 'post', 'component' => 'core', 'contextid' => 1));
534
535 upgrade_course_tags();
536 $records = array_values($DB->get_records('tag_instance', array(), 'id', '*'));
537 $this->assertEquals(2, count($records));
538 $this->assertEquals(123, $records[0]->itemid);
539 $this->assertEquals(333, $records[1]->itemid);
540
541 // Imagine we have tags 101, 102, 103, ... and courses 1, 2, 3, ... and users 1001, 1002, ... .
542 $keys = array('itemid', 'tagid', 'tiuserid');
543 $valuesets = array(
544 array(1, 101, 0),
545 array(1, 102, 0),
546
547 array(2, 102, 0),
548 array(2, 103, 1001),
549
550 array(3, 103, 0),
551 array(3, 103, 1001),
552
553 array(3, 104, 1006),
554 array(3, 104, 1001),
555 array(3, 104, 1002),
556 );
557
558 foreach ($valuesets as $values) {
559 $DB->insert_record('tag_instance', array_combine($keys, $values) +
560 array('itemtype' => 'course', 'component' => 'core', 'contextid' => 1));
561 }
562
563 upgrade_course_tags();
564 // There are 8 records in 'tag_instance' table and 7 of them do not have tiuserid (except for one 'post').
565 $records = array_values($DB->get_records('tag_instance', array(), 'id', '*'));
566 $this->assertEquals(8, count($records));
567 $this->assertEquals(7, $DB->count_records('tag_instance', array('tiuserid' => 0)));
568 // Course 1 is mapped to tags 101 and 102.
569 $this->assertEquals(array(101, 102), array_values($DB->get_fieldset_select('tag_instance', 'tagid',
570 'itemtype = ? AND itemid = ? ORDER BY tagid', array('course', 1))));
571 // Course 2 is mapped to tags 102 and 103.
572 $this->assertEquals(array(102, 103), array_values($DB->get_fieldset_select('tag_instance', 'tagid',
573 'itemtype = ? AND itemid = ? ORDER BY tagid', array('course', 2))));
574 // Course 1 is mapped to tags 101 and 102.
575 $this->assertEquals(array(103, 104), array_values($DB->get_fieldset_select('tag_instance', 'tagid',
576 'itemtype = ? AND itemid = ? ORDER BY tagid', array('course', 3))));
577 }
70dfd48f
AG
578
579 /**
580 * Test that the upgrade script correctly flags courses to be frozen due to letter boundary problems.
581 */
582 public function test_upgrade_course_letter_boundary() {
583 global $CFG, $DB;
584 $this->resetAfterTest(true);
585
ece791db
DM
586 require_once($CFG->libdir . '/db/upgradelib.php');
587
70dfd48f
AG
588 // Create a user.
589 $user = $this->getDataGenerator()->create_user();
590
591 // Create some courses.
592 $courses = array();
593 $contexts = array();
ece791db 594 for ($i = 0; $i < 37; $i++) {
70dfd48f
AG
595 $course = $this->getDataGenerator()->create_course();
596 $context = context_course::instance($course->id);
ece791db 597 if (in_array($i, array(2, 5, 10, 13, 14, 19, 23, 25, 30, 34, 36))) {
70dfd48f
AG
598 // Assign good letter boundaries.
599 $this->assign_good_letter_boundary($context->id);
600 }
ece791db 601 if (in_array($i, array(3, 6, 11, 15, 20, 24, 26, 31, 35))) {
70dfd48f
AG
602 // Assign bad letter boundaries.
603 $this->assign_bad_letter_boundary($context->id);
604 }
605
ece791db 606 if (in_array($i, array(9, 10, 11, 18, 19, 20, 29, 30, 31))) {
70dfd48f 607 grade_set_setting($course->id, 'displaytype', '3');
ece791db 608 } else if (in_array($i, array(8, 17, 28))) {
70dfd48f
AG
609 grade_set_setting($course->id, 'displaytype', '2');
610 }
611
612 if ($i >= 7) {
613 $assignrow = $this->getDataGenerator()->create_module('assign', array('course' => $course->id, 'name' => 'Test!'));
614 $gi = grade_item::fetch(
615 array('itemtype' => 'mod',
616 'itemmodule' => 'assign',
617 'iteminstance' => $assignrow->id,
618 'courseid' => $course->id));
ece791db 619 if (in_array($i, array(13, 14, 15, 23, 24, 34, 35, 36))) {
70dfd48f
AG
620 grade_item::set_properties($gi, array('display', 3));
621 $gi->update();
ece791db 622 } else if (in_array($i, array(12, 21, 32))) {
70dfd48f
AG
623 grade_item::set_properties($gi, array('display', 2));
624 $gi->update();
625 }
626 $gradegrade = new grade_grade();
627 $gradegrade->itemid = $gi->id;
628 $gradegrade->userid = $user->id;
629 $gradegrade->rawgrade = 55.5563;
630 $gradegrade->finalgrade = 55.5563;
631 $gradegrade->rawgrademax = 100;
632 $gradegrade->rawgrademin = 0;
633 $gradegrade->timecreated = time();
634 $gradegrade->timemodified = time();
635 $gradegrade->insert();
636 }
637
638 $contexts[] = $context;
639 $courses[] = $course;
640 }
641
642 upgrade_course_letter_boundary();
643
644 // No system setting for grade letter boundaries.
645 // [0] A course with no letter boundaries.
646 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[0]->id}));
647 // [1] A course with letter boundaries which are default.
648 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[1]->id}));
649 // [2] A course with letter boundaries which are custom but not affected.
650 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[2]->id}));
651 // [3] A course with letter boundaries which are custom and will be affected.
652 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[3]->id});
653 // [4] A course with no letter boundaries, but with a grade item with letter boundaries which are default.
654 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[4]->id}));
655 // [5] A course with no letter boundaries, but with a grade item with letter boundaries which are not default, but not affected.
656 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[5]->id}));
657 // [6] A course with no letter boundaries, but with a grade item with letter boundaries which are not default which will be affected.
658 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[6]->id});
659
660 // System setting for grade letter boundaries (default).
661 set_config('grade_displaytype', '3');
ece791db
DM
662 for ($i = 0; $i < 37; $i++) {
663 unset_config('gradebook_calculations_freeze_' . $courses[$i]->id);
664 }
70dfd48f 665 upgrade_course_letter_boundary();
ece791db 666
70dfd48f
AG
667 // [7] A course with no grade display settings for the course or grade items.
668 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[7]->id}));
669 // [8] A course with grade display settings, but for something that isn't letters.
670 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[8]->id}));
671 // [9] A course with grade display settings of letters which are default.
672 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[9]->id}));
673 // [10] A course with grade display settings of letters which are not default, but not affected.
674 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[10]->id}));
675 // [11] A course with grade display settings of letters which are not default, which will be affected.
676 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[11]->id});
677 // [12] A grade item with display settings that are not letters.
678 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[12]->id}));
679 // [13] A grade item with display settings of letters which are default.
680 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[13]->id}));
681 // [14] A grade item with display settings of letters which are not default, but not affected.
682 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[14]->id}));
683 // [15] A grade item with display settings of letters which are not default, which will be affected.
684 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[15]->id});
685
686 // System setting for grade letter boundaries (custom with problem).
687 $systemcontext = context_system::instance();
688 $this->assign_bad_letter_boundary($systemcontext->id);
ece791db
DM
689 for ($i = 0; $i < 37; $i++) {
690 unset_config('gradebook_calculations_freeze_' . $courses[$i]->id);
691 }
70dfd48f
AG
692 upgrade_course_letter_boundary();
693
694 // [16] A course with no grade display settings for the course or grade items.
695 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[16]->id});
696 // [17] A course with grade display settings, but for something that isn't letters.
697 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[17]->id}));
698 // [18] A course with grade display settings of letters which are default.
699 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[18]->id});
700 // [19] A course with grade display settings of letters which are not default, but not affected.
701 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[19]->id}));
702 // [20] A course with grade display settings of letters which are not default, which will be affected.
703 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[20]->id});
704 // [21] A grade item with display settings which are not letters. Grade total will be affected so should be frozen.
705 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[21]->id});
706 // [22] A grade item with display settings of letters which are default.
707 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[22]->id});
708 // [23] A grade item with display settings of letters which are not default, but not affected. Course uses new letter boundary setting.
709 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[23]->id}));
710 // [24] A grade item with display settings of letters which are not default, which will be affected.
711 $this->assertEquals(20160511, $CFG->{'gradebook_calculations_freeze_' . $courses[24]->id});
712 // [25] A course which is using the default grade display setting, but has updated the grade letter boundary (not 57) Should not be frozen.
713 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[25]->id}));
714 // [26] A course that is using the default display setting (letters) and altered the letter boundary with 57. Should be frozen.
715 $this->assertEquals(20160516, $CFG->{'gradebook_calculations_freeze_' . $courses[26]->id});
ece791db
DM
716
717 // System setting not showing letters.
718 set_config('grade_displaytype', '2');
719 for ($i = 0; $i < 37; $i++) {
720 unset_config('gradebook_calculations_freeze_' . $courses[$i]->id);
721 }
722 upgrade_course_letter_boundary();
723
724 // [27] A course with no grade display settings for the course or grade items.
725 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[27]->id}));
726 // [28] A course with grade display settings, but for something that isn't letters.
727 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[28]->id}));
728 // [29] A course with grade display settings of letters which are default.
729 $this->assertEquals(20160516, $CFG->{'gradebook_calculations_freeze_' . $courses[29]->id});
730 // [30] A course with grade display settings of letters which are not default, but not affected.
731 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[30]->id}));
732 // [31] A course with grade display settings of letters which are not default, which will be affected.
733 $this->assertEquals(20160516, $CFG->{'gradebook_calculations_freeze_' . $courses[31]->id});
734 // [32] A grade item with display settings which are not letters.
735 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[32]->id}));
736 // [33] All system defaults.
737 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[33]->id}));
738 // [34] A grade item with display settings of letters which are not default, but not affected. Course uses new letter boundary setting.
739 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[34]->id}));
740 // [35] A grade item with display settings of letters which are not default, which will be affected.
741 $this->assertEquals(20160516, $CFG->{'gradebook_calculations_freeze_' . $courses[35]->id});
742 // [36] A course with grade display settings of letters with modified and good boundary (not 57) Should not be frozen.
743 $this->assertTrue(empty($CFG->{'gradebook_calculations_freeze_' . $courses[36]->id}));
70dfd48f
AG
744 }
745
746 /**
747 * Test upgrade_letter_boundary_needs_freeze function.
748 */
749 public function test_upgrade_letter_boundary_needs_freeze() {
ece791db
DM
750 global $CFG;
751
70dfd48f
AG
752 $this->resetAfterTest();
753
ece791db
DM
754 require_once($CFG->libdir . '/db/upgradelib.php');
755
70dfd48f
AG
756 $courses = array();
757 $contexts = array();
758 for ($i = 0; $i < 3; $i++) {
759 $courses[] = $this->getDataGenerator()->create_course();
760 $contexts[] = context_course::instance($courses[$i]->id);
761 }
762
763 // Course one is not using a letter boundary.
764 $this->assertFalse(upgrade_letter_boundary_needs_freeze($contexts[0]));
765
766 // Let's make course 2 use the bad boundary.
767 $this->assign_bad_letter_boundary($contexts[1]->id);
768 $this->assertTrue(upgrade_letter_boundary_needs_freeze($contexts[1]));
769 // Course 3 has letter boundaries that are fine.
770 $this->assign_good_letter_boundary($contexts[2]->id);
771 $this->assertFalse(upgrade_letter_boundary_needs_freeze($contexts[2]));
772 // Try the system context not using a letter boundary.
773 $systemcontext = context_system::instance();
774 $this->assertFalse(upgrade_letter_boundary_needs_freeze($systemcontext));
775 }
776
777 /**
778 * Assigns letter boundaries with comparison problems.
779 *
780 * @param int $contextid Context ID.
781 */
782 private function assign_bad_letter_boundary($contextid) {
783 global $DB;
784 $newlettersscale = array(
785 array('contextid' => $contextid, 'lowerboundary' => 90.00000, 'letter' => 'A'),
786 array('contextid' => $contextid, 'lowerboundary' => 85.00000, 'letter' => 'A-'),
787 array('contextid' => $contextid, 'lowerboundary' => 80.00000, 'letter' => 'B+'),
788 array('contextid' => $contextid, 'lowerboundary' => 75.00000, 'letter' => 'B'),
789 array('contextid' => $contextid, 'lowerboundary' => 70.00000, 'letter' => 'B-'),
790 array('contextid' => $contextid, 'lowerboundary' => 65.00000, 'letter' => 'C+'),
791 array('contextid' => $contextid, 'lowerboundary' => 57.00000, 'letter' => 'C'),
792 array('contextid' => $contextid, 'lowerboundary' => 50.00000, 'letter' => 'C-'),
793 array('contextid' => $contextid, 'lowerboundary' => 40.00000, 'letter' => 'D+'),
794 array('contextid' => $contextid, 'lowerboundary' => 25.00000, 'letter' => 'D'),
795 array('contextid' => $contextid, 'lowerboundary' => 0.00000, 'letter' => 'F'),
796 );
ece791db
DM
797
798 $DB->delete_records('grade_letters', array('contextid' => $contextid));
70dfd48f
AG
799 foreach ($newlettersscale as $record) {
800 // There is no API to do this, so we have to manually insert into the database.
801 $DB->insert_record('grade_letters', $record);
802 }
803 }
804
805 /**
806 * Assigns letter boundaries with no comparison problems.
807 *
808 * @param int $contextid Context ID.
809 */
810 private function assign_good_letter_boundary($contextid) {
811 global $DB;
812 $newlettersscale = array(
813 array('contextid' => $contextid, 'lowerboundary' => 90.00000, 'letter' => 'A'),
814 array('contextid' => $contextid, 'lowerboundary' => 85.00000, 'letter' => 'A-'),
815 array('contextid' => $contextid, 'lowerboundary' => 80.00000, 'letter' => 'B+'),
816 array('contextid' => $contextid, 'lowerboundary' => 75.00000, 'letter' => 'B'),
817 array('contextid' => $contextid, 'lowerboundary' => 70.00000, 'letter' => 'B-'),
818 array('contextid' => $contextid, 'lowerboundary' => 65.00000, 'letter' => 'C+'),
819 array('contextid' => $contextid, 'lowerboundary' => 54.00000, 'letter' => 'C'),
820 array('contextid' => $contextid, 'lowerboundary' => 50.00000, 'letter' => 'C-'),
821 array('contextid' => $contextid, 'lowerboundary' => 40.00000, 'letter' => 'D+'),
822 array('contextid' => $contextid, 'lowerboundary' => 25.00000, 'letter' => 'D'),
823 array('contextid' => $contextid, 'lowerboundary' => 0.00000, 'letter' => 'F'),
824 );
ece791db
DM
825
826 $DB->delete_records('grade_letters', array('contextid' => $contextid));
70dfd48f
AG
827 foreach ($newlettersscale as $record) {
828 // There is no API to do this, so we have to manually insert into the database.
829 $DB->insert_record('grade_letters', $record);
830 }
831 }
6b7df0b5 832}