CronTest.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • cron/
  • tests/
  • CronTest.php
  • View
  • Commits
  • Open Download .zip Download (11 KB)
<?php
/**
 * Test the cron model.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Cron_Test_CronTest extends ModuleTest
{
    protected static $_counters = array();

    /**
     * Test run() method where each cron run should trigger all cycles.
     */
    public function testRunIndependent()
    {
        $tests = array(
            array(
                'line'      => __LINE__,
                'date'      => '2011-01-01 15:45:27'
            ),
            array(
                'line'      => __LINE__,
                'date'      => '2001-11-17 00:00:01'
            ),
            array(
                'line'      => __LINE__,
                'date'      => '2007-02-25 07:00:01'
            ),
            array(
                'line'      => __LINE__,
                'date'      => '2020-05-31 23:59:59'
            ),
            array(
                'line'      => __LINE__,
                'date'      => '2020-06-01 00:00:00'
            )
        );

        // run tests
        $this->_initSubscribe();
        $revisionsCount = 2;
        $cronFiles      = array('hourly', 'daily', 'weekly', 'monthly');

        foreach ($tests as $test) {
            static::_resetCounters();
            $timestamp = strtotime($test['date']);
            $result    = Cron_Model_Cron::run(null, $timestamp);

            $this->_verifyResult($result);
            $this->_verifyCounters(
                array('hourly' => 1, 'daily' => 1, 'weekly' => 1, 'monthly' => 1),
                "Line {$test['line']}: Expected each cron cycle was run once."
            );

            // verify cron file revisions and attributes
            foreach ($cronFiles as $record) {
                $cronFile = Cron_Model_Cron::fetch($record);

                // ensure 2 new revisions of each cron file have been created
                $this->assertSame(
                    $revisionsCount,
                    count($cronFile->toP4File()->getChanges()),
                    "Line {$test['line']}: Expected 2 new revisions created."
                );

                // verify last run attribute
                $this->assertSame(
                    $timestamp,
                    $cronFile->getRunTime(),
                    "Line {$test['line']}: Expected value for lastRun attribute."
                );

                // verify completed attribute
                $this->assertSame(
                    true,
                    $cronFile->getCompleted(),
                    "Line {$test['line']}: Expected value for completed attribute."
                );

                // ensure that returned messages are contained in the data attribute
                // of the cron file
                $messages = array(
                    "foo",
                    "p4cms.cron.$record published"
                );

                $this->assertTrue(
                    in_array($messages, $cronFile->getData()),
                    "Line {$test['line']}: Expected messages are captured in the cron data attribute."
                );

                // verify filetype of cron log file
                $this->assertSame(
                    'ctext',
                    $cronFile->toP4File()->getStatus('headType'),
                    "LINE {$test['line']}: Expected ctext filetype"
                );
            }

            $revisionsCount += 2;
        }
    }

    /**
     * Test run() method where each run may not trigger all cycles.
     */
    public function testRunDependent()
    {
        $this->_initSubscribe();

        static::_resetCounters();
        $timestamp = strtotime("2012-09-02 13:13:13");
        $result    = Cron_Model_Cron::run(null, $timestamp);

        $this->_verifyResult($result);
        $this->_verifyCounters(
            array('hourly' => 1, 'daily' => 1, 'weekly' => 1, 'monthly' => 1),
            "Expected each cron cycle was run once."
        );

        // run the same cron and ensure no cycles were run
        static::_resetCounters();
        $result = Cron_Model_Cron::run(null, $timestamp);

        $this->_verifyResult($result, 'ssss');
        $this->_verifyCounters(
            array(),
            "Expected no cron cycle was run."
        );

        // run with time shifted towards by half an hour
        $timestamp = strtotime("2012-09-02 13:43:13");
        static::_resetCounters();
        $result = Cron_Model_Cron::run(null, $timestamp);
        $this->_verifyResult($result, 'ssss');
        $this->_verifyCounters(
            array(),
            "Unexpected cron run cycle."
        );

        // run with time in next hour, should update only cron.hourly
        $timestamp = strtotime("2012-09-02 14:02:01");
        static::_resetCounters();
        $result = Cron_Model_Cron::run(null, $timestamp);

        $this->_verifyResult($result, 'esss');
        $this->_verifyCounters(
            array('hourly' => 1),
            "Unexpected cron run cycle."
        );

        // run next day
        $timestamp = strtotime("2012-09-03 01:00:05");
        static::_resetCounters();
        $result = Cron_Model_Cron::run(null, $timestamp);

        $this->_verifyResult($result, 'eees');
        $this->_verifyCounters(
            array('hourly' => 1, 'daily' => 1, 'weekly' => 1),
            "Unexpected cron run cycle."
        );
        
        // run next day
        $timestamp = strtotime("2012-09-04 01:00:05");
        static::_resetCounters();
        $result = Cron_Model_Cron::run(null, $timestamp);

        $this->_verifyResult($result, 'eess');
        $this->_verifyCounters(
            array('hourly' => 1, 'daily' => 1),
            "Unexpected cron run cycle."
        );
    }

    /**
     * Perform series of cron runs in constant intervals.
     */
    public function testRunPeriodic()
    {
        $this->_initSubscribe();

        $timestamp = strtotime("2012-10-31 00:00:01");
        $result    = Cron_Model_Cron::run(null, $timestamp);
        $this->_verifyResult($result);

        // simulate running cron for a day in 30-min intervals
        for ($i = 1; $i < 24; $i++) {
            // add 30 minutes
            $timestamp += 1800;

            static::_resetCounters();
            $result = Cron_Model_Cron::run(null, $timestamp);

            $expected = $i % 2 === 0 ? array('hourly' => 1) : array();
            $this->_verifyResult($result, count($expected) ? 'esss' : 'ssss');
            $this->_verifyCounters(
                $expected,
                "Unexpected cron run cycle."
            );
        }

        // simulate running cron for a month in 1-day intervals
        $timeStart = strtotime("2010-01-16 00:00:01");
        $timeStop  = strtotime("2010-02-16 00:00:00");
        static::_resetCounters();

        $timestamp = $timeStart;
        while ($timestamp < $timeStop) {
            $result = Cron_Model_Cron::run(null, $timestamp);
            $timestamp += 86400;
        }

        $this->_verifyCounters(
            array('hourly' => 31, 'daily' => 31, 'weekly' => 6, 'monthly' => 2),
            "Unexpected cron run cycle."
        );

        // ensure cron files have ctext filetype
        $records = Cron_Model_Cron::fetchAll();
        $this->assertSame(
            4,
            $records->count(),
            "Expected 4 cron log files."
        );
        foreach ($records as $record) {
            $this->assertSame(
                'ctext',
                $record->toP4File()->getStatus('headType'),
                "Expected ctext filetype of {$record->getId()} file."
            );
        }
    }

    /**
     * Helper function to subscribe to all cron topics.
     */
    protected function _initSubscribe()
    {
        // prepare callback for the pubsub
        $callback = function ($frequency)
        {
            return function() use ($frequency)
            {
                Cron_Test_CronTest::updateCounter($frequency);
                return array(
                    "foo",
                    "p4cms.cron.$frequency published"
                );
            };
        };

        P4Cms_PubSub::subscribe('p4cms.cron.hourly',  $callback('hourly'));
        P4Cms_PubSub::subscribe('p4cms.cron.daily',   $callback('daily'));
        P4Cms_PubSub::subscribe('p4cms.cron.weekly',  $callback('weekly'));
        P4Cms_PubSub::subscribe('p4cms.cron.monthly', $callback('monthly'));
    }

    /**
     * Helper function to compare counters with expected values.
     * 
     * @param   array   $expected   list of expected counters and their values
     * @param   type    $message    (optional) message for assert
     */
    protected function _verifyCounters(array $expected, $message = '')
    {
        $counters = static::$_counters;
        ksort($counters);
        ksort($expected);
        $this->assertSame($expected, $counters, $message);
    }

    /**
     * Helper function to compare result of Cron_Model_Cron::run() method with
     * provided $expected values where $expected is string of length 4 where first
     * letter represents expected result for hourly frequency, second for daily,
     * third for weekly and fourth for monthly. Letters are mapped as follows:
     *  e ... executed
     *  s ... skipped
     *  f ... failed
     *
     * @param   array       $result     result array returned by the Cron_Model_Cron::run()
     *                                  method
     * @param   string|null $expected   (optional) encoded expected result, defaults to 'eeee'
     */
    protected function _verifyResult(array $result, $expected = null)
    {
        if ($expected === null) {
            $expected = 'eeee';
        }

        $cron           = array('hourly', 'daily', 'weekly', 'monthly');
        $expectedResult = array();
        $statusMap      = array(
            'e' => 'executed',
            's' => 'skipped',
            'f' => 'failed'
        );

        foreach ($cron as $index => $frequence) {
            $statusShort = substr($expected, $index, 1);
            $expectedResult[$frequence] = $statusMap[$statusShort];
        }

        $this->assertSame(
            $expectedResult,
            $result,
            "Expected cron run result."
        );
    }

    /**
     * Resets counters.
     */
    protected static function _resetCounters()
    {
        static::$_counters = array();
    }

    /**
     * Updates specified counter by one.
     * 
     * @param   string  $counter    counter to update
     */
    public static function updateCounter($counter)
    {
        if (!isset(static::$_counters[$counter])) {
            static::$_counters[$counter] = 1;
        } else {
            static::$_counters[$counter]++;
        }
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/cron/tests/CronTest.php
#1 8972 Matt Attaway Initial add of the Chronicle source code