PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/grade/tests/grade_item_test.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 736 lines | 507 code | 148 blank | 81 comment | 4 complexity | 9d958535bd7e95c28082dce1f290e3fb MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-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_item_testcase extends grade_base_testcase {
  25. public function test_grade_item() {
  26. $this->sub_test_grade_item_construct();
  27. $this->sub_test_grade_item_insert();
  28. $this->sub_test_grade_item_delete();
  29. $this->sub_test_grade_item_update();
  30. $this->sub_test_grade_item_load_scale();
  31. $this->sub_test_grade_item_load_outcome();
  32. $this->sub_test_grade_item_qualifies_for_regrading();
  33. $this->sub_test_grade_item_force_regrading();
  34. $this->sub_test_grade_item_fetch();
  35. $this->sub_test_grade_item_fetch_all();
  36. $this->sub_test_grade_item_get_all_finals();
  37. $this->sub_test_grade_item_get_final();
  38. $this->sub_test_grade_item_get_sortorder();
  39. $this->sub_test_grade_item_set_sortorder();
  40. $this->sub_test_grade_item_move_after_sortorder();
  41. $this->sub_test_grade_item_get_name();
  42. $this->sub_test_grade_item_set_parent();
  43. $this->sub_test_grade_item_get_parent_category();
  44. $this->sub_test_grade_item_load_parent_category();
  45. $this->sub_test_grade_item_get_item_category();
  46. $this->sub_test_grade_item_load_item_category();
  47. $this->sub_test_grade_item_regrade_final_grades();
  48. $this->sub_test_grade_item_adjust_raw_grade();
  49. $this->sub_test_grade_item_set_locked();
  50. $this->sub_test_grade_item_is_locked();
  51. $this->sub_test_grade_item_set_hidden();
  52. $this->sub_test_grade_item_is_hidden();
  53. $this->sub_test_grade_item_is_category_item();
  54. $this->sub_test_grade_item_is_course_item();
  55. $this->sub_test_grade_item_fetch_course_item();
  56. $this->sub_test_grade_item_depends_on();
  57. $this->sub_test_refresh_grades();
  58. $this->sub_test_grade_item_is_calculated();
  59. $this->sub_test_grade_item_set_calculation();
  60. $this->sub_test_grade_item_get_calculation();
  61. $this->sub_test_grade_item_compute();
  62. $this->sub_test_update_final_grade();
  63. $this->sub_test_grade_item_can_control_visibility();
  64. $this->sub_test_grade_item_fix_sortorder();
  65. }
  66. protected function sub_test_grade_item_construct() {
  67. $params = new stdClass();
  68. $params->courseid = $this->courseid;
  69. $params->categoryid = $this->grade_categories[1]->id;
  70. $params->itemname = 'unittestgradeitem4';
  71. $params->itemtype = 'mod';
  72. $params->itemmodule = 'database';
  73. $params->iteminfo = 'Grade item used for unit testing';
  74. $grade_item = new grade_item($params, false);
  75. $this->assertEquals($params->courseid, $grade_item->courseid);
  76. $this->assertEquals($params->categoryid, $grade_item->categoryid);
  77. $this->assertEquals($params->itemmodule, $grade_item->itemmodule);
  78. }
  79. protected function sub_test_grade_item_insert() {
  80. $grade_item = new grade_item();
  81. $this->assertTrue(method_exists($grade_item, 'insert'));
  82. $grade_item->courseid = $this->courseid;
  83. $grade_item->categoryid = $this->grade_categories[1]->id;
  84. $grade_item->itemname = 'unittestgradeitem4';
  85. $grade_item->itemtype = 'mod';
  86. $grade_item->itemmodule = 'quiz';
  87. $grade_item->iteminfo = 'Grade item used for unit testing';
  88. $grade_item->insert();
  89. $last_grade_item = end($this->grade_items);
  90. $this->assertEquals($grade_item->id, $last_grade_item->id + 1);
  91. $this->assertEquals(18, $grade_item->sortorder);
  92. // Keep our reference collection the same as what is in the database.
  93. $this->grade_items[] = $grade_item;
  94. }
  95. protected function sub_test_grade_item_delete() {
  96. global $DB;
  97. $grade_item = new grade_item($this->grade_items[7], false); // Use a grade item not touched by previous (or future) tests.
  98. $this->assertTrue(method_exists($grade_item, 'delete'));
  99. $this->assertTrue($grade_item->delete());
  100. $this->assertFalse($DB->get_record('grade_items', array('id' => $grade_item->id)));
  101. // Keep our reference collection the same as the database.
  102. unset($this->grade_items[7]);
  103. }
  104. protected function sub_test_grade_item_update() {
  105. global $DB;
  106. $grade_item = new grade_item($this->grade_items[0], false);
  107. $this->assertTrue(method_exists($grade_item, 'update'));
  108. $grade_item->iteminfo = 'Updated info for this unittest grade_item';
  109. $this->assertTrue($grade_item->update());
  110. $grade_item->grademin = 14;
  111. $this->assertTrue($grade_item->qualifies_for_regrading());
  112. $this->assertTrue($grade_item->update());
  113. $iteminfo = $DB->get_field('grade_items', 'iteminfo', array('id' => $this->grade_items[0]->id));
  114. $this->assertEquals($grade_item->iteminfo, $iteminfo);
  115. }
  116. protected function sub_test_grade_item_load_scale() {
  117. $grade_item = new grade_item($this->grade_items[2], false);
  118. $this->assertTrue(method_exists($grade_item, 'load_scale'));
  119. $scale = $grade_item->load_scale();
  120. $this->assertFalse(empty($grade_item->scale));
  121. $this->assertEquals($scale->id, $this->grade_items[2]->scaleid);
  122. }
  123. protected function sub_test_grade_item_load_outcome() {
  124. $grade_item = new grade_item($this->grade_items[0], false);
  125. $this->assertTrue(method_exists($grade_item, 'load_outcome'));
  126. // TODO: add tests.
  127. }
  128. protected function sub_test_grade_item_qualifies_for_regrading() {
  129. $grade_item = new grade_item($this->grade_items[3], false); // Use a grade item not touched by previous tests.
  130. $this->assertTrue(method_exists($grade_item, 'qualifies_for_regrading'));
  131. $this->assertFalse($grade_item->qualifies_for_regrading());
  132. $grade_item->iteminfo = 'Updated info for this unittest grade_item';
  133. $this->assertFalse($grade_item->qualifies_for_regrading());
  134. $grade_item->grademin = 14;
  135. $this->assertTrue($grade_item->qualifies_for_regrading());
  136. }
  137. protected function sub_test_grade_item_force_regrading() {
  138. $grade_item = new grade_item($this->grade_items[3], false); // Use a grade item not touched by previous tests.
  139. $this->assertTrue(method_exists($grade_item, 'force_regrading'));
  140. $this->assertEquals(0, $grade_item->needsupdate);
  141. $grade_item->force_regrading();
  142. $this->assertEquals(1, $grade_item->needsupdate);
  143. $grade_item->update_from_db();
  144. $this->assertEquals(1, $grade_item->needsupdate);
  145. }
  146. protected function sub_test_grade_item_fetch() {
  147. $grade_item = new grade_item();
  148. $this->assertTrue(method_exists($grade_item, 'fetch'));
  149. // Not using $this->grade_items[0] as it's iteminfo was modified by sub_test_grade_item_qualifies_for_regrading().
  150. $grade_item = grade_item::fetch(array('id'=>$this->grade_items[1]->id));
  151. $this->assertEquals($this->grade_items[1]->id, $grade_item->id);
  152. $this->assertEquals($this->grade_items[1]->iteminfo, $grade_item->iteminfo);
  153. $grade_item = grade_item::fetch(array('itemtype'=>$this->grade_items[1]->itemtype, 'itemmodule'=>$this->grade_items[1]->itemmodule));
  154. $this->assertEquals($this->grade_items[1]->id, $grade_item->id);
  155. $this->assertEquals($this->grade_items[1]->iteminfo, $grade_item->iteminfo);
  156. }
  157. protected function sub_test_grade_item_fetch_all() {
  158. $grade_item = new grade_item();
  159. $this->assertTrue(method_exists($grade_item, 'fetch_all'));
  160. $grade_items = grade_item::fetch_all(array('courseid'=>$this->courseid));
  161. $this->assertEquals(count($this->grade_items), count($grade_items)-1); // -1 to account for the course grade item.
  162. }
  163. // Retrieve all final scores for a given grade_item.
  164. protected function sub_test_grade_item_get_all_finals() {
  165. $grade_item = new grade_item($this->grade_items[0], false);
  166. $this->assertTrue(method_exists($grade_item, 'get_final'));
  167. $final_grades = $grade_item->get_final();
  168. $this->assertEquals(3, count($final_grades));
  169. }
  170. // Retrieve all final scores for a specific userid.
  171. protected function sub_test_grade_item_get_final() {
  172. $grade_item = new grade_item($this->grade_items[0], false);
  173. $this->assertTrue(method_exists($grade_item, 'get_final'));
  174. $final_grade = $grade_item->get_final($this->user[1]->id);
  175. $this->assertEquals($this->grade_grades[0]->finalgrade, $final_grade->finalgrade);
  176. }
  177. protected function sub_test_grade_item_get_sortorder() {
  178. $grade_item = new grade_item($this->grade_items[0], false);
  179. $this->assertTrue(method_exists($grade_item, 'get_sortorder'));
  180. $sortorder = $grade_item->get_sortorder();
  181. $this->assertEquals($this->grade_items[0]->sortorder, $sortorder);
  182. }
  183. protected function sub_test_grade_item_set_sortorder() {
  184. $grade_item = new grade_item($this->grade_items[0], false);
  185. $this->assertTrue(method_exists($grade_item, 'set_sortorder'));
  186. $grade_item->set_sortorder(999);
  187. $this->assertEquals($grade_item->sortorder, 999);
  188. }
  189. protected function sub_test_grade_item_move_after_sortorder() {
  190. $grade_item = new grade_item($this->grade_items[0], false);
  191. $this->assertTrue(method_exists($grade_item, 'move_after_sortorder'));
  192. $grade_item->move_after_sortorder(5);
  193. $this->assertEquals($grade_item->sortorder, 6);
  194. $grade_item = grade_item::fetch(array('id'=>$this->grade_items[0]->id));
  195. $this->assertEquals($grade_item->sortorder, 6);
  196. $after = grade_item::fetch(array('id'=>$this->grade_items[6]->id));
  197. $this->assertEquals($after->sortorder, 8);
  198. }
  199. protected function sub_test_grade_item_get_name() {
  200. $grade_item = new grade_item($this->grade_items[0], false);
  201. $this->assertTrue(method_exists($grade_item, 'get_name'));
  202. $name = $grade_item->get_name();
  203. $this->assertEquals($this->grade_items[0]->itemname, $name);
  204. }
  205. protected function sub_test_grade_item_set_parent() {
  206. $grade_item = new grade_item($this->grade_items[0], false);
  207. $this->assertTrue(method_exists($grade_item, 'set_parent'));
  208. $old = $grade_item->get_parent_category();
  209. $new = new grade_category($this->grade_categories[3], false);
  210. $new_item = $new->get_grade_item();
  211. $this->assertTrue($grade_item->set_parent($new->id));
  212. $new_item->update_from_db();
  213. $grade_item->update_from_db();
  214. $this->assertEquals($grade_item->categoryid, $new->id);
  215. }
  216. protected function sub_test_grade_item_get_parent_category() {
  217. $grade_item = new grade_item($this->grade_items[0], false);
  218. $this->assertTrue(method_exists($grade_item, 'get_parent_category'));
  219. $category = $grade_item->get_parent_category();
  220. $this->assertEquals($this->grade_categories[1]->fullname, $category->fullname);
  221. }
  222. protected function sub_test_grade_item_load_parent_category() {
  223. $grade_item = new grade_item($this->grade_items[0], false);
  224. $this->assertTrue(method_exists($grade_item, 'load_parent_category'));
  225. $category = $grade_item->load_parent_category();
  226. $this->assertEquals($this->grade_categories[1]->fullname, $category->fullname);
  227. $this->assertEquals($this->grade_categories[1]->fullname, $grade_item->parent_category->fullname);
  228. }
  229. protected function sub_test_grade_item_get_item_category() {
  230. $grade_item = new grade_item($this->grade_items[3], false);
  231. $this->assertTrue(method_exists($grade_item, 'get_item_category'));
  232. $category = $grade_item->get_item_category();
  233. $this->assertEquals($this->grade_categories[0]->fullname, $category->fullname);
  234. }
  235. protected function sub_test_grade_item_load_item_category() {
  236. $grade_item = new grade_item($this->grade_items[3], false);
  237. $this->assertTrue(method_exists($grade_item, 'load_item_category'));
  238. $category = $grade_item->load_item_category();
  239. $this->assertEquals($this->grade_categories[0]->fullname, $category->fullname);
  240. $this->assertEquals($this->grade_categories[0]->fullname, $grade_item->item_category->fullname);
  241. }
  242. protected function sub_test_grade_item_regrade_final_grades() {
  243. $grade_item = new grade_item($this->grade_items[0], false);
  244. $this->assertTrue(method_exists($grade_item, 'regrade_final_grades'));
  245. $this->assertEquals(true, $grade_item->regrade_final_grades());
  246. // TODO: add more tests.
  247. }
  248. protected function sub_test_grade_item_adjust_raw_grade() {
  249. $grade_item = new grade_item($this->grade_items[2], false); // Anything but assignment module!
  250. $this->assertTrue(method_exists($grade_item, 'adjust_raw_grade'));
  251. $grade_raw = new stdClass();
  252. $grade_raw->rawgrade = 40;
  253. $grade_raw->grademax = 100;
  254. $grade_raw->grademin = 0;
  255. $grade_item->gradetype = GRADE_TYPE_VALUE;
  256. $grade_item->multfactor = 1;
  257. $grade_item->plusfactor = 0;
  258. $grade_item->grademax = 50;
  259. $grade_item->grademin = 0;
  260. $original_grade_raw = clone($grade_raw);
  261. $original_grade_item = clone($grade_item);
  262. $this->assertEquals(20, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
  263. // Try a larger maximum grade.
  264. $grade_item->grademax = 150;
  265. $grade_item->grademin = 0;
  266. $this->assertEquals(60, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
  267. // Try larger minimum grade.
  268. $grade_item->grademin = 50;
  269. $this->assertEquals(90, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
  270. // Rescaling from a small scale (0-50) to a larger scale (0-100).
  271. $grade_raw->grademax = 50;
  272. $grade_raw->grademin = 0;
  273. $grade_item->grademax = 100;
  274. $grade_item->grademin = 0;
  275. $this->assertEquals(80, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
  276. // Rescaling from a small scale (0-50) to a larger scale with offset (40-100).
  277. $grade_item->grademax = 100;
  278. $grade_item->grademin = 40;
  279. $this->assertEquals(88, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
  280. // Try multfactor and plusfactor.
  281. $grade_raw = clone($original_grade_raw);
  282. $grade_item = clone($original_grade_item);
  283. $grade_item->multfactor = 1.23;
  284. $grade_item->plusfactor = 3;
  285. $this->assertEquals(27.6, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax));
  286. // Try multfactor below 0 and a negative plusfactor.
  287. $grade_raw = clone($original_grade_raw);
  288. $grade_item = clone($original_grade_item);
  289. $grade_item->multfactor = 0.23;
  290. $grade_item->plusfactor = -3;
  291. $this->assertEquals(round(1.6), round($grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)));
  292. }
  293. protected function sub_test_grade_item_set_locked() {
  294. // Getting a grade_item from the DB as set_locked() will fail if the grade items needs to be updated
  295. // also needs to have at least one grade_grade or $grade_item->get_final(1) returns null.
  296. // $grade_item = new grade_item($this->grade_items[8]);
  297. $grade_item = grade_item::fetch(array('id'=>$this->grade_items[8]->id));
  298. $this->assertTrue(method_exists($grade_item, 'set_locked'));
  299. $grade_grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
  300. $this->assertTrue(empty($grade_item->locked));// Not locked.
  301. $this->assertTrue(empty($grade_grade->locked));// Not locked.
  302. $this->assertTrue($grade_item->set_locked(true, true, false));
  303. $grade_grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
  304. $this->assertFalse(empty($grade_item->locked));// Locked.
  305. $this->assertFalse(empty($grade_grade->locked)); // Individual grades should be locked too.
  306. $this->assertTrue($grade_item->set_locked(false, true, false));
  307. $grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
  308. $this->assertTrue(empty($grade_item->locked));
  309. $this->assertTrue(empty($grade->locked)); // Individual grades should be unlocked too.
  310. }
  311. protected function sub_test_grade_item_is_locked() {
  312. $grade_item = new grade_item($this->grade_items[10], false);
  313. $this->assertTrue(method_exists($grade_item, 'is_locked'));
  314. $this->assertFalse($grade_item->is_locked());
  315. $this->assertFalse($grade_item->is_locked($this->user[1]->id));
  316. $this->assertTrue($grade_item->set_locked(true, true, false));
  317. $this->assertTrue($grade_item->is_locked());
  318. $this->assertTrue($grade_item->is_locked($this->user[1]->id));
  319. }
  320. protected function sub_test_grade_item_set_hidden() {
  321. $grade_item = new grade_item($this->grade_items[0], false);
  322. $this->assertTrue(method_exists($grade_item, 'set_hidden'));
  323. $grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
  324. $this->assertEquals(0, $grade_item->hidden);
  325. $this->assertEquals(0, $grade->hidden);
  326. $grade_item->set_hidden(666, true);
  327. $grade = new grade_grade($grade_item->get_final($this->user[1]->id), false);
  328. $this->assertEquals(666, $grade_item->hidden);
  329. $this->assertEquals(666, $grade->hidden);
  330. }
  331. protected function sub_test_grade_item_is_hidden() {
  332. $grade_item = new grade_item($this->grade_items[0], false);
  333. $this->assertTrue(method_exists($grade_item, 'is_hidden'));
  334. $this->assertFalse($grade_item->is_hidden());
  335. $this->assertFalse($grade_item->is_hidden(1));
  336. $grade_item->set_hidden(1);
  337. $this->assertTrue($grade_item->is_hidden());
  338. $this->assertTrue($grade_item->is_hidden(1));
  339. $grade_item->set_hidden(666);
  340. $this->assertFalse($grade_item->is_hidden());
  341. $this->assertFalse($grade_item->is_hidden(1));
  342. $grade_item->set_hidden(time()+666);
  343. $this->assertTrue($grade_item->is_hidden());
  344. $this->assertTrue($grade_item->is_hidden(1));
  345. }
  346. protected function sub_test_grade_item_is_category_item() {
  347. $grade_item = new grade_item($this->grade_items[3], false);
  348. $this->assertTrue(method_exists($grade_item, 'is_category_item'));
  349. $this->assertTrue($grade_item->is_category_item());
  350. }
  351. protected function sub_test_grade_item_is_course_item() {
  352. $grade_item = grade_item::fetch_course_item($this->courseid);
  353. $this->assertTrue(method_exists($grade_item, 'is_course_item'));
  354. $this->assertTrue($grade_item->is_course_item());
  355. }
  356. protected function sub_test_grade_item_fetch_course_item() {
  357. $grade_item = grade_item::fetch_course_item($this->courseid);
  358. $this->assertTrue(method_exists($grade_item, 'fetch_course_item'));
  359. $this->assertEquals($grade_item->itemtype, 'course');
  360. }
  361. protected function sub_test_grade_item_depends_on() {
  362. global $CFG;
  363. $origenableoutcomes = $CFG->enableoutcomes;
  364. $CFG->enableoutcomes = 0;
  365. $grade_item = new grade_item($this->grade_items[1], false);
  366. // Calculated grade dependency.
  367. $deps = $grade_item->depends_on();
  368. sort($deps, SORT_NUMERIC); // For comparison.
  369. $this->assertEquals(array($this->grade_items[0]->id), $deps);
  370. // Simulate depends on returns none when locked.
  371. $grade_item->locked = time();
  372. $grade_item->update();
  373. $deps = $grade_item->depends_on();
  374. sort($deps, SORT_NUMERIC); // For comparison.
  375. $this->assertEquals(array(), $deps);
  376. // Category dependency.
  377. $grade_item = new grade_item($this->grade_items[3], false);
  378. $deps = $grade_item->depends_on();
  379. sort($deps, SORT_NUMERIC); // For comparison.
  380. $res = array($this->grade_items[4]->id, $this->grade_items[5]->id);
  381. $this->assertEquals($res, $deps);
  382. $CFG->enableoutcomes = 1;
  383. $origgradeincludescalesinaggregation = $CFG->grade_includescalesinaggregation;
  384. $CFG->grade_includescalesinaggregation = 1;
  385. // Item in category with aggregate sub categories + $CFG->grade_includescalesinaggregation = 1.
  386. $grade_item = new grade_item($this->grade_items[12], false);
  387. $deps = $grade_item->depends_on();
  388. sort($deps, SORT_NUMERIC);
  389. $res = array($this->grade_items[15]->id, $this->grade_items[16]->id);
  390. $this->assertEquals($res, $deps);
  391. // Item in category with aggregate sub categories + $CFG->grade_includescalesinaggregation = 0.
  392. $CFG->grade_includescalesinaggregation = 0;
  393. $grade_item = new grade_item($this->grade_items[12], false);
  394. $deps = $grade_item->depends_on();
  395. sort($deps, SORT_NUMERIC);
  396. $res = array($this->grade_items[15]->id);
  397. $this->assertEquals($res, $deps);
  398. $CFG->grade_includescalesinaggregation = 1;
  399. // Outcome item in category with with aggregate sub categories.
  400. $CFG->enableoutcomes = 0;
  401. $grade_item = new grade_item($this->grade_items[12], false);
  402. $deps = $grade_item->depends_on();
  403. sort($deps, SORT_NUMERIC);
  404. $res = array($this->grade_items[15]->id, $this->grade_items[16]->id, $this->grade_items[17]->id);
  405. $this->assertEquals($res, $deps);
  406. $CFG->enableoutcomes = $origenableoutcomes;
  407. $CFG->grade_includescalesinaggregation = $origgradeincludescalesinaggregation;
  408. }
  409. protected function sub_test_refresh_grades() {
  410. // Testing with the grade item for a mod_assignment instance.
  411. $grade_item = new grade_item($this->grade_items[0], false);
  412. $this->assertTrue(method_exists($grade_item, 'refresh_grades'));
  413. $this->assertTrue($grade_item->refresh_grades());
  414. // Break the grade item and check error handling.
  415. $grade_item->iteminstance = 123456789;
  416. $this->assertFalse($grade_item->refresh_grades());
  417. $this->assertDebuggingCalled();
  418. }
  419. protected function sub_test_grade_item_is_calculated() {
  420. $grade_item = new grade_item($this->grade_items[1], false);
  421. $this->assertTrue(method_exists($grade_item, 'is_calculated'));
  422. $this->assertTrue($grade_item->is_calculated());
  423. $grade_item = new grade_item($this->grade_items[0], false);
  424. $this->assertFalse($grade_item->is_calculated());
  425. }
  426. protected function sub_test_grade_item_set_calculation() {
  427. $grade_item = new grade_item($this->grade_items[1], false);
  428. $this->assertTrue(method_exists($grade_item, 'set_calculation'));
  429. $grade_itemsource = new grade_item($this->grade_items[0], false);
  430. $grade_item->set_calculation('=[['.$grade_itemsource->idnumber.']]');
  431. $this->assertTrue(!empty($grade_item->needsupdate));
  432. $this->assertEquals('=##gi'.$grade_itemsource->id.'##', $grade_item->calculation);
  433. }
  434. protected function sub_test_grade_item_get_calculation() {
  435. $grade_item = new grade_item($this->grade_items[1], false);
  436. $this->assertTrue(method_exists($grade_item, 'get_calculation'));
  437. $grade_itemsource = new grade_item($this->grade_items[0], false);
  438. $denormalizedformula = str_replace('##gi'.$grade_itemsource->id.'##', '[['.$grade_itemsource->idnumber.']]', $this->grade_items[1]->calculation);
  439. $formula = $grade_item->get_calculation();
  440. $this->assertTrue(!empty($grade_item->needsupdate));
  441. $this->assertEquals($denormalizedformula, $formula);
  442. }
  443. public function sub_test_grade_item_compute() {
  444. $grade_item = grade_item::fetch(array('id'=>$this->grade_items[1]->id));
  445. $this->assertTrue(method_exists($grade_item, 'compute'));
  446. // Check the grade_grades in the array match those in the DB then delete $this->grade_items[1]'s grade_grades.
  447. $this->grade_grades[3] = grade_grade::fetch(array('id'=>$this->grade_grades[3]->id));
  448. $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[3]->id));
  449. $grade_grade->delete();
  450. $this->grade_grades[4] = grade_grade::fetch(array('id'=>$this->grade_grades[4]->id));
  451. $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[4]->id));
  452. $grade_grade->delete();
  453. $this->grade_grades[5] = grade_grade::fetch(array('id'=>$this->grade_grades[5]->id));
  454. $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[5]->id));
  455. $grade_grade->delete();
  456. // Recalculate the grades (its a calculation so pulls values from other grade_items) and reinsert them.
  457. $grade_item->compute();
  458. $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[3]->userid, 'itemid'=>$this->grade_grades[3]->itemid));
  459. $this->assertEquals($this->grade_grades[3]->finalgrade, $grade_grade->finalgrade);
  460. $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[4]->userid, 'itemid'=>$this->grade_grades[4]->itemid));
  461. $this->assertEquals($this->grade_grades[4]->finalgrade, $grade_grade->finalgrade);
  462. $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[5]->userid, 'itemid'=>$this->grade_grades[5]->itemid));
  463. $this->assertEquals($this->grade_grades[5]->finalgrade, $grade_grade->finalgrade);
  464. }
  465. protected function sub_test_update_final_grade() {
  466. // MDL-31713 Check that min and max are set on the grade_grade instance
  467. // if the grade is overridden before the activity has supplied a grade.
  468. $min = 2;
  469. $max = 8;
  470. // Create a brand new grade item.
  471. $grade_item = new grade_item();
  472. $this->assertTrue(method_exists($grade_item, 'insert'));
  473. $grade_item->courseid = $this->courseid;
  474. $grade_item->categoryid = $this->grade_categories[1]->id;
  475. $grade_item->itemname = 'brand new unit test grade item';
  476. $grade_item->itemtype = 'mod';
  477. $grade_item->itemmodule = 'quiz';
  478. $grade_item->iteminfo = 'Grade item used for unit testing';
  479. $grade_item->grademin = $min;
  480. $grade_item->grademax = $max;
  481. $grade_item->insert();
  482. // Override the student grade.
  483. $grade_item->update_final_grade($this->user[1]->id, 7, 'gradebook', '', FORMAT_MOODLE);
  484. // Check the student's grade has the correct min and max grade.
  485. $grade_grade = grade_grade::fetch(array('userid'=>$this->user[1]->id, 'itemid'=>$grade_item->id));
  486. $this->assertEquals($min, $grade_grade->rawgrademin);
  487. $this->assertEquals($max, $grade_grade->rawgrademax);
  488. }
  489. protected function sub_test_grade_item_can_control_visibility() {
  490. // Grade item 0 == Course module 0 == Assignment.
  491. $grade_item = new grade_item($this->grade_items[0], false);
  492. $this->assertTrue($grade_item->can_control_visibility());
  493. // Grade item == Course module 7 == Quiz.
  494. $grade_item = new grade_item($this->grade_items[11], false);
  495. $this->assertFalse($grade_item->can_control_visibility());
  496. }
  497. /**
  498. * Test the {@link grade_item::fix_duplicate_sortorder() function with
  499. * faked duplicate sortorder data.
  500. */
  501. public function sub_test_grade_item_fix_sortorder() {
  502. global $DB;
  503. $this->resetAfterTest(true);
  504. // Each set is used for filling the db with fake data and will be representing the result of query:
  505. // "SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id".
  506. $testsets = array(
  507. // Items that need no action.
  508. array(1,2,3),
  509. array(5,6,7),
  510. array(7,6,1,3,2,5),
  511. // Items with sortorder duplicates
  512. array(1,2,2,3,3,4,5),
  513. // Only one sortorder duplicate.
  514. array(1,1),
  515. array(3,3),
  516. // Non-sequential sortorders with one or multiple duplicates.
  517. array(3,3,7,5,6,6,9,10,8,3),
  518. array(7,7,3),
  519. array(3,4,5,3,5,4,7,1)
  520. );
  521. $origsequence = array();
  522. // Generate the data and remember the initial sequence or items.
  523. foreach ($testsets as $testset) {
  524. $course = $this->getDataGenerator()->create_course();
  525. foreach ($testset as $sortorder) {
  526. $this->insert_fake_grade_item_sortorder($course->id, $sortorder);
  527. }
  528. $DB->get_records('grade_items');
  529. $origsequence[$course->id] = $DB->get_fieldset_sql("SELECT id FROM {grade_items} ".
  530. "WHERE courseid = ? ORDER BY sortorder, id", array($course->id));
  531. }
  532. $duplicatedetectionsql = "SELECT courseid, sortorder
  533. FROM {grade_items}
  534. WHERE courseid = :courseid
  535. GROUP BY courseid, sortorder
  536. HAVING COUNT(id) > 1";
  537. // Do the work.
  538. foreach ($origsequence as $courseid => $ignore) {
  539. grade_item::fix_duplicate_sortorder($courseid);
  540. // Verify that no duplicates are left in the database.
  541. $dupes = $DB->record_exists_sql($duplicatedetectionsql, array('courseid' => $courseid));
  542. $this->assertFalse($dupes);
  543. }
  544. // Verify that sequences are exactly the same as they were before upgrade script.
  545. $idx = 0;
  546. foreach ($origsequence as $courseid => $sequence) {
  547. if (count(($testsets[$idx])) == count(array_unique($testsets[$idx]))) {
  548. // If there were no duplicates for this course verify that sortorders are not modified.
  549. $newsortorders = $DB->get_fieldset_sql("SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id", array($courseid));
  550. $this->assertEquals($testsets[$idx], $newsortorders);
  551. }
  552. $newsequence = $DB->get_fieldset_sql("SELECT id FROM {grade_items} ".
  553. "WHERE courseid = ? ORDER BY sortorder, id", array($courseid));
  554. $this->assertEquals($sequence, $newsequence,
  555. "Sequences do not match for test set $idx : ".join(',', $testsets[$idx]));
  556. $idx++;
  557. }
  558. }
  559. /**
  560. * Populate some fake grade items into the database with specified
  561. * sortorder and course id.
  562. *
  563. * NOTE: This function doesn't make much attempt to respect the
  564. * gradebook internals, its simply used to fake some data for
  565. * testing the upgradelib function. Please don't use it for other
  566. * purposes.
  567. *
  568. * @param int $courseid id of course
  569. * @param int $sortorder numeric sorting order of item
  570. * @return stdClass grade item object from the database.
  571. */
  572. private function insert_fake_grade_item_sortorder($courseid, $sortorder) {
  573. global $DB, $CFG;
  574. require_once($CFG->libdir.'/gradelib.php');
  575. $item = new stdClass();
  576. $item->courseid = $courseid;
  577. $item->sortorder = $sortorder;
  578. $item->gradetype = GRADE_TYPE_VALUE;
  579. $item->grademin = 30;
  580. $item->grademax = 110;
  581. $item->itemnumber = 1;
  582. $item->iteminfo = '';
  583. $item->timecreated = time();
  584. $item->timemodified = time();
  585. $item->id = $DB->insert_record('grade_items', $item);
  586. return $DB->get_record('grade_items', array('id' => $item->id));
  587. }
  588. }