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 311 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_bank;
  20  use question_display_options;
  21  use question_state;
  22  use test_question_maker;
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  require_once (__DIR__ . '/../lib.php');
  27  require_once (__DIR__ . '/helpers.php');
  28  
  29  
  30  /**
  31   * End-to-end tests of attempting a question.
  32   *
  33   * @package    core_question
  34   * @copyright  2017 The Open University
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class walkthrough_test extends \qbehaviour_walkthrough_test_base {
  38  
  39      public function test_regrade_does_not_lose_flag() {
  40  
  41          // Create a true-false question with correct answer true.
  42          $tf = test_question_maker::make_question('truefalse', 'true');
  43          $this->start_attempt_at_question($tf, 'deferredfeedback', 2);
  44  
  45          // Process a true answer.
  46          $this->process_submission(array('answer' => 1));
  47  
  48          // Finish the attempt.
  49          $this->quba->finish_all_questions();
  50  
  51          // Flag the question.
  52          $this->get_question_attempt()->set_flagged(true);
  53  
  54          // Now change the correct answer to the question, and regrade.
  55          $tf->rightanswer = false;
  56          $this->quba->regrade_all_questions();
  57  
  58          // Verify the flag has not been lost.
  59          $this->assertTrue($this->get_question_attempt()->is_flagged());
  60      }
  61  
  62      /**
  63       * Test action_author function.
  64       */
  65      public function test_action_author_with_display_options_testcase() {
  66          $this->resetAfterTest(true);
  67          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
  68          $teacher = $this->getDataGenerator()->create_user();
  69          $student = $this->getDataGenerator()->create_user();
  70  
  71          // Create an essay question in the DB.
  72          $cat = $generator->create_question_category();
  73          $essay = $generator->create_question('essay', 'editorfilepicker', ['category' => $cat->id]);
  74  
  75          // Start attempt at the question.
  76          $q = question_bank::load_question($essay->id);
  77  
  78          // Student attempt the question.
  79          $this->setUser($student);
  80          $this->start_attempt_at_question($q, 'deferredfeedback', 10, 1);
  81  
  82          // Simulate some data submitted by the student.
  83          $this->process_submission(['answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML]);
  84          $this->finish();
  85  
  86          // Process a manual comment.
  87          $this->setUser($teacher);
  88          $this->manual_grade('Not good enough!', 10, FORMAT_HTML);
  89          $this->render();
  90          $this->save_quba();
  91  
  92          // Set display option userinfoinhistory to HIDDEN.
  93          $displayoptions = new question_display_options();
  94          $displayoptions->history = question_display_options::VISIBLE;
  95          $displayoptions->userinfoinhistory = question_display_options::HIDDEN;
  96  
  97          $this->load_quba();
  98          $result = $this->quba->render_question($this->slot, $displayoptions);
  99  
 100          // The profile user link should not display.
 101          preg_match("/<a ?.*>(.*)<\/a>/", $result, $matches);
 102          $this->assertEquals(false, isset($matches[0]));
 103  
 104          // Set display option userinfoinhistory to SHOW_ALL.
 105          $displayoptions = new question_display_options();
 106          $displayoptions->history = question_display_options::VISIBLE;
 107          $displayoptions->userinfoinhistory = question_display_options::SHOW_ALL;
 108  
 109          $this->load_quba();
 110          $result = $this->quba->render_question($this->slot, $displayoptions);
 111          $numsteps = $this->quba->get_question_attempt($this->slot)->get_num_steps();
 112  
 113          // All steps in the result should contain user profile link.
 114          preg_match_all("/<a ?.*>(.*)<\/a>/", $result, $matches);
 115          $this->assertEquals($numsteps, count($matches[0]));
 116  
 117          // Set the userinfoinhistory to student id.
 118          $displayoptions = new question_display_options();
 119          $displayoptions->history = question_display_options::VISIBLE;
 120          $displayoptions->userinfoinhistory = $student->id;
 121  
 122          $this->load_quba();
 123          $result = $this->quba->render_question($this->slot, $displayoptions);
 124  
 125          // The step just show the user profile link if the step's userid is different with student id.
 126          preg_match_all("/<a ?.*>(.*)<\/a>/", $result, $matches);
 127          $this->assertEquals(1, count($matches[0]));
 128      }
 129  
 130      /**
 131       * @covers \question_usage_by_activity::regrade_question
 132       * @covers \question_attempt::regrade
 133       * @covers \question_attempt::get_attempt_state_data_to_regrade_with_version
 134       */
 135      public function test_regrading_an_interactive_attempt_while_in_progress() {
 136  
 137          // Start an attempt at a matching question.
 138          $q = test_question_maker::make_question('match');
 139          $this->start_attempt_at_question($q, 'interactive', 1);
 140          $this->save_quba();
 141  
 142          // Verify.
 143          $this->check_current_state(question_state::$todo);
 144          $this->check_current_mark(null);
 145          $this->check_step_count(1);
 146          $this->check_current_output($this->get_tries_remaining_expectation(1));
 147  
 148          // Regrade the attempt.
 149          // Duplicating the question here essential to triggering the bug we are trying to reproduce.
 150          $reloadedquestion = clone($q);
 151          $this->quba->regrade_question($this->slot, false, null, $reloadedquestion);
 152  
 153          // Verify.
 154          $this->check_current_state(question_state::$todo);
 155          $this->check_current_mark(null);
 156          $this->check_step_count(1);
 157          $this->check_current_output($this->get_tries_remaining_expectation(1));
 158      }
 159  
 160      /**
 161       * @covers \question_usage_by_activity::regrade_question
 162       * @covers \question_attempt::regrade
 163       * @covers \question_attempt::get_attempt_state_data_to_regrade_with_version
 164       */
 165      public function test_regrading_does_not_lose_metadata() {
 166  
 167          // Start an attempt at a matching question.
 168          $q = test_question_maker::make_question('match');
 169          $this->start_attempt_at_question($q, 'interactive', 1);
 170          // Like in process_redo_question in mod_quiz.
 171          $this->quba->set_question_attempt_metadata($this->slot, 'originalslot', 42);
 172          $this->save_quba();
 173  
 174          // Verify.
 175          $this->check_current_state(question_state::$todo);
 176          $this->check_current_mark(null);
 177          $this->check_step_count(1);
 178          $this->check_current_output($this->get_tries_remaining_expectation(1));
 179  
 180          // Regrade the attempt.
 181          $reloadedquestion = clone($q);
 182          $this->quba->regrade_question($this->slot, false, null, $reloadedquestion);
 183  
 184          // Verify.
 185          $this->check_current_state(question_state::$todo);
 186          $this->check_current_mark(null);
 187          $this->check_step_count(1);
 188          $this->check_current_output($this->get_tries_remaining_expectation(1));
 189          $this->assertEquals(42, $this->quba->get_question_attempt_metadata($this->slot, 'originalslot'));
 190      }
 191  }