Search moodle.org's
Developer Documentation

  • 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 37 and 311] [Versions 38 and 311] [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  /**
      18   * @package    core_grades
      19   * @category   phpunit
      20   * @copyright  nicolas@moodle.com
      21   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      22   */
      23  
      24  defined('MOODLE_INTERNAL') || die();
      25  
      26  require_once (__DIR__.'/fixtures/lib.php');
      27  
      28  class core_grade_item_testcase extends grade_base_testcase {
      29      public function test_grade_item() {
      30          $this->sub_test_grade_item_construct();
      31          $this->sub_test_grade_item_insert();
      32          $this->sub_test_grade_item_delete();
      33          $this->sub_test_grade_item_update();
      34          $this->sub_test_grade_item_load_scale();
      35          $this->sub_test_grade_item_load_outcome();
      36          $this->sub_test_grade_item_qualifies_for_regrading();
      37          $this->sub_test_grade_item_force_regrading();
      38          $this->sub_test_grade_item_fetch();
      39          $this->sub_test_grade_item_fetch_all();
      40          $this->sub_test_grade_item_get_all_finals();
      41          $this->sub_test_grade_item_get_final();
      42          $this->sub_test_grade_item_get_sortorder();
      43          $this->sub_test_grade_item_set_sortorder();
      44          $this->sub_test_grade_item_move_after_sortorder();
      45          $this->sub_test_grade_item_get_name_escaped();
      46          $this->sub_test_grade_item_get_name_unescaped();
      47          $this->sub_test_grade_item_set_parent();
      48          $this->sub_test_grade_item_get_parent_category();
      49          $this->sub_test_grade_item_load_parent_category();
      50          $this->sub_test_grade_item_get_item_category();
      51          $this->sub_test_grade_item_load_item_category();
      52          $this->sub_test_grade_item_regrade_final_grades();
      53          $this->sub_test_grade_item_adjust_raw_grade();
      54          $this->sub_test_grade_item_rescale_grades_keep_percentage();
      55          $this->sub_test_grade_item_set_locked();
      56          $this->sub_test_grade_item_is_locked();
      57          $this->sub_test_grade_item_set_hidden();
      58          $this->sub_test_grade_item_is_hidden();
      59          $this->sub_test_grade_item_is_category_item();
      60          $this->sub_test_grade_item_is_course_item();
      61          $this->sub_test_grade_item_fetch_course_item();
      62          $this->sub_test_grade_item_depends_on();
      63          $this->sub_test_refresh_grades();
      64          $this->sub_test_grade_item_is_calculated();
      65          $this->sub_test_grade_item_set_calculation();
      66          $this->sub_test_grade_item_get_calculation();
      67          $this->sub_test_grade_item_compute();
      68          $this->sub_test_update_final_grade();
      69          $this->sub_test_grade_item_can_control_visibility();
      70          $this->sub_test_grade_item_fix_sortorder();
      71          $this->sub_test_grade_item_created_event();
      72          $this->sub_test_grade_item_updated_event();
      73      }
      74  
      75      protected function sub_test_grade_item_construct() {
      76          $params = new stdClass();
      77  
      78          $params->courseid = $this->courseid;
      79          $params->categoryid = $this->grade_categories[1]->id;
      80          $params->itemname = 'unittestgradeitem4';
      81          $params->itemtype = 'mod';
      82          $params->itemmodule = 'database';
      83          $params->iteminfo = 'Grade item used for unit testing';
      84  
      85          $grade_item = new grade_item($params, false);
      86  
      87          $this->assertEquals($params->courseid, $grade_item->courseid);
      88          $this->assertEquals($params->categoryid, $grade_item->categoryid);
      89          $this->assertEquals($params->itemmodule, $grade_item->itemmodule);
      90      }
      91  
      92      protected function sub_test_grade_item_insert() {
      93          $grade_item = new grade_item();
      94          $this->assertTrue(method_exists($grade_item, 'insert'));
      95  
      96          $grade_item->courseid = $this->courseid;
      97          $grade_item->categoryid = $this->grade_categories[1]->id;
      98          $grade_item->itemname = 'unittestgradeitem4';
      99          $grade_item->itemtype = 'mod';
     100          $grade_item->itemmodule = 'quiz';
     101          $grade_item->iteminfo = 'Grade item used for unit testing';
     102  
     103          $grade_item->insert();
     104  
     105          $last_grade_item = end($this->grade_items);
     106  
     107          $this->assertEquals($grade_item->id, $last_grade_item->id + 1);
     108          $this->assertEquals(18, $grade_item->sortorder);
     109  
     110          // Keep our reference collection the same as what is in the database.
     111          $this->grade_items[] = $grade_item;
     112      }
     113  
     114      protected function sub_test_grade_item_delete() {
     115          global $DB;
     116          $grade_item = new grade_item($this->grade_items[7], false); // Use a grade item not touched by previous (or future) tests.
     117          $this->assertTrue(method_exists($grade_item, 'delete'));
     118  
     119          // Add two files.
     120          $dummy = array(
     121              'contextid' => $grade_item->get_context()->id,
     122              'component' => GRADE_FILE_COMPONENT,
     123              'filearea' => GRADE_HISTORY_FEEDBACK_FILEAREA,
     124              'itemid' => 1,
     125              'filepath' => '/',
     126              'filename' => 'feedback1.txt'
     127          );
     128  
     129          $fs = get_file_storage();
     130          $fs->create_file_from_string($dummy, '');
     131  
     132          $dummy['itemid'] = 2;
     133          $fs->create_file_from_string($dummy, '');
     134  
     135          $files = $fs->get_area_files($grade_item->get_context()->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
     136          // Includes directories.
     137          $this->assertCount(4, $files);
     138  
     139          $this->assertTrue($grade_item->delete());
     140  
     141          $this->assertFalse($DB->get_record('grade_items', array('id' => $grade_item->id)));
     142  
     143          $files = $fs->get_area_files($grade_item->get_context()->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
     144          $this->assertEmpty($files);
     145  
     146          // Keep our reference collection the same as the database.
     147          unset($this->grade_items[7]);
     148      }
     149  
     150      protected function sub_test_grade_item_update() {
     151          global $DB;
     152          $grade_item = new grade_item($this->grade_items[0], false);
     153          $this->assertTrue(method_exists($grade_item, 'update'));
     154  
     155          $grade_item->iteminfo = 'Updated info for this unittest grade_item';
     156  
     157          $this->assertTrue($grade_item->update());
     158  
     159          $grade_item->grademin = 14;
     160          $this->assertTrue($grade_item->qualifies_for_regrading());
     161          $this->assertTrue($grade_item->update());
     162  
     163          $iteminfo = $DB->get_field('grade_items', 'iteminfo', array('id' => $this->grade_items[0]->id));
     164          $this->assertEquals($grade_item->iteminfo, $iteminfo);
     165      }
     166  
     167      protected function sub_test_grade_item_load_scale() {
     168          $grade_item = new grade_item($this->grade_items[2], false);
     169          $this->assertTrue(method_exists($grade_item, 'load_scale'));
     170          $scale = $grade_item->load_scale();
     171          $this->assertFalse(empty($grade_item->scale));
     172          $this->assertEquals($scale->id, $this->grade_items[2]->scaleid);
     173      }
     174  
     175      protected function sub_test_grade_item_load_outcome() {
     176          $grade_item = new grade_item($this->grade_items[0], false);
     177          $this->assertTrue(method_exists($grade_item, 'load_outcome'));
     178          // TODO: add tests.
     179      }
     180  
     181      protected function sub_test_grade_item_qualifies_for_regrading() {
     182          $grade_item = new grade_item($this->grade_items[3], false); // Use a grade item not touched by previous tests.
     183          $this->assertTrue(method_exists($grade_item, 'qualifies_for_regrading'));
     184  
     185          $this->assertFalse($grade_item->qualifies_for_regrading());
     186  
     187          $grade_item->iteminfo = 'Updated info for this unittest grade_item';
     188  
     189          $this->assertFalse($grade_item->qualifies_for_regrading());
     190  
     191          $grade_item->grademin = 14;
     192  
     193          $this->assertTrue($grade_item->qualifies_for_regrading());
     194      }
     195  
     196      protected function sub_test_grade_item_force_regrading() {
     197          $grade_item = new grade_item($this->grade_items[3], false); // Use a grade item not touched by previous tests.
     198          $this->assertTrue(method_exists($grade_item, 'force_regrading'));
     199  
     200          $this->assertEquals(0, $grade_item->needsupdate);
     201  
     202          $grade_item->force_regrading();
     203          $this->assertEquals(1, $grade_item->needsupdate);
     204          $grade_item->update_from_db();
     205          $this->assertEquals(1, $grade_item->needsupdate);
     206      }
     207  
     208      protected function sub_test_grade_item_fetch() {
     209          $grade_item = new grade_item();
     210          $this->assertTrue(method_exists($grade_item, 'fetch'));
     211  
     212          // Not using $this->grade_items[0] as it's iteminfo was modified by sub_test_grade_item_qualifies_for_regrading().
     213          $grade_item = grade_item::fetch(array('id'=>$this->grade_items[1]->id));
     214          $this->assertEquals($this->grade_items[1]->id, $grade_item->id);
     215          $this->assertEquals($this->grade_items[1]->iteminfo, $grade_item->iteminfo);
     216  
     217          $grade_item = grade_item::fetch(array('itemtype'=>$this->grade_items[1]->itemtype, 'itemmodule'=>$this->grade_items[1]->itemmodule));
     218          $this->assertEquals($this->grade_items[1]->id, $grade_item->id);
     219          $this->assertEquals($this->grade_items[1]->iteminfo, $grade_item->iteminfo);
     220      }
     221  
     222      protected function sub_test_grade_item_fetch_all() {
     223          $grade_item = new grade_item();
     224          $this->assertTrue(method_exists($grade_item, 'fetch_all'));
     225  
     226          $grade_items = grade_item::fetch_all(array('courseid'=>$this->courseid));
     227          $this->assertEquals(count($this->grade_items), count($grade_items)-1); // -1 to account for the course grade item.
     228      }
     229  
     230      // Retrieve all final scores for a given grade_item.
     231      protected function sub_test_grade_item_get_all_finals() {
     232          $grade_item = new grade_item($this->grade_items[0], false);
     233          $this->assertTrue(method_exists($grade_item, 'get_final'));
     234  
     235          $final_grades = $grade_item->get_final();
     236          $this->assertEquals(3, count($final_grades));
     237      }
     238  
     239  
     240      // Retrieve all final scores for a specific userid.
     241      protected function sub_test_grade_item_get_final() {
     242          $grade_item = new grade_item($this->grade_items[0], false);
     243          $this->assertTrue(method_exists($grade_item, 'get_final'));
     244          $final_grade = $grade_item->get_final($this->user[1]->id);
     245          $this->assertEquals($this->grade_grades[0]->finalgrade, $final_grade->finalgrade);
     246      }
     247  
     248      protected function sub_test_grade_item_get_sortorder() {
     249          $grade_item = new grade_item($this->grade_items[0], false);
     250          $this->assertTrue(method_exists($grade_item, 'get_sortorder'));
     251          $sortorder = $grade_item->get_sortorder();
     252          $this->assertEquals($this->grade_items[0]->sortorder, $sortorder);
     253      }
     254  
     255      protected function sub_test_grade_item_set_sortorder() {
     256          $grade_item = new grade_item($this->grade_items[0], false);
     257          $this->assertTrue(method_exists($grade_item, 'set_sortorder'));
     258          $grade_item->set_sortorder(999);
     259          $this->assertEquals($grade_item->sortorder, 999);
     260      }
     261  
     262      protected function sub_test_grade_item_move_after_sortorder() {
     263          $grade_item = new grade_item($this->grade_items[0], false);
     264          $this->assertTrue(method_exists($grade_item, 'move_after_sortorder'));
     265          $grade_item->move_after_sortorder(5);
     266          $this->assertEquals($grade_item->sortorder, 6);
     267  
     268          $grade_item = grade_item::fetch(array('id'=>$this->grade_items[0]->id));
     269          $this->assertEquals($grade_item->sortorder, 6);
     270  
     271          $after = grade_item::fetch(array('id'=>$this->grade_items[6]->id));
     272          $this->assertEquals($after->sortorder, 8);
     273      }
     274  
     275      /**
     276       * Tests the getter of the item name with escaped HTML.
     277       */
     278      protected function sub_test_grade_item_get_name_escaped() {
     279          $gradeitem = new grade_item($this->grade_items[0], false);
     280          $this->assertTrue(method_exists($gradeitem, 'get_name'));
     281          $this->assertEquals(format_string($this->grade_items[0]->itemname, true, ['escape' => true]),
     282              $gradeitem->get_name(false, true));
     283      }
     284  
     285      /**
     286       * Tests the getter of the item name with unescaped HTML.
     287       */
     288      protected function sub_test_grade_item_get_name_unescaped() {
     289          $gradeitem = new grade_item($this->grade_items[0], false);
     290          $this->assertTrue(method_exists($gradeitem, 'get_name'));
     291          $this->assertEquals(format_string($this->grade_items[0]->itemname, true, ['escape' => false]),
     292              $gradeitem->get_name(false, false));
     293      }
     294  
     295      protected function sub_test_grade_item_set_parent() {
     296          $grade_item = new grade_item($this->grade_items[0], false);
     297          $this->assertTrue(method_exists($grade_item, 'set_parent'));
     298  
     299          $old = $grade_item->get_parent_category();
     300          $new = new grade_category($this->grade_categories[3], false);
     301          $new_item = $new->get_grade_item();
     302  
     303          $this->assertTrue($grade_item->set_parent($new->id));
     304  
     305          $new_item->update_from_db();
     306          $grade_item->update_from_db();
     307  
     308          $this->assertEquals($grade_item->categoryid, $new->id);
     309      }
     310  
     311      protected function sub_test_grade_item_get_parent_category() {
     312          $grade_item = new grade_item($this->grade_items[0], false);
     313          $this->assertTrue(method_exists($grade_item, 'get_parent_category'));
     314  
     315          $category = $grade_item->get_parent_category();
     316          $this->assertEquals($this->grade_categories[1]->fullname, $category->fullname);
     317      }
     318  
     319      protected function sub_test_grade_item_load_parent_category() {
     320          $grade_item = new grade_item($this->grade_items[0], false);
     321          $this->assertTrue(method_exists($grade_item, 'load_parent_category'));
     322  
     323          $category = $grade_item->load_parent_category();
     324          $this->assertEquals($this->grade_categories[1]->fullname, $category->fullname);
     325          $this->assertEquals($this->grade_categories[1]->fullname, $grade_item->parent_category->fullname);
     326      }
     327  
     328      protected function sub_test_grade_item_get_item_category() {
     329          $grade_item = new grade_item($this->grade_items[3], false);
     330          $this->assertTrue(method_exists($grade_item, 'get_item_category'));
     331  
     332          $category = $grade_item->get_item_category();
     333          $this->assertEquals($this->grade_categories[0]->fullname, $category->fullname);
     334      }
     335  
     336      protected function sub_test_grade_item_load_item_category() {
     337          $grade_item = new grade_item($this->grade_items[3], false);
     338          $this->assertTrue(method_exists($grade_item, 'load_item_category'));
     339  
     340          $category = $grade_item->load_item_category();
     341          $this->assertEquals($this->grade_categories[0]->fullname, $category->fullname);
     342          $this->assertEquals($this->grade_categories[0]->fullname, $grade_item->item_category->fullname);
     343      }
     344  
     345      protected function sub_test_grade_item_regrade_final_grades() {
     346          $grade_item = new grade_item($this->grade_items[0], false);
     347          $this->assertTrue(method_exists($grade_item, 'regrade_final_grades'));
     348          $this->assertEquals(true, $grade_item->regrade_final_grades());
     349          // TODO: add more tests.
     350      }
     351  
     352      protected function sub_test_grade_item_adjust_raw_grade() {
     353          $grade_item = new grade_item($this->grade_items[2], false); // Anything but assignment module!
     354          $this->assertTrue(method_exists($grade_item, 'adjust_raw_grade'));
     355  
     356          $grade_raw = new stdClass();
     357          $grade_raw->rawgrade = 40;
     358          $grade_raw->grademax = 100;
     359          $grade_raw->grademin = 0;
     360  
     361          $grade_item->gradetype = GRADE_TYPE_VALUE;
     362          $grade_item->multfactor = 1;
     363          $grade_item->plusfactor = 0;
     364          $grade_item->grademax = 50;
     365          $grade_item->grademin = 0;
     366  
     367          $original_grade_raw  = clone($grade_raw);
     368          $original_grade_item = clone($grade_item);
     369  
     370          $this->assertEquals(20, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
     371  
     372          // Try a larger maximum grade.
     373          $grade_item->grademax = 150;
     374          $grade_item->grademin = 0;
     375          $this->assertEquals(60, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
     376  
     377          // Try larger minimum grade.
     378          $grade_item->grademin = 50;
     379  
     380          $this->assertEquals(90, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
     381  
     382          // Rescaling from a small scale (0-50) to a larger scale (0-100).
     383          $grade_raw->grademax = 50;
     384          $grade_raw->grademin = 0;
     385          $grade_item->grademax = 100;
     386          $grade_item->grademin = 0;
     387  
     388          $this->assertEquals(80, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
     389  
     390          // Rescaling from a small scale (0-50) to a larger scale with offset (40-100).
     391          $grade_item->grademax = 100;
     392          $grade_item->grademin = 40;
     393  
     394          $this->assertEquals(88, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
     395  
     396          // Try multfactor and plusfactor.
     397          $grade_raw = clone($original_grade_raw);
     398          $grade_item = clone($original_grade_item);
     399          $grade_item->multfactor = 1.23;
     400          $grade_item->plusfactor = 3;
     401  
     402          $this->assertEquals(27.6, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
     403  
     404          // Try multfactor below 0 and a negative plusfactor.
     405          $grade_raw = clone($original_grade_raw);
     406          $grade_item = clone($original_grade_item);
     407          $grade_item->multfactor = 0.23;
     408          $grade_item->plusfactor = -3;
     409  
     410          $this->assertEquals(round(1.6), round($grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)));
     411      }
     412  
     413      protected function sub_test_grade_item_rescale_grades_keep_percentage() {
     414          global $DB;
     415          $gradeitem = new grade_item($this->grade_items[10], false); // 10 is the manual grade item.
     416  
     417          // Create some grades to go with the grade item.
     418          $gradeids = array();
     419          $grade = new stdClass();
     420          $grade->itemid = $gradeitem->id;
     421          $grade->userid = $this->user[2]->id;
     422          $grade->finalgrade = 10;
     423          $grade->rawgrademax = $gradeitem->grademax;
     424          $grade->rawgrademin = $gradeitem->grademin;
     425          $grade->timecreated = time();
     426          $grade->timemodified = time();
     427          $gradeids[] = $DB->insert_record('grade_grades', $grade);
     428  
     429          $grade->userid = $this->user[3]->id;
     430          $grade->finalgrade = 50;
     431          $grade->rawgrademax = $gradeitem->grademax;
     432          $grade->rawgrademin = $gradeitem->grademin;
     433          $gradeids[] = $DB->insert_record('grade_grades', $grade);
     434  
     435          // Run the function.
     436          $gradeitem->grademax = 33;
     437          $gradeitem->grademin = 3;
     438          $gradeitem->update();
     439          $gradeitem->rescale_grades_keep_percentage(0, 100, 3, 33, 'test');
     440  
     441          // Check that the grades were updated to match the grade item.
     442          $grade = $DB->get_record('grade_grades', array('id' => $gradeids[0]));
     443          $this->assertEqualsWithDelta($gradeitem->grademax, $grade->rawgrademax, 0.0001, 'Max grade mismatch');
     444          $this->assertEqualsWithDelta($gradeitem->grademin, $grade->rawgrademin, 0.0001, 'Min grade mismatch');
     445          $this->assertEqualsWithDelta(6, $grade->finalgrade, 0.0001, 'Min grade mismatch');
     446  
     447          $grade = $DB->get_record('grade_grades', array('id' => $gradeids[1]));
     448          $this->assertEqualsWithDelta($gradeitem->grademax, $grade->rawgrademax, 0.0001, 'Max grade mismatch');
     449          $this->assertEqualsWithDelta($gradeitem->grademin, $grade->rawgrademin, 0.0001, 'Min grade mismatch');
     450          $this->assertEqualsWithDelta(18, $grade->finalgrade, 0.0001, 'Min grade mismatch');
     451      }
     452  
     453      protected function sub_test_grade_item_set_locked() {
     454          // Getting a grade_item from the DB as set_locked() will fail if the grade items needs to be updated
     455          // also needs to have at least one grade_grade or $grade_item->get_final(1) returns null.
     456          // $grade_item = new grade_item($this->grade_items[8]);
     457          $grade_item = grade_item::fetch(array('id'=>$this->grade_items[8]->id));
     458  
     459          $this->assertTrue(method_exists($grade_item, 'set_locked'));
     460  
     461          $grade_grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
     462          $this->assertTrue(empty($grade_item->locked));// Not locked.
     463          $this->assertTrue(empty($grade_grade->locked));// Not locked.
     464  
     465          $this->assertTrue($grade_item->set_locked(true, true, false));
     466          $grade_grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
     467  
     468          $this->assertFalse(empty($grade_item->locked));// Locked.
     469          $this->assertFalse(empty($grade_grade->locked)); // Individual grades should be locked too.
     470  
     471          $this->assertTrue($grade_item->set_locked(false, true, false));
     472          $grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
     473  
     474          $this->assertTrue(empty($grade_item->locked));
     475          $this->assertTrue(empty($grade->locked)); // Individual grades should be unlocked too.
     476      }
     477  
     478      protected function sub_test_grade_item_is_locked() {
     479          $grade_item = new grade_item($this->grade_items[10], false);
     480          $this->assertTrue(method_exists($grade_item, 'is_locked'));
     481  
     482          $this->assertFalse($grade_item->is_locked());
     483          $this->assertFalse($grade_item->is_locked($this->user[1]->id));
     484          $this->assertTrue($grade_item->set_locked(true, true, false));
     485          $this->assertTrue($grade_item->is_locked());
     486          $this->assertTrue($grade_item->is_locked($this->user[1]->id));
     487      }
     488  
     489      protected function sub_test_grade_item_set_hidden() {
     490          $grade_item = new grade_item($this->grade_items[0], false);
     491          $this->assertTrue(method_exists($grade_item, 'set_hidden'));
     492  
     493          $grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
     494          $this->assertEquals(0, $grade_item->hidden);
     495          $this->assertEquals(0, $grade->hidden);
     496  
     497          $grade_item->set_hidden(666, true);
     498          $grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
     499  
     500          $this->assertEquals(666, $grade_item->hidden);
     501          $this->assertEquals(666, $grade->hidden);
     502      }
     503  
     504      protected function sub_test_grade_item_is_hidden() {
     505          $grade_item = new grade_item($this->grade_items[0], false);
     506          $this->assertTrue(method_exists($grade_item, 'is_hidden'));
     507  
     508          $this->assertFalse($grade_item->is_hidden());
     509          $this->assertFalse($grade_item->is_hidden(1));
     510  
     511          $grade_item->set_hidden(1);
     512          $this->assertTrue($grade_item->is_hidden());
     513          $this->assertTrue($grade_item->is_hidden(1));
     514  
     515          $grade_item->set_hidden(666);
     516          $this->assertFalse($grade_item->is_hidden());
     517          $this->assertFalse($grade_item->is_hidden(1));
     518  
     519          $grade_item->set_hidden(time()+666);
     520          $this->assertTrue($grade_item->is_hidden());
     521          $this->assertTrue($grade_item->is_hidden(1));
     522      }
     523  
     524      protected function sub_test_grade_item_is_category_item() {
     525          $grade_item = new grade_item($this->grade_items[3], false);
     526          $this->assertTrue(method_exists($grade_item, 'is_category_item'));
     527          $this->assertTrue($grade_item->is_category_item());
     528      }
     529  
     530      protected function sub_test_grade_item_is_course_item() {
     531          $grade_item = grade_item::fetch_course_item($this->courseid);
     532          $this->assertTrue(method_exists($grade_item, 'is_course_item'));
     533          $this->assertTrue($grade_item->is_course_item());
     534      }
     535  
     536      protected function sub_test_grade_item_fetch_course_item() {
     537          $grade_item = grade_item::fetch_course_item($this->courseid);
     538          $this->assertTrue(method_exists($grade_item, 'fetch_course_item'));
     539          $this->assertEquals($grade_item->itemtype, 'course');
     540      }
     541  
     542      protected function sub_test_grade_item_depends_on() {
     543          global $CFG;
     544  
     545          $origenableoutcomes = $CFG->enableoutcomes;
     546          $CFG->enableoutcomes = 0;
     547          $grade_item = new grade_item($this->grade_items[1], false);
     548  
     549          // Calculated grade dependency.
     550          $deps = $grade_item->depends_on();
     551          sort($deps, SORT_NUMERIC); // For comparison.
     552          $this->assertEquals(array($this->grade_items[0]->id), $deps);
     553  
     554          // Simulate depends on returns none when locked.
     555          $grade_item->locked = time();
     556          $grade_item->update();
     557          $deps = $grade_item->depends_on();
     558          sort($deps, SORT_NUMERIC); // For comparison.
     559          $this->assertEquals(array(), $deps);
     560  
     561          // Category dependency.
     562          $grade_item = new grade_item($this->grade_items[3], false);
     563          $deps = $grade_item->depends_on();
     564          sort($deps, SORT_NUMERIC); // For comparison.
     565          $res = array($this->grade_items[4]->id, $this->grade_items[5]->id);
     566          $this->assertEquals($res, $deps);
     567      }
     568  
     569      protected function scales_outcomes_test_grade_item_depends_on() {
     570          $CFG->enableoutcomes = 1;
     571          $origgradeincludescalesinaggregation = $CFG->grade_includescalesinaggregation;
     572          $CFG->grade_includescalesinaggregation = 1;
     573  
     574          // Scale item in category with $CFG->grade_includescalesinaggregation = 1.
     575          $grade_item = new grade_item($this->grade_items[14], false);
     576          $deps = $grade_item->depends_on();
     577          sort($deps, SORT_NUMERIC);
     578          $res = array($this->grade_items[16]->id);
     579          $this->assertEquals($res, $deps);
     580  
     581          // Scale item in category with $CFG->grade_includescalesinaggregation = 0.
     582          $CFG->grade_includescalesinaggregation = 0;
     583          $grade_item = new grade_item($this->grade_items[14], false);
     584          $deps = $grade_item->depends_on();
     585          $res = array();
     586          $this->assertEquals($res, $deps);
     587          $CFG->grade_includescalesinaggregation = 1;
     588  
     589          // Outcome item in category with outcomes disabled.
     590          $CFG->enableoutcomes = 0;
     591          $grade_item = new grade_item($this->grade_items[14], false);
     592          $deps = $grade_item->depends_on();
     593          sort($deps, SORT_NUMERIC);
     594          $res = array($this->grade_items[16]->id, $this->grade_items[17]->id);
     595          $this->assertEquals($res, $deps);
     596  
     597          $CFG->enableoutcomes = $origenableoutcomes;
     598          $CFG->grade_includescalesinaggregation = $origgradeincludescalesinaggregation;
     599      }
     600  
     601      protected function sub_test_refresh_grades() {
     602          // Testing with the grade item for a mod_assignment instance.
     603          $grade_item = new grade_item($this->grade_items[0], false);
     604          $this->assertTrue(method_exists($grade_item, 'refresh_grades'));
     605          $this->assertTrue($grade_item->refresh_grades());
     606  
     607          // Break the grade item and check error handling.
     608          $grade_item->iteminstance = 123456789;
     609          $this->assertFalse($grade_item->refresh_grades());
     610          $this->assertDebuggingCalled();
     611      }
     612  
     613      protected function sub_test_grade_item_is_calculated() {
     614          $grade_item = new grade_item($this->grade_items[1], false);
     615          $this->assertTrue(method_exists($grade_item, 'is_calculated'));
     616          $this->assertTrue($grade_item->is_calculated());
     617  
     618          $grade_item = new grade_item($this->grade_items[0], false);
     619          $this->assertFalse($grade_item->is_calculated());
     620      }
     621  
     622      protected function sub_test_grade_item_set_calculation() {
     623          $grade_item = new grade_item($this->grade_items[1], false);
     624          $this->assertTrue(method_exists($grade_item, 'set_calculation'));
     625          $grade_itemsource = new grade_item($this->grade_items[0], false);
     626  
     627          $grade_item->set_calculation('=[['.$grade_itemsource->idnumber.']]');
     628  
     629          $this->assertTrue(!empty($grade_item->needsupdate));
     630          $this->assertEquals('=##gi'.$grade_itemsource->id.'##', $grade_item->calculation);
     631      }
     632  
     633      protected function sub_test_grade_item_get_calculation() {
     634          $grade_item = new grade_item($this->grade_items[1], false);
     635          $this->assertTrue(method_exists($grade_item, 'get_calculation'));
     636          $grade_itemsource = new grade_item($this->grade_items[0], false);
     637  
     638          $denormalizedformula = str_replace('##gi'.$grade_itemsource->id.'##', '[['.$grade_itemsource->idnumber.']]', $this->grade_items[1]->calculation);
     639  
     640          $formula = $grade_item->get_calculation();
     641          $this->assertTrue(!empty($grade_item->needsupdate));
     642          $this->assertEquals($denormalizedformula, $formula);
     643      }
     644  
     645      public function sub_test_grade_item_compute() {
     646          $grade_item = grade_item::fetch(array('id'=>$this->grade_items[1]->id));
     647          $this->assertTrue(method_exists($grade_item, 'compute'));
     648  
     649          // Check the grade_grades in the array match those in the DB then delete $this->grade_items[1]'s grade_grades.
     650          $this->grade_grades[3] = grade_grade::fetch(array('id'=>$this->grade_grades[3]->id));
     651          $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[3]->id));
     652          $grade_grade->delete();
     653  
     654          $this->grade_grades[4] = grade_grade::fetch(array('id'=>$this->grade_grades[4]->id));
     655          $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[4]->id));
     656          $grade_grade->delete();
     657  
     658          $this->grade_grades[5] = grade_grade::fetch(array('id'=>$this->grade_grades[5]->id));
     659          $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[5]->id));
     660          $grade_grade->delete();
     661  
     662          // Recalculate the grades (its a calculation so pulls values from other grade_items) and reinsert them.
     663          $grade_item->compute();
     664  
     665          $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[3]->userid, 'itemid'=>$this->grade_grades[3]->itemid));
     666          $this->assertEquals($this->grade_grades[3]->finalgrade, $grade_grade->finalgrade);
     667  
     668          $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[4]->userid, 'itemid'=>$this->grade_grades[4]->itemid));
     669          $this->assertEquals($this->grade_grades[4]->finalgrade, $grade_grade->finalgrade);
     670  
     671          $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[5]->userid, 'itemid'=>$this->grade_grades[5]->itemid));
     672          $this->assertEquals($this->grade_grades[5]->finalgrade, $grade_grade->finalgrade);
     673      }
     674  
     675      protected function sub_test_update_final_grade() {
     676  
     677          // MDL-31713 Check that min and max are set on the grade_grade instance
     678          // if the grade is overridden before the activity has supplied a grade.
     679          $min = 2;
     680          $max = 8;
     681  
     682          // Create a brand new grade item.
     683          $grade_item = new grade_item();
     684          $this->assertTrue(method_exists($grade_item, 'insert'));
     685  
     686          $grade_item->courseid = $this->courseid;
     687          $grade_item->categoryid = $this->grade_categories[1]->id;
     688          $grade_item->itemname = 'brand new unit test grade item';
     689          $grade_item->itemtype = 'mod';
     690          $grade_item->itemmodule = 'quiz';
     691          $grade_item->iteminfo = 'Grade item used for unit testing';
     692          $grade_item->iteminstance = $this->activities[7]->id;
     693          $grade_item->grademin = $min;
     694          $grade_item->grademax = $max;
     695          $grade_item->insert();
     696  
     697          // Override the student grade.
     698          $grade_item->update_final_grade($this->user[1]->id, 7, 'gradebook', '', FORMAT_MOODLE);
     699  
     700          // Check the student's grade has the correct min and max grade.
     701          $grade_grade = grade_grade::fetch(array('userid'=>$this->user[1]->id, 'itemid'=>$grade_item->id));
     702          $this->assertEquals($min, $grade_grade->rawgrademin);
     703          $this->assertEquals($max, $grade_grade->rawgrademax);
     704      }
     705  
     706      protected function sub_test_grade_item_can_control_visibility() {
     707          // Grade item 0 == Course module 0 == Assignment.
     708          $grade_item = new grade_item($this->grade_items[0], false);
     709          $this->assertTrue($grade_item->can_control_visibility());
     710  
     711          // Grade item  == Course module 7 == Quiz.
     712          $grade_item = new grade_item($this->grade_items[11], false);
     713          $this->assertFalse($grade_item->can_control_visibility());
     714      }
     715  
     716      /**
     717       * Test the {@link grade_item::fix_duplicate_sortorder() function with
     718       * faked duplicate sortorder data.
     719       */
     720      public function sub_test_grade_item_fix_sortorder() {
     721          global $DB;
     722  
     723          $this->resetAfterTest(true);
     724  
     725          // Each set is used for filling the db with fake data and will be representing the result of query:
     726          // "SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id".
     727          $testsets = array(
     728              // Items that need no action.
     729              array(1,2,3),
     730              array(5,6,7),
     731              array(7,6,1,3,2,5),
     732              // Items with sortorder duplicates
     733              array(1,2,2,3,3,4,5),
     734              // Only one sortorder duplicate.
     735              array(1,1),
     736              array(3,3),
     737              // Non-sequential sortorders with one or multiple duplicates.
     738              array(3,3,7,5,6,6,9,10,8,3),
     739              array(7,7,3),
     740              array(3,4,5,3,5,4,7,1)
     741          );
     742          $origsequence = array();
     743  
     744          // Generate the data and remember the initial sequence or items.
     745          foreach ($testsets as $testset) {
     746              $course = $this->getDataGenerator()->create_course();
     747              foreach ($testset as $sortorder) {
     748                  $this->insert_fake_grade_item_sortorder($course->id, $sortorder);
     749              }
     750              $DB->get_records('grade_items');
     751              $origsequence[$course->id] = $DB->get_fieldset_sql("SELECT id FROM {grade_items} ".
     752                  "WHERE courseid = ? ORDER BY sortorder, id", array($course->id));
     753          }
     754  
     755          $duplicatedetectionsql = "SELECT courseid, sortorder
     756                                      FROM {grade_items}
     757                                  WHERE courseid = :courseid
     758                                  GROUP BY courseid, sortorder
     759                                    HAVING COUNT(id) > 1";
     760  
     761          // Do the work.
     762          foreach ($origsequence as $courseid => $ignore) {
     763              grade_item::fix_duplicate_sortorder($courseid);
     764              // Verify that no duplicates are left in the database.
     765              $dupes = $DB->record_exists_sql($duplicatedetectionsql, array('courseid' => $courseid));
     766              $this->assertFalse($dupes);
     767          }
     768  
     769          // Verify that sequences are exactly the same as they were before upgrade script.
     770          $idx = 0;
     771          foreach ($origsequence as $courseid => $sequence) {
     772              if (count(($testsets[$idx])) == count(array_unique($testsets[$idx]))) {
     773                  // If there were no duplicates for this course verify that sortorders are not modified.
     774                  $newsortorders = $DB->get_fieldset_sql("SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id", array($courseid));
     775                  $this->assertEquals($testsets[$idx], $newsortorders);
     776              }
     777              $newsequence = $DB->get_fieldset_sql("SELECT id FROM {grade_items} ".
     778                  "WHERE courseid = ? ORDER BY sortorder, id", array($courseid));
     779              $this->assertEquals($sequence, $newsequence,
     780                      "Sequences do not match for test set $idx : ".join(',', $testsets[$idx]));
     781              $idx++;
     782          }
     783      }
     784  
     785      /**
     786       * Populate some fake grade items into the database with specified
     787       * sortorder and course id.
     788       *
     789       * NOTE: This function doesn't make much attempt to respect the
     790       * gradebook internals, its simply used to fake some data for
     791       * testing the upgradelib function. Please don't use it for other
     792       * purposes.
     793       *
     794       * @param int $courseid id of course
     795       * @param int $sortorder numeric sorting order of item
     796       * @return stdClass grade item object from the database.
     797       */
     798      private function insert_fake_grade_item_sortorder($courseid, $sortorder) {
     799          global $DB, $CFG;
     800          require_once($CFG->libdir.'/gradelib.php');
     801  
     802          $item = new stdClass();
     803          $item->courseid = $courseid;
     804          $item->sortorder = $sortorder;
     805          $item->gradetype = GRADE_TYPE_VALUE;
     806          $item->grademin = 30;
     807          $item->grademax = 110;
     808          $item->itemnumber = 1;
     809          $item->iteminfo = '';
     810          $item->timecreated = time();
     811          $item->timemodified = time();
     812  
     813          $item->id = $DB->insert_record('grade_items', $item);
     814  
     815          return $DB->get_record('grade_items', array('id' => $item->id));
     816      }
     817  
     818      public function test_set_aggregation_fields_for_aggregation() {
     819          $course = $this->getDataGenerator()->create_course();
     820          $gi = new grade_item(array('courseid' => $course->id, 'itemtype' => 'manual'), false);
     821  
     822          $methods = array(GRADE_AGGREGATE_MEAN, GRADE_AGGREGATE_MEDIAN, GRADE_AGGREGATE_MIN, GRADE_AGGREGATE_MAX,
     823              GRADE_AGGREGATE_MODE, GRADE_AGGREGATE_WEIGHTED_MEAN, GRADE_AGGREGATE_WEIGHTED_MEAN2,
     824              GRADE_AGGREGATE_EXTRACREDIT_MEAN, GRADE_AGGREGATE_SUM);
     825  
     826          // Switching from and to the same aggregation using the defaults.
     827          foreach ($methods as $method) {
     828              $defaults = grade_category::get_default_aggregation_coefficient_values($method);
     829              $gi->aggregationcoef = $defaults['aggregationcoef'];
     830              $gi->aggregationcoef2 = $defaults['aggregationcoef2'];
     831              $gi->weightoverride = $defaults['weightoverride'];
     832              $this->assertFalse($gi->set_aggregation_fields_for_aggregation($method, $method));
     833              $this->assertEquals($defaults['aggregationcoef'], $gi->aggregationcoef);
     834              $this->assertEquals($defaults['aggregationcoef2'], $gi->aggregationcoef2);
     835              $this->assertEquals($defaults['weightoverride'], $gi->weightoverride);
     836          }
     837  
     838          // Extra credit is kept across aggregation methods that support it.
     839          foreach ($methods as $from) {
     840              $fromsupportsec = grade_category::aggregation_uses_extracredit($from);
     841              $fromdefaults = grade_category::get_default_aggregation_coefficient_values($from);
     842  
     843              foreach ($methods as $to) {
     844                  $tosupportsec = grade_category::aggregation_uses_extracredit($to);
     845                  $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     846  
     847                  // Set the item to be extra credit, if supported.
     848                  if ($fromsupportsec) {
     849                      $gi->aggregationcoef = 1;
     850                  } else {
     851                      $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
     852                  }
     853  
     854                  // We ignore those fields, we know it is never used for extra credit.
     855                  $gi->aggregationcoef2 = $todefaults['aggregationcoef2'];
     856                  $gi->weightoverride = $todefaults['weightoverride'];
     857  
     858                  if ($fromsupportsec && $tosupportsec) {
     859                      $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     860                      $this->assertEquals(1, $gi->aggregationcoef);
     861  
     862                  } else if ($fromsupportsec && !$tosupportsec) {
     863                      if ($to == GRADE_AGGREGATE_WEIGHTED_MEAN) {
     864                          // Special case, aggregationcoef is used but for weights.
     865                          $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     866                          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     867                      } else {
     868                          $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     869                          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     870                      }
     871                  } else {
     872                      // The source does not support extra credit, everything will be reset.
     873                      if (($from == GRADE_AGGREGATE_WEIGHTED_MEAN || $to == GRADE_AGGREGATE_WEIGHTED_MEAN) && $from != $to) {
     874                          // Special case, aggregationcoef is used but for weights.
     875                          $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     876                          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     877                      } else {
     878                          $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     879                          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     880                      }
     881                  }
     882              }
     883          }
     884  
     885          // Extra credit can be higher than one for GRADE_AGGREGATE_EXTRACREDIT_MEAN, but will be normalised for others.
     886          $from = GRADE_AGGREGATE_EXTRACREDIT_MEAN;
     887          $fromdefaults = grade_category::get_default_aggregation_coefficient_values($from);
     888  
     889          foreach ($methods as $to) {
     890              if (!grade_category::aggregation_uses_extracredit($to)) {
     891                  continue;
     892              }
     893  
     894              $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     895              $gi->aggregationcoef = 8;
     896  
     897              // Ignore those fields, they are not used for extra credit.
     898              $gi->aggregationcoef2 = $todefaults['aggregationcoef2'];
     899              $gi->weightoverride = $todefaults['weightoverride'];
     900  
     901              if ($to == $from) {
     902                  $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     903                  $this->assertEquals(8, $gi->aggregationcoef);
     904              } else {
     905                  $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     906                  $this->assertEquals(1, $gi->aggregationcoef);
     907              }
     908          }
     909  
     910          // Weights are reset.
     911          $from = GRADE_AGGREGATE_SUM;
     912          $fromdefaults = grade_category::get_default_aggregation_coefficient_values($from);
     913  
     914          $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
     915          $gi->aggregationcoef2 = 0.321;
     916          $gi->weightoverride = $fromdefaults['weightoverride'];
     917  
     918          $to = GRADE_AGGREGATE_WEIGHTED_MEAN;
     919          $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     920  
     921          $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     922          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     923          $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
     924          $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
     925  
     926          $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
     927          $gi->aggregationcoef2 = 0.321;
     928          $gi->weightoverride = $fromdefaults['weightoverride'];
     929  
     930          $to = GRADE_AGGREGATE_SUM;
     931          $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     932  
     933          $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     934          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     935          $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
     936          $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
     937  
     938          // Weight is kept when using SUM with weight override.
     939          $from = GRADE_AGGREGATE_SUM;
     940          $fromdefaults = grade_category::get_default_aggregation_coefficient_values($from);
     941  
     942          $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
     943          $gi->aggregationcoef2 = 0.321;
     944          $gi->weightoverride = 1;
     945  
     946          $to = GRADE_AGGREGATE_SUM;
     947          $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     948  
     949          $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     950          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     951          $this->assertEquals(0.321, $gi->aggregationcoef2);
     952          $this->assertEquals(1, $gi->weightoverride);
     953  
     954          $gi->aggregationcoef2 = 0.321;
     955          $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
     956          $gi->weightoverride = 1;
     957  
     958          $to = GRADE_AGGREGATE_WEIGHTED_MEAN;
     959          $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     960  
     961          $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     962          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     963          $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
     964          $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
     965  
     966          // Weight is kept when staying in weighted mean.
     967          $from = GRADE_AGGREGATE_WEIGHTED_MEAN;
     968          $fromdefaults = grade_category::get_default_aggregation_coefficient_values($from);
     969  
     970          $gi->aggregationcoef = 18;
     971          $gi->aggregationcoef2 = $fromdefaults['aggregationcoef2'];
     972          $gi->weightoverride = $fromdefaults['weightoverride'];
     973  
     974          $to = GRADE_AGGREGATE_WEIGHTED_MEAN;
     975          $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     976  
     977          $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     978          $this->assertEquals(18, $gi->aggregationcoef);
     979          $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
     980          $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
     981  
     982          $gi->aggregationcoef = 18;
     983          $gi->aggregationcoef2 = $fromdefaults['aggregationcoef2'];
     984          $gi->weightoverride = $fromdefaults['weightoverride'];
     985  
     986          $to = GRADE_AGGREGATE_SUM;
     987          $todefaults = grade_category::get_default_aggregation_coefficient_values($to);
     988  
     989          $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
     990          $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
     991          $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
     992          $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
     993      }
     994  
     995      /**
     996       * Test that grade item event triggered when a grade item is created.
     997       */
     998      protected function sub_test_grade_item_created_event() {
     999          $sink = $this->redirectEvents();
    1000  
    1001          $gradeitem = new grade_item();
    1002  
    1003          $gradeitem->courseid = $this->courseid;
    1004          $gradeitem->categoryid = $this->grade_categories[1]->id;
    1005          $gradeitem->itemname = 'unittestgradeitem4';
    1006          $gradeitem->itemtype = 'mod';
    1007          $gradeitem->itemmodule = 'quiz';
    1008          $gradeitem->iteminfo = 'Grade item used for unit testing';
    1009  
    1010          $gradeitem->insert();
    1011  
    1012          $result = $sink->get_events();
    1013          $sink->close();
    1014  
    1015          $this->assertCount(1, $result);
    1016  
    1017          $event = reset($result);
    1018          $this->assertEventContextNotUsed($event);
    1019          $this->assertInstanceOf('core\event\grade_item_created', $event);
    1020  
    1021          $eventgradeitem = $event->get_grade_item();
    1022  
    1023          $this->assertInstanceOf('grade_item', $eventgradeitem);
    1024          $this->assertEquals($gradeitem->id, $eventgradeitem->id);
    1025          $this->assertEquals($gradeitem->itemname, $event->other['itemname']);
    1026          $this->assertEquals($gradeitem->itemtype, $event->other['itemtype']);
    1027          $this->assertEquals($gradeitem->itemmodule, $event->other['itemmodule']);
    1028      }
    1029  
    1030      /**
    1031       * Test that grade item event triggered when a grade item is updated.
    1032       */
    1033      protected function sub_test_grade_item_updated_event() {
    1034          $gradeitem = new grade_item();
    1035  
    1036          $gradeitem->courseid = $this->courseid;
    1037          $gradeitem->categoryid = $this->grade_categories[1]->id;
    1038          $gradeitem->itemname = 'unittestgradeitem4';
    1039          $gradeitem->itemtype = 'mod';
    1040          $gradeitem->itemmodule = 'quiz';
    1041          $gradeitem->iteminfo = 'Grade item used for unit testing';
    1042          $gradeitem->insert();
    1043  
    1044          $sink = $this->redirectEvents();
    1045  
    1046          $gradeitem->itemname = 'updatedname';
    1047          $gradeitem->update();
    1048  
    1049          $result = $sink->get_events();
    1050          $sink->close();
    1051  
    1052          $this->assertCount(1, $result);
    1053  
    1054          $event = reset($result);
    1055          $this->assertInstanceOf('core\event\grade_item_updated', $event);
    1056          $this->assertEventContextNotUsed($event);
    1057  
    1058          $eventgradeitem = $event->get_grade_item();
    1059  
    1060          $this->assertInstanceOf('grade_item', $eventgradeitem);
    1061          $this->assertEquals($gradeitem->id, $eventgradeitem->id);
    1062          $this->assertEquals($gradeitem->itemtype, $event->other['itemtype']);
    1063          $this->assertEquals($gradeitem->itemmodule, $event->other['itemmodule']);
    1064          $this->assertEquals('updatedname', $event->other['itemname']);
    1065      }
    1066  
    1067  
    1068      /**
    1069       * Test grade item duplication expecting success.
    1070       */
    1071      public function test_grade_duplicate_grade_item_success() {
    1072          $cat = new grade_category();
    1073          $cat->courseid = $this->courseid;
    1074          $cat->fullname = 'Grade category';
    1075          $cat->insert();
    1076  
    1077          // Method exists.
    1078          $gi = new grade_item();
    1079          $this->assertTrue(method_exists($gi, 'duplicate'));
    1080  
    1081          // Grade item is inserted and valid for duplication.
    1082          $gi->courseid = $this->courseid;
    1083          $gi->categoryid = $cat->id;
    1084          $gi->itemtype = 'manual';
    1085          $gi->itemname = 'Grade Item 1';
    1086          $gi->idnumber = '1000';
    1087          $gi->insert();
    1088          $gi2 = $gi->duplicate();
    1089  
    1090          $this->assertEquals($gi->courseid, $gi2->courseid);
    1091          $this->assertEquals($gi->categoryid, $gi2->categoryid);
    1092          $this->assertEquals($gi->itemtype, $gi2->itemtype);
    1093          $this->assertEquals($gi->gradetype, $gi2->gradetype);
    1094          $this->assertEquals($gi->grademax, $gi2->grademax);
    1095          $this->assertEquals($gi->grademin, $gi2->grademin);
    1096          $this->assertEquals($gi->gradepass, $gi2->gradepass);
    1097          $this->assertEquals($gi->display, $gi2->display);
    1098          $this->assertEquals($gi->decimals, $gi2->decimals);
    1099          $this->assertEquals($gi->hidden, $gi2->hidden);
    1100          $this->assertEquals($gi->weightoverride, $gi2->weightoverride);
    1101  
    1102          $this->assertNotEquals($gi->id, $gi2->id);
    1103          $this->assertNotEquals($gi->idnumber, $gi2->idnumber);
    1104          $this->assertNotEquals($gi->sortorder, $gi2->sortorder);
    1105          $this->assertNotEquals($gi->itemname, $gi2->itemname);
    1106      }
    1107  
    1108      /**
    1109       * Test grade item duplication exception expected with incomplete grade item.
    1110       */
    1111      public function test_grade_duplicate_grade_item_incomplete() {
    1112          // Grade item is not valid because it is empty.
    1113          $gi = new grade_item();
    1114          $gi->courseid = $this->courseid;
    1115          $this->expectException("moodle_exception");
    1116          $gi2 = $gi->duplicate();
    1117      }
    1118  
    1119      /**
    1120       * Test grade item duplication exception expected because item must be in db.
    1121       */
    1122      public function test_grade_duplicate_grade_item_not_in_db() {
    1123          $cat = new grade_category();
    1124          $cat->courseid = $this->courseid;
    1125          $cat->fullname = 'Grade category';
    1126          $cat->insert();
    1127  
    1128          // Grade item is valid for insertion but is not inserted into db.
    1129          // Duplicate method throws an exception.
    1130          $gi = new grade_item();
    1131          $gi->courseid = $this->courseid;
    1132          $gi->categoryid = $cat->id;
    1133          $gi->itemtype = 'manual';
    1134          $gi->itemname = 'Grade Item 1';
    1135          $gi->idnumber = '1000';
    1136  
    1137          $this->expectException("moodle_exception");
    1138          $gi2 = $gi->duplicate();
    1139      }
    1140  }