~ubuntu-branches/ubuntu/trusty/moodle/trusty-proposed

« back to all changes in this revision

Viewing changes to question/engine/tests/questionusage_autosave_test.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2013-07-19 08:52:46 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20130719085246-yebwditc2exoap2r
Tags: 2.5.1-1
* New upstream version: 2.5.1.
  - Fixes security issues:
    CVE-2013-2242 CVE-2013-2243 CVE-2013-2244 CVE-2013-2245
    CVE-2013-2246
* Depend on apache2 instead of obsolete apache2-mpm-prefork.
* Use packaged libphp-phpmailer (closes: #429339), adodb,
  HTMLPurifier, PclZip.
* Update debconf translations, thanks Salvatore Merone, Pietro Tollot,
  Joe Hansen, Yuri Kozlov, Holger Wansing, Américo Monteiro,
  Adriano Rafael Gomes, victory, Michał Kułach.
  (closes: #716972, #716986, #717080, #717108, #717278)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 * This file contains tests for the autosave code in the question_usage class.
 
19
 *
 
20
 * @package    moodlecore
 
21
 * @subpackage questionengine
 
22
 * @copyright  2013 The Open University
 
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
24
 */
 
25
 
 
26
 
 
27
defined('MOODLE_INTERNAL') || die();
 
28
 
 
29
global $CFG;
 
30
require_once(dirname(__FILE__) . '/../lib.php');
 
31
require_once(dirname(__FILE__) . '/helpers.php');
 
32
 
 
33
 
 
34
/**
 
35
 * Unit tests for the autosave parts of the {@link question_usage} class.
 
36
 *
 
37
 * @copyright 2013 The Open University
 
38
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
39
 */
 
40
class question_usage_autosave_test extends qbehaviour_walkthrough_test_base {
 
41
 
 
42
    public function test_autosave_then_display() {
 
43
        $this->resetAfterTest();
 
44
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
45
        $cat = $generator->create_question_category();
 
46
        $question = $generator->create_question('shortanswer', null,
 
47
                array('category' => $cat->id));
 
48
 
 
49
        // Start attempt at a shortanswer question.
 
50
        $q = question_bank::load_question($question->id);
 
51
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
52
 
 
53
        $this->check_current_state(question_state::$todo);
 
54
        $this->check_current_mark(null);
 
55
        $this->check_step_count(1);
 
56
 
 
57
        // Process a response and check the expected result.
 
58
        $this->process_submission(array('answer' => 'first response'));
 
59
 
 
60
        $this->check_current_state(question_state::$complete);
 
61
        $this->check_current_mark(null);
 
62
        $this->check_step_count(2);
 
63
        $this->save_quba();
 
64
 
 
65
        // Now check how that is re-displayed.
 
66
        $this->render();
 
67
        $this->check_output_contains_text_input('answer', 'first response');
 
68
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
69
 
 
70
        // Process an autosave.
 
71
        $this->load_quba();
 
72
        $this->process_autosave(array('answer' => 'second response'));
 
73
        $this->check_current_state(question_state::$complete);
 
74
        $this->check_current_mark(null);
 
75
        $this->check_step_count(3);
 
76
        $this->save_quba();
 
77
 
 
78
        // Now check how that is re-displayed.
 
79
        $this->load_quba();
 
80
        $this->render();
 
81
        $this->check_output_contains_text_input('answer', 'second response');
 
82
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
83
 
 
84
        $this->delete_quba();
 
85
    }
 
86
 
 
87
    public function test_autosave_then_autosave_different_data() {
 
88
        $this->resetAfterTest();
 
89
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
90
        $cat = $generator->create_question_category();
 
91
        $question = $generator->create_question('shortanswer', null,
 
92
                array('category' => $cat->id));
 
93
 
 
94
        // Start attempt at a shortanswer question.
 
95
        $q = question_bank::load_question($question->id);
 
96
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
97
 
 
98
        $this->check_current_state(question_state::$todo);
 
99
        $this->check_current_mark(null);
 
100
        $this->check_step_count(1);
 
101
 
 
102
        // Process a response and check the expected result.
 
103
        $this->process_submission(array('answer' => 'first response'));
 
104
 
 
105
        $this->check_current_state(question_state::$complete);
 
106
        $this->check_current_mark(null);
 
107
        $this->check_step_count(2);
 
108
        $this->save_quba();
 
109
 
 
110
        // Now check how that is re-displayed.
 
111
        $this->render();
 
112
        $this->check_output_contains_text_input('answer', 'first response');
 
113
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
114
 
 
115
        // Process an autosave.
 
116
        $this->load_quba();
 
117
        $this->process_autosave(array('answer' => 'second response'));
 
118
        $this->check_current_state(question_state::$complete);
 
119
        $this->check_current_mark(null);
 
120
        $this->check_step_count(3);
 
121
        $this->save_quba();
 
122
 
 
123
        // Now check how that is re-displayed.
 
124
        $this->load_quba();
 
125
        $this->render();
 
126
        $this->check_output_contains_text_input('answer', 'second response');
 
127
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
128
 
 
129
        // Process a second autosave.
 
130
        $this->load_quba();
 
131
        $this->process_autosave(array('answer' => 'third response'));
 
132
        $this->check_current_state(question_state::$complete);
 
133
        $this->check_current_mark(null);
 
134
        $this->check_step_count(3);
 
135
        $this->save_quba();
 
136
 
 
137
        // Now check how that is re-displayed.
 
138
        $this->load_quba();
 
139
        $this->render();
 
140
        $this->check_output_contains_text_input('answer', 'third response');
 
141
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
142
 
 
143
        $this->delete_quba();
 
144
    }
 
145
 
 
146
    public function test_autosave_then_autosave_same_data() {
 
147
        $this->resetAfterTest();
 
148
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
149
        $cat = $generator->create_question_category();
 
150
        $question = $generator->create_question('shortanswer', null,
 
151
                array('category' => $cat->id));
 
152
 
 
153
        // Start attempt at a shortanswer question.
 
154
        $q = question_bank::load_question($question->id);
 
155
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
156
 
 
157
        $this->check_current_state(question_state::$todo);
 
158
        $this->check_current_mark(null);
 
159
        $this->check_step_count(1);
 
160
 
 
161
        // Process a response and check the expected result.
 
162
        $this->process_submission(array('answer' => 'first response'));
 
163
 
 
164
        $this->check_current_state(question_state::$complete);
 
165
        $this->check_current_mark(null);
 
166
        $this->check_step_count(2);
 
167
        $this->save_quba();
 
168
 
 
169
        // Now check how that is re-displayed.
 
170
        $this->render();
 
171
        $this->check_output_contains_text_input('answer', 'first response');
 
172
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
173
 
 
174
        // Process an autosave.
 
175
        $this->load_quba();
 
176
        $this->process_autosave(array('answer' => 'second response'));
 
177
        $this->check_current_state(question_state::$complete);
 
178
        $this->check_current_mark(null);
 
179
        $this->check_step_count(3);
 
180
        $this->save_quba();
 
181
 
 
182
        // Now check how that is re-displayed.
 
183
        $this->load_quba();
 
184
        $this->render();
 
185
        $this->check_output_contains_text_input('answer', 'second response');
 
186
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
187
 
 
188
        $stepid = $this->quba->get_question_attempt($this->slot)->get_last_step()->get_id();
 
189
 
 
190
        // Process a second autosave.
 
191
        $this->load_quba();
 
192
        $this->process_autosave(array('answer' => 'second response'));
 
193
        $this->check_current_state(question_state::$complete);
 
194
        $this->check_current_mark(null);
 
195
        $this->check_step_count(3);
 
196
        $this->save_quba();
 
197
 
 
198
        // Try to check it is really the same step
 
199
        $newstepid = $this->quba->get_question_attempt($this->slot)->get_last_step()->get_id();
 
200
        $this->assertEquals($stepid, $newstepid);
 
201
 
 
202
        // Now check how that is re-displayed.
 
203
        $this->load_quba();
 
204
        $this->render();
 
205
        $this->check_output_contains_text_input('answer', 'second response');
 
206
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
207
 
 
208
        $this->delete_quba();
 
209
    }
 
210
 
 
211
    public function test_autosave_then_autosave_original_data() {
 
212
        $this->resetAfterTest();
 
213
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
214
        $cat = $generator->create_question_category();
 
215
        $question = $generator->create_question('shortanswer', null,
 
216
                array('category' => $cat->id));
 
217
 
 
218
        // Start attempt at a shortanswer question.
 
219
        $q = question_bank::load_question($question->id);
 
220
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
221
 
 
222
        $this->check_current_state(question_state::$todo);
 
223
        $this->check_current_mark(null);
 
224
        $this->check_step_count(1);
 
225
 
 
226
        // Process a response and check the expected result.
 
227
        $this->process_submission(array('answer' => 'first response'));
 
228
 
 
229
        $this->check_current_state(question_state::$complete);
 
230
        $this->check_current_mark(null);
 
231
        $this->check_step_count(2);
 
232
        $this->save_quba();
 
233
 
 
234
        // Now check how that is re-displayed.
 
235
        $this->render();
 
236
        $this->check_output_contains_text_input('answer', 'first response');
 
237
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
238
 
 
239
        // Process an autosave.
 
240
        $this->load_quba();
 
241
        $this->process_autosave(array('answer' => 'second response'));
 
242
        $this->check_current_state(question_state::$complete);
 
243
        $this->check_current_mark(null);
 
244
        $this->check_step_count(3);
 
245
        $this->save_quba();
 
246
 
 
247
        // Now check how that is re-displayed.
 
248
        $this->load_quba();
 
249
        $this->render();
 
250
        $this->check_output_contains_text_input('answer', 'second response');
 
251
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
252
 
 
253
        // Process a second autosave saving the original response.
 
254
        // This should remove the autosave step.
 
255
        $this->load_quba();
 
256
        $this->process_autosave(array('answer' => 'first response'));
 
257
        $this->check_current_state(question_state::$complete);
 
258
        $this->check_current_mark(null);
 
259
        $this->check_step_count(2);
 
260
        $this->save_quba();
 
261
 
 
262
        // Now check how that is re-displayed.
 
263
        $this->load_quba();
 
264
        $this->render();
 
265
        $this->check_output_contains_text_input('answer', 'first response');
 
266
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
267
 
 
268
        $this->delete_quba();
 
269
    }
 
270
 
 
271
    public function test_autosave_then_real_save() {
 
272
        $this->resetAfterTest();
 
273
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
274
        $cat = $generator->create_question_category();
 
275
        $question = $generator->create_question('shortanswer', null,
 
276
                array('category' => $cat->id));
 
277
 
 
278
        // Start attempt at a shortanswer question.
 
279
        $q = question_bank::load_question($question->id);
 
280
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
281
 
 
282
        $this->check_current_state(question_state::$todo);
 
283
        $this->check_current_mark(null);
 
284
        $this->check_step_count(1);
 
285
 
 
286
        // Process a response and check the expected result.
 
287
        $this->process_submission(array('answer' => 'first response'));
 
288
 
 
289
        $this->check_current_state(question_state::$complete);
 
290
        $this->check_current_mark(null);
 
291
        $this->check_step_count(2);
 
292
        $this->save_quba();
 
293
 
 
294
        // Now check how that is re-displayed.
 
295
        $this->render();
 
296
        $this->check_output_contains_text_input('answer', 'first response');
 
297
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
298
 
 
299
        // Process an autosave.
 
300
        $this->load_quba();
 
301
        $this->process_autosave(array('answer' => 'second response'));
 
302
        $this->check_current_state(question_state::$complete);
 
303
        $this->check_current_mark(null);
 
304
        $this->check_step_count(3);
 
305
        $this->save_quba();
 
306
 
 
307
        // Now check how that is re-displayed.
 
308
        $this->load_quba();
 
309
        $this->render();
 
310
        $this->check_output_contains_text_input('answer', 'second response');
 
311
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
312
 
 
313
        // Now save for real a third response.
 
314
        $this->process_submission(array('answer' => 'third response'));
 
315
 
 
316
        $this->check_current_state(question_state::$complete);
 
317
        $this->check_current_mark(null);
 
318
        $this->check_step_count(3);
 
319
        $this->save_quba();
 
320
 
 
321
        // Now check how that is re-displayed.
 
322
        $this->render();
 
323
        $this->check_output_contains_text_input('answer', 'third response');
 
324
        $this->check_output_contains_hidden_input(':sequencecheck', 3);
 
325
    }
 
326
 
 
327
    public function test_autosave_then_real_save_same() {
 
328
        $this->resetAfterTest();
 
329
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
330
        $cat = $generator->create_question_category();
 
331
        $question = $generator->create_question('shortanswer', null,
 
332
                array('category' => $cat->id));
 
333
 
 
334
        // Start attempt at a shortanswer question.
 
335
        $q = question_bank::load_question($question->id);
 
336
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
337
 
 
338
        $this->check_current_state(question_state::$todo);
 
339
        $this->check_current_mark(null);
 
340
        $this->check_step_count(1);
 
341
 
 
342
        // Process a response and check the expected result.
 
343
        $this->process_submission(array('answer' => 'first response'));
 
344
 
 
345
        $this->check_current_state(question_state::$complete);
 
346
        $this->check_current_mark(null);
 
347
        $this->check_step_count(2);
 
348
        $this->save_quba();
 
349
 
 
350
        // Now check how that is re-displayed.
 
351
        $this->render();
 
352
        $this->check_output_contains_text_input('answer', 'first response');
 
353
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
354
 
 
355
        // Process an autosave.
 
356
        $this->load_quba();
 
357
        $this->process_autosave(array('answer' => 'second response'));
 
358
        $this->check_current_state(question_state::$complete);
 
359
        $this->check_current_mark(null);
 
360
        $this->check_step_count(3);
 
361
        $this->save_quba();
 
362
 
 
363
        // Now check how that is re-displayed.
 
364
        $this->load_quba();
 
365
        $this->render();
 
366
        $this->check_output_contains_text_input('answer', 'second response');
 
367
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
368
 
 
369
        // Now save for real of the same response.
 
370
        $this->process_submission(array('answer' => 'second response'));
 
371
 
 
372
        $this->check_current_state(question_state::$complete);
 
373
        $this->check_current_mark(null);
 
374
        $this->check_step_count(3);
 
375
        $this->save_quba();
 
376
 
 
377
        // Now check how that is re-displayed.
 
378
        $this->render();
 
379
        $this->check_output_contains_text_input('answer', 'second response');
 
380
        $this->check_output_contains_hidden_input(':sequencecheck', 3);
 
381
    }
 
382
 
 
383
    public function test_autosave_then_submit() {
 
384
        $this->resetAfterTest();
 
385
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
386
        $cat = $generator->create_question_category();
 
387
        $question = $generator->create_question('shortanswer', null,
 
388
                array('category' => $cat->id));
 
389
 
 
390
        // Start attempt at a shortanswer question.
 
391
        $q = question_bank::load_question($question->id);
 
392
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
393
 
 
394
        $this->check_current_state(question_state::$todo);
 
395
        $this->check_current_mark(null);
 
396
        $this->check_step_count(1);
 
397
 
 
398
        // Process a response and check the expected result.
 
399
        $this->process_submission(array('answer' => 'first response'));
 
400
 
 
401
        $this->check_current_state(question_state::$complete);
 
402
        $this->check_current_mark(null);
 
403
        $this->check_step_count(2);
 
404
        $this->save_quba();
 
405
 
 
406
        // Now check how that is re-displayed.
 
407
        $this->render();
 
408
        $this->check_output_contains_text_input('answer', 'first response');
 
409
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
410
 
 
411
        // Process an autosave.
 
412
        $this->load_quba();
 
413
        $this->process_autosave(array('answer' => 'second response'));
 
414
        $this->check_current_state(question_state::$complete);
 
415
        $this->check_current_mark(null);
 
416
        $this->check_step_count(3);
 
417
        $this->save_quba();
 
418
 
 
419
        // Now check how that is re-displayed.
 
420
        $this->load_quba();
 
421
        $this->render();
 
422
        $this->check_output_contains_text_input('answer', 'second response');
 
423
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
424
 
 
425
        // Now submit a third response.
 
426
        $this->process_submission(array('answer' => 'third response'));
 
427
        $this->quba->finish_all_questions();
 
428
 
 
429
        $this->check_current_state(question_state::$gradedwrong);
 
430
        $this->check_current_mark(0);
 
431
        $this->check_step_count(4);
 
432
        $this->save_quba();
 
433
 
 
434
        // Now check how that is re-displayed.
 
435
        $this->render();
 
436
        $this->check_output_contains_text_input('answer', 'third response', false);
 
437
        $this->check_output_contains_hidden_input(':sequencecheck', 4);
 
438
    }
 
439
 
 
440
    public function test_autosave_and_save_concurrently() {
 
441
        // This test simulates the following scenario:
 
442
        // 1. Student looking at a page of the quiz, and edits a field then waits.
 
443
        // 2. Autosave starts.
 
444
        // 3. Student immediately clicks Next, which submits the current page.
 
445
        // In this situation, the real submit should beat the autosave, even
 
446
        // thought they happen concurrently. We simulate this by opening a
 
447
        // second db connections.
 
448
        global $DB;
 
449
 
 
450
        // Open second connection
 
451
        $cfg = $DB->export_dbconfig();
 
452
        if (!isset($cfg->dboptions)) {
 
453
            $cfg->dboptions = array();
 
454
        }
 
455
        $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
 
456
        $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
 
457
 
 
458
        // Since we need to commit our transactions in a given order, close the
 
459
        // standard unit test transaction.
 
460
        $this->preventResetByRollback();
 
461
 
 
462
        $this->resetAfterTest();
 
463
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
464
        $cat = $generator->create_question_category();
 
465
        $question = $generator->create_question('shortanswer', null,
 
466
                array('category' => $cat->id));
 
467
 
 
468
        // Start attempt at a shortanswer question.
 
469
        $q = question_bank::load_question($question->id);
 
470
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
471
        $this->save_quba();
 
472
 
 
473
        $this->check_current_state(question_state::$todo);
 
474
        $this->check_current_mark(null);
 
475
        $this->check_step_count(1);
 
476
 
 
477
        // Start to process an autosave on $DB.
 
478
        $transaction = $DB->start_delegated_transaction();
 
479
        $this->load_quba($DB);
 
480
        $this->process_autosave(array('answer' => 'autosaved response'));
 
481
        $this->check_current_state(question_state::$complete);
 
482
        $this->check_current_mark(null);
 
483
        $this->check_step_count(2);
 
484
        $this->save_quba($DB); // Don't commit the transaction yet.
 
485
 
 
486
        // Now process a real submit on $DB2 (using a different response).
 
487
        $transaction2 = $DB2->start_delegated_transaction();
 
488
        $this->load_quba($DB2);
 
489
        $this->process_submission(array('answer' => 'real response'));
 
490
        $this->check_current_state(question_state::$complete);
 
491
        $this->check_current_mark(null);
 
492
        $this->check_step_count(2);
 
493
 
 
494
        // Now commit the first transaction.
 
495
        $transaction->allow_commit();
 
496
 
 
497
        // Now commit the other transaction.
 
498
        $this->save_quba($DB2);
 
499
        $transaction2->allow_commit();
 
500
 
 
501
        // Now re-load and check how that is re-displayed.
 
502
        $this->load_quba();
 
503
        $this->check_current_state(question_state::$complete);
 
504
        $this->check_current_mark(null);
 
505
        $this->check_step_count(2);
 
506
        $this->render();
 
507
        $this->check_output_contains_text_input('answer', 'real response');
 
508
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
509
 
 
510
        $DB2->dispose();
 
511
    }
 
512
 
 
513
    public function test_concurrent_autosaves() {
 
514
        // This test simulates the following scenario:
 
515
        // 1. Student opens  a page of the quiz in two separate browser.
 
516
        // 2. Autosave starts in both at the same time.
 
517
        // In this situation, one autosave will work, and the other one will
 
518
        // get a unique key violation error. This is OK.
 
519
        global $DB;
 
520
 
 
521
        // Open second connection
 
522
        $cfg = $DB->export_dbconfig();
 
523
        if (!isset($cfg->dboptions)) {
 
524
            $cfg->dboptions = array();
 
525
        }
 
526
        $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
 
527
        $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
 
528
 
 
529
        // Since we need to commit our transactions in a given order, close the
 
530
        // standard unit test transaction.
 
531
        $this->preventResetByRollback();
 
532
 
 
533
        $this->resetAfterTest();
 
534
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
535
        $cat = $generator->create_question_category();
 
536
        $question = $generator->create_question('shortanswer', null,
 
537
                array('category' => $cat->id));
 
538
 
 
539
        // Start attempt at a shortanswer question.
 
540
        $q = question_bank::load_question($question->id);
 
541
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
542
        $this->save_quba();
 
543
 
 
544
        $this->check_current_state(question_state::$todo);
 
545
        $this->check_current_mark(null);
 
546
        $this->check_step_count(1);
 
547
 
 
548
        // Start to process an autosave on $DB.
 
549
        $transaction = $DB->start_delegated_transaction();
 
550
        $this->load_quba($DB);
 
551
        $this->process_autosave(array('answer' => 'autosaved response 1'));
 
552
        $this->check_current_state(question_state::$complete);
 
553
        $this->check_current_mark(null);
 
554
        $this->check_step_count(2);
 
555
        $this->save_quba($DB); // Don't commit the transaction yet.
 
556
 
 
557
        // Now process a real submit on $DB2 (using a different response).
 
558
        $transaction2 = $DB2->start_delegated_transaction();
 
559
        $this->load_quba($DB2);
 
560
        $this->process_autosave(array('answer' => 'autosaved response 2'));
 
561
        $this->check_current_state(question_state::$complete);
 
562
        $this->check_current_mark(null);
 
563
        $this->check_step_count(2);
 
564
 
 
565
        // Now commit the first transaction.
 
566
        $transaction->allow_commit();
 
567
 
 
568
        // Now commit the other transaction.
 
569
        $this->setExpectedException('dml_write_exception');
 
570
        $this->save_quba($DB2);
 
571
        $transaction2->allow_commit();
 
572
 
 
573
        // Now re-load and check how that is re-displayed.
 
574
        $this->load_quba();
 
575
        $this->check_current_state(question_state::$complete);
 
576
        $this->check_current_mark(null);
 
577
        $this->check_step_count(2);
 
578
        $this->render();
 
579
        $this->check_output_contains_text_input('answer', 'autosaved response 1');
 
580
        $this->check_output_contains_hidden_input(':sequencecheck', 1);
 
581
 
 
582
        $DB2->dispose();
 
583
    }
 
584
 
 
585
    public function test_autosave_with_wrong_seq_number_ignored() {
 
586
        $this->resetAfterTest();
 
587
        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 
588
        $cat = $generator->create_question_category();
 
589
        $question = $generator->create_question('shortanswer', null,
 
590
                array('category' => $cat->id));
 
591
 
 
592
        // Start attempt at a shortanswer question.
 
593
        $q = question_bank::load_question($question->id);
 
594
        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
 
595
 
 
596
        $this->check_current_state(question_state::$todo);
 
597
        $this->check_current_mark(null);
 
598
        $this->check_step_count(1);
 
599
 
 
600
        // Process a response and check the expected result.
 
601
        $this->process_submission(array('answer' => 'first response'));
 
602
 
 
603
        $this->check_current_state(question_state::$complete);
 
604
        $this->check_current_mark(null);
 
605
        $this->check_step_count(2);
 
606
        $this->save_quba();
 
607
 
 
608
        // Now check how that is re-displayed.
 
609
        $this->render();
 
610
        $this->check_output_contains_text_input('answer', 'first response');
 
611
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
612
 
 
613
        // Process an autosave with a sequence number 1 too small (so from the past).
 
614
        $this->load_quba();
 
615
        $postdata = $this->response_data_to_post(array('answer' => 'obsolete response'));
 
616
        $postdata[$this->quba->get_field_prefix($this->slot) . ':sequencecheck'] = $this->get_question_attempt()->get_sequence_check_count() - 1;
 
617
        $this->quba->process_all_autosaves(null, $postdata);
 
618
        $this->check_current_state(question_state::$complete);
 
619
        $this->check_current_mark(null);
 
620
        $this->check_step_count(2);
 
621
        $this->save_quba();
 
622
 
 
623
        // Now check how that is re-displayed.
 
624
        $this->load_quba();
 
625
        $this->render();
 
626
        $this->check_output_contains_text_input('answer', 'first response');
 
627
        $this->check_output_contains_hidden_input(':sequencecheck', 2);
 
628
 
 
629
        $this->delete_quba();
 
630
    }
 
631
}