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  /**
  19   * This file contains tests that walks a question through the adaptive
  20   * behaviour.
  21   *
  22   * @package    qbehaviour
  23   * @subpackage adaptive
  24   * @copyright  2009 The Open University
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  global $CFG;
  32  require_once (__DIR__ . '/../../../engine/lib.php');
  33  require_once (__DIR__ . '/../../../engine/tests/helpers.php');
  34  
  35  
  36  /**
  37   * Unit tests for the adaptive behaviour.
  38   *
  39   * @copyright  2009 The Open University
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_base {
  43      protected function get_contains_penalty_info_expectation($penalty) {
  44          $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive',
  45                                    format_float($penalty, $this->displayoptions->markdp));
  46          return new question_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
  47      }
  48  
  49      protected function get_does_not_contain_penalty_info_expectation() {
  50          $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive', 'XXXXX');
  51          $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo, '/')).'/';
  52          return new question_no_pattern_expectation($penaltypattern);
  53      }
  54  
  55      protected function get_contains_total_penalty_expectation($penalty) {
  56          $penaltyinfo = get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive',
  57                                    format_float($penalty, $this->displayoptions->markdp));
  58          return new question_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
  59      }
  60  
  61      protected function get_does_not_contain_total_penalty_expectation() {
  62          $penaltyinfo = get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive', 'XXXXX');
  63          $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo, '/')).'/';
  64          return new question_no_pattern_expectation($penaltypattern);
  65      }
  66  
  67      protected function get_contains_disregarded_info_expectation() {
  68          $penaltyinfo = get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive');
  69          return new question_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
  70      }
  71  
  72      protected function get_does_not_contain_disregarded_info_expectation() {
  73          $penaltyinfo = get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive');
  74          return new question_no_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
  75      }
  76  
  77      public function test_adaptive_multichoice() {
  78  
  79          // Create a multiple choice, single response question.
  80          $mc = test_question_maker::make_a_multichoice_single_question();
  81          $mc->penalty = 0.3333333;
  82          $this->start_attempt_at_question($mc, 'adaptive', 3);
  83  
  84          $rightindex = $this->get_mc_right_answer_index($mc);
  85          $wrongindex = ($rightindex + 1) % 3;
  86  
  87          // Check the initial state.
  88          $this->check_current_state(question_state::$todo);
  89          $this->check_current_mark(null);
  90          $this->check_current_output(
  91                  $this->get_contains_marked_out_of_summary(),
  92                  $this->get_contains_question_text_expectation($mc),
  93                  $this->get_contains_mc_radio_expectation(0, true, false),
  94                  $this->get_contains_mc_radio_expectation(1, true, false),
  95                  $this->get_contains_mc_radio_expectation(2, true, false),
  96                  $this->get_contains_submit_button_expectation(true),
  97                  $this->get_does_not_contain_feedback_expectation());
  98  
  99          // Process a submit.
 100          $this->process_submission(array('answer' => $wrongindex, '-submit' => 1));
 101  
 102          // Verify.
 103          $this->check_current_state(question_state::$todo);
 104          $this->check_current_mark(0);
 105          $this->check_current_output(
 106                  $this->get_contains_mark_summary(0),
 107                  $this->get_contains_mc_radio_expectation($wrongindex, true, true),
 108                  $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
 109                  $this->get_contains_mc_radio_expectation(($wrongindex + 2) % 3, true, false),
 110                  $this->get_contains_incorrect_expectation(),
 111                  $this->get_contains_penalty_info_expectation(1.00),
 112                  $this->get_does_not_contain_total_penalty_expectation());
 113          $this->assertRegExp('/B|C/',
 114                  $this->quba->get_response_summary($this->slot));
 115  
 116          // Process a change of answer to the right one, but not sumbitted.
 117          $this->process_submission(array('answer' => $rightindex));
 118  
 119          // Verify.
 120          $this->check_current_state(question_state::$todo);
 121          $this->check_current_mark(0);
 122          $this->check_current_output(
 123                  $this->get_contains_mark_summary(0),
 124                  $this->get_contains_mc_radio_expectation($rightindex, true, true),
 125                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false),
 126                  $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false));
 127          $this->assertRegExp('/B|C/',
 128                  $this->quba->get_response_summary($this->slot));
 129  
 130          // Now submit the right answer.
 131          $this->process_submission(array('answer' => $rightindex, '-submit' => 1));
 132  
 133          // Verify.
 134          $this->check_current_state(question_state::$complete);
 135          $this->check_current_mark(3 * (1 - $mc->penalty));
 136          $this->check_current_output(
 137                  $this->get_contains_mark_summary(3 * (1 - $mc->penalty)),
 138                  $this->get_contains_mc_radio_expectation($rightindex, true, true),
 139                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false),
 140                  $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false),
 141                  $this->get_contains_correct_expectation(),
 142                  $this->get_does_not_contain_penalty_info_expectation(),
 143                  $this->get_does_not_contain_total_penalty_expectation());
 144          $this->assertEquals('A',
 145                  $this->quba->get_response_summary($this->slot));
 146  
 147          // Finish the attempt.
 148          $this->quba->finish_all_questions();
 149  
 150          // Verify.
 151          $this->check_current_state(question_state::$gradedright);
 152          $this->check_current_mark(3 * (1 - $mc->penalty));
 153          $this->check_current_output(
 154                  $this->get_contains_mark_summary(3 * (1 - $mc->penalty)),
 155                  $this->get_contains_mc_radio_expectation($rightindex, false, true),
 156                  $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false),
 157                  $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, false, false),
 158                  $this->get_contains_correct_expectation());
 159  
 160          // Process a manual comment.
 161          $this->manual_grade('Not good enough!', 1, FORMAT_HTML);
 162  
 163          // Verify.
 164          $this->check_current_state(question_state::$mangrpartial);
 165          $this->check_current_mark(1);
 166          $this->check_current_output(
 167                  $this->get_contains_mark_summary(1),
 168                  new question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
 169  
 170          // Now change the correct answer to the question, and regrade.
 171          $mc->answers[13]->fraction = -0.33333333;
 172          $mc->answers[14]->fraction = 1; // We don't know which "wrong" index we chose above!
 173          $mc->answers[15]->fraction = 1; // Therefore, treat answers B and C with the same score.
 174          $this->quba->regrade_all_questions();
 175  
 176          // Verify.
 177          $this->check_current_state(question_state::$mangrpartial);
 178          $this->check_current_mark(1);
 179          $this->check_current_output(
 180                  $this->get_contains_mark_summary(1),
 181                  $this->get_contains_partcorrect_expectation());
 182  
 183          $autogradedstep = $this->get_step($this->get_step_count() - 2);
 184          $this->assertEqualsWithDelta($autogradedstep->get_fraction(), 1, 0.0000001);
 185      }
 186  
 187      public function test_adaptive_multichoice2() {
 188  
 189          // Create a multiple choice, multiple response question.
 190          $mc = test_question_maker::make_a_multichoice_multi_question();
 191          $mc->penalty = 0.3333333;
 192          $mc->shuffleanswers = 0;
 193          $this->start_attempt_at_question($mc, 'adaptive', 2);
 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_submit_button_expectation(true),
 202                  $this->get_does_not_contain_feedback_expectation());
 203  
 204          // Process a submit.
 205          $this->process_submission(array('choice0' => 1, 'choice2' => 1, '-submit' => 1));
 206  
 207          // Verify.
 208          $this->check_current_state(question_state::$complete);
 209          $this->check_current_mark(2);
 210          $this->check_current_output(
 211                  $this->get_contains_mark_summary(2),
 212                  $this->get_contains_submit_button_expectation(true),
 213                  $this->get_contains_correct_expectation(),
 214                  $this->get_does_not_contain_penalty_info_expectation(),
 215                  $this->get_does_not_contain_total_penalty_expectation());
 216  
 217          // Save the same correct answer again. Should not do anything.
 218          $numsteps = $this->get_step_count();
 219          $this->process_submission(array('choice0' => 1, 'choice2' => 1));
 220  
 221          // Verify.
 222          $this->check_step_count($numsteps);
 223          $this->check_current_mark(2);
 224          $this->check_current_state(question_state::$complete);
 225  
 226          // Finish the attempt.
 227          $this->quba->finish_all_questions();
 228  
 229          // Verify.
 230          $this->check_step_count($numsteps + 1);
 231          $this->check_current_state(question_state::$gradedright);
 232          $this->check_current_mark(2);
 233          $this->check_current_output(
 234                  $this->get_contains_mark_summary(2),
 235                  $this->get_does_not_contain_submit_button_expectation(),
 236                  $this->get_contains_correct_expectation());
 237      }
 238  
 239      public function test_adaptive_shortanswer_partially_right() {
 240  
 241          // Create a short answer question.
 242          $sa = test_question_maker::make_question('shortanswer');
 243          $this->start_attempt_at_question($sa, 'adaptive');
 244  
 245          // Check the initial state.
 246          $this->check_current_state(question_state::$todo);
 247          $this->check_current_mark(null);
 248          $this->render();
 249          $this->check_output_does_not_contain_text_input_with_class('answer', 'correct');
 250          $this->check_output_does_not_contain_text_input_with_class('answer', 'partiallycorrect');
 251          $this->check_output_does_not_contain_text_input_with_class('answer', 'incorrect');
 252          $this->check_current_output(
 253                  $this->get_contains_marked_out_of_summary(),
 254                  $this->get_contains_submit_button_expectation(true),
 255                  $this->get_does_not_contain_feedback_expectation());
 256  
 257          // Submit a partially correct answer.
 258          $this->process_submission(array('-submit' => 1, 'answer' => 'toad'));
 259  
 260          // Verify.
 261          $this->check_current_state(question_state::$todo);
 262          $this->check_current_mark(0.8);
 263          $this->render();
 264          $this->check_output_contains_text_input_with_class('answer', 'partiallycorrect');
 265          $this->check_current_output(
 266                  $this->get_contains_mark_summary(0.8),
 267                  $this->get_contains_submit_button_expectation(true),
 268                  $this->get_contains_partcorrect_expectation(),
 269                  $this->get_contains_penalty_info_expectation(0.33),
 270                  $this->get_does_not_contain_total_penalty_expectation(),
 271                  $this->get_does_not_contain_validation_error_expectation());
 272  
 273          // Submit an incorrect answer.
 274          $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
 275  
 276          // Verify.
 277          $this->check_current_state(question_state::$todo);
 278          $this->check_current_mark(0.8);
 279          $this->render();
 280          $this->check_output_contains_text_input_with_class('answer', 'incorrect');
 281          $this->check_current_output(
 282                  $this->get_contains_mark_summary(0.8),
 283                  $this->get_contains_submit_button_expectation(true),
 284                  $this->get_contains_incorrect_expectation(),
 285                  $this->get_contains_penalty_info_expectation(0.33),
 286                  $this->get_contains_total_penalty_expectation(0.67),
 287                  $this->get_does_not_contain_validation_error_expectation());
 288  
 289          // Submit a correct answer.
 290          $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
 291  
 292          // Verify.
 293          $this->check_current_state(question_state::$complete);
 294          $this->check_current_mark(0.8);
 295          $this->render();
 296          $this->check_output_contains_text_input_with_class('answer', 'correct');
 297          $this->check_current_output(
 298                  $this->get_contains_mark_summary(0.8),
 299                  $this->get_contains_submit_button_expectation(true),
 300                  $this->get_contains_correct_expectation(),
 301                  $this->get_does_not_contain_penalty_info_expectation(),
 302                  $this->get_does_not_contain_total_penalty_expectation(),
 303                  $this->get_does_not_contain_validation_error_expectation());
 304  
 305          // Finish the attempt.
 306          $this->quba->finish_all_questions();
 307  
 308          // Verify.
 309          $this->check_current_state(question_state::$gradedright);
 310          $this->check_current_mark(0.8);
 311          $this->check_current_output(
 312                  $this->get_contains_mark_summary(0.8),
 313                  $this->get_does_not_contain_submit_button_expectation(),
 314                  $this->get_contains_correct_expectation(),
 315                  $this->get_does_not_contain_validation_error_expectation());
 316      }
 317  
 318      public function test_adaptive_shortanswer_wrong_right_wrong() {
 319  
 320          // Create a short answer question.
 321          $sa = test_question_maker::make_question('shortanswer');
 322          $this->start_attempt_at_question($sa, 'adaptive', 6);
 323  
 324          // Check the initial state.
 325          $this->check_current_state(question_state::$todo);
 326          $this->check_current_mark(null);
 327          $this->check_current_output(
 328                  $this->get_contains_marked_out_of_summary(),
 329                  $this->get_contains_submit_button_expectation(true),
 330                  $this->get_does_not_contain_feedback_expectation());
 331  
 332          // Submit a wrong answer.
 333          $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
 334  
 335          // Verify.
 336          $this->check_current_state(question_state::$todo);
 337          $this->check_current_mark(0);
 338          $this->check_current_output(
 339                  $this->get_contains_mark_summary(0),
 340                  $this->get_contains_submit_button_expectation(true),
 341                  $this->get_contains_incorrect_expectation(),
 342                  $this->get_contains_penalty_info_expectation(2.00),
 343                  $this->get_does_not_contain_total_penalty_expectation(),
 344                  $this->get_does_not_contain_validation_error_expectation());
 345  
 346          // Submit the same wrong answer again. Nothing should change.
 347          $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
 348  
 349          // Verify.
 350          $this->check_current_state(question_state::$todo);
 351          $this->check_current_mark(0);
 352          $this->check_current_output(
 353                  $this->get_contains_mark_summary(0),
 354                  $this->get_contains_submit_button_expectation(true),
 355                  $this->get_contains_incorrect_expectation(),
 356                  $this->get_contains_penalty_info_expectation(2.00),
 357                  $this->get_does_not_contain_total_penalty_expectation(),
 358                  $this->get_does_not_contain_validation_error_expectation());
 359  
 360          // Submit a correct answer.
 361          $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
 362  
 363          // Verify.
 364          $this->check_current_state(question_state::$complete);
 365          $this->check_current_mark(4.00);
 366          $this->check_current_output(
 367                  $this->get_contains_mark_summary(4.00),
 368                  $this->get_contains_submit_button_expectation(true),
 369                  $this->get_contains_correct_expectation(),
 370                  $this->get_does_not_contain_penalty_info_expectation(),
 371                  $this->get_does_not_contain_total_penalty_expectation(),
 372                  $this->get_does_not_contain_validation_error_expectation());
 373  
 374          // Submit another incorrect answer.
 375          $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
 376  
 377          // Verify.
 378          $this->check_current_state(question_state::$complete);
 379          $this->check_current_mark(4.00);
 380          $this->check_current_output(
 381                  $this->get_contains_mark_summary(4.00),
 382                  $this->get_contains_submit_button_expectation(true),
 383                  $this->get_contains_incorrect_expectation(),
 384                  $this->get_does_not_contain_penalty_info_expectation(),
 385                  $this->get_does_not_contain_total_penalty_expectation(),
 386                  $this->get_does_not_contain_validation_error_expectation());
 387  
 388          // Finish the attempt.
 389          $this->quba->finish_all_questions();
 390  
 391          // Verify.
 392          $this->check_current_state(question_state::$gradedwrong);
 393          $this->check_current_mark(4.00);
 394          $this->check_current_output(
 395                  $this->get_contains_mark_summary(4.00),
 396                  $this->get_does_not_contain_submit_button_expectation(),
 397                  $this->get_contains_incorrect_expectation(),
 398                  $this->get_does_not_contain_validation_error_expectation());
 399      }
 400  
 401      public function test_adaptive_shortanswer_invalid_after_complete() {
 402  
 403          // Create a short answer question.
 404          $sa = test_question_maker::make_question('shortanswer');
 405          $this->start_attempt_at_question($sa, 'adaptive');
 406  
 407          // Check the initial state.
 408          $this->check_current_state(question_state::$todo);
 409          $this->check_current_mark(null);
 410          $this->check_current_output(
 411                  $this->get_contains_marked_out_of_summary(),
 412                  $this->get_contains_submit_button_expectation(true),
 413                  $this->get_does_not_contain_feedback_expectation());
 414  
 415          // Submit a wrong answer.
 416          $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
 417  
 418          // Verify.
 419          $this->check_current_state(question_state::$todo);
 420          $this->check_current_mark(0);
 421          $this->check_current_output(
 422                  $this->get_contains_mark_summary(0),
 423                  $this->get_contains_submit_button_expectation(true),
 424                  $this->get_contains_incorrect_expectation(),
 425                  $this->get_contains_penalty_info_expectation(0.33),
 426                  $this->get_does_not_contain_total_penalty_expectation(),
 427                  $this->get_does_not_contain_validation_error_expectation());
 428  
 429          // Submit a correct answer.
 430          $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
 431  
 432          // Verify.
 433          $this->check_current_state(question_state::$complete);
 434          $this->check_current_mark(0.66666667);
 435          $this->check_current_output(
 436                  $this->get_contains_mark_summary(0.67),
 437                  $this->get_contains_submit_button_expectation(true),
 438                  $this->get_contains_correct_expectation(),
 439                  $this->get_does_not_contain_penalty_info_expectation(),
 440                  $this->get_does_not_contain_total_penalty_expectation(),
 441                  $this->get_does_not_contain_validation_error_expectation());
 442  
 443          // Submit an empty answer.
 444          $this->process_submission(array('-submit' => 1, 'answer' => ''));
 445  
 446          // Verify.
 447          $this->check_current_state(question_state::$invalid);
 448          $this->check_current_mark(0.66666667);
 449          $this->check_current_output(
 450                  $this->get_contains_mark_summary(0.67),
 451                  $this->get_contains_submit_button_expectation(true),
 452                  $this->get_does_not_contain_penalty_info_expectation(),
 453                  $this->get_does_not_contain_total_penalty_expectation(),
 454                  $this->get_contains_validation_error_expectation());
 455  
 456          // Submit another wrong answer.
 457          $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
 458  
 459          // Verify.
 460          $this->check_current_state(question_state::$complete);
 461          $this->check_current_mark(0.66666667);
 462          $this->check_current_output(
 463                  $this->get_contains_mark_summary(0.67),
 464                  $this->get_contains_submit_button_expectation(true),
 465                  $this->get_contains_incorrect_expectation(),
 466                  $this->get_does_not_contain_penalty_info_expectation(),
 467                  $this->get_does_not_contain_total_penalty_expectation(),
 468                  $this->get_does_not_contain_validation_error_expectation());
 469  
 470          // Finish the attempt.
 471          $this->quba->finish_all_questions();
 472  
 473          // Verify.
 474          $this->check_current_state(question_state::$gradedwrong);
 475          $this->check_current_mark(0.66666667);
 476          $this->check_current_output(
 477                  $this->get_contains_mark_summary(0.67),
 478                  $this->get_does_not_contain_submit_button_expectation(),
 479                  $this->get_contains_incorrect_expectation(),
 480                  $this->get_does_not_contain_validation_error_expectation());
 481      }
 482  
 483      public function test_adaptive_shortanswer_zero_penalty() {
 484  
 485          // Create a short answer question.
 486          $sa = test_question_maker::make_question('shortanswer');
 487          // Disable penalties for this question.
 488          $sa->penalty = 0;
 489          $this->start_attempt_at_question($sa, 'adaptive');
 490  
 491          // Check the initial state.
 492          $this->check_current_state(question_state::$todo);
 493          $this->check_current_mark(null);
 494          $this->check_current_output(
 495                  $this->get_contains_marked_out_of_summary(),
 496                  $this->get_contains_submit_button_expectation(true),
 497                  $this->get_does_not_contain_feedback_expectation());
 498  
 499          // Submit a wrong answer.
 500          $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
 501  
 502          // Verify.
 503          $this->check_current_state(question_state::$todo);
 504          $this->check_current_mark(0);
 505          $this->check_current_output(
 506                  $this->get_contains_mark_summary(0),
 507                  $this->get_contains_submit_button_expectation(true),
 508                  $this->get_contains_incorrect_expectation(),
 509                  $this->get_does_not_contain_penalty_info_expectation(),
 510                  $this->get_does_not_contain_total_penalty_expectation(),
 511                  $this->get_does_not_contain_validation_error_expectation());
 512  
 513          // Submit a correct answer.
 514          $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
 515  
 516          // Verify.
 517          $this->check_current_state(question_state::$complete);
 518          $this->check_current_mark(1.0);
 519          $this->check_current_output(
 520                  $this->get_contains_mark_summary(1.0),
 521                  $this->get_contains_submit_button_expectation(true),
 522                  $this->get_contains_correct_expectation(),
 523                  $this->get_does_not_contain_penalty_info_expectation(),
 524                  $this->get_does_not_contain_total_penalty_expectation(),
 525                  $this->get_does_not_contain_validation_error_expectation());
 526  
 527          // Finish the attempt.
 528          $this->quba->finish_all_questions();
 529  
 530          // Verify.
 531          $this->check_current_state(question_state::$gradedright);
 532          $this->check_current_mark(1.0);
 533          $this->check_current_output(
 534                  $this->get_contains_mark_summary(1.0),
 535                  $this->get_does_not_contain_submit_button_expectation(),
 536                  $this->get_contains_correct_expectation(),
 537                  $this->get_does_not_contain_validation_error_expectation());
 538      }
 539  
 540      public function test_adaptive_shortanswer_try_to_submit_blank() {
 541  
 542          // Create a short answer question with correct answer true.
 543          $sa = test_question_maker::make_question('shortanswer');
 544          $this->start_attempt_at_question($sa, 'adaptive');
 545  
 546          // Check the initial state.
 547          $this->check_current_state(question_state::$todo);
 548          $this->check_current_mark(null);
 549          $this->check_current_output(
 550                  $this->get_contains_marked_out_of_summary(),
 551                  $this->get_contains_submit_button_expectation(true),
 552                  $this->get_does_not_contain_feedback_expectation());
 553  
 554          // Submit with blank answer.
 555          $this->process_submission(array('-submit' => 1, 'answer' => ''));
 556  
 557          // Verify.
 558          $this->check_current_state(question_state::$invalid);
 559          $this->check_current_mark(null);
 560          $this->check_current_output(
 561                  $this->get_contains_marked_out_of_summary(),
 562                  $this->get_contains_submit_button_expectation(true),
 563                  $this->get_does_not_contain_correctness_expectation(),
 564                  $this->get_does_not_contain_penalty_info_expectation(),
 565                  $this->get_does_not_contain_total_penalty_expectation(),
 566                  $this->get_contains_validation_error_expectation(),
 567                  $this->get_contains_disregarded_info_expectation());
 568          $this->assertNull($this->quba->get_response_summary($this->slot));
 569  
 570          // Now get it wrong.
 571          $this->process_submission(array('-submit' => 1, 'answer' => 'toad'));
 572  
 573          // Verify.
 574          $this->check_current_state(question_state::$todo);
 575          $this->check_current_mark(0.8);
 576          $this->check_current_output(
 577                  $this->get_contains_mark_summary(0.8),
 578                  $this->get_contains_submit_button_expectation(true),
 579                  $this->get_contains_partcorrect_expectation(),
 580                  $this->get_contains_penalty_info_expectation(0.33),
 581                  $this->get_does_not_contain_total_penalty_expectation(),
 582                  $this->get_does_not_contain_validation_error_expectation());
 583  
 584          // Now submit blank again.
 585          $this->process_submission(array('-submit' => 1, 'answer' => ''));
 586  
 587          // Verify.
 588          $this->check_current_state(question_state::$invalid);
 589          $this->check_current_mark(0.8);
 590          $this->check_current_output(
 591                  $this->get_contains_mark_summary(0.8),
 592                  $this->get_contains_submit_button_expectation(true),
 593                  $this->get_does_not_contain_correctness_expectation(),
 594                  $this->get_does_not_contain_penalty_info_expectation(),
 595                  $this->get_does_not_contain_total_penalty_expectation(),
 596                  $this->get_contains_validation_error_expectation());
 597      }
 598  
 599      public function test_adaptive_numerical() {
 600  
 601          // Create a numerical question.
 602          $sa = test_question_maker::make_question('numerical', 'pi');
 603          $this->start_attempt_at_question($sa, 'adaptive');
 604  
 605          // Check the initial state.
 606          $this->check_current_state(question_state::$todo);
 607          $this->check_current_mark(null);
 608          $this->check_current_output(
 609                  $this->get_contains_marked_out_of_summary(),
 610                  $this->get_contains_submit_button_expectation(true),
 611                  $this->get_does_not_contain_feedback_expectation());
 612  
 613          // Submit the correct answer.
 614          $this->process_submission(array('-submit' => 1, 'answer' => '3.14'));
 615  
 616          // Verify.
 617          $this->check_current_state(question_state::$complete);
 618          $this->check_current_mark(1);
 619          $this->check_current_output(
 620                  $this->get_contains_mark_summary(1),
 621                  $this->get_contains_submit_button_expectation(true),
 622                  $this->get_contains_correct_expectation(),
 623                  $this->get_does_not_contain_penalty_info_expectation(),
 624                  $this->get_does_not_contain_total_penalty_expectation(),
 625                  $this->get_does_not_contain_validation_error_expectation());
 626  
 627          // Submit an incorrect answer.
 628          $this->process_submission(array('-submit' => 1, 'answer' => '-5'));
 629  
 630          // Verify.
 631          $this->check_current_state(question_state::$complete);
 632          $this->check_current_mark(1);
 633          $this->check_current_output(
 634                  $this->get_contains_mark_summary(1),
 635                  $this->get_contains_submit_button_expectation(true),
 636                  $this->get_contains_incorrect_expectation(),
 637                  $this->get_does_not_contain_penalty_info_expectation(),
 638                  $this->get_does_not_contain_total_penalty_expectation(),
 639                  $this->get_does_not_contain_validation_error_expectation());
 640  
 641          // Finish the attempt.
 642          $this->quba->finish_all_questions();
 643  
 644          // Verify.
 645          $this->check_current_state(question_state::$gradedwrong);
 646          $this->check_current_mark(1);
 647          $this->check_current_output(
 648                  $this->get_contains_mark_summary(1),
 649                  $this->get_does_not_contain_submit_button_expectation(),
 650                  $this->get_contains_incorrect_expectation(),
 651                  $this->get_does_not_contain_validation_error_expectation());
 652      }
 653  
 654      public function test_adaptive_numerical_invalid() {
 655  
 656          // Create a numerical question.
 657          $numq = test_question_maker::make_question('numerical', 'pi');
 658          $numq->penalty = 0.1;
 659          $this->start_attempt_at_question($numq, 'adaptive');
 660  
 661          // Check the initial state.
 662          $this->check_current_state(question_state::$todo);
 663          $this->check_current_mark(null);
 664          $this->check_current_output(
 665                  $this->get_contains_marked_out_of_summary(),
 666                  $this->get_contains_submit_button_expectation(true),
 667                  $this->get_does_not_contain_feedback_expectation());
 668  
 669          // Submit a non-numerical answer.
 670          $this->process_submission(array('-submit' => 1, 'answer' => 'Pi'));
 671  
 672          // Verify.
 673          $this->check_current_state(question_state::$invalid);
 674          $this->check_current_mark(null);
 675          $this->check_current_output(
 676                  $this->get_contains_marked_out_of_summary(1),
 677                  $this->get_contains_submit_button_expectation(true),
 678                  $this->get_does_not_contain_correctness_expectation(),
 679                  $this->get_does_not_contain_penalty_info_expectation(),
 680                  $this->get_does_not_contain_total_penalty_expectation(),
 681                  $this->get_contains_validation_error_expectation(),
 682                  $this->get_contains_disregarded_info_expectation());
 683  
 684          // Submit an incorrect answer.
 685          $this->process_submission(array('-submit' => 1, 'answer' => '-5'));
 686  
 687          // Verify.
 688          $this->check_current_state(question_state::$todo);
 689          $this->check_current_mark(0);
 690          $this->check_current_output(
 691                  $this->get_contains_mark_summary(0),
 692                  $this->get_contains_submit_button_expectation(true),
 693                  $this->get_contains_incorrect_expectation(),
 694                  $this->get_contains_penalty_info_expectation(0.1),
 695                  $this->get_does_not_contain_total_penalty_expectation(),
 696                  $this->get_does_not_contain_validation_error_expectation(),
 697                  $this->get_does_not_contain_disregarded_info_expectation());
 698  
 699          // Submit another non-numerical answer.
 700          $this->process_submission(array('-submit' => 1, 'answer' => 'Pi*2'));
 701  
 702          // Verify.
 703          $this->check_current_state(question_state::$invalid);
 704          $this->check_current_mark(0);
 705          $this->check_current_output(
 706                  $this->get_contains_mark_summary(0),
 707                  $this->get_contains_submit_button_expectation(true),
 708                  $this->get_does_not_contain_correctness_expectation(),
 709                  $this->get_does_not_contain_penalty_info_expectation(),
 710                  $this->get_does_not_contain_total_penalty_expectation(),
 711                  $this->get_contains_validation_error_expectation(),
 712                  $this->get_contains_disregarded_info_expectation());
 713  
 714          // Submit the correct answer.
 715          $this->process_submission(array('-submit' => 1, 'answer' => '3.14'));
 716  
 717          // Verify.
 718          $this->check_current_state(question_state::$complete);
 719          $this->check_current_mark(0.9);
 720          $this->check_current_output(
 721                  $this->get_contains_mark_summary(0.9),
 722                  $this->get_contains_submit_button_expectation(true),
 723                  $this->get_contains_correct_expectation(),
 724                  $this->get_does_not_contain_penalty_info_expectation(),
 725                  $this->get_does_not_contain_total_penalty_expectation(),
 726                  $this->get_does_not_contain_validation_error_expectation(),
 727                  $this->get_does_not_contain_disregarded_info_expectation());
 728  
 729          // Submit another non-numerical answer.
 730          $this->process_submission(array('-submit' => 1, 'answer' => 'Pi/3'));
 731  
 732          // Verify.
 733          $this->check_current_state(question_state::$invalid);
 734          $this->check_current_mark(0.9);
 735          $this->check_current_output(
 736                  $this->get_contains_mark_summary(0.9),
 737                  $this->get_contains_submit_button_expectation(true),
 738                  $this->get_does_not_contain_correctness_expectation(),
 739                  $this->get_does_not_contain_penalty_info_expectation(),
 740                  $this->get_does_not_contain_total_penalty_expectation(),
 741                  $this->get_contains_validation_error_expectation(),
 742                  $this->get_contains_disregarded_info_expectation());
 743  
 744          // Finish the attempt.
 745          $this->quba->finish_all_questions();
 746  
 747          // Verify.
 748          $this->check_current_state(question_state::$gradedwrong);
 749          $this->check_current_mark(0.9);
 750          $this->check_current_output(
 751                  $this->get_contains_mark_summary(0.9),
 752                  $this->get_does_not_contain_submit_button_expectation(),
 753                  $this->get_contains_incorrect_expectation(),
 754                  $this->get_does_not_contain_validation_error_expectation(),
 755                  $this->get_does_not_contain_disregarded_info_expectation());
 756      }
 757  
 758      public function test_adaptive_multianswer() {
 759  
 760          // Create a multianswer question.
 761          $q = test_question_maker::make_question('multianswer', 'twosubq');
 762          // To simplify testing, multichoice subquestion's answers are not shuffled.
 763          $q->subquestions[2]->shuffleanswers = 0;
 764          $choices = array('0' => 'Bow-wow', '1' => 'Wiggly worm', '2' => 'Pussy-cat');
 765  
 766          $this->start_attempt_at_question($q, 'adaptive', 12);
 767  
 768          // Check the initial state.
 769          $this->check_current_state(question_state::$todo);
 770          $this->check_current_mark(null);
 771          $this->assertEquals('adaptive',
 772                  $this->quba->get_question_attempt($this->slot)->get_behaviour_name());
 773          $this->render();
 774          $this->check_output_contains_text_input('sub1_answer', '', true);
 775          $this->check_output_does_not_contain_text_input_with_class('sub1_answer', 'correct');
 776          $this->check_output_does_not_contain_text_input_with_class('sub1_answer', 'partiallycorrect');
 777          $this->check_output_does_not_contain_text_input_with_class('sub1_answer', 'incorrect');
 778          $this->check_current_output(
 779                  $this->get_contains_marked_out_of_summary(),
 780                  $this->get_contains_submit_button_expectation(true),
 781                  $this->get_does_not_contain_validation_error_expectation(),
 782                  $this->get_does_not_contain_feedback_expectation());
 783          $this->check_output_contains_selectoptions(
 784                  $this->get_contains_select_expectation('sub2_answer', $choices, null, true));
 785  
 786          // Submit an invalid response.
 787          $this->process_submission(array('sub1_answer' => '', 'sub2_answer' => 1, '-submit' => 1));
 788  
 789          // Verify.
 790          $this->check_current_state(question_state::$invalid);
 791          $this->check_current_mark(null);
 792          $this->check_output_contains_text_input('sub1_answer', '', true);
 793          $this->check_current_output(
 794                  $this->get_contains_submit_button_expectation(true),
 795                  $this->get_does_not_contain_penalty_info_expectation(),
 796                  $this->get_does_not_contain_total_penalty_expectation(),
 797                  $this->get_contains_disregarded_info_expectation());
 798          $this->check_output_contains_selectoptions(
 799                  $this->get_contains_select_expectation('sub2_answer', $choices, 1, true));
 800  
 801          // Check that extract responses will return the reset data.
 802          $prefix = $this->quba->get_field_prefix($this->slot);
 803          $this->assertEquals(array('sub2_answer' => 1),
 804                  $this->quba->extract_responses($this->slot, array($prefix . 'sub2_answer' => 1)));
 805  
 806          // Submit an incorrect response.
 807          $this->process_submission(array('sub1_answer' => 'Dog',
 808                  'sub2_answer' => 1, '-submit' => 1));
 809  
 810          // Verify.
 811          $this->check_current_state(question_state::$todo);
 812          $this->check_current_mark(0);
 813          $this->render();
 814          $this->check_output_contains_text_input('sub1_answer', 'Dog', true);
 815          $this->check_output_contains_text_input_with_class('sub1_answer', 'incorrect');
 816          $this->check_current_output(
 817                  $this->get_contains_mark_summary(0),
 818                  $this->get_contains_submit_button_expectation(true),
 819                  $this->get_contains_incorrect_expectation(),
 820                  $this->get_contains_penalty_info_expectation(4.00),
 821                  $this->get_does_not_contain_validation_error_expectation());
 822          $this->check_output_contains_selectoptions(
 823                  $this->get_contains_select_expectation('sub2_answer', $choices, 1, true));
 824  
 825          // Submit the right answer.
 826          $this->process_submission(array('sub1_answer' => 'Owl', 'sub2_answer' => 2, '-submit' => 1));
 827  
 828          // Verify.
 829          $this->check_current_state(question_state::$complete);
 830          $this->check_current_mark(8.00);
 831          $this->render();
 832          $this->check_output_contains_text_input('sub1_answer', 'Owl', true);
 833          $this->check_output_contains_text_input_with_class('sub1_answer', 'correct');
 834          $this->check_current_output(
 835                  $this->get_contains_mark_summary(8.00),
 836                  $this->get_contains_submit_button_expectation(true),
 837                  $this->get_contains_correct_expectation(),
 838                  $this->get_does_not_contain_penalty_info_expectation(),
 839                  $this->get_does_not_contain_total_penalty_expectation(),
 840                  $this->get_does_not_contain_validation_error_expectation());
 841          $this->check_output_contains_selectoptions(
 842                  $this->get_contains_select_expectation('sub2_answer', $choices, '2', true));
 843  
 844          // Finish the attempt.
 845          $this->quba->finish_all_questions();
 846  
 847          // Verify.
 848          $this->check_current_state(question_state::$gradedright);
 849          $this->check_current_mark(8.00);
 850          $this->render();
 851          $this->check_output_contains_text_input('sub1_answer', 'Owl', false);
 852          $this->check_output_contains_text_input_with_class('sub1_answer', 'correct');
 853          $this->check_current_output(
 854                  $this->get_contains_mark_summary(8.00),
 855                  $this->get_does_not_contain_submit_button_expectation(),
 856                  $this->get_contains_correct_expectation(),
 857                  $this->get_does_not_contain_validation_error_expectation());
 858      }
 859  }