MDL-70153 qtype_essay: Add behat test for attachments max size
[moodle.git] / question / engine / tests / datalib_reporting_queries_test.php
CommitLineData
5b0a31bf
TH
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/**
7ad0e2e6 18 * Unit tests for the parts of {@link question_engine_data_mapper} related to reporting.
5b0a31bf 19 *
7ad0e2e6
TH
20 * @package core_question
21 * @category test
22 * @copyright 2013 The Open University
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5b0a31bf
TH
24 */
25
26
27defined('MOODLE_INTERNAL') || die();
28
29global $CFG;
1fcf0ca8
RS
30require_once(__DIR__ . '/../lib.php');
31require_once(__DIR__ . '/helpers.php');
5b0a31bf
TH
32
33
34/**
7ad0e2e6 35 * Unit tests for the parts of {@link question_engine_data_mapper} related to reporting.
5b0a31bf
TH
36 *
37 * @copyright 2013 The Open University
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
7ad0e2e6 40class question_engine_data_mapper_reporting_testcase extends qbehaviour_walkthrough_test_base {
5b0a31bf
TH
41
42 /** @var question_engine_data_mapper */
43 protected $dm;
44
45 /** @var qtype_shortanswer_question */
46 protected $sa;
47
48 /** @var qtype_essay_question */
49 protected $essay;
50
51 /** @var array */
52 protected $usageids = array();
53
54 /** @var qubaid_condition */
55 protected $bothusages;
56
57 /** @var array */
58 protected $allslots = array();
59
60 /**
61 * Test the various methods that load data for reporting.
62 *
63 * Since these methods need an expensive set-up, and then only do read-only
64 * operations on the data, we use a single method to do the set-up, which
65 * calls diffents methods to test each query.
66 */
67 public function test_reporting_queries() {
68 // We create two usages, each with two questions, a short-answer marked
69 // out of 5, and and essay marked out of 10.
70 //
71 // In the first usage, the student answers the short-answer
72 // question correctly, and enters something in the essay.
73 //
74 // In the second useage, the student answers the short-answer question
75 // wrongly, and leaves the essay blank.
76 $this->resetAfterTest();
77 $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
78 $cat = $generator->create_question_category();
79 $this->sa = $generator->create_question('shortanswer', null,
80 array('category' => $cat->id));
81 $this->essay = $generator->create_question('essay', null,
82 array('category' => $cat->id));
83
84 $this->usageids = array();
85
86 // Create the first usage.
87 $q = question_bank::load_question($this->sa->id);
88 $this->start_attempt_at_question($q, 'interactive', 5);
89 $this->allslots[] = $this->slot;
90 $this->process_submission(array('answer' => 'cat'));
91 $this->process_submission(array('answer' => 'frog', '-submit' => 1));
92
93 $q = question_bank::load_question($this->essay->id);
94 $this->start_attempt_at_question($q, 'interactive', 10);
95 $this->allslots[] = $this->slot;
96 $this->process_submission(array('answer' => '<p>The cat sat on the mat.</p>', 'answerformat' => FORMAT_HTML));
97
98 $this->finish();
99 $this->save_quba();
100 $this->usageids[] = $this->quba->get_id();
101
102 // Create the second usage.
103 $this->quba = question_engine::make_questions_usage_by_activity('unit_test',
104 context_system::instance());
105
106 $q = question_bank::load_question($this->sa->id);
107 $this->start_attempt_at_question($q, 'interactive', 5);
108 $this->process_submission(array('answer' => 'fish'));
109
110 $q = question_bank::load_question($this->essay->id);
111 $this->start_attempt_at_question($q, 'interactive', 10);
112
113 $this->finish();
114 $this->save_quba();
115 $this->usageids[] = $this->quba->get_id();
116
117 // Set up some things the tests will need.
118 $this->dm = new question_engine_data_mapper();
119 $this->bothusages = new qubaid_list($this->usageids);
120
121 // Now test the various queries.
7b1b4787
TH
122 $this->dotest_load_questions_usages_latest_steps($this->allslots);
123 $this->dotest_load_questions_usages_latest_steps(null);
124 $this->dotest_load_questions_usages_question_state_summary($this->allslots);
125 $this->dotest_load_questions_usages_question_state_summary(null);
5b0a31bf 126 $this->dotest_load_questions_usages_where_question_in_state();
7b1b4787
TH
127 $this->dotest_load_average_marks($this->allslots);
128 $this->dotest_load_average_marks(null);
5b0a31bf
TH
129 $this->dotest_sum_usage_marks_subquery();
130 $this->dotest_question_attempt_latest_state_view();
131 }
132
7ad0e2e6
TH
133 /**
134 * This test is executed by {@link test_reporting_queries()}.
7b1b4787
TH
135 *
136 * @param array|null $slots list of slots to use in the call.
7ad0e2e6 137 */
7b1b4787
TH
138 protected function dotest_load_questions_usages_latest_steps($slots) {
139 $rawstates = $this->dm->load_questions_usages_latest_steps($this->bothusages, $slots,
5b0a31bf
TH
140 'qa.id AS questionattemptid, qa.questionusageid, qa.slot, ' .
141 'qa.questionid, qa.maxmark, qas.sequencenumber, qas.state');
142
143 $states = array();
144 foreach ($rawstates as $state) {
145 $states[$state->questionusageid][$state->slot] = $state;
146 unset($state->questionattemptid);
147 unset($state->questionusageid);
148 unset($state->slot);
149 }
150
151 $state = $states[$this->usageids[0]][$this->allslots[0]];
152 $this->assertEquals((object) array(
153 'questionid' => $this->sa->id,
87f196af 154 'maxmark' => 5.0,
5b0a31bf
TH
155 'sequencenumber' => 2,
156 'state' => (string) question_state::$gradedright,
157 ), $state);
158
159 $state = $states[$this->usageids[0]][$this->allslots[1]];
160 $this->assertEquals((object) array(
161 'questionid' => $this->essay->id,
87f196af 162 'maxmark' => 10.0,
5b0a31bf
TH
163 'sequencenumber' => 2,
164 'state' => (string) question_state::$needsgrading,
165 ), $state);
166
167 $state = $states[$this->usageids[1]][$this->allslots[0]];
168 $this->assertEquals((object) array(
169 'questionid' => $this->sa->id,
87f196af 170 'maxmark' => 5.0,
5b0a31bf
TH
171 'sequencenumber' => 2,
172 'state' => (string) question_state::$gradedwrong,
173 ), $state);
174
175 $state = $states[$this->usageids[1]][$this->allslots[1]];
176 $this->assertEquals((object) array(
177 'questionid' => $this->essay->id,
87f196af 178 'maxmark' => 10.0,
5b0a31bf
TH
179 'sequencenumber' => 1,
180 'state' => (string) question_state::$gaveup,
181 ), $state);
182 }
183
7ad0e2e6
TH
184 /**
185 * This test is executed by {@link test_reporting_queries()}.
7b1b4787
TH
186 *
187 * @param array|null $slots list of slots to use in the call.
7ad0e2e6 188 */
7b1b4787 189 protected function dotest_load_questions_usages_question_state_summary($slots) {
5b0a31bf 190 $summary = $this->dm->load_questions_usages_question_state_summary(
7b1b4787 191 $this->bothusages, $slots);
5b0a31bf
TH
192
193 $this->assertEquals($summary[$this->allslots[0] . ',' . $this->sa->id],
194 (object) array(
195 'slot' => $this->allslots[0],
196 'questionid' => $this->sa->id,
197 'name' => $this->sa->name,
198 'inprogress' => 0,
199 'needsgrading' => 0,
200 'autograded' => 2,
201 'manuallygraded' => 0,
202 'all' => 2,
203 ));
204 $this->assertEquals($summary[$this->allslots[1] . ',' . $this->essay->id],
205 (object) array(
206 'slot' => $this->allslots[1],
207 'questionid' => $this->essay->id,
208 'name' => $this->essay->name,
209 'inprogress' => 0,
210 'needsgrading' => 1,
211 'autograded' => 1,
212 'manuallygraded' => 0,
213 'all' => 2,
214 ));
215 }
216
7ad0e2e6
TH
217 /**
218 * This test is executed by {@link test_reporting_queries()}.
219 */
5b0a31bf
TH
220 protected function dotest_load_questions_usages_where_question_in_state() {
221 $this->assertEquals(
222 array(array($this->usageids[0], $this->usageids[1]), 2),
223 $this->dm->load_questions_usages_where_question_in_state($this->bothusages,
224 'all', $this->allslots[1], null, 'questionusageid'));
225
226 $this->assertEquals(
227 array(array($this->usageids[0], $this->usageids[1]), 2),
228 $this->dm->load_questions_usages_where_question_in_state($this->bothusages,
229 'autograded', $this->allslots[0], null, 'questionusageid'));
230
231 $this->assertEquals(
232 array(array($this->usageids[0]), 1),
233 $this->dm->load_questions_usages_where_question_in_state($this->bothusages,
234 'needsgrading', $this->allslots[1], null, 'questionusageid'));
235 }
236
7ad0e2e6
TH
237 /**
238 * This test is executed by {@link test_reporting_queries()}.
7b1b4787
TH
239 *
240 * @param array|null $slots list of slots to use in the call.
7ad0e2e6 241 */
7b1b4787
TH
242 protected function dotest_load_average_marks($slots) {
243 $averages = $this->dm->load_average_marks($this->bothusages, $slots);
5b0a31bf
TH
244
245 $this->assertEquals(array(
246 $this->allslots[0] => (object) array(
247 'slot' => $this->allslots[0],
248 'averagefraction' => 0.5,
249 'numaveraged' => 2,
250 ),
251 $this->allslots[1] => (object) array(
252 'slot' => $this->allslots[1],
253 'averagefraction' => 0,
254 'numaveraged' => 1,
255 ),
256 ), $averages);
257 }
258
7ad0e2e6
TH
259 /**
260 * This test is executed by {@link test_reporting_queries()}.
261 */
5b0a31bf
TH
262 protected function dotest_sum_usage_marks_subquery() {
263 global $DB;
264
265 $totals = $DB->get_records_sql_menu("SELECT qu.id, ({$this->dm->sum_usage_marks_subquery('qu.id')}) AS totalmark
266 FROM {question_usages} qu
267 WHERE qu.id IN ({$this->usageids[0]}, {$this->usageids[1]})");
268
269 $this->assertNull($totals[$this->usageids[0]]); // Since a question requires grading.
270
271 $this->assertNotNull($totals[$this->usageids[1]]); // Grrr! PHP null == 0 makes this hard.
272 $this->assertEquals(0, $totals[$this->usageids[1]]);
273 }
274
7ad0e2e6
TH
275 /**
276 * This test is executed by {@link test_reporting_queries()}.
277 */
5b0a31bf
TH
278 protected function dotest_question_attempt_latest_state_view() {
279 global $DB;
280
281 list($inlineview, $viewparams) = $this->dm->question_attempt_latest_state_view(
282 'lateststate', $this->bothusages);
283
284 $rawstates = $DB->get_records_sql("
285 SELECT lateststate.questionattemptid,
286 qu.id AS questionusageid,
287 lateststate.slot,
288 lateststate.questionid,
289 lateststate.maxmark,
290 lateststate.sequencenumber,
291 lateststate.state
292 FROM {question_usages} qu
293 LEFT JOIN $inlineview ON lateststate.questionusageid = qu.id
294 WHERE qu.id IN ({$this->usageids[0]}, {$this->usageids[1]})", $viewparams);
295
296 $states = array();
297 foreach ($rawstates as $state) {
298 $states[$state->questionusageid][$state->slot] = $state;
299 unset($state->questionattemptid);
300 unset($state->questionusageid);
301 unset($state->slot);
302 }
303
304 $state = $states[$this->usageids[0]][$this->allslots[0]];
305 $this->assertEquals((object) array(
306 'questionid' => $this->sa->id,
87f196af 307 'maxmark' => 5.0,
5b0a31bf
TH
308 'sequencenumber' => 2,
309 'state' => (string) question_state::$gradedright,
310 ), $state);
311
312 $state = $states[$this->usageids[0]][$this->allslots[1]];
313 $this->assertEquals((object) array(
314 'questionid' => $this->essay->id,
87f196af 315 'maxmark' => 10.0,
5b0a31bf
TH
316 'sequencenumber' => 2,
317 'state' => (string) question_state::$needsgrading,
318 ), $state);
319
320 $state = $states[$this->usageids[1]][$this->allslots[0]];
321 $this->assertEquals((object) array(
322 'questionid' => $this->sa->id,
87f196af 323 'maxmark' => 5.0,
5b0a31bf
TH
324 'sequencenumber' => 2,
325 'state' => (string) question_state::$gradedwrong,
326 ), $state);
327
328 $state = $states[$this->usageids[1]][$this->allslots[1]];
329 $this->assertEquals((object) array(
330 'questionid' => $this->essay->id,
87f196af 331 'maxmark' => 10.0,
5b0a31bf
TH
332 'sequencenumber' => 1,
333 'state' => (string) question_state::$gaveup,
334 ), $state);
335 }
336}