Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | 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
/**
18
 * This file contains the unit tests for the task logging system.
19
 *
20
 * @package   core
21
 * @category  test
22
 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
23
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace core\task;
27
 
28
defined('MOODLE_INTERNAL') || die();
29
require_once(__DIR__ . '/../fixtures/task_fixtures.php');
30
 
31
/**
32
 * This file contains the unit tests for the task logging system.
33
 *
34
 * @package   core
35
 * @category  test
36
 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
37
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
1441 ariadna 39
final class logging_test extends \advanced_testcase {
1 efrain 40
 
41
    /**
42
     * @var \moodle_database The original database prior to mocking
43
     */
44
    protected $DB;
45
 
46
    /**
47
     * Relevant tearDown for logging tests.
48
     */
49
    public function tearDown(): void {
50
        global $DB;
51
 
52
        // Ensure that any logging is always ended.
53
        logmanager::finalise_log();
54
 
55
        if (null !== $this->DB) {
56
            $DB = $this->DB;
57
            $this->DB = null;
58
        }
1441 ariadna 59
        parent::tearDown();
1 efrain 60
    }
61
 
62
    /**
63
     * When the logmode is set to none, logging should not start.
64
     */
11 efrain 65
    public function test_logmode_none(): void {
1 efrain 66
        global $CFG;
67
        $this->resetAfterTest();
68
 
69
        $CFG->task_logmode = logmanager::MODE_NONE;
70
 
71
        $initialbufferstate = ob_get_status();
72
 
73
        $task = $this->get_test_adhoc_task();
74
        logmanager::start_logging($task);
75
 
76
        // There will be no additional output buffer.
77
        $this->assertEquals($initialbufferstate, ob_get_status());
78
    }
79
 
80
    /**
81
     * When the logmode is set to all that log capture is started.
82
     */
11 efrain 83
    public function test_start_logmode_all(): void {
1 efrain 84
        global $CFG;
85
        $this->resetAfterTest();
86
 
87
        $CFG->task_logmode = logmanager::MODE_ALL;
88
 
89
        $initialbufferstate = ob_get_status();
90
 
91
        $task = $this->get_test_adhoc_task();
92
        logmanager::start_logging($task);
93
 
94
        // Fetch the new output buffer state.
95
        $state = ob_get_status();
96
 
97
        // There will be no additional output buffer.
98
        $this->assertNotEquals($initialbufferstate, $state);
99
    }
100
 
101
    /**
102
     * When the logmode is set to fail that log capture is started.
103
     */
11 efrain 104
    public function test_start_logmode_fail(): void {
1 efrain 105
        global $CFG;
106
        $this->resetAfterTest();
107
 
108
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
109
 
110
        $initialbufferstate = ob_get_status();
111
 
112
        $task = $this->get_test_adhoc_task();
113
        logmanager::start_logging($task);
114
 
115
        // Fetch the new output buffer state.
116
        $state = ob_get_status();
117
 
118
        // There will be no additional output buffer.
119
        $this->assertNotEquals($initialbufferstate, $state);
120
    }
121
 
122
    /**
123
     * When the logmode is set to fail, passing adhoc tests should not be logged.
124
     */
11 efrain 125
    public function test_logmode_fail_with_passing_adhoc_task(): void {
1 efrain 126
        global $CFG;
127
        $this->resetAfterTest();
128
 
129
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
130
 
131
        $logger = $this->get_mocked_logger();
132
 
133
        $initialbufferstate = ob_get_status();
134
 
135
        $task = $this->get_test_adhoc_task();
136
        logmanager::start_logging($task);
137
 
138
        manager::adhoc_task_complete($task);
139
 
140
        $this->assertEmpty($logger::$storelogfortask);
141
    }
142
 
143
    /**
144
     * When the logmode is set to fail, passing scheduled tests should not be logged.
145
     */
11 efrain 146
    public function test_logmode_fail_with_passing_scheduled_task(): void {
1 efrain 147
        global $CFG;
148
        $this->resetAfterTest();
149
 
150
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
151
 
152
        $logger = $this->get_mocked_logger();
153
 
154
        $initialbufferstate = ob_get_status();
155
 
156
        $task = $this->get_test_scheduled_task();
157
        logmanager::start_logging($task);
158
 
159
        manager::scheduled_task_complete($task);
160
 
161
        $this->assertEmpty($logger::$storelogfortask);
162
    }
163
 
164
    /**
165
     * When the logmode is set to fail, failing adhoc tests should be logged.
166
     */
11 efrain 167
    public function test_logmode_fail_with_failing_adhoc_task(): void {
1 efrain 168
        global $CFG;
169
 
170
        $this->resetAfterTest();
171
 
172
        // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
173
        $this->mock_database();
174
 
175
        $task = $this->get_test_adhoc_task();
176
 
177
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
178
 
179
        $logger = $this->get_mocked_logger();
180
 
181
        logmanager::start_logging($task);
182
        manager::adhoc_task_failed($task);
183
 
184
        $this->assertCount(1, $logger::$storelogfortask);
185
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
186
        $this->assertTrue($logger::$storelogfortask[0][2]);
187
    }
188
 
189
    /**
190
     * When the logmode is set to fail, failing scheduled tests should be logged.
191
     */
11 efrain 192
    public function test_logmode_fail_with_failing_scheduled_task(): void {
1 efrain 193
        global $CFG;
194
 
195
        $this->resetAfterTest();
196
 
197
        // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
198
        $this->mock_database();
199
 
200
        $task = $this->get_test_scheduled_task();
201
 
202
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
203
 
204
        $logger = $this->get_mocked_logger();
205
 
206
        logmanager::start_logging($task);
207
        manager::scheduled_task_failed($task);
208
 
209
        $this->assertCount(1, $logger::$storelogfortask);
210
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
211
        $this->assertTrue($logger::$storelogfortask[0][2]);
212
    }
213
 
214
    /**
215
     * When the logmode is set to fail, failing adhoc tests should be logged.
216
     */
11 efrain 217
    public function test_logmode_any_with_failing_adhoc_task(): void {
1 efrain 218
        global $CFG;
219
 
220
        $this->resetAfterTest();
221
 
222
        // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
223
        $this->mock_database();
224
 
225
        $task = $this->get_test_adhoc_task();
226
 
227
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
228
 
229
        $logger = $this->get_mocked_logger();
230
 
231
        logmanager::start_logging($task);
232
        manager::adhoc_task_failed($task);
233
 
234
        $this->assertCount(1, $logger::$storelogfortask);
235
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
236
        $this->assertTrue($logger::$storelogfortask[0][2]);
237
    }
238
 
239
    /**
240
     * When the logmode is set to fail, failing scheduled tests should be logged.
241
     */
11 efrain 242
    public function test_logmode_any_with_failing_scheduled_task(): void {
1 efrain 243
        global $CFG;
244
 
245
        $this->resetAfterTest();
246
 
247
        // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
248
        $this->mock_database();
249
 
250
        $task = $this->get_test_scheduled_task();
251
 
252
        $CFG->task_logmode = logmanager::MODE_FAILONLY;
253
 
254
        $logger = $this->get_mocked_logger();
255
 
256
        logmanager::start_logging($task);
257
        manager::scheduled_task_failed($task);
258
 
259
        $this->assertCount(1, $logger::$storelogfortask);
260
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
261
        $this->assertTrue($logger::$storelogfortask[0][2]);
262
    }
263
 
264
    /**
265
     * When the logmode is set to fail, passing adhoc tests should be logged.
266
     */
11 efrain 267
    public function test_logmode_any_with_passing_adhoc_task(): void {
1 efrain 268
        global $CFG;
269
 
270
        $this->resetAfterTest();
271
 
272
        $this->mock_database();
273
 
274
        $task = $this->get_test_adhoc_task();
275
 
276
        $CFG->task_logmode = logmanager::MODE_ALL;
277
 
278
        $logger = $this->get_mocked_logger();
279
 
280
        logmanager::start_logging($task);
281
        manager::adhoc_task_complete($task);
282
 
283
        $this->assertCount(1, $logger::$storelogfortask);
284
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
285
        $this->assertFalse($logger::$storelogfortask[0][2]);
286
    }
287
 
288
    /**
289
     * When the logmode is set to fail, passing scheduled tests should be logged.
290
     */
11 efrain 291
    public function test_logmode_any_with_passing_scheduled_task(): void {
1 efrain 292
        global $CFG;
293
 
294
        $this->resetAfterTest();
295
 
296
        $this->mock_database();
297
 
298
        $task = $this->get_test_scheduled_task();
299
 
300
        $CFG->task_logmode = logmanager::MODE_ALL;
301
 
302
        $logger = $this->get_mocked_logger();
303
 
304
        logmanager::start_logging($task);
305
        manager::scheduled_task_complete($task);
306
 
307
        $this->assertCount(1, $logger::$storelogfortask);
308
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
309
        $this->assertFalse($logger::$storelogfortask[0][2]);
310
    }
311
 
312
    /**
313
     * Ensure that start_logging cannot be called in a nested fashion.
314
     */
11 efrain 315
    public function test_prevent_nested_logging(): void {
1 efrain 316
        $this->resetAfterTest();
317
 
318
        $task = $this->get_test_adhoc_task();
319
        logmanager::start_logging($task);
320
 
321
        $this->expectException(\coding_exception::class);
322
        logmanager::start_logging($task);
323
    }
324
 
325
    /**
326
     * Ensure that logging can be called after a previous log has finished.
327
     */
11 efrain 328
    public function test_repeated_usages(): void {
1 efrain 329
        $this->resetAfterTest();
330
 
331
        $logger = $this->get_mocked_logger();
332
 
333
        $task = $this->get_test_adhoc_task();
334
        logmanager::start_logging($task);
335
        logmanager::finalise_log();
336
 
337
        logmanager::start_logging($task);
338
        logmanager::finalise_log();
339
 
340
        $this->assertCount(2, $logger::$storelogfortask);
341
        $this->assertEquals($task, $logger::$storelogfortask[0][0]);
342
        $this->assertFalse($logger::$storelogfortask[0][2]);
343
        $this->assertEquals($task, $logger::$storelogfortask[1][0]);
344
        $this->assertFalse($logger::$storelogfortask[1][2]);
345
    }
346
 
347
    /**
348
     * Enusre that when finalise_log is called when logging is not active, nothing happens.
349
     */
11 efrain 350
    public function test_finalise_log_no_logging(): void {
1 efrain 351
        $initialbufferstate = ob_get_status();
352
 
353
        logmanager::finalise_log();
354
 
355
        // There will be no additional output buffer.
356
        $this->assertEquals($initialbufferstate, ob_get_status());
357
    }
358
 
359
    /**
360
     * When log capture is enabled, calls to the flush function should cause log output to be both returned and captured.
361
     */
11 efrain 362
    public function test_flush_on_own_buffer(): void {
1 efrain 363
        $this->resetAfterTest();
364
 
365
        $logger = $this->get_mocked_logger();
366
 
367
        $testoutput = "I am the output under test.\n";
368
 
369
        $task = $this->get_test_adhoc_task();
370
        logmanager::start_logging($task);
371
 
372
        echo $testoutput;
373
 
374
        $this->expectOutputString($testoutput);
375
        logmanager::flush();
376
 
377
        // Finalise the log.
378
        logmanager::finalise_log();
379
 
380
        $this->assertCount(1, $logger::$storelogfortask);
381
        $this->assertEquals($testoutput, file_get_contents($logger::$storelogfortask[0][1]));
382
    }
383
 
384
    /**
385
     * When log capture is enabled, calls to the flush function should not affect any subsequent ob_start.
386
     */
11 efrain 387
    public function test_flush_does_not_flush_inner_buffers(): void {
1 efrain 388
        $this->resetAfterTest();
389
 
390
        $logger = $this->get_mocked_logger();
391
 
392
        $testoutput = "I am the output under test.\n";
393
 
394
        $task = $this->get_test_adhoc_task();
395
        logmanager::start_logging($task);
396
 
397
        ob_start();
398
        echo $testoutput;
399
        ob_end_clean();
400
 
401
        logmanager::flush();
402
 
403
        // Finalise the log.
404
        logmanager::finalise_log();
405
 
406
        $this->assertCount(1, $logger::$storelogfortask);
407
 
408
        // The task logger should not have captured the content of the inner buffer.
409
        $this->assertEquals('', file_get_contents($logger::$storelogfortask[0][1]));
410
    }
411
 
412
    /**
413
     * When log capture is enabled, calls to the flush function should not affect any subsequent ob_start.
414
     */
11 efrain 415
    public function test_inner_flushed_buffers_are_logged(): void {
1 efrain 416
        $this->resetAfterTest();
417
 
418
        $logger = $this->get_mocked_logger();
419
 
420
        $testoutput = "I am the output under test.\n";
421
 
422
        $task = $this->get_test_adhoc_task();
423
        logmanager::start_logging($task);
424
 
425
        // We are going to flush the inner buffer. That means that we should expect the output immediately.
426
        $this->expectOutputString($testoutput);
427
 
428
        ob_start();
429
        echo $testoutput;
430
        ob_end_flush();
431
 
432
        // Finalise the log.
433
        logmanager::finalise_log();
434
 
435
        $this->assertCount(1, $logger::$storelogfortask);
436
 
437
        // The task logger should not have captured the content of the inner buffer.
438
        $this->assertEquals($testoutput, file_get_contents($logger::$storelogfortask[0][1]));
439
    }
440
 
441
    /**
442
     * Get an example adhoc task to use for testing.
443
     *
444
     * @return  adhoc_task
445
     */
446
    protected function get_test_adhoc_task(): adhoc_task {
447
        $task = $this->getMockForAbstractClass(adhoc_task::class);
448
        $task->set_component('core');
449
 
450
        // Mock a lock on the task.
451
        $lock = $this->getMockBuilder(\core\lock\lock::class)
452
            ->disableOriginalConstructor()
453
            ->getMock();
454
        $task->set_lock($lock);
455
 
456
        return $task;
457
    }
458
 
459
    /**
460
     * Get an example scheduled task to use for testing.
461
     *
462
     * @return  scheduled_task
463
     */
464
    protected function get_test_scheduled_task(): scheduled_task {
465
        $task = $this->getMockForAbstractClass(scheduled_task::class);
466
 
467
        // Mock a lock on the task.
468
        $lock = $this->getMockBuilder(\core\lock\lock::class)
469
            ->disableOriginalConstructor()
470
            ->getMock();
471
        $task->set_lock($lock);
472
 
473
        return $task;
474
    }
475
 
476
    /**
477
     * Create and configure a mocked task logger.
478
     *
479
     * @return  logging_test_mocked_logger
480
     */
481
    protected function get_mocked_logger() {
482
        global $CFG;
483
 
484
        // We will modify config for the alternate logging class therefore we mnust reset after the test.
485
        $this->resetAfterTest();
486
 
487
        // Note PHPUnit does not support mocking static functions.
488
        $CFG->task_log_class = logging_test_mocked_logger::class;
489
        logging_test_mocked_logger::reset_test();
490
 
491
        return $CFG->task_log_class;
492
    }
493
 
494
    /**
495
     * Mock the database.
496
     */
497
    protected function mock_database() {
498
        global $DB;
499
 
500
        // Store the old Database for restoration in reset.
501
        $this->DB = $DB;
502
 
503
        $DB = $this->getMockBuilder(\moodle_database::class)
504
            ->getMock();
505
 
506
        $DB->method('get_record')
507
            ->willReturn((object) []);
508
    }
509
}
510
 
511
/**
512
 * Mocked logger.
513
 *
514
 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
515
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
516
 */
517
class logging_test_mocked_logger implements task_logger {
518
 
519
    /**
520
     * @var bool Whether this is configured.
521
     */
522
    public static $isconfigured = true;
523
 
524
    /**
525
     * @var array Arguments that store_log_for_task was called with.
526
     */
527
    public static $storelogfortask = [];
528
 
529
    /**
530
     * @var bool Whether this logger has a report.
531
     */
532
    public static $haslogreport = true;
533
 
534
    /**
535
     * Reset the test class.
536
     */
537
    public static function reset_test() {
538
        self::$isconfigured = true;
539
        self::$storelogfortask = [];
540
        self::$haslogreport = true;
541
    }
542
 
543
    /**
544
     * Whether the task is configured and ready to log.
545
     *
546
     * @return  bool
547
     */
548
    public static function is_configured(): bool {
549
        return self::$isconfigured;
550
    }
551
 
552
    /**
553
     * Store the log for the specified task.
554
     *
555
     * @param   task_base   $task The task that the log belongs to.
556
     * @param   string      $logpath The path to the log on disk
557
     * @param   bool        $failed Whether the task failed
558
     * @param   int         $dbreads The number of DB reads
559
     * @param   int         $dbwrites The number of DB writes
560
     * @param   float       $timestart The start time of the task
561
     * @param   float       $timeend The end time of the task
562
     */
563
    public static function store_log_for_task(task_base $task, string $logpath, bool $failed,
564
            int $dbreads, int $dbwrites, float $timestart, float $timeend) {
565
        self::$storelogfortask[] = func_get_args();
566
    }
567
 
568
    /**
569
     * Whether this task logger has a report available.
570
     *
571
     * @return  bool
572
     */
573
    public static function has_log_report(): bool {
574
        return self::$haslogreport;
575
    }
576
 
577
    /**
578
     * Get any URL available for viewing relevant task log reports.
579
     *
580
     * @param   string      $classname The task class to fetch for
581
     * @return  \moodle_url
582
     */
583
    public static function get_url_for_task_class(string $classname): \moodle_url {
584
        return new \moodle_url('');
585
    }
586
 
587
}