2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
18 * @package core_backup
20 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 defined('MOODLE_INTERNAL') || die();
26 // Include all the needed stuff
28 require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php');
29 require_once($CFG->dirroot . '/backup/backup.class.php');
30 require_once($CFG->dirroot . '/backup/util/loggers/base_logger.class.php');
31 require_once($CFG->dirroot . '/backup/util/loggers/error_log_logger.class.php');
32 require_once($CFG->dirroot . '/backup/util/loggers/output_text_logger.class.php');
33 require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
34 require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
35 require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
41 class backup_logger_testcase extends basic_testcase {
44 * test base_logger class
46 function test_base_logger() {
47 // Test logger with simple action (message * level)
48 $lo = new mock_base_logger1(backup::LOG_ERROR);
50 $this->assertEquals($lo->process($msg, backup::LOG_ERROR), $msg * backup::LOG_ERROR);
51 // With lowest level must return true
52 $lo = new mock_base_logger1(backup::LOG_ERROR);
54 $this->assertTrue($lo->process($msg, backup::LOG_DEBUG));
56 // Chain 2 loggers, we must get as result the result of the inner one
57 $lo1 = new mock_base_logger1(backup::LOG_ERROR);
58 $lo2 = new mock_base_logger2(backup::LOG_ERROR);
61 $this->assertEquals($lo1->process($msg, backup::LOG_ERROR), $msg + backup::LOG_ERROR);
63 // Try circular reference
64 $lo1 = new mock_base_logger1(backup::LOG_ERROR);
66 $lo1->set_next($lo1); //self
67 $this->assertTrue(false, 'base_logger_exception expected');
68 } catch (exception $e) {
69 $this->assertTrue($e instanceof base_logger_exception);
70 $this->assertEquals($e->errorcode, 'logger_circular_reference');
71 $this->assertTrue($e->a instanceof stdclass);
72 $this->assertEquals($e->a->main, get_class($lo1));
73 $this->assertEquals($e->a->alreadyinchain, get_class($lo1));
76 $lo1 = new mock_base_logger1(backup::LOG_ERROR);
77 $lo2 = new mock_base_logger2(backup::LOG_ERROR);
78 $lo3 = new mock_base_logger3(backup::LOG_ERROR);
83 $this->assertTrue(false, 'base_logger_exception expected');
84 } catch (exception $e) {
85 $this->assertTrue($e instanceof base_logger_exception);
86 $this->assertEquals($e->errorcode, 'logger_circular_reference');
87 $this->assertTrue($e->a instanceof stdclass);
88 $this->assertEquals($e->a->main, get_class($lo1));
89 $this->assertEquals($e->a->alreadyinchain, get_class($lo3));
92 // Test stopper logger
93 $lo1 = new mock_base_logger1(backup::LOG_ERROR);
94 $lo2 = new mock_base_logger2(backup::LOG_ERROR);
95 $lo3 = new mock_base_logger3(backup::LOG_ERROR);
98 $this->assertFalse($lo1->process('test', backup::LOG_ERROR));
100 // Test checksum correct
101 $lo1 = new mock_base_logger1(backup::LOG_ERROR);
102 $lo1->is_checksum_correct(get_class($lo1) . '-' . backup::LOG_ERROR);
104 // Test get_levelstr()
105 $lo1 = new mock_base_logger1(backup::LOG_ERROR);
106 $this->assertEquals($lo1->get_levelstr(backup::LOG_NONE), 'undefined');
107 $this->assertEquals($lo1->get_levelstr(backup::LOG_ERROR), 'error');
108 $this->assertEquals($lo1->get_levelstr(backup::LOG_WARNING), 'warn');
109 $this->assertEquals($lo1->get_levelstr(backup::LOG_INFO), 'info');
110 $this->assertEquals($lo1->get_levelstr(backup::LOG_DEBUG), 'debug');
114 * test error_log_logger class
116 function test_error_log_logger() {
117 // Not much really to test, just instantiate and execute, should return true
118 $lo = new error_log_logger(backup::LOG_ERROR);
119 $this->assertTrue($lo instanceof error_log_logger);
120 $message = 'This log exists because you have run Moodle unit tests: Ignore it';
121 $result = $lo->process($message, backup::LOG_ERROR);
122 $this->assertTrue($result);
126 * test output_text_logger class
128 function test_output_text_logger() {
129 // Instantiate without date nor level output
130 $lo = new output_text_logger(backup::LOG_ERROR);
131 $this->assertTrue($lo instanceof output_text_logger);
132 $message = 'testing output_text_logger';
133 ob_start(); // Capture output
134 $result = $lo->process($message, backup::LOG_ERROR);
135 $contents = ob_get_contents();
136 ob_end_clean(); // End capture and discard
137 $this->assertTrue($result);
138 $this->assertTrue(strpos($contents, $message) !== false);
140 // Instantiate with date and level output
141 $lo = new output_text_logger(backup::LOG_ERROR, true, true);
142 $this->assertTrue($lo instanceof output_text_logger);
143 $message = 'testing output_text_logger';
144 ob_start(); // Capture output
145 $result = $lo->process($message, backup::LOG_ERROR);
146 $contents = ob_get_contents();
147 ob_end_clean(); // End capture and discard
148 $this->assertTrue($result);
149 $this->assertTrue(strpos($contents,'[') === 0);
150 $this->assertTrue(strpos($contents,'[error]') !== false);
151 $this->assertTrue(strpos($contents, $message) !== false);
152 $this->assertTrue(substr_count($contents , '] ') >= 2);
156 * test output_indented_logger class
158 function test_output_indented_logger() {
159 // Instantiate without date nor level output
160 $options = array('depth' => 2);
161 $lo = new output_indented_logger(backup::LOG_ERROR);
162 $this->assertTrue($lo instanceof output_indented_logger);
163 $message = 'testing output_indented_logger';
164 ob_start(); // Capture output
165 $result = $lo->process($message, backup::LOG_ERROR, $options);
166 $contents = ob_get_contents();
167 ob_end_clean(); // End capture and discard
168 $this->assertTrue($result);
169 if (defined('STDOUT')) {
172 $check = ' ';
174 $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
176 // Instantiate with date and level output
177 $options = array('depth' => 3);
178 $lo = new output_indented_logger(backup::LOG_ERROR, true, true);
179 $this->assertTrue($lo instanceof output_indented_logger);
180 $message = 'testing output_indented_logger';
181 ob_start(); // Capture output
182 $result = $lo->process($message, backup::LOG_ERROR, $options);
183 $contents = ob_get_contents();
184 ob_end_clean(); // End capture and discard
185 $this->assertTrue($result);
186 $this->assertTrue(strpos($contents,'[') === 0);
187 $this->assertTrue(strpos($contents,'[error]') !== false);
188 $this->assertTrue(strpos($contents, $message) !== false);
189 $this->assertTrue(substr_count($contents , '] ') >= 2);
190 if (defined('STDOUT')) {
193 $check = ' ';
195 $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
199 * test database_logger class
201 function test_database_logger() {
202 // Instantiate with date and level output (and with specs from the global moodle "log" table so checks will pass
205 $levelcol = 'action';
206 $messagecol = 'info';
208 $columns = array('url' => 'http://127.0.0.1');
209 $loglevel = backup::LOG_ERROR;
210 $lo = new mock_database_logger(backup::LOG_ERROR, $datecol, $levelcol, $messagecol, $logtable, $columns);
211 $this->assertTrue($lo instanceof database_logger);
212 $message = 'testing database_logger';
213 $result = $lo->process($message, $loglevel);
214 // Check everything is ready to be inserted to DB
215 $this->assertEquals($result['table'], $logtable);
216 $this->assertTrue($result['columns'][$datecol] >= $now);
217 $this->assertEquals($result['columns'][$levelcol], $loglevel);
218 $this->assertEquals($result['columns'][$messagecol], $message);
219 $this->assertEquals($result['columns']['url'], $columns['url']);
223 * test file_logger class
225 function test_file_logger() {
228 $file = $CFG->tempdir . '/test/test_file_logger.txt';
229 // Remove the test dir and any content
230 @remove_dir(dirname($file));
232 if (!check_dir_exists(dirname($file), true, true)) {
233 throw new moodle_exception('error_creating_temp_dir', 'error', dirname($file));
236 // Instantiate with date and level output, and also use the depth option
237 $options = array('depth' => 3);
238 $lo1 = new file_logger(backup::LOG_ERROR, true, true, $file);
239 $this->assertTrue($lo1 instanceof file_logger);
240 $message1 = 'testing file_logger';
241 $result = $lo1->process($message1, backup::LOG_ERROR, $options);
242 $this->assertTrue($result);
244 // Another file_logger is going towrite there too without closing
246 $lo2 = new file_logger(backup::LOG_WARNING, true, true, $file);
247 $this->assertTrue($lo2 instanceof file_logger);
248 $message2 = 'testing file_logger2';
249 $result = $lo2->process($message2, backup::LOG_WARNING, $options);
250 $this->assertTrue($result);
256 // Load file results to analyze them
257 $fcontents = file_get_contents($file);
258 $acontents = explode(PHP_EOL, $fcontents); // Split by line
259 $this->assertTrue(strpos($acontents[0], $message1) !== false);
260 $this->assertTrue(strpos($acontents[0], '[error]') !== false);
261 $this->assertTrue(strpos($acontents[0], ' ') !== false);
262 $this->assertTrue(substr_count($acontents[0] , '] ') >= 2);
263 $this->assertTrue(strpos($acontents[1], $message2) !== false);
264 $this->assertTrue(strpos($acontents[1], '[warn]') !== false);
265 $this->assertTrue(strpos($acontents[1], ' ') === false);
266 $this->assertTrue(substr_count($acontents[1] , '] ') >= 2);
267 unlink($file); // delete file
270 check_dir_exists($CFG->tempdir . '/test');
271 $file = $CFG->tempdir . '/test/test_file_logger.html';
272 $options = array('depth' => 1);
273 $lo = new file_logger(backup::LOG_ERROR, true, true, $file);
274 $this->assertTrue($lo instanceof file_logger);
275 $this->assertTrue(file_exists($file));
276 $message = 'testing file_logger';
277 $result = $lo->process($message, backup::LOG_ERROR, $options);
278 // Get file contents and inspect them
279 $fcontents = file_get_contents($file);
280 $this->assertTrue($result);
281 $this->assertTrue(strpos($fcontents, $message) !== false);
282 $this->assertTrue(strpos($fcontents, '[error]') !== false);
283 $this->assertTrue(strpos($fcontents, ' ') !== false);
284 $this->assertTrue(substr_count($fcontents , '] ') >= 2);
285 $lo->__destruct(); // closes file handle
286 unlink($file); // delete file
288 // Instantiate, write something, force deletion, try to write again
289 check_dir_exists($CFG->tempdir . '/test');
290 $file = $CFG->tempdir . '/test/test_file_logger.html';
291 $lo = new mock_file_logger(backup::LOG_ERROR, true, true, $file);
292 $this->assertTrue(file_exists($file));
293 $message = 'testing file_logger';
294 $result = $lo->process($message, backup::LOG_ERROR);
295 fclose($lo->get_fhandle()); // close file
297 $result = @$lo->process($message, backup::LOG_ERROR); // Try to write again
298 $this->assertTrue(false, 'base_logger_exception expected');
299 } catch (exception $e) {
300 $this->assertTrue($e instanceof base_logger_exception);
301 $this->assertEquals($e->errorcode, 'error_writing_file');
304 // Instantiate without file
306 $lo = new file_logger(backup::LOG_WARNING, true, true, '');
307 $this->assertTrue(false, 'base_logger_exception expected');
308 } catch (exception $e) {
309 $this->assertTrue($e instanceof base_logger_exception);
310 $this->assertEquals($e->errorcode, 'missing_fullpath_parameter');
313 // Instantiate in (near) impossible path
314 $file = $CFG->tempdir . '/test_azby/test_file_logger.txt';
316 $lo = new file_logger(backup::LOG_WARNING, true, true, $file);
317 $this->assertTrue(false, 'base_logger_exception expected');
318 } catch (exception $e) {
319 $this->assertTrue($e instanceof base_logger_exception);
320 $this->assertEquals($e->errorcode, 'file_not_writable');
321 $this->assertEquals($e->a, $file);
324 // Instantiate one file logger with level = backup::LOG_NONE
325 $file = $CFG->tempdir . '/test/test_file_logger.txt';
326 $lo = new file_logger(backup::LOG_NONE, true, true, $file);
327 $this->assertTrue($lo instanceof file_logger);
328 $this->assertFalse(file_exists($file));
330 // Remove the test dir and any content
331 @remove_dir(dirname($file));
337 * helper extended base_logger class that implements some methods for testing
338 * Simply return the product of message and level
340 class mock_base_logger1 extends base_logger {
342 protected function action($message, $level, $options = null) {
343 return $message * $level; // Simply return that, for testing
345 public function get_levelstr($level) {
346 return parent::get_levelstr($level);
351 * helper extended base_logger class that implements some methods for testing
352 * Simply return the sum of message and level
354 class mock_base_logger2 extends base_logger {
356 protected function action($message, $level, $options = null) {
357 return $message + $level; // Simply return that, for testing
362 * helper extended base_logger class that implements some methods for testing
365 class mock_base_logger3 extends base_logger {
367 protected function action($message, $level, $options = null) {
368 return false; // Simply return false, for testing stopper
373 * helper extended database_logger class that implements some methods for testing
374 * Returns the complete info that normally will be used by insert record calls
376 class mock_database_logger extends database_logger {
378 protected function insert_log_record($table, $columns) {
379 return array('table' => $table, 'columns' => $columns);
384 * helper extended file_logger class that implements some methods for testing
385 * Returns the, usually protected, handle
387 class mock_file_logger extends file_logger {
389 function get_fhandle() {
390 return $this->fhandle;