PageRenderTime 46ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/grade/tests/grade_grade_test.php

https://bitbucket.org/moodle/moodle
PHP | 672 lines | 424 code | 122 blank | 126 comment | 3 complexity | e9426b74efe363a0be36b1bc648953e8 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  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. * @package core_grades
  18. * @category phpunit
  19. * @copyright nicolas@moodle.com
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. */
  22. defined('MOODLE_INTERNAL') || die();
  23. require_once(__DIR__.'/fixtures/lib.php');
  24. class core_grade_grade_testcase extends grade_base_testcase {
  25. public function test_grade_grade() {
  26. $this->sub_test_grade_grade_construct();
  27. $this->sub_test_grade_grade_insert();
  28. $this->sub_test_grade_grade_update();
  29. $this->sub_test_grade_grade_fetch();
  30. $this->sub_test_grade_grade_fetch_all();
  31. $this->sub_test_grade_grade_load_grade_item();
  32. $this->sub_test_grade_grade_standardise_score();
  33. $this->sub_test_grade_grade_is_locked();
  34. $this->sub_test_grade_grade_set_hidden();
  35. $this->sub_test_grade_grade_is_hidden();
  36. $this->sub_test_grade_grade_deleted();
  37. $this->sub_test_grade_grade_deleted_event();
  38. }
  39. protected function sub_test_grade_grade_construct() {
  40. $params = new stdClass();
  41. $params->itemid = $this->grade_items[0]->id;
  42. $params->userid = 1;
  43. $params->rawgrade = 88;
  44. $params->rawgrademax = 110;
  45. $params->rawgrademin = 18;
  46. $grade_grade = new grade_grade($params, false);
  47. $this->assertEquals($params->itemid, $grade_grade->itemid);
  48. $this->assertEquals($params->rawgrade, $grade_grade->rawgrade);
  49. }
  50. protected function sub_test_grade_grade_insert() {
  51. $grade_grade = new grade_grade();
  52. $this->assertTrue(method_exists($grade_grade, 'insert'));
  53. $grade_grade->itemid = $this->grade_items[0]->id;
  54. $grade_grade->userid = 10;
  55. $grade_grade->rawgrade = 88;
  56. $grade_grade->rawgrademax = 110;
  57. $grade_grade->rawgrademin = 18;
  58. // Check the grade_item's needsupdate variable first.
  59. $grade_grade->load_grade_item();
  60. $this->assertEmpty($grade_grade->grade_item->needsupdate);
  61. $grade_grade->insert();
  62. $last_grade_grade = end($this->grade_grades);
  63. $this->assertEquals($grade_grade->id, $last_grade_grade->id + 1);
  64. // Timecreated will only be set if the grade was submitted by an activity module.
  65. $this->assertTrue(empty($grade_grade->timecreated));
  66. // Timemodified will only be set if the grade was submitted by an activity module.
  67. $this->assertTrue(empty($grade_grade->timemodified));
  68. // Keep our collection the same as is in the database.
  69. $this->grade_grades[] = $grade_grade;
  70. }
  71. protected function sub_test_grade_grade_update() {
  72. $grade_grade = new grade_grade($this->grade_grades[0], false);
  73. $this->assertTrue(method_exists($grade_grade, 'update'));
  74. }
  75. protected function sub_test_grade_grade_fetch() {
  76. $grade_grade = new grade_grade();
  77. $this->assertTrue(method_exists($grade_grade, 'fetch'));
  78. $grades = grade_grade::fetch(array('id'=>$this->grade_grades[0]->id));
  79. $this->assertEquals($this->grade_grades[0]->id, $grades->id);
  80. $this->assertEquals($this->grade_grades[0]->rawgrade, $grades->rawgrade);
  81. }
  82. protected function sub_test_grade_grade_fetch_all() {
  83. $grade_grade = new grade_grade();
  84. $this->assertTrue(method_exists($grade_grade, 'fetch_all'));
  85. $grades = grade_grade::fetch_all(array());
  86. $this->assertEquals(count($this->grade_grades), count($grades));
  87. }
  88. protected function sub_test_grade_grade_load_grade_item() {
  89. $grade_grade = new grade_grade($this->grade_grades[0], false);
  90. $this->assertTrue(method_exists($grade_grade, 'load_grade_item'));
  91. $this->assertNull($grade_grade->grade_item);
  92. $this->assertNotEmpty($grade_grade->itemid);
  93. $this->assertNotNull($grade_grade->load_grade_item());
  94. $this->assertNotNull($grade_grade->grade_item);
  95. $this->assertEquals($this->grade_items[0]->id, $grade_grade->grade_item->id);
  96. }
  97. protected function sub_test_grade_grade_standardise_score() {
  98. $this->assertEquals(4, round(grade_grade::standardise_score(6, 0, 7, 0, 5)));
  99. $this->assertEquals(40, grade_grade::standardise_score(50, 30, 80, 0, 100));
  100. }
  101. /*
  102. * Disabling this test: the set_locked() arguments have been modified, rendering these tests useless until they are re-written
  103. protected function test_grade_grade_set_locked() {
  104. $grade_item = new grade_item($this->grade_items[0]);
  105. $grade = new grade_grade($grade_item->get_final(1));
  106. $this->assertTrue(method_exists($grade, 'set_locked'));
  107. $this->assertTrue(empty($grade_item->locked));
  108. $this->assertTrue(empty($grade->locked));
  109. $this->assertTrue($grade->set_locked(true));
  110. $this->assertFalse(empty($grade->locked));
  111. $this->assertTrue($grade->set_locked(false));
  112. $this->assertTrue(empty($grade->locked));
  113. $this->assertTrue($grade_item->set_locked(true, true));
  114. $grade = new grade_grade($grade_item->get_final(1));
  115. $this->assertFalse(empty($grade->locked));
  116. $this->assertFalse($grade->set_locked(true, false));
  117. $this->assertTrue($grade_item->set_locked(true, false));
  118. $grade = new grade_grade($grade_item->get_final(1));
  119. $this->assertTrue($grade->set_locked(true, false));
  120. }
  121. */
  122. protected function sub_test_grade_grade_is_locked() {
  123. $grade = new grade_grade($this->grade_grades[0], false);
  124. $this->assertTrue(method_exists($grade, 'is_locked'));
  125. $this->assertFalse($grade->is_locked());
  126. $grade->locked = time();
  127. $this->assertTrue($grade->is_locked());
  128. }
  129. protected function sub_test_grade_grade_set_hidden() {
  130. $grade = new grade_grade($this->grade_grades[0], false);
  131. $grade_item = new grade_item($this->grade_items[0], false);
  132. $this->assertTrue(method_exists($grade, 'set_hidden'));
  133. $this->assertEquals(0, $grade_item->hidden);
  134. $this->assertEquals(0, $grade->hidden);
  135. $grade->set_hidden(0);
  136. $this->assertEquals(0, $grade->hidden);
  137. $grade->set_hidden(1);
  138. $this->assertEquals(1, $grade->hidden);
  139. $grade->set_hidden(0);
  140. $this->assertEquals(0, $grade->hidden);
  141. }
  142. protected function sub_test_grade_grade_is_hidden() {
  143. $grade = new grade_grade($this->grade_grades[0], false);
  144. $this->assertTrue(method_exists($grade, 'is_hidden'));
  145. $this->assertFalse($grade->is_hidden());
  146. $grade->hidden = 1;
  147. $this->assertTrue($grade->is_hidden());
  148. $grade->hidden = time()-666;
  149. $this->assertFalse($grade->is_hidden());
  150. $grade->hidden = time()+666;
  151. $this->assertTrue($grade->is_hidden());
  152. }
  153. public function test_flatten_dependencies() {
  154. // First test a simple normal case.
  155. $a = array(1 => array(2, 3), 2 => array(), 3 => array(4), 4 => array());
  156. $b = array();
  157. $expecteda = array(1 => array(2, 3, 4), 2 => array(), 3 => array(4), 4 => array());
  158. $expectedb = array(1 => 1);
  159. test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
  160. $this->assertSame($expecteda, $a);
  161. $this->assertSame($expectedb, $b);
  162. // Edge case - empty arrays.
  163. $a = $b = $expecteda = $expectedb = array();
  164. test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
  165. $this->assertSame($expecteda, $a);
  166. $this->assertSame($expectedb, $b);
  167. // Circular dependency.
  168. $a = array(1 => array(2), 2 => array(3), 3 => array(1));
  169. $b = array();
  170. $expecteda = array(1 => array(1, 2, 3), 2 => array(1, 2, 3), 3 => array(1, 2, 3));
  171. test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
  172. $this->assertSame($expecteda, $a);
  173. // Note - we don't test the depth when we got circular dependencies - the main thing we wanted to test was that there was
  174. // no ka-boom. The result would be hard to understand and doesn't matter.
  175. // Circular dependency 2.
  176. $a = array(1 => array(2), 2 => array(3), 3 => array(4), 4 => array(2, 1));
  177. $b = array();
  178. $expecteda = array(1 => array(1, 2, 3, 4), 2 => array(1, 2, 3, 4), 3 => array(1, 2, 3, 4), 4 => array(1, 2, 3, 4));
  179. test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
  180. $this->assertSame($expecteda, $a);
  181. }
  182. public function test_grade_grade_min_max() {
  183. global $CFG;
  184. $initialminmaxtouse = $CFG->grade_minmaxtouse;
  185. $this->setAdminUser();
  186. $course = $this->getDataGenerator()->create_course();
  187. $user = $this->getDataGenerator()->create_user();
  188. $assignrecord = $this->getDataGenerator()->create_module('assign', array('course' => $course, 'grade' => 100));
  189. $cm = get_coursemodule_from_instance('assign', $assignrecord->id);
  190. $assigncontext = context_module::instance($cm->id);
  191. $assign = new assign($assigncontext, $cm, $course);
  192. // Fetch the assignment item.
  193. $giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $assignrecord->id,
  194. 'courseid' => $course->id, 'itemnumber' => 0);
  195. $gi = grade_item::fetch($giparams);
  196. $this->assertEquals(0, $gi->grademin);
  197. $this->assertEquals(100, $gi->grademax);
  198. // Give a grade to the student.
  199. $usergrade = $assign->get_user_grade($user->id, true);
  200. $usergrade->grade = 10;
  201. $assign->update_grade($usergrade);
  202. // Check the grade stored in gradebook.
  203. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  204. $this->assertEquals(10, $gg->rawgrade);
  205. $this->assertEquals(0, $gg->get_grade_min());
  206. $this->assertEquals(100, $gg->get_grade_max());
  207. // Change the min/max grade of the item.
  208. $gi->grademax = 50;
  209. $gi->grademin = 2;
  210. $gi->update();
  211. // Fetch the updated item.
  212. $gi = grade_item::fetch($giparams);
  213. // Now check the grade grade min/max with system setting.
  214. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
  215. grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting.
  216. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  217. $this->assertEquals(2, $gg->get_grade_min());
  218. $this->assertEquals(50, $gg->get_grade_max());
  219. // Now with other system setting.
  220. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
  221. grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache.
  222. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  223. $this->assertEquals(0, $gg->get_grade_min());
  224. $this->assertEquals(100, $gg->get_grade_max());
  225. // Now with overriden setting in course.
  226. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
  227. grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE);
  228. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  229. $this->assertEquals(0, $gg->get_grade_min());
  230. $this->assertEquals(100, $gg->get_grade_max());
  231. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
  232. grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM);
  233. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  234. $this->assertEquals(2, $gg->get_grade_min());
  235. $this->assertEquals(50, $gg->get_grade_max());
  236. $CFG->grade_minmaxtouse = $initialminmaxtouse;
  237. }
  238. public function test_grade_grade_min_max_with_course_item() {
  239. global $CFG, $DB;
  240. $initialminmaxtouse = $CFG->grade_minmaxtouse;
  241. $this->setAdminUser();
  242. $course = $this->getDataGenerator()->create_course();
  243. $user = $this->getDataGenerator()->create_user();
  244. $gi = grade_item::fetch_course_item($course->id);
  245. // Fetch the category item.
  246. $this->assertEquals(0, $gi->grademin);
  247. $this->assertEquals(100, $gi->grademax);
  248. // Give a grade to the student.
  249. $gi->update_final_grade($user->id, 10);
  250. // Check the grade min/max stored in gradebook.
  251. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  252. $this->assertEquals(0, $gg->get_grade_min());
  253. $this->assertEquals(100, $gg->get_grade_max());
  254. // Change the min/max grade of the item.
  255. $gi->grademin = 2;
  256. $gi->grademax = 50;
  257. $gi->update();
  258. // Fetch the updated item.
  259. $gi = grade_item::fetch_course_item($course->id);
  260. // Now check the grade grade min/max with system setting.
  261. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
  262. grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting.
  263. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  264. $this->assertEquals(0, $gg->get_grade_min());
  265. $this->assertEquals(100, $gg->get_grade_max());
  266. // Now with other system setting.
  267. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
  268. grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache.
  269. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  270. $this->assertEquals(0, $gg->get_grade_min());
  271. $this->assertEquals(100, $gg->get_grade_max());
  272. // Now with overriden setting in course.
  273. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
  274. grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE);
  275. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  276. $this->assertEquals(0, $gg->get_grade_min());
  277. $this->assertEquals(100, $gg->get_grade_max());
  278. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
  279. grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM);
  280. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  281. $this->assertEquals(0, $gg->get_grade_min());
  282. $this->assertEquals(100, $gg->get_grade_max());
  283. $CFG->grade_minmaxtouse = $initialminmaxtouse;
  284. }
  285. public function test_grade_grade_min_max_with_category_item() {
  286. global $CFG, $DB;
  287. $initialminmaxtouse = $CFG->grade_minmaxtouse;
  288. $this->setAdminUser();
  289. $course = $this->getDataGenerator()->create_course();
  290. $user = $this->getDataGenerator()->create_user();
  291. $coursegi = grade_item::fetch_course_item($course->id);
  292. // Create a category item.
  293. $gc = new grade_category(array('courseid' => $course->id, 'fullname' => 'test'), false);
  294. $gc->insert();
  295. $gi = $gc->get_grade_item();
  296. $gi->grademax = 100;
  297. $gi->grademin = 0;
  298. $gi->update();
  299. // Fetch the category item.
  300. $giparams = array('itemtype' => 'category', 'iteminstance' => $gc->id);
  301. $gi = grade_item::fetch($giparams);
  302. $this->assertEquals(0, $gi->grademin);
  303. $this->assertEquals(100, $gi->grademax);
  304. // Give a grade to the student.
  305. $gi->update_final_grade($user->id, 10);
  306. // Check the grade min/max stored in gradebook.
  307. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  308. $this->assertEquals(0, $gg->get_grade_min());
  309. $this->assertEquals(100, $gg->get_grade_max());
  310. // Change the min/max grade of the item.
  311. $gi->grademin = 2;
  312. $gi->grademax = 50;
  313. $gi->update();
  314. // Fetch the updated item.
  315. $gi = grade_item::fetch($giparams);
  316. // Now check the grade grade min/max with system setting.
  317. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
  318. grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting.
  319. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  320. $this->assertEquals(0, $gg->get_grade_min());
  321. $this->assertEquals(100, $gg->get_grade_max());
  322. // Now with other system setting.
  323. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
  324. grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache.
  325. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  326. $this->assertEquals(0, $gg->get_grade_min());
  327. $this->assertEquals(100, $gg->get_grade_max());
  328. // Now with overriden setting in course.
  329. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
  330. grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE);
  331. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  332. $this->assertEquals(0, $gg->get_grade_min());
  333. $this->assertEquals(100, $gg->get_grade_max());
  334. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
  335. grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM);
  336. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
  337. $this->assertEquals(0, $gg->get_grade_min());
  338. $this->assertEquals(100, $gg->get_grade_max());
  339. $CFG->grade_minmaxtouse = $initialminmaxtouse;
  340. }
  341. /**
  342. * Tests when a grade_grade has been deleted.
  343. */
  344. public function sub_test_grade_grade_deleted() {
  345. $dg = $this->getDataGenerator();
  346. // Create the data we need for the tests.
  347. $fs = new file_storage();
  348. $u1 = $dg->create_user();
  349. $c1 = $dg->create_course();
  350. $a1 = $dg->create_module('assign', ['course' => $c1->id]);
  351. $a1context = context_module::instance($a1->cmid);
  352. $gi = new grade_item($dg->create_grade_item(
  353. [
  354. 'courseid' => $c1->id,
  355. 'itemtype' => 'mod',
  356. 'itemmodule' => 'assign',
  357. 'iteminstance' => $a1->id
  358. ]
  359. ), false);
  360. // Add feedback files to copy as our update.
  361. $this->add_feedback_file_to_copy();
  362. $grades['feedback'] = 'Nice feedback!';
  363. $grades['feedbackformat'] = FORMAT_MOODLE;
  364. $grades['feedbackfiles'] = [
  365. 'contextid' => 1,
  366. 'component' => 'test',
  367. 'filearea' => 'testarea',
  368. 'itemid' => 1
  369. ];
  370. $grades['userid'] = $u1->id;
  371. grade_update('mod/assign', $gi->courseid, $gi->itemtype, $gi->itemmodule, $gi->iteminstance,
  372. $gi->itemnumber, $grades);
  373. // Feedback file area.
  374. $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
  375. $this->assertEquals(2, count($files));
  376. // History file area.
  377. $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
  378. $this->assertEquals(2, count($files));
  379. $gg = grade_grade::fetch(array('userid' => $u1->id, 'itemid' => $gi->id));
  380. $gg->delete();
  381. // Feedback file area.
  382. $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
  383. $this->assertEquals(0, count($files));
  384. // History file area.
  385. $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
  386. $this->assertEquals(2, count($files));
  387. }
  388. /**
  389. * Creates a feedback file to copy to the gradebook area.
  390. */
  391. private function add_feedback_file_to_copy() {
  392. $dummy = array(
  393. 'contextid' => 1,
  394. 'component' => 'test',
  395. 'filearea' => 'testarea',
  396. 'itemid' => 1,
  397. 'filepath' => '/',
  398. 'filename' => 'feedback1.txt'
  399. );
  400. $fs = get_file_storage();
  401. $fs->create_file_from_string($dummy, '');
  402. }
  403. /**
  404. * Tests grade_deleted event.
  405. */
  406. public function sub_test_grade_grade_deleted_event() {
  407. global $DB;
  408. $dg = $this->getDataGenerator();
  409. // Create the data we need for the tests.
  410. $u1 = $dg->create_user();
  411. $u2 = $dg->create_user();
  412. $c1 = $dg->create_course();
  413. $a1 = $dg->create_module('assign', ['course' => $c1->id]);
  414. $gi = new grade_item($dg->create_grade_item(
  415. [
  416. 'courseid' => $c1->id,
  417. 'itemtype' => 'mod',
  418. 'itemmodule' => 'assign',
  419. 'iteminstance' => $a1->id
  420. ]
  421. ), false);
  422. grade_update('mod/assign', $gi->courseid, $gi->itemtype, $gi->itemmodule, $gi->iteminstance,
  423. $gi->itemnumber, ['userid' => $u1->id]);
  424. grade_update('mod/assign', $gi->courseid, $gi->itemtype, $gi->itemmodule, $gi->iteminstance,
  425. $gi->itemnumber, ['userid' => $u2->id]);
  426. $gg = grade_grade::fetch(array('userid' => $u1->id, 'itemid' => $gi->id));
  427. $this->assertEquals($u1->id, $gg->userid);
  428. $gg->load_grade_item();
  429. $this->assertEquals($gi->id, $gg->grade_item->id);
  430. // Delete user with valid grade item.
  431. $sink = $this->redirectEvents();
  432. grade_user_delete($u1->id);
  433. $events = $sink->get_events();
  434. $event = reset($events);
  435. $sink->close();
  436. $this->assertInstanceOf('core\event\grade_deleted', $event);
  437. $gg = grade_grade::fetch(array('userid' => $u2->id, 'itemid' => $gi->id));
  438. $this->assertEquals($u2->id, $gg->userid);
  439. $gg->load_grade_item();
  440. $this->assertEquals($gi->id, $gg->grade_item->id);
  441. // Delete grade item, mock up orphaned grade_grades.
  442. $DB->delete_records('grade_items', ['id' => $gi->id]);
  443. $gg = grade_grade::fetch(array('userid' => $u2->id, 'itemid' => $gi->id));
  444. $this->assertEquals($u2->id, $gg->userid);
  445. // No event is triggered and there is a debugging message.
  446. $sink = $this->redirectEvents();
  447. grade_user_delete($u2->id);
  448. $this->assertDebuggingCalled("Missing grade item id $gi->id");
  449. $events = $sink->get_events();
  450. $sink->close();
  451. $this->assertEmpty($events);
  452. // The grade should be deleted.
  453. $gg = grade_grade::fetch(array('userid' => $u2->id, 'itemid' => $gi->id));
  454. $this->assertEmpty($gg);
  455. }
  456. /**
  457. * Tests get_hiding_affected by locked category and overridden grades.
  458. */
  459. public function test_category_get_hiding_affected() {
  460. $generator = $this->getDataGenerator();
  461. // Create the data we need for the tests.
  462. $course1 = $generator->create_course();
  463. $user1 = $generator->create_and_enrol($course1, 'student');
  464. $assignment2 = $generator->create_module('assign', ['course' => $course1->id]);
  465. // Create a category item.
  466. $gradecategory = new grade_category(array('courseid' => $course1->id, 'fullname' => 'test'), false);
  467. $gradecategoryid = $gradecategory->insert();
  468. // Create one hidden grade item.
  469. $gradeitem1a = new grade_item($generator->create_grade_item(
  470. [
  471. 'courseid' => $course1->id,
  472. 'itemtype' => 'mod',
  473. 'itemmodule' => 'assign',
  474. 'iteminstance' => $assignment2->id,
  475. 'categoryid' => $gradecategoryid,
  476. 'hidden' => 1,
  477. ]
  478. ), false);
  479. grade_update('mod/assign', $gradeitem1a->courseid, $gradeitem1a->itemtype, $gradeitem1a->itemmodule, $gradeitem1a->iteminstance,
  480. $gradeitem1a->itemnumber, ['userid' => $user1->id]);
  481. // Get category grade item.
  482. $gradeitem = $gradecategory->get_grade_item();
  483. // Reset needsupdate to allow set_locked.
  484. $gradeitem->needsupdate = 0;
  485. $gradeitem->update();
  486. // Lock category grade item.
  487. $gradeitem->set_locked(1);
  488. $hidingaffectedlocked = $this->call_get_hiding_affected($course1, $user1);
  489. // Since locked category now should be recalculated.
  490. // The number of unknown items is 2, this includes category item and course item.
  491. $this->assertEquals(2, count($hidingaffectedlocked['unknown']));
  492. // Unlock category.
  493. $gradeitem->set_locked(0);
  494. $hidingaffectedunlocked = $this->call_get_hiding_affected($course1, $user1);
  495. // When category unlocked, hidden item should exist in altered items.
  496. $this->assertTrue(in_array($gradeitem1a->id, array_keys($hidingaffectedunlocked['altered'])));
  497. // This creates all the grade_grades we need.
  498. grade_regrade_final_grades($course1->id);
  499. // Set grade override.
  500. $gradegrade = grade_grade::fetch([
  501. 'userid' => $user1->id,
  502. 'itemid' => $gradeitem->id,
  503. ]);
  504. // Set override grade grade, and check that grade submission has been overridden.
  505. $gradegrade->set_overridden(true);
  506. $this->assertEquals(true, $gradegrade->is_overridden());
  507. $hidingaffectedoverridden = $this->call_get_hiding_affected($course1, $user1);
  508. // No need to recalculate overridden grades.
  509. $this->assertTrue(in_array($gradegrade->itemid, array_keys($hidingaffectedoverridden['alteredaggregationstatus'])));
  510. $this->assertEquals('used', $hidingaffectedoverridden['alteredaggregationstatus'][$gradegrade->itemid]);
  511. }
  512. /**
  513. * Call get_hiding_affected().
  514. * @param stdClass $course The course object
  515. * @param stdClass $user The student object
  516. * @return array
  517. */
  518. private function call_get_hiding_affected($course, $user) {
  519. global $DB;
  520. $items = grade_item::fetch_all(array('courseid' => $course->id));
  521. $grades = array();
  522. $sql = "SELECT g.*
  523. FROM {grade_grades} g
  524. JOIN {grade_items} gi ON gi.id = g.itemid
  525. WHERE g.userid = :userid AND gi.courseid = :courseid";
  526. if ($gradesrecords = $DB->get_records_sql($sql, ['userid' => $user->id, 'courseid' => $course->id])) {
  527. foreach ($gradesrecords as $grade) {
  528. $grades[$grade->itemid] = new grade_grade($grade, false);
  529. }
  530. unset($gradesrecords);
  531. }
  532. foreach ($items as $itemid => $gradeitem) {
  533. if (!isset($grades[$itemid])) {
  534. $gradegrade = new grade_grade();
  535. $gradegrade->userid = $user->id;
  536. $gradegrade->itemid = $gradeitem->id;
  537. $grades[$itemid] = $gradegrade;
  538. }
  539. $gradeitem->grade_item = $gradeitem;
  540. }
  541. return grade_grade::get_hiding_affected($grades, $items);
  542. }
  543. }