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
declare(strict_types = 1);
18
 
19
namespace mod_scorm;
20
 
21
use advanced_testcase;
22
use cm_info;
23
use coding_exception;
24
use mod_scorm\completion\custom_completion;
25
use moodle_exception;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
global $CFG;
30
require_once($CFG->libdir . '/completionlib.php');
31
require_once($CFG->dirroot.'/mod/scorm/locallib.php');
32
 
33
/**
34
 * Class for unit testing mod_scorm/custom_completion.
35
 *
36
 * @package   mod_scorm
37
 * @copyright 2021 Michael Hawkins <michaelh@moodle.com>
38
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39
 */
40
class custom_completion_test extends advanced_testcase {
41
 
42
    /**
43
     * Data provider for get_state().
44
     *
45
     * @return array[]
46
     */
47
    public function get_state_provider(): array {
48
 
49
        // Prepare various reusable user scorm track data used to mock various completion states/requirements.
50
        $completionincomplete = (object) [
51
            'id' => 1,
52
            'scoid' => 1,
53
            'element' => 'cmi.completion_status',
54
            'value' => 'incomplete',
55
        ];
56
 
57
        $completionpassed = (object) [
58
            'id' => 1,
59
            'scoid' => 1,
60
            'element' => 'cmi.completion_status',
61
            'value' => 'passed',
62
        ];
63
 
64
        $completioncompleted = (object) [
65
            'id' => 1,
66
            'scoid' => 2,
67
            'element' => 'cmi.success_status',
68
            'value' => 'completed',
69
        ];
70
 
71
        $completionscorefail = (object) [
72
            'id' => 1,
73
            'scoid' => 1,
74
            'element' => 'cmi.score.raw',
75
            'value' => '20',
76
        ];
77
 
78
        $completionscorepass = (object) [
79
            'id' => 1,
80
            'scoid' => 1,
81
            'element' => 'cmi.score.raw',
82
            'value' => '100',
83
        ];
84
 
85
        return [
86
            'Undefined completion requirement' => [
87
                'somenonexistentrule', COMPLETION_ENABLED, [$completionincomplete], 0, null, coding_exception::class
88
            ],
89
            'Completion status requirement not available' => [
90
                'completionstatusrequired', COMPLETION_DISABLED, [$completionincomplete], 0, null, moodle_exception::class
91
            ],
92
            'Completion status Passed required, user has no completion status recorded' => [
93
                'completionstatusrequired', 2, [], 0, COMPLETION_INCOMPLETE, null
94
            ],
95
            'Completion status Passed required, user has not passed, can make another attempt' => [
96
                'completionstatusrequired', 2, [$completionincomplete], 0, COMPLETION_INCOMPLETE, null
97
            ],
98
            'Completion status Passed required, user has passed' => [
99
                'completionstatusrequired', 2, [$completionpassed], 0, COMPLETION_COMPLETE, null
100
            ],
101
            'Completion status Completed required, user has not completed, can make another attempt' => [
102
                'completionstatusrequired', 4, [$completionincomplete], 2, COMPLETION_INCOMPLETE, null
103
            ],
104
            'Completion status Completed required, user has completed' => [
105
                'completionstatusrequired', 4, [$completioncompleted], 1, COMPLETION_COMPLETE, null
106
            ],
107
            'Completion status Passed or Completed required, user has only completed, can make another attempt' => [
108
                'completionstatusrequired', 6, [$completioncompleted], 0, COMPLETION_COMPLETE, null
109
            ],
110
            'Completion status Passed or Completed required, user has completed and passed' => [
111
                'completionstatusrequired', 6, [$completionpassed, $completioncompleted], 0, COMPLETION_COMPLETE, null
112
            ],
113
            'Completion status Passed or Completed required, user has not passed or completed, but has another attempt' => [
114
                'completionstatusrequired', 6, [$completionincomplete], 2, COMPLETION_INCOMPLETE, null
115
            ],
116
            'Completion status Passed or Completed required, user has used all attempts, but not passed or completed' => [
117
                'completionstatusrequired', 6, [$completionincomplete], 1, COMPLETION_COMPLETE_FAIL, null
118
            ],
119
            'Completion status Passed required, user has used all attempts and completed, but not passed' => [
120
                'completionstatusrequired', 2, [$completioncompleted], 1, COMPLETION_COMPLETE_FAIL, null
121
            ],
122
            'Completion status Completed required, user has used all attempts, but not completed' => [
123
                'completionstatusrequired', 4, [$completionincomplete], 1, COMPLETION_COMPLETE_FAIL, null
124
            ],
125
            'Completion status Passed or Completed required, user has used all attempts, but not passed' => [
126
                'completionstatusrequired', 6, [$completionincomplete, $completioncompleted], 2, COMPLETION_COMPLETE, null
127
            ],
128
            'Completion score required, user has no score' => [
129
                'completionscorerequired', 80, [], 0, COMPLETION_INCOMPLETE, null
130
            ],
131
            'Completion score required, user score does not meet requirement, can make another attempt' => [
132
                'completionscorerequired', 80, [$completionscorefail], 0, COMPLETION_INCOMPLETE, null
133
            ],
134
            'Completion score required, user has used all attempts, but not reached the score' => [
135
                'completionscorerequired', 80, [$completionscorefail], 1, COMPLETION_COMPLETE_FAIL, null
136
            ],
137
            'Completion score required, user score meets requirement' => [
138
                'completionscorerequired', 80, [$completionscorepass], 0, COMPLETION_COMPLETE, null
139
            ],
140
            'Completion of all scos required, user has not completed, can make another attempt' => [
141
                'completionstatusallscos', 1, [$completionincomplete, $completioncompleted], 3, COMPLETION_INCOMPLETE, null
142
            ],
143
            'Completion of all scos required, user has completed' => [
144
                'completionstatusallscos', 1, [$completionpassed, $completioncompleted], 2, COMPLETION_COMPLETE, null
145
            ],
146
            'Completion of all scos required, user has used all attempts, but not completed all scos' => [
147
                'completionstatusallscos', 1, [$completionincomplete, $completioncompleted], 2, COMPLETION_COMPLETE_FAIL, null
148
            ],
149
        ];
150
    }
151
 
152
    /**
153
     * Test for get_state().
154
     *
155
     * @dataProvider get_state_provider
156
     * @param string $rule The custom completion condition.
157
     * @param int $rulevalue The custom completion rule value.
158
     * @param array $uservalue The relevant record database mock data recorded against the user for the rule.
159
     * @param int $maxattempts The number of attempts the activity allows (0 = unlimited).
160
     * @param int|null $status Expected completion status for the rule.
161
     * @param string|null $exception Expected exception.
162
     */
163
    public function test_get_state(string $rule, int $rulevalue, array $uservalue, int $maxattempts, ?int $status,
11 efrain 164
            ?string $exception): void {
1 efrain 165
        global $DB;
166
 
167
        if (!is_null($exception)) {
168
            $this->expectException($exception);
169
        }
170
 
171
        // Custom completion rule data for cm_info::customdata.
172
        $customdataval = [
173
            'customcompletionrules' => [
174
                $rule => $rulevalue
175
            ]
176
        ];
177
 
178
        // Build a mock cm_info instance.
179
        $mockcminfo = $this->getMockBuilder(cm_info::class)
180
            ->disableOriginalConstructor()
181
            ->onlyMethods(['__get'])
182
            ->getMock();
183
 
184
        // Mock the return of the magic getter method when fetching the cm_info object's
185
        // customdata and instance values.
186
        $mockcminfo->expects($this->any())
187
            ->method('__get')
188
            ->will($this->returnValueMap([
189
                ['customdata', $customdataval],
190
                ['instance', 1],
191
            ]));
192
 
193
        // Mock the DB call fetching user's SCORM track data.
194
        $DB = $this->createMock(get_class($DB));
195
        $DB->expects($this->atMost(1))
196
            ->method('get_records_sql')
197
            ->willReturn($uservalue);
198
 
199
        // For completed all scos tests, mock the DB call that fetches the sco IDs.
200
        if ($rule === 'completionstatusallscos') {
201
            $returnscos = [];
202
 
203
            foreach ($uservalue as $data) {
204
                $returnscos[$data->scoid] = (object) ['id' => $data->scoid];
205
            }
206
 
207
            $DB->expects($this->atMost(1))
208
                ->method('get_records')
209
                ->willReturn($returnscos);
210
        }
211
 
212
        // Anything not complete will check if attempts have been exhausted, mock the DB calls for that check.
213
        if ($status != COMPLETION_COMPLETE) {
214
            $mockscorm = (object) [
215
                'id' => 1,
216
                'version' => SCORM_13,
217
                'grademethod' => GRADESCOES,
218
                'maxattempt' => $maxattempts,
219
            ];
220
 
221
            $DB->expects($this->atMost(1))
222
                ->method('get_record')
223
                ->willReturn($mockscorm);
224
 
225
            $DB->expects($this->atMost(1))
226
                ->method('count_records_sql')
227
                ->willReturn(count($uservalue));
228
        }
229
 
230
        $customcompletion = new custom_completion($mockcminfo, 2);
231
 
232
        $this->assertEquals($status, $customcompletion->get_state($rule));
233
    }
234
 
235
    /**
236
     * Test for get_defined_custom_rules().
237
     */
11 efrain 238
    public function test_get_defined_custom_rules(): void {
1 efrain 239
        $expectedrules = [
240
            'completionstatusrequired',
241
            'completionscorerequired',
242
            'completionstatusallscos',
243
        ];
244
 
245
        $definedrules = custom_completion::get_defined_custom_rules();
246
        $this->assertCount(3, $definedrules);
247
 
248
        foreach ($definedrules as $definedrule) {
249
            $this->assertContains($definedrule, $expectedrules);
250
        }
251
    }
252
 
253
    /**
254
     * Test for get_defined_custom_rule_descriptions().
255
     */
11 efrain 256
    public function test_get_custom_rule_descriptions(): void {
1 efrain 257
        // Get defined custom rules.
258
        $rules = custom_completion::get_defined_custom_rules();
259
 
260
        // Build a mock cm_info instance.
261
        $mockcminfo = $this->getMockBuilder(cm_info::class)
262
            ->disableOriginalConstructor()
263
            ->onlyMethods(['__get'])
264
            ->getMock();
265
 
266
        // Instantiate a custom_completion object using the mocked cm_info.
267
        $customcompletion = new custom_completion($mockcminfo, 1);
268
 
269
        // Get custom rule descriptions.
270
        $ruledescriptions = $customcompletion->get_custom_rule_descriptions();
271
 
272
        // Confirm that defined rules and rule descriptions are consistent with each other.
273
        $this->assertEquals(count($rules), count($ruledescriptions));
274
        foreach ($rules as $rule) {
275
            $this->assertArrayHasKey($rule, $ruledescriptions);
276
        }
277
    }
278
 
279
    /**
280
     * Test for is_defined().
281
     */
11 efrain 282
    public function test_is_defined(): void {
1 efrain 283
        // Build a mock cm_info instance.
284
        $mockcminfo = $this->getMockBuilder(cm_info::class)
285
            ->disableOriginalConstructor()
286
            ->getMock();
287
 
288
        $customcompletion = new custom_completion($mockcminfo, 1);
289
 
290
        // All rules are defined.
291
        $this->assertTrue($customcompletion->is_defined('completionstatusrequired'));
292
        $this->assertTrue($customcompletion->is_defined('completionscorerequired'));
293
        $this->assertTrue($customcompletion->is_defined('completionstatusallscos'));
294
 
295
        // Undefined rule is not found.
296
        $this->assertFalse($customcompletion->is_defined('somerandomrule'));
297
    }
298
 
299
    /**
300
     * Data provider for test_get_available_custom_rules().
301
     *
302
     * @return array[]
303
     */
304
    public function get_available_custom_rules_provider(): array {
305
        return [
306
            'Completion status enabled only' => [
307
                [
308
                    'completionstatusrequired' => 4,
309
                    'completionscorerequired' => COMPLETION_DISABLED,
310
                    'completionstatusallscos' => COMPLETION_DISABLED,
311
                ],
312
                ['completionstatusrequired'],
313
            ],
314
            'Completion score enabled only' => [
315
                [
316
                    'completionstatusrequired' => COMPLETION_DISABLED,
317
                    'completionscorerequired' => 80,
318
                    'completionstatusallscos' => COMPLETION_DISABLED,
319
                ],
320
                ['completionscorerequired'],
321
            ],
322
            'Completion status and all scos completed both enabled' => [
323
                [
324
                    'completionstatusrequired' => 2,
325
                    'completionscorerequired' => COMPLETION_DISABLED,
326
                    'completionstatusallscos' => COMPLETION_ENABLED,
327
                ],
328
                ['completionstatusrequired', 'completionstatusallscos'],
329
            ],
330
            'Completion status and score both enabled' => [
331
                [
332
                    'completionstatusrequired' => COMPLETION_ENABLED,
333
                    'completionscorerequired' => 80,
334
                    'completionstatusallscos' => COMPLETION_DISABLED,
335
                ],
336
                ['completionstatusrequired', 'completionscorerequired'],
337
            ],
338
            'All custom completion conditions enabled' => [
339
                [
340
                    'completionstatusrequired' => 6,
341
                    'completionscorerequired' => 80,
342
                    'completionstatusallscos' => COMPLETION_ENABLED,
343
                ],
344
                ['completionstatusrequired', 'completionscorerequired', 'completionstatusallscos'],
345
            ],
346
        ];
347
    }
348
 
349
    /**
350
     * Test for get_available_custom_rules().
351
     *
352
     * @dataProvider get_available_custom_rules_provider
353
     * @param array $completionrulesvalues
354
     * @param array $expected
355
     */
11 efrain 356
    public function test_get_available_custom_rules(array $completionrulesvalues, array $expected): void {
1 efrain 357
        $customcompletionrules = [
358
            'customcompletionrules' => $completionrulesvalues,
359
        ];
360
 
361
        // Build a mock cm_info instance.
362
        $mockcminfo = $this->getMockBuilder(cm_info::class)
363
            ->disableOriginalConstructor()
364
            ->onlyMethods(['__get'])
365
            ->getMock();
366
 
367
        // Mock the return of magic getter for the customdata attribute.
368
        $mockcminfo->expects($this->any())
369
            ->method('__get')
370
            ->with('customdata')
371
            ->willReturn($customcompletionrules);
372
 
373
        $customcompletion = new custom_completion($mockcminfo, 1);
374
        $this->assertEquals($expected, $customcompletion->get_available_custom_rules());
375
    }
376
}