first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
<?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/>.
/**
* @package moodlecore
* @subpackage backup-logger
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Base abstract class for all the loggers to be used in backup/restore
*
* Any message passed will be processed by all the loggers in the defined chain
* (note some implementations may be not strictly "loggers" but classes performing
* other sort of tasks (avoiding browser/php timeouts, painters...). One simple 1-way
* basic chain of commands/responsibility pattern.
*
* TODO: Finish phpdocs
*/
abstract class base_logger implements checksumable {
protected $level; // minimum level of logging this logger must handle (valid level from @backup class)
protected $showdate; // flag to decide if the logger must output the date (true) or no (false)
protected $showlevel; // flag to decide if the logger must output the level (true) or no (false)
protected $next; // next logger in the chain
public function __construct($level, $showdate = false, $showlevel = false) {
// TODO: check level is correct
$this->level = $level;
$this->showdate = $showdate;
$this->showlevel = $showlevel;
$this->next = null;
}
final public function set_next($next) {
// TODO: Check is a base logger
// TODO: Check next hasn't been set already
// TODO: Avoid circular dependencies
if ($this->is_circular_reference($next)) {
$a = new stdclass();
$a->alreadyinchain = get_class($this);
$a->main = get_class($next);
throw new base_logger_exception('logger_circular_reference', $a);
}
$this->next = $next;
}
public function get_next() {
return $this->next;
}
public function get_level() {
return $this->level;
}
/**
* Destroy (nullify) the chain of loggers references, also closing resources when needed.
*
* @since Moodle 3.1
*/
final public function destroy() {
// Recursively destroy the chain.
if ($this->next !== null) {
$this->next->destroy();
$this->next = null;
}
// And close every logger.
$this->close();
}
/**
* Close any resource the logger may have open.
*
* @since Moodle 3.1
*/
public function close() {
// Nothing to do by default. Only loggers using resources (files, own connections...) need to override this.
}
// checksumable interface methods
public function calculate_checksum() {
// Checksum is a simple md5 hash of classname, level and
// on each specialised logger, its own atrributes
// Not following the chain at all.
return md5(get_class($this) . '-' . $this->level);
}
public function is_checksum_correct($checksum) {
return $this->calculate_checksum() === $checksum;
}
// Protected API starts here
abstract protected function action($message, $level, $options = null); // To implement
final public function process($message, $level, $options = null) {
$result = true;
if ($this->level != backup::LOG_NONE && $this->level >= $level
&& !(defined('BEHAT_TEST') && BEHAT_TEST)) { // Perform action conditionally.
$result = $this->action($message, $level, $options);
}
if ($result === false) { // Something was wrong, stop the chain
return $result;
}
if ($this->next !== null) { // The chain continues being processed
$result = $this->next->process($message, $level, $options);
}
return $result;
}
protected function is_circular_reference($obj) {
// Get object all nexts recursively and check if $this is already there
$nexts = $obj->get_nexts();
if (array_key_exists($this->calculate_checksum(), $nexts) || $obj == $this) {
return true;
}
return false;
}
protected function get_nexts() {
$nexts = array();
if ($this->next !== null) {
$nexts[$this->next->calculate_checksum()] = $this->next->calculate_checksum();
$nexts = array_merge($nexts, $this->next->get_nexts());
}
return $nexts;
}
protected function get_datestr() {
return userdate(time(), '%c');
}
protected function get_levelstr($level) {
$result = 'undefined';
switch ($level) {
case backup::LOG_ERROR:
$result = 'error';
break;
case backup::LOG_WARNING:
$result = 'warn';
break;
case backup::LOG_INFO:
$result = 'info';
break;
case backup::LOG_DEBUG:
$result = 'debug';
break;
}
return $result;
}
protected function get_prefix($level, $options) {
$prefix = '';
if ($this->showdate) {
$prefix .= '[' . $this->get_datestr() . '] ';
}
if ($this->showlevel) {
$prefix .= '[' . $this->get_levelstr($level) . '] ';
}
return $prefix;
}
}
/*
* Exception class used by all the @base_logger stuff
*/
class base_logger_exception extends backup_exception {
public function __construct($errorcode, $a=NULL, $debuginfo=null) {
parent::__construct($errorcode, $a, $debuginfo);
}
}
@@ -0,0 +1,46 @@
<?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/>.
/**
* Logger that stores HTML log data in memory, ready for later display.
*
* @package core_backup
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_backup_html_logger extends base_logger {
/**
* @var string HTML output
*/
protected $html = '';
protected function action($message, $level, $options = null) {
$prefix = $this->get_prefix($level, $options);
$depth = isset($options['depth']) ? $options['depth'] : 0;
$this->html .= $prefix . str_repeat('&nbsp;&nbsp;', $depth) .
s($message) . '<br/>' . PHP_EOL;
return true;
}
/**
* Gets the full HTML content of the log.
*
* @return string HTML content of log
*/
public function get_html() {
return $this->html;
}
}
@@ -0,0 +1,74 @@
<?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/>.
/**
* @package moodlecore
* @subpackage backup-logger
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Logger implementation that sends messages to database
*
* TODO: Finish phpdocs
*/
class database_logger extends base_logger {
protected $datecol; // Name of the field where the timestamp will be stored
protected $levelcol; // Name of the field where the level of the message will be stored
protected $messagecol; // Name of the field where the message will be stored
protected $logtable; // Table, without prefix where information must be logged
protected $columns; // Array of columns and values to set in all actions logged
// Protected API starts here
public function __construct($level, $datecol = false, $levelcol = false, $messagecol = null, $logtable = null, $columns = null) {
// TODO check $datecol exists
// TODO check $levelcol exists
// TODO check $logtable exists
// TODO check $messagecol exists
// TODO check all $columns exist
$this->datecol = $datecol;
$this->levelcol = $levelcol;
$this->messagecol = $messagecol;
$this->logtable = $logtable;
$this->columns = $columns;
parent::__construct($level, (bool)$datecol, (bool)$levelcol);
}
protected function action($message, $level, $options = null) {
$columns = $this->columns;
if ($this->datecol) {
$columns[$this->datecol] = time();
}
if ($this->levelcol) {
$columns[$this->levelcol] = $level;
}
$columns[$this->messagecol] = clean_param($message, PARAM_NOTAGS);
return $this->insert_log_record($this->logtable, $columns);
}
protected function insert_log_record($table, $columns) {
// TODO: Allow to use an alternate connection (created in constructor)
// based in some CFG->backup_database_logger_newconn = true in order
// to preserve DB logs if the whole backup/restore transaction is
// rollback
global $DB;
return $DB->insert_record($table, $columns, false); // Don't return inserted id
}
}
@@ -0,0 +1,41 @@
<?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/>.
/**
* @package moodlecore
* @subpackage backup-logger
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Logger implementation that sends messages to error_log()
*
* TODO: Finish phpdocs
*/
class error_log_logger extends base_logger {
// Protected API starts here
protected function action($message, $level, $options = null) {
if (PHPUNIT_TEST) {
// no logging from PHPUnit, it is admins fault if it does not work!!!
return true;
}
return error_log($message);
}
}
+105
View File
@@ -0,0 +1,105 @@
<?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/>.
/**
* @package moodlecore
* @subpackage backup-logger
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Logger implementation that sends indented messages (depth option) to one file
*
* TODO: Finish phpdocs
*/
class file_logger extends base_logger {
protected $fullpath; // Full path to OS file where contents will be stored
protected $fhandle; // File handle where all write operations happen
public function __construct($level, $showdate = false, $showlevel = false, $fullpath = null) {
if (empty($fullpath)) {
throw new base_logger_exception('missing_fullpath_parameter', $fullpath);
}
if (!is_writable(dirname($fullpath))) {
throw new base_logger_exception('file_not_writable', $fullpath);
}
// Open the OS file for writing (append)
$this->fullpath = $fullpath;
if ($level > backup::LOG_NONE) { // Only create the file if we are going to log something
if (! $this->fhandle = fopen($this->fullpath, 'a')) {
throw new base_logger_exception('error_opening_file', $fullpath);
}
}
parent::__construct($level, $showdate, $showlevel);
}
public function __destruct() {
if (is_resource($this->fhandle)) {
// Blindy close the file handler (no exceptions in destruct).
@fclose($this->fhandle);
}
}
public function __sleep() {
if (is_resource($this->fhandle)) {
// Blindy close the file handler before serialization.
@fclose($this->fhandle);
$this->fhandle = null;
}
return array('level', 'showdate', 'showlevel', 'next', 'fullpath');
}
public function __wakeup() {
if ($this->level > backup::LOG_NONE) { // Only create the file if we are going to log something
if (! $this->fhandle = fopen($this->fullpath, 'a')) {
throw new base_logger_exception('error_opening_file', $this->fullpath);
}
}
}
/**
* Close the logger resources (file handle) if still open.
*
* @since Moodle 3.1
*/
public function close() {
// Close the file handle if hasn't been closed already.
if (is_resource($this->fhandle)) {
fclose($this->fhandle);
$this->fhandle = null;
}
}
// Protected API starts here
protected function action($message, $level, $options = null) {
$prefix = $this->get_prefix($level, $options);
$depth = isset($options['depth']) ? $options['depth'] : 0;
// Depending of the type (extension of the file), format differently
if (substr($this->fullpath, -5) !== '.html') {
$content = $prefix . str_repeat(' ', $depth) . $message . PHP_EOL;
} else {
$content = $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
}
if (!is_resource($this->fhandle) || (false === fwrite($this->fhandle, $content))) {
throw new base_logger_exception('error_writing_file', $this->fullpath);
}
return true;
}
}
@@ -0,0 +1,46 @@
<?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/>.
/**
* @package moodlecore
* @subpackage backup-logger
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Logger implementation that sends indented messages (depth option) to output
*
* TODO: Finish phpdocs
*/
class output_indented_logger extends base_logger {
// Protected API starts here
protected function action($message, $level, $options = null) {
$prefix = $this->get_prefix($level, $options);
$depth = isset($options['depth']) ? $options['depth'] : 0;
// Depending of running from browser/command line, format differently
if (defined('STDOUT')) {
echo $prefix . str_repeat(' ', $depth) . $message . PHP_EOL;
} else {
echo $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
}
flush();
return true;
}
}
@@ -0,0 +1,45 @@
<?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/>.
/**
* @package moodlecore
* @subpackage backup-logger
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Logger implementation that sends text messages to output
*
* TODO: Finish phpdocs
*/
class output_text_logger extends base_logger {
// Protected API starts here
protected function action($message, $level, $options = null) {
$prefix = $this->get_prefix($level, $options);
// Depending of running from browser/command line, format differently
if (defined('STDOUT')) {
echo $prefix . $message . PHP_EOL;
} else {
echo $prefix . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
}
flush();
return true;
}
}
+422
View File
@@ -0,0 +1,422 @@
<?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/>.
/**
* Logger tests (all).
* @package core_backup
* @category test
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_backup;
use backup;
use base_logger;
use base_logger_exception;
use database_logger;
use error_log_logger;
use file_logger;
use output_indented_logger;
use output_text_logger;
defined('MOODLE_INTERNAL') || die();
// Include all the needed stuff
global $CFG;
require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php');
require_once($CFG->dirroot . '/backup/backup.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/base_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/error_log_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_text_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
/**
* Logger tests (all).
*
* @package core_backup
* @category test
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class logger_test extends \basic_testcase {
/**
* test base_logger class
*/
function test_base_logger(): void {
// Test logger with simple action (message * level)
$lo = new mock_base_logger1(backup::LOG_ERROR);
$msg = 13;
$this->assertEquals($lo->process($msg, backup::LOG_ERROR), $msg * backup::LOG_ERROR);
// With lowest level must return true
$lo = new mock_base_logger1(backup::LOG_ERROR);
$msg = 13;
$this->assertTrue($lo->process($msg, backup::LOG_DEBUG));
// Chain 2 loggers, we must get as result the result of the inner one
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
$lo2 = new mock_base_logger2(backup::LOG_ERROR);
$lo1->set_next($lo2);
$msg = 13;
$this->assertEquals($lo1->process($msg, backup::LOG_ERROR), $msg + backup::LOG_ERROR);
// Try circular reference
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
try {
$lo1->set_next($lo1); //self
$this->assertTrue(false, 'base_logger_exception expected');
} catch (\Exception $e) {
$this->assertTrue($e instanceof base_logger_exception);
$this->assertEquals($e->errorcode, 'logger_circular_reference');
$this->assertTrue($e->a instanceof \stdClass);
$this->assertEquals($e->a->main, get_class($lo1));
$this->assertEquals($e->a->alreadyinchain, get_class($lo1));
}
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
$lo2 = new mock_base_logger2(backup::LOG_ERROR);
$lo3 = new mock_base_logger3(backup::LOG_ERROR);
$lo1->set_next($lo2);
$lo2->set_next($lo3);
try {
$lo3->set_next($lo1);
$this->assertTrue(false, 'base_logger_exception expected');
} catch (\Exception $e) {
$this->assertTrue($e instanceof base_logger_exception);
$this->assertEquals($e->errorcode, 'logger_circular_reference');
$this->assertTrue($e->a instanceof \stdClass);
$this->assertEquals($e->a->main, get_class($lo1));
$this->assertEquals($e->a->alreadyinchain, get_class($lo3));
}
// Test stopper logger
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
$lo2 = new mock_base_logger2(backup::LOG_ERROR);
$lo3 = new mock_base_logger3(backup::LOG_ERROR);
$lo1->set_next($lo2);
$lo2->set_next($lo3);
$msg = 13;
$this->assertFalse($lo1->process($msg, backup::LOG_ERROR));
// Test checksum correct
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
$lo1->is_checksum_correct(get_class($lo1) . '-' . backup::LOG_ERROR);
// Test get_levelstr()
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
$this->assertEquals($lo1->get_levelstr(backup::LOG_NONE), 'undefined');
$this->assertEquals($lo1->get_levelstr(backup::LOG_ERROR), 'error');
$this->assertEquals($lo1->get_levelstr(backup::LOG_WARNING), 'warn');
$this->assertEquals($lo1->get_levelstr(backup::LOG_INFO), 'info');
$this->assertEquals($lo1->get_levelstr(backup::LOG_DEBUG), 'debug');
// Test destroy.
$lo1 = new mock_base_logger1(backup::LOG_ERROR);
$lo2 = new mock_base_logger2(backup::LOG_ERROR);
$lo1->set_next($lo2);
$this->assertInstanceOf('base_logger', $lo1->get_next());
$this->assertNull($lo2->get_next());
$lo1->destroy();
$this->assertNull($lo1->get_next());
$this->assertNull($lo2->get_next());
}
/**
* test error_log_logger class
*/
function test_error_log_logger(): void {
// Not much really to test, just instantiate and execute, should return true
$lo = new error_log_logger(backup::LOG_ERROR);
$this->assertTrue($lo instanceof error_log_logger);
$message = 'This log exists because you have run Moodle unit tests: Ignore it';
$result = $lo->process($message, backup::LOG_ERROR);
$this->assertTrue($result);
}
/**
* test output_text_logger class
*/
function test_output_text_logger(): void {
// Instantiate without date nor level output
$lo = new output_text_logger(backup::LOG_ERROR);
$this->assertTrue($lo instanceof output_text_logger);
$message = 'testing output_text_logger';
ob_start(); // Capture output
$result = $lo->process($message, backup::LOG_ERROR);
$contents = ob_get_contents();
ob_end_clean(); // End capture and discard
$this->assertTrue($result);
$this->assertTrue(strpos($contents, $message) !== false);
// Instantiate with date and level output
$lo = new output_text_logger(backup::LOG_ERROR, true, true);
$this->assertTrue($lo instanceof output_text_logger);
$message = 'testing output_text_logger';
ob_start(); // Capture output
$result = $lo->process($message, backup::LOG_ERROR);
$contents = ob_get_contents();
ob_end_clean(); // End capture and discard
$this->assertTrue($result);
$this->assertTrue(strpos($contents,'[') === 0);
$this->assertTrue(strpos($contents,'[error]') !== false);
$this->assertTrue(strpos($contents, $message) !== false);
$this->assertTrue(substr_count($contents , '] ') >= 2);
}
/**
* test output_indented_logger class
*/
function test_output_indented_logger(): void {
// Instantiate without date nor level output
$options = array('depth' => 2);
$lo = new output_indented_logger(backup::LOG_ERROR);
$this->assertTrue($lo instanceof output_indented_logger);
$message = 'testing output_indented_logger';
ob_start(); // Capture output
$result = $lo->process($message, backup::LOG_ERROR, $options);
$contents = ob_get_contents();
ob_end_clean(); // End capture and discard
$this->assertTrue($result);
if (defined('STDOUT')) {
$check = ' ';
} else {
$check = '&nbsp;&nbsp;';
}
$this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
// Instantiate with date and level output
$options = array('depth' => 3);
$lo = new output_indented_logger(backup::LOG_ERROR, true, true);
$this->assertTrue($lo instanceof output_indented_logger);
$message = 'testing output_indented_logger';
ob_start(); // Capture output
$result = $lo->process($message, backup::LOG_ERROR, $options);
$contents = ob_get_contents();
ob_end_clean(); // End capture and discard
$this->assertTrue($result);
$this->assertTrue(strpos($contents,'[') === 0);
$this->assertTrue(strpos($contents,'[error]') !== false);
$this->assertTrue(strpos($contents, $message) !== false);
$this->assertTrue(substr_count($contents , '] ') >= 2);
if (defined('STDOUT')) {
$check = ' ';
} else {
$check = '&nbsp;&nbsp;';
}
$this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
}
/**
* test database_logger class
*/
function test_database_logger(): void {
// Instantiate with date and level output (and with specs from the global moodle "log" table so checks will pass
$now = time();
$datecol = 'time';
$levelcol = 'action';
$messagecol = 'info';
$logtable = 'log';
$columns = array('url' => 'http://127.0.0.1');
$loglevel = backup::LOG_ERROR;
$lo = new mock_database_logger(backup::LOG_ERROR, $datecol, $levelcol, $messagecol, $logtable, $columns);
$this->assertTrue($lo instanceof database_logger);
$message = 'testing database_logger';
$result = $lo->process($message, $loglevel);
// Check everything is ready to be inserted to DB
$this->assertEquals($result['table'], $logtable);
$this->assertTrue($result['columns'][$datecol] >= $now);
$this->assertEquals($result['columns'][$levelcol], $loglevel);
$this->assertEquals($result['columns'][$messagecol], $message);
$this->assertEquals($result['columns']['url'], $columns['url']);
}
/**
* test file_logger class
*/
function test_file_logger(): void {
global $CFG;
$file = $CFG->tempdir . '/test/test_file_logger.txt';
// Remove the test dir and any content
@remove_dir(dirname($file));
// Recreate test dir
if (!check_dir_exists(dirname($file), true, true)) {
throw new \moodle_exception('error_creating_temp_dir', 'error', dirname($file));
}
// Instantiate with date and level output, and also use the depth option
$options = array('depth' => 3);
$lo1 = new file_logger(backup::LOG_ERROR, true, true, $file);
$this->assertTrue($lo1 instanceof file_logger);
$message1 = 'testing file_logger';
$result = $lo1->process($message1, backup::LOG_ERROR, $options);
$this->assertTrue($result);
// Another file_logger is going towrite there too without closing
$options = array();
$lo2 = new file_logger(backup::LOG_WARNING, true, true, $file);
$this->assertTrue($lo2 instanceof file_logger);
$message2 = 'testing file_logger2';
$result = $lo2->process($message2, backup::LOG_WARNING, $options);
$this->assertTrue($result);
// Destroy loggers.
$lo1->destroy();
$lo2->destroy();
// Load file results to analyze them
$fcontents = file_get_contents($file);
$acontents = explode(PHP_EOL, $fcontents); // Split by line
$this->assertTrue(strpos($acontents[0], $message1) !== false);
$this->assertTrue(strpos($acontents[0], '[error]') !== false);
$this->assertTrue(strpos($acontents[0], ' ') !== false);
$this->assertTrue(substr_count($acontents[0] , '] ') >= 2);
$this->assertTrue(strpos($acontents[1], $message2) !== false);
$this->assertTrue(strpos($acontents[1], '[warn]') !== false);
$this->assertTrue(strpos($acontents[1], ' ') === false);
$this->assertTrue(substr_count($acontents[1] , '] ') >= 2);
unlink($file); // delete file
// Try one html file
check_dir_exists($CFG->tempdir . '/test');
$file = $CFG->tempdir . '/test/test_file_logger.html';
$options = array('depth' => 1);
$lo = new file_logger(backup::LOG_ERROR, true, true, $file);
$this->assertTrue($lo instanceof file_logger);
$this->assertTrue(file_exists($file));
$message = 'testing file_logger';
$result = $lo->process($message, backup::LOG_ERROR, $options);
$lo->close(); // Closes logger.
// Get file contents and inspect them
$fcontents = file_get_contents($file);
$this->assertTrue($result);
$this->assertTrue(strpos($fcontents, $message) !== false);
$this->assertTrue(strpos($fcontents, '[error]') !== false);
$this->assertTrue(strpos($fcontents, '&nbsp;&nbsp;') !== false);
$this->assertTrue(substr_count($fcontents , '] ') >= 2);
unlink($file); // delete file
// Instantiate, write something, force deletion, try to write again
check_dir_exists($CFG->tempdir . '/test');
$file = $CFG->tempdir . '/test/test_file_logger.html';
$lo = new mock_file_logger(backup::LOG_ERROR, true, true, $file);
$this->assertTrue(file_exists($file));
$message = 'testing file_logger';
$result = $lo->process($message, backup::LOG_ERROR);
$lo->close();
$this->assertNull($lo->get_fhandle());
try {
$result = @$lo->process($message, backup::LOG_ERROR); // Try to write again
$this->assertTrue(false, 'base_logger_exception expected');
} catch (\Exception $e) {
$this->assertTrue($e instanceof base_logger_exception);
$this->assertEquals($e->errorcode, 'error_writing_file');
}
// Instantiate without file
try {
$lo = new file_logger(backup::LOG_WARNING, true, true, '');
$this->assertTrue(false, 'base_logger_exception expected');
} catch (\Exception $e) {
$this->assertTrue($e instanceof base_logger_exception);
$this->assertEquals($e->errorcode, 'missing_fullpath_parameter');
}
// Instantiate in (near) impossible path
$file = $CFG->tempdir . '/test_azby/test_file_logger.txt';
try {
$lo = new file_logger(backup::LOG_WARNING, true, true, $file);
$this->assertTrue(false, 'base_logger_exception expected');
} catch (\Exception $e) {
$this->assertTrue($e instanceof base_logger_exception);
$this->assertEquals($e->errorcode, 'file_not_writable');
$this->assertEquals($e->a, $file);
}
// Instantiate one file logger with level = backup::LOG_NONE
$file = $CFG->tempdir . '/test/test_file_logger.txt';
$lo = new file_logger(backup::LOG_NONE, true, true, $file);
$this->assertTrue($lo instanceof file_logger);
$this->assertFalse(file_exists($file));
$lo->close();
// Remove the test dir and any content
@remove_dir(dirname($file));
}
}
/**
* helper extended base_logger class that implements some methods for testing
* Simply return the product of message and level
*/
class mock_base_logger1 extends base_logger {
protected function action($message, $level, $options = null) {
return $message * $level; // Simply return that, for testing
}
public function get_levelstr($level) {
return parent::get_levelstr($level);
}
}
/**
* helper extended base_logger class that implements some methods for testing
* Simply return the sum of message and level
*/
class mock_base_logger2 extends base_logger {
protected function action($message, $level, $options = null) {
return $message + $level; // Simply return that, for testing
}
}
/**
* helper extended base_logger class that implements some methods for testing
* Simply return 8
*/
class mock_base_logger3 extends base_logger {
protected function action($message, $level, $options = null) {
return false; // Simply return false, for testing stopper
}
}
/**
* helper extended database_logger class that implements some methods for testing
* Returns the complete info that normally will be used by insert record calls
*/
class mock_database_logger extends database_logger {
protected function insert_log_record($table, $columns) {
return array('table' => $table, 'columns' => $columns);
}
}
/**
* helper extended file_logger class that implements some methods for testing
* Returns the, usually protected, handle
*/
class mock_file_logger extends file_logger {
function get_fhandle() {
return $this->fhandle;
}
}