0a1d84c878535052343fdd5bf5dd385851568cdb
[moodle.git] / mod / quiz / tests / structure_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  * Quiz events tests.
19  *
20  * @package   mod_quiz
21  * @category  test
22  * @copyright 2013 Adrian Greeve
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
31 /**
32  * Unit tests for quiz events.
33  *
34  * @copyright  2013 Adrian Greeve
35  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36  */
37 class mod_quiz_structure_testcase extends advanced_testcase {
39     /**
40      * Prepare the quiz object with standard data. Ready for testing.
41      */
42     protected function prepare_quiz_data() {
44         $this->resetAfterTest(true);
46         // Create a course.
47         $course = $this->getDataGenerator()->create_course();
49         // Make a quiz.
50         $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
52         $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0,
53             'grade' => 100.0, 'sumgrades' => 2));
55         $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id);
57         return array($quiz, $cm, $course);
58     }
60     /**
61      * Test getting the quiz slots.
62      */
63     public function test_get_quiz_slots() {
64         // Get basic quiz.
65         list($quiz, $cm, $course) = $this->prepare_quiz_data();
66         $quizobj = new quiz($quiz, $cm, $course);
67         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
69         // When no slots exist or slots propery is not set.
70         $slots = $structure->get_slots();
71         $this->assertInternalType('array', $slots);
72         $this->assertCount(0, $slots);
74         // Append slots to the quiz.
75         $this->add_eight_questions_to_the_quiz($quiz);
76         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
78         // Are the correct slots returned?
79         $slots = $structure->get_slots();
80         $this->assertCount(8, $slots);
81     }
83     /**
84      * Test getting the quiz sections.
85      */
86     public function test_get_quiz_sections() {
87         // Get basic quiz.
88         list($quiz, $cm, $course) = $this->prepare_quiz_data();
89         $quizobj = new quiz($quiz, $cm, $course);
90         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
92         // Are the correct sections returned?
93         $sections = $structure->get_quiz_sections();
94         $this->assertCount(1, $sections);
95     }
97     /**
98      * Verify that the given layout matches that expected.
99      * @param array $expectedlayout
100      * @param \mod_quiz\structure $structure
101      */
102     protected function assert_quiz_layout($expectedlayout, \mod_quiz\structure $structure) {
103         $slotnumber = 0;
104         foreach ($expectedlayout as $slotid => $page) {
105             $slotnumber += 1;
106             $this->assertEquals($slotid, $structure->get_question_in_slot($slotnumber)->slotid,
107                     'Wrong question in slot ' . $slotnumber);
108             $this->assertEquals($page, $structure->get_question_in_slot($slotnumber)->page,
109                     'Wrong page number for slot ' . $slotnumber);
110         }
111     }
113     /**
114      * Test moving slots in the quiz.
115      */
116     public function test_move_slot() {
117         // Create a test quiz with 8 questions.
118         list($quiz, $cm, $course) = $this->prepare_quiz_data();
119         $this->add_eight_questions_to_the_quiz($quiz);
120         $quizobj = new quiz($quiz, $cm, $course);
121         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
123         // Store the original order of slots, so we can assert what has changed.
124         $originalslotids = array();
125         foreach ($structure->get_slots() as $slot) {
126             $originalslotids[$slot->slot] = $slot->id;
127         }
129         // Don't actually move anything. Check the layout is unchanged.
130         $idmove = $structure->get_question_in_slot(2)->slotid;
131         $idbefore = $structure->get_question_in_slot(1)->slotid;
132         $structure->move_slot($idmove, $idbefore, 2);
134         // Having called move, we need to reload $structure.
135         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
136         $this->assert_quiz_layout(array(
137                     $originalslotids[1] => 1,
138                     $originalslotids[2] => 2,
139                     $originalslotids[3] => 2,
140                     $originalslotids[4] => 2,
141                     $originalslotids[5] => 2,
142                     $originalslotids[6] => 2,
143                     $originalslotids[7] => 3,
144                     $originalslotids[8] => 4,
145                 ), $structure);
147         // Slots don't move. Page changed.
148         $idmove = $structure->get_question_in_slot(2)->slotid;
149         $idbefore = $structure->get_question_in_slot(1)->slotid;
150         $structure->move_slot($idmove, $idbefore, 1);
152         // Having called move, we need to reload $structure.
153         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
154         $this->assert_quiz_layout(array(
155                     $originalslotids[1] => 1,
156                     $originalslotids[2] => 1,
157                     $originalslotids[3] => 2,
158                     $originalslotids[4] => 2,
159                     $originalslotids[5] => 2,
160                     $originalslotids[6] => 2,
161                     $originalslotids[7] => 3,
162                     $originalslotids[8] => 4,
163                 ), $structure);
165         // Slots move 2 > 3. Page unchanged. Pages not reordered.
166         $idmove = $structure->get_question_in_slot(2)->slotid;
167         $idbefore = $structure->get_question_in_slot(3)->slotid;
168         $structure->move_slot($idmove, $idbefore, '2');
170         // Having called move, we need to reload $structure.
171         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
172         $this->assert_quiz_layout(array(
173                 $originalslotids[1] => 1,
174                 $originalslotids[3] => 2,
175                 $originalslotids[2] => 2,
176                 $originalslotids[4] => 2,
177                 $originalslotids[5] => 2,
178                 $originalslotids[6] => 2,
179                 $originalslotids[7] => 3,
180                 $originalslotids[8] => 4,
181         ), $structure);
183         // Slots move 6 > 7. Page changed. Pages not reordered.
184         $idmove = $structure->get_question_in_slot(6)->slotid;
185         $idbefore = $structure->get_question_in_slot(7)->slotid;
186         $structure->move_slot($idmove, $idbefore, '3');
188         // Having called move, we need to reload $structure.
189         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
190         $this->assert_quiz_layout(array(
191                 $originalslotids[1] => 1,
192                 $originalslotids[3] => 2,
193                 $originalslotids[2] => 2,
194                 $originalslotids[4] => 2,
195                 $originalslotids[5] => 2,
196                 $originalslotids[7] => 3,
197                 $originalslotids[6] => 3,
198                 $originalslotids[8] => 4,
199         ), $structure);
201         // Page changed slot 6 . Pages not reordered.
202         $idmove = $structure->get_question_in_slot(6)->slotid;
203         $idbefore = $structure->get_question_in_slot(5)->slotid;
204         $structure->move_slot($idmove, $idbefore, 2);
206         // Having called move, we need to reload $structure.
207         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
208         $this->assert_quiz_layout(array(
209                 $originalslotids[1] => 1,
210                 $originalslotids[3] => 2,
211                 $originalslotids[2] => 2,
212                 $originalslotids[4] => 2,
213                 $originalslotids[5] => 2,
214                 $originalslotids[7] => 2,
215                 $originalslotids[6] => 3,
216                 $originalslotids[8] => 4,
217         ), $structure);
219         // Slots move 1 > 2. Page changed. Page 2 becomes page 1. Pages reordered.
220         $idmove = $structure->get_question_in_slot(1)->slotid;
221         $idbefore = $structure->get_question_in_slot(2)->slotid;
222         $structure->move_slot($idmove, $idbefore, 2);
224         // Having called move, we need to reload $structure.
225         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
226         $this->assert_quiz_layout(array(
227                 $originalslotids[3] => 1,
228                 $originalslotids[1] => 1,
229                 $originalslotids[2] => 1,
230                 $originalslotids[4] => 1,
231                 $originalslotids[5] => 1,
232                 $originalslotids[7] => 1,
233                 $originalslotids[6] => 2,
234                 $originalslotids[8] => 3,
235         ), $structure);
237         // Slots move 7 > 3. Page changed. Page 3 becomes page 2. Pages reordered.
238         $idmove = $structure->get_question_in_slot(7)->slotid;
239         $idbefore = $structure->get_question_in_slot(2)->slotid;
240         $structure->move_slot($idmove, $idbefore, 1);
242         // Having called move, we need to reload $structure.
243         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
244         $this->assert_quiz_layout(array(
245                 $originalslotids[3] => 1,
246                 $originalslotids[1] => 1,
247                 $originalslotids[6] => 1,
248                 $originalslotids[2] => 1,
249                 $originalslotids[4] => 1,
250                 $originalslotids[5] => 1,
251                 $originalslotids[7] => 1,
252                 $originalslotids[8] => 2,
253         ), $structure);
255         // Slots move 2 > top. No page changes.
256         $idmove = $structure->get_question_in_slot(2)->slotid;
257         $structure->move_slot($idmove, 0, 1);
259         // Having called move, we need to reload $structure.
260         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
261         $this->assert_quiz_layout(array(
262                 $originalslotids[1] => 1,
263                 $originalslotids[3] => 1,
264                 $originalslotids[6] => 1,
265                 $originalslotids[2] => 1,
266                 $originalslotids[4] => 1,
267                 $originalslotids[5] => 1,
268                 $originalslotids[7] => 1,
269                 $originalslotids[8] => 2,
270         ), $structure);
271     }
273     /**
274      * Test removing slots from a quiz.
275      */
276     public function test_quiz_remove_slot() {
277         global $SITE, $DB;
279         $this->resetAfterTest(true);
280         $this->setAdminUser();
282         // Setup a quiz with 1 standard and 1 random question.
283         $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
284         $quiz = $quizgenerator->create_instance(array('course' => $SITE->id, 'questionsperpage' => 3, 'grade' => 100.0));
286         $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
287         $cat = $questiongenerator->create_question_category();
288         $standardq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
290         quiz_add_quiz_question($standardq->id, $quiz);
291         quiz_add_random_questions($quiz, 0, $cat->id, 1, false);
293         // Get the random question.
294         $randomq = $DB->get_record('question', array('qtype' => 'random'));
296         $structure = \mod_quiz\structure::create_for($quiz);
298         // Check that the setup looks right.
299         $this->assertEquals(2, $structure->get_question_count());
300         $this->assertEquals($standardq->id, $structure->get_question_in_slot(1)->questionid);
301         $this->assertEquals($randomq->id, $structure->get_question_in_slot(2)->questionid);
303         // Remove the standard question.
304         $structure->remove_slot($quiz, 1);
306         $alteredstructure = \mod_quiz\structure::create_for($quiz);
308         // Check the new ordering, and that the slot number was updated.
309         $this->assertEquals(1, $alteredstructure->get_question_count());
310         $this->assertEquals($randomq->id, $alteredstructure->get_question_in_slot(1)->questionid);
312         // Check that the ordinary question was not deleted.
313         $this->assertTrue($DB->record_exists('question', array('id' => $standardq->id)));
315         // Remove the random question.
316         $structure->remove_slot($quiz, 1);
317         $alteredstructure = \mod_quiz\structure::create_for($quiz);
319         // Check that new ordering.
320         $this->assertEquals(0, $alteredstructure->get_question_count());
322         // Check that the random question was deleted.
323         $this->assertFalse($DB->record_exists('question', array('id' => $randomq->id)));
324     }
326     /**
327      * Test updating pagebreaks in the quiz.
328      */
329     public function test_update_page_break() {
330         // Create a test quiz with 8 questions.
331         list($quiz, $cm, $course) = $this->prepare_quiz_data();
332         $this->add_eight_questions_to_the_quiz($quiz);
333         $quizobj = new quiz($quiz, $cm, $course);
334         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
336         // Store the original order of slots, so we can assert what has changed.
337         $originalslotids = array();
338         foreach ($structure->get_slots() as $slot) {
339             $originalslotids[$slot->slot] = $slot->id;
340         }
342         // Test removing a page break.
343         $slotid = $structure->get_question_in_slot(2)->slotid;
344         $type = \mod_quiz\repaginate::LINK;
345         $slots = $structure->update_page_break($quiz, $slotid, $type);
347         // Having called update page break, we need to reload $structure.
348         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
349         $this->assert_quiz_layout(array(
350                     $originalslotids[1] => 1,
351                     $originalslotids[2] => 1,
352                     $originalslotids[3] => 1,
353                     $originalslotids[4] => 1,
354                     $originalslotids[5] => 1,
355                     $originalslotids[6] => 1,
356                     $originalslotids[7] => 2,
357                     $originalslotids[8] => 3,
358                 ), $structure);
360         // Test adding a page break.
361         $slotid = $structure->get_question_in_slot(2)->slotid;
362         $type = \mod_quiz\repaginate::UNLINK;
363         $slots = $structure->update_page_break($quiz, $slotid, $type);
365         // Having called update page break, we need to reload $structure.
366         $structure = \mod_quiz\structure::create_for_quiz($quizobj);
367         $this->assert_quiz_layout(array(
368                     $originalslotids[1] => 1,
369                     $originalslotids[2] => 2,
370                     $originalslotids[3] => 2,
371                     $originalslotids[4] => 2,
372                     $originalslotids[5] => 2,
373                     $originalslotids[6] => 2,
374                     $originalslotids[7] => 3,
375                     $originalslotids[8] => 4,
376                 ), $structure);
377     }
379     /**
380      * Populate quiz with eight questions.
381      * @param stdClass $quiz the quiz to add to.
382      */
383     public function add_eight_questions_to_the_quiz($quiz) {
384         // We add 8 numerical questions with this layout:
385         // Slot 1 2 3 4 5 6 7 8
386         // Page 1 2 2 2 2 2 3 4.
388         // Create slots.
389         $pagenumber = 1;
390         $pagenumberdefaults = array(2, 7, 8);
392         // Create a couple of questions.
393         $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
395         $cat = $questiongenerator->create_question_category();
396         for ($i = 0; $i < 8; $i ++) {
397             $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
399             if (in_array($i + 1, $pagenumberdefaults)) {
400                 $pagenumber++;
401             }
402             // Add them to the quiz.
403             quiz_add_quiz_question($numq->id, $quiz, $pagenumber);
404         }
405     }