See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 * SCORM module external functions tests 19 * 20 * @package mod_scorm 21 * @category external 22 * @copyright 2015 Juan Leyva <juan@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 3.0 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 31 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 32 require_once($CFG->dirroot . '/mod/scorm/lib.php'); 33 34 /** 35 * SCORM module external functions tests 36 * 37 * @package mod_scorm 38 * @category external 39 * @copyright 2015 Juan Leyva <juan@moodle.com> 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 * @since Moodle 3.0 42 */ 43 class mod_scorm_external_testcase extends externallib_advanced_testcase { 44 45 /** 46 * Set up for every test 47 */ 48 public function setUp() { 49 global $DB, $CFG; 50 $this->resetAfterTest(); 51 $this->setAdminUser(); 52 53 $CFG->enablecompletion = 1; 54 // Setup test data. 55 $this->course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 56 $this->scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $this->course->id), 57 array('completion' => 2, 'completionview' => 1)); 58 $this->context = context_module::instance($this->scorm->cmid); 59 $this->cm = get_coursemodule_from_instance('scorm', $this->scorm->id); 60 61 // Create users. 62 $this->student = self::getDataGenerator()->create_user(); 63 $this->teacher = self::getDataGenerator()->create_user(); 64 65 // Users enrolments. 66 $this->studentrole = $DB->get_record('role', array('shortname' => 'student')); 67 $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 68 $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual'); 69 $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual'); 70 } 71 72 /** 73 * Test view_scorm 74 */ 75 public function test_view_scorm() { 76 global $DB; 77 78 // Test invalid instance id. 79 try { 80 mod_scorm_external::view_scorm(0); 81 $this->fail('Exception expected due to invalid mod_scorm instance id.'); 82 } catch (moodle_exception $e) { 83 $this->assertEquals('invalidrecord', $e->errorcode); 84 } 85 86 // Test not-enrolled user. 87 $user = self::getDataGenerator()->create_user(); 88 $this->setUser($user); 89 try { 90 mod_scorm_external::view_scorm($this->scorm->id); 91 $this->fail('Exception expected due to not enrolled user.'); 92 } catch (moodle_exception $e) { 93 $this->assertEquals('requireloginerror', $e->errorcode); 94 } 95 96 // Test user with full capabilities. 97 $this->studentrole = $DB->get_record('role', array('shortname' => 'student')); 98 $this->getDataGenerator()->enrol_user($user->id, $this->course->id, $this->studentrole->id); 99 100 // Trigger and capture the event. 101 $sink = $this->redirectEvents(); 102 103 $result = mod_scorm_external::view_scorm($this->scorm->id); 104 $result = external_api::clean_returnvalue(mod_scorm_external::view_scorm_returns(), $result); 105 106 $events = $sink->get_events(); 107 $this->assertCount(1, $events); 108 $event = array_shift($events); 109 110 // Checking that the event contains the expected values. 111 $this->assertInstanceOf('\mod_scorm\event\course_module_viewed', $event); 112 $this->assertEquals($this->context, $event->get_context()); 113 $moodleurl = new \moodle_url('/mod/scorm/view.php', array('id' => $this->cm->id)); 114 $this->assertEquals($moodleurl, $event->get_url()); 115 $this->assertEventContextNotUsed($event); 116 $this->assertNotEmpty($event->get_name()); 117 } 118 119 /** 120 * Test get scorm attempt count 121 */ 122 public function test_mod_scorm_get_scorm_attempt_count_own_empty() { 123 // Set to the student user. 124 self::setUser($this->student); 125 126 // Retrieve my attempts (should be 0). 127 $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id); 128 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result); 129 $this->assertEquals(0, $result['attemptscount']); 130 } 131 132 public function test_mod_scorm_get_scorm_attempt_count_own_with_complete() { 133 // Set to the student user. 134 self::setUser($this->student); 135 136 // Create attempts. 137 $scoes = scorm_get_scoes($this->scorm->id); 138 $sco = array_shift($scoes); 139 scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); 140 scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed'); 141 142 $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id); 143 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result); 144 $this->assertEquals(2, $result['attemptscount']); 145 } 146 147 public function test_mod_scorm_get_scorm_attempt_count_own_incomplete() { 148 // Set to the student user. 149 self::setUser($this->student); 150 151 // Create a complete attempt, and an incomplete attempt. 152 $scoes = scorm_get_scoes($this->scorm->id); 153 $sco = array_shift($scoes); 154 scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); 155 scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 2, 'cmi.core.credit', '0'); 156 157 $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id, true); 158 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result); 159 $this->assertEquals(1, $result['attemptscount']); 160 } 161 162 public function test_mod_scorm_get_scorm_attempt_count_others_as_teacher() { 163 // As a teacher. 164 self::setUser($this->teacher); 165 166 // Create a completed attempt for student. 167 $scoes = scorm_get_scoes($this->scorm->id); 168 $sco = array_shift($scoes); 169 scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); 170 171 // I should be able to view the attempts for my students. 172 $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id); 173 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result); 174 $this->assertEquals(1, $result['attemptscount']); 175 } 176 177 /** 178 * @expectedException required_capability_exception 179 */ 180 public function test_mod_scorm_get_scorm_attempt_count_others_as_student() { 181 // Create a second student. 182 $student2 = self::getDataGenerator()->create_user(); 183 $this->getDataGenerator()->enrol_user($student2->id, $this->course->id, $this->studentrole->id, 'manual'); 184 185 // As a student. 186 self::setUser($student2); 187 188 // I should not be able to view the attempts of another student. 189 mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id); 190 } 191 192 /** 193 * @expectedException moodle_exception 194 */ 195 public function test_mod_scorm_get_scorm_attempt_count_invalid_instanceid() { 196 // As student. 197 self::setUser($this->student); 198 199 // Test invalid instance id. 200 mod_scorm_external::get_scorm_attempt_count(0, $this->student->id); 201 } 202 203 /** 204 * @expectedException moodle_exception 205 */ 206 public function test_mod_scorm_get_scorm_attempt_count_invalid_userid() { 207 // As student. 208 self::setUser($this->student); 209 210 mod_scorm_external::get_scorm_attempt_count($this->scorm->id, -1); 211 } 212 213 /** 214 * Test get scorm scoes 215 */ 216 public function test_mod_scorm_get_scorm_scoes() { 217 global $DB; 218 219 $this->resetAfterTest(true); 220 221 // Create users. 222 $student = self::getDataGenerator()->create_user(); 223 $teacher = self::getDataGenerator()->create_user(); 224 225 // Create courses to add the modules. 226 $course = self::getDataGenerator()->create_course(); 227 228 // First scorm, dates restriction. 229 $record = new stdClass(); 230 $record->course = $course->id; 231 $record->timeopen = time() + DAYSECS; 232 $record->timeclose = $record->timeopen + DAYSECS; 233 $scorm = self::getDataGenerator()->create_module('scorm', $record); 234 235 // Set to the student user. 236 self::setUser($student); 237 238 // Users enrolments. 239 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 240 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 241 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); 242 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual'); 243 244 // Retrieve my scoes, warning!. 245 try { 246 mod_scorm_external::get_scorm_scoes($scorm->id); 247 $this->fail('Exception expected due to invalid dates.'); 248 } catch (moodle_exception $e) { 249 $this->assertEquals('notopenyet', $e->errorcode); 250 } 251 252 $scorm->timeopen = time() - DAYSECS; 253 $scorm->timeclose = time() - HOURSECS; 254 $DB->update_record('scorm', $scorm); 255 256 try { 257 mod_scorm_external::get_scorm_scoes($scorm->id); 258 $this->fail('Exception expected due to invalid dates.'); 259 } catch (moodle_exception $e) { 260 $this->assertEquals('expired', $e->errorcode); 261 } 262 263 // Retrieve my scoes, user with permission. 264 self::setUser($teacher); 265 $result = mod_scorm_external::get_scorm_scoes($scorm->id); 266 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_scoes_returns(), $result); 267 $this->assertCount(2, $result['scoes']); 268 $this->assertCount(0, $result['warnings']); 269 270 $scoes = scorm_get_scoes($scorm->id); 271 $sco = array_shift($scoes); 272 $sco->extradata = array(); 273 $this->assertEquals((array) $sco, $result['scoes'][0]); 274 275 $sco = array_shift($scoes); 276 $sco->extradata = array(); 277 $sco->extradata[] = array( 278 'element' => 'isvisible', 279 'value' => $sco->isvisible 280 ); 281 $sco->extradata[] = array( 282 'element' => 'parameters', 283 'value' => $sco->parameters 284 ); 285 unset($sco->isvisible); 286 unset($sco->parameters); 287 288 // Sort the array (if we don't sort tests will fails for Postgres). 289 usort($result['scoes'][1]['extradata'], function($a, $b) { 290 return strcmp($a['element'], $b['element']); 291 }); 292 293 $this->assertEquals((array) $sco, $result['scoes'][1]); 294 295 // Use organization. 296 $organization = 'golf_sample_default_org'; 297 $result = mod_scorm_external::get_scorm_scoes($scorm->id, $organization); 298 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_scoes_returns(), $result); 299 $this->assertCount(1, $result['scoes']); 300 $this->assertEquals($organization, $result['scoes'][0]['organization']); 301 $this->assertCount(0, $result['warnings']); 302 303 // Test invalid instance id. 304 try { 305 mod_scorm_external::get_scorm_scoes(0); 306 $this->fail('Exception expected due to invalid instance id.'); 307 } catch (moodle_exception $e) { 308 $this->assertEquals('invalidrecord', $e->errorcode); 309 } 310 311 } 312 313 /** 314 * Test get scorm scoes (with a complex SCORM package) 315 */ 316 public function test_mod_scorm_get_scorm_scoes_complex_package() { 317 global $CFG; 318 319 // As student. 320 self::setUser($this->student); 321 322 $record = new stdClass(); 323 $record->course = $this->course->id; 324 $record->packagefilepath = $CFG->dirroot.'/mod/scorm/tests/packages/complexscorm.zip'; 325 $scorm = self::getDataGenerator()->create_module('scorm', $record); 326 327 $result = mod_scorm_external::get_scorm_scoes($scorm->id); 328 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_scoes_returns(), $result); 329 $this->assertCount(9, $result['scoes']); 330 $this->assertCount(0, $result['warnings']); 331 332 $expectedscoes = array(); 333 $scoreturnstructure = mod_scorm_external::get_scorm_scoes_returns(); 334 $scoes = scorm_get_scoes($scorm->id); 335 foreach ($scoes as $sco) { 336 $sco->extradata = array(); 337 foreach ($sco as $element => $value) { 338 // Add the extra data to the extradata array and remove the object element. 339 if (!isset($scoreturnstructure->keys['scoes']->content->keys[$element])) { 340 $sco->extradata[] = array( 341 'element' => $element, 342 'value' => $value 343 ); 344 unset($sco->{$element}); 345 } 346 } 347 $expectedscoes[] = (array) $sco; 348 } 349 350 $this->assertEquals($expectedscoes, $result['scoes']); 351 } 352 353 /* 354 * Test get scorm user data 355 */ 356 public function test_mod_scorm_get_scorm_user_data() { 357 global $DB; 358 359 $this->resetAfterTest(true); 360 361 // Create users. 362 $student1 = self::getDataGenerator()->create_user(); 363 $teacher = self::getDataGenerator()->create_user(); 364 365 // Set to the student user. 366 self::setUser($student1); 367 368 // Create courses to add the modules. 369 $course = self::getDataGenerator()->create_course(); 370 371 // First scorm. 372 $record = new stdClass(); 373 $record->course = $course->id; 374 $scorm = self::getDataGenerator()->create_module('scorm', $record); 375 376 // Users enrolments. 377 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 378 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 379 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id, 'manual'); 380 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual'); 381 382 // Create attempts. 383 $scoes = scorm_get_scoes($scorm->id); 384 $sco = array_shift($scoes); 385 scorm_insert_track($student1->id, $scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); 386 scorm_insert_track($student1->id, $scorm->id, $sco->id, 1, 'cmi.core.score.raw', '80'); 387 scorm_insert_track($student1->id, $scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed'); 388 389 $result = mod_scorm_external::get_scorm_user_data($scorm->id, 1); 390 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_user_data_returns(), $result); 391 $this->assertCount(2, $result['data']); 392 // Find our tracking data. 393 $found = 0; 394 foreach ($result['data'] as $scodata) { 395 foreach ($scodata['userdata'] as $userdata) { 396 if ($userdata['element'] == 'cmi.core.lesson_status' and $userdata['value'] == 'completed') { 397 $found++; 398 } 399 if ($userdata['element'] == 'cmi.core.score.raw' and $userdata['value'] == '80') { 400 $found++; 401 } 402 } 403 } 404 $this->assertEquals(2, $found); 405 406 // Test invalid instance id. 407 try { 408 mod_scorm_external::get_scorm_user_data(0, 1); 409 $this->fail('Exception expected due to invalid instance id.'); 410 } catch (moodle_exception $e) { 411 $this->assertEquals('invalidrecord', $e->errorcode); 412 } 413 } 414 415 /** 416 * Test insert scorm tracks 417 */ 418 public function test_mod_scorm_insert_scorm_tracks() { 419 global $DB; 420 421 $this->resetAfterTest(true); 422 423 // Create users. 424 $student = self::getDataGenerator()->create_user(); 425 426 // Create courses to add the modules. 427 $course = self::getDataGenerator()->create_course(); 428 429 // First scorm, dates restriction. 430 $record = new stdClass(); 431 $record->course = $course->id; 432 $record->timeopen = time() + DAYSECS; 433 $record->timeclose = $record->timeopen + DAYSECS; 434 $scorm = self::getDataGenerator()->create_module('scorm', $record); 435 436 // Get a SCO. 437 $scoes = scorm_get_scoes($scorm->id); 438 $sco = array_shift($scoes); 439 440 // Tracks. 441 $tracks = array(); 442 $tracks[] = array( 443 'element' => 'cmi.core.lesson_status', 444 'value' => 'completed' 445 ); 446 $tracks[] = array( 447 'element' => 'cmi.core.score.raw', 448 'value' => '80' 449 ); 450 451 // Set to the student user. 452 self::setUser($student); 453 454 // Users enrolments. 455 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 456 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); 457 458 // Exceptions first. 459 try { 460 mod_scorm_external::insert_scorm_tracks($sco->id, 1, $tracks); 461 $this->fail('Exception expected due to dates'); 462 } catch (moodle_exception $e) { 463 $this->assertEquals('notopenyet', $e->errorcode); 464 } 465 466 $scorm->timeopen = time() - DAYSECS; 467 $scorm->timeclose = time() - HOURSECS; 468 $DB->update_record('scorm', $scorm); 469 470 try { 471 mod_scorm_external::insert_scorm_tracks($sco->id, 1, $tracks); 472 $this->fail('Exception expected due to dates'); 473 } catch (moodle_exception $e) { 474 $this->assertEquals('expired', $e->errorcode); 475 } 476 477 // Test invalid instance id. 478 try { 479 mod_scorm_external::insert_scorm_tracks(0, 1, $tracks); 480 $this->fail('Exception expected due to invalid sco id.'); 481 } catch (moodle_exception $e) { 482 $this->assertEquals('cannotfindsco', $e->errorcode); 483 } 484 485 $scorm->timeopen = 0; 486 $scorm->timeclose = 0; 487 $DB->update_record('scorm', $scorm); 488 489 // Retrieve my tracks. 490 $result = mod_scorm_external::insert_scorm_tracks($sco->id, 1, $tracks); 491 $result = external_api::clean_returnvalue(mod_scorm_external::insert_scorm_tracks_returns(), $result); 492 $this->assertCount(0, $result['warnings']); 493 494 $trackids = $DB->get_records('scorm_scoes_track', array('userid' => $student->id, 'scoid' => $sco->id, 495 'scormid' => $scorm->id, 'attempt' => 1)); 496 // We use asort here to prevent problems with ids ordering. 497 $expectedkeys = array_keys($trackids); 498 $this->assertEquals(asort($expectedkeys), asort($result['trackids'])); 499 } 500 501 /** 502 * Test get scorm sco tracks 503 */ 504 public function test_mod_scorm_get_scorm_sco_tracks() { 505 global $DB; 506 507 $this->resetAfterTest(true); 508 509 // Create users. 510 $student = self::getDataGenerator()->create_user(); 511 $otherstudent = self::getDataGenerator()->create_user(); 512 $teacher = self::getDataGenerator()->create_user(); 513 514 // Set to the student user. 515 self::setUser($student); 516 517 // Create courses to add the modules. 518 $course = self::getDataGenerator()->create_course(); 519 520 // First scorm. 521 $record = new stdClass(); 522 $record->course = $course->id; 523 $scorm = self::getDataGenerator()->create_module('scorm', $record); 524 525 // Users enrolments. 526 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 527 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 528 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); 529 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual'); 530 531 // Create attempts. 532 $scoes = scorm_get_scoes($scorm->id); 533 $sco = array_shift($scoes); 534 scorm_insert_track($student->id, $scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); 535 scorm_insert_track($student->id, $scorm->id, $sco->id, 1, 'cmi.core.score.raw', '80'); 536 scorm_insert_track($student->id, $scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed'); 537 538 $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id, 1); 539 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result); 540 // 7 default elements + 2 custom ones. 541 $this->assertCount(9, $result['data']['tracks']); 542 $this->assertEquals(1, $result['data']['attempt']); 543 $this->assertCount(0, $result['warnings']); 544 // Find our tracking data. 545 $found = 0; 546 foreach ($result['data']['tracks'] as $userdata) { 547 if ($userdata['element'] == 'cmi.core.lesson_status' and $userdata['value'] == 'completed') { 548 $found++; 549 } 550 if ($userdata['element'] == 'cmi.core.score.raw' and $userdata['value'] == '80') { 551 $found++; 552 } 553 } 554 $this->assertEquals(2, $found); 555 556 // Try invalid attempt. 557 $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id, 10); 558 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result); 559 $this->assertCount(0, $result['data']['tracks']); 560 $this->assertEquals(10, $result['data']['attempt']); 561 $this->assertCount(1, $result['warnings']); 562 $this->assertEquals('notattempted', $result['warnings'][0]['warningcode']); 563 564 // Capabilities check. 565 try { 566 mod_scorm_external::get_scorm_sco_tracks($sco->id, $otherstudent->id); 567 $this->fail('Exception expected due to invalid instance id.'); 568 } catch (required_capability_exception $e) { 569 $this->assertEquals('nopermissions', $e->errorcode); 570 } 571 572 self::setUser($teacher); 573 // Ommit the attempt parameter, the function should calculate the last attempt. 574 $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id); 575 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result); 576 // 7 default elements + 1 custom one. 577 $this->assertCount(8, $result['data']['tracks']); 578 $this->assertEquals(2, $result['data']['attempt']); 579 580 // Test invalid instance id. 581 try { 582 mod_scorm_external::get_scorm_sco_tracks(0, 1); 583 $this->fail('Exception expected due to invalid instance id.'); 584 } catch (moodle_exception $e) { 585 $this->assertEquals('cannotfindsco', $e->errorcode); 586 } 587 // Invalid user. 588 try { 589 mod_scorm_external::get_scorm_sco_tracks($sco->id, 0); 590 $this->fail('Exception expected due to invalid instance id.'); 591 } catch (moodle_exception $e) { 592 $this->assertEquals('invaliduser', $e->errorcode); 593 } 594 } 595 596 /* 597 * Test get scorms by courses 598 */ 599 public function test_mod_scorm_get_scorms_by_courses() { 600 global $DB; 601 602 $this->resetAfterTest(true); 603 604 // Create users. 605 $student = self::getDataGenerator()->create_user(); 606 $teacher = self::getDataGenerator()->create_user(); 607 608 // Set to the student user. 609 self::setUser($student); 610 611 // Create courses to add the modules. 612 $course1 = self::getDataGenerator()->create_course(); 613 $course2 = self::getDataGenerator()->create_course(); 614 615 // First scorm. 616 $record = new stdClass(); 617 $record->introformat = FORMAT_HTML; 618 $record->course = $course1->id; 619 $record->hidetoc = 2; 620 $record->displayattemptstatus = 2; 621 $record->skipview = 2; 622 $scorm1 = self::getDataGenerator()->create_module('scorm', $record); 623 624 // Second scorm. 625 $record = new stdClass(); 626 $record->introformat = FORMAT_HTML; 627 $record->course = $course2->id; 628 $scorm2 = self::getDataGenerator()->create_module('scorm', $record); 629 630 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 631 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 632 633 // Users enrolments. 634 $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual'); 635 $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id, 'manual'); 636 637 // Execute real Moodle enrolment as we'll call unenrol() method on the instance later. 638 $enrol = enrol_get_plugin('manual'); 639 $enrolinstances = enrol_get_instances($course2->id, true); 640 foreach ($enrolinstances as $courseenrolinstance) { 641 if ($courseenrolinstance->enrol == "manual") { 642 $instance2 = $courseenrolinstance; 643 break; 644 } 645 } 646 $enrol->enrol_user($instance2, $student->id, $studentrole->id); 647 648 $returndescription = mod_scorm_external::get_scorms_by_courses_returns(); 649 650 // Test open/close dates. 651 652 $timenow = time(); 653 $scorm1->timeopen = $timenow - DAYSECS; 654 $scorm1->timeclose = $timenow - HOURSECS; 655 $DB->update_record('scorm', $scorm1); 656 657 $result = mod_scorm_external::get_scorms_by_courses(array($course1->id)); 658 $result = external_api::clean_returnvalue($returndescription, $result); 659 $this->assertCount(1, $result['warnings']); 660 // Only 'id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles'. 661 $this->assertCount(7, $result['scorms'][0]); 662 $this->assertEquals('expired', $result['warnings'][0]['warningcode']); 663 664 $scorm1->timeopen = $timenow + DAYSECS; 665 $scorm1->timeclose = $scorm1->timeopen + DAYSECS; 666 $DB->update_record('scorm', $scorm1); 667 668 $result = mod_scorm_external::get_scorms_by_courses(array($course1->id)); 669 $result = external_api::clean_returnvalue($returndescription, $result); 670 $this->assertCount(1, $result['warnings']); 671 // Only 'id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles'. 672 $this->assertCount(7, $result['scorms'][0]); 673 $this->assertEquals('notopenyet', $result['warnings'][0]['warningcode']); 674 675 // Reset times. 676 $scorm1->timeopen = 0; 677 $scorm1->timeclose = 0; 678 $DB->update_record('scorm', $scorm1); 679 680 // Create what we expect to be returned when querying the two courses. 681 // First for the student user. 682 $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'version', 'maxgrade', 683 'grademethod', 'whatgrade', 'maxattempt', 'forcecompleted', 'forcenewattempt', 'lastattemptlock', 684 'displayattemptstatus', 'displaycoursestructure', 'sha1hash', 'md5hash', 'revision', 'launch', 685 'skipview', 'hidebrowse', 'hidetoc', 'nav', 'navpositionleft', 'navpositiontop', 'auto', 686 'popup', 'width', 'height', 'timeopen', 'timeclose', 'displayactivityname', 'packagesize', 687 'packageurl', 'scormtype', 'reference'); 688 689 // Add expected coursemodule and data. 690 $scorm1->coursemodule = $scorm1->cmid; 691 $scorm1->section = 0; 692 $scorm1->visible = true; 693 $scorm1->groupmode = 0; 694 $scorm1->groupingid = 0; 695 696 $scorm2->coursemodule = $scorm2->cmid; 697 $scorm2->section = 0; 698 $scorm2->visible = true; 699 $scorm2->groupmode = 0; 700 $scorm2->groupingid = 0; 701 702 // SCORM size. The same package is used in both SCORMs. 703 $scormcontext1 = context_module::instance($scorm1->cmid); 704 $scormcontext2 = context_module::instance($scorm2->cmid); 705 $fs = get_file_storage(); 706 $packagefile = $fs->get_file($scormcontext1->id, 'mod_scorm', 'package', 0, '/', $scorm1->reference); 707 $packagesize = $packagefile->get_filesize(); 708 709 $packageurl1 = moodle_url::make_webservice_pluginfile_url( 710 $scormcontext1->id, 'mod_scorm', 'package', 0, '/', $scorm1->reference)->out(false); 711 $packageurl2 = moodle_url::make_webservice_pluginfile_url( 712 $scormcontext2->id, 'mod_scorm', 'package', 0, '/', $scorm2->reference)->out(false); 713 714 $scorm1->packagesize = $packagesize; 715 $scorm1->packageurl = $packageurl1; 716 $scorm2->packagesize = $packagesize; 717 $scorm2->packageurl = $packageurl2; 718 719 // Forced to boolean as it is returned as PARAM_BOOL. 720 $protectpackages = (bool)get_config('scorm', 'protectpackagedownloads'); 721 $expected1 = array('protectpackagedownloads' => $protectpackages); 722 $expected2 = array('protectpackagedownloads' => $protectpackages); 723 foreach ($expectedfields as $field) { 724 725 // Since we return the fields used as boolean as PARAM_BOOL instead PARAM_INT we need to force casting here. 726 // From the returned fields definition we obtain the type expected for the field. 727 if (empty($returndescription->keys['scorms']->content->keys[$field]->type)) { 728 continue; 729 } 730 $fieldtype = $returndescription->keys['scorms']->content->keys[$field]->type; 731 if ($fieldtype == PARAM_BOOL) { 732 $expected1[$field] = (bool) $scorm1->{$field}; 733 $expected2[$field] = (bool) $scorm2->{$field}; 734 } else { 735 $expected1[$field] = $scorm1->{$field}; 736 $expected2[$field] = $scorm2->{$field}; 737 } 738 } 739 $expected1['introfiles'] = []; 740 $expected2['introfiles'] = []; 741 742 $expectedscorms = array(); 743 $expectedscorms[] = $expected2; 744 $expectedscorms[] = $expected1; 745 746 // Call the external function passing course ids. 747 $result = mod_scorm_external::get_scorms_by_courses(array($course2->id, $course1->id)); 748 $result = external_api::clean_returnvalue($returndescription, $result); 749 $this->assertEquals($expectedscorms, $result['scorms']); 750 751 // Call the external function without passing course id. 752 $result = mod_scorm_external::get_scorms_by_courses(); 753 $result = external_api::clean_returnvalue($returndescription, $result); 754 $this->assertEquals($expectedscorms, $result['scorms']); 755 756 // Unenrol user from second course and alter expected scorms. 757 $enrol->unenrol_user($instance2, $student->id); 758 array_shift($expectedscorms); 759 760 // Call the external function without passing course id. 761 $result = mod_scorm_external::get_scorms_by_courses(); 762 $result = external_api::clean_returnvalue($returndescription, $result); 763 $this->assertEquals($expectedscorms, $result['scorms']); 764 765 // Call for the second course we unenrolled the user from, expected warning. 766 $result = mod_scorm_external::get_scorms_by_courses(array($course2->id)); 767 $this->assertCount(1, $result['warnings']); 768 $this->assertEquals('1', $result['warnings'][0]['warningcode']); 769 $this->assertEquals($course2->id, $result['warnings'][0]['itemid']); 770 771 // Now, try as a teacher for getting all the additional fields. 772 self::setUser($teacher); 773 774 $additionalfields = array('updatefreq', 'timemodified', 'options', 775 'completionstatusrequired', 'completionscorerequired', 'completionstatusallscos', 776 'autocommit', 'section', 'visible', 'groupmode', 'groupingid'); 777 778 foreach ($additionalfields as $field) { 779 $fieldtype = $returndescription->keys['scorms']->content->keys[$field]->type; 780 781 if ($fieldtype == PARAM_BOOL) { 782 $expectedscorms[0][$field] = (bool) $scorm1->{$field}; 783 } else { 784 $expectedscorms[0][$field] = $scorm1->{$field}; 785 } 786 } 787 788 $result = mod_scorm_external::get_scorms_by_courses(); 789 $result = external_api::clean_returnvalue($returndescription, $result); 790 $this->assertEquals($expectedscorms, $result['scorms']); 791 792 // Even with the SCORM closed in time teacher should retrieve the info. 793 $scorm1->timeopen = $timenow - DAYSECS; 794 $scorm1->timeclose = $timenow - HOURSECS; 795 $DB->update_record('scorm', $scorm1); 796 797 $expectedscorms[0]['timeopen'] = $scorm1->timeopen; 798 $expectedscorms[0]['timeclose'] = $scorm1->timeclose; 799 800 $result = mod_scorm_external::get_scorms_by_courses(); 801 $result = external_api::clean_returnvalue($returndescription, $result); 802 $this->assertEquals($expectedscorms, $result['scorms']); 803 804 // Admin also should get all the information. 805 self::setAdminUser(); 806 807 $result = mod_scorm_external::get_scorms_by_courses(array($course1->id)); 808 $result = external_api::clean_returnvalue($returndescription, $result); 809 $this->assertEquals($expectedscorms, $result['scorms']); 810 } 811 812 /** 813 * Test launch_sco 814 */ 815 public function test_launch_sco() { 816 global $DB; 817 818 // Test invalid instance id. 819 try { 820 mod_scorm_external::launch_sco(0); 821 $this->fail('Exception expected due to invalid mod_scorm instance id.'); 822 } catch (moodle_exception $e) { 823 $this->assertEquals('invalidrecord', $e->errorcode); 824 } 825 826 // Test not-enrolled user. 827 $user = self::getDataGenerator()->create_user(); 828 $this->setUser($user); 829 try { 830 mod_scorm_external::launch_sco($this->scorm->id); 831 $this->fail('Exception expected due to not enrolled user.'); 832 } catch (moodle_exception $e) { 833 $this->assertEquals('requireloginerror', $e->errorcode); 834 } 835 836 // Test user with full capabilities. 837 $this->setUser($this->student); 838 839 // Trigger and capture the event. 840 $sink = $this->redirectEvents(); 841 842 $scoes = scorm_get_scoes($this->scorm->id); 843 foreach ($scoes as $sco) { 844 // Find launchable SCO. 845 if ($sco->launch != '') { 846 break; 847 } 848 } 849 850 $result = mod_scorm_external::launch_sco($this->scorm->id, $sco->id); 851 $result = external_api::clean_returnvalue(mod_scorm_external::launch_sco_returns(), $result); 852 853 $events = $sink->get_events(); 854 $this->assertCount(3, $events); 855 $event = array_pop($events); 856 857 // Checking that the event contains the expected values. 858 $this->assertInstanceOf('\mod_scorm\event\sco_launched', $event); 859 $this->assertEquals($this->context, $event->get_context()); 860 $moodleurl = new \moodle_url('/mod/scorm/player.php', array('cm' => $this->cm->id, 'scoid' => $sco->id)); 861 $this->assertEquals($moodleurl, $event->get_url()); 862 $this->assertEventContextNotUsed($event); 863 $this->assertNotEmpty($event->get_name()); 864 865 $event = array_shift($events); 866 $this->assertInstanceOf('\core\event\course_module_completion_updated', $event); 867 868 // Check completion status. 869 $completion = new completion_info($this->course); 870 $completiondata = $completion->get_data($this->cm); 871 $this->assertEquals(COMPLETION_VIEWED, $completiondata->completionstate); 872 873 // Invalid SCO. 874 try { 875 mod_scorm_external::launch_sco($this->scorm->id, -1); 876 $this->fail('Exception expected due to invalid SCO id.'); 877 } catch (moodle_exception $e) { 878 $this->assertEquals('cannotfindsco', $e->errorcode); 879 } 880 } 881 882 /** 883 * Test mod_scorm_get_scorm_access_information. 884 */ 885 public function test_mod_scorm_get_scorm_access_information() { 886 global $DB; 887 888 $this->resetAfterTest(true); 889 890 $student = self::getDataGenerator()->create_user(); 891 $course = self::getDataGenerator()->create_course(); 892 // Create the scorm. 893 $record = new stdClass(); 894 $record->course = $course->id; 895 $scorm = self::getDataGenerator()->create_module('scorm', $record); 896 $context = context_module::instance($scorm->cmid); 897 898 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 899 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); 900 901 self::setUser($student); 902 $result = mod_scorm_external::get_scorm_access_information($scorm->id); 903 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_access_information_returns(), $result); 904 905 // Check default values for capabilities. 906 $enabledcaps = array('canskipview', 'cansavetrack', 'canviewscores'); 907 908 unset($result['warnings']); 909 foreach ($result as $capname => $capvalue) { 910 if (in_array($capname, $enabledcaps)) { 911 $this->assertTrue($capvalue); 912 } else { 913 $this->assertFalse($capvalue); 914 } 915 } 916 // Now, unassign one capability. 917 unassign_capability('mod/scorm:viewscores', $studentrole->id); 918 array_pop($enabledcaps); 919 accesslib_clear_all_caches_for_unit_testing(); 920 921 $result = mod_scorm_external::get_scorm_access_information($scorm->id); 922 $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_access_information_returns(), $result); 923 unset($result['warnings']); 924 foreach ($result as $capname => $capvalue) { 925 if (in_array($capname, $enabledcaps)) { 926 $this->assertTrue($capvalue); 927 } else { 928 $this->assertFalse($capvalue); 929 } 930 } 931 } 932 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body