6434ba83b82a99ac764e25fca229661c9d01e356
[moodle.git] / question / format / examview / tests / examviewformat_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  * Unit tests for the Moodle Examview format.
19  *
20  * @package    qformat_examview
21  * @copyright  2012 jean-Michel Vedrine
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/questionlib.php');
30 require_once($CFG->dirroot . '/question/format.php');
31 require_once($CFG->dirroot . '/question/format/examview/format.php');
32 require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
35 /**
36  * Unit tests for the examview question import format.
37  *
38  * @copyright  2012 Jean-Michel Vedrine
39  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  */
41 class qformat_examview_test extends question_testcase {
43     public function make_test_xml() {
44         $xml = "<?xml version='1.0' encoding='utf-8' standalone='yes'?>
45 <examview type='test' platform='Windows' app-version='4.0.2'>
46     <header>
47         <title>Moodle Example</title>
48         <version>A</version>
49     </header>
50     <font-table>
51         <font-entry number='1'>
52             <charset>ansi</charset>
53             <name>Times New Roman</name>
54             <pitch>variable</pitch>
55             <family>roman</family>
56         </font-entry>
57     </font-table>
58     <preferences>
59         <show>
60             <show-answer value='yes'/>
61             <show-difficulty value='yes'/>
62             <show-reference value='yes'/>
63             <show-text-objective value='yes'/>
64             <show-state-objective value='yes'/>
65             <show-topic value='yes'/>
66             <show-keywords value='yes'/>
67             <show-misc value='yes'/>
68             <show-notes value='yes'/>
69             <show-rationale value='yes'/>
70         </show>
71         <leave-answer-space>
72             <tf value='yes'/>
73             <mtf value='yes'/>
74             <mc value='yes'/>
75             <yn value='yes'/>
76             <nr value='no'/>
77             <co value='no'/>
78             <ma value='yes'/>
79             <sa value='no'/>
80             <pr value='no'/>
81             <es value='no'/>
82             <ca value='no'/>
83             <ot value='no'/>
84         </leave-answer-space>
85         <question-type-order value='tf,mtf,mc,yn,nr,co,ma,sa,pr,es,ca,ot'/>
86         <section-page-break value='no'/>
87         <bi-display-mode value='mc'/>
88         <tf-show-choices value='no'/>
89         <mc-conserve-paper value='no'/>
90         <mc-choice-sequence value='abcde'/>
91         <show-answer-lines value='no'/>
92         <question-numbering value='continuous'/>
93         <answer-style template='a.' style='none'/>
94         <number-style template='1.' style='none'/>
95         <max-question-id value='9'/>
96         <max-narrative-id value='0'/>
97         <max-group-id value='1'/>
98         <default-font target='title'>
99             <number>1</number>
100             <size>13</size>
101             <style>bold</style>
102             <text-rgb>#000000</text-rgb>
103         </default-font>
104         <default-font target='sectiontitle'>
105             <number>1</number>
106             <size>11</size>
107             <style>bold</style>
108             <text-rgb>#000000</text-rgb>
109         </default-font>
110         <default-font target='questionnumber'>
111             <number>1</number>
112             <size>11</size>
113             <text-rgb>#000000</text-rgb>
114         </default-font>
115         <default-font target='answerchoice'>
116             <number>1</number>
117             <size>11</size>
118             <text-rgb>#000000</text-rgb>
119         </default-font>
120         <default-font target='newquestiondefault'>
121             <number>1</number>
122             <size>11</size>
123             <text-rgb>#000000</text-rgb>
124         </default-font>
125     </preferences>
126     <test-header page='first'><para tabs='R10440'><b>Name: ________________________  Class: ___________________  Date: __________    ID: <field field-type='version'/></b></para></test-header>
127     <test-header page='subsequent'><para tabs='R10440'><b>Name: ________________________    ID: <field field-type='version'/></b></para></test-header>
128     <test-footer page='first'><para justify='center'><field field-type='pageNumber'/>
129 </para></test-footer>
130     <test-footer page='subsequent'><para justify='center'><field field-type='pageNumber'/>
131 </para></test-footer>
132     <instruction type='tf'><b>True/False</b><font size='10'>
133 </font><i>Indicate whether the sentence or statement is true or false.</i></instruction>
134     <instruction type='mtf'><b>Modified True/False</b><font size='10'>
135 </font><i>Indicate whether the sentence or statement is true or false.  If false, change the identified word or phrase to make the sentence or statement true.</i></instruction>
136     <instruction type='mc'><b>Multiple Choice</b><font size='10'>
137 </font><i>Identify the letter of the choice that best completes the statement or answers the question.</i></instruction>
138     <instruction type='yn'><b>Yes/No</b><font size='10'>
139 </font><i>Indicate whether you agree with the sentence or statement.</i></instruction>
140     <instruction type='nr'><b>Numeric Response</b></instruction>
141     <instruction type='co'><b>Completion</b><font size='10'>
142 </font><i>Complete each sentence or statement.</i></instruction>
143     <instruction type='ma'><b>Matching</b></instruction>
144     <instruction type='sa'><b>Short Answer</b></instruction>
145     <instruction type='pr'><b>Problem</b></instruction>
146     <instruction type='es'><b>Essay</b></instruction>
147     <instruction type='ca'><b>Case</b></instruction>
148     <instruction type='ot'><b>Other</b></instruction>
149     <question type='tf' question-id='1' bank-id='0'>
150         <text>42 is the Absolute Answer to everything.</text>
151         <rationale>42 is the Ultimate Answer.</rationale>
152         <answer>F</answer>
153     </question>
154     <question type='mc' question-id='2' bank-id='0'>
155         <text><font size='10'>What's between orange and green in the spectrum?</font></text>
156         <choices columns='2'>
157             <choice-a><font size='10'>red</font></choice-a>
158             <choice-b><font size='10'>yellow</font></choice-b>
159             <choice-c><font size='10'>blue</font></choice-c>
160         </choices>
161         <answer>B</answer>
162     </question>
163     <question type='nr' question-id='3' bank-id='0'>
164         <text>This is a numeric response question.  How much is 12 * 2?</text>
165         <answer>24</answer>
166         <info>
167             <answer-space>-1</answer-space>
168         </info>
169     </question>
170     <matching-group group-id='1' bank-id='0' name='Matching 1'>
171         <text>Classify the animals.</text>
172         <choices columns='2'>
173             <choice-a>insect</choice-a>
174             <choice-b>amphibian</choice-b>
175             <choice-c>mammal</choice-c>
176         </choices>
177     </matching-group>
178     <question type='ma' question-id='6' bank-id='0' group='Matching 1'>
179         <text>cat</text>
180         <answer>C</answer>
181     </question>
182     <question type='ma' question-id='7' bank-id='0' group='Matching 1'>
183         <text>frog</text>
184         <answer>B</answer>
185     </question>
186     <question type='ma' question-id='8' bank-id='0' group='Matching 1'>
187         <text>newt</text>
188         <answer>B</answer>
189     </question>
190     <question type='sa' question-id='5' bank-id='0'>
191         <text>Name an amphibian: __________.</text>
192         <answer>frog</answer>
193         <info>
194             <answer-space>-1</answer-space>
195         </info>
196     </question>
197     <question type='es' question-id='4' bank-id='0'>
198         <text>How are you?</text>
199         <answer>Examview answer for essay questions will be imported as informations for graders.</answer>
200         <info>
201             <answer-space>-1</answer-space>
202         </info>
203     </question>
204 </examview>";
205         return array(0=>$xml);
206     }
208     public function test_import_truefalse() {
210         $xml = $this->make_test_xml();
211         $questions = array();
213         $importer = new qformat_examview();
214         $questions = $importer->readquestions($xml);
215         $q = $questions[0];
217         $expectedq = new stdClass();
218         $expectedq->qtype = 'truefalse';
219         $expectedq->name = '42 is the Absolute Answer to everything.';
220         $expectedq->questiontext = "42 is the Absolute Answer to everything.";
221         $expectedq->questiontextformat = FORMAT_HTML;
222         $expectedq->generalfeedback = '';
223         $expectedq->generalfeedbackformat = FORMAT_MOODLE;
224         $expectedq->defaultmark = 1;
225         $expectedq->length = 1;
226         $expectedq->correctanswer = 0;
227         $expectedq->feedbacktrue = array(
228                 'text' => get_string('incorrect', 'question'),
229                 'format' => FORMAT_HTML,
230                 'files' => array(),
231             );
232         $expectedq->feedbackfalse = array(
233                 'text' => get_string('correct', 'question'),
234                 'format' => FORMAT_HTML,
235                 'files' => array(),
236             );
238         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
239     }
240     public function test_import_multichoice_single() {
241         $xml = $this->make_test_xml();
242         $questions = array();
244         $importer = new qformat_examview();
245         $questions = $importer->readquestions($xml);
246         $q = $questions[1];
248         $expectedq = new stdClass();
249         $expectedq->qtype = 'multichoice';
250         $expectedq->single = 1;
251         $expectedq->name = "What's between orange and green in the spectrum?";
252         $expectedq->questiontext = "What's between orange and green in the spectrum?";
253         $expectedq->questiontextformat = FORMAT_HTML;
254         $expectedq->correctfeedback = array('text' => '',
255                 'format' => FORMAT_HTML, 'files' => array());
256         $expectedq->partiallycorrectfeedback = array('text' => '',
257                 'format' => FORMAT_HTML, 'files' => array());
258         $expectedq->incorrectfeedback = array('text' => '',
259                 'format' => FORMAT_HTML, 'files' => array());
260         $expectedq->generalfeedback = '';
261         $expectedq->generalfeedbackformat = FORMAT_MOODLE;
262         $expectedq->defaultmark = 1;
263         $expectedq->length = 1;
264         $expectedq->penalty = 0.3333333;
265         $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers');
266         $expectedq->answer = array(
267                 0 => array(
268                     'text' => 'red',
269                     'format' => FORMAT_HTML,
270                     'files' => array(),
271                 ),
272                 1 => array(
273                     'text' => 'yellow',
274                     'format' => FORMAT_HTML,
275                     'files' => array(),
276                 ),
277                 2 => array(
278                     'text' => 'blue',
279                     'format' => FORMAT_HTML,
280                     'files' => array(),
281                 )
282             );
283         $expectedq->fraction = array(0, 1, 0);
284         $expectedq->feedback = array(
285                 0 => array(
286                     'text' => get_string('incorrect', 'question'),
287                     'format' => FORMAT_HTML,
288                     'files' => array(),
289                 ),
290                 1 => array(
291                     'text' => get_string('correct', 'question'),
292                     'format' => FORMAT_HTML,
293                     'files' => array(),
294                 ),
295                 2 => array(
296                     'text' => get_string('incorrect', 'question'),
297                     'format' => FORMAT_HTML,
298                     'files' => array(),
299                 )
300             );
302         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
303     }
305     public function test_import_numerical() {
307         $xml = $this->make_test_xml();
308         $questions = array();
310         $importer = new qformat_examview();
311         $questions = $importer->readquestions($xml);
312         $q = $questions[2];
314         $expectedq = new stdClass();
315         $expectedq->qtype = 'numerical';
316         $expectedq->name = 'This is a numeric response question.  How much is 12 * 2?';
317         $expectedq->questiontext = 'This is a numeric response question.  How much is 12 * 2?';
318         $expectedq->questiontextformat = FORMAT_HTML;
319         $expectedq->generalfeedback = '';
320         $expectedq->generalfeedbackformat = FORMAT_MOODLE;
321         $expectedq->defaultmark = 1;
322         $expectedq->length = 1;
323         $expectedq->penalty = 0.3333333;
324         $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers');
325         $expectedq->answer = array('24');
326         $expectedq->fraction = array(1);
327         $expectedq->feedback = array(
328                 0 => array(
329                     'text' => get_string('correct', 'question'),
330                     'format' => FORMAT_HTML,
331                     'files' => array(),
332                 ),
333             );
335         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
336     }
340     public function test_import_fill_in_the_blank() {
342         $xml = $this->make_test_xml();
343         $questions = array();
345         $importer = new qformat_examview();
346         $questions = $importer->readquestions($xml);
347         $q = $questions[3];
349         $expectedq = new stdClass();
350         $expectedq->qtype = 'shortanswer';
351         $expectedq->name = 'Name an amphibian: __________.';
352         $expectedq->questiontext = 'Name an amphibian: __________.';
353         $expectedq->questiontextformat = FORMAT_HTML;
354         $expectedq->generalfeedback = '';
355         $expectedq->generalfeedbackformat = FORMAT_MOODLE;
356         $expectedq->defaultmark = 1;
357         $expectedq->length = 1;
358         $expectedq->usecase = 0;
359         $expectedq->answer = array('frog', '*');
360         $expectedq->fraction = array(1, 0);
361         $expectedq->feedback = array(
362                 0 => array(
363                     'text' => get_string('correct', 'question'),
364                     'format' => FORMAT_HTML,
365                     'files' => array(),
366                 ),
367                 1 => array(
368                     'text' => get_string('incorrect', 'question'),
369                     'format' => FORMAT_HTML,
370                     'files' => array(),
371                 )
372             );
374         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
375     }
377     public function test_import_essay() {
379         $xml = $this->make_test_xml();
380         $questions = array();
382         $importer = new qformat_examview();
383         $questions = $importer->readquestions($xml);
384         $q = $questions[4];
386         $expectedq = new stdClass();
387         $expectedq->qtype = 'essay';
388         $expectedq->name = 'How are you?';
389         $expectedq->questiontext = 'How are you?';
390         $expectedq->questiontextformat = FORMAT_HTML;
391         $expectedq->generalfeedback = '';
392         $expectedq->generalfeedbackformat = FORMAT_MOODLE;
393         $expectedq->defaultmark = 1;
394         $expectedq->length = 1;
395         $expectedq->responseformat = 'editor';
396         $expectedq->responsefieldlines = 15;
397         $expectedq->attachments = 0;
398         $expectedq->graderinfo = array(
399                 'text' => 'Examview answer for essay questions will be imported as informations for graders.',
400                 'format' => FORMAT_HTML,
401                 'files' => array());
403         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
404     }
406     // Due to the way matching questions are parsed,
407     // the test for matching questions is somewhat different.
408     // First we test the parse_matching_groups method alone.
409     // Then we test the whole process wich involve parse_matching_groups,
410     // parse_ma and process_matches methods.
411     public function test_parse_matching_groups() {
412         $lines = $this->make_test_xml();
414         $importer = new qformat_examview();
415         $text = implode($lines, ' ');
417         $xml = xmlize($text, 0);
418         $importer->parse_matching_groups($xml['examview']['#']['matching-group']);
419         $matching = $importer->matching_questions;
420         $group = new stdClass();
421         $group->questiontext = 'Classify the animals.';
422         $group->subchoices = array('A' => 'insect', 'B' => 'amphibian', 'C' =>'mammal');
423             $group->subquestions = array();
424             $group->subanswers = array();
425         $expectedmatching = array( 'Matching 1' => $group);
427         $this->assertEquals($matching, $expectedmatching);
428     }
430     public function test_import_match() {
432         $xml = $this->make_test_xml();
433         $questions = array();
435         $importer = new qformat_examview();
436         $questions = $importer->readquestions($xml);
437         $q = $questions[5];
439         $expectedq = new stdClass();
440         $expectedq->qtype = 'match';
441         $expectedq->name = 'Classify the animals.';
442         $expectedq->questiontext = 'Classify the animals.';
443         $expectedq->questiontextformat = FORMAT_HTML;
444         $expectedq->correctfeedback = array('text' => '',
445                 'format' => FORMAT_HTML, 'files' => array());
446         $expectedq->partiallycorrectfeedback = array('text' => '',
447                 'format' => FORMAT_HTML, 'files' => array());
448         $expectedq->incorrectfeedback = array('text' => '',
449                 'format' => FORMAT_HTML, 'files' => array());
450         $expectedq->generalfeedback = '';
451         $expectedq->generalfeedbackformat = FORMAT_MOODLE;
452         $expectedq->defaultmark = 1;
453         $expectedq->length = 1;
454         $expectedq->penalty = 0.3333333;
455         $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers');
456         $expectedq->subquestions = array(
457             array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
458             array('text' => 'frog', 'format' => FORMAT_HTML, 'files' => array()),
459             array('text' => 'newt', 'format' => FORMAT_HTML, 'files' => array()),
460             array('text' => 'cat', 'format' => FORMAT_HTML, 'files' => array()));
461         $expectedq->subanswers = array('insect', 'amphibian', 'amphibian', 'mammal');
463         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
464     }