Differences Between: [Versions 311 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
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 * Contains unit tests for core_completion/cm_completion_details. 19 * 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 23 */ 24 25 declare(strict_types = 1); 26 27 namespace core_completion; 28 29 use advanced_testcase; 30 use cm_info; 31 use completion_info; 32 33 defined('MOODLE_INTERNAL') || die(); 34 35 global $CFG; 36 require_once($CFG->libdir . '/completionlib.php'); 37 38 /** 39 * Class for unit testing core_completion/cm_completion_details. 40 * 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 45 */ 46 class cm_completion_details_test extends advanced_testcase { 47 48 /** @var completion_info A completion object. */ 49 protected $completioninfo = null; 50 51 /** 52 * Fetches a mocked cm_completion_details instance. 53 * 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 59 */ 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; 64 } 65 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(); 70 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); 75 76 if (!empty($mockcompletiondata)) { 77 $this->completioninfo->expects($this->any()) 78 ->method('get_data') 79 ->willReturn($mockcompletiondata); 80 } 81 82 // Build a mock cm_info instance. 83 $mockcminfo = $this->getMockBuilder(cm_info::class) 84 ->disableOriginalConstructor() 85 ->onlyMethods(['__get']) 86 ->getMock(); 87 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 ])); 99 100 return new cm_completion_details($this->completioninfo, $mockcminfo, 2); 101 } 102 103 /** 104 * Provides data for test_has_completion(). 105 * 106 * @return array[] 107 */ 108 public function has_completion_provider(): array { 109 return [ 110 'Automatic' => [ 111 COMPLETION_TRACKING_AUTOMATIC, true 112 ], 113 'Manual' => [ 114 COMPLETION_TRACKING_MANUAL, true 115 ], 116 'None' => [ 117 COMPLETION_TRACKING_NONE, false 118 ], 119 ]; 120 } 121 122 /** 123 * Test for has_completion(). 124 * 125 * @covers ::has_completion 126 * @dataProvider has_completion_provider 127 * @param int $completion The completion tracking mode. 128 * @param bool $expectedresult Expected result. 129 */ 130 public function test_has_completion(int $completion, bool $expectedresult) { 131 $cmcompletion = $this->setup_data($completion); 132 133 $this->assertEquals($expectedresult, $cmcompletion->has_completion()); 134 } 135 136 /** 137 * Provides data for test_is_automatic(). 138 * 139 * @return array[] 140 */ 141 public function is_automatic_provider(): array { 142 return [ 143 'Automatic' => [ 144 COMPLETION_TRACKING_AUTOMATIC, true 145 ], 146 'Manual' => [ 147 COMPLETION_TRACKING_MANUAL, false 148 ], 149 'None' => [ 150 COMPLETION_TRACKING_NONE, false 151 ], 152 ]; 153 } 154 155 /** 156 * Test for is_available(). 157 * 158 * @covers ::is_automatic 159 * @dataProvider is_automatic_provider 160 * @param int $completion The completion tracking mode. 161 * @param bool $expectedresult Expected result. 162 */ 163 public function test_is_automatic(int $completion, bool $expectedresult) { 164 $cmcompletion = $this->setup_data($completion); 165 166 $this->assertEquals($expectedresult, $cmcompletion->is_automatic()); 167 } 168 169 /** 170 * Provides data for test_is_manual(). 171 * 172 * @return array[] 173 */ 174 public function is_manual_provider(): array { 175 return [ 176 'Automatic' => [ 177 COMPLETION_TRACKING_AUTOMATIC, false 178 ], 179 'Manual' => [ 180 COMPLETION_TRACKING_MANUAL, true 181 ], 182 'None' => [ 183 COMPLETION_TRACKING_NONE, false 184 ], 185 ]; 186 } 187 188 /** 189 * Test for is_manual(). 190 * 191 * @covers ::is_manual 192 * @dataProvider is_manual_provider 193 * @param int $completion The completion tracking mode. 194 * @param bool $expectedresult Expected result. 195 */ 196 public function test_is_manual(int $completion, bool $expectedresult) { 197 $cmcompletion = $this->setup_data($completion); 198 199 $this->assertEquals($expectedresult, $cmcompletion->is_manual()); 200 } 201 202 /** 203 * Data provider for test_get_overall_completion(). 204 * @return array[] 205 */ 206 public function overall_completion_provider(): array { 207 return [ 208 'Complete' => [COMPLETION_COMPLETE], 209 'Incomplete' => [COMPLETION_INCOMPLETE], 210 ]; 211 } 212 213 /** 214 * Test for get_overall_completion(). 215 * 216 * @covers ::get_overall_completion 217 * @dataProvider overall_completion_provider 218 * @param int $state 219 */ 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()); 224 } 225 226 /** 227 * Data provider for test_is_overall_complete(). 228 * @return array[] 229 */ 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, 239 ], 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, 247 ], 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, 255 ], 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, 263 ], 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, 271 ], 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, 279 ], 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, 287 ], 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, 295 ], 296 'Manual, incomplete' => [ 297 'expected' => false, 298 'completion' => COMPLETION_TRACKING_MANUAL, 299 'completionstate' => COMPLETION_INCOMPLETE, 300 ], 301 'Manual, complete' => [ 302 'expected' => true, 303 'completion' => COMPLETION_TRACKING_MANUAL, 304 'completionstate' => COMPLETION_COMPLETE, 305 ], 306 'None, incomplete' => [ 307 'expected' => false, 308 'completion' => COMPLETION_TRACKING_NONE, 309 'completionstate' => COMPLETION_INCOMPLETE, 310 ], 311 'None, complete' => [ 312 'expected' => false, 313 'completion' => COMPLETION_TRACKING_NONE, 314 'completionstate' => COMPLETION_COMPLETE, 315 ], 316 ]; 317 } 318 319 /** 320 * Test for is_overall_complete(). 321 * 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. 330 */ 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, 345 ]; 346 347 if (!is_null($completionview)) { 348 $options['completionview'] = true; 349 } 350 if (!is_null($completiongrade)) { 351 $options['completionusegrade'] = true; 352 } 353 if (!is_null($completionpassgrade)) { 354 $options['completionpassgrade'] = true; 355 } 356 357 $cmcompletion = $this->setup_data($completion, $options, $getdatareturn); 358 $this->assertEquals($expected, $cmcompletion->is_overall_complete()); 359 } 360 361 /** 362 * Data provider for test_get_details(). 363 * @return array[] 364 */ 365 public function get_details_provider() { 366 return [ 367 'No completion tracking' => [ 368 COMPLETION_TRACKING_NONE, null, null, null, [] 369 ], 370 'Manual completion tracking' => [ 371 COMPLETION_TRACKING_MANUAL, null, null, null, [] 372 ], 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'), 378 ] 379 ] 380 ], 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'), 386 ] 387 ] 388 ], 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'), 394 ] 395 ] 396 ], 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'), 402 ] 403 ] 404 ], 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'), 410 ], 411 'completionusegrade' => (object)[ 412 'status' => COMPLETION_INCOMPLETE, 413 'description' => get_string('detail_desc:receivegrade', 'completion'), 414 ] 415 ] 416 ], 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'), 422 ], 423 'completionusegrade' => (object)[ 424 'status' => COMPLETION_COMPLETE, 425 'description' => get_string('detail_desc:receivegrade', 'completion'), 426 ] 427 ] 428 ], 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'), 434 ], 435 'completionpassgrade' => (object)[ 436 'status' => COMPLETION_COMPLETE, 437 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 438 ], 439 ] 440 ], 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'), 446 ], 447 'completionpassgrade' => (object)[ 448 'status' => COMPLETION_INCOMPLETE, 449 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 450 ], 451 ] 452 ], 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'), 458 ], 459 'completionusegrade' => (object)[ 460 'status' => COMPLETION_COMPLETE, 461 'description' => get_string('detail_desc:receivegrade', 'completion'), 462 ], 463 'completionpassgrade' => (object)[ 464 'status' => COMPLETION_COMPLETE, 465 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 466 ], 467 ] 468 ], 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'), 474 ], 475 'completionusegrade' => (object)[ 476 'status' => COMPLETION_COMPLETE, 477 'description' => get_string('detail_desc:receivegrade', 'completion'), 478 ], 479 'completionpassgrade' => (object)[ 480 'status' => COMPLETION_COMPLETE, 481 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 482 ], 483 ] 484 ], 485 ]; 486 } 487 488 /** 489 * Test for \core_completion\cm_completion_details::get_details(). 490 * 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(). 498 */ 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, 506 ]; 507 508 if (!is_null($completionview)) { 509 $options['completionview'] = true; 510 } 511 if (!is_null($completiongrade)) { 512 $options['completionusegrade'] = true; 513 } 514 if (!is_null($completionpassgrade)) { 515 $options['completionpassgrade'] = true; 516 } 517 518 $cmcompletion = $this->setup_data($completion, $options, $getdatareturn); 519 $this->assertEquals($expecteddetails, $cmcompletion->get_details()); 520 } 521 522 /** 523 * Data provider for test_get_details_custom_order(). 524 * @return array[] 525 */ 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, 531 [ 532 'completionsubmit' => true, 533 ], 534 'assign', 535 ['completionview', 'completionsubmit', 'completionusegrade'], 536 ], 537 'Custom and view/grade standard conditions, grade not last' => [ 538 true, 539 true, 540 [ 541 'completionminattempts' => 2, 542 'completionusegrade' => 50, 543 'completionpassorattemptsexhausted' => 1, 544 ], 545 'quiz', 546 ['completionview', 'completionminattempts', 'completionusegrade', 'completionpassorattemptsexhausted'], 547 ], 548 'Custom and grade standard conditions only, no view condition' => [ 549 false, 550 true, 551 [ 552 'completionsubmit' => true, 553 ], 554 'assign', 555 ['completionsubmit', 'completionusegrade'], 556 ], 557 'Custom and view standard conditions only, no grade condition' => [ 558 true, 559 false, 560 [ 561 'completionsubmit' => true 562 ], 563 'assign', 564 ['completionview', 'completionsubmit'], 565 ], 566 'View and grade conditions only, activity with no custom conditions' => [ 567 true, 568 true, 569 [ 570 'completionview' => true, 571 'completionusegrade' => true 572 ], 573 'workshop', 574 ['completionview', 'completionusegrade'], 575 ], 576 'View condition only, activity with no custom conditions' => [ 577 true, 578 false, 579 [ 580 'completionview' => true, 581 ], 582 'workshop', 583 ['completionview'], 584 ], 585 ]; 586 } 587 588 /** 589 * Test custom sort order is functioning in \core_completion\cm_completion_details::get_details(). 590 * 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. 598 */ 599 public function test_get_details_custom_order(bool $completionview, bool $completiongrade, array $customcompletionrules, 600 string $modname, array $expectedorder) { 601 602 $options['customcompletion'] = []; 603 $customcompletiondata = []; 604 605 if ($completionview) { 606 $options['completionview'] = true; 607 } 608 609 if ($completiongrade) { 610 $options['completionusegrade'] = true; 611 } 612 613 // Set up the completion rules for the completion info. 614 foreach ($customcompletionrules as $customtype => $isenabled) { 615 $customcompletiondata[$customtype] = COMPLETION_COMPLETE; 616 } 617 618 $getdatareturn = (object)[ 619 'viewed' => $completionview ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE, 620 'completiongrade' => $completiongrade ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE, 621 'customcompletion' => $customcompletiondata, 622 ]; 623 624 $cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, $options, $getdatareturn, $modname); 625 626 $this->completioninfo->expects($this->any()) 627 ->method('get_data') 628 ->willReturn($getdatareturn); 629 630 $fetcheddetails = $cmcompletion->get_details(); 631 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)); 635 } 636 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body