See Release Notes
Long Term Support Release
Differences Between: [Versions 311 and 401] [Versions 401 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 */ 45 class cm_completion_details_test extends advanced_testcase { 46 47 /** @var completion_info A completion object. */ 48 protected $completioninfo = null; 49 50 /** 51 * Fetches a mocked cm_completion_details instance. 52 * 53 * @param int|null $completion The completion tracking mode for the module. 54 * @param array $completionoptions Completion options (e.g. completionview, completionusegrade, etc.) 55 * @param object $mockcompletiondata Mock data to be returned by get_data. 56 * @param string $modname The modname to set in the cm if a specific one is required. 57 * @return cm_completion_details 58 */ 59 protected function setup_data(?int $completion, array $completionoptions = [], 60 object $mockcompletiondata = null, $modname = 'somenonexistentmod'): cm_completion_details { 61 if (is_null($completion)) { 62 $completion = COMPLETION_TRACKING_AUTOMATIC; 63 } 64 65 // Mock a completion_info instance so we can simply mock the returns of completion_info::get_data() later. 66 $this->completioninfo = $this->getMockBuilder(completion_info::class) 67 ->disableOriginalConstructor() 68 ->getMock(); 69 70 // Mock return of completion_info's is_enabled() method to match the expected completion tracking for the module. 71 $this->completioninfo->expects($this->any()) 72 ->method('is_enabled') 73 ->willReturn($completion); 74 75 if (!empty($mockcompletiondata)) { 76 $this->completioninfo->expects($this->any()) 77 ->method('get_data') 78 ->willReturn($mockcompletiondata); 79 } 80 81 // Build a mock cm_info instance. 82 $mockcminfo = $this->getMockBuilder(cm_info::class) 83 ->disableOriginalConstructor() 84 ->onlyMethods(['__get']) 85 ->getMock(); 86 87 // Mock the return of the magic getter method when fetching the cm_info object's customdata and instance values. 88 $mockcminfo->expects($this->any()) 89 ->method('__get') 90 ->will($this->returnValueMap([ 91 ['completion', $completion], 92 ['instance', 1], 93 ['modname', $modname], 94 ['completionview', $completionoptions['completionview'] ?? COMPLETION_VIEW_NOT_REQUIRED], 95 ['completiongradeitemnumber', $completionoptions['completionusegrade'] ?? null], 96 ['completionpassgrade', $completionoptions['completionpassgrade'] ?? null], 97 ])); 98 99 return new cm_completion_details($this->completioninfo, $mockcminfo, 2); 100 } 101 102 /** 103 * Provides data for test_has_completion(). 104 * 105 * @return array[] 106 */ 107 public function has_completion_provider(): array { 108 return [ 109 'Automatic' => [ 110 COMPLETION_TRACKING_AUTOMATIC, true 111 ], 112 'Manual' => [ 113 COMPLETION_TRACKING_MANUAL, true 114 ], 115 'None' => [ 116 COMPLETION_TRACKING_NONE, false 117 ], 118 ]; 119 } 120 121 /** 122 * Test for has_completion(). 123 * 124 * @dataProvider has_completion_provider 125 * @param int $completion The completion tracking mode. 126 * @param bool $expectedresult Expected result. 127 */ 128 public function test_has_completion(int $completion, bool $expectedresult) { 129 $cmcompletion = $this->setup_data($completion); 130 131 $this->assertEquals($expectedresult, $cmcompletion->has_completion()); 132 } 133 134 /** 135 * Provides data for test_is_automatic(). 136 * 137 * @return array[] 138 */ 139 public function is_automatic_provider(): array { 140 return [ 141 'Automatic' => [ 142 COMPLETION_TRACKING_AUTOMATIC, true 143 ], 144 'Manual' => [ 145 COMPLETION_TRACKING_MANUAL, false 146 ], 147 'None' => [ 148 COMPLETION_TRACKING_NONE, false 149 ], 150 ]; 151 } 152 153 /** 154 * Test for is_available(). 155 * 156 * @dataProvider is_automatic_provider 157 * @param int $completion The completion tracking mode. 158 * @param bool $expectedresult Expected result. 159 */ 160 public function test_is_automatic(int $completion, bool $expectedresult) { 161 $cmcompletion = $this->setup_data($completion); 162 163 $this->assertEquals($expectedresult, $cmcompletion->is_automatic()); 164 } 165 166 /** 167 * Data provider for test_get_overall_completion(). 168 * @return array[] 169 */ 170 public function overall_completion_provider(): array { 171 return [ 172 'Complete' => [COMPLETION_COMPLETE], 173 'Incomplete' => [COMPLETION_INCOMPLETE], 174 ]; 175 } 176 177 /** 178 * Test for get_overall_completion(). 179 * 180 * @dataProvider overall_completion_provider 181 * @param int $state 182 */ 183 public function test_get_overall_completion(int $state) { 184 $completiondata = (object)['completionstate' => $state]; 185 $cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, [], $completiondata); 186 $this->assertEquals($state, $cmcompletion->get_overall_completion()); 187 } 188 189 /** 190 * Data provider for test_get_details(). 191 * @return array[] 192 */ 193 public function get_details_provider() { 194 return [ 195 'No completion tracking' => [ 196 COMPLETION_TRACKING_NONE, null, null, null, [] 197 ], 198 'Manual completion tracking' => [ 199 COMPLETION_TRACKING_MANUAL, null, null, null, [] 200 ], 201 'Automatic, require view, not viewed' => [ 202 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, null, null, [ 203 'completionview' => (object)[ 204 'status' => COMPLETION_INCOMPLETE, 205 'description' => get_string('detail_desc:view', 'completion'), 206 ] 207 ] 208 ], 209 'Automatic, require view, viewed' => [ 210 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, null, null, [ 211 'completionview' => (object)[ 212 'status' => COMPLETION_COMPLETE, 213 'description' => get_string('detail_desc:view', 'completion'), 214 ] 215 ] 216 ], 217 'Automatic, require grade, incomplete' => [ 218 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_INCOMPLETE, null, [ 219 'completionusegrade' => (object)[ 220 'status' => COMPLETION_INCOMPLETE, 221 'description' => get_string('detail_desc:receivegrade', 'completion'), 222 ] 223 ] 224 ], 225 'Automatic, require grade, complete' => [ 226 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, null, [ 227 'completionusegrade' => (object)[ 228 'status' => COMPLETION_COMPLETE, 229 'description' => get_string('detail_desc:receivegrade', 'completion'), 230 ] 231 ] 232 ], 233 'Automatic, require view (complete) and grade (incomplete)' => [ 234 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, null, [ 235 'completionview' => (object)[ 236 'status' => COMPLETION_COMPLETE, 237 'description' => get_string('detail_desc:view', 'completion'), 238 ], 239 'completionusegrade' => (object)[ 240 'status' => COMPLETION_INCOMPLETE, 241 'description' => get_string('detail_desc:receivegrade', 'completion'), 242 ] 243 ] 244 ], 245 'Automatic, require view (incomplete) and grade (complete)' => [ 246 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, null, [ 247 'completionview' => (object)[ 248 'status' => COMPLETION_INCOMPLETE, 249 'description' => get_string('detail_desc:view', 'completion'), 250 ], 251 'completionusegrade' => (object)[ 252 'status' => COMPLETION_COMPLETE, 253 'description' => get_string('detail_desc:receivegrade', 'completion'), 254 ] 255 ] 256 ], 257 'Automatic, require grade, require pass grade, complete' => [ 258 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [ 259 'completionusegrade' => (object)[ 260 'status' => COMPLETION_COMPLETE, 261 'description' => get_string('detail_desc:receivegrade', 'completion'), 262 ], 263 'completionpassgrade' => (object)[ 264 'status' => COMPLETION_COMPLETE, 265 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 266 ], 267 ] 268 ], 269 'Automatic, require grade, require pass grade, incomplete' => [ 270 COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, [ 271 'completionusegrade' => (object)[ 272 'status' => COMPLETION_COMPLETE, 273 'description' => get_string('detail_desc:receivegrade', 'completion'), 274 ], 275 'completionpassgrade' => (object)[ 276 'status' => COMPLETION_INCOMPLETE, 277 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 278 ], 279 ] 280 ], 281 'Automatic, require view (complete), require grade(complete), require pass grade(complete)' => [ 282 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [ 283 'completionview' => (object)[ 284 'status' => COMPLETION_COMPLETE, 285 'description' => get_string('detail_desc:view', 'completion'), 286 ], 287 'completionusegrade' => (object)[ 288 'status' => COMPLETION_COMPLETE, 289 'description' => get_string('detail_desc:receivegrade', 'completion'), 290 ], 291 'completionpassgrade' => (object)[ 292 'status' => COMPLETION_COMPLETE, 293 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 294 ], 295 ] 296 ], 297 'Automatic, require view (incomplete), require grade(complete), require pass grade(complete)' => [ 298 COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [ 299 'completionview' => (object)[ 300 'status' => COMPLETION_INCOMPLETE, 301 'description' => get_string('detail_desc:view', 'completion'), 302 ], 303 'completionusegrade' => (object)[ 304 'status' => COMPLETION_COMPLETE, 305 'description' => get_string('detail_desc:receivegrade', 'completion'), 306 ], 307 'completionpassgrade' => (object)[ 308 'status' => COMPLETION_COMPLETE, 309 'description' => get_string('detail_desc:receivepassgrade', 'completion'), 310 ], 311 ] 312 ], 313 ]; 314 } 315 316 /** 317 * Test for \core_completion\cm_completion_details::get_details(). 318 * 319 * @dataProvider get_details_provider 320 * @param int $completion The completion tracking mode. 321 * @param int|null $completionview Completion status of the "view" completion condition. 322 * @param int|null $completiongrade Completion status of the "must receive grade" completion condition. 323 * @param int|null $completionpassgrade Completion status of the "must receive passing grade" completion condition. 324 * @param array $expecteddetails Expected completion details returned by get_details(). 325 */ 326 public function test_get_details(int $completion, ?int $completionview, 327 ?int $completiongrade, ?int $completionpassgrade, array $expecteddetails) { 328 $options = []; 329 $getdatareturn = (object)[ 330 'viewed' => $completionview, 331 'completiongrade' => $completiongrade, 332 'passgrade' => $completionpassgrade, 333 ]; 334 335 if (!is_null($completionview)) { 336 $options['completionview'] = true; 337 } 338 if (!is_null($completiongrade)) { 339 $options['completionusegrade'] = true; 340 } 341 if (!is_null($completionpassgrade)) { 342 $options['completionpassgrade'] = true; 343 } 344 345 $cmcompletion = $this->setup_data($completion, $options, $getdatareturn); 346 $this->assertEquals($expecteddetails, $cmcompletion->get_details()); 347 } 348 349 /** 350 * Data provider for test_get_details_custom_order(). 351 * @return array[] 352 */ 353 public function get_details_custom_order_provider() { 354 return [ 355 'Custom and view/grade standard conditions, view first and grade last' => [ 356 true, 357 true, 358 [ 359 'completionsubmit' => true, 360 ], 361 'assign', 362 ['completionview', 'completionsubmit', 'completionusegrade'], 363 ], 364 'Custom and view/grade standard conditions, grade not last' => [ 365 true, 366 true, 367 [ 368 'completionminattempts' => 2, 369 'completionusegrade' => 50, 370 'completionpassorattemptsexhausted' => 1, 371 ], 372 'quiz', 373 ['completionview', 'completionminattempts', 'completionusegrade', 'completionpassorattemptsexhausted'], 374 ], 375 'Custom and grade standard conditions only, no view condition' => [ 376 false, 377 true, 378 [ 379 'completionsubmit' => true, 380 ], 381 'assign', 382 ['completionsubmit', 'completionusegrade'], 383 ], 384 'Custom and view standard conditions only, no grade condition' => [ 385 true, 386 false, 387 [ 388 'completionsubmit' => true 389 ], 390 'assign', 391 ['completionview', 'completionsubmit'], 392 ], 393 'View and grade conditions only, activity with no custom conditions' => [ 394 true, 395 true, 396 [ 397 'completionview' => true, 398 'completionusegrade' => true 399 ], 400 'workshop', 401 ['completionview', 'completionusegrade'], 402 ], 403 'View condition only, activity with no custom conditions' => [ 404 true, 405 false, 406 [ 407 'completionview' => true, 408 ], 409 'workshop', 410 ['completionview'], 411 ], 412 ]; 413 } 414 415 /** 416 * Test custom sort order is functioning in \core_completion\cm_completion_details::get_details(). 417 * 418 * @dataProvider get_details_custom_order_provider 419 * @param bool $completionview Completion status of the "view" completion condition. 420 * @param bool $completiongrade Completion status of the "must receive grade" completion condition. 421 * @param array $customcompletionrules Custom completion requirements, along with their values. 422 * @param string $modname The name of the module having data fetched. 423 * @param array $expectedorder The expected order of completion conditions returned about the module. 424 */ 425 public function test_get_details_custom_order(bool $completionview, bool $completiongrade, array $customcompletionrules, 426 string $modname, array $expectedorder) { 427 428 $options['customcompletion'] = []; 429 $customcompletiondata = []; 430 431 if ($completionview) { 432 $options['completionview'] = true; 433 } 434 435 if ($completiongrade) { 436 $options['completionusegrade'] = true; 437 } 438 439 // Set up the completion rules for the completion info. 440 foreach ($customcompletionrules as $customtype => $isenabled) { 441 $customcompletiondata[$customtype] = COMPLETION_COMPLETE; 442 } 443 444 $getdatareturn = (object)[ 445 'viewed' => $completionview ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE, 446 'completiongrade' => $completiongrade ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE, 447 'customcompletion' => $customcompletiondata, 448 ]; 449 450 $cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, $options, $getdatareturn, $modname); 451 452 $this->completioninfo->expects($this->any()) 453 ->method('get_data') 454 ->willReturn($getdatareturn); 455 456 $fetcheddetails = $cmcompletion->get_details(); 457 458 // Check the expected number of items are returned, and sorted in the correct order. 459 $this->assertCount(count($expectedorder), $fetcheddetails); 460 $this->assertTrue((array_keys($fetcheddetails) === $expectedorder)); 461 } 462 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body