MDL-38703 course: backport visibility tests from MDL-36417
[moodle.git] / course / tests / courselib_test.php
CommitLineData
354b214c
PS
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 * Course related unit tests
19 *
20 * @package core
21 * @category phpunit
22 * @copyright 2012 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28
29class courselib_testcase extends advanced_testcase {
30
31 public function test_reorder_sections() {
32 global $DB;
33 $this->resetAfterTest(true);
34
35 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
36 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
37 $oldsections = array();
38 $sections = array();
f31ee129 39 foreach ($DB->get_records('course_sections', array('course'=>$course->id), 'id') as $section) {
354b214c
PS
40 $oldsections[$section->section] = $section->id;
41 $sections[$section->id] = $section->section;
42 }
43 ksort($oldsections);
44
45 $neworder = reorder_sections($sections, 2, 4);
46 $neworder = array_keys($neworder);
47 $this->assertEquals($oldsections[0], $neworder[0]);
48 $this->assertEquals($oldsections[1], $neworder[1]);
49 $this->assertEquals($oldsections[2], $neworder[4]);
50 $this->assertEquals($oldsections[3], $neworder[2]);
51 $this->assertEquals($oldsections[4], $neworder[3]);
52 $this->assertEquals($oldsections[5], $neworder[5]);
53 $this->assertEquals($oldsections[6], $neworder[6]);
54
eb01aa2c
RT
55 $neworder = reorder_sections($sections, 4, 2);
56 $neworder = array_keys($neworder);
57 $this->assertEquals($oldsections[0], $neworder[0]);
58 $this->assertEquals($oldsections[1], $neworder[1]);
59 $this->assertEquals($oldsections[2], $neworder[3]);
60 $this->assertEquals($oldsections[3], $neworder[4]);
61 $this->assertEquals($oldsections[4], $neworder[2]);
62 $this->assertEquals($oldsections[5], $neworder[5]);
63 $this->assertEquals($oldsections[6], $neworder[6]);
64
354b214c
PS
65 $neworder = reorder_sections(1, 2, 4);
66 $this->assertFalse($neworder);
67 }
68
3d8fe482 69 public function test_move_section_down() {
354b214c
PS
70 global $DB;
71 $this->resetAfterTest(true);
72
73 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
74 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
75 $oldsections = array();
76 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
77 $oldsections[$section->section] = $section->id;
78 }
79 ksort($oldsections);
80
3d8fe482 81 // Test move section down..
354b214c
PS
82 move_section_to($course, 2, 4);
83 $sections = array();
84 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
85 $sections[$section->section] = $section->id;
86 }
87 ksort($sections);
88
89 $this->assertEquals($oldsections[0], $sections[0]);
90 $this->assertEquals($oldsections[1], $sections[1]);
91 $this->assertEquals($oldsections[2], $sections[4]);
92 $this->assertEquals($oldsections[3], $sections[2]);
93 $this->assertEquals($oldsections[4], $sections[3]);
94 $this->assertEquals($oldsections[5], $sections[5]);
95 $this->assertEquals($oldsections[6], $sections[6]);
96 }
97
3d8fe482
DP
98 public function test_move_section_up() {
99 global $DB;
100 $this->resetAfterTest(true);
101
102 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
103 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
104 $oldsections = array();
105 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
106 $oldsections[$section->section] = $section->id;
107 }
108 ksort($oldsections);
109
110 // Test move section up..
111 move_section_to($course, 6, 4);
112 $sections = array();
113 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
114 $sections[$section->section] = $section->id;
115 }
116 ksort($sections);
117
118 $this->assertEquals($oldsections[0], $sections[0]);
119 $this->assertEquals($oldsections[1], $sections[1]);
120 $this->assertEquals($oldsections[2], $sections[2]);
121 $this->assertEquals($oldsections[3], $sections[3]);
122 $this->assertEquals($oldsections[4], $sections[5]);
123 $this->assertEquals($oldsections[5], $sections[6]);
124 $this->assertEquals($oldsections[6], $sections[4]);
125 }
126
127 public function test_move_section_marker() {
128 global $DB;
129 $this->resetAfterTest(true);
130
131 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
132 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
133
134 // Set course marker to the section we are going to move..
135 course_set_marker($course->id, 2);
136 // Verify that the course marker is set correctly.
137 $course = $DB->get_record('course', array('id' => $course->id));
138 $this->assertEquals(2, $course->marker);
139
140 // Test move the marked section down..
141 move_section_to($course, 2, 4);
142
143 // Verify that the coruse marker has been moved along with the section..
144 $course = $DB->get_record('course', array('id' => $course->id));
145 $this->assertEquals(4, $course->marker);
146
147 // Test move the marked section up..
148 move_section_to($course, 4, 3);
149
150 // Verify that the course marker has been moved along with the section..
151 $course = $DB->get_record('course', array('id' => $course->id));
152 $this->assertEquals(3, $course->marker);
153
154 // Test moving a non-marked section above the marked section..
155 move_section_to($course, 4, 2);
156
157 // Verify that the course marker has been moved down to accomodate..
158 $course = $DB->get_record('course', array('id' => $course->id));
159 $this->assertEquals(4, $course->marker);
160
161 // Test moving a non-marked section below the marked section..
162 move_section_to($course, 3, 6);
163
164 // Verify that the course marker has been up to accomodate..
165 $course = $DB->get_record('course', array('id' => $course->id));
166 $this->assertEquals(3, $course->marker);
167 }
168
354b214c
PS
169 public function test_get_course_display_name_for_list() {
170 global $CFG;
171 $this->resetAfterTest(true);
172
173 $course = $this->getDataGenerator()->create_course(array('shortname' => 'FROG101', 'fullname' => 'Introduction to pond life'));
174
175 $CFG->courselistshortnames = 0;
176 $this->assertEquals('Introduction to pond life', get_course_display_name_for_list($course));
177
178 $CFG->courselistshortnames = 1;
179 $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course));
180 }
b1a8aa73
ARN
181
182 public function test_create_course_category() {
183 global $CFG, $DB;
184 $this->resetAfterTest(true);
185
186 // Create the category
187 $data = new stdClass();
188 $data->name = 'aaa';
189 $data->description = 'aaa';
190 $data->idnumber = '';
191
192 $category1 = create_course_category($data);
193
194 // Initially confirm that base data was inserted correctly
195 $this->assertEquals($data->name, $category1->name);
196 $this->assertEquals($data->description, $category1->description);
197 $this->assertEquals($data->idnumber, $category1->idnumber);
198
199 // sortorder should be blank initially
200 $this->assertEmpty($category1->sortorder);
201
202 // Calling fix_course_sortorder() should provide a new sortorder
203 fix_course_sortorder();
204 $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
205
206 $this->assertGreaterThanOrEqual(1, $category1->sortorder);
207
208 // Create two more categories and test the sortorder worked correctly
209 $data->name = 'ccc';
210 $category2 = create_course_category($data);
211 $this->assertEmpty($category2->sortorder);
212
213 $data->name = 'bbb';
214 $category3 = create_course_category($data);
215 $this->assertEmpty($category3->sortorder);
216
217 // Calling fix_course_sortorder() should provide a new sortorder to give category1,
218 // category2, category3. New course categories are ordered by id not name
219 fix_course_sortorder();
220
221 $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
222 $category2 = $DB->get_record('course_categories', array('id' => $category2->id));
223 $category3 = $DB->get_record('course_categories', array('id' => $category3->id));
224
225 $this->assertGreaterThanOrEqual($category1->sortorder, $category2->sortorder);
226 $this->assertGreaterThanOrEqual($category2->sortorder, $category3->sortorder);
227 $this->assertGreaterThanOrEqual($category1->sortorder, $category3->sortorder);
228 }
0c9e550d
ARN
229
230 public function test_move_module_in_course() {
231 global $DB;
232
233 $this->resetAfterTest(true);
234 // Setup fixture
235 $course = $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections' => true));
236 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
237
238 $cms = get_fast_modinfo($course)->get_cms();
239 $cm = reset($cms);
240
241 $newsection = get_fast_modinfo($course)->get_section_info(3);
242 $oldsectionid = $cm->section;
243
244 // Perform the move
245 $result = moveto_module($cm, $newsection);
246 $this->assertTrue($result);
247
248 // get_fast_modinfo(reset) is usually called the code calling moveto_module so call it here
249 $reset = 'reset';
250 get_fast_modinfo($reset);
251 $cms = get_fast_modinfo($course)->get_cms();
252 $cm = reset($cms);
253
254 // Check that the old section's sequence no longer contains this ID
255 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid));
256 $oldsequences = explode(',', $newsection->sequence);
257 $this->assertFalse(in_array($cm->id, $oldsequences));
258
259 // Check that the new section's sequence now contains this ID
260 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id));
261 $newsequences = explode(',', $newsection->sequence);
262 $this->assertTrue(in_array($cm->id, $newsequences));
263
264 // Check that the section number has been changed in the cm
265 $this->assertEquals($newsection->id, $cm->section);
266
267
268 // Perform a second move as some issues were only seen on the second move
269 $newsection = get_fast_modinfo($course)->get_section_info(2);
270 $oldsectionid = $cm->section;
271 $result = moveto_module($cm, $newsection);
272 $this->assertTrue($result);
273
274 // get_fast_modinfo(reset) is usually called the code calling moveto_module so call it here
275 $reset = 'reset';
276 get_fast_modinfo($reset);
277 $cms = get_fast_modinfo($course)->get_cms();
278 $cm = reset($cms);
279
280 // Check that the old section's sequence no longer contains this ID
281 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid));
282 $oldsequences = explode(',', $newsection->sequence);
283 $this->assertFalse(in_array($cm->id, $oldsequences));
284
285 // Check that the new section's sequence now contains this ID
286 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id));
287 $newsequences = explode(',', $newsection->sequence);
288 $this->assertTrue(in_array($cm->id, $newsequences));
289
290 // Check that the section number has been changed in the cm
291 $this->assertEquals($newsection->id, $cm->section);
292 }
1a5d5334 293
47b17f91
DP
294 public function test_module_visibility() {
295 $this->setAdminUser();
296 $this->resetAfterTest(true);
297
298 // Create course and modules.
299 $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
300 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
301 $page = $this->getDataGenerator()->create_module('page', array('duedate' => time(), 'course' => $course->id));
302 $modules = compact('forum', 'page');
303
304 // Hiding the modules.
305 foreach ($modules as $mod) {
306 set_coursemodule_visible($mod->cmid, 0);
307 $this->check_module_visibility($mod, 0, 0);
308 }
309
310 // Showing the modules.
311 foreach ($modules as $mod) {
312 set_coursemodule_visible($mod->cmid, 1);
313 $this->check_module_visibility($mod, 1, 1);
314 }
315 }
316
317 public function test_section_visibility() {
318 $this->setAdminUser();
319 $this->resetAfterTest(true);
320
321 // Create course.
322 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
323
324 // Testing an empty section.
325 $sectionnumber = 1;
326 set_section_visible($course->id, $sectionnumber, 0);
327 $section_info = get_fast_modinfo($course)->get_section_info($sectionnumber);
328 $this->assertEquals($section_info->visible, 0);
329 set_section_visible($course->id, $sectionnumber, 1);
330 $section_info = get_fast_modinfo($course)->get_section_info($sectionnumber);
331 $this->assertEquals($section_info->visible, 1);
332
333 // Testing a section with visible modules.
334 $sectionnumber = 2;
335 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
336 array('section' => $sectionnumber));
337 $page= $this->getDataGenerator()->create_module('page', array('duedate' => time(),
338 'course' => $course->id), array('section' => $sectionnumber));
339 $modules = compact('forum', 'page');
340 set_section_visible($course->id, $sectionnumber, 0);
341 $section_info = get_fast_modinfo($course)->get_section_info($sectionnumber);
342 $this->assertEquals($section_info->visible, 0);
343 foreach ($modules as $mod) {
344 $this->check_module_visibility($mod, 0, 1);
345 }
346 set_section_visible($course->id, $sectionnumber, 1);
347 $section_info = get_fast_modinfo($course)->get_section_info($sectionnumber);
348 $this->assertEquals($section_info->visible, 1);
349 foreach ($modules as $mod) {
350 $this->check_module_visibility($mod, 1, 1);
351 }
352
353 // Testing a section with hidden modules, which should stay hidden.
354 $sectionnumber = 3;
355 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
356 array('section' => $sectionnumber));
357 $page = $this->getDataGenerator()->create_module('page', array('duedate' => time(),
358 'course' => $course->id), array('section' => $sectionnumber));
359 $modules = compact('forum', 'page');
360 foreach ($modules as $mod) {
361 set_coursemodule_visible($mod->cmid, 0);
362 $this->check_module_visibility($mod, 0, 0);
363 }
364 set_section_visible($course->id, $sectionnumber, 0);
365 $section_info = get_fast_modinfo($course)->get_section_info($sectionnumber);
366 $this->assertEquals($section_info->visible, 0);
367 foreach ($modules as $mod) {
368 $this->check_module_visibility($mod, 0, 0);
369 }
370 set_section_visible($course->id, $sectionnumber, 1);
371 $section_info = get_fast_modinfo($course)->get_section_info($sectionnumber);
372 $this->assertEquals($section_info->visible, 1);
373 foreach ($modules as $mod) {
374 $this->check_module_visibility($mod, 0, 0);
375 }
376 }
377
378 /**
379 * Helper function to assert that a module has correctly been made visible, or hidden.
380 *
381 * @param stdClass $mod module information
382 * @param int $visibility the current state of the module
383 * @param int $visibleold the current state of the visibleold property
384 * @return void
385 */
386 public function check_module_visibility($mod, $visibility, $visibleold) {
387 global $DB;
388
389 rebuild_course_cache($mod->course);
390 $course = $DB->get_record('course', array('id' => $mod->course));
391 $cm = get_fast_modinfo($course)->get_cm($mod->cmid);
392
393 $this->assertEquals($visibility, $cm->visible);
394 $this->assertEquals($visibleold, $cm->visibleold);
395
396 // Check the module grade items.
397 $grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $cm->modname,
398 'iteminstance' => $cm->instance, 'courseid' => $cm->course));
399 if ($grade_items) {
400 foreach ($grade_items as $grade_item) {
401 if ($visibility) {
402 $this->assertFalse($grade_item->is_hidden(), "$cm->modname grade_item not visible");
403 } else {
404 $this->assertTrue($grade_item->is_hidden(), "$cm->modname grade_item not hidden");
405 }
406 }
407 }
408
409 // Check the events visibility.
410 if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $cm->modname))) {
411 foreach ($events as $event) {
412 $calevent = new calendar_event($event);
413 $this->assertEquals($visibility, $calevent->visible, "$cm->modname calendar_event visibility");
414 }
415 }
416 }
417
1a5d5334
DP
418 /**
419 * Tests moving a module between hidden/visible sections and
420 * verifies that the course/module visiblity seettings are
421 * retained.
422 */
423 public function test_moveto_module_between_hidden_sections() {
424 global $DB;
425
426 $this->resetAfterTest(true);
427
428 $course = $this->getDataGenerator()->create_course(array('numsections' => 4), array('createsections' => true));
429 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
430 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
431 $quiz= $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
432
433 // Set the page as hidden
434 set_coursemodule_visible($page->cmid, 0);
435
436 // Set sections 3 as hidden.
437 set_section_visible($course->id, 3, 0);
438
439 $modinfo = get_fast_modinfo($course);
440
441 $hiddensection = $modinfo->get_section_info(3);
442 // New section is definitely not visible:
443 $this->assertEquals($hiddensection->visible, 0);
444
445 $forumcm = $modinfo->cms[$forum->cmid];
446 $pagecm = $modinfo->cms[$page->cmid];
447
448 // Move the forum and the page to a hidden section.
449 moveto_module($forumcm, $hiddensection);
450 moveto_module($pagecm, $hiddensection);
451
452 // Reset modinfo cache.
453 $reset = 'reset';
454 get_fast_modinfo($reset);
455
456 $modinfo = get_fast_modinfo($course);
457
458 // Verify that forum and page have been moved to the hidden section and quiz has not.
459 $this->assertContains($forum->cmid, $modinfo->sections[3]);
460 $this->assertContains($page->cmid, $modinfo->sections[3]);
461 $this->assertNotContains($quiz->cmid, $modinfo->sections[3]);
462
463 // Verify that forum has been made invisible.
464 $forumcm = $modinfo->cms[$forum->cmid];
465 $this->assertEquals($forumcm->visible, 0);
466 // Verify that old state has been retained.
467 $this->assertEquals($forumcm->visibleold, 1);
468
469 // Verify that page has stayed invisible.
470 $pagecm = $modinfo->cms[$page->cmid];
471 $this->assertEquals($pagecm->visible, 0);
472 // Verify that old state has been retained.
473 $this->assertEquals($pagecm->visibleold, 0);
474
475 // Verify that quiz has been unaffected.
476 $quizcm = $modinfo->cms[$quiz->cmid];
477 $this->assertEquals($quizcm->visible, 1);
478
479 // Move forum and page back to visible section.
480 $visiblesection = $modinfo->get_section_info(2);
481 moveto_module($forumcm, $visiblesection);
482 moveto_module($pagecm, $visiblesection);
483
484 // Reset modinfo cache.
485 $reset = 'reset';
486 get_fast_modinfo($reset);
487 $modinfo = get_fast_modinfo($course);
488
489 // Verify that forum has been made visible.
490 $forumcm = $modinfo->cms[$forum->cmid];
491 $this->assertEquals($forumcm->visible, 1);
492
493 // Verify that page has stayed invisible.
494 $pagecm = $modinfo->cms[$page->cmid];
495 $this->assertEquals($pagecm->visible, 0);
496
497 // Move the page in the same section (this is what mod duplicate does_
498 moveto_module($pagecm, $visiblesection, $forumcm);
499
500 // Reset modinfo cache.
501 $reset = 'reset';
502 get_fast_modinfo($reset);
503
504 // Verify that the the page is still hidden
505 $modinfo = get_fast_modinfo($course);
506 $pagecm = $modinfo->cms[$page->cmid];
507 $this->assertEquals($pagecm->visible, 0);
508 }
509
510 /**
511 * Tests moving a module around in the same section. moveto_module()
512 * is called this way in modduplicate.
513 */
514 public function test_moveto_module_in_same_section() {
515 global $DB;
516
517 $this->resetAfterTest(true);
518
519 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
520 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
521 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
522
523 // Simulate inconsistent visible/visibleold values (MDL-38713).
524 $cm = $DB->get_record('course_modules', array('id' => $page->cmid), '*', MUST_EXIST);
525 $cm->visible = 0;
526 $cm->visibleold = 1;
527 $DB->update_record('course_modules', $cm);
528
529 $modinfo = get_fast_modinfo($course);
530 $forumcm = $modinfo->cms[$forum->cmid];
531 $pagecm = $modinfo->cms[$page->cmid];
532
533 // Verify that page is hidden.
534 $this->assertEquals($pagecm->visible, 0);
535
536 // Verify section 0 is where all mods added.
537 $section = $modinfo->get_section_info(0);
538 $this->assertEquals($section->id, $forumcm->section);
539 $this->assertEquals($section->id, $pagecm->section);
540
541
542 // Move the forum and the page to a hidden section.
543 moveto_module($pagecm, $section, $forumcm);
544
545 // Reset modinfo cache.
546 $reset = 'reset';
547 get_fast_modinfo($reset);
548
549 // Verify that the the page is still hidden
550 $modinfo = get_fast_modinfo($course);
551 $pagecm = $modinfo->cms[$page->cmid];
552 $this->assertEquals($pagecm->visible, 0);
553 }
354b214c 554}