ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ.. /** * This file contains the unit tests for the task logging system. * * @package core * @category test * @copyright 2018 Andrew Nicols * @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 * @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 logging_test_mocked_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::reset_test(); 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 * @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 reset_test() { 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(''); } }