Proyectos de Subversion Moodle

Rev

Rev 1 | | 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
global $CFG;
22
require_once($CFG->libdir . '/gradelib.php');
23
 
24
/**
25
 * Unit tests for /lib/gradelib.php.
26
 *
27
 * @package   core
28
 * @category  test
29
 * @copyright 2012 Andrew Davis
30
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31
 */
32
class gradelib_test extends \advanced_testcase {
33
 
11 efrain 34
    public function test_grade_update_mod_grades(): void {
1 efrain 35
 
36
        $this->resetAfterTest(true);
37
 
38
        // Create a broken module instance.
39
        $modinstance = new \stdClass();
40
        $modinstance->modname = 'doesntexist';
41
 
42
        $this->assertFalse(grade_update_mod_grades($modinstance));
43
        // A debug message should have been generated.
44
        $this->assertDebuggingCalled();
45
 
46
        // Create a course and instance of mod_assign.
47
        $course = $this->getDataGenerator()->create_course();
48
 
49
        $assigndata['course'] = $course->id;
50
        $assigndata['name'] = 'lightwork assignment';
51
        $modinstance = self::getDataGenerator()->create_module('assign', $assigndata);
52
 
53
        // Function grade_update_mod_grades() requires 2 additional properties, cmidnumber and modname.
54
        $cm = get_coursemodule_from_instance('assign', $modinstance->id, 0, false, MUST_EXIST);
55
        $modinstance->cmidnumber = $cm->id;
56
        $modinstance->modname = 'assign';
57
 
58
        $this->assertTrue(grade_update_mod_grades($modinstance));
59
    }
60
 
61
    /**
62
     * Tests the function remove_grade_letters().
63
     */
11 efrain 64
    public function test_remove_grade_letters(): void {
1 efrain 65
        global $DB;
66
 
67
        $this->resetAfterTest();
68
 
69
        $course = $this->getDataGenerator()->create_course();
70
 
71
        $context = \context_course::instance($course->id);
72
 
73
        // Add a grade letter to the course.
74
        $letter = new \stdClass();
75
        $letter->letter = 'M';
76
        $letter->lowerboundary = '100';
77
        $letter->contextid = $context->id;
78
        $DB->insert_record('grade_letters', $letter);
79
 
80
        // Pre-warm the cache, ensure that that the letter is cached.
81
        $cache = \cache::make('core', 'grade_letters');
82
 
83
        // Check that the cache is empty beforehand.
84
        $letters = $cache->get($context->id);
85
        $this->assertFalse($letters);
86
 
87
        // Call the function.
88
        grade_get_letters($context);
89
 
90
        $letters = $cache->get($context->id);
91
        $this->assertEquals(1, count($letters));
92
        $this->assertTrue(in_array($letter->letter, $letters));
93
 
94
        remove_grade_letters($context, false);
95
 
96
        // Confirm grade letter was deleted.
97
        $this->assertEquals(0, $DB->count_records('grade_letters'));
98
 
99
        // Confirm grade letter is also deleted from cache.
100
        $letters = $cache->get($context->id);
101
        $this->assertFalse($letters);
102
    }
103
 
104
    /**
105
     * Tests the function grade_course_category_delete().
106
     */
11 efrain 107
    public function test_grade_course_category_delete(): void {
1 efrain 108
        global $DB;
109
 
110
        $this->resetAfterTest();
111
 
112
        $category = \core_course_category::create(array('name' => 'Cat1'));
113
 
114
        // Add a grade letter to the category.
115
        $letter = new \stdClass();
116
        $letter->letter = 'M';
117
        $letter->lowerboundary = '100';
118
        $letter->contextid = \context_coursecat::instance($category->id)->id;
119
        $DB->insert_record('grade_letters', $letter);
120
 
121
        grade_course_category_delete($category->id, '', false);
122
 
123
        // Confirm grade letter was deleted.
124
        $this->assertEquals(0, $DB->count_records('grade_letters'));
125
    }
126
 
127
    /**
128
     * Tests the function grade_regrade_final_grades().
129
     */
11 efrain 130
    public function test_grade_regrade_final_grades(): void {
1 efrain 131
        global $DB;
132
 
133
        $this->resetAfterTest();
134
 
135
        // Setup some basics.
136
        $course = $this->getDataGenerator()->create_course();
137
        $user = $this->getDataGenerator()->create_user();
138
        $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
139
 
140
        // We need two grade items.
141
        $params = ['idnumber' => 'g1', 'courseid' => $course->id];
142
        $g1 = new \grade_item($this->getDataGenerator()->create_grade_item($params));
143
        unset($params['idnumber']);
144
        $g2 = new \grade_item($this->getDataGenerator()->create_grade_item($params));
145
 
146
        $category = new \grade_category($this->getDataGenerator()->create_grade_category($params));
147
        $catitem = $category->get_grade_item();
148
 
149
        // Now set a calculation.
150
        $catitem->set_calculation('=[[g1]]');
151
 
152
        $catitem->update();
153
 
154
        // Everything needs updating.
155
        $this->assertEquals(4, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
156
 
157
        grade_regrade_final_grades($course->id);
158
 
159
        // See that everything has been updated.
160
        $this->assertEquals(0, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
161
 
162
        $g1->delete();
163
 
164
        // Now there is one that needs updating.
165
        $this->assertEquals(1, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
166
 
167
        // This can cause an infinite loop if things go... poorly.
168
        grade_regrade_final_grades($course->id);
169
 
170
        // Now because of the failure, two things need updating.
171
        $this->assertEquals(2, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
172
    }
173
 
174
    /**
175
     * Tests for the grade_get_date_for_user_grade function.
176
     *
177
     * @dataProvider grade_get_date_for_user_grade_provider
178
     * @param \stdClass $grade
179
     * @param \stdClass $user
180
     * @param int $expected
181
     */
182
    public function test_grade_get_date_for_user_grade(\stdClass $grade, \stdClass $user, ?int $expected): void {
183
        $this->assertEquals($expected, grade_get_date_for_user_grade($grade, $user));
184
    }
185
 
186
    /**
187
     * Data provider for tests of the grade_get_date_for_user_grade function.
188
     *
189
     * @return array
190
     */
191
    public function grade_get_date_for_user_grade_provider(): array {
192
        $u1 = (object) [
193
            'id' => 42,
194
        ];
195
        $u2 = (object) [
196
            'id' => 930,
197
        ];
198
 
199
        $d1 = 1234567890;
200
        $d2 = 9876543210;
201
 
202
        $g1 = (object) [
203
            'usermodified' => $u1->id,
204
            'dategraded' => $d1,
205
            'datesubmitted' => $d2,
206
        ];
207
        $g2 = (object) [
208
            'usermodified' => $u1->id,
209
            'dategraded' => $d1,
210
            'datesubmitted' => 0,
211
        ];
212
 
213
        $g3 = (object) [
214
            'usermodified' => $u1->id,
215
            'dategraded' => null,
216
            'datesubmitted' => $d2,
217
        ];
218
 
219
        return [
220
            'If the user is the last person to have modified the grade_item then show the date that it was graded' => [
221
                $g1,
222
                $u1,
223
                $d1,
224
            ],
225
            'If there is no grade and there is no feedback, then show graded date as null' => [
226
                $g3,
227
                $u1,
228
                null,
229
            ],
230
            'If the user is not the last person to have modified the grade_item, ' .
231
            'and there is no submission date, then show the date that it was submitted' => [
232
                $g1,
233
                $u2,
234
                $d2,
235
            ],
236
            'If the user is not the last person to have modified the grade_item, ' .
237
            'but there is no submission date, then show the date that it was graded' => [
238
                $g2,
239
                $u2,
240
                $d1,
241
            ],
242
            'If the user is the last person to have modified the grade_item, ' .
243
            'and there is no submission date, then still show the date that it was graded' => [
244
                $g2,
245
                $u1,
246
                $d1,
247
            ],
248
        ];
249
    }
250
 
251
    /**
252
     * Test the caching of grade letters.
253
     */
11 efrain 254
    public function test_get_grade_letters(): void {
1 efrain 255
 
256
        $this->resetAfterTest();
257
 
258
        // Setup some basics.
259
        $course = $this->getDataGenerator()->create_course();
260
        $context = \context_course::instance($course->id);
261
 
262
        $cache = \cache::make('core', 'grade_letters');
263
        $letters = $cache->get($context->id);
264
 
265
        // Make sure the cache is empty.
266
        $this->assertFalse($letters);
267
 
268
        // Now check to see if the letters get cached.
269
        $actual = grade_get_letters($context);
270
 
271
        $expected = $cache->get($context->id);
272
 
273
        $this->assertEquals($expected, $actual);
274
    }
275
 
276
    /**
277
     * Test custom letters.
278
     */
11 efrain 279
    public function test_get_grade_letters_custom(): void {
1 efrain 280
        global $DB;
281
 
282
        $this->resetAfterTest();
283
 
284
        $course = $this->getDataGenerator()->create_course();
285
        $context = \context_course::instance($course->id);
286
 
287
        $cache = \cache::make('core', 'grade_letters');
288
        $letters = $cache->get($context->id);
289
 
290
        // Make sure the cache is empty.
291
        $this->assertFalse($letters);
292
 
293
        // Add a grade letter to the course.
294
        $letter = new \stdClass();
295
        $letter->letter = 'M';
296
        $letter->lowerboundary = '100';
297
        $letter->contextid = $context->id;
298
        $DB->insert_record('grade_letters', $letter);
299
 
300
        $actual = grade_get_letters($context);
301
        $expected = $cache->get($context->id);
302
 
303
        $this->assertEquals($expected, $actual);
304
    }
305
 
306
    /**
307
     * When getting a calculated grade containing an error, we mark grading finished and don't keep trying to regrade.
308
     *
309
     * @covers \grade_get_grades()
310
     * @return void
311
     */
11 efrain 312
    public function test_grade_get_grades_errors(): void {
1 efrain 313
        $this->resetAfterTest();
314
 
315
        // Setup some basics.
316
        $course = $this->getDataGenerator()->create_course();
317
        $user1 = $this->getDataGenerator()->create_user();
318
        $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
319
        $user2 = $this->getDataGenerator()->create_user();
320
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher');
321
        // Set up 2 gradeable activities.
322
        $assign = $this->getDataGenerator()->create_module('assign', ['idnumber' => 'a1', 'course' => $course->id]);
323
        $quiz = $this->getDataGenerator()->create_module('quiz', ['idnumber' => 'q1', 'course' => $course->id]);
324
 
325
        // Create a calculated grade item using the activities.
326
        $params = ['courseid' => $course->id];
327
        $g1 = new \grade_item($this->getDataGenerator()->create_grade_item($params));
328
        $g1->set_calculation('=[[a1]] + [[q1]]');
329
 
330
        // Now delete one of the activities to break the calculation.
331
        course_delete_module($assign->cmid);
332
 
333
        // Course grade item has needsupdate.
334
        $this->assertEquals(1, \grade_item::fetch_course_item($course->id)->needsupdate);
335
 
336
        // Get grades for the quiz, to trigger a regrade.
337
        $this->setUser($user2);
338
        $grades1 = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id);
339
        // We should get an error for the broken calculation.
340
        $this->assertNotEmpty($grades1->errors);
341
        $this->assertEquals(get_string('errorcalculationbroken', 'grades', $g1->itemname), reset($grades1->errors));
342
        // Course grade item should not have needsupdate so that we don't try to regrade again.
343
        $this->assertEquals(0, \grade_item::fetch_course_item($course->id)->needsupdate);
344
 
345
        // Get grades for the quiz again. This should not trigger the regrade and resulting error this time.
346
        $grades2 = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id);
347
        $this->assertEmpty($grades2->errors);
348
    }
349
}