Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | 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
 */
1441 ariadna 31
final class grade_item_test extends \grade_base_testcase {
11 efrain 32
    public function test_grade_item(): void {
1 efrain 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
 
1441 ariadna 527
    /**
528
     * Test is_agradable method.
529
     *
530
     * @param int $type the itemtype to test.
531
     * @param bool $expected result for is_gradable() method.
532
     * @return void
533
     */
534
    #[\PHPUnit\Framework\Attributes\DataProvider('provider_itemtype_is_gradable')]
535
    public function sub_test_grade_item_is_gradable(int $type, bool $expected): void {
536
        $gradeitem = new \grade_item($this->grade_items[0], false);
537
        $gradeitem->gradetype = $type;
538
        $this->assertEquals($expected, $gradeitem->is_gradable());
539
    }
540
 
541
    /**
542
     * Data provider for sub_test_grade_item_is_gradable.
543
     *
544
     * @return \Generator
545
     */
546
    public static function provider_itemtype_is_gradable(): \Generator {
547
        yield 'GRADE_TYPE_NONE' => [
548
            'type' => GRADE_TYPE_NONE,
549
            'expected' => false,
550
        ];
551
        yield 'GRADE_TYPE_VALUE' => [
552
            'type' => GRADE_TYPE_VALUE,
553
            'expected' => true,
554
        ];
555
        yield 'GRADE_TYPE_SCALE' => [
556
            'type' => GRADE_TYPE_SCALE,
557
            'expected' => true,
558
        ];
559
        yield 'GRADE_TYPE_TEXT' => [
560
            'type' => GRADE_TYPE_TEXT,
561
            'expected' => false,
562
        ];
563
    }
564
 
1 efrain 565
    protected function sub_test_grade_item_is_category_item() {
566
        $grade_item = new \grade_item($this->grade_items[3], false);
567
        $this->assertTrue(method_exists($grade_item, 'is_category_item'));
568
        $this->assertTrue($grade_item->is_category_item());
569
    }
570
 
571
    protected function sub_test_grade_item_is_course_item() {
572
        $grade_item = \grade_item::fetch_course_item($this->courseid);
573
        $this->assertTrue(method_exists($grade_item, 'is_course_item'));
574
        $this->assertTrue($grade_item->is_course_item());
575
    }
576
 
577
    protected function sub_test_grade_item_fetch_course_item() {
578
        $grade_item = \grade_item::fetch_course_item($this->courseid);
579
        $this->assertTrue(method_exists($grade_item, 'fetch_course_item'));
580
        $this->assertEquals($grade_item->itemtype, 'course');
581
    }
582
 
583
    protected function sub_test_grade_item_depends_on() {
584
        global $CFG;
585
 
586
        $origenableoutcomes = $CFG->enableoutcomes;
587
        $CFG->enableoutcomes = 0;
588
        $grade_item = new \grade_item($this->grade_items[1], false);
589
 
590
        // Calculated grade dependency.
591
        $deps = $grade_item->depends_on();
592
        sort($deps, SORT_NUMERIC); // For comparison.
593
        $this->assertEquals(array($this->grade_items[0]->id), $deps);
594
 
595
        // Simulate depends on returns none when locked.
596
        $grade_item->locked = time();
597
        $grade_item->update();
598
        $deps = $grade_item->depends_on();
599
        sort($deps, SORT_NUMERIC); // For comparison.
600
        $this->assertEquals(array(), $deps);
601
 
602
        // Category dependency.
603
        $grade_item = new \grade_item($this->grade_items[3], false);
604
        $deps = $grade_item->depends_on();
605
        sort($deps, SORT_NUMERIC); // For comparison.
606
        $res = array($this->grade_items[4]->id, $this->grade_items[5]->id);
607
        $this->assertEquals($res, $deps);
608
    }
609
 
610
    protected function scales_outcomes_test_grade_item_depends_on() {
611
        $CFG->enableoutcomes = 1;
612
        $origgradeincludescalesinaggregation = $CFG->grade_includescalesinaggregation;
613
        $CFG->grade_includescalesinaggregation = 1;
614
 
615
        // Scale item in category with $CFG->grade_includescalesinaggregation = 1.
616
        $grade_item = new \grade_item($this->grade_items[14], false);
617
        $deps = $grade_item->depends_on();
618
        sort($deps, SORT_NUMERIC);
619
        $res = array($this->grade_items[16]->id);
620
        $this->assertEquals($res, $deps);
621
 
622
        // Scale item in category with $CFG->grade_includescalesinaggregation = 0.
623
        $CFG->grade_includescalesinaggregation = 0;
624
        $grade_item = new \grade_item($this->grade_items[14], false);
625
        $deps = $grade_item->depends_on();
626
        $res = array();
627
        $this->assertEquals($res, $deps);
628
        $CFG->grade_includescalesinaggregation = 1;
629
 
630
        // Outcome item in category with outcomes disabled.
631
        $CFG->enableoutcomes = 0;
632
        $grade_item = new \grade_item($this->grade_items[14], false);
633
        $deps = $grade_item->depends_on();
634
        sort($deps, SORT_NUMERIC);
635
        $res = array($this->grade_items[16]->id, $this->grade_items[17]->id);
636
        $this->assertEquals($res, $deps);
637
 
638
        $CFG->enableoutcomes = $origenableoutcomes;
639
        $CFG->grade_includescalesinaggregation = $origgradeincludescalesinaggregation;
640
    }
641
 
642
    protected function sub_test_refresh_grades() {
643
        // Testing with the grade item for a mod_assign instance.
644
        $grade_item = new \grade_item($this->grade_items[0], false);
645
        $this->assertTrue(method_exists($grade_item, 'refresh_grades'));
646
        $this->assertTrue($grade_item->refresh_grades());
647
 
648
        // Break the grade item and check error handling.
649
        $grade_item->iteminstance = 123456789;
650
        $this->assertFalse($grade_item->refresh_grades());
651
        $this->assertDebuggingCalled();
652
    }
653
 
654
    protected function sub_test_grade_item_is_calculated() {
655
        $grade_item = new \grade_item($this->grade_items[1], false);
656
        $this->assertTrue(method_exists($grade_item, 'is_calculated'));
657
        $this->assertTrue($grade_item->is_calculated());
658
 
659
        $grade_item = new \grade_item($this->grade_items[0], false);
660
        $this->assertFalse($grade_item->is_calculated());
661
    }
662
 
663
    protected function sub_test_grade_item_set_calculation() {
664
        $grade_item = new \grade_item($this->grade_items[1], false);
665
        $this->assertTrue(method_exists($grade_item, 'set_calculation'));
666
        $grade_itemsource = new \grade_item($this->grade_items[0], false);
667
 
668
        $grade_item->set_calculation('=[['.$grade_itemsource->idnumber.']]');
669
 
670
        $this->assertTrue(!empty($grade_item->needsupdate));
671
        $this->assertEquals('=##gi'.$grade_itemsource->id.'##', $grade_item->calculation);
672
    }
673
 
674
    protected function sub_test_grade_item_get_calculation() {
675
        $grade_item = new \grade_item($this->grade_items[1], false);
676
        $this->assertTrue(method_exists($grade_item, 'get_calculation'));
677
        $grade_itemsource = new \grade_item($this->grade_items[0], false);
678
 
679
        $denormalizedformula = str_replace('##gi'.$grade_itemsource->id.'##', '[['.$grade_itemsource->idnumber.']]', $this->grade_items[1]->calculation);
680
 
681
        $formula = $grade_item->get_calculation();
682
        $this->assertTrue(!empty($grade_item->needsupdate));
683
        $this->assertEquals($denormalizedformula, $formula);
684
    }
685
 
686
    public function sub_test_grade_item_compute() {
687
        $grade_item = \grade_item::fetch(array('id'=>$this->grade_items[1]->id));
688
        $this->assertTrue(method_exists($grade_item, 'compute'));
689
 
690
        // Check the grade_grades in the array match those in the DB then delete $this->grade_items[1]'s grade_grades.
691
        $this->grade_grades[3] = \grade_grade::fetch(array('id'=>$this->grade_grades[3]->id));
692
        $grade_grade = \grade_grade::fetch(array('id'=>$this->grade_grades[3]->id));
693
        $grade_grade->delete();
694
 
695
        $this->grade_grades[4] = \grade_grade::fetch(array('id'=>$this->grade_grades[4]->id));
696
        $grade_grade = \grade_grade::fetch(array('id'=>$this->grade_grades[4]->id));
697
        $grade_grade->delete();
698
 
699
        $this->grade_grades[5] = \grade_grade::fetch(array('id'=>$this->grade_grades[5]->id));
700
        $grade_grade = \grade_grade::fetch(array('id'=>$this->grade_grades[5]->id));
701
        $grade_grade->delete();
702
 
703
        // Recalculate the grades (its a calculation so pulls values from other grade_items) and reinsert them.
704
        $grade_item->compute();
705
 
706
        $grade_grade = \grade_grade::fetch(array('userid'=>$this->grade_grades[3]->userid, 'itemid'=>$this->grade_grades[3]->itemid));
707
        $this->assertEquals($this->grade_grades[3]->finalgrade, $grade_grade->finalgrade);
708
 
709
        $grade_grade = \grade_grade::fetch(array('userid'=>$this->grade_grades[4]->userid, 'itemid'=>$this->grade_grades[4]->itemid));
710
        $this->assertEquals($this->grade_grades[4]->finalgrade, $grade_grade->finalgrade);
711
 
712
        $grade_grade = \grade_grade::fetch(array('userid'=>$this->grade_grades[5]->userid, 'itemid'=>$this->grade_grades[5]->itemid));
713
        $this->assertEquals($this->grade_grades[5]->finalgrade, $grade_grade->finalgrade);
714
    }
715
 
716
    protected function sub_test_update_final_grade() {
717
 
718
        // MDL-31713 Check that min and max are set on the grade_grade instance
719
        // if the grade is overridden before the activity has supplied a grade.
720
        $min = 2;
721
        $max = 8;
722
 
723
        // Create a brand new grade item.
724
        $grade_item = new \grade_item();
725
        $this->assertTrue(method_exists($grade_item, 'insert'));
726
 
727
        $grade_item->courseid = $this->courseid;
728
        $grade_item->categoryid = $this->grade_categories[1]->id;
729
        $grade_item->itemname = 'brand new unit test grade item';
730
        $grade_item->itemtype = 'mod';
731
        $grade_item->itemmodule = 'quiz';
732
        $grade_item->iteminfo = 'Grade item used for unit testing';
733
        $grade_item->iteminstance = $this->activities[7]->id;
734
        $grade_item->grademin = $min;
735
        $grade_item->grademax = $max;
736
        $grade_item->insert();
737
 
738
        // Override the student grade.
739
        $grade_item->update_final_grade($this->user[1]->id, 7, 'gradebook', '', FORMAT_MOODLE);
740
 
741
        // Check the student's grade has the correct min and max grade.
742
        $grade_grade = \grade_grade::fetch(array('userid'=>$this->user[1]->id, 'itemid'=>$grade_item->id));
743
        $this->assertEquals($min, $grade_grade->rawgrademin);
744
        $this->assertEquals($max, $grade_grade->rawgrademax);
745
    }
746
 
747
    protected function sub_test_grade_item_can_control_visibility() {
748
        // Grade item 0 == Course module 0 == Assignment.
749
        $grade_item = new \grade_item($this->grade_items[0], false);
750
        $this->assertTrue($grade_item->can_control_visibility());
751
 
752
        // Grade item  == Course module 7 == Quiz.
753
        $grade_item = new \grade_item($this->grade_items[11], false);
754
        $this->assertFalse($grade_item->can_control_visibility());
755
    }
756
 
757
    /**
758
     * Test the {@link \grade_item::fix_duplicate_sortorder() function with
759
     * faked duplicate sortorder data.
760
     */
761
    public function sub_test_grade_item_fix_sortorder() {
762
        global $DB;
763
 
764
        $this->resetAfterTest(true);
765
 
766
        // Each set is used for filling the db with fake data and will be representing the result of query:
767
        // "SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id".
768
        $testsets = array(
769
            // Items that need no action.
770
            array(1,2,3),
771
            array(5,6,7),
772
            array(7,6,1,3,2,5),
773
            // Items with sortorder duplicates
774
            array(1,2,2,3,3,4,5),
775
            // Only one sortorder duplicate.
776
            array(1,1),
777
            array(3,3),
778
            // Non-sequential sortorders with one or multiple duplicates.
779
            array(3,3,7,5,6,6,9,10,8,3),
780
            array(7,7,3),
781
            array(3,4,5,3,5,4,7,1)
782
        );
783
        $origsequence = array();
784
 
785
        // Generate the data and remember the initial sequence or items.
786
        foreach ($testsets as $testset) {
787
            $course = $this->getDataGenerator()->create_course();
788
            foreach ($testset as $sortorder) {
789
                $this->insert_fake_grade_item_sortorder($course->id, $sortorder);
790
            }
791
            $DB->get_records('grade_items');
792
            $origsequence[$course->id] = $DB->get_fieldset_sql("SELECT id FROM {grade_items} ".
793
                "WHERE courseid = ? ORDER BY sortorder, id", array($course->id));
794
        }
795
 
796
        $duplicatedetectionsql = "SELECT courseid, sortorder
797
                                    FROM {grade_items}
798
                                WHERE courseid = :courseid
799
                                GROUP BY courseid, sortorder
800
                                  HAVING COUNT(id) > 1";
801
 
802
        // Do the work.
803
        foreach ($origsequence as $courseid => $ignore) {
804
            \grade_item::fix_duplicate_sortorder($courseid);
805
            // Verify that no duplicates are left in the database.
806
            $dupes = $DB->record_exists_sql($duplicatedetectionsql, array('courseid' => $courseid));
807
            $this->assertFalse($dupes);
808
        }
809
 
810
        // Verify that sequences are exactly the same as they were before upgrade script.
811
        $idx = 0;
812
        foreach ($origsequence as $courseid => $sequence) {
813
            if (count(($testsets[$idx])) == count(array_unique($testsets[$idx]))) {
814
                // If there were no duplicates for this course verify that sortorders are not modified.
815
                $newsortorders = $DB->get_fieldset_sql("SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id", array($courseid));
816
                $this->assertEquals($testsets[$idx], $newsortorders);
817
            }
818
            $newsequence = $DB->get_fieldset_sql("SELECT id FROM {grade_items} ".
819
                "WHERE courseid = ? ORDER BY sortorder, id", array($courseid));
820
            $this->assertEquals($sequence, $newsequence,
821
                    "Sequences do not match for test set $idx : ".join(',', $testsets[$idx]));
822
            $idx++;
823
        }
824
    }
825
 
826
    /**
827
     * Populate some fake grade items into the database with specified
828
     * sortorder and course id.
829
     *
830
     * NOTE: This function doesn't make much attempt to respect the
831
     * gradebook internals, its simply used to fake some data for
832
     * testing the upgradelib function. Please don't use it for other
833
     * purposes.
834
     *
835
     * @param int $courseid id of course
836
     * @param int $sortorder numeric sorting order of item
837
     * @return \stdClass grade item object from the database.
838
     */
839
    private function insert_fake_grade_item_sortorder($courseid, $sortorder) {
840
        global $DB, $CFG;
841
        require_once($CFG->libdir.'/gradelib.php');
842
 
843
        $item = new \stdClass();
844
        $item->courseid = $courseid;
845
        $item->sortorder = $sortorder;
846
        $item->gradetype = GRADE_TYPE_VALUE;
847
        $item->grademin = 30;
848
        $item->grademax = 110;
849
        $item->itemnumber = 1;
850
        $item->iteminfo = '';
851
        $item->timecreated = time();
852
        $item->timemodified = time();
853
 
854
        $item->id = $DB->insert_record('grade_items', $item);
855
 
856
        return $DB->get_record('grade_items', array('id' => $item->id));
857
    }
858
 
11 efrain 859
    public function test_set_aggregation_fields_for_aggregation(): void {
1 efrain 860
        $course = $this->getDataGenerator()->create_course();
861
        $gi = new \grade_item(array('courseid' => $course->id, 'itemtype' => 'manual'), false);
862
 
863
        $methods = array(GRADE_AGGREGATE_MEAN, GRADE_AGGREGATE_MEDIAN, GRADE_AGGREGATE_MIN, GRADE_AGGREGATE_MAX,
864
            GRADE_AGGREGATE_MODE, GRADE_AGGREGATE_WEIGHTED_MEAN, GRADE_AGGREGATE_WEIGHTED_MEAN2,
865
            GRADE_AGGREGATE_EXTRACREDIT_MEAN, GRADE_AGGREGATE_SUM);
866
 
867
        // Switching from and to the same aggregation using the defaults.
868
        foreach ($methods as $method) {
869
            $defaults = \grade_category::get_default_aggregation_coefficient_values($method);
870
            $gi->aggregationcoef = $defaults['aggregationcoef'];
871
            $gi->aggregationcoef2 = $defaults['aggregationcoef2'];
872
            $gi->weightoverride = $defaults['weightoverride'];
873
            $this->assertFalse($gi->set_aggregation_fields_for_aggregation($method, $method));
874
            $this->assertEquals($defaults['aggregationcoef'], $gi->aggregationcoef);
875
            $this->assertEquals($defaults['aggregationcoef2'], $gi->aggregationcoef2);
876
            $this->assertEquals($defaults['weightoverride'], $gi->weightoverride);
877
        }
878
 
879
        // Extra credit is kept across aggregation methods that support it.
880
        foreach ($methods as $from) {
881
            $fromsupportsec = \grade_category::aggregation_uses_extracredit($from);
882
            $fromdefaults = \grade_category::get_default_aggregation_coefficient_values($from);
883
 
884
            foreach ($methods as $to) {
885
                $tosupportsec = \grade_category::aggregation_uses_extracredit($to);
886
                $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
887
 
888
                // Set the item to be extra credit, if supported.
889
                if ($fromsupportsec) {
890
                    $gi->aggregationcoef = 1;
891
                } else {
892
                    $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
893
                }
894
 
895
                // We ignore those fields, we know it is never used for extra credit.
896
                $gi->aggregationcoef2 = $todefaults['aggregationcoef2'];
897
                $gi->weightoverride = $todefaults['weightoverride'];
898
 
899
                if ($fromsupportsec && $tosupportsec) {
900
                    $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
901
                    $this->assertEquals(1, $gi->aggregationcoef);
902
 
903
                } else if ($fromsupportsec && !$tosupportsec) {
904
                    if ($to == GRADE_AGGREGATE_WEIGHTED_MEAN) {
905
                        // Special case, aggregationcoef is used but for weights.
906
                        $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
907
                        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
908
                    } else {
909
                        $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
910
                        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
911
                    }
912
                } else {
913
                    // The source does not support extra credit, everything will be reset.
914
                    if (($from == GRADE_AGGREGATE_WEIGHTED_MEAN || $to == GRADE_AGGREGATE_WEIGHTED_MEAN) && $from != $to) {
915
                        // Special case, aggregationcoef is used but for weights.
916
                        $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
917
                        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
918
                    } else {
919
                        $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
920
                        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
921
                    }
922
                }
923
            }
924
        }
925
 
926
        // Extra credit can be higher than one for GRADE_AGGREGATE_EXTRACREDIT_MEAN, but will be normalised for others.
927
        $from = GRADE_AGGREGATE_EXTRACREDIT_MEAN;
928
        $fromdefaults = \grade_category::get_default_aggregation_coefficient_values($from);
929
 
930
        foreach ($methods as $to) {
931
            if (!\grade_category::aggregation_uses_extracredit($to)) {
932
                continue;
933
            }
934
 
935
            $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
936
            $gi->aggregationcoef = 8;
937
 
938
            // Ignore those fields, they are not used for extra credit.
939
            $gi->aggregationcoef2 = $todefaults['aggregationcoef2'];
940
            $gi->weightoverride = $todefaults['weightoverride'];
941
 
942
            if ($to == $from) {
943
                $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
944
                $this->assertEquals(8, $gi->aggregationcoef);
945
            } else {
946
                $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
947
                $this->assertEquals(1, $gi->aggregationcoef);
948
            }
949
        }
950
 
951
        // Weights are reset.
952
        $from = GRADE_AGGREGATE_SUM;
953
        $fromdefaults = \grade_category::get_default_aggregation_coefficient_values($from);
954
 
955
        $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
956
        $gi->aggregationcoef2 = 0.321;
957
        $gi->weightoverride = $fromdefaults['weightoverride'];
958
 
959
        $to = GRADE_AGGREGATE_WEIGHTED_MEAN;
960
        $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
961
 
962
        $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
963
        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
964
        $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
965
        $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
966
 
967
        $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
968
        $gi->aggregationcoef2 = 0.321;
969
        $gi->weightoverride = $fromdefaults['weightoverride'];
970
 
971
        $to = GRADE_AGGREGATE_SUM;
972
        $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
973
 
974
        $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
975
        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
976
        $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
977
        $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
978
 
979
        // Weight is kept when using SUM with weight override.
980
        $from = GRADE_AGGREGATE_SUM;
981
        $fromdefaults = \grade_category::get_default_aggregation_coefficient_values($from);
982
 
983
        $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
984
        $gi->aggregationcoef2 = 0.321;
985
        $gi->weightoverride = 1;
986
 
987
        $to = GRADE_AGGREGATE_SUM;
988
        $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
989
 
990
        $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
991
        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
992
        $this->assertEquals(0.321, $gi->aggregationcoef2);
993
        $this->assertEquals(1, $gi->weightoverride);
994
 
995
        $gi->aggregationcoef2 = 0.321;
996
        $gi->aggregationcoef = $fromdefaults['aggregationcoef'];
997
        $gi->weightoverride = 1;
998
 
999
        $to = GRADE_AGGREGATE_WEIGHTED_MEAN;
1000
        $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
1001
 
1002
        $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
1003
        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
1004
        $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
1005
        $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
1006
 
1007
        // Weight is kept when staying in weighted mean.
1008
        $from = GRADE_AGGREGATE_WEIGHTED_MEAN;
1009
        $fromdefaults = \grade_category::get_default_aggregation_coefficient_values($from);
1010
 
1011
        $gi->aggregationcoef = 18;
1012
        $gi->aggregationcoef2 = $fromdefaults['aggregationcoef2'];
1013
        $gi->weightoverride = $fromdefaults['weightoverride'];
1014
 
1015
        $to = GRADE_AGGREGATE_WEIGHTED_MEAN;
1016
        $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
1017
 
1018
        $this->assertFalse($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
1019
        $this->assertEquals(18, $gi->aggregationcoef);
1020
        $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
1021
        $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
1022
 
1023
        $gi->aggregationcoef = 18;
1024
        $gi->aggregationcoef2 = $fromdefaults['aggregationcoef2'];
1025
        $gi->weightoverride = $fromdefaults['weightoverride'];
1026
 
1027
        $to = GRADE_AGGREGATE_SUM;
1028
        $todefaults = \grade_category::get_default_aggregation_coefficient_values($to);
1029
 
1030
        $this->assertTrue($gi->set_aggregation_fields_for_aggregation($from, $to), "From: $from, to: $to");
1031
        $this->assertEquals($todefaults['aggregationcoef'], $gi->aggregationcoef);
1032
        $this->assertEquals($todefaults['aggregationcoef2'], $gi->aggregationcoef2);
1033
        $this->assertEquals($todefaults['weightoverride'], $gi->weightoverride);
1034
    }
1035
 
1036
    /**
1037
     * Test that grade item event triggered when a grade item is created.
1038
     */
1039
    protected function sub_test_grade_item_created_event() {
1040
        $sink = $this->redirectEvents();
1041
 
1042
        $gradeitem = new \grade_item();
1043
 
1044
        $gradeitem->courseid = $this->courseid;
1045
        $gradeitem->categoryid = $this->grade_categories[1]->id;
1046
        $gradeitem->itemname = 'unittestgradeitem4';
1047
        $gradeitem->itemtype = 'mod';
1048
        $gradeitem->itemmodule = 'quiz';
1049
        $gradeitem->iteminfo = 'Grade item used for unit testing';
1050
 
1051
        $gradeitem->insert();
1052
 
1053
        $result = $sink->get_events();
1054
        $sink->close();
1055
 
1056
        $this->assertCount(1, $result);
1057
 
1058
        $event = reset($result);
1059
        $this->assertEventContextNotUsed($event);
1060
        $this->assertInstanceOf('core\event\grade_item_created', $event);
1061
 
1062
        $eventgradeitem = $event->get_grade_item();
1063
 
1064
        $this->assertInstanceOf('grade_item', $eventgradeitem);
1065
        $this->assertEquals($gradeitem->id, $eventgradeitem->id);
1066
        $this->assertEquals($gradeitem->itemname, $event->other['itemname']);
1067
        $this->assertEquals($gradeitem->itemtype, $event->other['itemtype']);
1068
        $this->assertEquals($gradeitem->itemmodule, $event->other['itemmodule']);
1069
    }
1070
 
1071
    /**
1072
     * Test that grade item event triggered when a grade item is updated.
1073
     */
1074
    protected function sub_test_grade_item_updated_event() {
1075
        $gradeitem = new \grade_item();
1076
 
1077
        $gradeitem->courseid = $this->courseid;
1078
        $gradeitem->categoryid = $this->grade_categories[1]->id;
1079
        $gradeitem->itemname = 'unittestgradeitem4';
1080
        $gradeitem->itemtype = 'mod';
1081
        $gradeitem->itemmodule = 'quiz';
1082
        $gradeitem->iteminfo = 'Grade item used for unit testing';
1083
        $gradeitem->insert();
1084
 
1085
        $sink = $this->redirectEvents();
1086
 
1087
        $gradeitem->itemname = 'updatedname';
1088
        $gradeitem->update();
1089
 
1090
        $result = $sink->get_events();
1091
        $sink->close();
1092
 
1093
        $this->assertCount(1, $result);
1094
 
1095
        $event = reset($result);
1096
        $this->assertInstanceOf('core\event\grade_item_updated', $event);
1097
        $this->assertEventContextNotUsed($event);
1098
 
1099
        $eventgradeitem = $event->get_grade_item();
1100
 
1101
        $this->assertInstanceOf('grade_item', $eventgradeitem);
1102
        $this->assertEquals($gradeitem->id, $eventgradeitem->id);
1103
        $this->assertEquals($gradeitem->itemtype, $event->other['itemtype']);
1104
        $this->assertEquals($gradeitem->itemmodule, $event->other['itemmodule']);
1105
        $this->assertEquals('updatedname', $event->other['itemname']);
1106
    }
1107
 
1108
 
1109
    /**
1110
     * Test grade item duplication expecting success.
1111
     */
11 efrain 1112
    public function test_grade_duplicate_grade_item_success(): void {
1 efrain 1113
        $cat = new \grade_category();
1114
        $cat->courseid = $this->courseid;
1115
        $cat->fullname = 'Grade category';
1116
        $cat->insert();
1117
 
1118
        // Method exists.
1119
        $gi = new \grade_item();
1120
        $this->assertTrue(method_exists($gi, 'duplicate'));
1121
 
1122
        // Grade item is inserted and valid for duplication.
1123
        $gi->courseid = $this->courseid;
1124
        $gi->categoryid = $cat->id;
1125
        $gi->itemtype = 'manual';
1126
        $gi->itemname = 'Grade Item 1';
1127
        $gi->idnumber = '1000';
1128
        $gi->insert();
1129
        $gi2 = $gi->duplicate();
1130
 
1131
        $this->assertEquals($gi->courseid, $gi2->courseid);
1132
        $this->assertEquals($gi->categoryid, $gi2->categoryid);
1133
        $this->assertEquals($gi->itemtype, $gi2->itemtype);
1134
        $this->assertEquals($gi->gradetype, $gi2->gradetype);
1135
        $this->assertEquals($gi->grademax, $gi2->grademax);
1136
        $this->assertEquals($gi->grademin, $gi2->grademin);
1137
        $this->assertEquals($gi->gradepass, $gi2->gradepass);
1138
        $this->assertEquals($gi->display, $gi2->display);
1139
        $this->assertEquals($gi->decimals, $gi2->decimals);
1140
        $this->assertEquals($gi->hidden, $gi2->hidden);
1141
        $this->assertEquals($gi->weightoverride, $gi2->weightoverride);
1142
 
1143
        $this->assertNotEquals($gi->id, $gi2->id);
1144
        $this->assertNotEquals($gi->idnumber, $gi2->idnumber);
1145
        $this->assertNotEquals($gi->sortorder, $gi2->sortorder);
1146
        $this->assertNotEquals($gi->itemname, $gi2->itemname);
1147
    }
1148
 
1149
    /**
1150
     * Test grade item duplication exception expected with incomplete grade item.
1151
     */
11 efrain 1152
    public function test_grade_duplicate_grade_item_incomplete(): void {
1 efrain 1153
        // Grade item is not valid because it is empty.
1154
        $gi = new \grade_item();
1155
        $gi->courseid = $this->courseid;
1156
        $this->expectException("moodle_exception");
1157
        $gi2 = $gi->duplicate();
1158
    }
1159
 
1160
    /**
1161
     * Test grade item duplication exception expected because item must be in db.
1162
     */
11 efrain 1163
    public function test_grade_duplicate_grade_item_not_in_db(): void {
1 efrain 1164
        $cat = new \grade_category();
1165
        $cat->courseid = $this->courseid;
1166
        $cat->fullname = 'Grade category';
1167
        $cat->insert();
1168
 
1169
        // Grade item is valid for insertion but is not inserted into db.
1170
        // Duplicate method throws an exception.
1171
        $gi = new \grade_item();
1172
        $gi->courseid = $this->courseid;
1173
        $gi->categoryid = $cat->id;
1174
        $gi->itemtype = 'manual';
1175
        $gi->itemname = 'Grade Item 1';
1176
        $gi->idnumber = '1000';
1177
 
1178
        $this->expectException("moodle_exception");
1179
        $gi2 = $gi->duplicate();
1180
    }
1181
}