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