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
/**
18
 * mod_h5pactivity grader tests
19
 *
20
 * @package    mod_h5pactivity
21
 * @category   test
22
 * @copyright  2020 Ferran Recio <ferran@moodle.com>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace mod_h5pactivity\local;
27
 
28
use grade_item;
29
use stdClass;
30
 
31
/**
32
 * Grader tests class for mod_h5pactivity.
33
 *
34
 * @package    mod_h5pactivity
35
 * @category   test
36
 * @copyright  2020 Ferran Recio <ferran@moodle.com>
37
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
1441 ariadna 39
final class grader_test extends \advanced_testcase {
1 efrain 40
 
41
    /**
42
     * Setup to ensure that fixtures are loaded.
43
     */
44
    public static function setupBeforeClass(): void {
45
        global $CFG;
46
        require_once($CFG->libdir.'/gradelib.php');
47
    }
48
 
49
    /**
50
     * Test for grade item delete.
51
     */
11 efrain 52
    public function test_grade_item_delete(): void {
1 efrain 53
 
54
        $this->resetAfterTest();
55
        $this->setAdminUser();
56
 
57
        $course = $this->getDataGenerator()->create_course();
58
        $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
59
        $user = $this->getDataGenerator()->create_and_enrol($course, 'student');
60
 
61
        $grader = new grader($activity);
62
 
63
        // Force a user grade.
64
        $this->generate_fake_attempt($activity, $user, 5, 10);
65
        $grader->update_grades($user->id);
66
 
67
        $gradeinfo = grade_get_grades($course->id, 'mod', 'h5pactivity', $activity->id, $user->id);
68
        $this->assertNotEquals(0, count($gradeinfo->items));
69
        $this->assertArrayHasKey($user->id, $gradeinfo->items[0]->grades);
70
 
71
        $grader->grade_item_delete();
72
 
73
        $gradeinfo = grade_get_grades($course->id, 'mod', 'h5pactivity', $activity->id, $user->id);
74
        $this->assertEquals(0, count($gradeinfo->items));
75
    }
76
 
77
    /**
78
     * Test for grade item update.
79
     *
80
     * @dataProvider grade_item_update_data
81
     * @param int $newgrade new activity grade
82
     * @param bool $reset if has to reset grades
83
     * @param string $idnumber the new idnumber
84
     */
11 efrain 85
    public function test_grade_item_update(int $newgrade, bool $reset, string $idnumber): void {
1 efrain 86
 
87
        $this->resetAfterTest();
88
        $this->setAdminUser();
89
 
90
        $course = $this->getDataGenerator()->create_course();
91
        $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
92
        $user = $this->getDataGenerator()->create_and_enrol($course, 'student');
93
 
94
        // Force a user initial grade.
95
        $grader = new grader($activity);
96
        $this->generate_fake_attempt($activity, $user, 5, 10);
97
        $grader->update_grades($user->id);
98
 
99
        $gradeinfo = grade_get_grades($course->id, 'mod', 'h5pactivity', $activity->id, $user->id);
100
        $this->assertNotEquals(0, count($gradeinfo->items));
101
        $item = array_shift($gradeinfo->items);
102
        $this->assertArrayHasKey($user->id, $item->grades);
103
        $this->assertEquals(50, round($item->grades[$user->id]->grade));
104
 
105
        // Module grade value determine the way gradebook acts. That means that the expected
106
        // result depends on this value.
107
        // - Grade > 0: regular max grade value.
108
        // - Grade = 0: no grading is used (but grademax remains the same).
109
        // - Grade < 0: a scaleid is used (value = -scaleid).
110
        if ($newgrade > 0) {
111
            $grademax = $newgrade;
112
            $scaleid = null;
113
            $usergrade = ($newgrade > 50) ? 50 : $newgrade;
114
        } else if ($newgrade == 0) {
115
            $grademax = 100;
116
            $scaleid = null;
117
            $usergrade = null; // No user grades expected.
118
        } else if ($newgrade < 0) {
119
            $scale = $this->getDataGenerator()->create_scale(array("scale" => "value1, value2, value3"));
120
            $newgrade = -1 * $scale->id;
121
            $grademax = 3;
122
            $scaleid = $scale->id;
123
            $usergrade = 3; // 50 value will ve converted to "value 3" on scale.
124
        }
125
 
126
        // Update grade item.
127
        $activity->grade = $newgrade;
128
 
129
        // In case a reset is need, usergrade will be empty.
130
        if ($reset) {
131
            $param = 'reset';
132
            $usergrade = null;
133
        } else {
134
            // Individual user gradings will be tested as a subcall of update_grades.
135
            $param = null;
136
        }
137
 
138
        $grader = new grader($activity, $idnumber);
139
        $grader->grade_item_update($param);
140
 
141
        // Check new grade item and grades.
1441 ariadna 142
        grade_regrade_final_grades($course->id);
1 efrain 143
        $gradeinfo = grade_get_grades($course->id, 'mod', 'h5pactivity', $activity->id, $user->id);
144
        $item = array_shift($gradeinfo->items);
145
        $this->assertEquals($scaleid, $item->scaleid);
146
        $this->assertEquals($grademax, $item->grademax);
147
        $this->assertArrayHasKey($user->id, $item->grades);
148
        if ($usergrade) {
149
            $this->assertEquals($usergrade, round($item->grades[$user->id]->grade));
150
        } else {
151
            $this->assertEmpty($item->grades[$user->id]->grade);
152
        }
153
        if (!empty($idnumber)) {
154
            $gradeitem = grade_item::fetch(['idnumber' => $idnumber, 'courseid' => $course->id]);
155
            $this->assertInstanceOf('grade_item', $gradeitem);
156
        }
157
    }
158
 
159
    /**
160
     * Data provider for test_grade_item_update.
161
     *
162
     * @return array
163
     */
1441 ariadna 164
    public static function grade_item_update_data(): array {
1 efrain 165
        return [
166
            'Change idnumber' => [
167
                100, false, 'newidnumber'
168
            ],
169
            'Increase max grade to 110' => [
170
                110, false, ''
171
            ],
172
            'Decrease max grade to 80' => [
173
                40, false, ''
174
            ],
175
            'Decrease max grade to 40 (less than actual grades)' => [
176
                40, false, ''
177
            ],
178
            'Reset grades' => [
179
                100, true, ''
180
            ],
181
            'Disable grades' => [
182
                0, false, ''
183
            ],
184
            'Use scales' => [
185
                -1, false, ''
186
            ],
187
            'Use scales with reset' => [
188
                -1, true, ''
189
            ],
190
        ];
191
    }
192
 
193
    /**
194
     * Test for grade update.
195
     *
196
     * @dataProvider update_grades_data
197
     * @param int $newgrade the new activity grade
198
     * @param bool $all if has to be applied to all students or just to one
199
     * @param int $completion 1 all student have the activity completed, 0 one have incompleted
200
     * @param array $results expected results (user1 grade, user2 grade)
201
     */
11 efrain 202
    public function test_update_grades(int $newgrade, bool $all, int $completion, array $results): void {
1 efrain 203
 
204
        $this->resetAfterTest();
205
        $this->setAdminUser();
206
 
207
        $course = $this->getDataGenerator()->create_course();
208
        $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
209
        $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
210
        $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
211
 
212
        // Force a user initial grade.
213
        $grader = new grader($activity);
214
        $this->generate_fake_attempt($activity, $user1, 5, 10);
215
        $this->generate_fake_attempt($activity, $user2, 3, 12, $completion);
216
        $grader->update_grades();
217
 
218
        $gradeinfo = grade_get_grades($course->id, 'mod', 'h5pactivity', $activity->id, [$user1->id, $user2->id]);
219
        $this->assertNotEquals(0, count($gradeinfo->items));
220
        $item = array_shift($gradeinfo->items);
221
        $this->assertArrayHasKey($user1->id, $item->grades);
222
        $this->assertArrayHasKey($user2->id, $item->grades);
223
        $this->assertEquals(50, $item->grades[$user1->id]->grade);
224
        // Uncompleted attempts does not generate grades.
225
        if ($completion) {
226
            $this->assertEquals(25, $item->grades[$user2->id]->grade);
227
        } else {
228
            $this->assertNull($item->grades[$user2->id]->grade);
229
 
230
        }
231
 
232
        // Module grade value determine the way gradebook acts. That means that the expected
233
        // result depends on this value.
234
        // - Grade > 0: regular max grade value.
235
        // - Grade <= 0: no grade calculation is used (scale and no grading).
236
        if ($newgrade < 0) {
237
            $scale = $this->getDataGenerator()->create_scale(array("scale" => "value1, value2, value3"));
238
            $activity->grade = -1 * $scale->id;
239
        } else {
240
            $activity->grade = $newgrade;
241
        }
242
 
243
        $userid = ($all) ? 0 : $user1->id;
244
 
245
        $grader = new grader($activity);
246
        $grader->update_grades($userid);
247
 
248
        // Check new grade item and grades.
1441 ariadna 249
        grade_regrade_final_grades($course->id);
1 efrain 250
        $gradeinfo = grade_get_grades($course->id, 'mod', 'h5pactivity', $activity->id, [$user1->id, $user2->id]);
251
        $item = array_shift($gradeinfo->items);
252
        $this->assertArrayHasKey($user1->id, $item->grades);
253
        $this->assertArrayHasKey($user2->id, $item->grades);
254
        $this->assertEquals($results[0], $item->grades[$user1->id]->grade);
255
        $this->assertEquals($results[1], $item->grades[$user2->id]->grade);
256
    }
257
 
258
    /**
259
     * Data provider for test_grade_item_update.
260
     *
261
     * @return array
262
     */
1441 ariadna 263
    public static function update_grades_data(): array {
1 efrain 264
        return [
265
            // Quantitative grade, all attempts completed.
266
            'Same grademax, all users, all completed' => [
267
                100, true, 1, [50, 25]
268
            ],
269
            'Same grademax, one user, all completed' => [
270
                100, false, 1, [50, 25]
271
            ],
272
            'Increade max, all users, all completed' => [
273
                200, true, 1, [100, 50]
274
            ],
275
            'Increade max, one user, all completed' => [
276
                200, false, 1, [100, 25]
277
            ],
278
            'Decrease max, all users, all completed' => [
279
                50, true, 1, [25, 12.5]
280
            ],
281
            'Decrease max, one user, all completed' => [
282
                50, false, 1, [25, 25]
283
            ],
284
            // Quantitative grade, some attempts not completed.
285
            'Same grademax, all users, not completed' => [
286
                100, true, 0, [50, null]
287
            ],
288
            'Same grademax, one user, not completed' => [
289
                100, false, 0, [50, null]
290
            ],
291
            'Increade max, all users, not completed' => [
292
                200, true, 0, [100, null]
293
            ],
294
            'Increade max, one user, not completed' => [
295
                200, false, 0, [100, null]
296
            ],
297
            'Decrease max, all users, not completed' => [
298
                50, true, 0, [25, null]
299
            ],
300
            'Decrease max, one user, not completed' => [
301
                50, false, 0, [25, null]
302
            ],
303
            // No grade (no grade will be used).
304
            'No grade, all users, all completed' => [
305
                0, true, 1, [null, null]
306
            ],
307
            'No grade, one user, all completed' => [
308
                0, false, 1, [null, null]
309
            ],
310
            'No grade, all users, not completed' => [
311
                0, true, 0, [null, null]
312
            ],
313
            'No grade, one user, not completed' => [
314
                0, false, 0, [null, null]
315
            ],
316
            // Scale (grate item will updated but without regrading).
317
            'Scale, all users, all completed' => [
318
                -1, true, 1, [3, 3]
319
            ],
320
            'Scale, one user, all completed' => [
321
                -1, false, 1, [3, 3]
322
            ],
323
            'Scale, all users, not completed' => [
324
                -1, true, 0, [3, null]
325
            ],
326
            'Scale, one user, not completed' => [
327
                -1, false, 0, [3, null]
328
            ],
329
        ];
330
    }
331
 
332
    /**
333
     * Create a fake attempt for a specific user.
334
     *
335
     * @param stdClass $activity activity instance record.
336
     * @param stdClass $user user record
337
     * @param int $rawscore score obtained
338
     * @param int $maxscore attempt max score
339
     * @param int $completion 1 for activity completed, 0 for not completed yet
340
     * @return stdClass the attempt record
341
     */
342
    private function generate_fake_attempt(stdClass $activity, stdClass $user,
343
            int $rawscore, int $maxscore, int $completion = 1): stdClass {
344
        global $DB;
345
 
346
        $attempt = (object)[
347
            'h5pactivityid' => $activity->id,
348
            'userid' => $user->id,
349
            'timecreated' => 10,
350
            'timemodified' => 20,
351
            'attempt' => 1,
352
            'rawscore' => $rawscore,
353
            'maxscore' => $maxscore,
354
            'duration' => 2,
355
            'completion' => $completion,
356
            'success' => 0,
357
        ];
358
        $attempt->scaled = $attempt->rawscore / $attempt->maxscore;
359
        $attempt->id = $DB->insert_record('h5pactivity_attempts', $attempt);
360
        return $attempt;
361
    }
362
}