Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | Ultima modificación | Ver Log |

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