mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
83b490a594
Applied the following changes to various testcase classes: - Namespaced with component[\level2-API] - Moved to level2-API subdirectory when required. - Fixed incorrect use statements with leading backslash. - Remove file phpdoc block - Remove MOODLE_INTERNAL if not needed. - Changed code to point to global scope when needed. - Fix some relative paths and comments here and there. - All them passing individually. - Complete runs passing too. Special mention to: - The following task tests have been moved within the level2 directory: - \core\adhoc_task_test => \core\task\adhoc_task_test - \core\scheduled_task_test => \core\task\scheduled_task_test - \core\calendar_cron_task_test => \core\task\calendar_cron_task_test - \core\h5p_get_content_types_task_test => \core\task\h5p_get_content_types_task_test - \core\task_database_logger_test => \core\task\database_logger_test - \core\task_logging_test => \core\task\logging_test - The following event tests have been moved within level2 directory: - \core\event_context_locked_test => \core\event\context_locked_test - \core\event_deprecated_test => \core\event\deprecated_test - \core\event_grade_deleted_test => \core\event\grade_deleted_test - \core\event_profile_field_test => \core\event\profile_field_test - \core\event_unknown_logged_test => \core\event\unknown_logged_test - \core\event_user_graded_test => \core\event\user_graded_test - \core\event_user_password_updated_test => \core\event\user_password_updated_test - The following output tests have been moved within level2 directory: - \core\mustache_template_finder_test => \core\output\mustache_template_finder_test - \core\mustache_template_source_loader_test => \core\output\mustache_template_source_loader_test - \core\output_mustache_helper_collection_test => \core\output\mustache_helper_collection_test - The following tests have been moved to their correct tests directories: - lib/tests/time_splittings_test.php => analytics/tests/time_splittings_test.php - All the classes and tests under lib/filebrowser and lib/filestorage belong to core, not to core_files. Some day we should move them to their correct subsystem. - All the classes and tests under lib/grade belong to core, not to core_grades. Some day we should move them to their correct subsystem. - The core_grades_external class and its \core\grades_external_test unit test should belong to the grades subsystem or, alternatively, to \core\external, they both should be moved together. - The core_grading_external class and its \core\grading_external_test unit test should belong to the grading subsystem or, alternatively, to \core\external, they both should be moved together. - The \core\message\message and \core\message\inbound (may be others) classes, and their associated tests should go to the core_message subsystem. - The core_user class, and its associated tests should go to the core_user subsystem. - The \core\update namespace is plain wrong (update is not valid API) and needs action 1) create it or 2) move elsewhere.
587 lines
17 KiB
PHP
587 lines
17 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* This file contains the unit tests for the task logging system.
|
|
*
|
|
* @package core
|
|
* @category test
|
|
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
namespace core\task;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
require_once(__DIR__ . '/../fixtures/task_fixtures.php');
|
|
|
|
/**
|
|
* This file contains the unit tests for the task logging system.
|
|
*
|
|
* @package core
|
|
* @category test
|
|
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class logging_test extends \advanced_testcase {
|
|
|
|
/**
|
|
* @var \moodle_database The original database prior to mocking
|
|
*/
|
|
protected $DB;
|
|
|
|
/**
|
|
* Relevant tearDown for logging tests.
|
|
*/
|
|
public function tearDown(): void {
|
|
global $DB;
|
|
|
|
// Ensure that any logging is always ended.
|
|
logmanager::finalise_log();
|
|
|
|
if (null !== $this->DB) {
|
|
$DB = $this->DB;
|
|
$this->DB = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to none, logging should not start.
|
|
*/
|
|
public function test_logmode_none() {
|
|
global $CFG;
|
|
$this->resetAfterTest();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_NONE;
|
|
|
|
$initialbufferstate = ob_get_status();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
// There will be no additional output buffer.
|
|
$this->assertEquals($initialbufferstate, ob_get_status());
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to all that log capture is started.
|
|
*/
|
|
public function test_start_logmode_all() {
|
|
global $CFG;
|
|
$this->resetAfterTest();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_ALL;
|
|
|
|
$initialbufferstate = ob_get_status();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
// Fetch the new output buffer state.
|
|
$state = ob_get_status();
|
|
|
|
// There will be no additional output buffer.
|
|
$this->assertNotEquals($initialbufferstate, $state);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail that log capture is started.
|
|
*/
|
|
public function test_start_logmode_fail() {
|
|
global $CFG;
|
|
$this->resetAfterTest();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$initialbufferstate = ob_get_status();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
// Fetch the new output buffer state.
|
|
$state = ob_get_status();
|
|
|
|
// There will be no additional output buffer.
|
|
$this->assertNotEquals($initialbufferstate, $state);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, passing adhoc tests should not be logged.
|
|
*/
|
|
public function test_logmode_fail_with_passing_adhoc_task() {
|
|
global $CFG;
|
|
$this->resetAfterTest();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
$initialbufferstate = ob_get_status();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
manager::adhoc_task_complete($task);
|
|
|
|
$this->assertEmpty($logger::$storelogfortask);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, passing scheduled tests should not be logged.
|
|
*/
|
|
public function test_logmode_fail_with_passing_scheduled_task() {
|
|
global $CFG;
|
|
$this->resetAfterTest();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
$initialbufferstate = ob_get_status();
|
|
|
|
$task = $this->get_test_scheduled_task();
|
|
logmanager::start_logging($task);
|
|
|
|
manager::scheduled_task_complete($task);
|
|
|
|
$this->assertEmpty($logger::$storelogfortask);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, failing adhoc tests should be logged.
|
|
*/
|
|
public function test_logmode_fail_with_failing_adhoc_task() {
|
|
global $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
|
|
$this->mock_database();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
logmanager::start_logging($task);
|
|
manager::adhoc_task_failed($task);
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertTrue($logger::$storelogfortask[0][2]);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, failing scheduled tests should be logged.
|
|
*/
|
|
public function test_logmode_fail_with_failing_scheduled_task() {
|
|
global $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
|
|
$this->mock_database();
|
|
|
|
$task = $this->get_test_scheduled_task();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
logmanager::start_logging($task);
|
|
manager::scheduled_task_failed($task);
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertTrue($logger::$storelogfortask[0][2]);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, failing adhoc tests should be logged.
|
|
*/
|
|
public function test_logmode_any_with_failing_adhoc_task() {
|
|
global $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
|
|
$this->mock_database();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
logmanager::start_logging($task);
|
|
manager::adhoc_task_failed($task);
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertTrue($logger::$storelogfortask[0][2]);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, failing scheduled tests should be logged.
|
|
*/
|
|
public function test_logmode_any_with_failing_scheduled_task() {
|
|
global $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Mock the database. Marking jobs as failed updates a DB record which doesn't exist.
|
|
$this->mock_database();
|
|
|
|
$task = $this->get_test_scheduled_task();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_FAILONLY;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
logmanager::start_logging($task);
|
|
manager::scheduled_task_failed($task);
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertTrue($logger::$storelogfortask[0][2]);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, passing adhoc tests should be logged.
|
|
*/
|
|
public function test_logmode_any_with_passing_adhoc_task() {
|
|
global $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$this->mock_database();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_ALL;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
logmanager::start_logging($task);
|
|
manager::adhoc_task_complete($task);
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertFalse($logger::$storelogfortask[0][2]);
|
|
}
|
|
|
|
/**
|
|
* When the logmode is set to fail, passing scheduled tests should be logged.
|
|
*/
|
|
public function test_logmode_any_with_passing_scheduled_task() {
|
|
global $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$this->mock_database();
|
|
|
|
$task = $this->get_test_scheduled_task();
|
|
|
|
$CFG->task_logmode = logmanager::MODE_ALL;
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
logmanager::start_logging($task);
|
|
manager::scheduled_task_complete($task);
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertFalse($logger::$storelogfortask[0][2]);
|
|
}
|
|
|
|
/**
|
|
* Ensure that start_logging cannot be called in a nested fashion.
|
|
*/
|
|
public function test_prevent_nested_logging() {
|
|
$this->resetAfterTest();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
$this->expectException(\coding_exception::class);
|
|
logmanager::start_logging($task);
|
|
}
|
|
|
|
/**
|
|
* Ensure that logging can be called after a previous log has finished.
|
|
*/
|
|
public function test_repeated_usages() {
|
|
$this->resetAfterTest();
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
logmanager::finalise_log();
|
|
|
|
logmanager::start_logging($task);
|
|
logmanager::finalise_log();
|
|
|
|
$this->assertCount(2, $logger::$storelogfortask);
|
|
$this->assertEquals($task, $logger::$storelogfortask[0][0]);
|
|
$this->assertFalse($logger::$storelogfortask[0][2]);
|
|
$this->assertEquals($task, $logger::$storelogfortask[1][0]);
|
|
$this->assertFalse($logger::$storelogfortask[1][2]);
|
|
}
|
|
|
|
/**
|
|
* Enusre that when finalise_log is called when logging is not active, nothing happens.
|
|
*/
|
|
public function test_finalise_log_no_logging() {
|
|
$initialbufferstate = ob_get_status();
|
|
|
|
logmanager::finalise_log();
|
|
|
|
// There will be no additional output buffer.
|
|
$this->assertEquals($initialbufferstate, ob_get_status());
|
|
}
|
|
|
|
/**
|
|
* When log capture is enabled, calls to the flush function should cause log output to be both returned and captured.
|
|
*/
|
|
public function test_flush_on_own_buffer() {
|
|
$this->resetAfterTest();
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
$testoutput = "I am the output under test.\n";
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
echo $testoutput;
|
|
|
|
$this->expectOutputString($testoutput);
|
|
logmanager::flush();
|
|
|
|
// Finalise the log.
|
|
logmanager::finalise_log();
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
$this->assertEquals($testoutput, file_get_contents($logger::$storelogfortask[0][1]));
|
|
}
|
|
|
|
/**
|
|
* When log capture is enabled, calls to the flush function should not affect any subsequent ob_start.
|
|
*/
|
|
public function test_flush_does_not_flush_inner_buffers() {
|
|
$this->resetAfterTest();
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
$testoutput = "I am the output under test.\n";
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
ob_start();
|
|
echo $testoutput;
|
|
ob_end_clean();
|
|
|
|
logmanager::flush();
|
|
|
|
// Finalise the log.
|
|
logmanager::finalise_log();
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
|
|
// The task logger should not have captured the content of the inner buffer.
|
|
$this->assertEquals('', file_get_contents($logger::$storelogfortask[0][1]));
|
|
}
|
|
|
|
/**
|
|
* When log capture is enabled, calls to the flush function should not affect any subsequent ob_start.
|
|
*/
|
|
public function test_inner_flushed_buffers_are_logged() {
|
|
$this->resetAfterTest();
|
|
|
|
$logger = $this->get_mocked_logger();
|
|
|
|
$testoutput = "I am the output under test.\n";
|
|
|
|
$task = $this->get_test_adhoc_task();
|
|
logmanager::start_logging($task);
|
|
|
|
// We are going to flush the inner buffer. That means that we should expect the output immediately.
|
|
$this->expectOutputString($testoutput);
|
|
|
|
ob_start();
|
|
echo $testoutput;
|
|
ob_end_flush();
|
|
|
|
// Finalise the log.
|
|
logmanager::finalise_log();
|
|
|
|
$this->assertCount(1, $logger::$storelogfortask);
|
|
|
|
// The task logger should not have captured the content of the inner buffer.
|
|
$this->assertEquals($testoutput, file_get_contents($logger::$storelogfortask[0][1]));
|
|
}
|
|
|
|
/**
|
|
* Get an example adhoc task to use for testing.
|
|
*
|
|
* @return adhoc_task
|
|
*/
|
|
protected function get_test_adhoc_task() : adhoc_task {
|
|
$task = $this->getMockForAbstractClass(adhoc_task::class);
|
|
$task->set_component('core');
|
|
|
|
// Mock a lock on the task.
|
|
$lock = $this->getMockBuilder(\core\lock\lock::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$task->set_lock($lock);
|
|
|
|
return $task;
|
|
}
|
|
|
|
/**
|
|
* Get an example scheduled task to use for testing.
|
|
*
|
|
* @return scheduled_task
|
|
*/
|
|
protected function get_test_scheduled_task() : scheduled_task {
|
|
$task = $this->getMockForAbstractClass(scheduled_task::class);
|
|
|
|
// Mock a lock on the task.
|
|
$lock = $this->getMockBuilder(\core\lock\lock::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$task->set_lock($lock);
|
|
|
|
return $task;
|
|
}
|
|
|
|
/**
|
|
* Create and configure a mocked task logger.
|
|
*
|
|
* @return task_logger
|
|
*/
|
|
protected function get_mocked_logger() {
|
|
global $CFG;
|
|
|
|
// We will modify config for the alternate logging class therefore we mnust reset after the test.
|
|
$this->resetAfterTest();
|
|
|
|
// Note PHPUnit does not support mocking static functions.
|
|
$CFG->task_log_class = logging_test_mocked_logger::class;
|
|
logging_test_mocked_logger::test_reset();
|
|
|
|
return $CFG->task_log_class;
|
|
}
|
|
|
|
/**
|
|
* Mock the database.
|
|
*/
|
|
protected function mock_database() {
|
|
global $DB;
|
|
|
|
// Store the old Database for restoration in reset.
|
|
$this->DB = $DB;
|
|
|
|
$DB = $this->getMockBuilder(\moodle_database::class)
|
|
->getMock();
|
|
|
|
$DB->method('get_record')
|
|
->willReturn((object) []);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mocked logger.
|
|
*
|
|
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class logging_test_mocked_logger implements task_logger {
|
|
|
|
/**
|
|
* @var bool Whether this is configured.
|
|
*/
|
|
public static $isconfigured = true;
|
|
|
|
/**
|
|
* @var array Arguments that store_log_for_task was called with.
|
|
*/
|
|
public static $storelogfortask = [];
|
|
|
|
/**
|
|
* @var bool Whether this logger has a report.
|
|
*/
|
|
public static $haslogreport = true;
|
|
|
|
/**
|
|
* Reset the test class.
|
|
*/
|
|
public static function test_reset() {
|
|
self::$isconfigured = true;
|
|
self::$storelogfortask = [];
|
|
self::$haslogreport = true;
|
|
}
|
|
|
|
/**
|
|
* Whether the task is configured and ready to log.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function is_configured() : bool {
|
|
return self::$isconfigured;
|
|
}
|
|
|
|
/**
|
|
* Store the log for the specified task.
|
|
*
|
|
* @param task_base $task The task that the log belongs to.
|
|
* @param string $logpath The path to the log on disk
|
|
* @param bool $failed Whether the task failed
|
|
* @param int $dbreads The number of DB reads
|
|
* @param int $dbwrites The number of DB writes
|
|
* @param float $timestart The start time of the task
|
|
* @param float $timeend The end time of the task
|
|
*/
|
|
public static function store_log_for_task(task_base $task, string $logpath, bool $failed,
|
|
int $dbreads, int $dbwrites, float $timestart, float $timeend) {
|
|
self::$storelogfortask[] = func_get_args();
|
|
}
|
|
|
|
/**
|
|
* Whether this task logger has a report available.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function has_log_report() : bool {
|
|
return self::$haslogreport;
|
|
}
|
|
|
|
/**
|
|
* Get any URL available for viewing relevant task log reports.
|
|
*
|
|
* @param string $classname The task class to fetch for
|
|
* @return \moodle_url
|
|
*/
|
|
public static function get_url_for_task_class(string $classname) : \moodle_url {
|
|
return new \moodle_url('');
|
|
}
|
|
|
|
}
|