Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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      public function current() {
 206          $this->count++;
 207          return false;
 208      }
 209  
 210      /**
 211       * Counts iterator usage.
 212       *
 213       * @return int Number of times current() was called
 214       */
 215      public function get_count() {
 216          return $this->count;
 217      }
 218  
 219      /**
 220       * Goes on to the next element.
 221       */
 222      public function next() {
 223          $this->pos++;
 224      }
 225  
 226      /**
 227       * Gets the key (not supported)
 228       *
 229       * @throws \coding_exception Always
 230       */
 231      public function key() {
 232          throw new \coding_exception('Unsupported');
 233      }
 234  
 235      /**
 236       * Checks if iterato is valid (still has entries).
 237       *
 238       * @return bool True if still valid
 239       */
 240      public function valid() {
 241          return $this->pos < 3;
 242      }
 243  
 244      /**
 245       * Rewinds the iterator.
 246       */
 247      public function rewind() {
 248          $this->pos = 0;
 249      }
 250  }