You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
16 KiB
406 lines
16 KiB
2 years ago
|
<?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 core_backup
|
||
|
* @category phpunit
|
||
|
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
|
||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||
|
*/
|
||
|
|
||
|
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)
|
||
|
*/
|
||
|
class backup_logger_testcase extends basic_testcase {
|
||
|
|
||
|
/**
|
||
|
* test base_logger class
|
||
|
*/
|
||
|
function test_base_logger() {
|
||
|
// 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() {
|
||
|
// 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() {
|
||
|
// 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() {
|
||
|
// 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 = ' ';
|
||
|
}
|
||
|
$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 = ' ';
|
||
|
}
|
||
|
$this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* test database_logger class
|
||
|
*/
|
||
|
function test_database_logger() {
|
||
|
// 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() {
|
||
|
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, ' ') !== 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;
|
||
|
}
|
||
|
}
|