MDL-68525 behat: chrome/switches caps not allowed in Chrome > 81
[moodle.git] / lib / tests / task_logging_test.php
blobd4f4e12de8b46db87788dd89cac09269bfd3a886
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.
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/>.
17 /**
18 * This file contains the unit tests for the task logging system.
20 * @package core
21 * @category phpunit
22 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
27 require_once(__DIR__ . '/fixtures/task_fixtures.php');
30 /**
31 * This file contains the unit tests for the task logging system.
33 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class core_task_logmanager extends advanced_testcase {
38 /**
39 * @var \moodle_database The original database prior to mocking
41 protected $DB;
43 /**
44 * Relevant tearDown for logging tests.
46 public function tearDown() {
47 global $DB;
49 // Ensure that any logging is always ended.
50 \core\task\logmanager::finalise_log();
52 if (null !== $this->DB) {
53 $DB = $this->DB;
54 $this->DB = null;
58 /**
59 * When the logmode is set to none, logging should not start.
61 public function test_logmode_none() {
62 global $CFG;
63 $this->resetAfterTest();
65 $CFG->task_logmode = \core\task\logmanager::MODE_NONE;
67 $initialbufferstate = ob_get_status();
69 $task = $this->get_test_adhoc_task();
70 \core\task\logmanager::start_logging($task);
72 // There will be no additional output buffer.
73 $this->assertEquals($initialbufferstate, ob_get_status());
76 /**
77 * When the logmode is set to all that log capture is started.
79 public function test_start_logmode_all() {
80 global $CFG;
81 $this->resetAfterTest();
83 $CFG->task_logmode = \core\task\logmanager::MODE_ALL;
85 $initialbufferstate = ob_get_status();
87 $task = $this->get_test_adhoc_task();
88 \core\task\logmanager::start_logging($task);
90 // Fetch the new output buffer state.
91 $state = ob_get_status();
93 // There will be no additional output buffer.
94 $this->assertNotEquals($initialbufferstate, $state);
97 /**
98 * When the logmode is set to fail that log capture is started.
100 public function test_start_logmode_fail() {
101 global $CFG;
102 $this->resetAfterTest();
104 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
106 $initialbufferstate = ob_get_status();
108 $task = $this->get_test_adhoc_task();
109 \core\task\logmanager::start_logging($task);
111 // Fetch the new output buffer state.
112 $state = ob_get_status();
114 // There will be no additional output buffer.
115 $this->assertNotEquals($initialbufferstate, $state);
119 * When the logmode is set to fail, passing adhoc tests should not be logged.
121 public function test_logmode_fail_with_passing_adhoc_task() {
122 global $CFG;
123 $this->resetAfterTest();
125 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
127 $logger = $this->get_mocked_logger();
129 $initialbufferstate = ob_get_status();
131 $task = $this->get_test_adhoc_task();
132 \core\task\logmanager::start_logging($task);
134 \core\task\manager::adhoc_task_complete($task);
136 $this->assertEmpty($logger::$storelogfortask);
140 * When the logmode is set to fail, passing scheduled tests should not be logged.
142 public function test_logmode_fail_with_passing_scheduled_task() {
143 global $CFG;
144 $this->resetAfterTest();
146 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
148 $logger = $this->get_mocked_logger();
150 $initialbufferstate = ob_get_status();
152 $task = $this->get_test_scheduled_task();
153 \core\task\logmanager::start_logging($task);
155 \core\task\manager::scheduled_task_complete($task);
157 $this->assertEmpty($logger::$storelogfortask);
161 * When the logmode is set to fail, failing adhoc tests should be logged.
163 public function test_logmode_fail_with_failing_adhoc_task() {
164 global $CFG;
166 $this->resetAfterTest();
168 // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
169 $this->mock_database();
171 $task = $this->get_test_adhoc_task();
173 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
175 $logger = $this->get_mocked_logger();
177 \core\task\logmanager::start_logging($task);
178 \core\task\manager::adhoc_task_failed($task);
180 $this->assertCount(1, $logger::$storelogfortask);
181 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
182 $this->assertTrue($logger::$storelogfortask[0][2]);
186 * When the logmode is set to fail, failing scheduled tests should be logged.
188 public function test_logmode_fail_with_failing_scheduled_task() {
189 global $CFG;
191 $this->resetAfterTest();
193 // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
194 $this->mock_database();
196 $task = $this->get_test_scheduled_task();
198 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
200 $logger = $this->get_mocked_logger();
202 \core\task\logmanager::start_logging($task);
203 \core\task\manager::scheduled_task_failed($task);
205 $this->assertCount(1, $logger::$storelogfortask);
206 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
207 $this->assertTrue($logger::$storelogfortask[0][2]);
211 * When the logmode is set to fail, failing adhoc tests should be logged.
213 public function test_logmode_any_with_failing_adhoc_task() {
214 global $CFG;
216 $this->resetAfterTest();
218 // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
219 $this->mock_database();
221 $task = $this->get_test_adhoc_task();
223 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
225 $logger = $this->get_mocked_logger();
227 \core\task\logmanager::start_logging($task);
228 \core\task\manager::adhoc_task_failed($task);
230 $this->assertCount(1, $logger::$storelogfortask);
231 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
232 $this->assertTrue($logger::$storelogfortask[0][2]);
236 * When the logmode is set to fail, failing scheduled tests should be logged.
238 public function test_logmode_any_with_failing_scheduled_task() {
239 global $CFG;
241 $this->resetAfterTest();
243 // Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
244 $this->mock_database();
246 $task = $this->get_test_scheduled_task();
248 $CFG->task_logmode = \core\task\logmanager::MODE_FAILONLY;
250 $logger = $this->get_mocked_logger();
252 \core\task\logmanager::start_logging($task);
253 \core\task\manager::scheduled_task_failed($task);
255 $this->assertCount(1, $logger::$storelogfortask);
256 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
257 $this->assertTrue($logger::$storelogfortask[0][2]);
261 * When the logmode is set to fail, passing adhoc tests should be logged.
263 public function test_logmode_any_with_passing_adhoc_task() {
264 global $CFG;
266 $this->resetAfterTest();
268 $this->mock_database();
270 $task = $this->get_test_adhoc_task();
272 $CFG->task_logmode = \core\task\logmanager::MODE_ALL;
274 $logger = $this->get_mocked_logger();
276 \core\task\logmanager::start_logging($task);
277 \core\task\manager::adhoc_task_complete($task);
279 $this->assertCount(1, $logger::$storelogfortask);
280 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
281 $this->assertFalse($logger::$storelogfortask[0][2]);
285 * When the logmode is set to fail, passing scheduled tests should be logged.
287 public function test_logmode_any_with_passing_scheduled_task() {
288 global $CFG;
290 $this->resetAfterTest();
292 $this->mock_database();
294 $task = $this->get_test_scheduled_task();
296 $CFG->task_logmode = \core\task\logmanager::MODE_ALL;
298 $logger = $this->get_mocked_logger();
300 \core\task\logmanager::start_logging($task);
301 \core\task\manager::scheduled_task_complete($task);
303 $this->assertCount(1, $logger::$storelogfortask);
304 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
305 $this->assertFalse($logger::$storelogfortask[0][2]);
309 * Ensure that start_logging cannot be called in a nested fashion.
311 public function test_prevent_nested_logging() {
312 $this->resetAfterTest();
314 $task = $this->get_test_adhoc_task();
315 \core\task\logmanager::start_logging($task);
317 $this->expectException(\coding_exception::class);
318 \core\task\logmanager::start_logging($task);
322 * Ensure that logging can be called after a previous log has finished.
324 public function test_repeated_usages() {
325 $this->resetAfterTest();
327 $logger = $this->get_mocked_logger();
329 $task = $this->get_test_adhoc_task();
330 \core\task\logmanager::start_logging($task);
331 \core\task\logmanager::finalise_log();
333 \core\task\logmanager::start_logging($task);
334 \core\task\logmanager::finalise_log();
336 $this->assertCount(2, $logger::$storelogfortask);
337 $this->assertEquals($task, $logger::$storelogfortask[0][0]);
338 $this->assertFalse($logger::$storelogfortask[0][2]);
339 $this->assertEquals($task, $logger::$storelogfortask[1][0]);
340 $this->assertFalse($logger::$storelogfortask[1][2]);
344 * Enusre that when finalise_log is called when logging is not active, nothing happens.
346 public function test_finalise_log_no_logging() {
347 $initialbufferstate = ob_get_status();
349 \core\task\logmanager::finalise_log();
351 // There will be no additional output buffer.
352 $this->assertEquals($initialbufferstate, ob_get_status());
356 * When log capture is enabled, calls to the flush function should cause log output to be both returned and captured.
358 public function test_flush_on_own_buffer() {
359 $this->resetAfterTest();
361 $logger = $this->get_mocked_logger();
363 $testoutput = "I am the output under test.\n";
365 $task = $this->get_test_adhoc_task();
366 \core\task\logmanager::start_logging($task);
368 echo $testoutput;
370 $this->expectOutputString($testoutput);
371 \core\task\logmanager::flush();
373 // Finalise the log.
374 \core\task\logmanager::finalise_log();
376 $this->assertCount(1, $logger::$storelogfortask);
377 $this->assertEquals($testoutput, file_get_contents($logger::$storelogfortask[0][1]));
381 * When log capture is enabled, calls to the flush function should not affect any subsequent ob_start.
383 public function test_flush_does_not_flush_inner_buffers() {
384 $this->resetAfterTest();
386 $logger = $this->get_mocked_logger();
388 $testoutput = "I am the output under test.\n";
390 $task = $this->get_test_adhoc_task();
391 \core\task\logmanager::start_logging($task);
393 ob_start();
394 echo $testoutput;
395 ob_end_clean();
397 \core\task\logmanager::flush();
399 // Finalise the log.
400 \core\task\logmanager::finalise_log();
402 $this->assertCount(1, $logger::$storelogfortask);
404 // The task logger should not have captured the content of the inner buffer.
405 $this->assertEquals('', file_get_contents($logger::$storelogfortask[0][1]));
409 * When log capture is enabled, calls to the flush function should not affect any subsequent ob_start.
411 public function test_inner_flushed_buffers_are_logged() {
412 $this->resetAfterTest();
414 $logger = $this->get_mocked_logger();
416 $testoutput = "I am the output under test.\n";
418 $task = $this->get_test_adhoc_task();
419 \core\task\logmanager::start_logging($task);
421 // We are going to flush the inner buffer. That means that we should expect the output immediately.
422 $this->expectOutputString($testoutput);
424 ob_start();
425 echo $testoutput;
426 ob_end_flush();
428 // Finalise the log.
429 \core\task\logmanager::finalise_log();
431 $this->assertCount(1, $logger::$storelogfortask);
433 // The task logger should not have captured the content of the inner buffer.
434 $this->assertEquals($testoutput, file_get_contents($logger::$storelogfortask[0][1]));
438 * Get an example adhoc task to use for testing.
440 * @return \core\task\adhoc_task
442 protected function get_test_adhoc_task() : \core\task\adhoc_task {
443 $task = $this->getMockForAbstractClass(\core\task\adhoc_task::class);
445 // Mock a lock on the task.
446 $lock = $this->getMockBuilder(\core\lock\lock::class)
447 ->disableOriginalConstructor()
448 ->getMock();
449 $task->set_lock($lock);
451 return $task;
455 * Get an example scheduled task to use for testing.
457 * @return \core\task\scheduled_task
459 protected function get_test_scheduled_task() : \core\task\scheduled_task {
460 $task = $this->getMockForAbstractClass(\core\task\scheduled_task::class);
462 // Mock a lock on the task.
463 $lock = $this->getMockBuilder(\core\lock\lock::class)
464 ->disableOriginalConstructor()
465 ->getMock();
466 $task->set_lock($lock);
468 return $task;
472 * Create and configure a mocked task logger.
474 * @return \core\task\task_logger
476 protected function get_mocked_logger() {
477 global $CFG;
479 // We will modify config for the alternate logging class therefore we mnust reset after the test.
480 $this->resetAfterTest();
482 // Note PHPUnit does not support mocking static functions.
483 $CFG->task_log_class = \task_logging_test_mocked_logger::class;
484 \task_logging_test_mocked_logger::test_reset();
486 return $CFG->task_log_class;
490 * Mock the database.
492 protected function mock_database() {
493 global $DB;
495 // Store the old Database for restoration in reset.
496 $this->DB = $DB;
498 $DB = $this->getMockBuilder(\moodle_database::class)
499 ->getMock();
501 $DB->method('get_record')
502 ->willReturn((object) []);
507 * Mocked logger.
509 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
510 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
512 class task_logging_test_mocked_logger implements \core\task\task_logger {
515 * @var bool Whether this is configured.
517 public static $isconfigured = true;
520 * @var array Arguments that store_log_for_task was called with.
522 public static $storelogfortask = [];
525 * @var bool Whether this logger has a report.
527 public static $haslogreport = true;
530 * Reset the test class.
532 public static function test_reset() {
533 self::$isconfigured = true;
534 self::$storelogfortask = [];
535 self::$haslogreport = true;
539 * Whether the task is configured and ready to log.
541 * @return bool
543 public static function is_configured() : bool {
544 return self::$isconfigured;
548 * Store the log for the specified task.
550 * @param \core\task\task_base $task The task that the log belongs to.
551 * @param string $logpath The path to the log on disk
552 * @param bool $failed Whether the task failed
553 * @param int $dbreads The number of DB reads
554 * @param int $dbwrites The number of DB writes
555 * @param float $timestart The start time of the task
556 * @param float $timeend The end time of the task
558 public static function store_log_for_task(\core\task\task_base $task, string $logpath, bool $failed,
559 int $dbreads, int $dbwrites, float $timestart, float $timeend) {
560 self::$storelogfortask[] = func_get_args();
564 * Whether this task logger has a report available.
566 * @return bool
568 public static function has_log_report() : bool {
569 return self::$haslogreport;
573 * Get any URL available for viewing relevant task log reports.
575 * @param string $classname The task class to fetch for
576 * @return \moodle_url
578 public static function get_url_for_task_class(string $classname) : \moodle_url {
579 return new \moodle_url('');