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_lesson; 20 21 use advanced_testcase; 22 use cm_info; 23 use coding_exception; 24 use mod_lesson\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 32 /** 33 * Class for unit testing mod_lesson/custom_completion. 34 * 35 * @package mod_lesson 36 * @copyright 2021 Michael Hawkins <michaelh@moodle.com> 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 class custom_completion_test extends advanced_testcase { 40 41 /** 42 * Data provider for get_state(). 43 * 44 * @return array[] 45 */ 46 public function get_state_provider(): array { 47 return [ 48 'Undefined completion requirement' => [ 49 'somenonexistentrule', COMPLETION_ENABLED, 3, null, coding_exception::class 50 ], 51 'Minimum time spent requirement not available' => [ 52 'completionstatusrequired', COMPLETION_DISABLED, 3, null, moodle_exception::class 53 ], 54 'Minimum time spent required, user has not spent time in the lesson' => [ 55 'completiontimespent', 30, false, COMPLETION_INCOMPLETE, null 56 ], 57 'Minimum time spent required, user has not met completion requirement' => [ 58 'completiontimespent', 30, 10, COMPLETION_INCOMPLETE, null 59 ], 60 'Minimum time spent required, user has met completion requirement' => [ 61 'completiontimespent', 30, 30, COMPLETION_COMPLETE, null 62 ], 63 'Minimum time spent required, user has exceeded completion requirement' => [ 64 'completiontimespent', 30, 40, COMPLETION_COMPLETE, null 65 ], 66 'User must reach end of lesson, has not met completion requirement' => [ 67 'completionendreached', 1, false, COMPLETION_INCOMPLETE, null 68 ], 69 'User must reach end of lesson, has not met completion requirement' => [ 70 'completionendreached', 1, true, COMPLETION_COMPLETE, null 71 ], 72 ]; 73 } 74 75 /** 76 * Test for get_state(). 77 * 78 * @dataProvider get_state_provider 79 * @param string $rule The custom completion condition. 80 * @param int $rulevalue The custom completion rule value. 81 * @param mixed $uservalue The database value returned when checking the rule for the user. 82 * @param int|null $status Expected completion status for the rule. 83 * @param string|null $exception Expected exception. 84 */ 85 public function test_get_state(string $rule, int $rulevalue, $uservalue, ?int $status, ?string $exception) { 86 global $DB; 87 88 if (!is_null($exception)) { 89 $this->expectException($exception); 90 } 91 92 // Custom completion rule data for cm_info::customdata. 93 $customdataval = [ 94 'customcompletionrules' => [ 95 $rule => $rulevalue 96 ] 97 ]; 98 99 // Build a mock cm_info instance. 100 $mockcminfo = $this->getMockBuilder(cm_info::class) 101 ->disableOriginalConstructor() 102 ->onlyMethods(['__get']) 103 ->getMock(); 104 105 // Mock the return of the magic getter method when fetching the cm_info object's 106 // customdata and instance values. 107 $mockcminfo->expects($this->any()) 108 ->method('__get') 109 ->will($this->returnValueMap([ 110 ['customdata', $customdataval], 111 ['instance', 1], 112 ])); 113 114 if ($rule === 'completiontimespent') { 115 // Mock the DB call fetching user's lesson time spent. 116 $DB = $this->createMock(get_class($DB)); 117 $DB->expects($this->atMost(1)) 118 ->method('get_field_sql') 119 ->willReturn($uservalue); 120 } else if ($rule === 'completionendreached') { 121 // Mock the DB call fetching user's end reached state. 122 $DB = $this->createMock(get_class($DB)); 123 $DB->expects($this->atMost(1)) 124 ->method('record_exists') 125 ->willReturn($uservalue); 126 } 127 128 $customcompletion = new custom_completion($mockcminfo, 2); 129 130 $this->assertEquals($status, $customcompletion->get_state($rule)); 131 } 132 133 /** 134 * Test for get_defined_custom_rules(). 135 */ 136 public function test_get_defined_custom_rules() { 137 $expectedrules = [ 138 'completiontimespent', 139 'completionendreached', 140 ]; 141 142 $definedrules = custom_completion::get_defined_custom_rules(); 143 $this->assertCount(2, $definedrules); 144 145 foreach ($definedrules as $definedrule) { 146 $this->assertContains($definedrule, $expectedrules); 147 } 148 } 149 150 /** 151 * Test for get_defined_custom_rule_descriptions(). 152 */ 153 public function test_get_custom_rule_descriptions() { 154 // Get defined custom rules. 155 $rules = custom_completion::get_defined_custom_rules(); 156 157 // Build a mock cm_info instance. 158 $mockcminfo = $this->getMockBuilder(cm_info::class) 159 ->disableOriginalConstructor() 160 ->onlyMethods(['__get']) 161 ->getMock(); 162 163 // Instantiate a custom_completion object using the mocked cm_info. 164 $customcompletion = new custom_completion($mockcminfo, 1); 165 166 // Get custom rule descriptions. 167 $ruledescriptions = $customcompletion->get_custom_rule_descriptions(); 168 169 // Confirm that defined rules and rule descriptions are consistent with each other. 170 $this->assertEquals(count($rules), count($ruledescriptions)); 171 foreach ($rules as $rule) { 172 $this->assertArrayHasKey($rule, $ruledescriptions); 173 } 174 } 175 176 /** 177 * Test for is_defined(). 178 */ 179 public function test_is_defined() { 180 // Build a mock cm_info instance. 181 $mockcminfo = $this->getMockBuilder(cm_info::class) 182 ->disableOriginalConstructor() 183 ->getMock(); 184 185 $customcompletion = new custom_completion($mockcminfo, 1); 186 187 // All rules are defined. 188 $this->assertTrue($customcompletion->is_defined('completiontimespent')); 189 $this->assertTrue($customcompletion->is_defined('completionendreached')); 190 191 // Undefined rule is not found. 192 $this->assertFalse($customcompletion->is_defined('somerandomrule')); 193 } 194 195 /** 196 * Data provider for test_get_available_custom_rules(). 197 * 198 * @return array[] 199 */ 200 public function get_available_custom_rules_provider(): array { 201 return [ 202 'No completion conditions enabled' => [ 203 [ 204 'completiontimespent' => COMPLETION_DISABLED, 205 'completionendreached' => COMPLETION_DISABLED, 206 ], 207 [], 208 ], 209 'Completion end reached enabled only' => [ 210 [ 211 'completiontimespent' => COMPLETION_DISABLED, 212 'completionendreached' => COMPLETION_ENABLED, 213 ], 214 ['completionendreached'], 215 ], 216 'Completion time spent enabled only' => [ 217 [ 218 'completiontimespent' => 60, 219 'completionendreached' => COMPLETION_DISABLED, 220 ], 221 ['completiontimespent'], 222 ], 223 'Completion end reached and time spent both enabled' => [ 224 [ 225 'completiontimespent' => 90, 226 'completionendreached' => COMPLETION_ENABLED, 227 ], 228 ['completiontimespent', 'completionendreached'], 229 ], 230 ]; 231 } 232 233 /** 234 * Test for get_available_custom_rules(). 235 * 236 * @dataProvider get_available_custom_rules_provider 237 * @param array $completionrulesvalues 238 * @param array $expected 239 */ 240 public function test_get_available_custom_rules(array $completionrulesvalues, array $expected) { 241 $customcompletionrules = [ 242 'customcompletionrules' => $completionrulesvalues, 243 ]; 244 245 // Build a mock cm_info instance. 246 $mockcminfo = $this->getMockBuilder(cm_info::class) 247 ->disableOriginalConstructor() 248 ->onlyMethods(['__get']) 249 ->getMock(); 250 251 // Mock the return of magic getter for the customdata attribute. 252 $mockcminfo->expects($this->any()) 253 ->method('__get') 254 ->with('customdata') 255 ->willReturn($customcompletionrules); 256 257 $customcompletion = new custom_completion($mockcminfo, 1); 258 $this->assertEquals($expected, $customcompletion->get_available_custom_rules()); 259 } 260 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body