MDL-50349 scorm: New WS mod_scorm_get_scorms_by_courses
[moodle.git] / mod / scorm / tests / externallib_test.php
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/>.
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  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
32 require_once($CFG->dirroot . '/mod/scorm/lib.php');
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 {
45     /**
46      * Set up for every test
47      */
48     public function setUp() {
49         global $DB;
50         $this->resetAfterTest();
51         $this->setAdminUser();
53         // Setup test data.
54         $this->course = $this->getDataGenerator()->create_course();
55         $this->scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $this->course->id));
56         $this->context = context_module::instance($this->scorm->cmid);
57         $this->cm = get_coursemodule_from_instance('scorm', $this->scorm->id);
59         // Create users.
60         $this->student = self::getDataGenerator()->create_user();
61         $this->teacher = self::getDataGenerator()->create_user();
63         // Users enrolments.
64         $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
65         $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
66         $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
67         $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
68     }
70     /**
71      * Test view_scorm
72      */
73     public function test_view_scorm() {
74         global $DB;
76         // Test invalid instance id.
77         try {
78             mod_scorm_external::view_scorm(0);
79             $this->fail('Exception expected due to invalid mod_scorm instance id.');
80         } catch (moodle_exception $e) {
81             $this->assertEquals('invalidrecord', $e->errorcode);
82         }
84         // Test not-enrolled user.
85         $user = self::getDataGenerator()->create_user();
86         $this->setUser($user);
87         try {
88             mod_scorm_external::view_scorm($this->scorm->id);
89             $this->fail('Exception expected due to not enrolled user.');
90         } catch (moodle_exception $e) {
91             $this->assertEquals('requireloginerror', $e->errorcode);
92         }
94         // Test user with full capabilities.
95         $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
96         $this->getDataGenerator()->enrol_user($user->id, $this->course->id, $this->studentrole->id);
98         // Trigger and capture the event.
99         $sink = $this->redirectEvents();
101         $result = mod_scorm_external::view_scorm($this->scorm->id);
102         $result = external_api::clean_returnvalue(mod_scorm_external::view_scorm_returns(), $result);
104         $events = $sink->get_events();
105         $this->assertCount(1, $events);
106         $event = array_shift($events);
108         // Checking that the event contains the expected values.
109         $this->assertInstanceOf('\mod_scorm\event\course_module_viewed', $event);
110         $this->assertEquals($this->context, $event->get_context());
111         $moodleurl = new \moodle_url('/mod/scorm/view.php', array('id' => $this->cm->id));
112         $this->assertEquals($moodleurl, $event->get_url());
113         $this->assertEventContextNotUsed($event);
114         $this->assertNotEmpty($event->get_name());
115     }
117     /**
118      * Test get scorm attempt count
119      */
120     public function test_mod_scorm_get_scorm_attempt_count_own_empty() {
121         // Set to the student user.
122         self::setUser($this->student);
124         // Retrieve my attempts (should be 0).
125         $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id);
126         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result);
127         $this->assertEquals(0, $result['attemptscount']);
128     }
130     public function test_mod_scorm_get_scorm_attempt_count_own_with_complete() {
131         // Set to the student user.
132         self::setUser($this->student);
134         // Create attempts.
135         $scoes = scorm_get_scoes($this->scorm->id);
136         $sco = array_shift($scoes);
137         scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed');
138         scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed');
140         $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id);
141         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result);
142         $this->assertEquals(2, $result['attemptscount']);
143     }
145     public function test_mod_scorm_get_scorm_attempt_count_own_incomplete() {
146         // Set to the student user.
147         self::setUser($this->student);
149         // Create a complete attempt, and an incomplete attempt.
150         $scoes = scorm_get_scoes($this->scorm->id);
151         $sco = array_shift($scoes);
152         scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed');
153         scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 2, 'cmi.core.credit', '0');
155         $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id, true);
156         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result);
157         $this->assertEquals(1, $result['attemptscount']);
158     }
160     public function test_mod_scorm_get_scorm_attempt_count_others_as_teacher() {
161         // As a teacher.
162         self::setUser($this->teacher);
164         // Create a completed attempt for student.
165         $scoes = scorm_get_scoes($this->scorm->id);
166         $sco = array_shift($scoes);
167         scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed');
169         // I should be able to view the attempts for my students.
170         $result = mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id);
171         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_attempt_count_returns(), $result);
172         $this->assertEquals(1, $result['attemptscount']);
173     }
175     public function test_mod_scorm_get_scorm_attempt_count_others_as_student() {
176         // Create a second student.
177         $student2 = self::getDataGenerator()->create_user();
178         $this->getDataGenerator()->enrol_user($student2->id, $this->course->id, $this->studentrole->id, 'manual');
180         // As a student.
181         self::setUser($student2);
183         // I should not be able to view the attempts of another student.
184         $this->setExpectedException('required_capability_exception');
185         mod_scorm_external::get_scorm_attempt_count($this->scorm->id, $this->student->id);
186     }
188     public function test_mod_scorm_get_scorm_attempt_count_invalid_instanceid() {
189         // As student.
190         self::setUser($this->student);
192         // Test invalid instance id.
193         $this->setExpectedException('moodle_exception');
194         mod_scorm_external::get_scorm_attempt_count(0, $this->student->id);
195     }
197     public function test_mod_scorm_get_scorm_attempt_count_invalid_userid() {
198         // As student.
199         self::setUser($this->student);
201         $this->setExpectedException('moodle_exception');
202         mod_scorm_external::get_scorm_attempt_count($this->scorm->id, -1);
203     }
205     /**
206      * Test get scorm scoes
207      */
208     public function test_mod_scorm_get_scorm_scoes() {
209         global $DB;
211         $this->resetAfterTest(true);
213         // Create users.
214         $student = self::getDataGenerator()->create_user();
215         $teacher = self::getDataGenerator()->create_user();
217         // Set to the student user.
218         self::setUser($student);
220         // Create courses to add the modules.
221         $course = self::getDataGenerator()->create_course();
223         // First scorm, dates restriction.
224         $record = new stdClass();
225         $record->course = $course->id;
226         $record->timeopen = time() + DAYSECS;
227         $record->timeclose = $record->timeopen + DAYSECS;
228         $scorm = self::getDataGenerator()->create_module('scorm', $record);
230         // Users enrolments.
231         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
232         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
233         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
234         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
236         // Retrieve my scoes, warning!.
237         try {
238              mod_scorm_external::get_scorm_scoes($scorm->id);
239             $this->fail('Exception expected due to invalid dates.');
240         } catch (moodle_exception $e) {
241             $this->assertEquals('notopenyet', $e->errorcode);
242         }
244         $scorm->timeopen = time() - DAYSECS;
245         $scorm->timeclose = time() - HOURSECS;
246         $DB->update_record('scorm', $scorm);
248         try {
249              mod_scorm_external::get_scorm_scoes($scorm->id);
250             $this->fail('Exception expected due to invalid dates.');
251         } catch (moodle_exception $e) {
252             $this->assertEquals('expired', $e->errorcode);
253         }
255         // Retrieve my scoes, user with permission.
256         self::setUser($teacher);
257         $result = mod_scorm_external::get_scorm_scoes($scorm->id);
258         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_scoes_returns(), $result);
259         $this->assertCount(2, $result['scoes']);
260         $this->assertCount(0, $result['warnings']);
262         $scoes = scorm_get_scoes($scorm->id);
263         $sco = array_shift($scoes);
264         $this->assertEquals((array) $sco, $result['scoes'][0]);
266         $sco = array_shift($scoes);
267         // Remove specific sco data.
268         unset($sco->isvisible);
269         unset($sco->parameters);
270         $this->assertEquals((array) $sco, $result['scoes'][1]);
272         // Use organization.
273         $organization = 'golf_sample_default_org';
274         $result = mod_scorm_external::get_scorm_scoes($scorm->id, $organization);
275         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_scoes_returns(), $result);
276         $this->assertCount(1, $result['scoes']);
277         $this->assertEquals($organization, $result['scoes'][0]['organization']);
278         $this->assertCount(0, $result['warnings']);
280         // Test invalid instance id.
281         try {
282              mod_scorm_external::get_scorm_scoes(0);
283             $this->fail('Exception expected due to invalid instance id.');
284         } catch (moodle_exception $e) {
285             $this->assertEquals('invalidrecord', $e->errorcode);
286         }
287     }
289     /*
290      * Test get scorm user data
291      */
292     public function test_mod_scorm_get_scorm_user_data() {
293         global $DB;
295         $this->resetAfterTest(true);
297         // Create users.
298         $student1 = self::getDataGenerator()->create_user();
299         $teacher = self::getDataGenerator()->create_user();
301         // Set to the student user.
302         self::setUser($student1);
304         // Create courses to add the modules.
305         $course = self::getDataGenerator()->create_course();
307         // First scorm.
308         $record = new stdClass();
309         $record->course = $course->id;
310         $scorm = self::getDataGenerator()->create_module('scorm', $record);
312         // Users enrolments.
313         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
314         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
315         $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id, 'manual');
316         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
318         // Create attempts.
319         $scoes = scorm_get_scoes($scorm->id);
320         $sco = array_shift($scoes);
321         scorm_insert_track($student1->id, $scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed');
322         scorm_insert_track($student1->id, $scorm->id, $sco->id, 1, 'cmi.core.score.raw', '80');
323         scorm_insert_track($student1->id, $scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed');
325         $result = mod_scorm_external::get_scorm_user_data($scorm->id, 1);
326         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_user_data_returns(), $result);
327         $this->assertCount(2, $result['data']);
328         // Find our tracking data.
329         $found = 0;
330         foreach ($result['data'] as $scodata) {
331             foreach ($scodata['userdata'] as $userdata) {
332                 if ($userdata['element'] == 'cmi.core.lesson_status' and $userdata['value'] == 'completed') {
333                     $found++;
334                 }
335                 if ($userdata['element'] == 'cmi.core.score.raw' and $userdata['value'] == '80') {
336                     $found++;
337                 }
338             }
339         }
340         $this->assertEquals(2, $found);
342         // Test invalid instance id.
343         try {
344              mod_scorm_external::get_scorm_user_data(0, 1);
345             $this->fail('Exception expected due to invalid instance id.');
346         } catch (moodle_exception $e) {
347             $this->assertEquals('invalidrecord', $e->errorcode);
348         }
349     }
351     /**
352      * Test insert scorm tracks
353      */
354     public function test_mod_scorm_insert_scorm_tracks() {
355         global $DB;
357         $this->resetAfterTest(true);
359         // Create users.
360         $student = self::getDataGenerator()->create_user();
362         // Set to the student user.
363         self::setUser($student);
365         // Create courses to add the modules.
366         $course = self::getDataGenerator()->create_course();
368         // First scorm, dates restriction.
369         $record = new stdClass();
370         $record->course = $course->id;
371         $record->timeopen = time() + DAYSECS;
372         $record->timeclose = $record->timeopen + DAYSECS;
373         $scorm = self::getDataGenerator()->create_module('scorm', $record);
375         // Get a SCO.
376         $scoes = scorm_get_scoes($scorm->id);
377         $sco = array_shift($scoes);
379         // Tracks.
380         $tracks = array();
381         $tracks[] = array(
382             'element' => 'cmi.core.lesson_status',
383             'value' => 'completed'
384         );
385         $tracks[] = array(
386             'element' => 'cmi.core.score.raw',
387             'value' => '80'
388         );
390         // Users enrolments.
391         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
392         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
394         // Exceptions first.
395         try {
396             mod_scorm_external::insert_scorm_tracks($sco->id, 1, $tracks);
397             $this->fail('Exception expected due to dates');
398         } catch (moodle_exception $e) {
399             $this->assertEquals('notopenyet', $e->errorcode);
400         }
402         $scorm->timeopen = time() - DAYSECS;
403         $scorm->timeclose = time() - HOURSECS;
404         $DB->update_record('scorm', $scorm);
406         try {
407             mod_scorm_external::insert_scorm_tracks($sco->id, 1, $tracks);
408             $this->fail('Exception expected due to dates');
409         } catch (moodle_exception $e) {
410             $this->assertEquals('expired', $e->errorcode);
411         }
413         // Test invalid instance id.
414         try {
415              mod_scorm_external::insert_scorm_tracks(0, 1, $tracks);
416             $this->fail('Exception expected due to invalid sco id.');
417         } catch (moodle_exception $e) {
418             $this->assertEquals('cannotfindsco', $e->errorcode);
419         }
421         $scorm->timeopen = 0;
422         $scorm->timeclose = 0;
423         $DB->update_record('scorm', $scorm);
425         // Retrieve my tracks.
426         $result = mod_scorm_external::insert_scorm_tracks($sco->id, 1, $tracks);
427         $result = external_api::clean_returnvalue(mod_scorm_external::insert_scorm_tracks_returns(), $result);
428         $this->assertCount(0, $result['warnings']);
430         $trackids = $DB->get_records('scorm_scoes_track', array('userid' => $student->id, 'scoid' => $sco->id,
431                                                                 'scormid' => $scorm->id, 'attempt' => 1));
432         // We use asort here to prevent problems with ids ordering.
433         $expectedkeys = array_keys($trackids);
434         $this->assertEquals(asort($expectedkeys), asort($result['trackids']));
435     }
437     /**
438      * Test get scorm sco tracks
439      */
440     public function test_mod_scorm_get_scorm_sco_tracks() {
441         global $DB;
443         $this->resetAfterTest(true);
445         // Create users.
446         $student = self::getDataGenerator()->create_user();
447         $otherstudent = self::getDataGenerator()->create_user();
448         $teacher = self::getDataGenerator()->create_user();
450         // Set to the student user.
451         self::setUser($student);
453         // Create courses to add the modules.
454         $course = self::getDataGenerator()->create_course();
456         // First scorm.
457         $record = new stdClass();
458         $record->course = $course->id;
459         $scorm = self::getDataGenerator()->create_module('scorm', $record);
461         // Users enrolments.
462         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
463         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
464         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
465         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
467         // Create attempts.
468         $scoes = scorm_get_scoes($scorm->id);
469         $sco = array_shift($scoes);
470         scorm_insert_track($student->id, $scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed');
471         scorm_insert_track($student->id, $scorm->id, $sco->id, 1, 'cmi.core.score.raw', '80');
472         scorm_insert_track($student->id, $scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed');
474         $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id, 1);
475         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result);
476         // 7 default elements + 2 custom ones.
477         $this->assertCount(9, $result['data']['tracks']);
478         $this->assertEquals(1, $result['data']['attempt']);
479         // Find our tracking data.
480         $found = 0;
481         foreach ($result['data']['tracks'] as $userdata) {
482             if ($userdata['element'] == 'cmi.core.lesson_status' and $userdata['value'] == 'completed') {
483                 $found++;
484             }
485             if ($userdata['element'] == 'cmi.core.score.raw' and $userdata['value'] == '80') {
486                 $found++;
487             }
488         }
489         $this->assertEquals(2, $found);
491         // Capabilities check.
492         try {
493              mod_scorm_external::get_scorm_sco_tracks($sco->id, $otherstudent->id);
494             $this->fail('Exception expected due to invalid instance id.');
495         } catch (required_capability_exception $e) {
496             $this->assertEquals('nopermissions', $e->errorcode);
497         }
499         self::setUser($teacher);
500         // Ommit the attempt parameter, the function should calculate the last attempt.
501         $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id);
502         $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result);
503         // 7 default elements + 1 custom one.
504         $this->assertCount(8, $result['data']['tracks']);
505         $this->assertEquals(2, $result['data']['attempt']);
507         // Test invalid instance id.
508         try {
509              mod_scorm_external::get_scorm_sco_tracks(0, 1);
510             $this->fail('Exception expected due to invalid instance id.');
511         } catch (moodle_exception $e) {
512             $this->assertEquals('cannotfindsco', $e->errorcode);
513         }
514         // Invalid user.
515         try {
516              mod_scorm_external::get_scorm_sco_tracks($sco->id, 0);
517             $this->fail('Exception expected due to invalid instance id.');
518         } catch (moodle_exception $e) {
519             $this->assertEquals('invaliduser', $e->errorcode);
520         }
521     }
523     /*
524      * Test get scorms by courses
525      */
526     public function test_mod_scorm_get_scorms_by_courses() {
527         global $DB;
529         $this->resetAfterTest(true);
531         // Create users.
532         $student = self::getDataGenerator()->create_user();
533         $teacher = self::getDataGenerator()->create_user();
535         // Set to the student user.
536         self::setUser($student);
538         // Create courses to add the modules.
539         $course1 = self::getDataGenerator()->create_course();
540         $course2 = self::getDataGenerator()->create_course();
542         // First scorm.
543         $record = new stdClass();
544         $record->introformat = FORMAT_HTML;
545         $record->course = $course1->id;
546         $scorm1 = self::getDataGenerator()->create_module('scorm', $record);
548         // Second scorm.
549         $record = new stdClass();
550         $record->introformat = FORMAT_HTML;
551         $record->course = $course2->id;
552         $scorm2 = self::getDataGenerator()->create_module('scorm', $record);
554         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
555         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
557         // Users enrolments.
558         $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
559         $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id, 'manual');
561         // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
562         $enrol = enrol_get_plugin('manual');
563         $enrolinstances = enrol_get_instances($course2->id, true);
564         foreach ($enrolinstances as $courseenrolinstance) {
565             if ($courseenrolinstance->enrol == "manual") {
566                 $instance2 = $courseenrolinstance;
567                 break;
568             }
569         }
570         $enrol->enrol_user($instance2, $student->id, $studentrole->id);
572         $returndescription = mod_scorm_external::get_scorms_by_courses_returns();
574         // Test open/close dates.
576         $timenow = time();
577         $scorm1->timeopen = $timenow - DAYSECS;
578         $scorm1->timeclose = $timenow - HOURSECS;
579         $DB->update_record('scorm', $scorm1);
581         $result = mod_scorm_external::get_scorms_by_courses(array($course1->id));
582         $result = external_api::clean_returnvalue($returndescription, $result);
583         $this->assertCount(1, $result['warnings']);
584         // Only 'id', 'coursemodule', 'course', 'name', 'intro', 'introformat'.
585         $this->assertCount(6, $result['scorms'][0]);
586         $this->assertEquals('expired', $result['warnings'][0]['warningcode']);
588         $scorm1->timeopen = $timenow + DAYSECS;
589         $scorm1->timeclose = $scorm1->timeopen + DAYSECS;
590         $DB->update_record('scorm', $scorm1);
592         $result = mod_scorm_external::get_scorms_by_courses(array($course1->id));
593         $result = external_api::clean_returnvalue($returndescription, $result);
594         $this->assertCount(1, $result['warnings']);
595         // Only 'id', 'coursemodule', 'course', 'name', 'intro', 'introformat'.
596         $this->assertCount(6, $result['scorms'][0]);
597         $this->assertEquals('notopenyet', $result['warnings'][0]['warningcode']);
599         // Reset times.
600         $scorm1->timeopen = 0;
601         $scorm1->timeclose = 0;
602         $DB->update_record('scorm', $scorm1);
604         // Create what we expect to be returned when querying the two courses.
605         // First for the student user.
606         $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'version', 'maxgrade',
607                                 'grademethod', 'whatgrade', 'maxattempt', 'forcecompleted', 'forcenewattempt', 'lastattemptlock',
608                                 'displayattemptstatus', 'displaycoursestructure', 'sha1hash', 'md5hash', 'revision', 'launch',
609                                 'skipview', 'hidebrowse', 'hidetoc', 'nav', 'navpositionleft', 'navpositiontop', 'auto',
610                                 'popup', 'width', 'height', 'timeopen', 'timeclose', 'displayactivityname', 'packagesize',
611                                 'packageurl', 'scormtype', 'reference');
613         // Add expected coursemodule and data.
614         $scorm1->coursemodule = $scorm1->cmid;
615         $scorm1->section = 0;
616         $scorm1->visible = true;
617         $scorm1->groupmode = 0;
618         $scorm1->groupingid = 0;
620         $scorm2->coursemodule = $scorm2->cmid;
621         $scorm2->section = 0;
622         $scorm2->visible = true;
623         $scorm2->groupmode = 0;
624         $scorm2->groupingid = 0;
626         // SCORM size. The same package is used in both SCORMs.
627         $scormcontext1 = context_module::instance($scorm1->cmid);
628         $scormcontext2 = context_module::instance($scorm2->cmid);
629         $fs = get_file_storage();
630         $packagefile = $fs->get_file($scormcontext1->id, 'mod_scorm', 'package', 0, '/', $scorm1->reference);
631         $packagesize = $packagefile->get_filesize();
633         $packageurl1 = moodle_url::make_webservice_pluginfile_url(
634                             $scormcontext1->id, 'mod_scorm', 'package', 0, '/', $scorm1->reference)->out(false);
635         $packageurl2 = moodle_url::make_webservice_pluginfile_url(
636                             $scormcontext2->id, 'mod_scorm', 'package', 0, '/', $scorm2->reference)->out(false);
638         $scorm1->packagesize = $packagesize;
639         $scorm1->packageurl = $packageurl1;
640         $scorm2->packagesize = $packagesize;
641         $scorm2->packageurl = $packageurl2;
643         $expected1 = array();
644         $expected2 = array();
645         foreach ($expectedfields as $field) {
647             // Since we return the fields used as boolean as PARAM_BOOL instead PARAM_INT we need to force casting here.
648             // From the returned fields definition we obtain the type expected for the field.
649             $fieldtype = $returndescription->keys['scorms']->content->keys[$field]->type;
650             if ($fieldtype == PARAM_BOOL) {
651                 $expected1[$field] = (bool) $scorm1->{$field};
652                 $expected2[$field] = (bool) $scorm2->{$field};
653             } else {
654                 $expected1[$field] = $scorm1->{$field};
655                 $expected2[$field] = $scorm2->{$field};
656             }
657         }
659         $expectedscorms = array();
660         $expectedscorms[] = $expected2;
661         $expectedscorms[] = $expected1;
663         // Call the external function passing course ids.
664         $result = mod_scorm_external::get_scorms_by_courses(array($course2->id, $course1->id));
665         $result = external_api::clean_returnvalue($returndescription, $result);
666         $this->assertEquals($expectedscorms, $result['scorms']);
668         // Call the external function without passing course id.
669         $result = mod_scorm_external::get_scorms_by_courses();
670         $result = external_api::clean_returnvalue($returndescription, $result);
671         $this->assertEquals($expectedscorms, $result['scorms']);
673         // Unenrol user from second course and alter expected scorms.
674         $enrol->unenrol_user($instance2, $student->id);
675         array_shift($expectedscorms);
677         // Call the external function without passing course id.
678         $result = mod_scorm_external::get_scorms_by_courses();
679         $result = external_api::clean_returnvalue($returndescription, $result);
680         $this->assertEquals($expectedscorms, $result['scorms']);
682         // Call for the second course we unenrolled the user from, expected warning.
683         $result = mod_scorm_external::get_scorms_by_courses(array($course2->id));
684         $this->assertCount(1, $result['warnings']);
685         $this->assertEquals('1', $result['warnings'][0]['warningcode']);
686         $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
688         // Now, try as a teacher for getting all the additional fields.
689         self::setUser($teacher);
691         $additionalfields = array('updatefreq', 'timemodified', 'options',
692                                     'completionstatusrequired', 'completionscorerequired', 'autocommit',
693                                     'section', 'visible', 'groupmode', 'groupingid');
695         foreach ($additionalfields as $field) {
696             $fieldtype = $returndescription->keys['scorms']->content->keys[$field]->type;
698             if ($fieldtype == PARAM_BOOL) {
699                 $expectedscorms[0][$field] = (bool) $scorm1->{$field};
700             } else {
701                 $expectedscorms[0][$field] = $scorm1->{$field};
702             }
703         }
705         $result = mod_scorm_external::get_scorms_by_courses();
706         $result = external_api::clean_returnvalue($returndescription, $result);
707         $this->assertEquals($expectedscorms, $result['scorms']);
709         // Even with the SCORM closed in time teacher should retrieve the info.
710         $scorm1->timeopen = $timenow - DAYSECS;
711         $scorm1->timeclose = $timenow - HOURSECS;
712         $DB->update_record('scorm', $scorm1);
714         $expectedscorms[0]['timeopen'] = $scorm1->timeopen;
715         $expectedscorms[0]['timeclose'] = $scorm1->timeclose;
717         $result = mod_scorm_external::get_scorms_by_courses();
718         $result = external_api::clean_returnvalue($returndescription, $result);
719         $this->assertEquals($expectedscorms, $result['scorms']);
721         // Admin also should get all the information.
722         self::setAdminUser();
724         $result = mod_scorm_external::get_scorms_by_courses(array($course1->id));
725         $result = external_api::clean_returnvalue($returndescription, $result);
726         $this->assertEquals($expectedscorms, $result['scorms']);
727     }