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 tool_usertours;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once (__DIR__ . '/helper_trait.php');
  23  
  24  /**
  25   * Tests for cache.
  26   *
  27   * @package    tool_usertours
  28   * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class cache_test extends \advanced_testcase {
  32      // There are shared helpers for these tests in the helper trait.
  33      use \tool_usertours_helper_trait;
  34  
  35      /**
  36       * Test that get_enabled_tourdata does not return disabled tours.
  37       */
  38      public function test_get_enabled_tourdata_disabled() {
  39          $this->resetAfterTest();
  40  
  41          $tour = $this->helper_create_tour((object)['enabled' => false]);
  42          $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
  43  
  44          $matches = \tool_usertours\cache::get_enabled_tourdata();
  45          $this->assertEmpty($matches);
  46      }
  47  
  48      /**
  49       * Test that get_enabled_tourdata does not return an enabled but empty tour.
  50       */
  51      public function test_get_enabled_tourdata_enabled_no_steps() {
  52          $this->resetAfterTest();
  53  
  54          $this->helper_create_tour();
  55  
  56          $matches = \tool_usertours\cache::get_enabled_tourdata();
  57          $this->assertEmpty($matches);
  58      }
  59  
  60      /**
  61       * Test that get_enabled_tourdata returns a tour with steps.
  62       */
  63      public function test_get_enabled_tourdata_enabled() {
  64          $this->resetAfterTest();
  65  
  66          // Create two tours. Only the second has steps.
  67          $this->helper_create_tour();
  68          $tour2 = $this->helper_create_tour();
  69          $this->helper_create_step((object) ['tourid' => $tour2->get_id()]);
  70  
  71          $matches = \tool_usertours\cache::get_enabled_tourdata();
  72          $this->assertNotEmpty($matches);
  73          $this->assertCount(1, $matches);
  74  
  75          $match = array_shift($matches);
  76          $this->assertEquals($tour2->get_id(), $match->id);
  77      }
  78  
  79      /**
  80       * Test that get_enabled_tourdata returns tours in the correct sortorder
  81       */
  82      public function test_get_enabled_tourdata_enabled_sortorder() {
  83          $this->resetAfterTest();
  84  
  85          $tour1 = $this->helper_create_tour();
  86          $this->helper_create_step((object) ['tourid' => $tour1->get_id()]);
  87          $tour2 = $this->helper_create_tour();
  88          $this->helper_create_step((object) ['tourid' => $tour2->get_id()]);
  89  
  90          $matches = \tool_usertours\cache::get_enabled_tourdata();
  91          $this->assertNotEmpty($matches);
  92          $this->assertCount(2, $matches);
  93  
  94          $match = array_shift($matches);
  95          $this->assertEquals($tour1->get_id(), $match->id);
  96          $match = array_shift($matches);
  97          $this->assertEquals($tour2->get_id(), $match->id);
  98      }
  99  
 100      /**
 101       * Test that caching prevents additional DB reads.
 102       */
 103      public function test_get_enabled_tourdata_single_fetch() {
 104          global $DB;
 105  
 106          $this->resetAfterTest();
 107  
 108          $tour1 = $this->helper_create_tour();
 109          $this->helper_create_step((object) ['tourid' => $tour1->get_id()]);
 110          $tour2 = $this->helper_create_tour();
 111          $this->helper_create_step((object) ['tourid' => $tour2->get_id()]);
 112  
 113          // Only one read for the first call.
 114          $startreads = $DB->perf_get_reads();
 115          $matches = \tool_usertours\cache::get_enabled_tourdata();
 116          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 117  
 118          // No subsequent reads for any further calls.
 119          $matches = \tool_usertours\cache::get_enabled_tourdata();
 120          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 121  
 122      }
 123  
 124      /**
 125       * Data provider for get_matching_tourdata.
 126       *
 127       * @return  array
 128       */
 129      public function get_matching_tourdata_provider() {
 130          $tourconfigs = [
 131              (object) [
 132                  'name' => 'my_exact_1',
 133                  'pathmatch' => '/my/view.php'
 134              ],
 135              (object) [
 136                  'name' => 'my_failed_regex',
 137                  'pathmatch' => '/my/*.php'
 138              ],
 139              (object) [
 140                  'name' => 'my_glob_1',
 141                  'pathmatch' => '/my/%'
 142              ],
 143              (object) [
 144                  'name' => 'my_glob_2',
 145                  'pathmatch' => '/my/%'
 146              ],
 147              (object) [
 148                  'name' => 'frontpage_only',
 149                  'pathmatch' => 'FRONTPAGE'
 150              ],
 151              (object) [
 152                  'name' => 'frontpage_match',
 153                  'pathmatch' => '/?%'
 154              ],
 155          ];
 156  
 157          return [
 158              'Matches expected glob' => [
 159                  $tourconfigs,
 160                  '/my/index.php',
 161                  ['my_glob_1', 'my_glob_2'],
 162              ],
 163              'Matches expected glob and exact' => [
 164                  $tourconfigs,
 165                  '/my/view.php',
 166                  ['my_exact_1', 'my_glob_1', 'my_glob_2'],
 167              ],
 168              'Special constant FRONTPAGE must match front page only' => [
 169                  $tourconfigs,
 170                  '/',
 171                  ['frontpage_only'],
 172              ],
 173              'Standard frontpage URL matches both the special constant, and a correctly formed pathmatch' => [
 174                  $tourconfigs,
 175                  '/?redirect=0',
 176                  ['frontpage_only', 'frontpage_match'],
 177              ],
 178          ];
 179      }
 180  
 181      /**
 182       * Tests for the get_matching_tourdata function.
 183       *
 184       * @dataProvider    get_matching_tourdata_provider
 185       * @param   array   $tourconfigs    The configuration for the tours to create
 186       * @param   string  $targetmatch    The match to be tested
 187       * @param   array   $expected       An array containing the ordered names of the expected tours
 188       */
 189      public function test_get_matching_tourdata($tourconfigs, $targetmatch, $expected) {
 190          $this->resetAfterTest();
 191          foreach ($tourconfigs as $tourconfig) {
 192              $tour = $this->helper_create_tour($tourconfig);
 193              $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 194          }
 195  
 196          $matches = \tool_usertours\cache::get_matching_tourdata(new \moodle_url($targetmatch));
 197          $this->assertCount(count($expected), $matches);
 198  
 199          for ($i = 0; $i < count($matches); $i++) {
 200              $match = array_shift($matches);
 201              $this->assertEquals($expected[$i], $match->name);
 202          }
 203      }
 204  
 205      /**
 206       * Test that notify_tour_change clears the cache.
 207       */
 208      public function test_notify_tour_change() {
 209          global $DB;
 210  
 211          $this->resetAfterTest();
 212  
 213          $tour1 = $this->helper_create_tour();
 214          $this->helper_create_step((object) ['tourid' => $tour1->get_id()]);
 215          $tour2 = $this->helper_create_tour();
 216          $this->helper_create_step((object) ['tourid' => $tour2->get_id()]);
 217  
 218          // Only one read for the first call.
 219          $startreads = $DB->perf_get_reads();
 220          $matches = \tool_usertours\cache::get_enabled_tourdata();
 221          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 222  
 223          // No subsequent reads for any further calls.
 224          $matches = \tool_usertours\cache::get_enabled_tourdata();
 225          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 226  
 227          // Reset.
 228          \tool_usertours\cache::notify_tour_change();
 229  
 230          // An additional DB read now.
 231          $startreads = $DB->perf_get_reads();
 232          $matches = \tool_usertours\cache::get_enabled_tourdata();
 233          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 234      }
 235  
 236      /**
 237       * Test that get_stepdata returns an empty array when no steps were found.
 238       */
 239      public function test_get_stepdata_no_steps() {
 240          $this->resetAfterTest();
 241  
 242          $tour = $this->helper_create_tour((object)['enabled' => false]);
 243  
 244          $data = \tool_usertours\cache::get_stepdata($tour->get_id());
 245          $this->assertIsArray($data);
 246          $this->assertEmpty($data);
 247      }
 248  
 249      /**
 250       * Test that get_stepdata returns an empty array when no steps were found.
 251       */
 252      public function test_get_stepdata_correct_tour() {
 253          $this->resetAfterTest();
 254  
 255          $tour1 = $this->helper_create_tour((object)['enabled' => false]);
 256          $this->helper_create_step((object) ['tourid' => $tour1->get_id()]);
 257          $this->helper_create_step((object) ['tourid' => $tour1->get_id()]);
 258          $this->helper_create_step((object) ['tourid' => $tour1->get_id()]);
 259          $tour2 = $this->helper_create_tour((object)['enabled' => false]);
 260  
 261          $data = \tool_usertours\cache::get_stepdata($tour1->get_id());
 262          $this->assertIsArray($data);
 263          $this->assertCount(3, $data);
 264  
 265          $data = \tool_usertours\cache::get_stepdata($tour2->get_id());
 266          $this->assertIsArray($data);
 267          $this->assertEmpty($data);
 268      }
 269  
 270      /**
 271       * Test that get_stepdata returns an array containing multiple steps in
 272       * the same order.
 273       *
 274       * This is very difficult to determine because the act of changing the
 275       * order will likely change the DB natural sorting.
 276       */
 277      public function test_get_stepdata_ordered_steps() {
 278          $this->resetAfterTest();
 279  
 280          $tour = $this->helper_create_tour((object)['enabled' => false]);
 281          $steps = [];
 282          $steps[] = $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 283          $steps[] = $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 284          $steps[] = $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 285          $steps[] = $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 286          $steps[0]->set_sortorder(10)->persist();
 287  
 288          $data = \tool_usertours\cache::get_stepdata($tour->get_id());
 289          $this->assertIsArray($data);
 290          $this->assertCount(4, $data);
 291  
 292          // Re-order the steps.
 293          usort($steps, function($a, $b) {
 294              return ($a->get_sortorder() < $b->get_sortorder()) ? -1 : 1;
 295          });
 296  
 297          for ($i = 0; $i < count($data); $i++) {
 298              $step = array_shift($data);
 299              $this->assertEquals($steps[$i]->get_id(), $step->id);
 300          }
 301      }
 302  
 303      /**
 304       * Test that caching prevents additional DB reads.
 305       */
 306      public function test_get_stepdata_single_fetch() {
 307          global $DB;
 308  
 309          $this->resetAfterTest();
 310  
 311          $tour = $this->helper_create_tour();
 312          $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 313  
 314          // Only one read for the first call.
 315          $startreads = $DB->perf_get_reads();
 316          $matches = \tool_usertours\cache::get_stepdata($tour->get_id());
 317          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 318  
 319          // No subsequent reads for any further calls.
 320          $matches = \tool_usertours\cache::get_stepdata($tour->get_id());
 321          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 322      }
 323  
 324      /**
 325       * Test that notify_step_change clears the cache.
 326       */
 327      public function test_notify_step_change() {
 328          global $DB;
 329  
 330          $this->resetAfterTest();
 331  
 332          $tour = $this->helper_create_tour();
 333          $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
 334  
 335          // Only one read for the first call.
 336          $startreads = $DB->perf_get_reads();
 337          $matches = \tool_usertours\cache::get_stepdata($tour->get_id());
 338          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 339  
 340          // No subsequent reads for any further calls.
 341          $matches = \tool_usertours\cache::get_stepdata($tour->get_id());
 342          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 343  
 344          // Reset.
 345          \tool_usertours\cache::notify_step_change($tour->get_id());
 346  
 347          // An additional DB read now.
 348          $startreads = $DB->perf_get_reads();
 349          $matches = \tool_usertours\cache::get_stepdata($tour->get_id());
 350          $this->assertEquals(1, $DB->perf_get_reads() - $startreads);
 351      }
 352  }