Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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 qubaid_list;
  20  use question_bank;
  21  use question_engine;
  22  
  23  /**
  24   * Tests for the {@link core_question\bank\random_question_loader} class.
  25   *
  26   * @package    core_question
  27   * @copyright  2015 The Open University
  28   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  29   */
  30  class random_question_loader_test extends \advanced_testcase {
  31  
  32      public function test_empty_category_gives_null() {
  33          $this->resetAfterTest();
  34          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
  35  
  36          $cat = $generator->create_question_category();
  37          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
  38  
  39          $this->assertNull($loader->get_next_question_id($cat->id, 0));
  40          $this->assertNull($loader->get_next_question_id($cat->id, 1));
  41      }
  42  
  43      public function test_unknown_category_behaves_like_empty() {
  44          // It is up the caller to make sure the category id is valid.
  45          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
  46          $this->assertNull($loader->get_next_question_id(-1, 1));
  47      }
  48  
  49      public function test_descriptions_not_returned() {
  50          $this->resetAfterTest();
  51          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
  52  
  53          $cat = $generator->create_question_category();
  54          $info = $generator->create_question('description', null, array('category' => $cat->id));
  55          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
  56  
  57          $this->assertNull($loader->get_next_question_id($cat->id, 0));
  58      }
  59  
  60      public function test_hidden_questions_not_returned() {
  61          global $DB;
  62          $this->resetAfterTest();
  63          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
  64  
  65          $cat = $generator->create_question_category();
  66          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
  67          $DB->set_field('question', 'hidden', 1, array('id' => $question1->id));
  68          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
  69  
  70          $this->assertNull($loader->get_next_question_id($cat->id, 0));
  71      }
  72  
  73      public function test_cloze_subquestions_not_returned() {
  74          $this->resetAfterTest();
  75          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
  76  
  77          $cat = $generator->create_question_category();
  78          $question1 = $generator->create_question('multianswer', null, array('category' => $cat->id));
  79          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
  80  
  81          $this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0));
  82          $this->assertNull($loader->get_next_question_id($cat->id, 0));
  83      }
  84  
  85      public function test_random_questions_not_returned() {
  86          $this->resetAfterTest();
  87          $this->setAdminUser();
  88          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
  89  
  90          $cat = $generator->create_question_category();
  91          $course = $this->getDataGenerator()->create_course();
  92          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course));
  93          quiz_add_random_questions($quiz, 1, $cat->id, 1, false);
  94          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
  95  
  96          $this->assertNull($loader->get_next_question_id($cat->id, 0));
  97      }
  98  
  99      public function test_one_question_category_returns_that_q_then_null() {
 100          $this->resetAfterTest();
 101          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 102  
 103          $cat = $generator->create_question_category();
 104          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 105          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
 106  
 107          $this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 1));
 108          $this->assertNull($loader->get_next_question_id($cat->id, 0));
 109      }
 110  
 111      public function test_two_question_category_returns_both_then_null() {
 112          $this->resetAfterTest();
 113          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 114  
 115          $cat = $generator->create_question_category();
 116          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 117          $question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 118          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
 119  
 120          $questionids = array();
 121          $questionids[] = $loader->get_next_question_id($cat->id, 0);
 122          $questionids[] = $loader->get_next_question_id($cat->id, 0);
 123          sort($questionids);
 124          $this->assertEquals(array($question1->id, $question2->id), $questionids);
 125  
 126          $this->assertNull($loader->get_next_question_id($cat->id, 1));
 127      }
 128  
 129      public function test_nested_categories() {
 130          $this->resetAfterTest();
 131          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 132  
 133          $cat1 = $generator->create_question_category();
 134          $cat2 = $generator->create_question_category(array('parent' => $cat1->id));
 135          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat1->id));
 136          $question2 = $generator->create_question('shortanswer', null, array('category' => $cat2->id));
 137          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
 138  
 139          $this->assertEquals($question2->id, $loader->get_next_question_id($cat2->id, 1));
 140          $this->assertEquals($question1->id, $loader->get_next_question_id($cat1->id, 1));
 141  
 142          $this->assertNull($loader->get_next_question_id($cat1->id, 0));
 143      }
 144  
 145      public function test_used_question_not_returned_until_later() {
 146          $this->resetAfterTest();
 147          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 148  
 149          $cat = $generator->create_question_category();
 150          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 151          $question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 152          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()),
 153                  array($question2->id => 2));
 154  
 155          $this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0));
 156          $this->assertNull($loader->get_next_question_id($cat->id, 0));
 157      }
 158  
 159      public function test_previously_used_question_not_returned_until_later() {
 160          $this->resetAfterTest();
 161          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 162  
 163          $cat = $generator->create_question_category();
 164          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 165          $question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 166          $quba = question_engine::make_questions_usage_by_activity('test', \context_system::instance());
 167          $quba->set_preferred_behaviour('deferredfeedback');
 168          $question = question_bank::load_question($question2->id);
 169          $quba->add_question($question);
 170          $quba->add_question($question);
 171          $quba->start_all_questions();
 172          question_engine::save_questions_usage_by_activity($quba);
 173  
 174          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array($quba->get_id())));
 175  
 176          $this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0));
 177          $this->assertEquals($question2->id, $loader->get_next_question_id($cat->id, 0));
 178          $this->assertNull($loader->get_next_question_id($cat->id, 0));
 179      }
 180  
 181      public function test_empty_category_does_not_have_question_available() {
 182          $this->resetAfterTest();
 183          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 184  
 185          $cat = $generator->create_question_category();
 186          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
 187  
 188          $this->assertFalse($loader->is_question_available($cat->id, 0, 1));
 189          $this->assertFalse($loader->is_question_available($cat->id, 1, 1));
 190      }
 191  
 192      public function test_descriptions_not_available() {
 193          $this->resetAfterTest();
 194          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 195  
 196          $cat = $generator->create_question_category();
 197          $info = $generator->create_question('description', null, array('category' => $cat->id));
 198          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
 199  
 200          $this->assertFalse($loader->is_question_available($cat->id, 0, $info->id));
 201          $this->assertFalse($loader->is_question_available($cat->id, 1, $info->id));
 202      }
 203  
 204      public function test_existing_question_is_available_but_then_marked_used() {
 205          $this->resetAfterTest();
 206          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 207  
 208          $cat = $generator->create_question_category();
 209          $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
 210          $loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
 211  
 212          $this->assertTrue($loader->is_question_available($cat->id, 0, $question1->id));
 213          $this->assertFalse($loader->is_question_available($cat->id, 0, $question1->id));
 214  
 215          $this->assertFalse($loader->is_question_available($cat->id, 0, -1));
 216      }
 217  
 218      /**
 219       * Data provider for the get_questions test.
 220       */
 221      public function get_questions_test_cases() {
 222          return [
 223              'empty category' => [
 224                  'categoryindex' => 'emptycat',
 225                  'includesubcategories' => false,
 226                  'usetagnames' => [],
 227                  'expectedquestionindexes' => []
 228              ],
 229              'single category' => [
 230                  'categoryindex' => 'cat1',
 231                  'includesubcategories' => false,
 232                  'usetagnames' => [],
 233                  'expectedquestionindexes' => ['cat1q1', 'cat1q2']
 234              ],
 235              'include sub category' => [
 236                  'categoryindex' => 'cat1',
 237                  'includesubcategories' => true,
 238                  'usetagnames' => [],
 239                  'expectedquestionindexes' => ['cat1q1', 'cat1q2', 'subcatq1', 'subcatq2']
 240              ],
 241              'single category with tags' => [
 242                  'categoryindex' => 'cat1',
 243                  'includesubcategories' => false,
 244                  'usetagnames' => ['cat1'],
 245                  'expectedquestionindexes' => ['cat1q1']
 246              ],
 247              'include sub category with tag on parent' => [
 248                  'categoryindex' => 'cat1',
 249                  'includesubcategories' => true,
 250                  'usetagnames' => ['cat1'],
 251                  'expectedquestionindexes' => ['cat1q1']
 252              ],
 253              'include sub category with tag on sub' => [
 254                  'categoryindex' => 'cat1',
 255                  'includesubcategories' => true,
 256                  'usetagnames' => ['subcat'],
 257                  'expectedquestionindexes' => ['subcatq1']
 258              ],
 259              'include sub category with same tag on parent and sub' => [
 260                  'categoryindex' => 'cat1',
 261                  'includesubcategories' => true,
 262                  'usetagnames' => ['foo'],
 263                  'expectedquestionindexes' => ['cat1q1', 'subcatq1']
 264              ],
 265              'include sub category with tag not matching' => [
 266                  'categoryindex' => 'cat1',
 267                  'includesubcategories' => true,
 268                  'usetagnames' => ['cat1', 'cat2'],
 269                  'expectedquestionindexes' => []
 270              ]
 271          ];
 272      }
 273  
 274      /**
 275       * Test the get_questions function with various parameter combinations.
 276       *
 277       * This function creates a data set as follows:
 278       *      Category: cat1
 279       *          Question: cat1q1
 280       *              Tags: 'cat1', 'foo'
 281       *          Question: cat1q2
 282       *      Category: cat2
 283       *          Question: cat2q1
 284       *              Tags: 'cat2', 'foo'
 285       *          Question: cat2q2
 286       *      Category: subcat
 287       *          Question: subcatq1
 288       *              Tags: 'subcat', 'foo'
 289       *          Question: subcatq2
 290       *          Parent: cat1
 291       *      Category: emptycat
 292       *
 293       * @dataProvider get_questions_test_cases()
 294       * @param string $categoryindex The named index for the category to use
 295       * @param bool $includesubcategories If the search should include subcategories
 296       * @param string[] $usetagnames The tag names to include in the search
 297       * @param string[] $expectedquestionindexes The questions expected in the result
 298       */
 299      public function test_get_questions_variations(
 300          $categoryindex,
 301          $includesubcategories,
 302          $usetagnames,
 303          $expectedquestionindexes
 304      ) {
 305          $this->resetAfterTest();
 306  
 307          $categories = [];
 308          $questions = [];
 309          $tagnames = [
 310              'cat1',
 311              'cat2',
 312              'subcat',
 313              'foo'
 314          ];
 315          $collid = \core_tag_collection::get_default();
 316          $tags = \core_tag_tag::create_if_missing($collid, $tagnames);
 317          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 318  
 319          // First category and questions.
 320          list($category, $categoryquestions) = $this->create_category_and_questions(2, ['cat1', 'foo']);
 321          $categories['cat1'] = $category;
 322          $questions['cat1q1'] = $categoryquestions[0];
 323          $questions['cat1q2'] = $categoryquestions[1];
 324          // Second category and questions.
 325          list($category, $categoryquestions) = $this->create_category_and_questions(2, ['cat2', 'foo']);
 326          $categories['cat2'] = $category;
 327          $questions['cat2q1'] = $categoryquestions[0];
 328          $questions['cat2q2'] = $categoryquestions[1];
 329          // Sub category and questions.
 330          list($category, $categoryquestions) = $this->create_category_and_questions(2, ['subcat', 'foo'], $categories['cat1']);
 331          $categories['subcat'] = $category;
 332          $questions['subcatq1'] = $categoryquestions[0];
 333          $questions['subcatq2'] = $categoryquestions[1];
 334          // Empty category.
 335          list($category, $categoryquestions) = $this->create_category_and_questions(0);
 336          $categories['emptycat'] = $category;
 337  
 338          // Generate the arguments for the get_questions function.
 339          $category = $categories[$categoryindex];
 340          $tagids = array_map(function($tagname) use ($tags) {
 341              return $tags[$tagname]->id;
 342          }, $usetagnames);
 343  
 344          $loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
 345          $result = $loader->get_questions($category->id, $includesubcategories, $tagids);
 346          // Generate the expected question set.
 347          $expectedquestions = array_map(function($index) use ($questions) {
 348              return $questions[$index];
 349          }, $expectedquestionindexes);
 350  
 351          // Ensure the result matches what was expected.
 352          $this->assertCount(count($expectedquestions), $result);
 353          foreach ($expectedquestions as $question) {
 354              $this->assertEquals($result[$question->id]->id, $question->id);
 355              $this->assertEquals($result[$question->id]->category, $question->category);
 356          }
 357      }
 358  
 359      /**
 360       * get_questions should allow limiting and offsetting of the result set.
 361       */
 362      public function test_get_questions_with_limit_and_offset() {
 363          $this->resetAfterTest();
 364          $numberofquestions = 5;
 365          $includesubcategories = false;
 366          $tagids = [];
 367          $limit = 1;
 368          $offset = 0;
 369          $loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
 370          list($category, $questions) = $this->create_category_and_questions($numberofquestions);
 371  
 372          // Sort the questions by id to match the ordering of the get_questions
 373          // function.
 374          usort($questions, function($a, $b) {
 375              $aid = $a->id;
 376              $bid = $b->id;
 377  
 378              if ($aid == $bid) {
 379                  return 0;
 380              }
 381              return $aid < $bid ? -1 : 1;
 382          });
 383  
 384          for ($i = 0; $i < $numberofquestions; $i++) {
 385              $result = $loader->get_questions(
 386                  $category->id,
 387                  $includesubcategories,
 388                  $tagids,
 389                  $limit,
 390                  $offset
 391              );
 392  
 393              $this->assertCount($limit, $result);
 394              $actual = array_shift($result);
 395              $expected = $questions[$i];
 396              $this->assertEquals($expected->id, $actual->id);
 397              $offset++;
 398          }
 399      }
 400  
 401      /**
 402       * get_questions should allow retrieving questions with only a subset of
 403       * fields populated.
 404       */
 405      public function test_get_questions_with_restricted_fields() {
 406          $this->resetAfterTest();
 407          $includesubcategories = false;
 408          $tagids = [];
 409          $limit = 10;
 410          $offset = 0;
 411          $fields = ['id', 'name'];
 412          $loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
 413          list($category, $questions) = $this->create_category_and_questions(1);
 414  
 415          $result = $loader->get_questions(
 416              $category->id,
 417              $includesubcategories,
 418              $tagids,
 419              $limit,
 420              $offset,
 421              $fields
 422          );
 423  
 424          $expectedquestion = array_shift($questions);
 425          $actualquestion = array_shift($result);
 426          $actualfields = get_object_vars($actualquestion);
 427          $actualfields = array_keys($actualfields);
 428          sort($actualfields);
 429          sort($fields);
 430  
 431          $this->assertEquals($fields, $actualfields);
 432      }
 433  
 434      /**
 435       * Data provider for the count_questions test.
 436       */
 437      public function count_questions_test_cases() {
 438          return [
 439              'empty category' => [
 440                  'categoryindex' => 'emptycat',
 441                  'includesubcategories' => false,
 442                  'usetagnames' => [],
 443                  'expectedcount' => 0
 444              ],
 445              'single category' => [
 446                  'categoryindex' => 'cat1',
 447                  'includesubcategories' => false,
 448                  'usetagnames' => [],
 449                  'expectedcount' => 2
 450              ],
 451              'include sub category' => [
 452                  'categoryindex' => 'cat1',
 453                  'includesubcategories' => true,
 454                  'usetagnames' => [],
 455                  'expectedcount' => 4
 456              ],
 457              'single category with tags' => [
 458                  'categoryindex' => 'cat1',
 459                  'includesubcategories' => false,
 460                  'usetagnames' => ['cat1'],
 461                  'expectedcount' => 1
 462              ],
 463              'include sub category with tag on parent' => [
 464                  'categoryindex' => 'cat1',
 465                  'includesubcategories' => true,
 466                  'usetagnames' => ['cat1'],
 467                  'expectedcount' => 1
 468              ],
 469              'include sub category with tag on sub' => [
 470                  'categoryindex' => 'cat1',
 471                  'includesubcategories' => true,
 472                  'usetagnames' => ['subcat'],
 473                  'expectedcount' => 1
 474              ],
 475              'include sub category with same tag on parent and sub' => [
 476                  'categoryindex' => 'cat1',
 477                  'includesubcategories' => true,
 478                  'usetagnames' => ['foo'],
 479                  'expectedcount' => 2
 480              ],
 481              'include sub category with tag not matching' => [
 482                  'categoryindex' => 'cat1',
 483                  'includesubcategories' => true,
 484                  'usetagnames' => ['cat1', 'cat2'],
 485                  'expectedcount' => 0
 486              ]
 487          ];
 488      }
 489  
 490      /**
 491       * Test the count_questions function with various parameter combinations.
 492       *
 493       * This function creates a data set as follows:
 494       *      Category: cat1
 495       *          Question: cat1q1
 496       *              Tags: 'cat1', 'foo'
 497       *          Question: cat1q2
 498       *      Category: cat2
 499       *          Question: cat2q1
 500       *              Tags: 'cat2', 'foo'
 501       *          Question: cat2q2
 502       *      Category: subcat
 503       *          Question: subcatq1
 504       *              Tags: 'subcat', 'foo'
 505       *          Question: subcatq2
 506       *          Parent: cat1
 507       *      Category: emptycat
 508       *
 509       * @dataProvider count_questions_test_cases()
 510       * @param string $categoryindex The named index for the category to use
 511       * @param bool $includesubcategories If the search should include subcategories
 512       * @param string[] $usetagnames The tag names to include in the search
 513       * @param int $expectedcount The number of questions expected in the result
 514       */
 515      public function test_count_questions_variations(
 516          $categoryindex,
 517          $includesubcategories,
 518          $usetagnames,
 519          $expectedcount
 520      ) {
 521          $this->resetAfterTest();
 522  
 523          $categories = [];
 524          $questions = [];
 525          $tagnames = [
 526              'cat1',
 527              'cat2',
 528              'subcat',
 529              'foo'
 530          ];
 531          $collid = \core_tag_collection::get_default();
 532          $tags = \core_tag_tag::create_if_missing($collid, $tagnames);
 533          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 534  
 535          // First category and questions.
 536          list($category, $categoryquestions) = $this->create_category_and_questions(2, ['cat1', 'foo']);
 537          $categories['cat1'] = $category;
 538          $questions['cat1q1'] = $categoryquestions[0];
 539          $questions['cat1q2'] = $categoryquestions[1];
 540          // Second category and questions.
 541          list($category, $categoryquestions) = $this->create_category_and_questions(2, ['cat2', 'foo']);
 542          $categories['cat2'] = $category;
 543          $questions['cat2q1'] = $categoryquestions[0];
 544          $questions['cat2q2'] = $categoryquestions[1];
 545          // Sub category and questions.
 546          list($category, $categoryquestions) = $this->create_category_and_questions(2, ['subcat', 'foo'], $categories['cat1']);
 547          $categories['subcat'] = $category;
 548          $questions['subcatq1'] = $categoryquestions[0];
 549          $questions['subcatq2'] = $categoryquestions[1];
 550          // Empty category.
 551          list($category, $categoryquestions) = $this->create_category_and_questions(0);
 552          $categories['emptycat'] = $category;
 553  
 554          // Generate the arguments for the get_questions function.
 555          $category = $categories[$categoryindex];
 556          $tagids = array_map(function($tagname) use ($tags) {
 557              return $tags[$tagname]->id;
 558          }, $usetagnames);
 559  
 560          $loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
 561          $result = $loader->count_questions($category->id, $includesubcategories, $tagids);
 562  
 563          // Ensure the result matches what was expected.
 564          $this->assertEquals($expectedcount, $result);
 565      }
 566  
 567      /**
 568       * Create a question category and create questions in that category. Tag
 569       * the first question in each category with the given tags.
 570       *
 571       * @param int $questioncount How many questions to create.
 572       * @param string[] $tagnames The list of tags to use.
 573       * @param stdClass|null $parentcategory The category to set as the parent of the created category.
 574       * @return array The category and questions.
 575       */
 576      protected function create_category_and_questions($questioncount, $tagnames = [], $parentcategory = null) {
 577          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 578  
 579          if ($parentcategory) {
 580              $catparams = ['parent' => $parentcategory->id];
 581          } else {
 582              $catparams = [];
 583          }
 584  
 585          $category = $generator->create_question_category($catparams);
 586          $questions = [];
 587  
 588          for ($i = 0; $i < $questioncount; $i++) {
 589              $questions[] = $generator->create_question('shortanswer', null, ['category' => $category->id]);
 590          }
 591  
 592          if (!empty($tagnames) && !empty($questions)) {
 593              $context = \context::instance_by_id($category->contextid);
 594              \core_tag_tag::set_item_tags('core_question', 'question', $questions[0]->id, $context, $tagnames);
 595          }
 596  
 597          return [$category, $questions];
 598      }
 599  }