Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
/**
20
 * Test class for adhoc tasks.
21
 *
22
 * @package core
23
 * @category test
24
 * @copyright 2013 Damyon Wiese
25
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 * @covers \core\task\manager
27
 */
28
final class adhoc_task_test extends \advanced_testcase {
29
    public static function setUpBeforeClass(): void {
30
        parent::setUpBeforeClass();
31
 
32
        require_once(__DIR__ . '/../fixtures/task_fixtures.php');
33
    }
34
 
35
    /**
36
     * Test getting name of task that implements it's own get_name method
37
     *
38
     * @covers \core\task\adhoc_task
39
     */
40
    public function test_get_name(): void {
41
        $task = new \core\task\adhoc_test_task();
42
        $this->assertEquals('Test adhoc class', $task->get_name());
43
    }
44
 
45
    /**
46
     * Test getting name of task that uses the default implementation of get_name
47
     *
48
     * @covers \core\task\adhoc_task
49
     */
50
    public function test_get_name_default(): void {
51
        $task = new \mod_fake\task\adhoc_component_task();
52
        $this->assertEquals('Adhoc component task', $task->get_name());
53
    }
54
 
55
    /**
56
     * Test basic adhoc task execution.
57
     */
58
    public function test_get_next_adhoc_task_now(): void {
59
        $this->resetAfterTest(true);
60
 
61
        // Create an adhoc task.
62
        $task = new adhoc_test_task();
63
 
64
        // Queue it.
65
        manager::queue_adhoc_task($task);
66
 
67
        $now = time();
68
        // Get it from the scheduler.
69
        $task = manager::get_next_adhoc_task($now);
70
        $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
71
        $task->execute();
72
        manager::adhoc_task_complete($task);
73
    }
74
 
75
    /**
76
     * Test basic adhoc task execution.
77
     */
78
    public function test_get_next_adhoc_task_class(): void {
79
        $this->resetAfterTest(true);
80
 
81
        // Create an adhoc task.
82
        $task = new \core\task\adhoc_test_task();
83
 
84
        // Queue it.
85
        manager::queue_adhoc_task($task);
86
 
87
        $now = time();
88
        $classname = get_class($task);
89
 
90
        // The task will not be returned.
91
        $this->assertNull(manager::get_next_adhoc_task($now, true, "{$classname}notexists"));
92
 
93
        // Get it from the scheduler.
94
        $task = manager::get_next_adhoc_task($now, true, $classname);
95
        $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
96
        $task->execute();
97
        manager::adhoc_task_complete($task);
98
    }
99
 
100
    /**
101
     * Test adhoc task failure retry backoff.
102
     */
103
    public function test_get_next_adhoc_task_fail_retry(): void {
104
        $this->resetAfterTest(true);
105
 
106
        // Create an adhoc task.
107
        $task = new adhoc_test_task();
108
        manager::queue_adhoc_task($task);
109
 
110
        $now = time();
111
 
112
        // Get it from the scheduler, execute it, and mark it as failed.
113
        $task = manager::get_next_adhoc_task($now);
114
        $taskid = $task->get_id();
115
        $task->execute();
116
        manager::adhoc_task_failed($task);
117
 
118
        // The task will not be returned immediately.
119
        $this->assertNull(manager::get_next_adhoc_task($now));
120
 
121
        // Should get the adhoc task (retry after delay). Fail it again.
122
        $task = manager::get_next_adhoc_task($now + 120);
123
        $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
124
        $this->assertEquals($taskid, $task->get_id());
125
        $task->execute();
126
        manager::adhoc_task_failed($task);
127
 
128
        // Should get the adhoc task immediately.
129
        $task = manager::get_adhoc_task($taskid);
130
        $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
131
        $this->assertEquals($taskid, $task->get_id());
132
        $task->execute();
133
        manager::adhoc_task_complete($task);
134
 
135
        // Should not get any task.
136
        $this->assertNull(manager::get_next_adhoc_task($now));
137
    }
138
 
139
    /**
140
     * Test that failed tasks eventually hit the maximum delay.
141
     *
142
     * @covers \core\task\adhoc_task
143
     */
144
    public function test_get_next_adhoc_task_maximum_fail_delay(): void {
145
        $this->resetAfterTest(true);
146
 
147
        $now = time();
148
 
149
        // Create an adhoc task.
150
        $task = new adhoc_test_task();
151
        $attemptsavailable = $task->get_attempts_available();
152
        manager::queue_adhoc_task($task);
153
 
154
        // Exhaust all attempts available.
155
        for ($x = 0; $x < $attemptsavailable; $x++) {
156
            $delay = $task->get_fail_delay() * 2;
157
            $task = manager::get_next_adhoc_task($now + $delay);
158
            $task->execute();
159
            manager::adhoc_task_failed($task);
160
        }
161
        // Check that the fail delay is now set to 24 hours (the maximum amount of times).
162
        $this->assertEquals(DAYSECS, $task->get_fail_delay());
163
    }
164
 
165
    /**
166
     * Test adhoc task failure retry backoff.
167
     */
168
    public function test_adhoc_task_with_retry_flag(): void {
169
        global $DB;
170
        $this->resetAfterTest();
171
 
172
        $now = time();
173
        // Create a normal adhoc task.
174
        $task = new adhoc_test_task();
175
        $taskid1 = manager::queue_adhoc_task(task: $task);
176
 
177
        // This is a normal task, so it should have limited attempts.
178
        $attemptsavailable = $DB->get_field(
179
            table: 'task_adhoc',
180
            return: 'attemptsavailable',
181
            conditions: ['id' => $taskid1]
182
        );
183
        $this->assertEquals(expected: 12, actual: $attemptsavailable);
184
 
185
        // Get the task from the scheduler, execute it, and mark it as failed.
186
        $task = manager::get_next_adhoc_task(timestart: $now);
187
        $taskid1 = $task->get_id();
188
        $task->execute();
189
        manager::adhoc_task_failed(task: $task);
190
 
191
        // Now that the task has failed, there should be one less attempt available.
192
        $attemptsavailable = $DB->get_field(
193
            table: 'task_adhoc',
194
            return: 'attemptsavailable',
195
            conditions: ['id' => $taskid1]
196
        );
197
        $this->assertEquals(expected: 12 - 1, actual: $attemptsavailable);
198
 
199
        // Create a no-retry adhoc task.
200
        $now = time();
201
        $task = new no_retry_adhoc_task();
202
        $taskid2 = manager::queue_adhoc_task(task: $task);
203
 
204
        // This is no-retry task, so it should have only 1 attempt available.
205
        $attemptsavailable = $DB->get_field(
206
            table: 'task_adhoc',
207
            return: 'attemptsavailable',
208
            conditions: ['id' => $taskid2]
209
        );
210
        $this->assertEquals(
211
            expected: 1,
212
            actual: $attemptsavailable,
213
        );
214
 
215
        // Get the task from the scheduler, execute it, and mark it as failed.
216
        $task = manager::get_next_adhoc_task(timestart: $now);
217
        $taskid2 = $task->get_id();
218
        $task->execute();
219
        manager::adhoc_task_failed(task: $task);
220
 
221
        // This is no-retry task, the remaining available attempts should be reduced to 0.
222
        $attemptsavailable = $DB->get_field(
223
            table: 'task_adhoc',
224
            return: 'attemptsavailable',
225
            conditions: ['id' => $taskid2]
226
        );
227
        $this->assertEquals(
228
            expected: 0,
229
            actual: $attemptsavailable,
230
        );
231
 
232
        // There will be two records in the task_adhoc table, one for each task.
233
        $this->assertEquals(
234
            expected: 2,
235
            actual: $DB->count_records(table: 'task_adhoc')
236
        );
237
        // But get_next_adhoc_task() should return only the allowed re-try task.
238
        // The no-retry task should not be returned because it has no remaining attempts.
239
        do {
240
            $task = manager::get_next_adhoc_task(timestart: $now + 86400);
241
            if ($task) {
242
                manager::adhoc_task_failed(task: $task);
243
                $this->assertEquals(
244
                    expected: $taskid1,
245
                    actual: $task->get_id(),
246
                );
247
            }
248
        } while ($task);
249
 
250
        // Mark the normal task as complete.
251
        $task = manager::get_adhoc_task(taskid: $taskid1);
252
        manager::adhoc_task_complete($task);
253
 
254
        // There will be one record in the task_adhoc table.
255
        $this->assertEquals(
256
            expected: 1,
257
            actual: $DB->count_records(table: 'task_adhoc')
258
        );
259
 
260
        // But get_next_adhoc_task() should return nothing.
261
        $this->assertNull(manager::get_next_adhoc_task(timestart: $now + 86400));
262
    }
263
 
264
    /**
265
     * Test adhoc task failure cleanup.
266
     */
267
    public function test_adhoc_task_clean_up(): void {
268
        global $DB, $CFG;
269
        $this->resetAfterTest();
270
 
271
        // Create two no-retry adhoc tasks.
272
        $task1 = new no_retry_adhoc_task();
273
        $taskid1 = manager::queue_adhoc_task(task: $task1);
274
        $task2 = new no_retry_adhoc_task();
275
        $taskid2 = manager::queue_adhoc_task(task: $task2);
276
 
277
        // Get the tasks and mark it as failed.
278
        $task = manager::get_adhoc_task($taskid1);
279
        manager::adhoc_task_failed(task: $task);
280
        $task = manager::get_adhoc_task($taskid2);
281
        manager::adhoc_task_failed(task: $task);
282
 
283
        // These are no-retry tasks, the remaining available attempts should be reduced to 0.
284
        $this->assertEquals(
285
            expected: 0,
286
            actual: $DB->get_field(
287
                table: 'task_adhoc',
288
                return: 'attemptsavailable',
289
                conditions: ['id' => $taskid1],
290
            ),
291
        );
292
        $this->assertEquals(
293
            expected: 0,
294
            actual: $DB->get_field(
295
                table: 'task_adhoc',
296
                return: 'attemptsavailable',
297
                conditions: ['id' => $taskid2],
298
            ),
299
        );
300
 
301
        // There will be two records in the task_adhoc table.
302
        $this->assertEquals(
303
            expected: 2,
304
            actual: $DB->count_records(table: 'task_adhoc'),
305
        );
306
 
307
        // Clean up failed adhoc tasks. This will clean nothing because the tasks are not old enough.
308
        manager::clean_failed_adhoc_tasks();
309
 
310
        // There will be two records in the task_adhoc table.
311
        $this->assertEquals(
312
            expected: 2,
313
            actual: $DB->count_records(table: 'task_adhoc'),
314
        );
315
 
316
        // Update the time of the task2 to be older more than 2 days.
317
        $DB->set_field(
318
            table: 'task_adhoc',
319
            newfield: 'firststartingtime',
320
            newvalue: time() - (DAYSECS * 2) - 10, // Plus 10 seconds to make sure it is older than 2 days.
321
            conditions: ['id' => $taskid2],
322
        );
323
 
324
        // Clean up failed adhoc tasks. This will clean nothing because the tasks are not old enough.
325
        manager::clean_failed_adhoc_tasks();
326
 
327
        // There will be two records in the task_adhoc table.
328
        $this->assertEquals(
329
            expected: 2,
330
            actual: $DB->count_records(table: 'task_adhoc'),
331
        );
332
 
333
        // Update the time of the task1 to be older than the cleanup time.
334
        $DB->set_field(
335
            table: 'task_adhoc',
336
            newfield: 'firststartingtime',
337
            // Plus 10 seconds to make sure it is older than the retention time.
338
            newvalue: time() - $CFG->task_adhoc_failed_retention - 10,
339
            conditions: ['id' => $taskid1],
340
        );
341
 
342
        // Clean up failed adhoc tasks. task1 should be cleaned now.
343
        manager::clean_failed_adhoc_tasks();
344
 
345
        // There will be one record in the task_adhoc table.
346
        $this->assertEquals(
347
            expected: 1,
348
            actual: $DB->count_records(table: 'task_adhoc'),
349
        );
350
 
351
        // Update the duration of the Failed ad hoc task retention period to one day.
352
        $CFG->task_adhoc_failed_retention = DAYSECS;
353
 
354
        // Clean up failed adhoc tasks. task2 should be cleaned now.
355
        manager::clean_failed_adhoc_tasks();
356
 
357
        // The task_adhoc table should be empty now.
358
        $this->assertEquals(
359
            expected: 0,
360
            actual: $DB->count_records(table: 'task_adhoc'),
361
        );
362
    }
363
 
364
    /**
365
     * Test adhoc task failure will retain the time information.
366
     */
367
    public function test_adhoc_task_failed_will_retain_time_info(): void {
368
        global $DB;
369
        $this->resetAfterTest();
370
 
371
        $now = time();
372
        // Create an adhoc task.
373
        $task = new adhoc_test_task();
374
        // Queue it.
375
        $taskid = manager::queue_adhoc_task(task: $task);
376
 
377
        // Update the timecreated of the task to be older.
378
        $DB->set_field(
379
            table: 'task_adhoc',
380
            newfield: 'timecreated',
381
            newvalue: time() - DAYSECS,
382
            conditions: ['id' => $taskid],
383
        );
384
 
385
        // Get the timecreated value before marking the task as failed.
386
        $timecreatedbefore = $DB->get_field(
387
            table: 'task_adhoc',
388
            return: 'timecreated',
389
            conditions: ['id' => $taskid],
390
        );
391
 
392
        // Get the task from the scheduler.
393
        $task = manager::get_next_adhoc_task(timestart: $now);
394
        // Execute the task.
395
        $task->execute();
396
        // Mark the task as failed.
397
        manager::adhoc_task_failed(task: $task);
398
 
399
        // Get the timecreated value after marking the task as failed.
400
        $timecreatedafter = $DB->get_field(
401
            table: 'task_adhoc',
402
            return: 'timecreated',
403
            conditions: ['id' => $taskid],
404
        );
405
 
406
        // The timecreated values should be the same.
407
        $this->assertEquals($timecreatedbefore, $timecreatedafter);
408
    }
409
 
410
    /**
411
     * Test future adhoc task execution.
412
     */
413
    public function test_get_next_adhoc_task_future(): void {
414
        $this->resetAfterTest(true);
415
 
416
        $now = time();
417
        // Create an adhoc task in future.
418
        $task = new adhoc_test_task();
419
        $task->set_next_run_time($now + 1000);
420
        manager::queue_adhoc_task($task);
421
 
422
        // Fetching the next task should not return anything.
423
        $this->assertNull(manager::get_next_adhoc_task($now));
424
 
425
        // Fetching in the future should return the task.
426
        $task = manager::get_next_adhoc_task($now + 1020);
427
        $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
428
        $task->execute();
429
        manager::adhoc_task_complete($task);
430
    }
431
 
432
    /**
433
     * Test queueing an adhoc task belonging to a component, where we set the task component accordingly
434
     */
435
    public function test_queue_adhoc_task_for_component(): void {
436
        $this->resetAfterTest();
437
 
438
        $task = new \mod_forum\task\send_user_digests();
439
        $task->set_component('mod_test');
440
 
441
        manager::queue_adhoc_task($task);
442
        $this->assertDebuggingNotCalled();
443
    }
444
 
445
    /**
446
     * Test queueing an adhoc task belonging to a component, where we do not set the task component
447
     */
448
    public function test_queue_task_for_component_without_set_component(): void {
449
        $this->resetAfterTest();
450
 
451
        $task = new \mod_forum\task\send_user_digests();
452
 
453
        manager::queue_adhoc_task($task);
454
        $this->assertDebuggingNotCalled();
455
 
456
        // Assert the missing component was set.
457
        $this->assertEquals('mod_forum', $task->get_component());
458
    }
459
 
460
    /**
461
     * Test queueing an adhoc task belonging to an invalid component, where we do not set the task component
462
     */
463
    public function test_queue_task_for_invalid_component_without_set_component(): void {
464
        $this->resetAfterTest();
465
 
466
        $task = new \mod_fake\task\adhoc_component_task();
467
 
468
        manager::queue_adhoc_task($task);
469
        $this->assertDebuggingCalled('Component not set and the class namespace does not match a valid component (mod_fake).');
470
    }
471
 
472
    /**
473
     * Test empty set of adhoc tasks
474
     */
475
    public function test_get_adhoc_tasks_empty_set(): void {
476
        $this->resetAfterTest(true);
477
 
478
        $this->assertEquals([], manager::get_adhoc_tasks('\\core\\task\\adhoc_test_task'));
479
    }
480
 
481
    /**
482
     * Test correct set of adhoc tasks is returned for class.
483
     */
484
    public function test_get_adhoc_tasks_result_set(): void {
485
        $this->resetAfterTest(true);
486
 
487
        for ($i = 0; $i < 3; $i++) {
488
            $task = new adhoc_test_task();
489
            manager::queue_adhoc_task($task);
490
        }
491
 
492
        for ($i = 0; $i < 3; $i++) {
493
            $task = new adhoc_test2_task();
494
            manager::queue_adhoc_task($task);
495
        }
496
 
497
        $adhoctests = manager::get_adhoc_tasks('\\core\\task\\adhoc_test_task');
498
        $adhoctest2s = manager::get_adhoc_tasks('\\core\\task\\adhoc_test2_task');
499
 
500
        $this->assertCount(3, $adhoctests);
501
        $this->assertCount(3, $adhoctest2s);
502
 
503
        foreach ($adhoctests as $task) {
504
            $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
505
        }
506
 
507
        foreach ($adhoctest2s as $task) {
508
            $this->assertInstanceOf('\\core\\task\\adhoc_test2_task', $task);
509
        }
510
    }
511
 
512
    /**
513
     * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if no tasks exist.
514
     */
515
    public function test_reschedule_or_queue_adhoc_task_no_existing(): void {
516
        $this->resetAfterTest(true);
517
 
518
        // Schedule adhoc task.
519
        $task = new adhoc_test_task();
520
        $task->set_custom_data(['courseid' => 10]);
521
        manager::reschedule_or_queue_adhoc_task($task);
522
        $this->assertEquals(1, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
523
    }
524
 
525
    /**
526
     * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if a task for the same user does
527
     * not exist.
528
     */
529
    public function test_reschedule_or_queue_adhoc_task_different_user(): void {
530
        $this->resetAfterTest(true);
531
        $user = \core_user::get_user_by_username('admin');
532
 
533
        // Schedule adhoc task.
534
        $task = new adhoc_test_task();
535
        $task->set_custom_data(['courseid' => 10]);
536
        manager::reschedule_or_queue_adhoc_task($task);
537
 
538
        // Schedule adhoc task for a different user.
539
        $task = new adhoc_test_task();
540
        $task->set_custom_data(['courseid' => 10]);
541
        $task->set_userid($user->id);
542
        manager::reschedule_or_queue_adhoc_task($task);
543
 
544
        $this->assertEquals(2, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
545
    }
546
 
547
    /**
548
     * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if a task with different custom
549
     * data exists.
550
     */
551
    public function test_reschedule_or_queue_adhoc_task_different_data(): void {
552
        $this->resetAfterTest(true);
553
 
554
        // Schedule adhoc task.
555
        $task = new adhoc_test_task();
556
        $task->set_custom_data(['courseid' => 10]);
557
        manager::reschedule_or_queue_adhoc_task($task);
558
 
559
        // Schedule adhoc task for a different user.
560
        $task = new adhoc_test_task();
561
        $task->set_custom_data(['courseid' => 11]);
562
        manager::reschedule_or_queue_adhoc_task($task);
563
 
564
        $this->assertEquals(2, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
565
    }
566
 
567
    /**
568
     * Ensure that the reschedule_or_queue_adhoc_task function will not make any change for matching data if no time was
569
     * specified.
570
     */
571
    public function test_reschedule_or_queue_adhoc_task_match_no_change(): void {
572
        $this->resetAfterTest(true);
573
 
574
        // Schedule adhoc task.
575
        $task = new adhoc_test_task();
576
        $task->set_custom_data(['courseid' => 10]);
577
        $task->set_next_run_time(time() + DAYSECS);
578
        manager::reschedule_or_queue_adhoc_task($task);
579
 
580
        $before = manager::get_adhoc_tasks('core\task\adhoc_test_task');
581
 
582
        // Schedule the task again but do not specify a time.
583
        $task = new adhoc_test_task();
584
        $task->set_custom_data(['courseid' => 10]);
585
        manager::reschedule_or_queue_adhoc_task($task);
586
 
587
        $this->assertEquals(1, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
588
        $this->assertEquals($before, manager::get_adhoc_tasks('core\task\adhoc_test_task'));
589
    }
590
 
591
    /**
592
     * Ensure that the reschedule_or_queue_adhoc_task function will update the run time if there are planned changes.
593
     */
594
    public function test_reschedule_or_queue_adhoc_task_match_update_runtime(): void {
595
        $this->resetAfterTest(true);
596
        $initialruntime = time() + DAYSECS;
597
        $newruntime = time() + WEEKSECS;
598
 
599
        // Schedule adhoc task.
600
        $task = new adhoc_test_task();
601
        $task->set_custom_data(['courseid' => 10]);
602
        $task->set_next_run_time($initialruntime);
603
        manager::reschedule_or_queue_adhoc_task($task);
604
 
605
        $before = manager::get_adhoc_tasks('core\task\adhoc_test_task');
606
 
607
        // Schedule the task again.
608
        $task = new adhoc_test_task();
609
        $task->set_custom_data(['courseid' => 10]);
610
        $task->set_next_run_time($newruntime);
611
        manager::reschedule_or_queue_adhoc_task($task);
612
 
613
        $tasks = manager::get_adhoc_tasks('core\task\adhoc_test_task');
614
        $this->assertEquals(1, count($tasks));
615
        $this->assertNotEquals($before, $tasks);
616
        $firsttask = reset($tasks);
617
        $this->assertEquals($newruntime, $firsttask->get_next_run_time());
618
    }
619
 
620
    /**
621
     * Test queue_adhoc_task "if not scheduled".
622
     */
623
    public function test_queue_adhoc_task_if_not_scheduled(): void {
624
        $this->resetAfterTest(true);
625
        $user = \core_user::get_user_by_username('admin');
626
 
627
        // Schedule adhoc task.
628
        $task = new adhoc_test_task();
629
        $task->set_custom_data(['courseid' => 10]);
630
        $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
631
        $this->assertEquals(1, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
632
 
633
        // Schedule adhoc task with a user.
634
        $task = new adhoc_test_task();
635
        $task->set_custom_data(['courseid' => 10]);
636
        $task->set_userid($user->id);
637
        $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
638
        $this->assertEquals(2, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
639
 
640
        // Schedule same adhoc task with different custom data.
641
        $task = new adhoc_test_task();
642
        $task->set_custom_data(['courseid' => 1]);
643
        $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
644
        $this->assertEquals(3, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
645
 
646
        // Schedule same adhoc task with same custom data.
647
        $task = new adhoc_test_task();
648
        $task->set_custom_data(['courseid' => 1]);
649
        $this->assertEmpty(manager::queue_adhoc_task($task, true));
650
        $this->assertEquals(3, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
651
 
652
        // Schedule same adhoc task with same custom data and a user.
653
        $task = new adhoc_test_task();
654
        $task->set_custom_data(['courseid' => 1]);
655
        $task->set_userid($user->id);
656
        $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
657
        $this->assertEquals(4, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
658
 
659
        // Schedule same adhoc task without custom data.
660
        // Note: This task was created earlier.
661
        $task = new adhoc_test_task();
662
        $this->assertNotEmpty(manager::queue_adhoc_task($task, true));
663
        $this->assertEquals(5, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
664
 
665
        // Schedule same adhoc task without custom data (again).
666
        $task5 = new adhoc_test_task();
667
        $this->assertEmpty(manager::queue_adhoc_task($task5, true));
668
        $this->assertEquals(5, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
669
 
670
        // Schedule same adhoc task without custom data but with a userid.
671
        $task6 = new adhoc_test_task();
672
        $user = \core_user::get_user_by_username('admin');
673
        $task6->set_userid($user->id);
674
        $this->assertNotEmpty(manager::queue_adhoc_task($task6, true));
675
        $this->assertEquals(6, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
676
 
677
        // Schedule same adhoc task again without custom data but with a userid.
678
        $task6 = new adhoc_test_task();
679
        $user = \core_user::get_user_by_username('admin');
680
        $task6->set_userid($user->id);
681
        $this->assertEmpty(manager::queue_adhoc_task($task6, true));
682
        $this->assertEquals(6, count(manager::get_adhoc_tasks('core\task\adhoc_test_task')));
683
    }
684
 
685
    /**
686
     * Test that when no userid is specified, it returns empty from the DB
687
     * too.
688
     * @covers \core\task\adhoc_task
689
     */
690
    public function test_adhoc_task_user_empty(): void {
691
        $this->resetAfterTest(true);
692
 
693
        // Create an adhoc task in future.
694
        $task = new adhoc_test_task();
695
        manager::queue_adhoc_task($task);
696
 
697
        // Get it back from the scheduler.
698
        $now = time();
699
        $task = manager::get_next_adhoc_task($now);
700
        manager::adhoc_task_complete($task);
701
 
702
        $this->assertEmpty($task->get_userid());
703
    }
704
 
705
    /**
706
     * Test that when a userid is specified, that userid is subsequently
707
     * returned.
708
     *
709
     * @covers \core\task\adhoc_task
710
     */
711
    public function test_adhoc_task_user_set(): void {
712
        $this->resetAfterTest(true);
713
 
714
        // Create an adhoc task in future.
715
        $task = new adhoc_test_task();
716
        $user = \core_user::get_user_by_username('admin');
717
        $task->set_userid($user->id);
718
        manager::queue_adhoc_task($task);
719
 
720
        // Get it back from the scheduler.
721
        $now = time();
722
        $task = manager::get_next_adhoc_task($now);
723
        manager::adhoc_task_complete($task);
724
 
725
        $this->assertEquals($user->id, $task->get_userid());
726
    }
727
 
728
    /**
729
     * Test adhoc task with the first starting time.
730
     */
731
    public function test_adhoc_task_get_first_starting_time(): void {
732
        global $DB;
733
        $this->resetAfterTest(true);
734
 
735
        $now = time();
736
 
737
        // Create an adhoc task.
738
        $task = new adhoc_test_task();
739
        // Queue it.
740
        $taskid = manager::queue_adhoc_task(task: $task);
741
 
742
        // Get the firststartingtime value.
743
        $firststartingtime = $DB->get_field(
744
            table: 'task_adhoc',
745
            return: 'firststartingtime',
746
            conditions: ['id' => $taskid],
747
        );
748
        $this->assertNull($firststartingtime);
749
 
750
        // This will make sure that the task will be started after the $now value.
751
        sleep(3);
752
 
753
        // Get the task from the scheduler.
754
        $task = manager::get_next_adhoc_task(timestart: $now);
755
        // Mark the task as starting.
756
        manager::adhoc_task_starting($task);
757
        // Execute the task.
758
        $task->execute();
759
        // Mark the task as failed.
760
        manager::adhoc_task_failed(task: $task);
761
 
762
        // Get the firststartingtime value.
763
        $origintimestarted = $DB->get_field(
764
            table: 'task_adhoc',
765
            return: 'firststartingtime',
766
            conditions: ['id' => $taskid],
767
        );
768
        $this->assertNotNull($origintimestarted);
769
        $this->assertGreaterThan($now, $origintimestarted);
770
 
771
        // Get the task from the scheduler.
772
        $task = manager::get_next_adhoc_task(timestart: $now + 86400);
773
        // Mark the task as starting.
774
        manager::adhoc_task_starting($task);
775
        // Execute the task.
776
        $task->execute();
777
        // Mark the task as failed.
778
        manager::adhoc_task_failed(task: $task);
779
 
780
        // Get the firststartingtime value.
781
        $firststartingtime = $DB->get_field(
782
            table: 'task_adhoc',
783
            return: 'firststartingtime',
784
            conditions: ['id' => $taskid],
785
        );
786
 
787
        // The firststartingtime value should not be changed.
788
        $this->assertEquals($origintimestarted, $firststartingtime);
789
    }
790
 
791
    /**
792
     * Test get_concurrency_limit() method to return 0 by default.
793
     *
794
     * @covers \core\task\adhoc_task
795
     */
796
    public function test_get_concurrency_limit(): void {
797
        $this->resetAfterTest(true);
798
        $task = new adhoc_test_task();
799
        $concurrencylimit = $task->get_concurrency_limit();
800
        $this->assertEquals(0, $concurrencylimit);
801
    }
802
 
803
    /**
804
     * Test get_concurrency_limit() method to return a default value set in config.
805
     * @covers \core\task\adhoc_task
806
     */
807
    public function test_get_concurrency_limit_default(): void {
808
        $this->resetAfterTest(true);
809
        set_config('task_concurrency_limit_default', 10);
810
        $task = new adhoc_test_task();
811
        $concurrencylimit = $task->get_concurrency_limit();
812
        $this->assertEquals(10, $concurrencylimit);
813
    }
814
 
815
    /**
816
     * Test get_concurrency_limit() method to return a value for specific task class.
817
     * @covers \core\task\adhoc_task
818
     */
819
    public function test_get_concurrency_limit_for_task(): void {
820
        global $CFG;
821
        $this->resetAfterTest(true);
822
        set_config('task_concurrency_limit_default', 10);
823
        $CFG->task_concurrency_limit = ['core\task\adhoc_test_task' => 5];
824
        $task = new adhoc_test_task();
825
        $concurrencylimit = $task->get_concurrency_limit();
826
        $this->assertEquals(5, $concurrencylimit);
827
    }
828
 
829
    /**
830
     * Test adhoc task sorting.
831
     */
832
    public function test_get_next_adhoc_task_sorting(): void {
833
        $this->resetAfterTest(true);
834
 
835
        // Create adhoc tasks.
836
        $task1 = new adhoc_test_task();
837
        $task1->set_next_run_time(1510000000);
838
        $task1->set_custom_data_as_string('Task 1');
839
        manager::queue_adhoc_task($task1);
840
 
841
        $task2 = new adhoc_test_task();
842
        $task2->set_next_run_time(1520000000);
843
        $task2->set_custom_data_as_string('Task 2');
844
        manager::queue_adhoc_task($task2);
845
 
846
        $task3 = new adhoc_test_task();
847
        $task3->set_next_run_time(1520000000);
848
        $task3->set_custom_data_as_string('Task 3');
849
        manager::queue_adhoc_task($task3);
850
 
851
        // Shuffle tasks.
852
        $task1->set_next_run_time(1540000000);
853
        manager::reschedule_or_queue_adhoc_task($task1);
854
 
855
        $task3->set_next_run_time(1530000000);
856
        manager::reschedule_or_queue_adhoc_task($task3);
857
 
858
        $task2->set_next_run_time(1530000000);
859
        manager::reschedule_or_queue_adhoc_task($task2);
860
 
861
        // Confirm, that tasks are sorted by nextruntime and then by id (ascending).
862
        $task = manager::get_next_adhoc_task(time());
863
        $this->assertEquals('Task 2', $task->get_custom_data_as_string());
864
        manager::adhoc_task_complete($task);
865
 
866
        $task = manager::get_next_adhoc_task(time());
867
        $this->assertEquals('Task 3', $task->get_custom_data_as_string());
868
        manager::adhoc_task_complete($task);
869
 
870
        $task = manager::get_next_adhoc_task(time());
871
        $this->assertEquals('Task 1', $task->get_custom_data_as_string());
872
        manager::adhoc_task_complete($task);
873
    }
874
 
875
    /**
876
     * Test adhoc task run from CLI.
877
     */
878
    public function test_run_adhoc_from_cli(): void {
879
        $this->resetAfterTest(true);
880
 
881
        $taskid = 1;
882
 
883
        if (!manager::is_runnable()) {
884
            $this->markTestSkipped("Cannot run tasks");
885
        }
886
 
887
        ob_start();
888
        manager::run_adhoc_from_cli($taskid);
889
        $output = ob_get_contents();
890
        ob_end_clean();
891
 
892
        $this->assertMatchesRegularExpression(
893
            sprintf('!admin/cli/adhoc_task.php\W+--id=%d\W+--force!', $taskid),
894
            $output
895
        );
896
    }
897
 
898
    /**
899
     * Test adhoc class run from CLI.
900
     */
901
    public function test_run_all_adhoc_from_cli(): void {
902
        $this->resetAfterTest(true);
903
 
904
        $classname = 'fake';
905
 
906
        if (!manager::is_runnable()) {
907
            $this->markTestSkipped("Cannot run tasks");
908
        }
909
 
910
        ob_start();
911
        manager::run_all_adhoc_from_cli(false, $classname);
912
        $output = ob_get_contents();
913
        ob_end_clean();
914
 
915
        $this->assertMatchesRegularExpression(
916
            sprintf('!admin/cli/adhoc_task.php\W+--classname=%s\W+--force!', $classname),
917
            $output
918
        );
919
    }
920
 
921
    /**
922
     * Test send messages when adhoc task reaches the max fail delay time.
923
     *
924
     * @covers ::adhoc_task_failed
925
     * @covers ::send_failed_task_max_delay_message
926
     */
927
    public function test_adhoc_message_max_fail_delay(): void {
928
        $this->resetAfterTest();
929
        $this->setAdminUser();
930
 
931
        // Redirect messages.
932
        $messagesink = $this->redirectMessages();
933
 
934
        // Create an adhoc task.
935
        $task = new adhoc_test_task();
936
        manager::queue_adhoc_task($task);
937
 
938
        $now = time();
939
 
940
        // Get it from the scheduler, execute it, and mark it as failed.
941
        $task = manager::get_next_adhoc_task($now);
942
        $taskid = $task->get_id();
943
        $task->execute();
944
 
945
        // Catch the message. The task has not reach the max time delay yet.
946
        manager::adhoc_task_failed($task);
947
        $messages = $messagesink->get_messages();
948
        $this->assertCount(0, $messages);
949
 
950
        // Should get the adhoc task immediately.
951
        $task = manager::get_adhoc_task($taskid);
952
        $task->set_fail_delay(86400);
953
        $this->assertInstanceOf('\\core\\task\\adhoc_test_task', $task);
954
        $this->assertEquals($taskid, $task->get_id());
955
        $task->execute();
956
 
957
        // Catch the message.
958
        manager::adhoc_task_failed($task);
959
        $messages = $messagesink->get_messages();
960
        $this->assertCount(1, $messages);
961
 
962
        // Get the task and execute it second time.
963
        $task = manager::get_adhoc_task($taskid);
964
        // Set the fail delay to 12 hours.
965
        $task->set_fail_delay(43200);
966
        $task->execute();
967
        manager::adhoc_task_failed($task);
968
 
969
        // Catch the message.
970
        $messages = $messagesink->get_messages();
971
        $this->assertCount(2, $messages);
972
 
973
        // Get the task and execute it third time.
974
        $task = manager::get_adhoc_task($taskid);
975
        // Set the fail delay to 48 hours.
976
        $task->set_fail_delay(172800);
977
        $task->execute();
978
        manager::adhoc_task_failed($task);
979
 
980
        // Catch the message.
981
        $messages = $messagesink->get_messages();
982
        $this->assertCount(3, $messages);
983
 
984
        // Check first message information.
985
        $this->assertStringContainsString('Task failed: Test adhoc class', $messages[0]->subject);
986
        $this->assertEquals('failedtaskmaxdelay', $messages[0]->eventtype);
987
        $this->assertEquals('-10', $messages[0]->useridfrom);
988
        $this->assertEquals('2', $messages[0]->useridto);
989
 
990
        // Close sink.
991
        $messagesink->close();
992
    }
993
}