Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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 that walks a question through the interactive
  19   * behaviour.
  20   *
  21   * @package    qbehaviour_interactive
  22   * @copyright  2009 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 (__DIR__ . '/../../../engine/lib.php');
  31  require_once (__DIR__ . '/../../../engine/tests/helpers.php');
  32  
  33  
  34  /**
  35   * Unit tests for the interactive behaviour.
  36   *
  37   * @copyright  2009 The Open University
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_test_base {
  41  
  42      public function test_interactive_feedback_multichoice_right() {
  43  
  44          // Create a multichoice single question.
  45          $mc = test_question_maker::make_a_multichoice_single_question();
  46          $mc->hints = array(
  47              new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, false),
  48              new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true),
  49          );
  50          $this->start_attempt_at_question($mc, 'interactive');
  51  
  52          $rightindex = $this->get_mc_right_answer_index($mc);
  53          $wrongindex = ($rightindex + 1) % 3;
  54  
  55          // Check the initial state.
  56          $this->check_current_state(question_state::$todo);
  57          $this->check_current_mark(null);
  58          $this->check_current_output(
  59                  $this->get_contains_marked_out_of_summary(),
  60                  $this->get_contains_question_text_expectation($mc),
  61                  $this->get_contains_mc_radio_expectation(0, true, false),
  62                  $this->get_contains_mc_radio_expectation(1, true, false),
  63                  $this->get_contains_mc_radio_expectation(2, true, false),
  64                  $this->get_contains_submit_button_expectation(true),
  65                  $this->get_does_not_contain_feedback_expectation(),
  66                  $this->get_tries_remaining_expectation(3),
  67                  $this->get_no_hint_visible_expectation());
  68  
  69          // Save the wrong answer.
  70          $this->process_submission(array('answer' => $wrongindex));
  71  
  72          // Verify.
  73          $this->check_current_state(question_state::$todo);
  74          $this->check_current_mark(null);
  75          $this->check_current_output(
  76                  $this->get_contains_marked_out_of_summary(),
  77                  $this->get_contains_mc_radio_expectation($wrongindex, true, true),
  78                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
  79                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
  80                  $this->get_contains_submit_button_expectation(true),
  81                  $this->get_does_not_contain_correctness_expectation(),
  82                  $this->get_does_not_contain_feedback_expectation(),
  83                  $this->get_tries_remaining_expectation(3),
  84                  $this->get_no_hint_visible_expectation());
  85  
  86          // Submit the wrong answer.
  87          $this->process_submission(array('answer' => $wrongindex, '-submit' => 1));
  88  
  89          // Verify.
  90          $this->check_current_state(question_state::$todo);
  91          $this->check_current_mark(null);
  92          $this->check_current_output(
  93                  $this->get_contains_marked_out_of_summary(),
  94                  $this->get_contains_mc_radio_expectation($wrongindex, false, true),
  95                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false),
  96                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false),
  97                  $this->get_does_not_contain_submit_button_expectation(),
  98                  $this->get_contains_try_again_button_expectation(true),
  99                  $this->get_does_not_contain_correctness_expectation(),
 100                  new question_pattern_expectation('/Tries remaining: 2/'),
 101                  $this->get_contains_hint_expectation('This is the first hint'));
 102  
 103          // Check that, if we review in this state, the try again button is disabled.
 104          $displayoptions = new question_display_options();
 105          $displayoptions->readonly = true;
 106          $html = $this->quba->render_question($this->slot, $displayoptions);
 107          $this->assert($this->get_contains_try_again_button_expectation(false), $html);
 108  
 109          // Do try again.
 110          $this->process_submission(array('-tryagain' => 1));
 111  
 112          // Verify.
 113          $this->check_current_state(question_state::$todo);
 114          $this->check_current_mark(null);
 115          $this->check_current_output(
 116                  $this->get_contains_marked_out_of_summary(),
 117                  $this->get_contains_mc_radio_expectation($wrongindex, true, true),
 118                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
 119                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
 120                  $this->get_contains_submit_button_expectation(true),
 121                  $this->get_does_not_contain_correctness_expectation(),
 122                  $this->get_does_not_contain_feedback_expectation(),
 123                  $this->get_tries_remaining_expectation(2),
 124                  $this->get_no_hint_visible_expectation());
 125  
 126          // Submit the right answer.
 127          $this->process_submission(array('answer' => $rightindex, '-submit' => 1));
 128  
 129          // Verify.
 130          $this->check_current_state(question_state::$gradedright);
 131          $this->check_current_mark(0.6666667);
 132          $this->check_current_output(
 133                  $this->get_contains_mark_summary(0.6666667),
 134                  $this->get_contains_mc_radio_expectation($rightindex, false, true),
 135                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false),
 136                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false),
 137                  $this->get_does_not_contain_submit_button_expectation(),
 138                  $this->get_contains_correct_expectation(),
 139                  $this->get_no_hint_visible_expectation());
 140  
 141          // Finish the attempt - should not need to add a new state.
 142          $numsteps = $this->get_step_count();
 143          $this->quba->finish_all_questions();
 144  
 145          // Verify.
 146          $this->assertEquals($numsteps, $this->get_step_count());
 147          $this->check_current_state(question_state::$gradedright);
 148          $this->check_current_mark(0.6666667);
 149          $this->check_current_output(
 150                  $this->get_contains_mark_summary(0.6666667),
 151                  $this->get_contains_mc_radio_expectation($rightindex, false, true),
 152                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false),
 153                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false),
 154                  $this->get_contains_correct_expectation(),
 155                  $this->get_no_hint_visible_expectation());
 156  
 157          // Process a manual comment.
 158          $this->manual_grade('Not good enough!', 0.5, FORMAT_HTML);
 159  
 160          // Verify.
 161          $this->check_current_state(question_state::$mangrpartial);
 162          $this->check_current_mark(0.5);
 163          $this->check_current_output(
 164                  $this->get_contains_mark_summary(0.5),
 165                  $this->get_contains_partcorrect_expectation(),
 166                  new question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
 167  
 168          // Check regrading does not mess anything up.
 169          $this->quba->regrade_all_questions();
 170  
 171          // Verify.
 172          $this->check_current_state(question_state::$mangrpartial);
 173          $this->check_current_mark(0.5);
 174          $this->check_current_output(
 175                  $this->get_contains_mark_summary(0.5),
 176                  $this->get_contains_partcorrect_expectation());
 177  
 178          $autogradedstep = $this->get_step($this->get_step_count() - 2);
 179          $this->assertEqualsWithDelta($autogradedstep->get_fraction(), 0.6666667, 0.0000001);
 180      }
 181  
 182      public function test_interactive_finish_when_try_again_showing() {
 183  
 184          // Create a multichoice single question.
 185          $mc = test_question_maker::make_a_multichoice_single_question();
 186          $mc->showstandardinstruction = true;
 187          $mc->hints = array(
 188              new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, false),
 189          );
 190          $this->start_attempt_at_question($mc, 'interactive');
 191  
 192          $rightindex = $this->get_mc_right_answer_index($mc);
 193          $wrongindex = ($rightindex + 1) % 3;
 194  
 195          // Check the initial state.
 196          $this->check_current_state(question_state::$todo);
 197          $this->check_current_mark(null);
 198          $this->check_current_output(
 199                  $this->get_contains_marked_out_of_summary(),
 200                  $this->get_contains_question_text_expectation($mc),
 201                  $this->get_contains_mc_radio_expectation(0, true, false),
 202                  $this->get_contains_mc_radio_expectation(1, true, false),
 203                  $this->get_contains_mc_radio_expectation(2, true, false),
 204                  $this->get_contains_submit_button_expectation(true),
 205                  $this->get_does_not_contain_feedback_expectation(),
 206                  $this->get_tries_remaining_expectation(2),
 207                  $this->get_no_hint_visible_expectation(),
 208                  new question_pattern_expectation('/' .
 209                          preg_quote(get_string('selectone', 'qtype_multichoice'), '/') . '/'));
 210  
 211          // Submit the wrong answer.
 212          $this->process_submission(array('answer' => $wrongindex, '-submit' => 1));
 213  
 214          // Verify.
 215          $this->check_current_state(question_state::$todo);
 216          $this->check_current_mark(null);
 217          $this->check_current_output(
 218                  $this->get_contains_marked_out_of_summary(),
 219                  $this->get_contains_mc_radio_expectation($wrongindex, false, true),
 220                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false),
 221                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false),
 222                  $this->get_does_not_contain_submit_button_expectation(),
 223                  $this->get_contains_try_again_button_expectation(true),
 224                  $this->get_does_not_contain_correctness_expectation(),
 225                  new question_pattern_expectation('/Tries remaining: 1/'),
 226                  $this->get_contains_hint_expectation('This is the first hint'));
 227  
 228          // Finish the attempt.
 229          $this->quba->finish_all_questions();
 230  
 231          // Verify.
 232          $this->check_current_state(question_state::$gradedwrong);
 233          $this->check_current_mark(0);
 234          $this->check_current_output(
 235                  $this->get_contains_mark_summary(0),
 236                  $this->get_contains_mc_radio_expectation($wrongindex, false, true),
 237                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false),
 238                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false),
 239                  $this->get_contains_incorrect_expectation(),
 240                  $this->get_no_hint_visible_expectation());
 241      }
 242  
 243      public function test_interactive_shortanswer_try_to_submit_blank() {
 244  
 245          // Create a short answer question.
 246          $sa = test_question_maker::make_question('shortanswer');
 247          $sa->hints = array(
 248              new question_hint(0, 'This is the first hint.', FORMAT_HTML),
 249              new question_hint(0, 'This is the second hint.', FORMAT_HTML),
 250          );
 251          $this->start_attempt_at_question($sa, 'interactive');
 252  
 253          // Check the initial state.
 254          $this->check_current_state(question_state::$todo);
 255          $this->check_current_mark(null);
 256          $this->check_current_output(
 257                  $this->get_contains_marked_out_of_summary(),
 258                  $this->get_contains_submit_button_expectation(true),
 259                  $this->get_does_not_contain_feedback_expectation(),
 260                  $this->get_does_not_contain_validation_error_expectation(),
 261                  $this->get_does_not_contain_try_again_button_expectation(),
 262                  $this->get_no_hint_visible_expectation());
 263  
 264          // Submit blank.
 265          $this->process_submission(array('-submit' => 1, 'answer' => ''));
 266  
 267          // Verify.
 268          $this->check_current_state(question_state::$invalid);
 269          $this->check_current_mark(null);
 270          $this->check_current_output(
 271                  $this->get_contains_marked_out_of_summary(),
 272                  $this->get_contains_submit_button_expectation(true),
 273                  $this->get_does_not_contain_feedback_expectation(),
 274                  $this->get_contains_validation_error_expectation(),
 275                  $this->get_does_not_contain_try_again_button_expectation(),
 276                  $this->get_no_hint_visible_expectation());
 277  
 278          // Now get it wrong.
 279          $this->process_submission(array('-submit' => 1, 'answer' => 'newt'));
 280  
 281          // Verify.
 282          $this->check_current_state(question_state::$todo);
 283          $this->check_current_mark(null);
 284          $this->check_current_output(
 285                  $this->get_contains_marked_out_of_summary(),
 286                  $this->get_does_not_contain_submit_button_expectation(),
 287                  $this->get_does_not_contain_validation_error_expectation(),
 288                  $this->get_contains_try_again_button_expectation(true),
 289                  new question_pattern_expectation('/Tries remaining: 2/'),
 290                  $this->get_contains_hint_expectation('This is the first hint'));
 291          $this->assertEquals('newt',
 292                  $this->quba->get_response_summary($this->slot));
 293  
 294          // Try again.
 295          $this->process_submission(array('-tryagain' => 1));
 296  
 297          // Verify.
 298          $this->check_current_state(question_state::$todo);
 299          $this->check_current_mark(null);
 300          $this->check_current_output(
 301                  $this->get_contains_marked_out_of_summary(),
 302                  $this->get_contains_submit_button_expectation(true),
 303                  $this->get_does_not_contain_feedback_expectation(),
 304                  $this->get_does_not_contain_validation_error_expectation(),
 305                  $this->get_does_not_contain_try_again_button_expectation(),
 306                  $this->get_no_hint_visible_expectation());
 307  
 308          // Now submit blank again.
 309          $this->process_submission(array('-submit' => 1, 'answer' => ''));
 310  
 311          // Verify.
 312          $this->check_current_state(question_state::$invalid);
 313          $this->check_current_mark(null);
 314          $this->check_current_output(
 315                  $this->get_contains_marked_out_of_summary(),
 316                  $this->get_contains_submit_button_expectation(true),
 317                  $this->get_does_not_contain_feedback_expectation(),
 318                  $this->get_contains_validation_error_expectation(),
 319                  $this->get_does_not_contain_try_again_button_expectation(),
 320                  $this->get_no_hint_visible_expectation());
 321  
 322          // Now get it right.
 323          $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
 324  
 325          // Verify.
 326          $this->check_current_state(question_state::$gradedright);
 327          $this->check_current_mark(0.6666667);
 328          $this->check_current_output(
 329                  $this->get_contains_mark_summary(0.6666667),
 330                  $this->get_does_not_contain_submit_button_expectation(),
 331                  $this->get_contains_correct_expectation(),
 332                  $this->get_does_not_contain_validation_error_expectation(),
 333                  $this->get_no_hint_visible_expectation());
 334          $this->assertEquals('frog',
 335                  $this->quba->get_response_summary($this->slot));
 336      }
 337  
 338      public function test_interactive_feedback_multichoice_multiple_reset() {
 339  
 340          // Create a multichoice multiple question.
 341          $mc = test_question_maker::make_a_multichoice_multi_question();
 342          $mc->showstandardinstruction = true;
 343          $mc->hints = array(
 344              new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true),
 345              new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true),
 346          );
 347          $this->start_attempt_at_question($mc, 'interactive', 2);
 348  
 349          $right = array_keys($mc->get_correct_response());
 350          $wrong = array_diff(array('choice0', 'choice1', 'choice2', 'choice3'), $right);
 351          $wrong = array_values(array_diff(
 352                  array('choice0', 'choice1', 'choice2', 'choice3'), $right));
 353  
 354          // Check the initial state.
 355          $this->check_current_state(question_state::$todo);
 356          $this->check_current_mark(null);
 357          $this->check_current_output(
 358                  $this->get_contains_marked_out_of_summary(),
 359                  $this->get_contains_question_text_expectation($mc),
 360                  $this->get_contains_mc_checkbox_expectation('choice0', true, false),
 361                  $this->get_contains_mc_checkbox_expectation('choice1', true, false),
 362                  $this->get_contains_mc_checkbox_expectation('choice2', true, false),
 363                  $this->get_contains_mc_checkbox_expectation('choice3', true, false),
 364                  $this->get_contains_submit_button_expectation(true),
 365                  $this->get_does_not_contain_feedback_expectation(),
 366                  $this->get_does_not_contain_num_parts_correct(),
 367                  $this->get_tries_remaining_expectation(3),
 368                  $this->get_no_hint_visible_expectation(),
 369                  new question_pattern_expectation('/' .
 370                          preg_quote(get_string('selectmulti', 'qtype_multichoice'), '/') . '/'));
 371  
 372          // Submit an answer with one right, and one wrong.
 373          $this->process_submission(array($right[0] => 1, $wrong[0] => 1, '-submit' => 1));
 374  
 375          // Verify.
 376          $this->check_current_state(question_state::$todo);
 377          $this->check_current_mark(null);
 378          $this->check_current_output(
 379                  $this->get_contains_marked_out_of_summary(),
 380                  $this->get_contains_mc_checkbox_expectation($right[0], false, true),
 381                  $this->get_contains_mc_checkbox_expectation($right[1], false, false),
 382                  $this->get_contains_mc_checkbox_expectation($wrong[0], false, true),
 383                  $this->get_contains_mc_checkbox_expectation($wrong[1], false, false),
 384                  $this->get_does_not_contain_submit_button_expectation(),
 385                  $this->get_contains_try_again_button_expectation(true),
 386                  $this->get_does_not_contain_correctness_expectation(),
 387                  new question_pattern_expectation('/Tries remaining: 2/'),
 388                  $this->get_contains_hint_expectation('This is the first hint'),
 389                  $this->get_contains_num_parts_correct(1),
 390                  $this->get_contains_standard_incorrect_combined_feedback_expectation(),
 391                  $this->get_contains_hidden_expectation(
 392                          $this->quba->get_field_prefix($this->slot) . $right[0], '1'),
 393                  $this->get_does_not_contain_hidden_expectation(
 394                          $this->quba->get_field_prefix($this->slot) . $right[1]),
 395                  $this->get_contains_hidden_expectation(
 396                          $this->quba->get_field_prefix($this->slot) . $wrong[0], '0'),
 397                  $this->get_does_not_contain_hidden_expectation(
 398                          $this->quba->get_field_prefix($this->slot) . $wrong[1]));
 399  
 400          // Do try again.
 401          $this->process_submission(array($right[0] => 1, '-tryagain' => 1));
 402  
 403          // Verify.
 404          $this->check_current_state(question_state::$todo);
 405          $this->check_current_mark(null);
 406          $this->check_current_output(
 407                  $this->get_contains_marked_out_of_summary(),
 408                  $this->get_contains_mc_checkbox_expectation($right[0], true, true),
 409                  $this->get_contains_mc_checkbox_expectation($right[1], true, false),
 410                  $this->get_contains_mc_checkbox_expectation($wrong[0], true, false),
 411                  $this->get_contains_mc_checkbox_expectation($wrong[1], true, false),
 412                  $this->get_contains_submit_button_expectation(true),
 413                  $this->get_does_not_contain_correctness_expectation(),
 414                  $this->get_does_not_contain_feedback_expectation(),
 415                  $this->get_tries_remaining_expectation(2),
 416                  $this->get_no_hint_visible_expectation());
 417      }
 418  
 419      public function test_interactive_regrade_changing_num_tries_leaving_open() {
 420          // Create a multichoice multiple question.
 421          $q = test_question_maker::make_question('shortanswer');
 422          $q->hints = array(
 423              new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true),
 424              new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true),
 425          );
 426          $this->start_attempt_at_question($q, 'interactive', 3);
 427  
 428          // Check the initial state.
 429          $this->check_current_state(question_state::$todo);
 430          $this->check_current_mark(null);
 431          $this->check_current_output(
 432                  $this->get_tries_remaining_expectation(3));
 433  
 434          // Submit the right answer.
 435          $this->process_submission(array('answer' => 'frog', '-submit' => 1));
 436  
 437          // Verify.
 438          $this->check_current_state(question_state::$gradedright);
 439          $this->check_current_mark(3);
 440  
 441          // Now change the quiestion so that answer is only partially right, and regrade.
 442          $q->answers[13]->fraction = 0.6666667;
 443          $q->answers[14]->fraction = 1;
 444  
 445          $this->quba->regrade_all_questions(false);
 446  
 447          // Verify.
 448          $this->check_current_state(question_state::$todo);
 449          $this->check_current_mark(null);
 450      }
 451  
 452      public function test_interactive_regrade_changing_num_tries_finished() {
 453          // Create a multichoice multiple question.
 454          $q = test_question_maker::make_question('shortanswer');
 455          $q->hints = array(
 456              new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true),
 457              new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true),
 458          );
 459          $this->start_attempt_at_question($q, 'interactive', 3);
 460  
 461          // Check the initial state.
 462          $this->check_current_state(question_state::$todo);
 463          $this->check_current_mark(null);
 464          $this->check_current_output(
 465                  $this->get_tries_remaining_expectation(3));
 466  
 467          // Submit the right answer.
 468          $this->process_submission(array('answer' => 'frog', '-submit' => 1));
 469  
 470          // Verify.
 471          $this->check_current_state(question_state::$gradedright);
 472          $this->check_current_mark(3);
 473  
 474          // Now change the quiestion so that answer is only partially right, and regrade.
 475          $q->answers[13]->fraction = 0.6666667;
 476          $q->answers[14]->fraction = 1;
 477  
 478          $this->quba->regrade_all_questions(true);
 479  
 480          // Verify.
 481          $this->check_current_state(question_state::$gradedpartial);
 482          // TODO I don't think 1 is the right fraction here. However, it is what
 483          // you get attempting a question like this without regrading being involved,
 484          // and I am currently interested in testing regrading here.
 485          $this->check_current_mark(1);
 486      }
 487  
 488      public function test_review_of_interactive_questions_before_finished() {
 489          // Create a multichoice multiple question.
 490          $q = test_question_maker::make_question('shortanswer');
 491          $q->hints = array(
 492                  new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true),
 493                  new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true),
 494          );
 495          $this->start_attempt_at_question($q, 'interactive', 3);
 496  
 497          // Check the initial state.
 498          $this->check_current_state(question_state::$todo);
 499          $this->check_current_mark(null);
 500          $this->check_current_output(
 501                  $this->get_contains_submit_button_expectation(true),
 502                  $this->get_does_not_contain_feedback_expectation(),
 503                  $this->get_tries_remaining_expectation(3),
 504                  $this->get_does_not_contain_try_again_button_expectation());
 505  
 506          // Now check what the teacher sees when they review the question.
 507          $this->displayoptions->readonly = true;
 508          $this->check_current_output(
 509                  $this->get_contains_submit_button_expectation(false),
 510                  $this->get_does_not_contain_feedback_expectation(),
 511                  $this->get_tries_remaining_expectation(3),
 512                  $this->get_does_not_contain_try_again_button_expectation());
 513          $this->displayoptions->readonly = false;
 514  
 515          // Submit a wrong answer.
 516          $this->process_submission(array('answer' => 'cat', '-submit' => 1));
 517  
 518          // Check the Try again button now shows up correctly.
 519          $this->check_current_state(question_state::$todo);
 520          $this->check_current_mark(null);
 521          $this->check_current_output(
 522                  $this->get_does_not_contain_submit_button_expectation(),
 523                  $this->get_contains_hint_expectation('This is the first hint.'),
 524                  $this->get_tries_remaining_expectation(2),
 525                  $this->get_contains_try_again_button_expectation(true));
 526  
 527          // And check that a disabled Try again button shows up when the question is reviewed.
 528          $this->displayoptions->readonly = true;
 529          $this->check_current_output(
 530                  $this->get_does_not_contain_submit_button_expectation(),
 531                  $this->get_contains_hint_expectation('This is the first hint.'),
 532                  $this->get_tries_remaining_expectation(2),
 533                  $this->get_contains_try_again_button_expectation(false));
 534      }
 535  }