[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/tests/ -> scheduled_task_test.php (source)

   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   * This file contains the unittests for scheduled tasks.
  19   *
  20   * @package   core
  21   * @category  phpunit
  22   * @copyright 2013 Damyon Wiese
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  require_once (__DIR__ . '/fixtures/task_fixtures.php');
  28  
  29  /**
  30   * Test class for scheduled task.
  31   *
  32   * @package core
  33   * @category task
  34   * @copyright 2013 Damyon Wiese
  35   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class core_scheduled_task_testcase extends advanced_testcase {
  38  
  39      /**
  40       * Test the cron scheduling method
  41       */
  42      public function test_eval_cron_field() {
  43          $testclass = new \core\task\scheduled_test_task();
  44  
  45          $this->assertEquals(20, count($testclass->eval_cron_field('*/3', 0, 59)));
  46          $this->assertEquals(31, count($testclass->eval_cron_field('1,*/2', 0, 59)));
  47          $this->assertEquals(15, count($testclass->eval_cron_field('1-10,5-15', 0, 59)));
  48          $this->assertEquals(13, count($testclass->eval_cron_field('1-10,5-15/2', 0, 59)));
  49          $this->assertEquals(3, count($testclass->eval_cron_field('1,2,3,1,2,3', 0, 59)));
  50          $this->assertEquals(1, count($testclass->eval_cron_field('-1,10,80', 0, 59)));
  51      }
  52  
  53      public function test_get_next_scheduled_time() {
  54          global $CFG;
  55          $this->resetAfterTest();
  56  
  57          $this->setTimezone('Europe/London');
  58  
  59          // Test job run at 1 am.
  60          $testclass = new \core\task\scheduled_test_task();
  61  
  62          // All fields default to '*'.
  63          $testclass->set_hour('1');
  64          $testclass->set_minute('0');
  65          // Next valid time should be 1am of the next day.
  66          $nexttime = $testclass->get_next_scheduled_time();
  67  
  68          $oneamdate = new DateTime('now', new DateTimeZone('Europe/London'));
  69          $oneamdate->setTime(1, 0, 0);
  70          // Make it 1 am tomorrow if the time is after 1am.
  71          if ($oneamdate->getTimestamp() < time()) {
  72              $oneamdate->add(new DateInterval('P1D'));
  73          }
  74          $oneam = $oneamdate->getTimestamp();
  75  
  76          $this->assertEquals($oneam, $nexttime, 'Next scheduled time is 1am.');
  77  
  78          // Disabled flag does not affect next time.
  79          $testclass->set_disabled(true);
  80          $nexttime = $testclass->get_next_scheduled_time();
  81          $this->assertEquals($oneam, $nexttime, 'Next scheduled time is 1am.');
  82  
  83          // Now test for job run every 10 minutes.
  84          $testclass = new \core\task\scheduled_test_task();
  85  
  86          // All fields default to '*'.
  87          $testclass->set_minute('*/10');
  88          // Next valid time should be next 10 minute boundary.
  89          $nexttime = $testclass->get_next_scheduled_time();
  90  
  91          $minutes = ((intval(date('i') / 10))+1) * 10;
  92          $nexttenminutes = mktime(date('H'), $minutes, 0);
  93  
  94          $this->assertEquals($nexttenminutes, $nexttime, 'Next scheduled time is in 10 minutes.');
  95  
  96          // Disabled flag does not affect next time.
  97          $testclass->set_disabled(true);
  98          $nexttime = $testclass->get_next_scheduled_time();
  99          $this->assertEquals($nexttenminutes, $nexttime, 'Next scheduled time is in 10 minutes.');
 100  
 101          // Test hourly job executed on Sundays only.
 102          $testclass = new \core\task\scheduled_test_task();
 103          $testclass->set_minute('0');
 104          $testclass->set_day_of_week('7');
 105  
 106          $nexttime = $testclass->get_next_scheduled_time();
 107  
 108          $this->assertEquals(7, date('N', $nexttime));
 109          $this->assertEquals(0, date('i', $nexttime));
 110  
 111          // Test monthly job
 112          $testclass = new \core\task\scheduled_test_task();
 113          $testclass->set_minute('32');
 114          $testclass->set_hour('0');
 115          $testclass->set_day('1');
 116  
 117          $nexttime = $testclass->get_next_scheduled_time();
 118  
 119          $this->assertEquals(32, date('i', $nexttime));
 120          $this->assertEquals(0, date('G', $nexttime));
 121          $this->assertEquals(1, date('j', $nexttime));
 122      }
 123  
 124      public function test_timezones() {
 125          global $CFG, $USER;
 126  
 127          // The timezones used in this test are chosen because they do not use DST - that would break the test.
 128          $this->resetAfterTest();
 129  
 130          $this->setTimezone('Asia/Kabul');
 131  
 132          $testclass = new \core\task\scheduled_test_task();
 133  
 134          // Scheduled tasks should always use servertime - so this is 03:30 GMT.
 135          $testclass->set_hour('1');
 136          $testclass->set_minute('0');
 137  
 138          // Next valid time should be 1am of the next day.
 139          $nexttime = $testclass->get_next_scheduled_time();
 140  
 141          // GMT+05:45.
 142          $USER->timezone = 'Asia/Kathmandu';
 143          $userdate = userdate($nexttime);
 144  
 145          // Should be displayed in user timezone.
 146          // I used http://www.timeanddate.com/worldclock/fixedtime.html?msg=Moodle+Test&iso=20160502T01&p1=113
 147          // setting my location to Kathmandu to verify this time.
 148          $this->assertContains('2:15 AM', core_text::strtoupper($userdate));
 149      }
 150  
 151      public function test_reset_scheduled_tasks_for_component() {
 152          global $DB;
 153  
 154          $this->resetAfterTest(true);
 155          // Remember the defaults.
 156          $defaulttasks = \core\task\manager::load_scheduled_tasks_for_component('moodle');
 157          $initcount = count($defaulttasks);
 158          // Customise a task.
 159          $firsttask = reset($defaulttasks);
 160          $firsttask->set_minute('1');
 161          $firsttask->set_hour('2');
 162          $firsttask->set_month('3');
 163          $firsttask->set_day_of_week('4');
 164          $firsttask->set_day('5');
 165          $firsttask->set_customised('1');
 166          \core\task\manager::configure_scheduled_task($firsttask);
 167          $firsttaskrecord = \core\task\manager::record_from_scheduled_task($firsttask);
 168          // We reset this field, because we do not want to compare it.
 169          $firsttaskrecord->nextruntime = '0';
 170  
 171          // Delete a task to simulate the fact that its new.
 172          $secondtask = next($defaulttasks);
 173          $DB->delete_records('task_scheduled', array('classname' => '\\' . trim(get_class($secondtask), '\\')));
 174          $this->assertFalse(\core\task\manager::get_scheduled_task(get_class($secondtask)));
 175  
 176          // Edit a task to simulate a change in its definition (as if it was not customised).
 177          $thirdtask = next($defaulttasks);
 178          $thirdtask->set_minute('1');
 179          $thirdtask->set_hour('2');
 180          $thirdtask->set_month('3');
 181          $thirdtask->set_day_of_week('4');
 182          $thirdtask->set_day('5');
 183          $thirdtaskbefore = \core\task\manager::get_scheduled_task(get_class($thirdtask));
 184          $thirdtaskbefore->set_next_run_time(null);      // Ignore this value when comparing.
 185          \core\task\manager::configure_scheduled_task($thirdtask);
 186          $thirdtask = \core\task\manager::get_scheduled_task(get_class($thirdtask));
 187          $thirdtask->set_next_run_time(null);            // Ignore this value when comparing.
 188          $this->assertNotEquals($thirdtaskbefore, $thirdtask);
 189  
 190          // Now call reset on all the tasks.
 191          \core\task\manager::reset_scheduled_tasks_for_component('moodle');
 192  
 193          // Load the tasks again.
 194          $defaulttasks = \core\task\manager::load_scheduled_tasks_for_component('moodle');
 195          $finalcount = count($defaulttasks);
 196          // Compare the first task.
 197          $newfirsttask = reset($defaulttasks);
 198          $newfirsttaskrecord = \core\task\manager::record_from_scheduled_task($newfirsttask);
 199          // We reset this field, because we do not want to compare it.
 200          $newfirsttaskrecord->nextruntime = '0';
 201  
 202          // Assert a customised task was not altered by reset.
 203          $this->assertEquals($firsttaskrecord, $newfirsttaskrecord);
 204  
 205          // Assert that the second task was added back.
 206          $secondtaskafter = \core\task\manager::get_scheduled_task(get_class($secondtask));
 207          $secondtaskafter->set_next_run_time(null);   // Do not compare the nextruntime.
 208          $secondtask->set_next_run_time(null);
 209          $this->assertEquals($secondtask, $secondtaskafter);
 210  
 211          // Assert that the third task edits were overridden.
 212          $thirdtaskafter = \core\task\manager::get_scheduled_task(get_class($thirdtask));
 213          $thirdtaskafter->set_next_run_time(null);
 214          $this->assertEquals($thirdtaskbefore, $thirdtaskafter);
 215  
 216          // Assert we have the same number of tasks.
 217          $this->assertEquals($initcount, $finalcount);
 218      }
 219  
 220      /**
 221       * Tests that the reset function deletes old tasks.
 222       */
 223      public function test_reset_scheduled_tasks_for_component_delete() {
 224          global $DB;
 225          $this->resetAfterTest(true);
 226  
 227          $count = $DB->count_records('task_scheduled', array('component' => 'moodle'));
 228          $allcount = $DB->count_records('task_scheduled');
 229  
 230          $task = new \core\task\scheduled_test_task();
 231          $task->set_component('moodle');
 232          $record = \core\task\manager::record_from_scheduled_task($task);
 233          $DB->insert_record('task_scheduled', $record);
 234          $this->assertTrue($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test_task',
 235              'component' => 'moodle')));
 236  
 237          $task = new \core\task\scheduled_test2_task();
 238          $task->set_component('moodle');
 239          $record = \core\task\manager::record_from_scheduled_task($task);
 240          $DB->insert_record('task_scheduled', $record);
 241          $this->assertTrue($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test2_task',
 242              'component' => 'moodle')));
 243  
 244          $aftercount = $DB->count_records('task_scheduled', array('component' => 'moodle'));
 245          $afterallcount = $DB->count_records('task_scheduled');
 246  
 247          $this->assertEquals($count + 2, $aftercount);
 248          $this->assertEquals($allcount + 2, $afterallcount);
 249  
 250          // Now check that the right things were deleted.
 251          \core\task\manager::reset_scheduled_tasks_for_component('moodle');
 252  
 253          $this->assertEquals($count, $DB->count_records('task_scheduled', array('component' => 'moodle')));
 254          $this->assertEquals($allcount, $DB->count_records('task_scheduled'));
 255          $this->assertFalse($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test2_task',
 256              'component' => 'moodle')));
 257          $this->assertFalse($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test_task',
 258              'component' => 'moodle')));
 259      }
 260  
 261      public function test_get_next_scheduled_task() {
 262          global $DB;
 263  
 264          $this->resetAfterTest(true);
 265          // Delete all existing scheduled tasks.
 266          $DB->delete_records('task_scheduled');
 267          // Add a scheduled task.
 268  
 269          // A task that runs once per hour.
 270          $record = new stdClass();
 271          $record->blocking = true;
 272          $record->minute = '0';
 273          $record->hour = '0';
 274          $record->dayofweek = '*';
 275          $record->day = '*';
 276          $record->month = '*';
 277          $record->component = 'test_scheduled_task';
 278          $record->classname = '\core\task\scheduled_test_task';
 279  
 280          $DB->insert_record('task_scheduled', $record);
 281          // And another one to test failures.
 282          $record->classname = '\core\task\scheduled_test2_task';
 283          $DB->insert_record('task_scheduled', $record);
 284          // And disabled test.
 285          $record->classname = '\core\task\scheduled_test3_task';
 286          $record->disabled = 1;
 287          $DB->insert_record('task_scheduled', $record);
 288  
 289          $now = time();
 290  
 291          // Should get handed the first task.
 292          $task = \core\task\manager::get_next_scheduled_task($now);
 293          $this->assertInstanceOf('\core\task\scheduled_test_task', $task);
 294          $task->execute();
 295  
 296          \core\task\manager::scheduled_task_complete($task);
 297          // Should get handed the second task.
 298          $task = \core\task\manager::get_next_scheduled_task($now);
 299          $this->assertInstanceOf('\core\task\scheduled_test2_task', $task);
 300          $task->execute();
 301  
 302          \core\task\manager::scheduled_task_failed($task);
 303          // Should not get any task.
 304          $task = \core\task\manager::get_next_scheduled_task($now);
 305          $this->assertNull($task);
 306  
 307          // Should get the second task (retry after delay).
 308          $task = \core\task\manager::get_next_scheduled_task($now + 120);
 309          $this->assertInstanceOf('\core\task\scheduled_test2_task', $task);
 310          $task->execute();
 311  
 312          \core\task\manager::scheduled_task_complete($task);
 313  
 314          // Should not get any task.
 315          $task = \core\task\manager::get_next_scheduled_task($now);
 316          $this->assertNull($task);
 317  
 318          // Check ordering.
 319          $DB->delete_records('task_scheduled');
 320          $record->lastruntime = 2;
 321          $record->disabled = 0;
 322          $record->classname = '\core\task\scheduled_test_task';
 323          $DB->insert_record('task_scheduled', $record);
 324  
 325          $record->lastruntime = 1;
 326          $record->classname = '\core\task\scheduled_test2_task';
 327          $DB->insert_record('task_scheduled', $record);
 328  
 329          // Should get handed the second task.
 330          $task = \core\task\manager::get_next_scheduled_task($now);
 331          $this->assertInstanceOf('\core\task\scheduled_test2_task', $task);
 332          $task->execute();
 333          \core\task\manager::scheduled_task_complete($task);
 334  
 335          // Should get handed the first task.
 336          $task = \core\task\manager::get_next_scheduled_task($now);
 337          $this->assertInstanceOf('\core\task\scheduled_test_task', $task);
 338          $task->execute();
 339          \core\task\manager::scheduled_task_complete($task);
 340  
 341          // Should not get any task.
 342          $task = \core\task\manager::get_next_scheduled_task($now);
 343          $this->assertNull($task);
 344      }
 345  
 346      public function test_get_broken_scheduled_task() {
 347          global $DB;
 348  
 349          $this->resetAfterTest(true);
 350          // Delete all existing scheduled tasks.
 351          $DB->delete_records('task_scheduled');
 352          // Add a scheduled task.
 353  
 354          // A broken task that runs all the time.
 355          $record = new stdClass();
 356          $record->blocking = true;
 357          $record->minute = '*';
 358          $record->hour = '*';
 359          $record->dayofweek = '*';
 360          $record->day = '*';
 361          $record->month = '*';
 362          $record->component = 'test_scheduled_task';
 363          $record->classname = '\core\task\scheduled_test_task_broken';
 364  
 365          $DB->insert_record('task_scheduled', $record);
 366  
 367          $now = time();
 368          // Should not get any task.
 369          $task = \core\task\manager::get_next_scheduled_task($now);
 370          $this->assertDebuggingCalled();
 371          $this->assertNull($task);
 372      }
 373  
 374      /**
 375       * Tests the use of 'R' syntax in time fields of tasks to get
 376       * tasks be configured with a non-uniform time.
 377       */
 378      public function test_random_time_specification() {
 379  
 380          // Testing non-deterministic things in a unit test is not really
 381          // wise, so we just test the values have changed within allowed bounds.
 382          $testclass = new \core\task\scheduled_test_task();
 383  
 384          // The test task defaults to '*'.
 385          $this->assertInternalType('string', $testclass->get_minute());
 386          $this->assertInternalType('string', $testclass->get_hour());
 387  
 388          // Set a random value.
 389          $testclass->set_minute('R');
 390          $testclass->set_hour('R');
 391          $testclass->set_day_of_week('R');
 392  
 393          // Verify the minute has changed within allowed bounds.
 394          $minute = $testclass->get_minute();
 395          $this->assertInternalType('int', $minute);
 396          $this->assertGreaterThanOrEqual(0, $minute);
 397          $this->assertLessThanOrEqual(59, $minute);
 398  
 399          // Verify the hour has changed within allowed bounds.
 400          $hour = $testclass->get_hour();
 401          $this->assertInternalType('int', $hour);
 402          $this->assertGreaterThanOrEqual(0, $hour);
 403          $this->assertLessThanOrEqual(23, $hour);
 404  
 405          // Verify the dayofweek has changed within allowed bounds.
 406          $dayofweek = $testclass->get_day_of_week();
 407          $this->assertInternalType('int', $dayofweek);
 408          $this->assertGreaterThanOrEqual(0, $dayofweek);
 409          $this->assertLessThanOrEqual(6, $dayofweek);
 410      }
 411  
 412      /**
 413       * Test that the file_temp_cleanup_task removes directories and
 414       * files as expected.
 415       */
 416      public function test_file_temp_cleanup_task() {
 417          global $CFG;
 418  
 419          // Create directories.
 420          $dir = $CFG->tempdir . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'backup01' . DIRECTORY_SEPARATOR . 'courses';
 421          mkdir($dir, 0777, true);
 422  
 423          // Create files to be checked and then deleted.
 424          $file01 = $dir . DIRECTORY_SEPARATOR . 'sections.xml';
 425          file_put_contents($file01, 'test data 001');
 426          $file02 = $dir . DIRECTORY_SEPARATOR . 'modules.xml';
 427          file_put_contents($file02, 'test data 002');
 428          // Change the time modified for the first file, to a time that will be deleted by the task (greater than seven days).
 429          touch($file01, time() - (8 * 24 * 3600));
 430  
 431          $task = \core\task\manager::get_scheduled_task('\\core\\task\\file_temp_cleanup_task');
 432          $this->assertInstanceOf('\core\task\file_temp_cleanup_task', $task);
 433          $task->execute();
 434  
 435          // Scan the directory. Only modules.xml should be left.
 436          $filesarray = scandir($dir);
 437          $this->assertEquals('modules.xml', $filesarray[2]);
 438          $this->assertEquals(3, count($filesarray));
 439  
 440          // Change the time modified on modules.xml.
 441          touch($file02, time() - (8 * 24 * 3600));
 442          // Change the time modified on the courses directory.
 443          touch($CFG->tempdir . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'backup01' . DIRECTORY_SEPARATOR .
 444                  'courses', time() - (8 * 24 * 3600));
 445          // Run the scheduled task to remove the file and directory.
 446          $task->execute();
 447          $filesarray = scandir($CFG->tempdir . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'backup01');
 448          // There should only be two items in the array, '.' and '..'.
 449          $this->assertEquals(2, count($filesarray));
 450  
 451          // Change the time modified on all of the files and directories.
 452          $dir = new \RecursiveDirectoryIterator($CFG->tempdir);
 453          // Show all child nodes prior to their parent.
 454          $iter = new \RecursiveIteratorIterator($dir, \RecursiveIteratorIterator::CHILD_FIRST);
 455  
 456          for ($iter->rewind(); $iter->valid(); $iter->next()) {
 457              if ($iter->isDir() && !$iter->isDot()) {
 458                  $node = $iter->getRealPath();
 459                  touch($node, time() - (8 * 24 * 3600));
 460              }
 461          }
 462  
 463          // Run the scheduled task again to remove all of the files and directories.
 464          $task->execute();
 465          $filesarray = scandir($CFG->tempdir);
 466          // All of the files and directories should be deleted.
 467          // There should only be two items in the array, '.' and '..'.
 468          $this->assertEquals(2, count($filesarray));
 469      }
 470  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1