Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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