2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for the lib/upgradelib.php library.
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
26 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->libdir.'/upgradelib.php');
32 * Tests various classes and functions in upgradelib.php library.
34 class core_upgradelib_testcase extends advanced_testcase {
37 * Test the {@link upgrade_stale_php_files_present() function
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());
46 * Populate some fake grade items into the database with specified
47 * sortorder and course id.
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
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.
58 private function insert_fake_grade_item_sortorder($courseid, $sortorder) {
60 require_once($CFG->libdir.'/gradelib.php');
62 $item = new stdClass();
63 $item->courseid = $courseid;
64 $item->sortorder = $sortorder;
65 $item->gradetype = GRADE_TYPE_VALUE;
67 $item->grademax = 110;
68 $item->itemnumber = 1;
70 $item->timecreated = time();
71 $item->timemodified = time();
73 $item->id = $DB->insert_record('grade_items', $item);
75 return $DB->get_record('grade_items', array('id' => $item->id));
78 public function test_upgrade_fix_missing_root_folders_draft() {
81 $this->resetAfterTest(true);
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);
90 file_prepare_draft_area($draftitemid, $context->id, 'mod_resource', 'content', 0);
93 'component' => 'user',
94 'contextid' => $usercontext->id,
95 'filearea' => 'draft',
96 'itemid' => $draftitemid,
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' => '.'));
105 // Delete record with filename '.' and make sure it does not exist any more.
106 $DB->delete_records('files', $queryparams + array('filename' => '.'));
108 $records = $DB->get_records_menu('files', $queryparams, '', 'id, filename');
109 $this->assertEquals(1, count($records));
110 $this->assertFalse(in_array('.', $records));
112 // Run upgrade script and make sure the record is restored.
113 upgrade_fix_missing_root_folders_draft();
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);
123 * Test upgrade minmaxgrade step.
125 public function test_upgrade_minmaxgrade() {
127 require_once($CFG->libdir . '/gradelib.php');
128 $initialminmax = $CFG->grade_minmaxtouse;
129 $this->resetAfterTest();
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));
139 $cm1 = get_coursemodule_from_instance('assign', $a1->id);
140 $ctx1 = context_module::instance($cm1->id);
141 $assign1 = new assign($ctx1, $cm1, $c1);
143 $cm2 = get_coursemodule_from_instance('assign', $a2->id);
144 $ctx2 = context_module::instance($cm2->id);
145 $assign2 = new assign($ctx2, $cm2, $c2);
147 $cm3 = get_coursemodule_from_instance('assign', $a3->id);
148 $ctx3 = context_module::instance($cm3->id);
149 $assign3 = new assign($ctx3, $cm3, $c3);
151 // Give a grade to the student.
152 $ug = $assign1->get_user_grade($u1->id, true);
154 $assign1->update_grade($ug);
156 $ug = $assign2->get_user_grade($u1->id, true);
158 $assign2->update_grade($ug);
160 $ug = $assign3->get_user_grade($u1->id, true);
162 $assign3->update_grade($ug);
166 upgrade_minmaxgrade();
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)));
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);
186 $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $a2->id,
187 'courseid' => $c2->id, 'itemnumber' => 0);
188 $gi = grade_item::fetch($giparams);
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;
197 upgrade_minmaxgrade();
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)));
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)));
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.
218 upgrade_minmaxgrade();
221 $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c2->id, 'minmaxtouse', false, true));
223 // Nothing has happened for C1.
224 $this->assertSame('-1', grade_get_setting($c1->id, 'minmaxtouse', false, true));
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)));
232 // Final check, this time we'll unset the default config.
233 unset($CFG->grade_minmaxtouse);
234 grade_set_setting($c1->id, 'minmaxtouse', null);
237 upgrade_minmaxgrade();
240 $this->assertSame((string) GRADE_MIN_MAX_FROM_GRADE_GRADE, grade_get_setting($c1->id, 'minmaxtouse', false, true));
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)));
248 $CFG->grade_minmaxtouse = $initialminmax;
251 public function test_upgrade_extra_credit_weightoverride() {
254 $this->resetAfterTest(true);
256 require_once($CFG->libdir . '/db/upgradelib.php');
261 for ($i=0; $i<5; $i++) {
262 $c[$i] = $this->getDataGenerator()->create_course();
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);
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;
279 $gi[0][2]->weightoverride = 1;
282 // Case 2: Course $c[1] has neither extra credits nor overrides
284 // Case 3: Course $c[2] has extra credits but no overrides
285 $gi[2][1]->aggregationcoef = 1;
288 // Case 4: Course $c[3] has no extra credits and has overrides
289 $gi[3][2]->weightoverride = 1;
292 // Case 5: Course $c[4] has both extra credits and overrides
293 $gi[4][1]->aggregationcoef = 1;
295 $gi[4][2]->weightoverride = 1;
298 // Run the upgrade script and make sure only course $c[4] was marked as needed to be fixed.
299 upgrade_extra_credit_weightoverride();
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});
307 set_config('gradebook_calculations_freeze_' . $c[4]->id, null);
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});
317 * Test the upgrade function for flagging courses with calculated grade item problems.
319 public function test_upgrade_calculated_grade_items_freeze() {
322 $this->resetAfterTest();
324 require_once($CFG->libdir . '/db/upgradelib.php');
327 $user = $this->getDataGenerator()->create_user();
329 // Create a couple of courses.
330 $course1 = $this->getDataGenerator()->create_course();
331 $course2 = $this->getDataGenerator()->create_course();
332 $course3 = $this->getDataGenerator()->create_course();
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);
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;
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;
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();
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();
371 // Setting a grade for the student.
372 $grade = $gradeitem->get_grade($user->id, true);
373 $grade->finalgrade = 50;
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();
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();
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();
401 // Assigning a grade for the user.
402 $grade = $gradeitem->get_grade($user->id, true);
403 $grade->finalgrade = 10;
406 // Setting all of the grade_grade items.
407 grade_regrade_final_grades($course2->id);
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();
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();
428 // Assigning a grade for the user.
429 $grade = $gradeitem->get_grade($user->id, true);
430 $grade->finalgrade = 10;
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});
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);
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});
450 function test_upgrade_calculated_grade_items_regrade() {
453 $this->resetAfterTest();
455 require_once($CFG->libdir . '/db/upgradelib.php');
458 $user = $this->getDataGenerator()->create_user();
461 $course = $this->getDataGenerator()->create_course();
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);
469 set_config('upgrade_calculatedgradeitemsonlyregrade', 1);
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;
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();
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();
494 // Setting a grade for the student.
495 $grade = $gradeitem->get_grade($user->id, true);
496 $grade->finalgrade = 50;
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;
504 $this->assertNotEquals($gradecategoryitem->grademax, $grade->rawgrademax);
505 $this->assertNotEquals($gradecategoryitem->grademin, $grade->rawgrademin);
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);
512 $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
514 $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax);
515 $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin);
518 public function test_upgrade_course_tags() {
521 $this->resetAfterTest();
523 require_once($CFG->libdir . '/db/upgradelib.php');
525 // Running upgrade script when there are no tags.
526 upgrade_course_tags();
527 $this->assertFalse($DB->record_exists('tag_instance', array()));
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));
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);
541 // Imagine we have tags 101, 102, 103, ... and courses 1, 2, 3, ... and users 1001, 1002, ... .
542 $keys = array('itemid', 'tagid', 'tiuserid');
558 foreach ($valuesets as $values) {
559 $DB->insert_record('tag_instance', array_combine($keys, $values) +
560 array('itemtype' => 'course', 'component' => 'core', 'contextid' => 1));
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))));
580 * Test that the upgrade script correctly flags courses to be frozen due to letter boundary problems.
582 public function test_upgrade_course_letter_boundary() {
584 $this->resetAfterTest(true);
586 require_once($CFG->libdir . '/db/upgradelib.php');
589 $user = $this->getDataGenerator()->create_user();
591 // Create some courses.
594 for ($i = 0; $i < 37; $i++) {
595 $course = $this->getDataGenerator()->create_course();
596 $context = context_course::instance($course->id);
597 if (in_array($i, array(2, 5, 10, 13, 14, 19, 23, 25, 30, 34, 36))) {
598 // Assign good letter boundaries.
599 $this->assign_good_letter_boundary($context->id);
601 if (in_array($i, array(3, 6, 11, 15, 20, 24, 26, 31, 35))) {
602 // Assign bad letter boundaries.
603 $this->assign_bad_letter_boundary($context->id);
606 if (in_array($i, array(9, 10, 11, 18, 19, 20, 29, 30, 31))) {
607 grade_set_setting($course->id, 'displaytype', '3');
608 } else if (in_array($i, array(8, 17, 28))) {
609 grade_set_setting($course->id, 'displaytype', '2');
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));
619 if (in_array($i, array(13, 14, 15, 23, 24, 34, 35, 36))) {
620 grade_item::set_properties($gi, array('display', 3));
622 } else if (in_array($i, array(12, 21, 32))) {
623 grade_item::set_properties($gi, array('display', 2));
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();
638 $contexts[] = $context;
639 $courses[] = $course;
642 upgrade_course_letter_boundary();
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(20160518, $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(20160518, $CFG->{'gradebook_calculations_freeze_' . $courses[6]->id});
660 // System setting for grade letter boundaries (default).
661 set_config('grade_displaytype', '3');
662 for ($i = 0; $i < 37; $i++) {
663 unset_config('gradebook_calculations_freeze_' . $courses[$i]->id);
665 upgrade_course_letter_boundary();
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(20160518, $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(20160518, $CFG->{'gradebook_calculations_freeze_' . $courses[15]->id});
686 // System setting for grade letter boundaries (custom with problem).
687 $systemcontext = context_system::instance();
688 $this->assign_bad_letter_boundary($systemcontext->id);
689 for ($i = 0; $i < 37; $i++) {
690 unset_config('gradebook_calculations_freeze_' . $courses[$i]->id);
692 upgrade_course_letter_boundary();
694 // [16] A course with no grade display settings for the course or grade items.
695 $this->assertEquals(20160518, $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(20160518, $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(20160518, $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(20160518, $CFG->{'gradebook_calculations_freeze_' . $courses[21]->id});
706 // [22] A grade item with display settings of letters which are default.
707 $this->assertEquals(20160518, $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(20160518, $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(20160518, $CFG->{'gradebook_calculations_freeze_' . $courses[26]->id});
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);
722 upgrade_course_letter_boundary();
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(20160518, $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(20160518, $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(20160518, $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}));
747 * Test upgrade_letter_boundary_needs_freeze function.
749 public function test_upgrade_letter_boundary_needs_freeze() {
752 $this->resetAfterTest();
754 require_once($CFG->libdir . '/db/upgradelib.php');
758 for ($i = 0; $i < 3; $i++) {
759 $courses[] = $this->getDataGenerator()->create_course();
760 $contexts[] = context_course::instance($courses[$i]->id);
763 // Course one is not using a letter boundary.
764 $this->assertFalse(upgrade_letter_boundary_needs_freeze($contexts[0]));
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));
778 * Assigns letter boundaries with comparison problems.
780 * @param int $contextid Context ID.
782 private function assign_bad_letter_boundary($contextid) {
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'),
798 $DB->delete_records('grade_letters', array('contextid' => $contextid));
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);
806 * Assigns letter boundaries with no comparison problems.
808 * @param int $contextid Context ID.
810 private function assign_good_letter_boundary($contextid) {
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'),
826 $DB->delete_records('grade_letters', array('contextid' => $contextid));
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);