PageRenderTime 67ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/grade/tests/grade_category_test.php

https://github.com/thepurpleblob/gumoodle
PHP | 706 lines | 479 code | 120 blank | 107 comment | 3 complexity | 6dc482187b7dbb961dc84ae3473e767a MD5 | raw file
Possible License(s): Apache-2.0, GPL-3.0, BSD-3-Clause, LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-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 grade_category_testcase extends grade_base_testcase {
  25. public function test_grade_category() {
  26. $this->sub_test_grade_category_construct();
  27. $this->sub_test_grade_category_build_path();
  28. $this->sub_test_grade_category_fetch();
  29. $this->sub_test_grade_category_fetch_all();
  30. $this->sub_test_grade_category_update();
  31. $this->sub_test_grade_category_delete();
  32. $this->sub_test_grade_category_insert();
  33. $this->sub_test_grade_category_qualifies_for_regrading();
  34. $this->sub_test_grade_category_force_regrading();
  35. $this->sub_test_grade_category_aggregate_grades();
  36. $this->sub_test_grade_category_apply_limit_rules();
  37. $this->sub_test_grade_category_is_aggregationcoef_used();
  38. $this->sub_test_grade_category_fetch_course_tree();
  39. $this->sub_test_grade_category_get_children();
  40. $this->sub_test_grade_category_load_grade_item();
  41. $this->sub_test_grade_category_get_grade_item();
  42. $this->sub_test_grade_category_load_parent_category();
  43. $this->sub_test_grade_category_get_parent_category();
  44. $this->sub_test_grade_category_get_name();
  45. $this->sub_test_grade_category_set_parent();
  46. $this->sub_test_grade_category_get_final();
  47. $this->sub_test_grade_category_get_sortorder();
  48. $this->sub_test_grade_category_set_sortorder();
  49. $this->sub_test_grade_category_is_editable();
  50. $this->sub_test_grade_category_move_after_sortorder();
  51. $this->sub_test_grade_category_is_course_category();
  52. $this->sub_test_grade_category_fetch_course_category();
  53. $this->sub_test_grade_category_is_locked();
  54. $this->sub_test_grade_category_set_locked();
  55. $this->sub_test_grade_category_is_hidden();
  56. $this->sub_test_grade_category_set_hidden();
  57. //this won't work until MDL-11837 is complete
  58. //$this->sub_test_grade_category_generate_grades();
  59. //do this last as adding a second course category messes up the data
  60. $this->sub_test_grade_category_insert_course_category();
  61. }
  62. //adds 3 new grade categories at various depths
  63. protected function sub_test_grade_category_construct() {
  64. $course_category = grade_category::fetch_course_category($this->courseid);
  65. $params = new stdClass();
  66. $params->courseid = $this->courseid;
  67. $params->fullname = 'unittestcategory4';
  68. $grade_category = new grade_category($params, false);
  69. $grade_category->insert();
  70. $this->grade_categories[] = $grade_category;
  71. $this->assertEquals($params->courseid, $grade_category->courseid);
  72. $this->assertEquals($params->fullname, $grade_category->fullname);
  73. $this->assertEquals(2, $grade_category->depth);
  74. $this->assertEquals("/$course_category->id/$grade_category->id/", $grade_category->path);
  75. $parentpath = $grade_category->path;
  76. // Test a child category
  77. $params->parent = $grade_category->id;
  78. $params->fullname = 'unittestcategory5';
  79. $grade_category = new grade_category($params, false);
  80. $grade_category->insert();
  81. $this->grade_categories[] = $grade_category;
  82. $this->assertEquals(3, $grade_category->depth);
  83. $this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path);
  84. $parentpath = $grade_category->path;
  85. // Test a third depth category
  86. $params->parent = $grade_category->id;
  87. $params->fullname = 'unittestcategory6';
  88. $grade_category = new grade_category($params, false);
  89. $grade_category->insert();
  90. $this->grade_categories[50] = $grade_category;//going to delete this one later hence the special index
  91. $this->assertEquals(4, $grade_category->depth);
  92. $this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path);
  93. }
  94. protected function sub_test_grade_category_build_path() {
  95. $grade_category = new grade_category($this->grade_categories[1]);
  96. $this->assertTrue(method_exists($grade_category, 'build_path'));
  97. $path = grade_category::build_path($grade_category);
  98. $this->assertEquals($grade_category->path, $path);
  99. }
  100. protected function sub_test_grade_category_fetch() {
  101. $grade_category = new grade_category();
  102. $this->assertTrue(method_exists($grade_category, 'fetch'));
  103. $grade_category = grade_category::fetch(array('id'=>$this->grade_categories[0]->id));
  104. $this->assertEquals($this->grade_categories[0]->id, $grade_category->id);
  105. $this->assertEquals($this->grade_categories[0]->fullname, $grade_category->fullname);
  106. }
  107. protected function sub_test_grade_category_fetch_all() {
  108. $grade_category = new grade_category();
  109. $this->assertTrue(method_exists($grade_category, 'fetch_all'));
  110. $grade_categories = grade_category::fetch_all(array('courseid'=>$this->courseid));
  111. $this->assertEquals(count($this->grade_categories), count($grade_categories)-1);
  112. }
  113. protected function sub_test_grade_category_update() {
  114. global $DB;
  115. $grade_category = new grade_category($this->grade_categories[0]);
  116. $this->assertTrue(method_exists($grade_category, 'update'));
  117. $grade_category->fullname = 'Updated info for this unittest grade_category';
  118. $grade_category->path = null; // path must be recalculated if missing
  119. $grade_category->depth = null;
  120. $grade_category->aggregation = GRADE_AGGREGATE_MAX; // should force regrading
  121. $grade_item = $grade_category->get_grade_item();
  122. $this->assertEquals(0, $grade_item->needsupdate);
  123. $this->assertTrue($grade_category->update());
  124. $fullname = $DB->get_field('grade_categories', 'fullname', array('id' => $this->grade_categories[0]->id));
  125. $this->assertEquals($grade_category->fullname, $fullname);
  126. $path = $DB->get_field('grade_categories', 'path', array('id' => $this->grade_categories[0]->id));
  127. $this->assertEquals($grade_category->path, $path);
  128. $depth = $DB->get_field('grade_categories', 'depth', array('id' => $this->grade_categories[0]->id));
  129. $this->assertEquals($grade_category->depth, $depth);
  130. $grade_item = $grade_category->get_grade_item();
  131. $this->assertEquals(1, $grade_item->needsupdate);
  132. }
  133. protected function sub_test_grade_category_delete() {
  134. global $DB;
  135. $grade_category = new grade_category($this->grade_categories[50]);
  136. $this->assertTrue(method_exists($grade_category, 'delete'));
  137. $this->assertTrue($grade_category->delete());
  138. $this->assertFalse($DB->get_record('grade_categories', array('id' => $grade_category->id)));
  139. }
  140. protected function sub_test_grade_category_insert() {
  141. $course_category = grade_category::fetch_course_category($this->courseid);
  142. $grade_category = new grade_category();
  143. $this->assertTrue(method_exists($grade_category, 'insert'));
  144. $grade_category->fullname = 'unittestcategory4';
  145. $grade_category->courseid = $this->courseid;
  146. $grade_category->aggregation = GRADE_AGGREGATE_MEAN;
  147. $grade_category->aggregateonlygraded = 1;
  148. $grade_category->keephigh = 100;
  149. $grade_category->droplow = 10;
  150. $grade_category->hidden = 0;
  151. $grade_category->parent = $this->grade_categories[1]->id; //sub_test_grade_category_delete() removed the category at 0
  152. $grade_category->insert();
  153. $this->assertEquals('/'.$course_category->id.'/'.$this->grade_categories[1]->parent.'/'.$this->grade_categories[1]->id.'/'.$grade_category->id.'/', $grade_category->path);
  154. $this->assertEquals(4, $grade_category->depth);
  155. $last_grade_category = end($this->grade_categories);
  156. $this->assertFalse(empty($grade_category->grade_item));
  157. $this->assertEquals($grade_category->id, $grade_category->grade_item->iteminstance);
  158. $this->assertEquals('category', $grade_category->grade_item->itemtype);
  159. $this->assertEquals($grade_category->id, $last_grade_category->id + 1);
  160. $this->assertFalse(empty($grade_category->timecreated));
  161. $this->assertFalse(empty($grade_category->timemodified));
  162. }
  163. protected function sub_test_grade_category_qualifies_for_regrading() {
  164. $grade_category = new grade_category($this->grade_categories[1]);
  165. $this->assertTrue(method_exists($grade_category, 'qualifies_for_regrading'));
  166. $this->assertFalse($grade_category->qualifies_for_regrading());
  167. $grade_category->aggregation = GRADE_AGGREGATE_MAX;
  168. $this->assertTrue($grade_category->qualifies_for_regrading());
  169. $grade_category = new grade_category($this->grade_categories[1]);
  170. $grade_category->droplow = 99;
  171. $this->assertTrue($grade_category->qualifies_for_regrading());
  172. $grade_category = new grade_category($this->grade_categories[1]);
  173. $grade_category->keephigh = 99;
  174. $this->assertTrue($grade_category->qualifies_for_regrading());
  175. }
  176. protected function sub_test_grade_category_force_regrading() {
  177. $grade_category = new grade_category($this->grade_categories[1]);
  178. $this->assertTrue(method_exists($grade_category, 'force_regrading'));
  179. $grade_category->load_grade_item();
  180. $this->assertEquals(0, $grade_category->grade_item->needsupdate);
  181. $grade_category->force_regrading();
  182. $grade_category->grade_item = null;
  183. $grade_category->load_grade_item();
  184. $this->assertEquals(1, $grade_category->grade_item->needsupdate);
  185. }
  186. /**
  187. * Tests the calculation of grades using the various aggregation methods with and without hidden grades
  188. * This will not work entirely until MDL-11837 is done
  189. * @global type $DB
  190. */
  191. protected function sub_test_grade_category_generate_grades() {
  192. global $DB;
  193. //inserting some special grade items to make testing the final grade calculation easier
  194. $params->courseid = $this->courseid;
  195. $params->fullname = 'unittestgradecalccategory';
  196. $params->aggregation = GRADE_AGGREGATE_MEAN;
  197. $params->aggregateonlygraded = 0;
  198. $grade_category = new grade_category($params, false);
  199. $grade_category->insert();
  200. $this->assertTrue(method_exists($grade_category, 'generate_grades'));
  201. $grade_category->load_grade_item();
  202. $cgi = $grade_category->get_grade_item();
  203. $cgi->grademin = 0;
  204. $cgi->grademax = 20;//3 grade items out of 10 but category is out of 20 to force scaling to occur
  205. $cgi->update();
  206. //3 grade items each with a maximum grade of 10
  207. $grade_items = array();
  208. for ($i=0; $i<3; $i++) {
  209. $grade_items[$i] = new grade_item();
  210. $grade_items[$i]->courseid = $this->courseid;
  211. $grade_items[$i]->categoryid = $grade_category->id;
  212. $grade_items[$i]->itemname = 'manual grade_item '.$i;
  213. $grade_items[$i]->itemtype = 'manual';
  214. $grade_items[$i]->itemnumber = 0;
  215. $grade_items[$i]->needsupdate = false;
  216. $grade_items[$i]->gradetype = GRADE_TYPE_VALUE;
  217. $grade_items[$i]->grademin = 0;
  218. $grade_items[$i]->grademax = 10;
  219. $grade_items[$i]->iteminfo = 'Manual grade item used for unit testing';
  220. $grade_items[$i]->timecreated = time();
  221. $grade_items[$i]->timemodified = time();
  222. //used as the weight by weighted mean and as extra credit by mean with extra credit
  223. //Will be 0, 1 and 2
  224. $grade_items[$i]->aggregationcoef = $i;
  225. $grade_items[$i]->insert();
  226. }
  227. //a grade for each grade item
  228. $grade_grades = array();
  229. for ($i=0; $i<3; $i++) {
  230. $grade_grades[$i] = new grade_grade();
  231. $grade_grades[$i]->itemid = $grade_items[$i]->id;
  232. $grade_grades[$i]->userid = $this->userid;
  233. $grade_grades[$i]->rawgrade = ($i+1)*2;//produce grade grades of 2, 4 and 6
  234. $grade_grades[$i]->finalgrade = ($i+1)*2;
  235. $grade_grades[$i]->timecreated = time();
  236. $grade_grades[$i]->timemodified = time();
  237. $grade_grades[$i]->information = '1 of 2 grade_grades';
  238. $grade_grades[$i]->informationformat = FORMAT_PLAIN;
  239. $grade_grades[$i]->feedback = 'Good, but not good enough..';
  240. $grade_grades[$i]->feedbackformat = FORMAT_PLAIN;
  241. $grade_grades[$i]->insert();
  242. }
  243. //3 grade items with 1 grade_grade each.
  244. //grade grades have the values 2, 4 and 6
  245. //First correct answer is the aggregate with all 3 grades
  246. //Second correct answer is with the first grade (value 2) hidden
  247. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEDIAN, 'GRADE_AGGREGATE_MEDIAN', 8, 8);
  248. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MAX, 'GRADE_AGGREGATE_MAX', 12, 12);
  249. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MODE, 'GRADE_AGGREGATE_MODE', 12, 12);
  250. //weighted mean. note grade totals are rounded to an int to prevent rounding discrepancies. correct final grade isnt actually exactly 10
  251. //3 items with grades 2, 4 and 6 with weights 0, 1 and 2 and all out of 10. then doubled to be out of 20.
  252. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN, 'GRADE_AGGREGATE_WEIGHTED_MEAN', 10, 10);
  253. //simple weighted mean
  254. //3 items with grades 2, 4 and 6 equally weighted and all out of 10. then doubled to be out of 20.
  255. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN2, 'GRADE_AGGREGATE_WEIGHTED_MEAN2', 8, 10);
  256. //mean of grades with extra credit
  257. //3 items with grades 2, 4 and 6 with extra credit 0, 1 and 2 equally weighted and all out of 10. then doubled to be out of 20.
  258. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_EXTRACREDIT_MEAN, 'GRADE_AGGREGATE_EXTRACREDIT_MEAN', 10, 13);
  259. //aggregation tests the are affected by a hidden grade currently dont work as we dont store the altered grade in the database
  260. //instead an in memory recalculation is done. This should be remedied by MDL-11837
  261. //fails with 1 grade hidden. still reports 8 as being correct
  262. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEAN, 'GRADE_AGGREGATE_MEAN', 8, 10);
  263. //fails with 1 grade hidden. still reports 4 as being correct
  264. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MIN, 'GRADE_AGGREGATE_MIN', 4, 8);
  265. //fails with 1 grade hidden. still reports 12 as being correct
  266. $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_SUM, 'GRADE_AGGREGATE_SUM', 12, 10);
  267. }
  268. /**
  269. * Test grade category aggregation using the supplied grade objects and aggregation method
  270. * @param grade_category $grade_category the category to be tested
  271. * @param array $grade_items array of instance of grade_item
  272. * @param array $grade_grades array of instances of grade_grade
  273. * @param int $aggmethod the aggregation method to apply ie GRADE_AGGREGATE_MEAN
  274. * @param string $aggmethodname the name of the aggregation method to apply. Used to display any test failure messages
  275. * @param int $correct1 the correct final grade for the category with NO items hidden
  276. * @param int $correct2 the correct final grade for the category with the grade at $grade_grades[0] hidden
  277. * @return void
  278. */
  279. protected function helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, $aggmethod, $aggmethodname, $correct1, $correct2) {
  280. global $DB;
  281. $grade_category->aggregation = $aggmethod;
  282. $grade_category->update();
  283. //check grade_item isnt hidden from a previous test
  284. $grade_items[0]->set_hidden(0, true);
  285. $this->helper_test_grade_aggregation_result($grade_category, $correct1, 'Testing aggregation method('.$aggmethodname.') with no items hidden %s');
  286. //hide the grade item with grade of 2
  287. $grade_items[0]->set_hidden(1, true);
  288. $this->helper_test_grade_aggregation_result($grade_category, $correct2, 'Testing aggregation method('.$aggmethodname.') with 1 item hidden %s');
  289. }
  290. /**
  291. * Verify the value of the category grade item for $this->userid
  292. * @param grade_category $grade_category the category to be tested
  293. * @param int $correctgrade the expected grade
  294. * @param string msg The message that should be displayed if the correct grade is not found
  295. * @return void
  296. */
  297. protected function helper_test_grade_aggregation_result($grade_category, $correctgrade, $msg) {
  298. global $DB;
  299. $category_grade_item = $grade_category->get_grade_item();
  300. //this creates all the grade_grades we need
  301. grade_regrade_final_grades($this->courseid);
  302. $grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid));
  303. $this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax);
  304. $this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg);
  305. /*
  306. * TODO this doesnt work as the grade_grades created by $grade_category->generate_grades(); dont
  307. * observe the category's max grade
  308. //delete the grade_grades for the category itself and check they get recreated correctly
  309. $DB->delete_records('grade_grades', array('itemid'=>$category_grade_item->id));
  310. $grade_category->generate_grades();
  311. $grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid));
  312. $this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax);
  313. $this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg);
  314. *
  315. */
  316. }
  317. protected function sub_test_grade_category_aggregate_grades() {
  318. $category = new grade_category($this->grade_categories[0]);
  319. $this->assertTrue(method_exists($category, 'aggregate_grades'));
  320. // tested more fully via test_grade_category_generate_grades()
  321. }
  322. protected function sub_test_grade_category_apply_limit_rules() {
  323. $items[$this->grade_items[0]->id] = new grade_item($this->grade_items[0], false);
  324. $items[$this->grade_items[1]->id] = new grade_item($this->grade_items[1], false);
  325. $items[$this->grade_items[2]->id] = new grade_item($this->grade_items[2], false);
  326. $items[$this->grade_items[4]->id] = new grade_item($this->grade_items[4], false);
  327. // Test excluding the lowest 2 out of 4 grades from aggregation with no 0 grades
  328. $category = new grade_category();
  329. $category->droplow = 2;
  330. $grades = array($this->grade_items[0]->id=>5.374,
  331. $this->grade_items[1]->id=>9.4743,
  332. $this->grade_items[2]->id=>2.5474,
  333. $this->grade_items[4]->id=>7.3754);
  334. $category->apply_limit_rules($grades, $items);
  335. $this->assertEquals(count($grades), 2);
  336. $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743);
  337. $this->assertEquals($grades[$this->grade_items[4]->id], 7.3754);
  338. // Test aggregating only the highest 1 out of 4 grades
  339. $category = new grade_category();
  340. $category->keephigh = 1;
  341. $category->droplow = 0;
  342. $grades = array($this->grade_items[0]->id=>5.374,
  343. $this->grade_items[1]->id=>9.4743,
  344. $this->grade_items[2]->id=>2.5474,
  345. $this->grade_items[4]->id=>7.3754);
  346. $category->apply_limit_rules($grades, $items);
  347. $this->assertEquals(count($grades), 1);
  348. $grade = reset($grades);
  349. $this->assertEquals(9.4743, $grade);
  350. // Test excluding the lowest 2 out of 4 grades from aggregation with no 0 grades
  351. // An extra credit grade item should be kept even if droplow means it would otherwise be excluded
  352. $category = new grade_category();
  353. $category->droplow = 2;
  354. $category->aggregation = GRADE_AGGREGATE_SUM;
  355. $items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit"
  356. $grades = array($this->grade_items[0]->id=>5.374,
  357. $this->grade_items[1]->id=>9.4743,
  358. $this->grade_items[2]->id=>2.5474,
  359. $this->grade_items[4]->id=>7.3754);
  360. $category->apply_limit_rules($grades, $items);
  361. $this->assertEquals(count($grades), 2);
  362. $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743);
  363. $this->assertEquals($grades[$this->grade_items[2]->id], 2.5474);
  364. // Test only aggregating the highest 1 out of 4 grades
  365. // An extra credit grade item is retained in addition to the highest grade
  366. $category = new grade_category();
  367. $category->keephigh = 1;
  368. $category->droplow = 0;
  369. $category->aggregation = GRADE_AGGREGATE_SUM;
  370. $items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit"
  371. $grades = array($this->grade_items[0]->id=>5.374,
  372. $this->grade_items[1]->id=>9.4743,
  373. $this->grade_items[2]->id=>2.5474,
  374. $this->grade_items[4]->id=>7.3754);
  375. $category->apply_limit_rules($grades, $items);
  376. $this->assertEquals(count($grades), 2);
  377. $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743);
  378. $this->assertEquals($grades[$this->grade_items[2]->id], 2.5474);
  379. // Test excluding the lowest 1 out of 4 grades from aggregation with two 0 grades
  380. $items[$this->grade_items[2]->id]->aggregationcoef = 0; // Undo marking grade item 2 as "extra credit"
  381. $category = new grade_category();
  382. $category->droplow = 1;
  383. $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // simple weighted mean
  384. $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
  385. $this->grade_items[1]->id=>5, // 5 out of 100
  386. $this->grade_items[2]->id=>2, // 0 out of 6
  387. $this->grade_items[4]->id=>0); // 0 out of 100
  388. $category->apply_limit_rules($grades, $items);
  389. $this->assertEquals(count($grades), 3);
  390. $this->assertEquals($grades[$this->grade_items[1]->id], 5);
  391. $this->assertEquals($grades[$this->grade_items[2]->id], 2);
  392. $this->assertEquals($grades[$this->grade_items[4]->id], 0);
  393. // Test excluding the lowest 2 out of 4 grades from aggregation with three 0 grades
  394. $category = new grade_category();
  395. $category->droplow = 2;
  396. $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // simple weighted mean
  397. $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
  398. $this->grade_items[1]->id=>5, // 5 out of 100
  399. $this->grade_items[2]->id=>0, // 0 out of 6
  400. $this->grade_items[4]->id=>0); // 0 out of 100. Should be excluded from aggregation.
  401. $category->apply_limit_rules($grades, $items);
  402. $this->assertEquals(count($grades), 2);
  403. $this->assertEquals($grades[$this->grade_items[1]->id], 5);
  404. $this->assertEquals($grades[$this->grade_items[2]->id], 0);
  405. // Test excluding the lowest 5 out of 4 grades from aggregation
  406. // Just to check we handle this sensibly
  407. $category = new grade_category();
  408. $category->droplow = 5;
  409. $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // simple weighted mean
  410. $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
  411. $this->grade_items[1]->id=>5, // 5 out of 100
  412. $this->grade_items[2]->id=>6, // 6 out of 6
  413. $this->grade_items[4]->id=>1);// 1 out of 100. Should be excluded from aggregation.
  414. $category->apply_limit_rules($grades, $items);
  415. $this->assertEquals(count($grades), 0);
  416. // Test excluding the lowest 4 out of 4 grades from aggregation with one marked as extra credit
  417. $category = new grade_category();
  418. $category->droplow = 4;
  419. $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // simple weighted mean
  420. $items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit"
  421. $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
  422. $this->grade_items[1]->id=>5, // 5 out of 100. Should be excluded from aggregation.
  423. $this->grade_items[2]->id=>6, // 6 out of 6. Extra credit. Should be retained.
  424. $this->grade_items[4]->id=>1);// 1 out of 100. Should be excluded from aggregation.
  425. $category->apply_limit_rules($grades, $items);
  426. $this->assertEquals(count($grades), 1);
  427. $this->assertEquals($grades[$this->grade_items[2]->id], 6);
  428. }
  429. /**
  430. * TODO implement
  431. */
  432. protected function sub_test_grade_category_is_aggregationcoef_used() {
  433. }
  434. protected function sub_test_grade_category_fetch_course_tree() {
  435. $category = new grade_category();
  436. $this->assertTrue(method_exists($category, 'fetch_course_tree'));
  437. //TODO: add some tests
  438. }
  439. protected function sub_test_grade_category_get_children() {
  440. $course_category = grade_category::fetch_course_category($this->courseid);
  441. $category = new grade_category($this->grade_categories[0]);
  442. $this->assertTrue(method_exists($category, 'get_children'));
  443. $children_array = $category->get_children(0);
  444. $this->assertTrue(is_array($children_array));
  445. $this->assertFalse(empty($children_array[2]));
  446. $this->assertFalse(empty($children_array[2]['object']));
  447. $this->assertFalse(empty($children_array[2]['children']));
  448. $this->assertEquals($this->grade_categories[1]->id, $children_array[2]['object']->id);
  449. $this->assertEquals($this->grade_categories[2]->id, $children_array[5]['object']->id);
  450. $this->assertEquals($this->grade_items[0]->id, $children_array[2]['children'][3]['object']->id);
  451. $this->assertEquals($this->grade_items[1]->id, $children_array[2]['children'][4]['object']->id);
  452. $this->assertEquals($this->grade_items[2]->id, $children_array[5]['children'][6]['object']->id);
  453. }
  454. protected function sub_test_grade_category_load_grade_item() {
  455. $category = new grade_category($this->grade_categories[0]);
  456. $this->assertTrue(method_exists($category, 'load_grade_item'));
  457. $this->assertEquals(null, $category->grade_item);
  458. $category->load_grade_item();
  459. $this->assertEquals($this->grade_items[3]->id, $category->grade_item->id);
  460. }
  461. protected function sub_test_grade_category_get_grade_item() {
  462. $category = new grade_category($this->grade_categories[0]);
  463. $this->assertTrue(method_exists($category, 'get_grade_item'));
  464. $grade_item = $category->get_grade_item();
  465. $this->assertEquals($this->grade_items[3]->id, $grade_item->id);
  466. }
  467. protected function sub_test_grade_category_load_parent_category() {
  468. $category = new grade_category($this->grade_categories[1]);
  469. $this->assertTrue(method_exists($category, 'load_parent_category'));
  470. $this->assertEquals(null, $category->parent_category);
  471. $category->load_parent_category();
  472. $this->assertEquals($this->grade_categories[0]->id, $category->parent_category->id);
  473. }
  474. protected function sub_test_grade_category_get_parent_category() {
  475. $category = new grade_category($this->grade_categories[1]);
  476. $this->assertTrue(method_exists($category, 'get_parent_category'));
  477. $parent_category = $category->get_parent_category();
  478. $this->assertEquals($this->grade_categories[0]->id, $parent_category->id);
  479. }
  480. protected function sub_test_grade_category_get_name() {
  481. $category = new grade_category($this->grade_categories[0]);
  482. $this->assertTrue(method_exists($category, 'get_name'));
  483. $this->assertEquals($this->grade_categories[0]->fullname, $category->get_name());
  484. }
  485. protected function sub_test_grade_category_set_parent() {
  486. $category = new grade_category($this->grade_categories[1]);
  487. $this->assertTrue(method_exists($category, 'set_parent'));
  488. // TODO: implement detailed tests
  489. $course_category = grade_category::fetch_course_category($this->courseid);
  490. $this->assertTrue($category->set_parent($course_category->id));
  491. $this->assertEquals($course_category->id, $category->parent);
  492. }
  493. protected function sub_test_grade_category_get_final() {
  494. $category = new grade_category($this->grade_categories[0]);
  495. $this->assertTrue(method_exists($category, 'get_final'));
  496. $category->load_grade_item();
  497. $this->assertEquals($category->get_final(), $category->grade_item->get_final());
  498. }
  499. protected function sub_test_grade_category_get_sortorder() {
  500. $category = new grade_category($this->grade_categories[0]);
  501. $this->assertTrue(method_exists($category, 'get_sortorder'));
  502. $category->load_grade_item();
  503. $this->assertEquals($category->get_sortorder(), $category->grade_item->get_sortorder());
  504. }
  505. protected function sub_test_grade_category_set_sortorder() {
  506. $category = new grade_category($this->grade_categories[0]);
  507. $this->assertTrue(method_exists($category, 'set_sortorder'));
  508. $category->load_grade_item();
  509. $this->assertEquals($category->set_sortorder(10), $category->grade_item->set_sortorder(10));
  510. }
  511. protected function sub_test_grade_category_move_after_sortorder() {
  512. $category = new grade_category($this->grade_categories[0]);
  513. $this->assertTrue(method_exists($category, 'move_after_sortorder'));
  514. $category->load_grade_item();
  515. $this->assertEquals($category->move_after_sortorder(10), $category->grade_item->move_after_sortorder(10));
  516. }
  517. protected function sub_test_grade_category_is_course_category() {
  518. $category = grade_category::fetch_course_category($this->courseid);
  519. $this->assertTrue(method_exists($category, 'is_course_category'));
  520. $this->assertTrue($category->is_course_category());
  521. }
  522. protected function sub_test_grade_category_fetch_course_category() {
  523. $category = new grade_category();
  524. $this->assertTrue(method_exists($category, 'fetch_course_category'));
  525. $category = grade_category::fetch_course_category($this->courseid);
  526. $this->assertTrue(empty($category->parent));
  527. }
  528. /**
  529. * TODO implement
  530. */
  531. protected function sub_test_grade_category_is_editable() {
  532. }
  533. protected function sub_test_grade_category_is_locked() {
  534. $category = new grade_category($this->grade_categories[0]);
  535. $this->assertTrue(method_exists($category, 'is_locked'));
  536. $category->load_grade_item();
  537. $this->assertEquals($category->is_locked(), $category->grade_item->is_locked());
  538. }
  539. protected function sub_test_grade_category_set_locked() {
  540. $category = new grade_category($this->grade_categories[0]);
  541. $this->assertTrue(method_exists($category, 'set_locked'));
  542. //will return false as cannot lock a grade that needs updating
  543. $this->assertFalse($category->set_locked(1));
  544. grade_regrade_final_grades($this->courseid);
  545. //get the category from the db again
  546. $category = new grade_category($this->grade_categories[0]);
  547. $this->assertTrue($category->set_locked(1));
  548. }
  549. protected function sub_test_grade_category_is_hidden() {
  550. $category = new grade_category($this->grade_categories[0]);
  551. $this->assertTrue(method_exists($category, 'is_hidden'));
  552. $category->load_grade_item();
  553. $this->assertEquals($category->is_hidden(), $category->grade_item->is_hidden());
  554. }
  555. protected function sub_test_grade_category_set_hidden() {
  556. $category = new grade_category($this->grade_categories[0]);
  557. $this->assertTrue(method_exists($category, 'set_hidden'));
  558. $category->set_hidden(1);
  559. $category->load_grade_item();
  560. $this->assertEquals(true, $category->grade_item->is_hidden());
  561. }
  562. //beware: adding a duplicate course category messes up the data in a way that's hard to recover from
  563. protected function sub_test_grade_category_insert_course_category() {
  564. $grade_category = new grade_category();
  565. $this->assertTrue(method_exists($grade_category, 'insert_course_category'));
  566. $id = $grade_category->insert_course_category($this->courseid);
  567. $this->assertNotNull($id);
  568. $this->assertEquals('?', $grade_category->fullname);
  569. $this->assertEquals(GRADE_AGGREGATE_WEIGHTED_MEAN2, $grade_category->aggregation);
  570. $this->assertEquals("/$id/", $grade_category->path);
  571. $this->assertEquals(1, $grade_category->depth);
  572. $this->assertNull($grade_category->parent);
  573. }
  574. protected function generate_random_raw_grade($item, $userid) {
  575. $grade = new grade_grade();
  576. $grade->itemid = $item->id;
  577. $grade->userid = $userid;
  578. $grade->grademin = 0;
  579. $grade->grademax = 1;
  580. $valuetype = "grade$item->gradetype";
  581. $grade->rawgrade = rand(0, 1000) / 1000;
  582. $grade->insert();
  583. return $grade->rawgrade;
  584. }
  585. }