Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   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 qbank_usage;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  use mod_quiz\quiz_attempt;
  22  
  23  global $CFG;
  24  require_once($CFG->dirroot . '/mod/quiz/tests/quiz_question_helper_test_trait.php');
  25  
  26  /**
  27   * Helper test.
  28   *
  29   * @package    qbank_usage
  30   * @copyright  2021 Catalyst IT Australia Pty Ltd
  31   * @author     Safat Shahin <safatshahin@catalyst-au.net>
  32   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   * @coversDefaultClass \qbank_usage\helper
  34   */
  35  class helper_test extends \advanced_testcase {
  36  
  37      use \quiz_question_helper_test_trait;
  38  
  39      /**
  40       * @var \stdClass $quiz
  41       */
  42      protected $quiz;
  43  
  44      /**
  45       * @var \stdClass $user
  46       */
  47      protected $user;
  48  
  49      /**
  50       * @var \core_question_generator $questiongenerator
  51       */
  52      protected $questiongenerator;
  53  
  54      /**
  55       * @var array $questions
  56       */
  57      protected $questions = [];
  58  
  59      /**
  60       * Test setup.
  61       */
  62      public function setup(): void {
  63          $this->resetAfterTest();
  64          $layout = '1,2,0';
  65          // Make a user to do the quiz.
  66          $this->user = $this->getDataGenerator()->create_user();
  67          $course = $this->getDataGenerator()->create_course();
  68          // Make a quiz.
  69          $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
  70          $this->quiz = $quizgenerator->create_instance(['course' => $course->id,
  71                  'grade' => 100.0, 'sumgrades' => 2, 'layout' => $layout]);
  72  
  73          $this->questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
  74          $cat = $this->questiongenerator->create_question_category();
  75  
  76          $page = 1;
  77          foreach (explode(',', $layout) as $slot) {
  78              if ($slot == 0) {
  79                  $page += 1;
  80                  continue;
  81              }
  82  
  83              $question = $this->questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
  84              quiz_add_quiz_question($question->id, $this->quiz, $page);
  85              $this->questions [] = $question;
  86          }
  87      }
  88  
  89      /**
  90       * Record a quiz attempt.
  91       *
  92       * @return void
  93       */
  94      protected function attempt_quiz(): void {
  95          $quizobj = \mod_quiz\quiz_settings::create($this->quiz->id, $this->user->id);
  96  
  97          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
  98          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
  99          $timenow = time();
 100          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $this->user->id);
 101          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 102          quiz_attempt_save_started($quizobj, $quba, $attempt);
 103          quiz_attempt::create($attempt->id);
 104      }
 105  
 106      /**
 107       * Test question attempt count.
 108       *
 109       * @covers ::get_question_attempts_count_in_quiz
 110       */
 111      public function test_get_question_attempts_count_in_quiz() {
 112          $this->attempt_quiz();
 113          foreach ($this->questions as $question) {
 114              $questionattemptcount = helper::get_question_attempts_count_in_quiz($question->id, $this->quiz->id);
 115              // Test the attempt count matches the usage count, each question should have one count.
 116              $this->assertEquals(1, $questionattemptcount);
 117          }
 118      }
 119  
 120      /**
 121       * Test test usage data.
 122       *
 123       * @covers ::get_question_entry_usage_count
 124       */
 125      public function test_get_question_entry_usage_count() {
 126          foreach ($this->questions as $question) {
 127              $count = helper::get_question_entry_usage_count(\question_bank::load_question($question->id));
 128              // Test that the attempt data matches the usage data for the count.
 129              $this->assertEquals(1, $count);
 130          }
 131      }
 132  
 133      /**
 134       * If a question has been included via a random question attempt, this should be counted as a usage.
 135       *
 136       * @covers ::get_question_entry_usage_count
 137       * @return void
 138       */
 139      public function test_get_random_question_attempts_usage_count(): void {
 140          $this->setAdminUser();
 141          $cat = $this->questiongenerator->create_question_category();
 142          $question = $this->questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
 143          $this->add_random_questions($this->quiz->id, 1, $cat->id, 1);
 144  
 145          $qdef = \question_bank::load_question($question->id);
 146          $count = helper::get_question_entry_usage_count($qdef);
 147          $this->assertEquals(0, $count);
 148  
 149          $this->attempt_quiz();
 150  
 151          $count = helper::get_question_entry_usage_count($qdef);
 152          $this->assertEquals(1, $count);
 153      }
 154  
 155      /**
 156       * When a question referenced directly is edited, the usage count of all versions remains the same.
 157       *
 158       * When checking usage of separate versions, the new version should show usages but the original version should not.
 159       *
 160       * @covers ::get_question_entry_usage_count
 161       * @return void
 162       */
 163      public function test_edited_question_usage_counts(): void {
 164          foreach ($this->questions as $question) {
 165              $qdef = \question_bank::load_question($question->id);
 166              $count1 = helper::get_question_entry_usage_count($qdef);
 167              // Each question should have 1 usage.
 168              $this->assertEquals(1, $count1);
 169  
 170              $newversion = $this->questiongenerator->update_question($question);
 171              $newqdef = \question_bank::load_question($newversion->id);
 172  
 173              // Either version should return the same count if not checking a specific version.
 174              $count2 = helper::get_question_entry_usage_count($qdef);
 175              $this->assertEquals(1, $count2);
 176              $count3 = helper::get_question_entry_usage_count($newqdef);
 177              $this->assertEquals(1, $count3);
 178              // Checking the specific version count should return the counts for each version.
 179              // The original version is no longer included in the quiz, so has 0 usages.
 180              $count4 = helper::get_question_entry_usage_count($qdef, true);
 181              $this->assertEquals(0, $count4);
 182              // The new version is now included in the quiz, so has 1 usage.
 183              $count5 = helper::get_question_entry_usage_count($newqdef, true);
 184              $this->assertEquals(1, $count5);
 185          }
 186      }
 187  
 188      /**
 189       * When a question referenced directly with attempts is edited, the usage count of all versions remains the same.
 190       *
 191       * When checking usage of separate versions, both versions should show usage.
 192       *
 193       * @covers ::get_question_entry_usage_count
 194       * @return void
 195       */
 196      public function test_edited_attempted_question_usage_counts(): void {
 197          $this->attempt_quiz();
 198  
 199          foreach ($this->questions as $question) {
 200              $qdef = \question_bank::load_question($question->id);
 201              $count1 = helper::get_question_entry_usage_count($qdef);
 202              // Each question should have 1 usage.
 203              $this->assertEquals(1, $count1);
 204  
 205              $newversion = $this->questiongenerator->update_question($question);
 206              $newqdef = \question_bank::load_question($newversion->id);
 207  
 208              // Either version should return the same count if not checking a specific version.
 209              $count2 = helper::get_question_entry_usage_count($qdef);
 210              $this->assertEquals(1, $count2);
 211              $count3 = helper::get_question_entry_usage_count($newqdef);
 212              $this->assertEquals(1, $count3);
 213              // Checking the specific version count should return the counts for each version.
 214              // The original version is no longer included in the quiz. However, the is still an attempt using this question version,
 215              // so it has 1 usage.
 216              $count4 = helper::get_question_entry_usage_count($qdef, true);
 217              $this->assertEquals(1, $count4);
 218              // The new version is now included in the quiz, so has 1 usage.
 219              $count5 = helper::get_question_entry_usage_count($newqdef, true);
 220              $this->assertEquals(1, $count5);
 221          }
 222      }
 223  
 224      /**
 225       * When a random question with attempts is edited, it should still have the same usage count.
 226       *
 227       * When checking usage of separate versions, the original version should still show usage but the new version should not.
 228       *
 229       * @covers ::get_question_entry_usage_count
 230       * @return void
 231       */
 232      public function test_edited_attempted_random_question_usage_count(): void {
 233          $this->setAdminUser();
 234          $cat = $this->questiongenerator->create_question_category();
 235          $question = $this->questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
 236          $this->add_random_questions($this->quiz->id, 1, $cat->id, 1);
 237  
 238          $this->attempt_quiz();
 239  
 240          $qdef = \question_bank::load_question($question->id);
 241          $count1 = helper::get_question_entry_usage_count($qdef);
 242          $this->assertEquals(1, $count1);
 243  
 244          $newversion = $this->questiongenerator->update_question($question);
 245          $newqdef = \question_bank::load_question($newversion->id);
 246  
 247          // Either version should return the same count if not checking a specific version.
 248          $count2 = helper::get_question_entry_usage_count($qdef);
 249          $this->assertEquals(1, $count2);
 250          $count3 = helper::get_question_entry_usage_count($newqdef);
 251          $this->assertEquals(1, $count3);
 252          // Checking the specific version count should return the counts for each version.
 253          // There is still an attempt of the original version has part of the random question attempt, so it has 1 usage.
 254          $count4 = helper::get_question_entry_usage_count($qdef, true);
 255          $this->assertEquals(1, $count4);
 256          // There is no attempt of the new version, so it has 0 usages.
 257          $count5 = helper::get_question_entry_usage_count($newqdef, true);
 258          $this->assertEquals(0, $count5);
 259      }
 260  }