Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   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  namespace core_grades;
  18  
  19  use grade_plugin_return;
  20  use grade_report_grader;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once($CFG->dirroot.'/grade/lib.php');
  26  require_once($CFG->dirroot.'/grade/report/grader/lib.php');
  27  
  28  /**
  29   * Tests grade_report_grader (the grader report)
  30   *
  31   * @package  core_grades
  32   * @category test
  33   * @copyright 2012 Andrew Davis
  34   * @license  http://www.gnu.org/copyleft/gpl.html GNU Public License
  35   */
  36  class report_graderlib_test extends \advanced_testcase {
  37  
  38      /**
  39       * Tests grade_report_grader::process_data()
  40       *
  41       * process_data() processes submitted grade and feedback data
  42       */
  43      public function test_process_data() {
  44          global $DB, $CFG;
  45  
  46          $this->resetAfterTest(true);
  47  
  48          $course = $this->getDataGenerator()->create_course();
  49  
  50          // Create and enrol a student.
  51          $student = $this->getDataGenerator()->create_user(array('username' => 'student_sam'));
  52          $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
  53          $this->getDataGenerator()->enrol_user($student->id, $course->id, $role->id);
  54  
  55          // Test with limited grades.
  56          $CFG->unlimitedgrades = 0;
  57  
  58          $forummax = 80;
  59          $forum1 = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => $forummax, 'course' => $course->id));
  60          // Switch the stdClass instance for a grade item instance.
  61          $forum1 = \grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum1->id, 'courseid' => $course->id));
  62  
  63          $report = $this->create_report($course);
  64          $testgrade = 60.00;
  65  
  66          $data = new \stdClass();
  67          $data->id = $course->id;
  68          $data->report = 'grader';
  69          $data->timepageload = time();
  70  
  71          $data->grade = array();
  72          $data->grade[$student->id] = array();
  73          $data->grade[$student->id][$forum1->id] = $testgrade;
  74  
  75          $warnings = $report->process_data($data);
  76          $this->assertEquals(count($warnings), 0);
  77  
  78          $studentgrade = \grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id));
  79          $this->assertEquals($studentgrade->finalgrade, $testgrade);
  80  
  81          // Grade above max. Should be pulled down to max.
  82          $toobig = 200.00;
  83          $data->grade[$student->id][$forum1->id] = $toobig;
  84          $data->timepageload = time();
  85          $warnings = $report->process_data($data);
  86          $this->assertEquals(count($warnings), 1);
  87  
  88          $studentgrade = \grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id));
  89          $this->assertEquals($studentgrade->finalgrade, $forummax);
  90  
  91          // Grade below min. Should be pulled up to min.
  92          $toosmall = -10.00;
  93          $data->grade[$student->id][$forum1->id] = $toosmall;
  94          $data->timepageload = time();
  95          $warnings = $report->process_data($data);
  96          $this->assertEquals(count($warnings), 1);
  97  
  98          $studentgrade = \grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id));
  99          $this->assertEquals($studentgrade->finalgrade, 0);
 100  
 101          // Test unlimited grades so we can give a student a grade about max.
 102          $CFG->unlimitedgrades = 1;
 103  
 104          $data->grade[$student->id][$forum1->id] = $toobig;
 105          $data->timepageload = time();
 106          $warnings = $report->process_data($data);
 107          $this->assertEquals(count($warnings), 0);
 108  
 109          $studentgrade = \grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id));
 110          $this->assertEquals($studentgrade->finalgrade, $toobig);
 111      }
 112  
 113      public function test_collapsed_preferences() {
 114          $this->resetAfterTest(true);
 115  
 116          $emptypreferences = array('aggregatesonly' => array(), 'gradesonly' => array());
 117  
 118          $user1 = $this->getDataGenerator()->create_user();
 119          $course1 = $this->getDataGenerator()->create_course();
 120          $course2 = $this->getDataGenerator()->create_course();
 121          $course3 = $this->getDataGenerator()->create_course();
 122  
 123          $this->setUser($user1);
 124  
 125          $report = $this->create_report($course1);
 126          $this->assertEquals($emptypreferences, $report->collapsed);
 127  
 128          // Validating preferences set/get for one course.
 129          $report->process_action('cg13', 'switch_minus');
 130          $report = $this->create_report($course1);
 131          $this->assertEquals(array(13), $report->collapsed['aggregatesonly']);
 132          $this->assertEmpty($report->collapsed['gradesonly']);
 133  
 134          $report->process_action('cg13', 'switch_plus');
 135          $report = $this->create_report($course1);
 136          $this->assertEmpty($report->collapsed['aggregatesonly']);
 137          $this->assertEquals(array(13), $report->collapsed['gradesonly']);
 138  
 139          $report->process_action('cg13', 'switch_whole');
 140          $report = $this->create_report($course1);
 141          $this->assertEquals($emptypreferences, $report->collapsed);
 142  
 143          // Validating preferences set/get for several courses.
 144  
 145          $course1cats = $course2cats = $course3cats = array();
 146          for ($i=0;$i<10;$i++) {
 147              $course1cats[] = $this->create_grade_category($course1)->id;
 148              $course2cats[] = $this->create_grade_category($course2)->id;
 149              $course3cats[] = $this->create_grade_category($course3)->id;
 150          }
 151  
 152          $report1 = $this->create_report($course1);
 153          foreach ($course1cats as $catid) {
 154              $report1->process_action('cg'.$catid, 'switch_minus');
 155          }
 156          $report2 = $this->create_report($course2);
 157          foreach ($course2cats as $catid) {
 158              $report2->process_action('cg'.$catid, 'switch_minus');
 159              $report2->process_action('cg'.$catid, 'switch_plus');
 160          }
 161          $report3 = $this->create_report($course3);
 162          foreach ($course3cats as $catid) {
 163              $report3->process_action('cg'.$catid, 'switch_minus');
 164              if (($i++)%2) {
 165                  $report3->process_action('cg'.$catid, 'switch_plus');
 166              }
 167          }
 168  
 169          $report1 = $this->create_report($course1);
 170          $this->assertEquals(10, count($report1->collapsed['aggregatesonly']));
 171          $this->assertEquals(0, count($report1->collapsed['gradesonly']));
 172          $report2 = $this->create_report($course2);
 173          $this->assertEquals(0, count($report2->collapsed['aggregatesonly']));
 174          $this->assertEquals(10, count($report2->collapsed['gradesonly']));
 175          $report3 = $this->create_report($course3);
 176          $this->assertEquals(5, count($report3->collapsed['aggregatesonly']));
 177          $this->assertEquals(5, count($report3->collapsed['gradesonly']));
 178  
 179          // Test upgrade script.
 180          // Combine data generated for user1 and set it in the old format for user2, Try to retrieve it and make sure it is converted.
 181  
 182          $user2 = $this->getDataGenerator()->create_user();
 183          $alldata = array(
 184              'aggregatesonly' => array_merge($report1->collapsed['aggregatesonly'], $report2->collapsed['aggregatesonly'], $report3->collapsed['aggregatesonly']),
 185              'gradesonly' => array_merge($report1->collapsed['gradesonly'], $report2->collapsed['gradesonly'], $report3->collapsed['gradesonly']),
 186          );
 187          set_user_preference('grade_report_grader_collapsed_categories', serialize($alldata), $user2);
 188  
 189          $this->setUser($user2);
 190          $convertedreport1 = $this->create_report($course1);
 191          $this->assertEquals($report1->collapsed, $convertedreport1->collapsed);
 192          $convertedreport2 = $this->create_report($course2);
 193          $this->assertEquals($report2->collapsed, $convertedreport2->collapsed);
 194          $convertedreport3 = $this->create_report($course3);
 195          $this->assertEquals($report3->collapsed, $convertedreport3->collapsed);
 196          // Make sure the old style user preference is removed now.
 197          $this->assertEmpty(get_user_preferences('grade_report_grader_collapsed_categories'));
 198  
 199          // Test overflowing the setting with non-existing categories (only validated if new setting size exceeds 1333 chars).
 200  
 201          $toobigvalue = $expectedvalue = $report1->collapsed;
 202          for ($i = 0; strlen(json_encode($toobigvalue)) < 1333; $i++) {
 203              $toobigvalue[($i < 7) ? 'gradesonly' : 'aggregatesonly'][] = $course1cats[9] + 1 + $i;
 204          }
 205          $lastvalue = array_pop($toobigvalue['gradesonly']);
 206          set_user_preference('grade_report_grader_collapsed_categories'.$course1->id, json_encode($toobigvalue));
 207  
 208          $report1 = $this->create_report($course1);
 209          $report1->process_action('cg'.$lastvalue, 'switch_minus');
 210  
 211          $report1 = $this->create_report($course1);
 212          $this->assertEquals($expectedvalue, $report1->collapsed);
 213  
 214          // Test overflowing the setting with existing categories.
 215  
 216          $toobigvalue = $report1->collapsed;
 217          for ($i = 0; strlen(json_encode($toobigvalue)) < 1333; $i++) {
 218              $catid = $this->create_grade_category($course1)->id;
 219              $toobigvalue[($i < 7) ? 'gradesonly' : 'aggregatesonly'][] = $catid;
 220          }
 221          $lastcatid = array_pop($toobigvalue['gradesonly']);
 222          set_user_preference('grade_report_grader_collapsed_categories'.$course1->id, json_encode($toobigvalue));
 223          $toobigvalue['aggregatesonly'][] = $lastcatid;
 224  
 225          $report1 = $this->create_report($course1);
 226          $report1->process_action('cg'.$lastcatid, 'switch_minus');
 227  
 228          // One last value should be removed from both arrays.
 229          $report1 = $this->create_report($course1);
 230          $this->assertEquals(count($toobigvalue['aggregatesonly']) - 1, count($report1->collapsed['aggregatesonly']));
 231          $this->assertEquals(count($toobigvalue['gradesonly']) - 1, count($report1->collapsed['gradesonly']));
 232      }
 233  
 234      /**
 235       * Test some special cases of the conversion from old preferences to new ones
 236       *
 237       * @covers \grade_report_grader::get_collapsed_preferences
 238       * @covers \grade_report_grader::filter_collapsed_categories
 239       */
 240      public function test_old_collapsed_preferences() {
 241          $this->resetAfterTest(true);
 242  
 243          $user1 = $this->getDataGenerator()->create_user();
 244          $course1 = $this->getDataGenerator()->create_course();
 245          $course2 = $this->getDataGenerator()->create_course();
 246          $course3 = $this->getDataGenerator()->create_course();
 247  
 248          $course1cats = $course2cats = $course3cats = [];
 249          for ($i = 0; $i < 10; $i++) {
 250              $course1cats[] = $this->create_grade_category($course1)->id;
 251              $course2cats[] = $this->create_grade_category($course2)->id;
 252              $course3cats[] = $this->create_grade_category($course3)->id;
 253          }
 254  
 255          $report1 = $this->create_report($course1);
 256          // Collapse all the cats in course1.
 257          foreach ($course1cats as $catid) {
 258              $report1->process_action('cg'. $catid, 'switch_minus');
 259          }
 260  
 261          // Expand all the cats in course2.
 262          $report2 = $this->create_report($course2);
 263          foreach ($course2cats as $catid) {
 264              $report2->process_action('cg'.$catid, 'switch_minus');
 265              $report2->process_action('cg'.$catid, 'switch_plus');
 266          }
 267  
 268          // Collapse odd cats and expand even cats in course3.
 269          $report3 = $this->create_report($course3);
 270          foreach ($course3cats as $catid) {
 271              $report3->process_action('cg'.$catid, 'switch_minus');
 272              if (($i++) % 2) {
 273                  $report3->process_action('cg'.$catid, 'switch_plus');
 274              }
 275          }
 276  
 277          $report1 = $this->create_report($course1);
 278          $this->assertEquals(10, count($report1->collapsed['aggregatesonly']));
 279          $this->assertEquals(0, count($report1->collapsed['gradesonly']));
 280          $report2 = $this->create_report($course2);
 281          $this->assertEquals(0, count($report2->collapsed['aggregatesonly']));
 282          $this->assertEquals(10, count($report2->collapsed['gradesonly']));
 283          $report3 = $this->create_report($course3);
 284          $this->assertEquals(5, count($report3->collapsed['aggregatesonly']));
 285          $this->assertEquals(5, count($report3->collapsed['gradesonly']));
 286  
 287          // Use the preferences generated for user1 and set it in the old format for other users.
 288  
 289          // User2: both gradesonly and aggregatesonly.
 290          $user2 = $this->getDataGenerator()->create_user();
 291          $alldata = [
 292              'gradesonly' => array_merge(
 293                  $report1->collapsed['gradesonly'],
 294                  $report2->collapsed['gradesonly'],
 295                  $report3->collapsed['gradesonly']),
 296              'aggregatesonly' => array_merge(
 297                  $report1->collapsed['aggregatesonly'],
 298                  $report2->collapsed['aggregatesonly'],
 299                  $report3->collapsed['aggregatesonly']),
 300          ];
 301          set_user_preference('grade_report_grader_collapsed_categories', serialize($alldata), $user2);
 302  
 303          $this->setUser($user2);
 304          $convertedreport1 = $this->create_report($course1);
 305          $this->assertEquals($report1->collapsed['gradesonly'], $convertedreport1->collapsed['gradesonly']);
 306          $this->assertEquals($report1->collapsed['aggregatesonly'], $convertedreport1->collapsed['aggregatesonly']);
 307          $newprefs1 = get_user_preferences('grade_report_grader_collapsed_categories' . $course1->id); // Also verify new prefs.
 308          $this->assertEquals($report1->collapsed['gradesonly'], json_decode($newprefs1, true)['gradesonly']);
 309          $this->assertEquals($report1->collapsed['aggregatesonly'], json_decode($newprefs1, true)['aggregatesonly']);
 310  
 311          $convertedreport2 = $this->create_report($course2);
 312          $this->assertEquals($report2->collapsed['gradesonly'], $convertedreport2->collapsed['gradesonly']);
 313          $this->assertEquals($report2->collapsed['aggregatesonly'], $convertedreport2->collapsed['aggregatesonly']);
 314          $newprefs2 = get_user_preferences('grade_report_grader_collapsed_categories' . $course2->id); // Also verify new prefs.
 315          $this->assertEquals($report2->collapsed['gradesonly'], json_decode($newprefs2, true)['gradesonly']);
 316          $this->assertEquals($report2->collapsed['aggregatesonly'], json_decode($newprefs2, true)['aggregatesonly']);
 317  
 318          $convertedreport3 = $this->create_report($course3);
 319          $this->assertEquals($report3->collapsed['gradesonly'], $convertedreport3->collapsed['gradesonly']);
 320          $this->assertEquals($report3->collapsed['aggregatesonly'], $convertedreport3->collapsed['aggregatesonly']);
 321          $newprefs3 = get_user_preferences('grade_report_grader_collapsed_categories' . $course3->id); // Also verify new prefs.
 322          $this->assertEquals($report3->collapsed['gradesonly'], json_decode($newprefs3, true)['gradesonly']);
 323          $this->assertEquals($report3->collapsed['aggregatesonly'], json_decode($newprefs3, true)['aggregatesonly']);
 324  
 325          // Make sure the old style user preference is removed now.
 326          $this->assertEmpty(get_user_preferences('grade_report_grader_collapsed_categories'));
 327  
 328          // User3: only gradesonly (missing aggregatesonly).
 329          $user3 = $this->getDataGenerator()->create_user();
 330          $alldata = [
 331              'gradesonly' => array_merge(
 332                  $report1->collapsed['gradesonly'],
 333                  $report2->collapsed['gradesonly'],
 334                  $report3->collapsed['gradesonly']),
 335          ];
 336          set_user_preference('grade_report_grader_collapsed_categories', serialize($alldata), $user3);
 337  
 338          $this->setUser($user3);
 339          $convertedreport1 = $this->create_report($course1);
 340          $this->assertEquals($report1->collapsed['gradesonly'], $convertedreport1->collapsed['gradesonly']);
 341          $this->assertEquals([], $convertedreport1->collapsed['aggregatesonly']);
 342          $newprefs1 = get_user_preferences('grade_report_grader_collapsed_categories' . $course1->id); // Also verify new prefs.
 343          $this->assertNull($newprefs1);
 344  
 345          $convertedreport2 = $this->create_report($course2);
 346          $this->assertEquals($report2->collapsed['gradesonly'], $convertedreport2->collapsed['gradesonly']);
 347          $this->assertEquals([], $convertedreport2->collapsed['aggregatesonly']);
 348          $newprefs2 = get_user_preferences('grade_report_grader_collapsed_categories' . $course2->id); // Also verify new prefs.
 349          $this->assertEquals($report2->collapsed['gradesonly'], json_decode($newprefs2, true)['gradesonly']);
 350          $this->assertEquals([], json_decode($newprefs2, true)['aggregatesonly']);
 351  
 352          $convertedreport3 = $this->create_report($course3);
 353          $this->assertEquals($report3->collapsed['gradesonly'], $convertedreport3->collapsed['gradesonly']);
 354          $this->assertEquals([], $convertedreport3->collapsed['aggregatesonly']);
 355          $newprefs3 = get_user_preferences('grade_report_grader_collapsed_categories' . $course3->id); // Also verify new prefs.
 356          $this->assertEquals($report3->collapsed['gradesonly'], json_decode($newprefs3, true)['gradesonly']);
 357          $this->assertEquals([], json_decode($newprefs3, true)['aggregatesonly']);
 358  
 359          // Make sure the old style user preference is removed now.
 360          $this->assertEmpty(get_user_preferences('grade_report_grader_collapsed_categories'));
 361  
 362          // User4: only aggregatesonly (missing gradesonly).
 363          $user4 = $this->getDataGenerator()->create_user();
 364          $alldata = [
 365              'aggregatesonly' => array_merge(
 366                  $report1->collapsed['aggregatesonly'],
 367                  $report2->collapsed['aggregatesonly'],
 368                  $report3->collapsed['aggregatesonly']),
 369          ];
 370          set_user_preference('grade_report_grader_collapsed_categories', serialize($alldata), $user4);
 371  
 372          $this->setUser($user4);
 373          $convertedreport1 = $this->create_report($course1);
 374          $this->assertEquals([], $convertedreport1->collapsed['gradesonly']);
 375          $this->assertEquals($report1->collapsed['aggregatesonly'], $convertedreport1->collapsed['aggregatesonly']);
 376          $newprefs1 = get_user_preferences('grade_report_grader_collapsed_categories' . $course1->id); // Also verify new prefs.
 377          $this->assertEquals([], json_decode($newprefs1, true)['gradesonly']);
 378          $this->assertEquals($report1->collapsed['aggregatesonly'], json_decode($newprefs1, true)['aggregatesonly']);
 379  
 380          $convertedreport2 = $this->create_report($course2);
 381          $this->assertEquals([], $convertedreport2->collapsed['gradesonly']);
 382          $this->assertEquals($report2->collapsed['aggregatesonly'], $convertedreport2->collapsed['aggregatesonly']);
 383          $newprefs2 = get_user_preferences('grade_report_grader_collapsed_categories' . $course2->id); // Also verify new prefs.
 384          $this->assertNull($newprefs2);
 385  
 386          $convertedreport3 = $this->create_report($course3);
 387          $this->assertEquals([], $convertedreport3->collapsed['gradesonly']);
 388          $this->assertEquals($report3->collapsed['aggregatesonly'], $convertedreport3->collapsed['aggregatesonly']);
 389          $newprefs3 = get_user_preferences('grade_report_grader_collapsed_categories' . $course3->id); // Also verify new prefs.
 390          $this->assertEquals([], json_decode($newprefs3, true)['gradesonly']);
 391          $this->assertEquals($report3->collapsed['aggregatesonly'], json_decode($newprefs3, true)['aggregatesonly']);
 392  
 393          // Make sure the old style user preference is removed now.
 394          $this->assertEmpty(get_user_preferences('grade_report_grader_collapsed_categories'));
 395  
 396          // User5: both missing gradesonly and aggregatesonly.
 397          $user5 = $this->getDataGenerator()->create_user();
 398          $alldata = [];
 399          set_user_preference('grade_report_grader_collapsed_categories', serialize($alldata), $user5);
 400  
 401          $this->setUser($user5);
 402          $convertedreport1 = $this->create_report($course1);
 403          $this->assertEquals([], $convertedreport1->collapsed['gradesonly']);
 404          $this->assertEquals([], $convertedreport1->collapsed['aggregatesonly']);
 405          $newprefs1 = get_user_preferences('grade_report_grader_collapsed_categories' . $course1->id); // Also verify new prefs.
 406          $this->assertNull($newprefs1);
 407  
 408          $convertedreport2 = $this->create_report($course2);
 409          $this->assertEquals([], $convertedreport2->collapsed['gradesonly']);
 410          $this->assertEquals([], $convertedreport2->collapsed['aggregatesonly']);
 411          $newprefs2 = get_user_preferences('grade_report_grader_collapsed_categories' . $course2->id); // Also verify new prefs.
 412          $this->assertNull($newprefs2);
 413  
 414          $convertedreport3 = $this->create_report($course3);
 415          $this->assertEquals([], $convertedreport3->collapsed['gradesonly']);
 416          $this->assertEquals([], $convertedreport3->collapsed['aggregatesonly']);
 417          $newprefs3 = get_user_preferences('grade_report_grader_collapsed_categories' . $course3->id); // Also verify new prefs.
 418          $this->assertNull($newprefs3);
 419  
 420          // Make sure the old style user preference is removed now.
 421          $this->assertEmpty(get_user_preferences('grade_report_grader_collapsed_categories'));
 422  
 423          // User6: both empty gradesonly and aggregatesonly.
 424          $user6 = $this->getDataGenerator()->create_user();
 425          $alldata = [
 426              'gradesonly' => [],
 427              'aggregatesonly' => []
 428          ];
 429          set_user_preference('grade_report_grader_collapsed_categories', serialize($alldata), $user6);
 430  
 431          $this->setUser($user6);
 432          $convertedreport1 = $this->create_report($course1);
 433          $this->assertEquals([], $convertedreport1->collapsed['gradesonly']);
 434          $this->assertEquals([], $convertedreport1->collapsed['aggregatesonly']);
 435          $newprefs1 = get_user_preferences('grade_report_grader_collapsed_categories' . $course1->id); // Also verify new prefs.
 436          $this->assertNull($newprefs1);
 437  
 438          $convertedreport2 = $this->create_report($course2);
 439          $this->assertEquals([], $convertedreport2->collapsed['gradesonly']);
 440          $this->assertEquals([], $convertedreport2->collapsed['aggregatesonly']);
 441          $newprefs2 = get_user_preferences('grade_report_grader_collapsed_categories' . $course2->id); // Also verify new prefs.
 442          $this->assertNull($newprefs2);
 443  
 444          $convertedreport3 = $this->create_report($course3);
 445          $this->assertEquals([], $convertedreport3->collapsed['gradesonly']);
 446          $this->assertEquals([], $convertedreport3->collapsed['aggregatesonly']);
 447          $newprefs3 = get_user_preferences('grade_report_grader_collapsed_categories' . $course3->id); // Also verify new prefs.
 448          $this->assertNull($newprefs3);
 449  
 450          // Make sure the old style user preference is removed now.
 451          $this->assertEmpty(get_user_preferences('grade_report_grader_collapsed_categories'));
 452      }
 453  
 454      /**
 455       * Tests the get_right_rows function with one 'normal' and one 'ungraded' quiz.
 456       *
 457       * Previously, with an ungraded quiz (which results in a grade item with type GRADETYPE_NONE)
 458       * there was a bug in get_right_rows in some situations.
 459       */
 460      public function test_get_right_rows() {
 461          global $USER, $DB;
 462          $this->resetAfterTest(true);
 463  
 464          // Create manager and student on a course.
 465          $generator = $this->getDataGenerator();
 466          $manager = $generator->create_user();
 467          $student = $generator->create_user();
 468          $course = $generator->create_course();
 469          $generator->enrol_user($manager->id, $course->id, 'manager');
 470          $generator->enrol_user($student->id, $course->id, 'student');
 471  
 472          // Create a couple of quizzes on the course.
 473          $normalquiz = $generator->create_module('quiz', array('course' => $course->id,
 474                  'name' => 'NormalQuiz'));
 475          $ungradedquiz = $generator->create_module('quiz', array('course' => $course->id,
 476                  'name' => 'UngradedQuiz'));
 477  
 478          // Set the grade for the second one to 0 (note, you have to do this after creating it,
 479          // otherwise it doesn't create an ungraded grade item).
 480          $ungradedquiz->instance = $ungradedquiz->id;
 481          quiz_set_grade(0, $ungradedquiz);
 482  
 483          // Set current user.
 484          $this->setUser($manager);
 485          $USER->gradeediting[$course->id] = false;
 486  
 487          // Get the report.
 488          $report = $this->create_report($course);
 489          $report->load_users();
 490          $report->load_final_grades();
 491          $result = $report->get_right_rows(false);
 492  
 493          // There should be 3 rows (2 header rows, plus the grades for the first user).
 494          $this->assertCount(3, $result);
 495  
 496          // The second row should contain 2 cells - one for the graded quiz and course total.
 497          $this->assertCount(2, $result[1]->cells);
 498          $this->assertStringContainsString('NormalQuiz', $result[1]->cells[0]->text);
 499          $this->assertStringContainsString('Course total', $result[1]->cells[1]->text);
 500  
 501          // User row should contain grade values '-'.
 502          $this->assertCount(2, $result[2]->cells);
 503          $this->assertStringContainsString('>-<', $result[2]->cells[0]->text);
 504          $this->assertStringContainsString('>-<', $result[2]->cells[1]->text);
 505  
 506          // Supposing the user cannot view hidden grades, this shouldn't make any difference (due
 507          // to a bug, it previously did).
 508          $context = \context_course::instance($course->id);
 509          $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
 510          assign_capability('moodle/grade:viewhidden', CAP_PROHIBIT, $managerroleid, $context->id, true);
 511          $this->assertFalse(has_capability('moodle/grade:viewhidden', $context));
 512  
 513          // Recreate the report. Confirm it returns successfully still.
 514          $report = $this->create_report($course);
 515          $report->load_users();
 516          $report->load_final_grades();
 517          $result = $report->get_right_rows(false);
 518          $this->assertCount(3, $result);
 519      }
 520  
 521      private function create_grade_category($course) {
 522          static $cnt = 0;
 523          $cnt++;
 524          $gradecat = new \grade_category(array('courseid' => $course->id, 'fullname' => 'Cat '.$cnt), false);
 525          $gradecat->apply_default_settings();
 526          $gradecat->apply_forced_settings();
 527          $gradecat->insert();
 528          return $gradecat;
 529      }
 530  
 531      private function create_report($course) {
 532  
 533          $coursecontext = \context_course::instance($course->id);
 534          $gpr = new grade_plugin_return(array('type' => 'report', 'plugin'=>'grader', 'courseid' => $course->id));
 535          $report = new grade_report_grader($course->id, $gpr, $coursecontext);
 536  
 537          return $report;
 538      }
 539  }