Search moodle.org's
Developer Documentation

See Release Notes

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

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

   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   * A test helper trait.
  19   *
  20   * @package    quizaccess_seb
  21   * @author     Andrew Madden <andrewmadden@catalyst-au.net>
  22   * @copyright  2019 Catalyst IT
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  use mod_quiz\local\access_rule_base;
  27  use mod_quiz\quiz_attempt;
  28  use quizaccess_seb\seb_access_manager;
  29  use quizaccess_seb\settings_provider;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  global $CFG;
  34  require_once($CFG->dirroot . "/mod/quiz/accessrule/seb/rule.php"); // Include plugin rule class.
  35  require_once($CFG->dirroot . "/mod/quiz/mod_form.php"); // Include plugin rule class.
  36  
  37  /**
  38   * A test helper trait. It has some common helper methods.
  39   *
  40   * @copyright  2020 Catalyst IT
  41   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  trait quizaccess_seb_test_helper_trait {
  44  
  45      /** @var \stdClass $course Test course to contain quiz. */
  46      protected $course;
  47  
  48      /** @var \stdClass $quiz A test quiz. */
  49      protected $quiz;
  50  
  51      /** @var \stdClass $user A test logged-in user. */
  52      protected $user;
  53  
  54      /**
  55       * Assign a capability to $USER
  56       * The function creates a student $USER if $USER->id is empty
  57       *
  58       * @param string $capability Capability name.
  59       * @param int $contextid Context ID.
  60       * @param int $roleid Role ID.
  61       * @return int The role id - mainly returned for creation, so calling function can reuse it.
  62       */
  63      protected function assign_user_capability($capability, $contextid, $roleid = null) {
  64          global $USER;
  65  
  66          // Create a new student $USER if $USER doesn't exist.
  67          if (empty($USER->id)) {
  68              $user = $this->getDataGenerator()->create_user();
  69              $this->setUser($user);
  70          }
  71  
  72          if (empty($roleid)) {
  73              $roleid = \create_role('Dummy role', 'dummyrole', 'dummy role description');
  74          }
  75  
  76          \assign_capability($capability, CAP_ALLOW, $roleid, $contextid);
  77  
  78          \role_assign($roleid, $USER->id, $contextid);
  79  
  80          \accesslib_clear_all_caches_for_unit_testing();
  81  
  82          return $roleid;
  83      }
  84  
  85      /**
  86       * Strip the seb_ prefix from each setting key.
  87       *
  88       * @param \stdClass $settings Object containing settings.
  89       * @return \stdClass The modified settings object.
  90       */
  91      protected function strip_all_prefixes(\stdClass $settings) : \stdClass {
  92          $newsettings = new \stdClass();
  93          foreach ($settings as $name => $setting) {
  94              $newname = preg_replace("/^seb_/", "", $name);
  95              $newsettings->$newname = $setting; // Add new key.
  96          }
  97          return $newsettings;
  98      }
  99  
 100      /**
 101       * Creates a file in the user draft area.
 102       *
 103       * @param string $xml
 104       * @return int The user draftarea id
 105       */
 106      protected function create_test_draftarea_file(string $xml) : int {
 107          global $USER;
 108  
 109          $itemid = 0;
 110          $usercontext = \context_user::instance($USER->id);
 111          $filerecord = [
 112              'contextid' => \context_user::instance($USER->id)->id,
 113              'component' => 'user',
 114              'filearea' => 'draft',
 115              'itemid' => $itemid,
 116              'filepath' => '/',
 117              'filename' => 'test.xml'
 118          ];
 119  
 120          $fs = get_file_storage();
 121          $fs->create_file_from_string($filerecord, $xml);
 122  
 123          $draftitemid = 0;
 124          file_prepare_draft_area($draftitemid, $usercontext->id, 'user', 'draft', 0);
 125  
 126          return $draftitemid;
 127      }
 128  
 129      /**
 130       * Create a file in a modules filearea.
 131       *
 132       * @param string $xml XML content of the file.
 133       * @param string $cmid Course module id.
 134       * @return int Item ID of file.
 135       */
 136      protected function create_module_test_file(string $xml, string $cmid) : int {
 137          $itemid = 0;
 138          $fs = get_file_storage();
 139          $filerecord = [
 140              'contextid' => \context_module::instance($cmid)->id,
 141              'component' => 'quizaccess_seb',
 142              'filearea' => 'filemanager_sebconfigfile',
 143              'itemid' => $itemid,
 144              'filepath' => '/',
 145              'filename' => 'test.xml'
 146          ];
 147          $fs->create_file_from_string($filerecord, $xml);
 148          return $itemid;
 149      }
 150  
 151      /**
 152       * Create a test quiz for the specified course.
 153       *
 154       * @param \stdClass $course
 155       * @param int $requiresafeexambrowser How to use SEB for this quiz?
 156       * @return  array
 157       */
 158      protected function create_test_quiz($course, $requiresafeexambrowser = settings_provider::USE_SEB_NO) {
 159          $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
 160  
 161          $quiz = $quizgenerator->create_instance([
 162              'course' => $course->id,
 163              'questionsperpage' => 0,
 164              'grade' => 100.0,
 165              'sumgrades' => 2,
 166              'seb_requiresafeexambrowser' => $requiresafeexambrowser,
 167          ]);
 168          $quiz->seb_showsebdownloadlink = 1;
 169          $quiz->coursemodule = $quiz->cmid;
 170  
 171          // Create a couple of questions.
 172          $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
 173          $cat = $questiongenerator->create_question_category();
 174  
 175          $saq = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
 176          quiz_add_quiz_question($saq->id, $quiz);
 177          $numq = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
 178          quiz_add_quiz_question($numq->id, $quiz);
 179  
 180          return $quiz;
 181      }
 182  
 183      /**
 184       * Answer questions for a quiz + user.
 185       *
 186       * @param \stdClass $quiz Quiz to attempt.
 187       * @param \stdClass $user A user to attempt the quiz.
 188       * @return  array
 189       */
 190      protected function attempt_quiz($quiz, $user) {
 191          $this->setUser($user);
 192  
 193          $starttime = time();
 194          $quizobj = mod_quiz\quiz_settings::create($quiz->id, $user->id);
 195  
 196          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
 197          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 198  
 199          // Start the attempt.
 200          $attempt = quiz_create_attempt($quizobj, 1, false, $starttime, false, $user->id);
 201          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $starttime);
 202          quiz_attempt_save_started($quizobj, $quba, $attempt);
 203  
 204          // Answer the questions.
 205          $attemptobj = quiz_attempt::create($attempt->id);
 206  
 207          $tosubmit = [
 208              1 => ['answer' => 'frog'],
 209              2 => ['answer' => '3.14'],
 210          ];
 211  
 212          $attemptobj->process_submitted_actions($starttime, false, $tosubmit);
 213  
 214          // Finish the attempt.
 215          $attemptobj = quiz_attempt::create($attempt->id);
 216          $attemptobj->process_finish($starttime, false);
 217  
 218          $this->setUser();
 219  
 220          return [$quizobj, $quba, $attemptobj];
 221      }
 222  
 223      /**
 224       * Create test template.
 225       *
 226       * @param string|null $xml Template content.
 227       * @return \quizaccess_seb\template Just created template.
 228       */
 229      public function create_template(string $xml = null) {
 230          $data = [];
 231  
 232          if (!is_null($xml)) {
 233              $data['content'] = $xml;
 234          }
 235  
 236          return $this->getDataGenerator()->get_plugin_generator('quizaccess_seb')->create_template($data);
 237      }
 238  
 239      /**
 240       * Get access manager for testing.
 241       *
 242       * @return \quizaccess_seb\seb_access_manager
 243       */
 244      protected function get_access_manager() {
 245          return new seb_access_manager(new mod_quiz\quiz_settings($this->quiz,
 246              get_coursemodule_from_id('quiz', $this->quiz->cmid), $this->course));
 247      }
 248  
 249      /**
 250       * A helper method to make the rule form the currently created quiz and  course.
 251       *
 252       * @return access_rule_base|null
 253       */
 254      protected function make_rule() {
 255          return \quizaccess_seb::make(
 256              new mod_quiz\quiz_settings($this->quiz, get_coursemodule_from_id('quiz', $this->quiz->cmid), $this->course),
 257              0,
 258              true
 259          );
 260      }
 261  
 262      /**
 263       * A helper method to set up quiz view page.
 264       */
 265      protected function set_up_quiz_view_page() {
 266          global $PAGE;
 267  
 268          $page = new \moodle_page();
 269          $page->set_context(\context_module::instance($this->quiz->cmid));
 270          $page->set_course($this->course);
 271          $page->set_pagelayout('standard');
 272          $page->set_pagetype("mod-quiz-view");
 273          $page->set_url('/mod/quiz/view.php?id=' . $this->quiz->cmid);
 274  
 275          $PAGE = $page;
 276      }
 277  
 278      /**
 279       * Get a test object containing mock test settings.
 280       *
 281       * @return \stdClass Settings.
 282       */
 283      protected function get_test_settings(array $settings = []) : \stdClass {
 284          return (object) array_merge([
 285              'quizid' => 1,
 286              'cmid' => 1,
 287              'requiresafeexambrowser' => '1',
 288              'showsebtaskbar' => '1',
 289              'showwificontrol' => '0',
 290              'showreloadbutton' => '1',
 291              'showtime' => '0',
 292              'showkeyboardlayout' => '1',
 293              'allowuserquitseb' => '1',
 294              'quitpassword' => 'test',
 295              'linkquitseb' => '',
 296              'userconfirmquit' => '1',
 297              'enableaudiocontrol' => '1',
 298              'muteonstartup' => '0',
 299              'allowspellchecking' => '0',
 300              'allowreloadinexam' => '1',
 301              'activateurlfiltering' => '1',
 302              'filterembeddedcontent' => '0',
 303              'expressionsallowed' => 'test.com',
 304              'regexallowed' => '',
 305              'expressionsblocked' => '',
 306              'regexblocked' => '',
 307              'showsebdownloadlink' => '1',
 308          ], $settings);
 309      }
 310  
 311  }