MDL-81717 h5p: Improve robustness content type fetching
[moodle.git] / completion / tests / cm_completion_details_test.php
blob5fca54e8ee83c4d1ea5e06f14512ca9c191e5c34
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.
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/>.
17 /**
18 * Contains unit tests for core_completion/cm_completion_details.
20 * @package core_completion
21 * @copyright 2021 Jun Pataleta <jun@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 declare(strict_types = 1);
27 namespace core_completion;
29 use advanced_testcase;
30 use cm_info;
31 use completion_info;
33 defined('MOODLE_INTERNAL') || die();
35 global $CFG;
36 require_once($CFG->libdir . '/completionlib.php');
38 /**
39 * Class for unit testing core_completion/cm_completion_details.
41 * @package core_completion
42 * @copyright 2021 Jun Pataleta <jun@moodle.com>
43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44 * @coversDefaultClass \core_completion\cm_completion_details
46 class cm_completion_details_test extends advanced_testcase {
48 /** @var completion_info A completion object. */
49 protected $completioninfo = null;
51 /**
52 * Fetches a mocked cm_completion_details instance.
54 * @param int|null $completion The completion tracking mode for the module.
55 * @param array $completionoptions Completion options (e.g. completionview, completionusegrade, etc.)
56 * @param object $mockcompletiondata Mock data to be returned by get_data.
57 * @param string $modname The modname to set in the cm if a specific one is required.
58 * @return cm_completion_details
60 protected function setup_data(?int $completion, array $completionoptions = [],
61 object $mockcompletiondata = null, $modname = 'somenonexistentmod'): cm_completion_details {
62 if (is_null($completion)) {
63 $completion = COMPLETION_TRACKING_AUTOMATIC;
66 // Mock a completion_info instance so we can simply mock the returns of completion_info::get_data() later.
67 $this->completioninfo = $this->getMockBuilder(completion_info::class)
68 ->disableOriginalConstructor()
69 ->getMock();
71 // Mock return of completion_info's is_enabled() method to match the expected completion tracking for the module.
72 $this->completioninfo->expects($this->any())
73 ->method('is_enabled')
74 ->willReturn($completion);
76 if (!empty($mockcompletiondata)) {
77 $this->completioninfo->expects($this->any())
78 ->method('get_data')
79 ->willReturn($mockcompletiondata);
82 // Build a mock cm_info instance.
83 $mockcminfo = $this->getMockBuilder(cm_info::class)
84 ->disableOriginalConstructor()
85 ->onlyMethods(['__get'])
86 ->getMock();
88 // Mock the return of the magic getter method when fetching the cm_info object's customdata and instance values.
89 $mockcminfo->expects($this->any())
90 ->method('__get')
91 ->will($this->returnValueMap([
92 ['completion', $completion],
93 ['instance', 1],
94 ['modname', $modname],
95 ['completionview', $completionoptions['completionview'] ?? COMPLETION_VIEW_NOT_REQUIRED],
96 ['completiongradeitemnumber', $completionoptions['completionusegrade'] ?? null],
97 ['completionpassgrade', $completionoptions['completionpassgrade'] ?? null],
98 ]));
100 return new cm_completion_details($this->completioninfo, $mockcminfo, 2);
104 * Provides data for test_has_completion().
106 * @return array[]
108 public function has_completion_provider(): array {
109 return [
110 'Automatic' => [
111 COMPLETION_TRACKING_AUTOMATIC, true
113 'Manual' => [
114 COMPLETION_TRACKING_MANUAL, true
116 'None' => [
117 COMPLETION_TRACKING_NONE, false
123 * Test for has_completion().
125 * @covers ::has_completion
126 * @dataProvider has_completion_provider
127 * @param int $completion The completion tracking mode.
128 * @param bool $expectedresult Expected result.
130 public function test_has_completion(int $completion, bool $expectedresult) {
131 $cmcompletion = $this->setup_data($completion);
133 $this->assertEquals($expectedresult, $cmcompletion->has_completion());
137 * Provides data for test_is_automatic().
139 * @return array[]
141 public function is_automatic_provider(): array {
142 return [
143 'Automatic' => [
144 COMPLETION_TRACKING_AUTOMATIC, true
146 'Manual' => [
147 COMPLETION_TRACKING_MANUAL, false
149 'None' => [
150 COMPLETION_TRACKING_NONE, false
156 * Test for is_available().
158 * @covers ::is_automatic
159 * @dataProvider is_automatic_provider
160 * @param int $completion The completion tracking mode.
161 * @param bool $expectedresult Expected result.
163 public function test_is_automatic(int $completion, bool $expectedresult) {
164 $cmcompletion = $this->setup_data($completion);
166 $this->assertEquals($expectedresult, $cmcompletion->is_automatic());
170 * Provides data for test_is_manual().
172 * @return array[]
174 public function is_manual_provider(): array {
175 return [
176 'Automatic' => [
177 COMPLETION_TRACKING_AUTOMATIC, false
179 'Manual' => [
180 COMPLETION_TRACKING_MANUAL, true
182 'None' => [
183 COMPLETION_TRACKING_NONE, false
189 * Test for is_manual().
191 * @covers ::is_manual
192 * @dataProvider is_manual_provider
193 * @param int $completion The completion tracking mode.
194 * @param bool $expectedresult Expected result.
196 public function test_is_manual(int $completion, bool $expectedresult) {
197 $cmcompletion = $this->setup_data($completion);
199 $this->assertEquals($expectedresult, $cmcompletion->is_manual());
203 * Data provider for test_get_overall_completion().
204 * @return array[]
206 public function overall_completion_provider(): array {
207 return [
208 'Complete' => [COMPLETION_COMPLETE],
209 'Incomplete' => [COMPLETION_INCOMPLETE],
214 * Test for get_overall_completion().
216 * @covers ::get_overall_completion
217 * @dataProvider overall_completion_provider
218 * @param int $state
220 public function test_get_overall_completion(int $state) {
221 $completiondata = (object)['completionstate' => $state];
222 $cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, [], $completiondata);
223 $this->assertEquals($state, $cmcompletion->get_overall_completion());
227 * Data provider for test_is_overall_complete().
228 * @return array[]
230 public static function is_overall_complete_provider(): array {
231 return [
232 'Automatic, require view, not viewed' => [
233 'expected' => false,
234 'completion' => COMPLETION_TRACKING_AUTOMATIC,
235 'completionstate' => COMPLETION_INCOMPLETE,
236 'completionview' => COMPLETION_INCOMPLETE,
237 'completiongrade' => null,
238 'completionpassgrade' => null,
240 'Automatic, require view, viewed' => [
241 'expected' => true,
242 'completion' => COMPLETION_TRACKING_AUTOMATIC,
243 'completionstate' => COMPLETION_COMPLETE,
244 'completionview' => COMPLETION_COMPLETE,
245 'completiongrade' => null,
246 'completionpassgrade' => null,
248 'Automatic, require grade, not graded' => [
249 'expected' => false,
250 'completion' => COMPLETION_TRACKING_AUTOMATIC,
251 'completionstate' => COMPLETION_INCOMPLETE,
252 'completionview' => null,
253 'completiongrade' => COMPLETION_INCOMPLETE,
254 'completionpassgrade' => null,
256 'Automatic, require grade, graded with fail' => [
257 'expected' => true,
258 'completion' => COMPLETION_TRACKING_AUTOMATIC,
259 'completionstate' => COMPLETION_COMPLETE_FAIL,
260 'completionview' => null,
261 'completiongrade' => COMPLETION_COMPLETE_FAIL,
262 'completionpassgrade' => null,
264 'Automatic, require grade, graded with passing' => [
265 'expected' => true,
266 'completion' => COMPLETION_TRACKING_AUTOMATIC,
267 'completionstate' => COMPLETION_COMPLETE_PASS,
268 'completionview' => null,
269 'completiongrade' => COMPLETION_COMPLETE_PASS,
270 'completionpassgrade' => null,
272 'Automatic, require passgrade, not graded' => [
273 'expected' => false,
274 'completion' => COMPLETION_TRACKING_AUTOMATIC,
275 'completionstate' => COMPLETION_INCOMPLETE,
276 'completionview' => null,
277 'completiongrade' => null,
278 'completionpassgrade' => COMPLETION_INCOMPLETE,
280 'Automatic, require passgrade, graded with fail' => [
281 'expected' => false,
282 'completion' => COMPLETION_TRACKING_AUTOMATIC,
283 'completionstate' => COMPLETION_COMPLETE_FAIL,
284 'completionview' => null,
285 'completiongrade' => null,
286 'completionpassgrade' => COMPLETION_COMPLETE_FAIL,
288 'Automatic, require passgrade, graded with passing' => [
289 'expected' => true,
290 'completion' => COMPLETION_TRACKING_AUTOMATIC,
291 'completionstate' => COMPLETION_COMPLETE_PASS,
292 'completionview' => null,
293 'completiongrade' => null,
294 'completionpassgrade' => COMPLETION_COMPLETE_PASS,
296 'Manual, incomplete' => [
297 'expected' => false,
298 'completion' => COMPLETION_TRACKING_MANUAL,
299 'completionstate' => COMPLETION_INCOMPLETE,
301 'Manual, complete' => [
302 'expected' => true,
303 'completion' => COMPLETION_TRACKING_MANUAL,
304 'completionstate' => COMPLETION_COMPLETE,
306 'None, incomplete' => [
307 'expected' => false,
308 'completion' => COMPLETION_TRACKING_NONE,
309 'completionstate' => COMPLETION_INCOMPLETE,
311 'None, complete' => [
312 'expected' => false,
313 'completion' => COMPLETION_TRACKING_NONE,
314 'completionstate' => COMPLETION_COMPLETE,
320 * Test for is_overall_complete().
322 * @covers ::is_overall_complete
323 * @dataProvider is_overall_complete_provider
324 * @param bool $expected Expected result returned by is_overall_complete().
325 * @param int $completion The completion tracking mode.
326 * @param int $completionstate The overall completion state.
327 * @param int|null $completionview Completion status of the "view" completion condition.
328 * @param int|null $completiongrade Completion status of the "must receive grade" completion condition.
329 * @param int|null $completionpassgrade Completion status of the "must receive passing grade" completion condition.
331 public function test_is_overall_complete(
332 bool $expected,
333 int $completion,
334 int $completionstate,
335 ?int $completionview = null,
336 ?int $completiongrade = null,
337 ?int $completionpassgrade = null,
338 ): void {
339 $options = [];
340 $getdatareturn = (object)[
341 'completionstate' => $completionstate,
342 'viewed' => $completionview,
343 'completiongrade' => $completiongrade,
344 'passgrade' => $completionpassgrade,
347 if (!is_null($completionview)) {
348 $options['completionview'] = true;
350 if (!is_null($completiongrade)) {
351 $options['completionusegrade'] = true;
353 if (!is_null($completionpassgrade)) {
354 $options['completionpassgrade'] = true;
357 $cmcompletion = $this->setup_data($completion, $options, $getdatareturn);
358 $this->assertEquals($expected, $cmcompletion->is_overall_complete());
362 * Data provider for test_get_details().
363 * @return array[]
365 public function get_details_provider() {
366 return [
367 'No completion tracking' => [
368 COMPLETION_TRACKING_NONE, null, null, null, []
370 'Manual completion tracking' => [
371 COMPLETION_TRACKING_MANUAL, null, null, null, []
373 'Automatic, require view, not viewed' => [
374 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, null, null, [
375 'completionview' => (object)[
376 'status' => COMPLETION_INCOMPLETE,
377 'description' => get_string('detail_desc:view', 'completion'),
381 'Automatic, require view, viewed' => [
382 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, null, null, [
383 'completionview' => (object)[
384 'status' => COMPLETION_COMPLETE,
385 'description' => get_string('detail_desc:view', 'completion'),
389 'Automatic, require grade, incomplete' => [
390 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_INCOMPLETE, null, [
391 'completionusegrade' => (object)[
392 'status' => COMPLETION_INCOMPLETE,
393 'description' => get_string('detail_desc:receivegrade', 'completion'),
397 'Automatic, require grade, complete' => [
398 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, null, [
399 'completionusegrade' => (object)[
400 'status' => COMPLETION_COMPLETE,
401 'description' => get_string('detail_desc:receivegrade', 'completion'),
405 'Automatic, require view (complete) and grade (incomplete)' => [
406 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, null, [
407 'completionview' => (object)[
408 'status' => COMPLETION_COMPLETE,
409 'description' => get_string('detail_desc:view', 'completion'),
411 'completionusegrade' => (object)[
412 'status' => COMPLETION_INCOMPLETE,
413 'description' => get_string('detail_desc:receivegrade', 'completion'),
417 'Automatic, require view (incomplete) and grade (complete)' => [
418 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, null, [
419 'completionview' => (object)[
420 'status' => COMPLETION_INCOMPLETE,
421 'description' => get_string('detail_desc:view', 'completion'),
423 'completionusegrade' => (object)[
424 'status' => COMPLETION_COMPLETE,
425 'description' => get_string('detail_desc:receivegrade', 'completion'),
429 'Automatic, require grade, require pass grade, complete' => [
430 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [
431 'completionusegrade' => (object)[
432 'status' => COMPLETION_COMPLETE,
433 'description' => get_string('detail_desc:receivegrade', 'completion'),
435 'completionpassgrade' => (object)[
436 'status' => COMPLETION_COMPLETE,
437 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
441 'Automatic, require grade, require pass grade, incomplete' => [
442 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, [
443 'completionusegrade' => (object)[
444 'status' => COMPLETION_COMPLETE,
445 'description' => get_string('detail_desc:receivegrade', 'completion'),
447 'completionpassgrade' => (object)[
448 'status' => COMPLETION_INCOMPLETE,
449 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
453 'Automatic, require view (complete), require grade(complete), require pass grade(complete)' => [
454 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [
455 'completionview' => (object)[
456 'status' => COMPLETION_COMPLETE,
457 'description' => get_string('detail_desc:view', 'completion'),
459 'completionusegrade' => (object)[
460 'status' => COMPLETION_COMPLETE,
461 'description' => get_string('detail_desc:receivegrade', 'completion'),
463 'completionpassgrade' => (object)[
464 'status' => COMPLETION_COMPLETE,
465 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
469 'Automatic, require view (incomplete), require grade(complete), require pass grade(complete)' => [
470 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [
471 'completionview' => (object)[
472 'status' => COMPLETION_INCOMPLETE,
473 'description' => get_string('detail_desc:view', 'completion'),
475 'completionusegrade' => (object)[
476 'status' => COMPLETION_COMPLETE,
477 'description' => get_string('detail_desc:receivegrade', 'completion'),
479 'completionpassgrade' => (object)[
480 'status' => COMPLETION_COMPLETE,
481 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
489 * Test for \core_completion\cm_completion_details::get_details().
491 * @covers ::get_details
492 * @dataProvider get_details_provider
493 * @param int $completion The completion tracking mode.
494 * @param int|null $completionview Completion status of the "view" completion condition.
495 * @param int|null $completiongrade Completion status of the "must receive grade" completion condition.
496 * @param int|null $completionpassgrade Completion status of the "must receive passing grade" completion condition.
497 * @param array $expecteddetails Expected completion details returned by get_details().
499 public function test_get_details(int $completion, ?int $completionview,
500 ?int $completiongrade, ?int $completionpassgrade, array $expecteddetails) {
501 $options = [];
502 $getdatareturn = (object)[
503 'viewed' => $completionview,
504 'completiongrade' => $completiongrade,
505 'passgrade' => $completionpassgrade,
508 if (!is_null($completionview)) {
509 $options['completionview'] = true;
511 if (!is_null($completiongrade)) {
512 $options['completionusegrade'] = true;
514 if (!is_null($completionpassgrade)) {
515 $options['completionpassgrade'] = true;
518 $cmcompletion = $this->setup_data($completion, $options, $getdatareturn);
519 $this->assertEquals($expecteddetails, $cmcompletion->get_details());
523 * Data provider for test_get_details_custom_order().
524 * @return array[]
526 public function get_details_custom_order_provider() {
527 return [
528 'Custom and view/grade standard conditions, view first and grade last' => [
529 true,
530 true,
532 'completionsubmit' => true,
534 'assign',
535 ['completionview', 'completionsubmit', 'completionusegrade'],
537 'Custom and view/grade standard conditions, grade not last' => [
538 true,
539 true,
541 'completionminattempts' => 2,
542 'completionusegrade' => 50,
543 'completionpassorattemptsexhausted' => 1,
545 'quiz',
546 ['completionview', 'completionminattempts', 'completionusegrade', 'completionpassorattemptsexhausted'],
548 'Custom and grade standard conditions only, no view condition' => [
549 false,
550 true,
552 'completionsubmit' => true,
554 'assign',
555 ['completionsubmit', 'completionusegrade'],
557 'Custom and view standard conditions only, no grade condition' => [
558 true,
559 false,
561 'completionsubmit' => true
563 'assign',
564 ['completionview', 'completionsubmit'],
566 'View and grade conditions only, activity with no custom conditions' => [
567 true,
568 true,
570 'completionview' => true,
571 'completionusegrade' => true
573 'workshop',
574 ['completionview', 'completionusegrade'],
576 'View condition only, activity with no custom conditions' => [
577 true,
578 false,
580 'completionview' => true,
582 'workshop',
583 ['completionview'],
589 * Test custom sort order is functioning in \core_completion\cm_completion_details::get_details().
591 * @covers ::get_details
592 * @dataProvider get_details_custom_order_provider
593 * @param bool $completionview Completion status of the "view" completion condition.
594 * @param bool $completiongrade Completion status of the "must receive grade" completion condition.
595 * @param array $customcompletionrules Custom completion requirements, along with their values.
596 * @param string $modname The name of the module having data fetched.
597 * @param array $expectedorder The expected order of completion conditions returned about the module.
599 public function test_get_details_custom_order(bool $completionview, bool $completiongrade, array $customcompletionrules,
600 string $modname, array $expectedorder) {
602 $options['customcompletion'] = [];
603 $customcompletiondata = [];
605 if ($completionview) {
606 $options['completionview'] = true;
609 if ($completiongrade) {
610 $options['completionusegrade'] = true;
613 // Set up the completion rules for the completion info.
614 foreach ($customcompletionrules as $customtype => $isenabled) {
615 $customcompletiondata[$customtype] = COMPLETION_COMPLETE;
618 $getdatareturn = (object)[
619 'viewed' => $completionview ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE,
620 'completiongrade' => $completiongrade ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE,
621 'customcompletion' => $customcompletiondata,
624 $cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, $options, $getdatareturn, $modname);
626 $this->completioninfo->expects($this->any())
627 ->method('get_data')
628 ->willReturn($getdatareturn);
630 $fetcheddetails = $cmcompletion->get_details();
632 // Check the expected number of items are returned, and sorted in the correct order.
633 $this->assertCount(count($expectedorder), $fetcheddetails);
634 $this->assertTrue((array_keys($fetcheddetails) === $expectedorder));