Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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