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 311 and 401] [Versions 39 and 401] [Versions 400 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  /**
  18   * Test iterator that skips future documents
  19   *
  20   * @package core_search
  21   * @category test
  22   * @copyright 2017 The Open University
  23   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core_search;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  /**
  31   * Test iterator that skips future documents
  32   *
  33   * @package core_search
  34   * @category test
  35   * @copyright 2017 The Open University
  36   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class skip_future_documents_iterator_test extends \basic_testcase {
  39  
  40      /**
  41       * Test normal case with all documents in the past.
  42       */
  43      public function test_iterator_all_in_past() {
  44          $past = strtotime('2017-11-01');
  45          $documents = [
  46              self::make_doc($past, 1),
  47              self::make_doc($past + 1, 2),
  48              self::make_doc($past + 2, 3)
  49          ];
  50          $this->assertEquals('mod_x-frog-1.mod_x-frog-2.mod_x-frog-3.',
  51                  self::do_iterator($documents));
  52      }
  53  
  54      /**
  55       * Confirm that the iterator does not call its parent iterator current() function too many
  56       * times.
  57       */
  58      public function test_iterator_performance() {
  59          $counter = new test_counting_iterator();
  60          $iterator = new skip_future_documents_iterator($counter);
  61          $items = 0;
  62          foreach ($iterator as $value) {
  63              $this->assertEquals(false, $value);
  64              $items++;
  65          }
  66          $this->assertEquals(3, $items);
  67          $this->assertEquals(3, $counter->get_count());
  68      }
  69  
  70      /**
  71       * Test with no documents at all.
  72       */
  73      public function test_iterator_empty() {
  74          $this->assertEquals('', self::do_iterator([]));
  75      }
  76  
  77      /**
  78       * Test if some documents are in the future.
  79       */
  80      public function test_iterator_some_in_future() {
  81          $past = strtotime('2017-11-01');
  82          $future = time() + 1000;
  83          $documents = [
  84              self::make_doc($past, 1),
  85              self::make_doc($past + 1, 2),
  86              self::make_doc($future, 3)
  87          ];
  88          $this->assertEquals('mod_x-frog-1.mod_x-frog-2.',
  89                  self::do_iterator($documents));
  90      }
  91  
  92      /**
  93       * Test if all documents are in the future.
  94       */
  95      public function test_iterator_all_in_future() {
  96          $future = time() + 1000;
  97          $documents = [
  98              self::make_doc($future, 1),
  99              self::make_doc($future + 1, 2),
 100              self::make_doc($future + 2, 3)
 101          ];
 102          $this->assertEquals('', self::do_iterator($documents));
 103      }
 104  
 105      /**
 106       * Test when some documents return error.
 107       */
 108      public function test_iterator_some_false() {
 109          $past = strtotime('2017-11-01');
 110          $documents = [
 111              self::make_doc($past, 1),
 112              false,
 113              self::make_doc($past + 2, 3)
 114          ];
 115          $this->assertEquals('mod_x-frog-1.false.mod_x-frog-3.',
 116                  self::do_iterator($documents));
 117      }
 118  
 119      /**
 120       * Test when all documents return error.
 121       */
 122      public function test_iterator_all_false() {
 123          $documents = [
 124              false,
 125              false,
 126              false
 127          ];
 128          $this->assertEquals('false.false.false.',
 129                  self::do_iterator($documents));
 130      }
 131  
 132      /**
 133       * Test iterator with all cases.
 134       */
 135      public function test_iterator_past_false_and_future() {
 136          $past = strtotime('2017-11-01');
 137          $future = time() + 1000;
 138          $documents = [
 139              false,
 140              self::make_doc($past, 1),
 141              false,
 142              self::make_doc($past + 1, 2),
 143              false,
 144              self::make_doc($future, 3),
 145              false
 146          ];
 147          $this->assertEquals('false.mod_x-frog-1.false.mod_x-frog-2.false.',
 148                  self::do_iterator($documents));
 149      }
 150  
 151      /**
 152       * Helper function to create a search document.
 153       *
 154       * @param int $time Modified time
 155       * @param int $index Item id
 156       * @return document Search document
 157       */
 158      protected static function make_doc($time, $index) {
 159          $doc = new document($index, 'mod_x', 'frog');
 160          $doc->set('modified', $time);
 161          return $doc;
 162      }
 163  
 164      /**
 165       * Puts documents through the iterator and returns result as a string for easy testing.
 166       *
 167       * @param document[] $documents Array of documents
 168       * @return string Documents converted to string
 169       */
 170      protected static function do_iterator(array $documents) {
 171          $parent = new \ArrayIterator($documents);
 172          $iterator = new skip_future_documents_iterator($parent);
 173          $result = '';
 174          foreach ($iterator as $rec) {
 175              if (!$rec) {
 176                  $result .= 'false.';
 177              } else {
 178                  $result .= $rec->get('id') . '.';
 179              }
 180          }
 181          return $result;
 182      }
 183  }
 184  
 185  /**
 186   * Fake iterator just for counting how many times current() is called. It returns 'false' 3 times.
 187   *
 188   * @package core_search
 189   * @category test
 190   * @copyright 2017 The Open University
 191   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 192   */
 193  class test_counting_iterator implements \Iterator {
 194  
 195      /** @var int Current position in iterator */
 196      protected $pos = 0;
 197      /** @var int Number of calls to current() function */
 198      protected $count = 0;
 199  
 200      /**
 201       * Returns the current element.
 202       *
 203       * @return mixed Can return any type.
 204       */
 205      #[\ReturnTypeWillChange]
 206      public function current() {
 207          $this->count++;
 208          return false;
 209      }
 210  
 211      /**
 212       * Counts iterator usage.
 213       *
 214       * @return int Number of times current() was called
 215       */
 216      public function get_count() {
 217          return $this->count;
 218      }
 219  
 220      /**
 221       * Goes on to the next element.
 222       */
 223      public function next(): void {
 224          $this->pos++;
 225      }
 226  
 227      /**
 228       * Gets the key (not supported)
 229       *
 230       * @throws \coding_exception Always
 231       */
 232      #[\ReturnTypeWillChange]
 233      public function key() {
 234          throw new \coding_exception('Unsupported');
 235      }
 236  
 237      /**
 238       * Checks if iterato is valid (still has entries).
 239       *
 240       * @return bool True if still valid
 241       */
 242      public function valid(): bool {
 243          return $this->pos < 3;
 244      }
 245  
 246      /**
 247       * Rewinds the iterator.
 248       */
 249      public function rewind(): void {
 250          $this->pos = 0;
 251      }
 252  }