SeekquarrySeekquarry - Yioop Repo - Seekquarry

/ tests / CrawlModelTest.php⤓
<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see
 * <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @package seek_quarry\tests
 */
namespace seekquarry\yioop\tests;

use seekquarry\yioop\configs as C;
use seekquarry\yioop\library as L;
use seekquarry\yioop\library\CrawlDaemon;
use seekquarry\yioop\library\UnitTest;
use seekquarry\yioop\models\CrawlModel;
use seekquarry\yioop\models\Model;

/**
 * Checks that a crawl whose stop has been sent shows as stopping before
 * its queue server has read the stop. The cases write the name server's
 * message file for an unused channel, as clicking stop does, and ask
 * CrawlModel::markStopRequested what the status becomes.
 *
 * @author Chris Pollett
 */
class CrawlModelTest extends UnitTest
{
    /**
     * CHANNEL is the channel number of the crawl the cases make up.
     */
    const CHANNEL = 97;
    /**
     * The crawl model under test.
     * @var CrawlModel
     */
    public $crawl_model;
    /**
     * setUp builds the crawl model and points CrawlDaemon at SCHEDULES, so
     * the message files the cases write are in the tests folder.
     */
    public function setUp()
    {
        $this->crawl_model = new CrawlModel();
        L\makePath(self::SCHEDULES);
        CrawlDaemon::$schedules_folder = self::SCHEDULES;
    }
    /**
     * SCHEDULES is the folder, under tests/test_files, that CrawlDaemon
     * keeps the name server's message file in for these cases.
     */
    const SCHEDULES = C\TEST_DIR . "/test_files/crawl_model_schedules";
    /**
     * tearDown deletes SCHEDULES, with the channel's name server message
     * file, and points CrawlDaemon back at the work directory's schedules
     * folder.
     */
    public function tearDown()
    {
        (new Model())->db->unlinkRecursive(self::SCHEDULES);
        CrawlDaemon::$schedules_folder = C\SCHEDULES_DIR;
    }
    /**
     * writeNameServerMessage writes the channel's message file as the
     * crawl component does.
     *
     * @param string $status the STATUS the message carries
     */
    public function writeNameServerMessage($status)
    {
        file_put_contents(CrawlDaemon::getMessageFileName("NameServer",
            self::CHANNEL), serialize([CrawlModel::STATUS => $status]));
    }
    /**
     * A running crawl whose stop has been sent shows SHUTDOWN_QUEUE, and a
     * crawl on another channel is left as it was.
     */
    public function sentStopShowsAtOnceTestCase()
    {
        $this->writeNameServerMessage("STOP_CRAWL");
        $status = [self::CHANNEL => ['DESCRIPTION' => "Test Syn 3"],
            self::CHANNEL + 1 => ['DESCRIPTION' => "Another crawl"]];
        $this->crawl_model->markStopRequested($status);
        $this->assertEqual($status[self::CHANNEL]['DESCRIPTION'],
            "SHUTDOWN_QUEUE", "a sent stop shows as the first stopping step");
        $this->assertEqual($status[self::CHANNEL + 1]['DESCRIPTION'],
            "Another crawl", "a channel with no stop is unchanged");
    }
    /**
     * Once the queue server writes its own shutdown step, that step shows
     * rather than SHUTDOWN_QUEUE.
     */
    public function queueServerStepIsKeptTestCase()
    {
        $this->writeNameServerMessage("STOP_CRAWL");
        $status = [self::CHANNEL => ['DESCRIPTION' => "SHUTDOWN_DICTIONARY"]];
        $this->crawl_model->markStopRequested($status);
        $this->assertEqual($status[self::CHANNEL]['DESCRIPTION'],
            "SHUTDOWN_DICTIONARY", "the queue server's step is kept");
    }
    /**
     * A crawl whose message file holds a start, or which has no message
     * file, shows its own description.
     */
    public function noStopLeavesDescriptionTestCase()
    {
        $status = [self::CHANNEL => ['DESCRIPTION' => "Test Syn 3"]];
        $this->crawl_model->markStopRequested($status);
        $this->assertEqual($status[self::CHANNEL]['DESCRIPTION'],
            "Test Syn 3", "no message file leaves the description");
        $this->writeNameServerMessage("NEW_CRAWL");
        $this->crawl_model->markStopRequested($status);
        $this->assertEqual($status[self::CHANNEL]['DESCRIPTION'],
            "Test Syn 3", "a start message leaves the description");
    }
    /**
     * speedHistoryReadsBacklogAndFetcherRateTestCase checks that
     * crawlSpeedHistory gives a line's indexer backlog and fetcher
     * downloads per hour, and gives "" for both from a line logged before
     * those columns were kept. Lines logged before 27 October hold the
     * crawl's channel after its timestamp, and later lines do not; both
     * give the same row. A line of another crawl is left out.
     */
    public function speedHistoryReadsBacklogAndFetcherRateTestCase()
    {
        $speed_log = self::SCHEDULES . "/CrawlSpeed.log";
        file_put_contents($speed_log,
            "100\t7\t0\t1500.5\t20\t900\n" .
            "200\t8\t0\t9.0\t1\t2\t3\t4\n" .
            "300\t7\t0\t1600.25\t30\t950\t12\t170000\n" .
            "400\t7\t1700.75\t40\t990\t3\t180000\n");
        $this->assertEqual($this->crawl_model->crawlSpeedHistory(7,
            $speed_log), [["100", "1500.5", "20", "900", "", ""],
            ["300", "1600.25", "30", "950", "12", "170000"],
            ["400", "1700.75", "40", "990", "3", "180000"]],
            "the old line has no backlog or rate, the later ones have " .
            "both, with or without the channel");
    }
    /**
     * fetcherRatesCountEachFetcherOnceTestCase checks that aggregateStatuses
     * keeps one rate for a fetcher in the FETCHER_RATES of two machines,
     * the newer one, and that FETCHER_DOWNLOADS_PER_HOUR sums the rates
     * of the different fetchers.
     */
    public function fetcherRatesCountEachFetcherOnceTestCase()
    {
        $statuses = [];
        foreach ([["0-97-Yioop" => [100, 17000], "1-97-Yioop" => [90, 9000]],
            ["0-97-Yioop" => [120, 18000]]] as $rates) {
            $statuses[] = [CrawlModel::PAGE => L\webencode(serialize([
                self::CHANNEL => ["FETCHER_RATES" => $rates,
                "CRAWL_TIME" => 7]]))];
        }
        $status = $this->crawl_model->aggregateStatuses($statuses);
        $this->assertEqual($status[self::CHANNEL]["FETCHER_RATES"],
            ["0-97-Yioop" => [120, 18000], "1-97-Yioop" => [90, 9000]],
            "a fetcher on two machines keeps its newer rate");
        $this->assertEqual($status[self::CHANNEL]
            ["FETCHER_DOWNLOADS_PER_HOUR"], 27000,
            "the rates of different fetchers are summed");
    }
    /**
     * backlogIsSummedAcrossMachinesTestCase checks that aggregateStatuses
     * adds up the INDEXER_BACKLOG of each machine's crawl status for a
     * channel, as it does the visited urls count.
     */
    public function backlogIsSummedAcrossMachinesTestCase()
    {
        $statuses = [];
        foreach ([[5, 100], [7, 50]] as [$backlog, $visited]) {
            $statuses[] = [CrawlModel::PAGE => L\webencode(serialize([
                self::CHANNEL => ["INDEXER_BACKLOG" => $backlog,
                "VISITED_URLS_COUNT" => $visited, "CRAWL_TIME" => 7]]))];
        }
        $status = $this->crawl_model->aggregateStatuses($statuses);
        $this->assertEqual([$status[self::CHANNEL]["INDEXER_BACKLOG"],
            $status[self::CHANNEL]["VISITED_URLS_COUNT"]], [12, 150],
            "the backlogs are summed like the visited counts");
    }
}