Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 311 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

   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  namespace core\task;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  require_once (__DIR__ . '/../fixtures/task_fixtures.php');
  21  
  22  
  23  /**
  24   * Test class for adhoc tasks.
  25   *
  26   * @package core
  27   * @category test
  28   * @copyright 2013 Damyon Wiese
  29   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   * @coversDefaultClass \core\task\manager
  31   */
  32  class adhoc_task_test extends \advanced_testcase {
  33  
  34      /**
  35       * Test basic adhoc task execution.
  36       */
  37      public function test_get_next_adhoc_task_now() {
  38          $this->resetAfterTest(true);
  39  
  40          // Create an adhoc task.
  41          $task = new adhoc_test_task();
  42  
  43          // Queue it.
  44          manager::queue_adhoc_task($task);
  45  
  46          $now = time();
  47          // Get it from the scheduler.
  48          $task = manager::get_next_adhoc_task($now);
  49          $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
  50          $task->execute();
  51          manager::adhoc_task_complete($task);
  52      }
  53  
  54      /**
  55       * Test adhoc task failure retry backoff.
  56       *
  57       * @covers ::get_next_adhoc_task
  58       */
  59      public function test_get_next_adhoc_task_fail_retry() {
  60          $this->resetAfterTest(true);
  61  
  62          // Create an adhoc task.
  63          $task = new adhoc_test_task();
  64          manager::queue_adhoc_task($task);
  65  
  66          $now = time();
  67  
  68          // Get it from the scheduler, execute it, and mark it as failed.
  69          $task = manager::get_next_adhoc_task($now);
  70          $task->execute();
  71          manager::adhoc_task_failed($task);
  72  
  73          // The task will not be returned immediately.
  74          $this->assertNull(manager::get_next_adhoc_task($now));
  75  
  76          // Should get the adhoc task (retry after delay).
  77          $task = manager::get_next_adhoc_task($now + 120);
  78          $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
  79          $task->execute();
  80  
  81          manager::adhoc_task_complete($task);
  82  
  83          // Should not get any task.
  84          $this->assertNull(manager::get_next_adhoc_task($now));
  85      }
  86  
  87      /**
  88       * Test future adhoc task execution.
  89       * @covers ::get_next_adhoc_task
  90       */
  91      public function test_get_next_adhoc_task_future() {
  92          $this->resetAfterTest(true);
  93  
  94          $now = time();
  95          // Create an adhoc task in future.
  96          $task = new adhoc_test_task();
  97          $task->set_next_run_time($now + 1000);
  98          manager::queue_adhoc_task($task);
  99  
 100          // Fetching the next task should not return anything.
 101          $this->assertNull(manager::get_next_adhoc_task($now));
 102  
 103          // Fetching in the future should return the task.
 104          $task = manager::get_next_adhoc_task($now + 1020);
 105          $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
 106          $task->execute();
 107          manager::adhoc_task_complete($task);
 108      }
 109  
 110      /**
 111       * Test queueing an adhoc task belonging to a component, where we set the task component accordingly
 112       * @covers ::queue_adhoc_task
 113       */
 114      public function test_queue_adhoc_task_for_component(): void {
 115          $this->resetAfterTest();
 116  
 117          $task = new \mod_forum\task\refresh_forum_post_counts();
 118          $task->set_component('mod_test');
 119  
 120          manager::queue_adhoc_task($task);
 121          $this->assertDebuggingNotCalled();
 122      }
 123  
 124      /**
 125       * Test queueing an adhoc task belonging to a component, where we do not set the task component
 126       * @covers ::queue_adhoc_task
 127       */
 128      public function test_queue_task_for_component_without_set_component(): void {
 129          $this->resetAfterTest();
 130  
 131          $task = new \mod_forum\task\refresh_forum_post_counts();
 132  
 133          manager::queue_adhoc_task($task);
 134          $this->assertDebuggingNotCalled();
 135  
 136          // Assert the missing component was set.
 137          $this->assertEquals('mod_forum', $task->get_component());
 138      }
 139  
 140      /**
 141       * Test queueing an adhoc task belonging to an invalid component, where we do not set the task component
 142       * @covers ::queue_adhoc_task
 143       */
 144      public function test_queue_task_for_invalid_component_without_set_component(): void {
 145          $this->resetAfterTest();
 146  
 147          $task = new \mod_fake\task\adhoc_component_task();
 148  
 149          manager::queue_adhoc_task($task);
 150          $this->assertDebuggingCalled('Component not set and the class namespace does not match a valid component (mod_fake).');
 151      }
 152  
 153      /**
 154       * Test empty set of adhoc tasks
 155       * @covers ::get_adhoc_tasks
 156       */
 157      public function test_get_adhoc_tasks_empty_set() {
 158          $this->resetAfterTest(true);
 159  
 160          $this->assertEquals([], manager::get_adhoc_tasks('\\core\\task\\adhoc_test_task'));
 161      }
 162  
 163      /**
 164       * Test correct set of adhoc tasks is returned for class.
 165       * @covers ::get_adhoc_tasks
 166       */
 167      public function test_get_adhoc_tasks_result_set() {
 168          $this->resetAfterTest(true);
 169  
 170          for ($i = 0; $i < 3; $i++) {
 171              $task = new adhoc_test_task();
 172              manager::queue_adhoc_task($task);
 173          }
 174  
 175          for ($i = 0; $i < 3; $i++) {
 176              $task = new adhoc_test2_task();
 177              manager::queue_adhoc_task($task);
 178          }
 179  
 180          $adhoctests = manager::get_adhoc_tasks('\\core\\task\\adhoc_test_task');
 181          $adhoctest2s = manager::get_adhoc_tasks('\\core\\task\\adhoc_test2_task');
 182  
 183          $this->assertCount(3, $adhoctests);
 184          $this->assertCount(3, $adhoctest2s);
 185  
 186          foreach ($adhoctests as $task) {
 187              $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
 188          }
 189  
 190          foreach ($adhoctest2s as $task) {
 191              $this->assertInstanceOf('\\core\\task\\adhoc_test2_task', $task);
 192          }
 193      }
 194  
 195      /**
 196       * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if no tasks exist.
 197       * @covers ::reschedule_or_queue_adhoc_task
 198       */
 199      public function test_reschedule_or_queue_adhoc_task_no_existing() {
 200          $this->resetAfterTest(true);
 201  
 202          // Schedule adhoc task.
 203          $task = new adhoc_test_task();
 204          $task->set_custom_data(['courseid' => 10]);
 205          manager::reschedule_or_queue_adhoc_task($task);
 206          $this->assertEquals(1, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 207      }
 208  
 209      /**
 210       * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if a task for the same user does
 211       * not exist.
 212       * @covers ::reschedule_or_queue_adhoc_task
 213       */
 214      public function test_reschedule_or_queue_adhoc_task_different_user() {
 215          $this->resetAfterTest(true);
 216          $user = \core_user::get_user_by_username('admin');
 217  
 218          // Schedule adhoc task.
 219          $task = new adhoc_test_task();
 220          $task->set_custom_data(['courseid' => 10]);
 221          manager::reschedule_or_queue_adhoc_task($task);
 222  
 223          // Schedule adhoc task for a different user.
 224          $task = new adhoc_test_task();
 225          $task->set_custom_data(['courseid' => 10]);
 226          $task->set_userid($user->id);
 227          manager::reschedule_or_queue_adhoc_task($task);
 228  
 229          $this->assertEquals(2, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 230      }
 231  
 232      /**
 233       * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if a task with different custom
 234       * data exists.
 235       * @covers ::reschedule_or_queue_adhoc_task
 236       */
 237      public function test_reschedule_or_queue_adhoc_task_different_data() {
 238          $this->resetAfterTest(true);
 239  
 240          // Schedule adhoc task.
 241          $task = new adhoc_test_task();
 242          $task->set_custom_data(['courseid' => 10]);
 243          manager::reschedule_or_queue_adhoc_task($task);
 244  
 245          // Schedule adhoc task for a different user.
 246          $task = new adhoc_test_task();
 247          $task->set_custom_data(['courseid' => 11]);
 248          manager::reschedule_or_queue_adhoc_task($task);
 249  
 250          $this->assertEquals(2, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 251      }
 252  
 253      /**
 254       * Ensure that the reschedule_or_queue_adhoc_task function will not make any change for matching data if no time was
 255       * specified.
 256       * @covers ::reschedule_or_queue_adhoc_task
 257       */
 258      public function test_reschedule_or_queue_adhoc_task_match_no_change() {
 259          $this->resetAfterTest(true);
 260  
 261          // Schedule adhoc task.
 262          $task = new adhoc_test_task();
 263          $task->set_custom_data(['courseid' => 10]);
 264          $task->set_next_run_time(time() + DAYSECS);
 265          manager::reschedule_or_queue_adhoc_task($task);
 266  
 267          $before = manager::get_adhoc_tasks('core\task\adhoc_test_task');
 268  
 269          // Schedule the task again but do not specify a time.
 270          $task = new adhoc_test_task();
 271          $task->set_custom_data(['courseid' => 10]);
 272          manager::reschedule_or_queue_adhoc_task($task);
 273  
 274          $this->assertEquals(1, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 275          $this->assertEquals($before, manager::get_adhoc_tasks('core\task\adhoc_test_task'));
 276      }
 277  
 278      /**
 279       * Ensure that the reschedule_or_queue_adhoc_task function will update the run time if there are planned changes.
 280       * @covers ::reschedule_or_queue_adhoc_task
 281       */
 282      public function test_reschedule_or_queue_adhoc_task_match_update_runtime() {
 283          $this->resetAfterTest(true);
 284          $initialruntime = time() + DAYSECS;
 285          $newruntime = time() + WEEKSECS;
 286  
 287          // Schedule adhoc task.
 288          $task = new adhoc_test_task();
 289          $task->set_custom_data(['courseid' => 10]);
 290          $task->set_next_run_time($initialruntime);
 291          manager::reschedule_or_queue_adhoc_task($task);
 292  
 293          $before = manager::get_adhoc_tasks('core\task\adhoc_test_task');
 294  
 295          // Schedule the task again.
 296          $task = new adhoc_test_task();
 297          $task->set_custom_data(['courseid' => 10]);
 298          $task->set_next_run_time($newruntime);
 299          manager::reschedule_or_queue_adhoc_task($task);
 300  
 301          $tasks = manager::get_adhoc_tasks('core\task\adhoc_test_task');
 302          $this->assertEquals(1, count($tasks));
 303          $this->assertNotEquals($before, $tasks);
 304          $firsttask = reset($tasks);
 305          $this->assertEquals($newruntime, $firsttask->get_next_run_time());
 306      }
 307  
 308      /**
 309       * Test queue_adhoc_task "if not scheduled".
 310       * @covers ::queue_adhoc_task
 311       */
 312      public function test_queue_adhoc_task_if_not_scheduled() {
 313          $this->resetAfterTest(true);
 314          $user = \core_user::get_user_by_username('admin');
 315  
 316          // Schedule adhoc task.
 317          $task = new adhoc_test_task();
 318          $task->set_custom_data(array('courseid' => 10));
 319          $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
 320          $this->assertEquals(1, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 321  
 322          // Schedule adhoc task with a user.
 323          $task = new adhoc_test_task();
 324          $task->set_custom_data(array('courseid' => 10));
 325          $task->set_userid($user->id);
 326          $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
 327          $this->assertEquals(2, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 328  
 329          // Schedule same adhoc task with different custom data.
 330          $task = new adhoc_test_task();
 331          $task->set_custom_data(array('courseid' => 1));
 332          $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
 333          $this->assertEquals(3, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 334  
 335          // Schedule same adhoc task with same custom data.
 336          $task = new adhoc_test_task();
 337          $task->set_custom_data(array('courseid' => 1));
 338          $this->assertEmpty(manager::queue_adhoc_task($task, true));
 339          $this->assertEquals(3, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 340  
 341          // Schedule same adhoc task with same custom data and a user.
 342          $task = new adhoc_test_task();
 343          $task->set_custom_data(array('courseid' => 1));
 344          $task->set_userid($user->id);
 345          $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
 346          $this->assertEquals(4, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 347  
 348          // Schedule same adhoc task without custom data.
 349          // Note: This task was created earlier.
 350          $task = new adhoc_test_task();
 351          $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
 352          $this->assertEquals(5, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 353  
 354          // Schedule same adhoc task without custom data (again).
 355          $task5 = new adhoc_test_task();
 356          $this->assertEmpty(manager::queue_adhoc_task($task5, true));
 357          $this->assertEquals(5, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 358  
 359          // Schedule same adhoc task without custom data but with a userid.
 360          $task6 = new adhoc_test_task();
 361          $user = \core_user::get_user_by_username('admin');
 362          $task6->set_userid($user->id);
 363          $this->assertNotEmpty(manager::queue_adhoc_task($task6, true));
 364          $this->assertEquals(6, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 365  
 366          // Schedule same adhoc task again without custom data but with a userid.
 367          $task6 = new adhoc_test_task();
 368          $user = \core_user::get_user_by_username('admin');
 369          $task6->set_userid($user->id);
 370          $this->assertEmpty(manager::queue_adhoc_task($task6, true));
 371          $this->assertEquals(6, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
 372      }
 373  
 374      /**
 375       * Test that when no userid is specified, it returns empty from the DB
 376       * too.
 377       * @covers \core\task\adhoc_task::get_userid
 378       */
 379      public function test_adhoc_task_user_empty() {
 380          $this->resetAfterTest(true);
 381  
 382          // Create an adhoc task in future.
 383          $task = new adhoc_test_task();
 384          manager::queue_adhoc_task($task);
 385  
 386          // Get it back from the scheduler.
 387          $now = time();
 388          $task = manager::get_next_adhoc_task($now);
 389          manager::adhoc_task_complete($task);
 390  
 391          $this->assertEmpty($task->get_userid());
 392      }
 393  
 394      /**
 395       * Test that when a userid is specified, that userid is subsequently
 396       * returned.
 397       *
 398       * @covers \core\task\adhoc_task::set_userid
 399       * @covers \core\task\adhoc_task::get_userid
 400       */
 401      public function test_adhoc_task_user_set() {
 402          $this->resetAfterTest(true);
 403  
 404          // Create an adhoc task in future.
 405          $task = new adhoc_test_task();
 406          $user = \core_user::get_user_by_username('admin');
 407          $task->set_userid($user->id);
 408          manager::queue_adhoc_task($task);
 409  
 410          // Get it back from the scheduler.
 411          $now = time();
 412          $task = manager::get_next_adhoc_task($now);
 413          manager::adhoc_task_complete($task);
 414  
 415          $this->assertEquals($user->id, $task->get_userid());
 416      }
 417  
 418      /**
 419       * Test get_concurrency_limit() method to return 0 by default.
 420       *
 421       * @covers \core\task\adhoc_task::get_concurrency_limit
 422       */
 423      public function test_get_concurrency_limit() {
 424          $this->resetAfterTest(true);
 425          $task = new adhoc_test_task();
 426          $concurrencylimit = $task->get_concurrency_limit();
 427          $this->assertEquals(0, $concurrencylimit);
 428      }
 429  
 430      /**
 431       * Test get_concurrency_limit() method to return a default value set in config.
 432       * @covers \core\task\adhoc_task::get_concurrency_limit
 433       */
 434      public function test_get_concurrency_limit_default() {
 435          $this->resetAfterTest(true);
 436          set_config('task_concurrency_limit_default', 10);
 437          $task = new adhoc_test_task();
 438          $concurrencylimit = $task->get_concurrency_limit();
 439          $this->assertEquals(10, $concurrencylimit);
 440      }
 441  
 442      /**
 443       * Test get_concurrency_limit() method to return a value for specific task class.
 444       * @covers \core\task\adhoc_task::get_concurrency_limit
 445       */
 446      public function test_get_concurrency_limit_for_task() {
 447          global $CFG;
 448          $this->resetAfterTest(true);
 449          set_config('task_concurrency_limit_default', 10);
 450          $CFG->task_concurrency_limit = array('core\task\adhoc_test_task' => 5);
 451          $task = new adhoc_test_task();
 452          $concurrencylimit = $task->get_concurrency_limit();
 453          $this->assertEquals(5, $concurrencylimit);
 454      }
 455  
 456      /**
 457       * Test adhoc task sorting.
 458       * @covers ::get_next_adhoc_task
 459       */
 460      public function test_get_next_adhoc_task_sorting() {
 461          $this->resetAfterTest(true);
 462  
 463          // Create adhoc tasks.
 464          $task1 = new adhoc_test_task();
 465          $task1->set_next_run_time(1510000000);
 466          $task1->set_custom_data_as_string('Task 1');
 467          manager::queue_adhoc_task($task1);
 468  
 469          $task2 = new adhoc_test_task();
 470          $task2->set_next_run_time(1520000000);
 471          $task2->set_custom_data_as_string('Task 2');
 472          manager::queue_adhoc_task($task2);
 473  
 474          $task3 = new adhoc_test_task();
 475          $task3->set_next_run_time(1520000000);
 476          $task3->set_custom_data_as_string('Task 3');
 477          manager::queue_adhoc_task($task3);
 478  
 479          // Shuffle tasks.
 480          $task1->set_next_run_time(1540000000);
 481          manager::reschedule_or_queue_adhoc_task($task1);
 482  
 483          $task3->set_next_run_time(1530000000);
 484          manager::reschedule_or_queue_adhoc_task($task3);
 485  
 486          $task2->set_next_run_time(1530000000);
 487          manager::reschedule_or_queue_adhoc_task($task2);
 488  
 489          // Confirm, that tasks are sorted by nextruntime and then by id (ascending).
 490          $task = manager::get_next_adhoc_task(time());
 491          $this->assertEquals('Task 2', $task->get_custom_data_as_string());
 492          manager::adhoc_task_complete($task);
 493  
 494          $task = manager::get_next_adhoc_task(time());
 495          $this->assertEquals('Task 3', $task->get_custom_data_as_string());
 496          manager::adhoc_task_complete($task);
 497  
 498          $task = manager::get_next_adhoc_task(time());
 499          $this->assertEquals('Task 1', $task->get_custom_data_as_string());
 500          manager::adhoc_task_complete($task);
 501      }
 502  }