Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

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