/
var
/
www
/
html
/
cetesb
/
availability
/
tests
/
Upload File
HOME
<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Unit tests for the condition tree class and related logic. * * @package core_availability * @copyright 2014 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_availability\capability_checker; use \core_availability\tree; defined('MOODLE_INTERNAL') || die(); /** * Unit tests for the condition tree class and related logic. * * @package core_availability * @copyright 2014 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class tree_testcase extends \advanced_testcase { public function setUp() { // Load the mock classes so they can be used. require_once(__DIR__ . '/fixtures/mock_condition.php'); require_once(__DIR__ . '/fixtures/mock_info.php'); } /** * Tests constructing a tree with errors. */ public function test_construct_errors() { try { new \core_availability\tree('frog'); $this->fail(); } catch (coding_exception $e) { $this->assertContains('not object', $e->getMessage()); } try { new \core_availability\tree((object)array()); $this->fail(); } catch (coding_exception $e) { $this->assertContains('missing ->op', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '*')); $this->fail(); } catch (coding_exception $e) { $this->assertContains('unknown ->op', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|')); $this->fail(); } catch (coding_exception $e) { $this->assertContains('missing ->show', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|', 'show' => 0)); $this->fail(); } catch (coding_exception $e) { $this->assertContains('->show not bool', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '&')); $this->fail(); } catch (coding_exception $e) { $this->assertContains('missing ->showc', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '&', 'showc' => 0)); $this->fail(); } catch (coding_exception $e) { $this->assertContains('->showc not array', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '&', 'showc' => array(0))); $this->fail(); } catch (coding_exception $e) { $this->assertContains('->showc value not bool', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|', 'show' => true)); $this->fail(); } catch (coding_exception $e) { $this->assertContains('missing ->c', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|', 'show' => true, 'c' => 'side')); $this->fail(); } catch (coding_exception $e) { $this->assertContains('->c not array', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|', 'show' => true, 'c' => array(3))); $this->fail(); } catch (coding_exception $e) { $this->assertContains('child not object', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|', 'show' => true, 'c' => array((object)array('type' => 'doesnotexist')))); $this->fail(); } catch (coding_exception $e) { $this->assertContains('Unknown condition type: doesnotexist', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '|', 'show' => true, 'c' => array((object)array()))); $this->fail(); } catch (coding_exception $e) { $this->assertContains('missing ->op', $e->getMessage()); } try { new \core_availability\tree((object)array('op' => '&', 'c' => array((object)array('op' => '&', 'c' => array())), 'showc' => array(true, true) )); $this->fail(); } catch (coding_exception $e) { $this->assertContains('->c, ->showc mismatch', $e->getMessage()); } } /** * Tests constructing a tree with plugin that does not exist (ignored). */ public function test_construct_ignore_missing_plugin() { // Construct a tree with & combination of one condition that doesn't exist. $tree = new \core_availability\tree(self::tree(array( (object)array('type' => 'doesnotexist'))), true); // Expected result is an empty tree with | condition, shown. $this->assertEquals('+|()', (string)$tree); } /** * Tests constructing a tree with subtrees using all available operators. */ public function test_construct_just_trees() { $structure = self::tree(array( self::tree(array()), self::tree(array( self::tree(array(), '!|')), '!&')), '&', null, array(true, true)); $tree = new \core_availability\tree($structure); $this->assertEquals('&(+|(),+!&(!|()))', (string)$tree); } /** * Tests constructing tree using the mock plugin. */ public function test_construct_with_mock_plugin() { $structure = self::tree(array( self::mock(array('a' => true, 'm' => '')))); $tree = new \core_availability\tree($structure); $this->assertEquals('+|({mock:y,})', (string)$tree); } /** * Tests the check_available and get_result_information functions. */ public function test_check_available() { global $USER; // Setup. $this->resetAfterTest(); $info = new \core_availability\mock_info(); $this->setAdminUser(); $information = ''; // No conditions. $structure = self::tree(array()); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); // One condition set to yes. $structure->c = array( self::mock(array('a' => true))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); // One condition set to no. $structure->c = array( self::mock(array('a' => false, 'm' => 'no'))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertEquals('SA: no', $information); // Two conditions, OR, resolving as true. $structure->c = array( self::mock(array('a' => false, 'm' => 'no')), self::mock(array('a' => true))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); $this->assertEquals('', $information); // Two conditions, OR, resolving as false. $structure->c = array( self::mock(array('a' => false, 'm' => 'no')), self::mock(array('a' => false, 'm' => 'way'))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertRegExp('~any of.*no.*way~', $information); // Two conditions, OR, resolving as false, no display. $structure->show = false; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertEquals('', $information); // Two conditions, AND, resolving as true. $structure->op = '&'; unset($structure->show); $structure->showc = array(true, true); $structure->c = array( self::mock(array('a' => true)), self::mock(array('a' => true))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); // Two conditions, AND, one false. $structure->c = array( self::mock(array('a' => false, 'm' => 'wom')), self::mock(array('a' => true, 'm' => ''))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertEquals('SA: wom', $information); // Two conditions, AND, both false. $structure->c = array( self::mock(array('a' => false, 'm' => 'wom')), self::mock(array('a' => false, 'm' => 'bat'))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertRegExp('~wom.*bat~', $information); // Two conditions, AND, both false, show turned off for one. When // show is turned off, that means if you don't have that condition // you don't get to see anything at all. $structure->showc[0] = false; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertEquals('', $information); $structure->showc[0] = true; // Two conditions, NOT OR, both false. $structure->op = '!|'; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); // Two conditions, NOT OR, one true. $structure->c[0]->a = true; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertEquals('SA: !wom', $information); // Two conditions, NOT OR, both true. $structure->c[1]->a = true; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertRegExp('~!wom.*!bat~', $information); // Two conditions, NOT AND, both true. $structure->op = '!&'; unset($structure->showc); $structure->show = true; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertRegExp('~any of.*!wom.*!bat~', $information); // Two conditions, NOT AND, one true. $structure->c[1]->a = false; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); // Nested NOT conditions; true. $structure->c = array( self::tree(array( self::mock(array('a' => true, 'm' => 'no'))), '!&')); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertTrue($available); // Nested NOT conditions; false (note no ! in message). $structure->c[0]->c[0]->a = false; list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertEquals('SA: no', $information); // Nested condition groups, message test. $structure->op = '|'; $structure->c = array( self::tree(array( self::mock(array('a' => false, 'm' => '1')), self::mock(array('a' => false, 'm' => '2')) ), '&', null, array(true, true)), self::mock(array('a' => false, 'm' => 3))); list ($available, $information) = $this->get_available_results( $structure, $info, $USER->id); $this->assertFalse($available); $this->assertRegExp('~<ul.*<ul.*<li.*1.*<li.*2.*</ul>.*<li.*3~', $information); } /** * Shortcut function to check availability and also get information. * * @param stdClass $structure Tree structure * @param \core_availability\info $info Location info * @param int $userid User id */ protected function get_available_results($structure, \core_availability\info $info, $userid) { $tree = new \core_availability\tree($structure); $result = $tree->check_available(false, $info, true, $userid); return array($result->is_available(), $tree->get_result_information($info, $result)); } /** * Tests the is_available_for_all() function. */ public function test_is_available_for_all() { // Empty tree is always available. $structure = self::tree(array()); $tree = new \core_availability\tree($structure); $this->assertTrue($tree->is_available_for_all()); // Tree with normal item in it, not always available. $structure->c[0] = (object)array('type' => 'mock'); $tree = new \core_availability\tree($structure); $this->assertFalse($tree->is_available_for_all()); // OR tree with one always-available item. $structure->c[1] = self::mock(array('all' => true)); $tree = new \core_availability\tree($structure); $this->assertTrue($tree->is_available_for_all()); // AND tree with one always-available and one not. $structure->op = '&'; $structure->showc = array(true, true); unset($structure->show); $tree = new \core_availability\tree($structure); $this->assertFalse($tree->is_available_for_all()); // Test NOT conditions (items not always-available). $structure->op = '!&'; $structure->show = true; unset($structure->showc); $tree = new \core_availability\tree($structure); $this->assertFalse($tree->is_available_for_all()); // Test again with one item always-available for NOT mode. $structure->c[1]->allnot = true; $tree = new \core_availability\tree($structure); $this->assertTrue($tree->is_available_for_all()); } /** * Tests the get_full_information() function. */ public function test_get_full_information() { // Setup. $info = new \core_availability\mock_info(); // No conditions. $structure = self::tree(array()); $tree = new \core_availability\tree($structure); $this->assertEquals('', $tree->get_full_information($info)); // Condition (normal and NOT). $structure->c = array( self::mock(array('m' => 'thing'))); $tree = new \core_availability\tree($structure); $this->assertEquals('SA: [FULL]thing', $tree->get_full_information($info)); $structure->op = '!&'; $tree = new \core_availability\tree($structure); $this->assertEquals('SA: ![FULL]thing', $tree->get_full_information($info)); // Complex structure. $structure->op = '|'; $structure->c = array( self::tree(array( self::mock(array('m' => '1')), self::mock(array('m' => '2'))), '&'), self::mock(array('m' => 3))); $tree = new \core_availability\tree($structure); $this->assertRegExp('~<ul.*<ul.*<li.*1.*<li.*2.*</ul>.*<li.*3~', $tree->get_full_information($info)); // Test intro messages before list. First, OR message. $structure->c = array( self::mock(array('m' => '1')), self::mock(array('m' => '2')) ); $tree = new \core_availability\tree($structure); $this->assertRegExp('~Not available unless any of:.*<ul>~', $tree->get_full_information($info)); // Now, OR message when not shown. $structure->show = false; $tree = new \core_availability\tree($structure); $this->assertRegExp('~hidden.*<ul>~', $tree->get_full_information($info)); // AND message. $structure->op = '&'; unset($structure->show); $structure->showc = array(false, false); $tree = new \core_availability\tree($structure); $this->assertRegExp('~Not available unless:.*<ul>~', $tree->get_full_information($info)); // Hidden markers on items. $this->assertRegExp('~1.*hidden.*2.*hidden~', $tree->get_full_information($info)); // Hidden markers on child tree and items. $structure->c[1] = self::tree(array( self::mock(array('m' => '2')), self::mock(array('m' => '3'))), '&'); $tree = new \core_availability\tree($structure); $this->assertRegExp('~1.*hidden.*All of \(hidden.*2.*3~', $tree->get_full_information($info)); $structure->c[1]->op = '|'; $tree = new \core_availability\tree($structure); $this->assertRegExp('~1.*hidden.*Any of \(hidden.*2.*3~', $tree->get_full_information($info)); // Hidden markers on single-item display, AND and OR. $structure->showc = array(false); $structure->c = array( self::mock(array('m' => '1')) ); $tree = new \core_availability\tree($structure); $this->assertRegExp('~1.*hidden~', $tree->get_full_information($info)); unset($structure->showc); $structure->show = false; $structure->op = '|'; $tree = new \core_availability\tree($structure); $this->assertRegExp('~1.*hidden~', $tree->get_full_information($info)); // Hidden marker if single item is tree. $structure->c[0] = self::tree(array( self::mock(array('m' => '1')), self::mock(array('m' => '2'))), '&', null); $tree = new \core_availability\tree($structure); $this->assertRegExp('~Not available \(hidden.*1.*2~', $tree->get_full_information($info)); // Single item tree containing single item. unset($structure->c[0]->c[1]); $tree = new \core_availability\tree($structure); $this->assertRegExp('~SA.*1.*hidden~', $tree->get_full_information($info)); } /** * Tests the is_empty() function. */ public function test_is_empty() { // Tree with nothing in should be empty. $structure = self::tree(array()); $tree = new tree($structure); $this->assertTrue($tree->is_empty()); // Tree with something in is not empty. $structure = self::tree(array(self::mock(array('m' => '1')))); $tree = new tree($structure); $this->assertFalse($tree->is_empty()); } /** * Tests the get_all_children() function. */ public function test_get_all_children() { // Create a tree with nothing in. $structure = self::tree(array()); $tree1 = new \core_availability\tree($structure); // Create second tree with complex structure. $structure->c = array( self::tree(array( self::mock(array('m' => '1')), self::mock(array('m' => '2')) )), self::mock(array('m' => 3))); $tree2 = new \core_availability\tree($structure); // Check list of conditions from both trees. $this->assertEquals(array(), $tree1->get_all_children('core_availability\condition')); $result = $tree2->get_all_children('core_availability\condition'); $this->assertEquals(3, count($result)); $this->assertEquals('{mock:n,1}', (string)$result[0]); $this->assertEquals('{mock:n,2}', (string)$result[1]); $this->assertEquals('{mock:n,3}', (string)$result[2]); // Check specific type, should give same results. $result2 = $tree2->get_all_children('availability_mock\condition'); $this->assertEquals($result, $result2); } /** * Tests the update_dependency_id() function. */ public function test_update_dependency_id() { // Create tree with structure of 3 mocks. $structure = self::tree(array( self::tree(array( self::mock(array('table' => 'frogs', 'id' => 9)), self::mock(array('table' => 'zombies', 'id' => 9)) )), self::mock(array('table' => 'frogs', 'id' => 9)))); // Get 'before' value. $tree = new \core_availability\tree($structure); $before = $tree->save(); // Try replacing a table or id that isn't used. $this->assertFalse($tree->update_dependency_id('toads', 9, 13)); $this->assertFalse($tree->update_dependency_id('frogs', 7, 8)); $this->assertEquals($before, $tree->save()); // Replace the zombies one. $this->assertTrue($tree->update_dependency_id('zombies', 9, 666)); $after = $tree->save(); $this->assertEquals(666, $after->c[0]->c[1]->id); // And the frogs one. $this->assertTrue($tree->update_dependency_id('frogs', 9, 3)); $after = $tree->save(); $this->assertEquals(3, $after->c[0]->c[0]->id); $this->assertEquals(3, $after->c[1]->id); } /** * Tests the filter_users function. */ public function test_filter_users() { $info = new \core_availability\mock_info(); $checker = new capability_checker($info->get_context()); // Don't need to create real users in database, just use these ids. $users = array(1 => null, 2 => null, 3 => null); // Test basic tree with one condition that doesn't filter. $structure = self::tree(array(self::mock(array()))); $tree = new \core_availability\tree($structure); $result = $tree->filter_user_list($users, false, $info, $checker); ksort($result); $this->assertEquals(array(1, 2, 3), array_keys($result)); // Now a tree with one condition that filters. $structure = self::tree(array(self::mock(array('filter' => array(2, 3))))); $tree = new \core_availability\tree($structure); $result = $tree->filter_user_list($users, false, $info, $checker); ksort($result); $this->assertEquals(array(2, 3), array_keys($result)); // Tree with two conditions that both filter (|). $structure = self::tree(array( self::mock(array('filter' => array(3))), self::mock(array('filter' => array(1))))); $tree = new \core_availability\tree($structure); $result = $tree->filter_user_list($users, false, $info, $checker); ksort($result); $this->assertEquals(array(1, 3), array_keys($result)); // Tree with two condition that both filter (&). $structure = self::tree(array( self::mock(array('filter' => array(2, 3))), self::mock(array('filter' => array(1, 2)))), '&', false, array(true, true)); $tree = new \core_availability\tree($structure); $result = $tree->filter_user_list($users, false, $info, $checker); ksort($result); $this->assertEquals(array(2), array_keys($result)); // Tree with child tree with NOT condition. $structure = self::tree(array( self::tree( self::mock(array('filter' => array(1))), '!&', null))); $tree = new \core_availability\tree($structure); $result = $tree->filter_user_list($users, false, $info, $checker); ksort($result); $this->assertEquals(array(2, 3), array_keys($result)); } /** * Utility function to build the PHP structure representing a tree. * * @param array|tree_node $children Children or single child * @param string $op Operator * @param bool|null $show True/false show param or null if none * @param array|null $showc showc param or null if none * @return \stdClass Structure object */ protected static function tree($children, $op = '|', $show = true, $showc = null) { $structure = new stdClass(); $structure->op = $op; if (!is_array($children)) { $children = array($children); } $structure->c = $children; if ($showc !== null) { $structure->showc = $showc; } if ($show !== null) { $structure->show = $show; } return $structure; } /** * Utility function to build the PHP structure representing a mock condition. * * @param array $params Mock parameters * @return \stdClass Structure object */ protected static function mock(array $params) { $params['type'] = 'mock'; return (object)$params; } }