Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Test iterator that skips future documents
 *
 * @package core_search
 * @category test
 * @copyright 2017 The Open University
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace core_search;

defined('MOODLE_INTERNAL') || die();

/**
 * Test iterator that skips future documents
 *
 * @package core_search
 * @category test
 * @copyright 2017 The Open University
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class skip_future_documents_iterator_test extends \basic_testcase {

    /**
     * Test normal case with all documents in the past.
     */
    public function test_iterator_all_in_past() {
        $past = strtotime('2017-11-01');
        $documents = [
            self::make_doc($past, 1),
            self::make_doc($past + 1, 2),
            self::make_doc($past + 2, 3)
        ];
        $this->assertEquals('mod_x-frog-1.mod_x-frog-2.mod_x-frog-3.',
                self::do_iterator($documents));
    }

    /**
     * Confirm that the iterator does not call its parent iterator current() function too many
     * times.
     */
    public function test_iterator_performance() {
        $counter = new test_counting_iterator();
        $iterator = new skip_future_documents_iterator($counter);
        $items = 0;
        foreach ($iterator as $value) {
            $this->assertEquals(false, $value);
            $items++;
        }
        $this->assertEquals(3, $items);
        $this->assertEquals(3, $counter->get_count());
    }

    /**
     * Test with no documents at all.
     */
    public function test_iterator_empty() {
        $this->assertEquals('', self::do_iterator([]));
    }

    /**
     * Test if some documents are in the future.
     */
    public function test_iterator_some_in_future() {
        $past = strtotime('2017-11-01');
        $future = time() + 1000;
        $documents = [
            self::make_doc($past, 1),
            self::make_doc($past + 1, 2),
            self::make_doc($future, 3)
        ];
        $this->assertEquals('mod_x-frog-1.mod_x-frog-2.',
                self::do_iterator($documents));
    }

    /**
     * Test if all documents are in the future.
     */
    public function test_iterator_all_in_future() {
        $future = time() + 1000;
        $documents = [
            self::make_doc($future, 1),
            self::make_doc($future + 1, 2),
            self::make_doc($future + 2, 3)
        ];
        $this->assertEquals('', self::do_iterator($documents));
    }

    /**
     * Test when some documents return error.
     */
    public function test_iterator_some_false() {
        $past = strtotime('2017-11-01');
        $documents = [
            self::make_doc($past, 1),
            false,
            self::make_doc($past + 2, 3)
        ];
        $this->assertEquals('mod_x-frog-1.false.mod_x-frog-3.',
                self::do_iterator($documents));
    }

    /**
     * Test when all documents return error.
     */
    public function test_iterator_all_false() {
        $documents = [
            false,
            false,
            false
        ];
        $this->assertEquals('false.false.false.',
                self::do_iterator($documents));
    }

    /**
     * Test iterator with all cases.
     */
    public function test_iterator_past_false_and_future() {
        $past = strtotime('2017-11-01');
        $future = time() + 1000;
        $documents = [
            false,
            self::make_doc($past, 1),
            false,
            self::make_doc($past + 1, 2),
            false,
            self::make_doc($future, 3),
            false
        ];
        $this->assertEquals('false.mod_x-frog-1.false.mod_x-frog-2.false.',
                self::do_iterator($documents));
    }

    /**
     * Helper function to create a search document.
     *
     * @param int $time Modified time
     * @param int $index Item id
     * @return document Search document
     */
    protected static function make_doc($time, $index) {
        $doc = new document($index, 'mod_x', 'frog');
        $doc->set('modified', $time);
        return $doc;
    }

    /**
     * Puts documents through the iterator and returns result as a string for easy testing.
     *
     * @param document[] $documents Array of documents
     * @return string Documents converted to string
     */
    protected static function do_iterator(array $documents) {
        $parent = new \ArrayIterator($documents);
        $iterator = new skip_future_documents_iterator($parent);
        $result = '';
        foreach ($iterator as $rec) {
            if (!$rec) {
                $result .= 'false.';
            } else {
                $result .= $rec->get('id') . '.';
            }
        }
        return $result;
    }
}

/**
 * Fake iterator just for counting how many times current() is called. It returns 'false' 3 times.
 *
 * @package core_search
 * @category test
 * @copyright 2017 The Open University
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class test_counting_iterator implements \Iterator {

    /** @var int Current position in iterator */
    protected $pos = 0;
    /** @var int Number of calls to current() function */
    protected $count = 0;

    /**
     * Returns the current element.
     *
     * @return mixed Can return any type.
     */
> #[\ReturnTypeWillChange]
public function current() { $this->count++; return false; } /** * Counts iterator usage. * * @return int Number of times current() was called */ public function get_count() { return $this->count; } /** * Goes on to the next element. */
< public function next() {
> public function next(): void {
$this->pos++; } /** * Gets the key (not supported) * * @throws \coding_exception Always */
> #[\ReturnTypeWillChange]
public function key() { throw new \coding_exception('Unsupported'); } /** * Checks if iterato is valid (still has entries). * * @return bool True if still valid */
< public function valid() {
> public function valid(): bool {
return $this->pos < 3; } /** * Rewinds the iterator. */
< public function rewind() {
> public function rewind(): void {
$this->pos = 0; } }