Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   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  namespace core_question;
  18  
  19  use question_attempt;
  20  use question_bank;
  21  use question_engine;
  22  use question_state;
  23  use question_test_recordset;
  24  use question_usage_null_observer;
  25  use testable_question_engine_unit_of_work;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once (__DIR__ . '/../lib.php');
  31  require_once (__DIR__ . '/helpers.php');
  32  
  33  /**
  34   * Unit tests for loading data into the {@link question_attempt} class.
  35   *
  36   * Action methods like start, process_action and finish are assumed to be
  37   * tested by walkthrough tests in the various behaviours.
  38   *
  39   * @package    core_question
  40   * @category   test
  41   * @copyright  2009 The Open University
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class questionattempt_db_test extends \data_loading_method_test_base {
  45      public function test_load() {
  46          $records = new question_test_recordset(array(
  47              array('questionattemptid', 'contextid', 'questionusageid', 'slot',
  48                                     'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction', 'flagged',
  49                                                                                         'questionsummary', 'rightanswer', 'responsesummary', 'timemodified',
  50                                                                                                                 'attemptstepid', 'sequencenumber', 'state', 'fraction',
  51                                                                                                                                                  'timecreated', 'userid', 'name', 'value'),
  52              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo',              null, 1256233700, 1,       null, null),
  53              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 2, 1, 'complete',          null, 1256233705, 1,   'answer',  '1'),
  54              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 1, '', '', '', 1256233790, 3, 2, 'complete',          null, 1256233710, 1,   'answer',  '0'),
  55              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 4, 3, 'complete',          null, 1256233715, 1,   'answer',  '1'),
  56              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 5, 4, 'gradedright',  1.0000000, 1256233720, 1,  '-finish',  '1'),
  57              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-comment', 'Not good enough!'),
  58              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1,    '-mark',  '1'),
  59              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-maxmark',  '2'),
  60          ));
  61  
  62          $question = \test_question_maker::make_question('truefalse', 'true');
  63          $question->id = -1;
  64  
  65          question_bank::start_unit_test();
  66          question_bank::load_test_question_data($question);
  67          $qa = question_attempt::load_from_records($records, 1, new question_usage_null_observer(), 'deferredfeedback');
  68          question_bank::end_unit_test();
  69  
  70          $this->assertEquals($question->questiontext, $qa->get_question(false)->questiontext);
  71  
  72          $this->assertEquals(6, $qa->get_num_steps());
  73  
  74          $step = $qa->get_step(0);
  75          $this->assertEquals(question_state::$todo, $step->get_state());
  76          $this->assertNull($step->get_fraction());
  77          $this->assertEquals(1256233700, $step->get_timecreated());
  78          $this->assertEquals(1, $step->get_user_id());
  79          $this->assertEquals(array(), $step->get_all_data());
  80  
  81          $step = $qa->get_step(1);
  82          $this->assertEquals(question_state::$complete, $step->get_state());
  83          $this->assertNull($step->get_fraction());
  84          $this->assertEquals(1256233705, $step->get_timecreated());
  85          $this->assertEquals(1, $step->get_user_id());
  86          $this->assertEquals(array('answer' => '1'), $step->get_all_data());
  87  
  88          $step = $qa->get_step(2);
  89          $this->assertEquals(question_state::$complete, $step->get_state());
  90          $this->assertNull($step->get_fraction());
  91          $this->assertEquals(1256233710, $step->get_timecreated());
  92          $this->assertEquals(1, $step->get_user_id());
  93          $this->assertEquals(array('answer' => '0'), $step->get_all_data());
  94  
  95          $step = $qa->get_step(3);
  96          $this->assertEquals(question_state::$complete, $step->get_state());
  97          $this->assertNull($step->get_fraction());
  98          $this->assertEquals(1256233715, $step->get_timecreated());
  99          $this->assertEquals(1, $step->get_user_id());
 100          $this->assertEquals(array('answer' => '1'), $step->get_all_data());
 101  
 102          $step = $qa->get_step(4);
 103          $this->assertEquals(question_state::$gradedright, $step->get_state());
 104          $this->assertEquals(1, $step->get_fraction());
 105          $this->assertEquals(1256233720, $step->get_timecreated());
 106          $this->assertEquals(1, $step->get_user_id());
 107          $this->assertEquals(array('-finish' => '1'), $step->get_all_data());
 108  
 109          $step = $qa->get_step(5);
 110          $this->assertEquals(question_state::$mangrpartial, $step->get_state());
 111          $this->assertEquals(0.5, $step->get_fraction());
 112          $this->assertEquals(1256233790, $step->get_timecreated());
 113          $this->assertEquals(1, $step->get_user_id());
 114          $this->assertEquals(array('-comment' => 'Not good enough!', '-mark' => '1', '-maxmark' => '2'),
 115                  $step->get_all_data());
 116      }
 117  
 118      public function test_load_missing_question() {
 119          $records = new question_test_recordset(array(
 120              array('questionattemptid', 'contextid', 'questionusageid', 'slot',
 121                                     'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction', 'flagged',
 122                                                                                         'questionsummary', 'rightanswer', 'responsesummary', 'timemodified',
 123                                                                                                                 'attemptstepid', 'sequencenumber', 'state', 'fraction',
 124                                                                                                                                                  'timecreated', 'userid', 'name', 'value'),
 125              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo',              null, 1256233700, 1,       null, null),
 126          ));
 127  
 128          question_bank::start_unit_test();
 129          $qa = question_attempt::load_from_records($records, 1, new question_usage_null_observer(), 'deferredfeedback');
 130          question_bank::end_unit_test();
 131  
 132          $missingq = question_bank::get_qtype('missingtype')->make_deleted_instance(-1, 2);
 133          $this->assertEquals($missingq, $qa->get_question(false));
 134  
 135          $this->assertEquals(1, $qa->get_num_steps());
 136  
 137          $step = $qa->get_step(0);
 138          $this->assertEquals(question_state::$todo, $step->get_state());
 139          $this->assertNull($step->get_fraction());
 140          $this->assertEquals(1256233700, $step->get_timecreated());
 141          $this->assertEquals(1, $step->get_user_id());
 142          $this->assertEquals(array(), $step->get_all_data());
 143      }
 144  
 145      public function test_load_with_autosaved_data() {
 146          $records = new question_test_recordset(array(
 147              array('questionattemptid', 'contextid', 'questionusageid', 'slot',
 148                                     'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction', 'flagged',
 149                                                                                         'questionsummary', 'rightanswer', 'responsesummary', 'timemodified',
 150                                                                                                               'attemptstepid', 'sequencenumber', 'state', 'fraction',
 151                                                                                                                                                  'timecreated', 'userid', 'name', 'value'),
 152              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 4, -3, 'complete',          null, 1256233715, 1,   'answer',  '1'),
 153              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 1,  0, 'todo',              null, 1256233700, 1,       null, null),
 154              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 2,  1, 'complete',          null, 1256233705, 1,   'answer',  '1'),
 155              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 1, '', '', '', 1256233790, 3,  2, 'complete',          null, 1256233710, 1,   'answer',  '0'),
 156          ));
 157  
 158          $question = \test_question_maker::make_question('truefalse', 'true');
 159          $question->id = -1;
 160  
 161          question_bank::start_unit_test();
 162          question_bank::load_test_question_data($question);
 163          $qa = question_attempt::load_from_records($records, 1, new question_usage_null_observer(), 'deferredfeedback');
 164          question_bank::end_unit_test();
 165  
 166          $this->assertEquals($question->questiontext, $qa->get_question(false)->questiontext);
 167  
 168          $this->assertEquals(4, $qa->get_num_steps());
 169          $this->assertTrue($qa->has_autosaved_step());
 170  
 171          $step = $qa->get_step(0);
 172          $this->assertEquals(question_state::$todo, $step->get_state());
 173          $this->assertNull($step->get_fraction());
 174          $this->assertEquals(1256233700, $step->get_timecreated());
 175          $this->assertEquals(1, $step->get_user_id());
 176          $this->assertEquals(array(), $step->get_all_data());
 177  
 178          $step = $qa->get_step(1);
 179          $this->assertEquals(question_state::$complete, $step->get_state());
 180          $this->assertNull($step->get_fraction());
 181          $this->assertEquals(1256233705, $step->get_timecreated());
 182          $this->assertEquals(1, $step->get_user_id());
 183          $this->assertEquals(array('answer' => '1'), $step->get_all_data());
 184  
 185          $step = $qa->get_step(2);
 186          $this->assertEquals(question_state::$complete, $step->get_state());
 187          $this->assertNull($step->get_fraction());
 188          $this->assertEquals(1256233710, $step->get_timecreated());
 189          $this->assertEquals(1, $step->get_user_id());
 190          $this->assertEquals(array('answer' => '0'), $step->get_all_data());
 191  
 192          $step = $qa->get_step(3);
 193          $this->assertEquals(question_state::$complete, $step->get_state());
 194          $this->assertNull($step->get_fraction());
 195          $this->assertEquals(1256233715, $step->get_timecreated());
 196          $this->assertEquals(1, $step->get_user_id());
 197          $this->assertEquals(array('answer' => '1'), $step->get_all_data());
 198      }
 199  
 200      public function test_load_with_unnecessary_autosaved_data() {
 201          // The point here is that the somehow (probably due to two things
 202          // happening concurrently, we have autosaved data in the database that
 203          // has already been superceded by real data, so it should be ignored.
 204          // There is also a second lot of redundant data to delete.
 205          $records = new question_test_recordset(array(
 206              array('questionattemptid', 'contextid', 'questionusageid', 'slot',
 207                                     'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction', 'flagged',
 208                                                                                         'questionsummary', 'rightanswer', 'responsesummary', 'timemodified',
 209                                                                                                               'attemptstepid', 'sequencenumber', 'state', 'fraction',
 210                                                                                                                                                  'timecreated', 'userid', 'name', 'value'),
 211              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 5, -2, 'complete',          null, 1256233715, 1,   'answer',  '0'),
 212              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 4, -1, 'complete',          null, 1256233715, 1,   'answer',  '0'),
 213              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 1,  0, 'todo',              null, 1256233700, 1,       null, null),
 214              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 0, '', '', '', 1256233790, 2,  1, 'complete',          null, 1256233705, 1,   'answer',  '1'),
 215              array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1.0000000, 1, '', '', '', 1256233790, 3,  2, 'complete',          null, 1256233710, 1,   'answer',  '0'),
 216          ));
 217  
 218          $question = \test_question_maker::make_question('truefalse', 'true');
 219          $question->id = -1;
 220  
 221          question_bank::start_unit_test();
 222          question_bank::load_test_question_data($question);
 223          $observer = new testable_question_engine_unit_of_work(
 224                  question_engine::make_questions_usage_by_activity('unit_test', \context_system::instance()));
 225          $qa = question_attempt::load_from_records($records, 1, $observer, 'deferredfeedback');
 226          question_bank::end_unit_test();
 227  
 228          $this->assertEquals($question->questiontext, $qa->get_question(false)->questiontext);
 229  
 230          $this->assertEquals(3, $qa->get_num_steps());
 231          $this->assertFalse($qa->has_autosaved_step());
 232  
 233          $step = $qa->get_step(0);
 234          $this->assertEquals(question_state::$todo, $step->get_state());
 235          $this->assertNull($step->get_fraction());
 236          $this->assertEquals(1256233700, $step->get_timecreated());
 237          $this->assertEquals(1, $step->get_user_id());
 238          $this->assertEquals(array(), $step->get_all_data());
 239  
 240          $step = $qa->get_step(1);
 241          $this->assertEquals(question_state::$complete, $step->get_state());
 242          $this->assertNull($step->get_fraction());
 243          $this->assertEquals(1256233705, $step->get_timecreated());
 244          $this->assertEquals(1, $step->get_user_id());
 245          $this->assertEquals(array('answer' => '1'), $step->get_all_data());
 246  
 247          $step = $qa->get_step(2);
 248          $this->assertEquals(question_state::$complete, $step->get_state());
 249          $this->assertNull($step->get_fraction());
 250          $this->assertEquals(1256233710, $step->get_timecreated());
 251          $this->assertEquals(1, $step->get_user_id());
 252          $this->assertEquals(array('answer' => '0'), $step->get_all_data());
 253  
 254          $this->assertEquals(2, count($observer->get_steps_deleted()));
 255      }
 256  }