. /** * 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; 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('~