Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   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 availability_grouping;
  18  
  19  /**
  20   * Unit tests for the condition.
  21   *
  22   * @package availability_grouping
  23   * @copyright 2014 The Open University
  24   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  class condition_test extends \advanced_testcase {
  27      /**
  28       * Load required classes.
  29       */
  30      public function setUp(): void {
  31          // Load the mock info class so that it can be used.
  32          global $CFG;
  33          require_once($CFG->dirroot . '/availability/tests/fixtures/mock_info.php');
  34      }
  35  
  36      /**
  37       * Tests constructing and using condition.
  38       */
  39      public function test_usage() {
  40          global $CFG, $USER;
  41          $this->resetAfterTest();
  42          $CFG->enableavailability = true;
  43  
  44          // Erase static cache before test.
  45          condition::wipe_static_cache();
  46  
  47          // Make a test course and user.
  48          $generator = $this->getDataGenerator();
  49          $course = $generator->create_course();
  50          $user = $generator->create_user();
  51          $generator->enrol_user($user->id, $course->id);
  52          $info = new \core_availability\mock_info($course, $user->id);
  53  
  54          // Make a test grouping and group.
  55          $grouping = $generator->create_grouping(array('courseid' => $course->id,
  56                  'name' => 'Grouping!'));
  57          $group = $generator->create_group(array('courseid' => $course->id));
  58          groups_assign_grouping($grouping->id, $group->id);
  59  
  60          // Do test (not in grouping).
  61          $structure = (object)array('type' => 'grouping', 'id' => (int)$grouping->id);
  62          $cond = new condition($structure);
  63  
  64          // Check if available (when not available).
  65          $this->assertFalse($cond->is_available(false, $info, true, $user->id));
  66          $information = $cond->get_description(false, false, $info);
  67          $this->assertMatchesRegularExpression('~belong to a group in.*Grouping!~', $information);
  68          $this->assertTrue($cond->is_available(true, $info, true, $user->id));
  69  
  70          // Add user to grouping and refresh cache.
  71          groups_add_member($group, $user);
  72          get_fast_modinfo($course->id, 0, true);
  73  
  74          // Recheck.
  75          $this->assertTrue($cond->is_available(false, $info, true, $user->id));
  76          $this->assertFalse($cond->is_available(true, $info, true, $user->id));
  77          $information = $cond->get_description(false, true, $info);
  78          $this->assertMatchesRegularExpression('~do not belong to a group in.*Grouping!~', $information);
  79  
  80          // Admin user doesn't belong to the grouping, but they can access it
  81          // either way (positive or NOT) because of accessallgroups.
  82          $this->setAdminUser();
  83          $infoadmin = new \core_availability\mock_info($course, $USER->id);
  84          $this->assertTrue($cond->is_available(false, $infoadmin, true, $USER->id));
  85          $this->assertTrue($cond->is_available(true, $infoadmin, true, $USER->id));
  86  
  87          // Grouping that doesn't exist uses 'missing' text.
  88          $cond = new condition((object)array('id' => $grouping->id + 1000));
  89          $this->assertFalse($cond->is_available(false, $info, true, $user->id));
  90          $information = $cond->get_description(false, false, $info);
  91          $this->assertMatchesRegularExpression('~belong to a group in.*(Missing grouping)~', $information);
  92  
  93          // We need an actual cm object to test the 'grouping from cm' option.
  94          $pagegen = $generator->get_plugin_generator('mod_page');
  95          $page = $pagegen->create_instance(array('course' => $course->id,
  96                  'groupingid' => $grouping->id, 'availability' =>
  97                  '{"op":"|","show":true,"c":[{"type":"grouping","activity":true}]}'));
  98          rebuild_course_cache($course->id, true);
  99  
 100          // Check if available using the 'from course-module' grouping option.
 101          $modinfo = get_fast_modinfo($course, $user->id);
 102          $cm = $modinfo->get_cm($page->cmid);
 103          $info = new \core_availability\info_module($cm);
 104          $information = '';
 105          $this->assertTrue($info->is_available($information, false, $user->id));
 106  
 107          // Remove user from grouping again and recheck.
 108          groups_remove_member($group, $user);
 109          get_fast_modinfo($course->id, 0, true);
 110          $this->assertFalse($info->is_available($information, false, $user->id));
 111          $this->assertMatchesRegularExpression('~belong to a group in.*Grouping!~', $information);
 112      }
 113  
 114      /**
 115       * Tests the constructor including error conditions. Also tests the
 116       * string conversion feature (intended for debugging only).
 117       */
 118      public function test_constructor() {
 119          // No parameters.
 120          $structure = new \stdClass();
 121          try {
 122              $cond = new condition($structure);
 123              $this->fail();
 124          } catch (\coding_exception $e) {
 125              $this->assertStringContainsString('Missing ->id / ->activity', $e->getMessage());
 126          }
 127  
 128          // Invalid id (not int).
 129          $structure->id = 'bourne';
 130          try {
 131              $cond = new condition($structure);
 132              $this->fail();
 133          } catch (\coding_exception $e) {
 134              $this->assertStringContainsString('Invalid ->id', $e->getMessage());
 135          }
 136  
 137          // Invalid activity option (not bool).
 138          unset($structure->id);
 139          $structure->activity = 42;
 140          try {
 141              $cond = new condition($structure);
 142              $this->fail();
 143          } catch (\coding_exception $e) {
 144              $this->assertStringContainsString('Invalid ->activity', $e->getMessage());
 145          }
 146  
 147          // Invalid activity option (false).
 148          $structure->activity = false;
 149          try {
 150              $cond = new condition($structure);
 151              $this->fail();
 152          } catch (\coding_exception $e) {
 153              $this->assertStringContainsString('Invalid ->activity', $e->getMessage());
 154          }
 155  
 156          // Valid with id.
 157          $structure->id = 123;
 158          $cond = new condition($structure);
 159          $this->assertEquals('{grouping:#123}', (string)$cond);
 160  
 161          // Valid with activity.
 162          unset($structure->id);
 163          $structure->activity = true;
 164          $cond = new condition($structure);
 165          $this->assertEquals('{grouping:CM}', (string)$cond);
 166      }
 167  
 168      /**
 169       * Tests the save() function.
 170       */
 171      public function test_save() {
 172          $structure = (object)array('id' => 123);
 173          $cond = new condition($structure);
 174          $structure->type = 'grouping';
 175          $this->assertEquals($structure, $cond->save());
 176  
 177          $structure = (object)array('activity' => true);
 178          $cond = new condition($structure);
 179          $structure->type = 'grouping';
 180          $this->assertEquals($structure, $cond->save());
 181      }
 182  
 183      /**
 184       * Tests the update_dependency_id() function.
 185       */
 186      public function test_update_dependency_id() {
 187          $cond = new condition((object)array('id' => 123));
 188          $this->assertFalse($cond->update_dependency_id('frogs', 123, 456));
 189          $this->assertFalse($cond->update_dependency_id('groupings', 12, 34));
 190          $this->assertTrue($cond->update_dependency_id('groupings', 123, 456));
 191          $after = $cond->save();
 192          $this->assertEquals(456, $after->id);
 193  
 194          $cond = new condition((object)array('activity' => true));
 195          $this->assertFalse($cond->update_dependency_id('frogs', 123, 456));
 196      }
 197  
 198      /**
 199       * Tests the filter_users (bulk checking) function. Also tests the SQL
 200       * variant get_user_list_sql.
 201       */
 202      public function test_filter_users() {
 203          global $DB, $CFG;
 204          $this->resetAfterTest();
 205          $CFG->enableavailability = true;
 206  
 207          // Erase static cache before test.
 208          condition::wipe_static_cache();
 209  
 210          // Make a test course and some users.
 211          $generator = $this->getDataGenerator();
 212          $course = $generator->create_course();
 213          $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
 214          $teacher = $generator->create_user();
 215          $generator->enrol_user($teacher->id, $course->id, $roleids['editingteacher']);
 216          $allusers = array($teacher->id => $teacher);
 217          $students = array();
 218          for ($i = 0; $i < 3; $i++) {
 219              $student = $generator->create_user();
 220              $students[$i] = $student;
 221              $generator->enrol_user($student->id, $course->id, $roleids['student']);
 222              $allusers[$student->id] = $student;
 223          }
 224          $info = new \core_availability\mock_info($course);
 225          $checker = new \core_availability\capability_checker($info->get_context());
 226  
 227          // Make test groups.
 228          $group1 = $generator->create_group(array('courseid' => $course->id));
 229          $group2 = $generator->create_group(array('courseid' => $course->id));
 230          $grouping1 = $generator->create_grouping(array('courseid' => $course->id));
 231          $grouping2 = $generator->create_grouping(array('courseid' => $course->id));
 232          groups_assign_grouping($grouping1->id, $group1->id);
 233          groups_assign_grouping($grouping2->id, $group2->id);
 234  
 235          // Make page in grouping 2.
 236          $pagegen = $generator->get_plugin_generator('mod_page');
 237          $page = $pagegen->create_instance(array('course' => $course->id,
 238                  'groupingid' => $grouping2->id, 'availability' =>
 239                  '{"op":"|","show":true,"c":[{"type":"grouping","activity":true}]}'));
 240  
 241          // Assign students to groups as follows (teacher is not in a group):
 242          // 0: no groups.
 243          // 1: in group 1/grouping 1.
 244          // 2: in group 2/grouping 2.
 245          groups_add_member($group1, $students[1]);
 246          groups_add_member($group2, $students[2]);
 247  
 248          // Test specific grouping.
 249          $cond = new condition((object)array('id' => (int)$grouping1->id));
 250          $result = array_keys($cond->filter_user_list($allusers, false, $info, $checker));
 251          ksort($result);
 252          $expected = array($teacher->id, $students[1]->id);
 253          $this->assertEquals($expected, $result);
 254  
 255          // Test it with get_user_list_sql.
 256          list ($sql, $params) = $cond->get_user_list_sql(false, $info, true);
 257          $result = $DB->get_fieldset_sql($sql, $params);
 258          sort($result);
 259          $this->assertEquals($expected, $result);
 260  
 261          // NOT test.
 262          $result = array_keys($cond->filter_user_list($allusers, true, $info, $checker));
 263          ksort($result);
 264          $expected = array($teacher->id, $students[0]->id, $students[2]->id);
 265          $this->assertEquals($expected, $result);
 266  
 267          // NOT with get_user_list_sql.
 268          list ($sql, $params) = $cond->get_user_list_sql(true, $info, true);
 269          $result = $DB->get_fieldset_sql($sql, $params);
 270          sort($result);
 271          $this->assertEquals($expected, $result);
 272  
 273          // Test course-module grouping.
 274          $modinfo = get_fast_modinfo($course);
 275          $cm = $modinfo->get_cm($page->cmid);
 276          $info = new \core_availability\info_module($cm);
 277          $result = array_keys($info->filter_user_list($allusers, $course));
 278          $expected = array($teacher->id, $students[2]->id);
 279          $this->assertEquals($expected, $result);
 280  
 281          // With get_user_list_sql.
 282          list ($sql, $params) = $info->get_user_list_sql(true);
 283          $result = $DB->get_fieldset_sql($sql, $params);
 284          sort($result);
 285          $this->assertEquals($expected, $result);
 286      }
 287  }