2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for the condition tree class and related logic.
20 * @package core_availability
21 * @copyright 2014 The Open University
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 use core_availability\capability_checker
;
26 use \core_availability\tree
;
28 defined('MOODLE_INTERNAL') ||
die();
31 * Unit tests for the condition tree class and related logic.
33 * @package core_availability
34 * @copyright 2014 The Open University
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 class tree_testcase
extends \advanced_testcase
{
38 public function setUp(): void
{
39 // Load the mock classes so they can be used.
40 require_once(__DIR__
. '/fixtures/mock_condition.php');
41 require_once(__DIR__
. '/fixtures/mock_info.php');
45 * Tests constructing a tree with errors.
47 public function test_construct_errors() {
51 } catch (coding_exception
$e) {
52 $this->assertStringContainsString('not object', $e->getMessage());
55 new tree((object)array());
57 } catch (coding_exception
$e) {
58 $this->assertStringContainsString('missing ->op', $e->getMessage());
61 new tree((object)array('op' => '*'));
63 } catch (coding_exception
$e) {
64 $this->assertStringContainsString('unknown ->op', $e->getMessage());
67 new tree((object)array('op' => '|'));
69 } catch (coding_exception
$e) {
70 $this->assertStringContainsString('missing ->show', $e->getMessage());
73 new tree((object)array('op' => '|', 'show' => 0));
75 } catch (coding_exception
$e) {
76 $this->assertStringContainsString('->show not bool', $e->getMessage());
79 new tree((object)array('op' => '&'));
81 } catch (coding_exception
$e) {
82 $this->assertStringContainsString('missing ->showc', $e->getMessage());
85 new tree((object)array('op' => '&', 'showc' => 0));
87 } catch (coding_exception
$e) {
88 $this->assertStringContainsString('->showc not array', $e->getMessage());
91 new tree((object)array('op' => '&', 'showc' => array(0)));
93 } catch (coding_exception
$e) {
94 $this->assertStringContainsString('->showc value not bool', $e->getMessage());
97 new tree((object)array('op' => '|', 'show' => true));
99 } catch (coding_exception
$e) {
100 $this->assertStringContainsString('missing ->c', $e->getMessage());
103 new tree((object)array('op' => '|', 'show' => true,
106 } catch (coding_exception
$e) {
107 $this->assertStringContainsString('->c not array', $e->getMessage());
110 new tree((object)array('op' => '|', 'show' => true,
113 } catch (coding_exception
$e) {
114 $this->assertStringContainsString('child not object', $e->getMessage());
117 new tree((object)array('op' => '|', 'show' => true,
118 'c' => array((object)array('type' => 'doesnotexist'))));
120 } catch (coding_exception
$e) {
121 $this->assertStringContainsString('Unknown condition type: doesnotexist', $e->getMessage());
124 new tree((object)array('op' => '|', 'show' => true,
125 'c' => array((object)array())));
127 } catch (coding_exception
$e) {
128 $this->assertStringContainsString('missing ->op', $e->getMessage());
131 new tree((object)array('op' => '&',
132 'c' => array((object)array('op' => '&', 'c' => array())),
133 'showc' => array(true, true)
136 } catch (coding_exception
$e) {
137 $this->assertStringContainsString('->c, ->showc mismatch', $e->getMessage());
142 * Tests constructing a tree with plugin that does not exist (ignored).
144 public function test_construct_ignore_missing_plugin() {
145 // Construct a tree with & combination of one condition that doesn't exist.
146 $tree = new tree(tree
::get_root_json(array(
147 (object)array('type' => 'doesnotexist')), tree
::OP_OR
), true);
148 // Expected result is an empty tree with | condition, shown.
149 $this->assertEquals('+|()', (string)$tree);
153 * Tests constructing a tree with subtrees using all available operators.
155 public function test_construct_just_trees() {
156 $structure = tree
::get_root_json(array(
157 tree
::get_nested_json(array(), tree
::OP_OR
),
158 tree
::get_nested_json(array(
159 tree
::get_nested_json(array(), tree
::OP_NOT_OR
)), tree
::OP_NOT_AND
)),
160 tree
::OP_AND
, array(true, true));
161 $tree = new tree($structure);
162 $this->assertEquals('&(+|(),+!&(!|()))', (string)$tree);
166 * Tests constructing tree using the mock plugin.
168 public function test_construct_with_mock_plugin() {
169 $structure = tree
::get_root_json(array(
170 self
::mock(array('a' => true, 'm' => ''))), tree
::OP_OR
);
171 $tree = new tree($structure);
172 $this->assertEquals('+|({mock:y,})', (string)$tree);
176 * Tests the check_available and get_result_information functions.
178 public function test_check_available() {
182 $this->resetAfterTest();
183 $info = new \core_availability\
mock_info();
184 $this->setAdminUser();
188 $structure = tree
::get_root_json(array(), tree
::OP_OR
);
189 list ($available, $information) = $this->get_available_results(
190 $structure, $info, $USER->id
);
191 $this->assertTrue($available);
193 // One condition set to yes.
194 $structure->c
= array(
195 self
::mock(array('a' => true)));
196 list ($available, $information) = $this->get_available_results(
197 $structure, $info, $USER->id
);
198 $this->assertTrue($available);
200 // One condition set to no.
201 $structure->c
= array(
202 self
::mock(array('a' => false, 'm' => 'no')));
203 list ($available, $information) = $this->get_available_results(
204 $structure, $info, $USER->id
);
205 $this->assertFalse($available);
206 $this->assertEquals('SA: no', $information);
208 // Two conditions, OR, resolving as true.
209 $structure->c
= array(
210 self
::mock(array('a' => false, 'm' => 'no')),
211 self
::mock(array('a' => true)));
212 list ($available, $information) = $this->get_available_results(
213 $structure, $info, $USER->id
);
214 $this->assertTrue($available);
215 $this->assertEquals('', $information);
217 // Two conditions, OR, resolving as false.
218 $structure->c
= array(
219 self
::mock(array('a' => false, 'm' => 'no')),
220 self
::mock(array('a' => false, 'm' => 'way')));
221 list ($available, $information) = $this->get_available_results(
222 $structure, $info, $USER->id
);
223 $this->assertFalse($available);
224 $this->assertMatchesRegularExpression('~any of.*no.*way~', $information);
226 // Two conditions, OR, resolving as false, no display.
227 $structure->show
= false;
228 list ($available, $information) = $this->get_available_results(
229 $structure, $info, $USER->id
);
230 $this->assertFalse($available);
231 $this->assertEquals('', $information);
233 // Two conditions, AND, resolving as true.
234 $structure->op
= '&';
235 unset($structure->show
);
236 $structure->showc
= array(true, true);
237 $structure->c
= array(
238 self
::mock(array('a' => true)),
239 self
::mock(array('a' => true)));
240 list ($available, $information) = $this->get_available_results(
241 $structure, $info, $USER->id
);
242 $this->assertTrue($available);
244 // Two conditions, AND, one false.
245 $structure->c
= array(
246 self
::mock(array('a' => false, 'm' => 'wom')),
247 self
::mock(array('a' => true, 'm' => '')));
248 list ($available, $information) = $this->get_available_results(
249 $structure, $info, $USER->id
);
250 $this->assertFalse($available);
251 $this->assertEquals('SA: wom', $information);
253 // Two conditions, AND, both false.
254 $structure->c
= array(
255 self
::mock(array('a' => false, 'm' => 'wom')),
256 self
::mock(array('a' => false, 'm' => 'bat')));
257 list ($available, $information) = $this->get_available_results(
258 $structure, $info, $USER->id
);
259 $this->assertFalse($available);
260 $this->assertMatchesRegularExpression('~wom.*bat~', $information);
262 // Two conditions, AND, both false, show turned off for one. When
263 // show is turned off, that means if you don't have that condition
264 // you don't get to see anything at all.
265 $structure->showc
[0] = false;
266 list ($available, $information) = $this->get_available_results(
267 $structure, $info, $USER->id
);
268 $this->assertFalse($available);
269 $this->assertEquals('', $information);
270 $structure->showc
[0] = true;
272 // Two conditions, NOT OR, both false.
273 $structure->op
= '!|';
274 list ($available, $information) = $this->get_available_results(
275 $structure, $info, $USER->id
);
276 $this->assertTrue($available);
278 // Two conditions, NOT OR, one true.
279 $structure->c
[0]->a
= true;
280 list ($available, $information) = $this->get_available_results(
281 $structure, $info, $USER->id
);
282 $this->assertFalse($available);
283 $this->assertEquals('SA: !wom', $information);
285 // Two conditions, NOT OR, both true.
286 $structure->c
[1]->a
= true;
287 list ($available, $information) = $this->get_available_results(
288 $structure, $info, $USER->id
);
289 $this->assertFalse($available);
290 $this->assertMatchesRegularExpression('~!wom.*!bat~', $information);
292 // Two conditions, NOT AND, both true.
293 $structure->op
= '!&';
294 unset($structure->showc
);
295 $structure->show
= true;
296 list ($available, $information) = $this->get_available_results(
297 $structure, $info, $USER->id
);
298 $this->assertFalse($available);
299 $this->assertMatchesRegularExpression('~any of.*!wom.*!bat~', $information);
301 // Two conditions, NOT AND, one true.
302 $structure->c
[1]->a
= false;
303 list ($available, $information) = $this->get_available_results(
304 $structure, $info, $USER->id
);
305 $this->assertTrue($available);
307 // Nested NOT conditions; true.
308 $structure->c
= array(
309 tree
::get_nested_json(array(
310 self
::mock(array('a' => true, 'm' => 'no'))), tree
::OP_NOT_AND
));
311 list ($available, $information) = $this->get_available_results(
312 $structure, $info, $USER->id
);
313 $this->assertTrue($available);
315 // Nested NOT conditions; false (note no ! in message).
316 $structure->c
[0]->c
[0]->a
= false;
317 list ($available, $information) = $this->get_available_results(
318 $structure, $info, $USER->id
);
319 $this->assertFalse($available);
320 $this->assertEquals('SA: no', $information);
322 // Nested condition groups, message test.
323 $structure->op
= '|';
324 $structure->c
= array(
325 tree
::get_nested_json(array(
326 self
::mock(array('a' => false, 'm' => '1')),
327 self
::mock(array('a' => false, 'm' => '2'))
329 self
::mock(array('a' => false, 'm' => 3)));
330 list ($available, $information) = $this->get_available_results(
331 $structure, $info, $USER->id
);
332 $this->assertFalse($available);
333 $this->assertMatchesRegularExpression('~<ul.*<ul.*<li.*1.*<li.*2.*</ul>.*<li.*3~', $information);
337 * Shortcut function to check availability and also get information.
339 * @param stdClass $structure Tree structure
340 * @param \core_availability\info $info Location info
341 * @param int $userid User id
343 protected function get_available_results($structure, \core_availability\info
$info, $userid) {
345 $tree = new tree($structure);
346 $result = $tree->check_available(false, $info, true, $userid);
347 $information = $tree->get_result_information($info, $result);
348 if (!is_string($information)) {
349 $renderer = $PAGE->get_renderer('core', 'availability');
350 $information = $renderer->render($information);
352 return array($result->is_available(), $information);
356 * Tests the is_available_for_all() function.
358 public function test_is_available_for_all() {
359 // Empty tree is always available.
360 $structure = tree
::get_root_json(array(), tree
::OP_OR
);
361 $tree = new tree($structure);
362 $this->assertTrue($tree->is_available_for_all());
364 // Tree with normal item in it, not always available.
365 $structure->c
[0] = (object)array('type' => 'mock');
366 $tree = new tree($structure);
367 $this->assertFalse($tree->is_available_for_all());
369 // OR tree with one always-available item.
370 $structure->c
[1] = self
::mock(array('all' => true));
371 $tree = new tree($structure);
372 $this->assertTrue($tree->is_available_for_all());
374 // AND tree with one always-available and one not.
375 $structure->op
= '&';
376 $structure->showc
= array(true, true);
377 unset($structure->show
);
378 $tree = new tree($structure);
379 $this->assertFalse($tree->is_available_for_all());
381 // Test NOT conditions (items not always-available).
382 $structure->op
= '!&';
383 $structure->show
= true;
384 unset($structure->showc
);
385 $tree = new tree($structure);
386 $this->assertFalse($tree->is_available_for_all());
388 // Test again with one item always-available for NOT mode.
389 $structure->c
[1]->allnot
= true;
390 $tree = new tree($structure);
391 $this->assertTrue($tree->is_available_for_all());
395 * Tests the get_full_information() function.
397 public function test_get_full_information() {
399 $renderer = $PAGE->get_renderer('core', 'availability');
401 $info = new \core_availability\
mock_info();
404 $structure = tree
::get_root_json(array(), tree
::OP_OR
);
405 $tree = new tree($structure);
406 $this->assertEquals('', $tree->get_full_information($info));
408 // Condition (normal and NOT).
409 $structure->c
= array(
410 self
::mock(array('m' => 'thing')));
411 $tree = new tree($structure);
412 $this->assertEquals('SA: [FULL]thing',
413 $tree->get_full_information($info));
414 $structure->op
= '!&';
415 $tree = new tree($structure);
416 $this->assertEquals('SA: ![FULL]thing',
417 $tree->get_full_information($info));
419 // Complex structure.
420 $structure->op
= '|';
421 $structure->c
= array(
422 tree
::get_nested_json(array(
423 self
::mock(array('m' => '1')),
424 self
::mock(array('m' => '2'))), tree
::OP_AND
),
425 self
::mock(array('m' => 3)));
426 $tree = new tree($structure);
427 $this->assertMatchesRegularExpression('~<ul.*<ul.*<li.*1.*<li.*2.*</ul>.*<li.*3~',
428 $renderer->render($tree->get_full_information($info)));
430 // Test intro messages before list. First, OR message.
431 $structure->c
= array(
432 self
::mock(array('m' => '1')),
433 self
::mock(array('m' => '2'))
435 $tree = new tree($structure);
436 $this->assertMatchesRegularExpression('~Not available unless any of:.*<ul>~',
437 $renderer->render($tree->get_full_information($info)));
439 // Now, OR message when not shown.
440 $structure->show
= false;
441 $tree = new tree($structure);
442 $this->assertMatchesRegularExpression('~hidden.*<ul>~',
443 $renderer->render($tree->get_full_information($info)));
446 $structure->op
= '&';
447 unset($structure->show
);
448 $structure->showc
= array(false, false);
449 $tree = new tree($structure);
450 $this->assertMatchesRegularExpression('~Not available unless:.*<ul>~',
451 $renderer->render($tree->get_full_information($info)));
453 // Hidden markers on items.
454 $this->assertMatchesRegularExpression('~1.*hidden.*2.*hidden~',
455 $renderer->render($tree->get_full_information($info)));
457 // Hidden markers on child tree and items.
458 $structure->c
[1] = tree
::get_nested_json(array(
459 self
::mock(array('m' => '2')),
460 self
::mock(array('m' => '3'))), tree
::OP_AND
);
461 $tree = new tree($structure);
462 $this->assertMatchesRegularExpression('~1.*hidden.*All of \(hidden.*2.*3~',
463 $renderer->render($tree->get_full_information($info)));
464 $structure->c
[1]->op
= '|';
465 $tree = new tree($structure);
466 $this->assertMatchesRegularExpression('~1.*hidden.*Any of \(hidden.*2.*3~',
467 $renderer->render($tree->get_full_information($info)));
469 // Hidden markers on single-item display, AND and OR.
470 $structure->showc
= array(false);
471 $structure->c
= array(
472 self
::mock(array('m' => '1'))
474 $tree = new tree($structure);
475 $this->assertMatchesRegularExpression('~1.*hidden~',
476 $tree->get_full_information($info));
478 unset($structure->showc
);
479 $structure->show
= false;
480 $structure->op
= '|';
481 $tree = new tree($structure);
482 $this->assertMatchesRegularExpression('~1.*hidden~',
483 $tree->get_full_information($info));
485 // Hidden marker if single item is tree.
486 $structure->c
[0] = tree
::get_nested_json(array(
487 self
::mock(array('m' => '1')),
488 self
::mock(array('m' => '2'))), tree
::OP_AND
);
489 $tree = new tree($structure);
490 $this->assertMatchesRegularExpression('~Not available \(hidden.*1.*2~',
491 $renderer->render($tree->get_full_information($info)));
493 // Single item tree containing single item.
494 unset($structure->c
[0]->c
[1]);
495 $tree = new tree($structure);
496 $this->assertMatchesRegularExpression('~SA.*1.*hidden~',
497 $tree->get_full_information($info));
501 * Tests the is_empty() function.
503 public function test_is_empty() {
504 // Tree with nothing in should be empty.
505 $structure = tree
::get_root_json(array(), tree
::OP_OR
);
506 $tree = new tree($structure);
507 $this->assertTrue($tree->is_empty());
509 // Tree with something in is not empty.
510 $structure = tree
::get_root_json(array(self
::mock(array('m' => '1'))), tree
::OP_OR
);
511 $tree = new tree($structure);
512 $this->assertFalse($tree->is_empty());
516 * Tests the get_all_children() function.
518 public function test_get_all_children() {
519 // Create a tree with nothing in.
520 $structure = tree
::get_root_json(array(), tree
::OP_OR
);
521 $tree1 = new tree($structure);
523 // Create second tree with complex structure.
524 $structure->c
= array(
525 tree
::get_nested_json(array(
526 self
::mock(array('m' => '1')),
527 self
::mock(array('m' => '2'))
529 self
::mock(array('m' => 3)));
530 $tree2 = new tree($structure);
532 // Check list of conditions from both trees.
533 $this->assertEquals(array(), $tree1->get_all_children('core_availability\condition'));
534 $result = $tree2->get_all_children('core_availability\condition');
535 $this->assertEquals(3, count($result));
536 $this->assertEquals('{mock:n,1}', (string)$result[0]);
537 $this->assertEquals('{mock:n,2}', (string)$result[1]);
538 $this->assertEquals('{mock:n,3}', (string)$result[2]);
540 // Check specific type, should give same results.
541 $result2 = $tree2->get_all_children('availability_mock\condition');
542 $this->assertEquals($result, $result2);
546 * Tests the update_dependency_id() function.
548 public function test_update_dependency_id() {
549 // Create tree with structure of 3 mocks.
550 $structure = tree
::get_root_json(array(
551 tree
::get_nested_json(array(
552 self
::mock(array('table' => 'frogs', 'id' => 9)),
553 self
::mock(array('table' => 'zombies', 'id' => 9))
555 self
::mock(array('table' => 'frogs', 'id' => 9))));
557 // Get 'before' value.
558 $tree = new tree($structure);
559 $before = $tree->save();
561 // Try replacing a table or id that isn't used.
562 $this->assertFalse($tree->update_dependency_id('toads', 9, 13));
563 $this->assertFalse($tree->update_dependency_id('frogs', 7, 8));
564 $this->assertEquals($before, $tree->save());
566 // Replace the zombies one.
567 $this->assertTrue($tree->update_dependency_id('zombies', 9, 666));
568 $after = $tree->save();
569 $this->assertEquals(666, $after->c
[0]->c
[1]->id
);
571 // And the frogs one.
572 $this->assertTrue($tree->update_dependency_id('frogs', 9, 3));
573 $after = $tree->save();
574 $this->assertEquals(3, $after->c
[0]->c
[0]->id
);
575 $this->assertEquals(3, $after->c
[1]->id
);
579 * Tests the filter_users function.
581 public function test_filter_users() {
582 $info = new \core_availability\
mock_info();
583 $checker = new capability_checker($info->get_context());
585 // Don't need to create real users in database, just use these ids.
586 $users = array(1 => null, 2 => null, 3 => null);
588 // Test basic tree with one condition that doesn't filter.
589 $structure = tree
::get_root_json(array(self
::mock(array())));
590 $tree = new tree($structure);
591 $result = $tree->filter_user_list($users, false, $info, $checker);
593 $this->assertEquals(array(1, 2, 3), array_keys($result));
595 // Now a tree with one condition that filters.
596 $structure = tree
::get_root_json(array(self
::mock(array('filter' => array(2, 3)))));
597 $tree = new tree($structure);
598 $result = $tree->filter_user_list($users, false, $info, $checker);
600 $this->assertEquals(array(2, 3), array_keys($result));
602 // Tree with two conditions that both filter (|).
603 $structure = tree
::get_root_json(array(
604 self
::mock(array('filter' => array(3))),
605 self
::mock(array('filter' => array(1)))), tree
::OP_OR
);
606 $tree = new tree($structure);
607 $result = $tree->filter_user_list($users, false, $info, $checker);
609 $this->assertEquals(array(1, 3), array_keys($result));
611 // Tree with OR condition one of which doesn't filter.
612 $structure = tree
::get_root_json(array(
613 self
::mock(array('filter' => array(3))),
614 self
::mock(array())), tree
::OP_OR
);
615 $tree = new tree($structure);
616 $result = $tree->filter_user_list($users, false, $info, $checker);
618 $this->assertEquals(array(1, 2, 3), array_keys($result));
620 // Tree with two condition that both filter (&).
621 $structure = tree
::get_root_json(array(
622 self
::mock(array('filter' => array(2, 3))),
623 self
::mock(array('filter' => array(1, 2)))));
624 $tree = new tree($structure);
625 $result = $tree->filter_user_list($users, false, $info, $checker);
627 $this->assertEquals(array(2), array_keys($result));
629 // Tree with child tree with NOT condition.
630 $structure = tree
::get_root_json(array(
631 tree
::get_nested_json(array(
632 self
::mock(array('filter' => array(1)))), tree
::OP_NOT_AND
)));
633 $tree = new tree($structure);
634 $result = $tree->filter_user_list($users, false, $info, $checker);
636 $this->assertEquals(array(2, 3), array_keys($result));
640 * Tests the get_json methods in tree (which are mainly for use in testing
641 * but might be used elsewhere).
643 public function test_get_json() {
644 // Create a simple child object (fake).
645 $child = (object)array('type' => 'fake');
646 $childstr = json_encode($child);
650 (object)array('op' => '&', 'c' => array()),
651 tree
::get_nested_json(array()));
652 // Children and different operator.
654 (object)array('op' => '|', 'c' => array($child, $child)),
655 tree
::get_nested_json(array($child, $child), tree
::OP_OR
));
658 $this->assertEquals('{"op":"&","c":[],"showc":[]}',
659 json_encode(tree
::get_root_json(array(), tree
::OP_AND
)));
660 // Root with children (multi-show operator).
661 $this->assertEquals('{"op":"&","c":[' . $childstr . ',' . $childstr .
662 '],"showc":[true,true]}',
663 json_encode(tree
::get_root_json(array($child, $child), tree
::OP_AND
)));
664 // Root with children (single-show operator).
665 $this->assertEquals('{"op":"|","c":[' . $childstr . ',' . $childstr .
667 json_encode(tree
::get_root_json(array($child, $child), tree
::OP_OR
)));
668 // Root with children (specified show boolean).
669 $this->assertEquals('{"op":"&","c":[' . $childstr . ',' . $childstr .
670 '],"showc":[false,false]}',
671 json_encode(tree
::get_root_json(array($child, $child), tree
::OP_AND
, false)));
672 // Root with children (specified show array).
673 $this->assertEquals('{"op":"&","c":[' . $childstr . ',' . $childstr .
674 '],"showc":[true,false]}',
675 json_encode(tree
::get_root_json(array($child, $child), tree
::OP_AND
, array(true, false))));
679 * Tests the behaviour of the counter in unique_sql_parameter().
681 * There was a problem with static counters used to implement a sequence of
682 * parameter placeholders (MDL-53481). As always with static variables, it
683 * is a bit tricky to unit test the behaviour reliably as it depends on the
684 * actual tests executed and also their order.
686 * To minimise risk of false expected behaviour, this test method should be
687 * first one where {@link core_availability\tree::get_user_list_sql()} is
688 * used. We also use higher number of condition instances to increase the
689 * risk of the counter collision, should there remain a problem.
691 public function test_unique_sql_parameter_behaviour() {
693 $this->resetAfterTest();
694 $generator = $this->getDataGenerator();
696 // Create a test course with multiple groupings and groups and a student in each of them.
697 $course = $generator->create_course();
698 $user = $generator->create_user();
699 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
700 $generator->enrol_user($user->id
, $course->id
, $studentroleid);
701 // The total number of groupings and groups must not be greater than 61.
702 // There is a limit in MySQL on the max number of joined tables.
704 for ($i = 0; $i < 25; $i++
) {
705 $group = $generator->create_group(array('courseid' => $course->id
));
706 groups_add_member($group, $user);
710 for ($i = 0; $i < 25; $i++
) {
711 $groupings[] = $generator->create_grouping(array('courseid' => $course->id
));
713 foreach ($groupings as $grouping) {
714 foreach ($groups as $group) {
715 groups_assign_grouping($grouping->id
, $group->id
);
718 $info = new \core_availability\
mock_info($course);
720 // Make a huge tree with 'AND' of all groups and groupings conditions.
722 foreach ($groups as $group) {
723 $conditions[] = \availability_group\condition
::get_json($group->id
);
725 foreach ($groupings as $groupingid) {
726 $conditions[] = \availability_grouping\condition
::get_json($grouping->id
);
728 shuffle($conditions);
729 $tree = new tree(tree
::get_root_json($conditions));
730 list($sql, $params) = $tree->get_user_list_sql(false, $info, false);
731 // This must not throw exception.
732 $DB->fix_sql_params($sql, $params);
736 * Tests get_user_list_sql.
738 public function test_get_user_list_sql() {
740 $this->resetAfterTest();
741 $generator = $this->getDataGenerator();
743 // Create a test course with 2 groups and users in each combination of them.
744 $course = $generator->create_course();
745 $group1 = $generator->create_group(array('courseid' => $course->id
));
746 $group2 = $generator->create_group(array('courseid' => $course->id
));
747 $userin1 = $generator->create_user();
748 $userin2 = $generator->create_user();
749 $userinboth = $generator->create_user();
750 $userinneither = $generator->create_user();
751 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
752 foreach (array($userin1, $userin2, $userinboth, $userinneither) as $user) {
753 $generator->enrol_user($user->id
, $course->id
, $studentroleid);
755 groups_add_member($group1, $userin1);
756 groups_add_member($group2, $userin2);
757 groups_add_member($group1, $userinboth);
758 groups_add_member($group2, $userinboth);
759 $info = new \core_availability\
mock_info($course);
761 // Tree with single group condition.
762 $tree = new tree(tree
::get_root_json(array(
763 \availability_group\condition
::get_json($group1->id
)
765 list($sql, $params) = $tree->get_user_list_sql(false, $info, false);
766 $result = $DB->get_fieldset_sql($sql, $params);
768 $this->assertEquals(array($userin1->id
, $userinboth->id
), $result);
770 // Tree with 'AND' of both group conditions.
771 $tree = new tree(tree
::get_root_json(array(
772 \availability_group\condition
::get_json($group1->id
),
773 \availability_group\condition
::get_json($group2->id
)
775 list($sql, $params) = $tree->get_user_list_sql(false, $info, false);
776 $result = $DB->get_fieldset_sql($sql, $params);
778 $this->assertEquals(array($userinboth->id
), $result);
780 // Tree with 'AND' of both group conditions.
781 $tree = new tree(tree
::get_root_json(array(
782 \availability_group\condition
::get_json($group1->id
),
783 \availability_group\condition
::get_json($group2->id
)
785 list($sql, $params) = $tree->get_user_list_sql(false, $info, false);
786 $result = $DB->get_fieldset_sql($sql, $params);
788 $this->assertEquals(array($userin1->id
, $userin2->id
, $userinboth->id
), $result);
790 // Check with flipped logic (NOT above level of tree).
791 list($sql, $params) = $tree->get_user_list_sql(true, $info, false);
792 $result = $DB->get_fieldset_sql($sql, $params);
794 $this->assertEquals(array($userinneither->id
), $result);
796 // Tree with 'OR' of group conditions and a non-filtering condition.
797 // The non-filtering condition should mean that ALL users are included.
798 $tree = new tree(tree
::get_root_json(array(
799 \availability_group\condition
::get_json($group1->id
),
800 \availability_date\condition
::get_json(\availability_date\condition
::DIRECTION_UNTIL
, 3)
802 list($sql, $params) = $tree->get_user_list_sql(false, $info, false);
803 $this->assertEquals('', $sql);
804 $this->assertEquals(array(), $params);
808 * Utility function to build the PHP structure representing a mock condition.
810 * @param array $params Mock parameters
811 * @return \stdClass Structure object
813 protected static function mock(array $params) {
814 $params['type'] = 'mock';
815 return (object)$params;