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_date;
  18  
  19  use core_availability\tree;
  20  
  21  /**
  22   * Unit tests for the date condition.
  23   *
  24   * @package availability_date
  25   * @copyright 2014 The Open University
  26   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  class condition_test extends \advanced_testcase {
  29      /**
  30       * Load required classes.
  31       */
  32      public function setUp(): void {
  33          // Load the mock info class so that it can be used.
  34          global $CFG;
  35          require_once($CFG->dirroot . '/availability/tests/fixtures/mock_info.php');
  36      }
  37  
  38      /**
  39       * Tests constructing and using date condition as part of tree.
  40       */
  41      public function test_in_tree() {
  42          global $SITE, $USER, $CFG;
  43          $this->resetAfterTest();
  44          $this->setAdminUser();
  45  
  46          // Set server timezone for test. (Important as otherwise the timezone
  47          // could be anything - this is modified by other unit tests, too.)
  48          $this->setTimezone('UTC');
  49  
  50          // SEt user to GMT+5.
  51          $USER->timezone = 5;
  52  
  53          // Construct tree with date condition.
  54          $time = strtotime('2014-02-18 14:20:00 GMT');
  55          $structure = (object)array('op' => '|', 'show' => true, 'c' => array(
  56                  (object)array('type' => 'date', 'd' => '>=', 't' => $time)));
  57          $tree = new \core_availability\tree($structure);
  58          $info = new \core_availability\mock_info();
  59  
  60          // Check if available (when not available).
  61          condition::set_current_time_for_test($time - 1);
  62          $information = '';
  63          $result = $tree->check_available(false, $info, true, $USER->id);
  64          $this->assertFalse($result->is_available());
  65          $information = $tree->get_result_information($info, $result);
  66  
  67          // Note: PM is normally upper-case, but an issue with PHP on Mac means
  68          // that on that platform, it is reported lower-case.
  69          $this->assertMatchesRegularExpression('~from.*18 February 2014, 7:20 (PM|pm)~', $information);
  70  
  71          // Check if available (when available).
  72          condition::set_current_time_for_test($time);
  73          $result = $tree->check_available(false, $info, true, $USER->id);
  74          $this->assertTrue($result->is_available());
  75          $information = $tree->get_result_information($info, $result);
  76          $this->assertEquals('', $information);
  77      }
  78  
  79      /**
  80       * Tests the constructor including error conditions. Also tests the
  81       * string conversion feature (intended for debugging only).
  82       */
  83      public function test_constructor() {
  84          // No parameters.
  85          $structure = (object)array();
  86          try {
  87              $date = new condition($structure);
  88              $this->fail();
  89          } catch (\coding_exception $e) {
  90              $this->assertStringContainsString('Missing or invalid ->d', $e->getMessage());
  91          }
  92  
  93          // Invalid ->d.
  94          $structure->d = 'woo hah!!';
  95          try {
  96              $date = new condition($structure);
  97              $this->fail();
  98          } catch (\coding_exception $e) {
  99              $this->assertStringContainsString('Missing or invalid ->d', $e->getMessage());
 100          }
 101  
 102          // Missing ->t.
 103          $structure->d = '>=';
 104          try {
 105              $date = new condition($structure);
 106              $this->fail();
 107          } catch (\coding_exception $e) {
 108              $this->assertStringContainsString('Missing or invalid ->t', $e->getMessage());
 109          }
 110  
 111          // Invalid ->t.
 112          $structure->t = 'got you all in check';
 113          try {
 114              $date = new condition($structure);
 115              $this->fail();
 116          } catch (\coding_exception $e) {
 117              $this->assertStringContainsString('Missing or invalid ->t', $e->getMessage());
 118          }
 119  
 120          // Valid conditions of both types.
 121          $structure = (object)array('d' => '>=', 't' => strtotime('2014-02-18 14:43:17 GMT'));
 122          $date = new condition($structure);
 123          $this->assertEquals('{date:>= 2014-02-18 14:43:17}', (string)$date);
 124          $structure->d = '<';
 125          $date = new condition($structure);
 126          $this->assertEquals('{date:< 2014-02-18 14:43:17}', (string)$date);
 127      }
 128  
 129      /**
 130       * Tests the save() function.
 131       */
 132      public function test_save() {
 133          $structure = (object)array('d' => '>=', 't' => 12345);
 134          $cond = new condition($structure);
 135          $structure->type = 'date';
 136          $this->assertEquals($structure, $cond->save());
 137      }
 138  
 139      /**
 140       * Tests the is_available() and is_available_to_all() functions.
 141       */
 142      public function test_is_available() {
 143          global $SITE, $USER;
 144  
 145          $time = strtotime('2014-02-18 14:50:10 GMT');
 146          $info = new \core_availability\mock_info();
 147  
 148          // Test with >=.
 149          $date = new condition((object)array('d' => '>=', 't' => $time));
 150          condition::set_current_time_for_test($time - 1);
 151          $this->assertFalse($date->is_available(false, $info, true, $USER->id));
 152          condition::set_current_time_for_test($time);
 153          $this->assertTrue($date->is_available(false, $info, true, $USER->id));
 154  
 155          // Test with <.
 156          $date = new condition((object)array('d' => '<', 't' => $time));
 157          condition::set_current_time_for_test($time);
 158          $this->assertFalse($date->is_available(false, $info, true, $USER->id));
 159          condition::set_current_time_for_test($time - 1);
 160          $this->assertTrue($date->is_available(false, $info, true, $USER->id));
 161  
 162          // Repeat this test with is_available_to_all() - it should be the same.
 163          $date = new condition((object)array('d' => '<', 't' => $time));
 164          condition::set_current_time_for_test($time);
 165          $this->assertFalse($date->is_available_for_all(false));
 166          condition::set_current_time_for_test($time - 1);
 167          $this->assertTrue($date->is_available_for_all(false));
 168      }
 169  
 170      /**
 171       * Tests the get_description and get_standalone_description functions.
 172       */
 173      public function test_get_description() {
 174          global $SITE, $CFG;
 175  
 176          $this->resetAfterTest();
 177          $this->setTimezone('UTC');
 178  
 179          $modinfo = get_fast_modinfo($SITE);
 180          $info = new \core_availability\mock_info();
 181          $time = strtotime('2014-02-18 14:55:01 GMT');
 182  
 183          // Test with >=.
 184          $date = new condition((object)array('d' => '>=', 't' => $time));
 185          $information = $date->get_description(true, false, $info);
 186          $this->assertMatchesRegularExpression('~after.*18 February 2014, 2:55 (PM|pm)~', $information);
 187          $information = $date->get_description(true, true, $info);
 188          $this->assertMatchesRegularExpression('~before.*18 February 2014, 2:55 (PM|pm)~', $information);
 189          $information = $date->get_standalone_description(true, false, $info);
 190          $this->assertMatchesRegularExpression('~from.*18 February 2014, 2:55 (PM|pm)~', $information);
 191          $information = $date->get_standalone_description(true, true, $info);
 192          $this->assertMatchesRegularExpression('~until.*18 February 2014, 2:55 (PM|pm)~', $information);
 193  
 194          // Test with <.
 195          $date = new condition((object)array('d' => '<', 't' => $time));
 196          $information = $date->get_description(true, false, $info);
 197          $this->assertMatchesRegularExpression('~before.*18 February 2014, 2:55 (PM|pm)~', $information);
 198          $information = $date->get_description(true, true, $info);
 199          $this->assertMatchesRegularExpression('~after.*18 February 2014, 2:55 (PM|pm)~', $information);
 200          $information = $date->get_standalone_description(true, false, $info);
 201          $this->assertMatchesRegularExpression('~until.*18 February 2014, 2:55 (PM|pm)~', $information);
 202          $information = $date->get_standalone_description(true, true, $info);
 203          $this->assertMatchesRegularExpression('~from.*18 February 2014, 2:55 (PM|pm)~', $information);
 204  
 205          // Test special case for dates that are midnight.
 206          $date = new condition((object)array('d' => '>=',
 207                  't' => strtotime('2014-03-05 00:00 GMT')));
 208          $information = $date->get_description(true, false, $info);
 209          $this->assertMatchesRegularExpression('~on or after.*5 March 2014([^0-9]*)$~', $information);
 210          $information = $date->get_description(true, true, $info);
 211          $this->assertMatchesRegularExpression('~before.*end of.*4 March 2014([^0-9]*)$~', $information);
 212          $information = $date->get_standalone_description(true, false, $info);
 213          $this->assertMatchesRegularExpression('~from.*5 March 2014([^0-9]*)$~', $information);
 214          $information = $date->get_standalone_description(true, true, $info);
 215          $this->assertMatchesRegularExpression('~until end of.*4 March 2014([^0-9]*)$~', $information);
 216  
 217          // In the 'until' case for midnight, it shows the previous day. (I.e.
 218          // if the date is 5 March 00:00, then we show it as available until 4
 219          // March, implying 'the end of'.)
 220          $date = new condition((object)array('d' => '<',
 221                  't' => strtotime('2014-03-05 00:00 GMT')));
 222          $information = $date->get_description(true, false, $info);
 223          $this->assertMatchesRegularExpression('~before end of.*4 March 2014([^0-9]*)$~', $information);
 224          $information = $date->get_description(true, true, $info);
 225          $this->assertMatchesRegularExpression('~on or after.*5 March 2014([^0-9]*)$~', $information);
 226          $information = $date->get_standalone_description(true, false, $info);
 227          $this->assertMatchesRegularExpression('~until end of.*4 March 2014([^0-9]*)$~', $information);
 228          $information = $date->get_standalone_description(true, true, $info);
 229          $this->assertMatchesRegularExpression('~from.*5 March 2014([^0-9]*)$~', $information);
 230      }
 231  
 232      /**
 233       * Tests the update_all_dates function.
 234       */
 235      public function test_update_all_dates() {
 236          global $DB;
 237          $this->resetAfterTest();
 238  
 239          // Create a course with 3 pages.
 240          $generator = $this->getDataGenerator();
 241          $course = $generator->create_course();
 242          $rec = array('course' => $course);
 243          $page1 = $generator->get_plugin_generator('mod_page')->create_instance($rec);
 244          $page2 = $generator->get_plugin_generator('mod_page')->create_instance($rec);
 245          $page3 = $generator->get_plugin_generator('mod_page')->create_instance($rec);
 246  
 247          // Set the availability page 2 to a simple date condition. You can access
 248          // it from 1337 onwards.
 249          $simplecondition = tree::get_root_json(array(
 250                  condition::get_json(condition::DIRECTION_FROM, 1337)));
 251          $DB->set_field('course_modules', 'availability',
 252                  json_encode($simplecondition), array('id' => $page2->cmid));
 253  
 254          // Set page 3 to a complex set of conditions including a nested date condition.
 255          // You can access it until 1459, *or* after 2810 if you belong to a group.
 256          $complexcondition = tree::get_root_json(array(
 257                  condition::get_json(condition::DIRECTION_UNTIL, 1459),
 258                  tree::get_nested_json(array(
 259                      condition::get_json(condition::DIRECTION_FROM, 2810),
 260                      \availability_group\condition::get_json()))),
 261                  tree::OP_OR);
 262          $DB->set_field('course_modules', 'availability',
 263                  json_encode($complexcondition), array('id' => $page3->cmid));
 264  
 265          // Now use the update_all_dates function to move date forward 100000.
 266          condition::update_all_dates($course->id, 100000);
 267  
 268          // Get the expected conditions after adjusting time, and compare to database.
 269          $simplecondition->c[0]->t = 101337;
 270          $complexcondition->c[0]->t = 101459;
 271          $complexcondition->c[1]->c[0]->t = 102810;
 272          $this->assertEquals($simplecondition, json_decode(
 273                  $DB->get_field('course_modules', 'availability', array('id' => $page2->cmid))));
 274          $this->assertEquals($complexcondition, json_decode(
 275                  $DB->get_field('course_modules', 'availability', array('id' => $page3->cmid))));
 276  
 277          // The one without availability conditions should still be null.
 278          $this->assertNull($DB->get_field('course_modules', 'availability', array('id' => $page1->cmid)));
 279      }
 280  }