67f8e7472c66b6eb83c9176522542bfb3d089044
[moodle.git] / backup / util / loggers / tests / logger_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
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.
8 //
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.
13 //
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/>.
17 /**
18  * @package    core_backup
19  * @category   phpunit
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
22  */
24 defined('MOODLE_INTERNAL') || die();
26 // Include all the needed stuff
27 global $CFG;
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');
38 /**
39  * logger tests (all)
40  */
41 class backup_logger_testcase extends basic_testcase {
43     /**
44      * test base_logger class
45      */
46     function test_base_logger() {
47         // Test logger with simple action (message * level)
48         $lo = new mock_base_logger1(backup::LOG_ERROR);
49         $msg = 13;
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);
53         $msg = 13;
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);
59         $lo1->set_next($lo2);
60         $msg = 13;
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);
65         try {
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));
74         }
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);
79         $lo1->set_next($lo2);
80         $lo2->set_next($lo3);
81         try {
82             $lo3->set_next($lo1);
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));
90         }
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);
96         $lo1->set_next($lo2);
97         $lo2->set_next($lo3);
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');
111     }
113     /**
114      * test error_log_logger class
115      */
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);
123     }
125     /**
126      * test output_text_logger class
127      */
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);
153     }
155     /**
156      * test output_indented_logger class
157      */
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')) {
170             $check = '  ';
171         } else {
172             $check = '&nbsp;&nbsp;';
173         }
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')) {
191             $check = '  ';
192         } else {
193             $check = '&nbsp;&nbsp;';
194         }
195         $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
196     }
198     /**
199      * test database_logger class
200      */
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
203         $now = time();
204         $datecol = 'time';
205         $levelcol = 'action';
206         $messagecol = 'info';
207         $logtable = 'log';
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']);
220     }
222     /**
223      * test file_logger class
224      */
225     function test_file_logger() {
226         global $CFG;
228         $file = $CFG->tempdir . '/test/test_file_logger.txt';
229         // Remove the test dir and any content
230         @remove_dir(dirname($file));
231         // Recreate test dir
232         if (!check_dir_exists(dirname($file), true, true)) {
233             throw new moodle_exception('error_creating_temp_dir', 'error', dirname($file));
234         }
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
245         $options = array();
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);
252         // Destruct loggers
253         $lo1 = null;
254         $lo2 = null;
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
269         // Try one html 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, '&nbsp;&nbsp;') !== 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
296         try {
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');
302         }
304         // Instantiate without file
305         try {
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');
311         }
313         // Instantiate in (near) impossible path
314         $file =  $CFG->tempdir . '/test_azby/test_file_logger.txt';
315         try {
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);
322         }
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));
332     }
336 /**
337  * helper extended base_logger class that implements some methods for testing
338  * Simply return the product of message and level
339  */
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
344     }
345     public function get_levelstr($level) {
346         return parent::get_levelstr($level);
347     }
350 /**
351  * helper extended base_logger class that implements some methods for testing
352  * Simply return the sum of message and level
353  */
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
358     }
361 /**
362  * helper extended base_logger class that implements some methods for testing
363  * Simply return 8
364  */
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
369     }
372 /**
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
375  */
376 class mock_database_logger extends database_logger {
378     protected function insert_log_record($table, $columns) {
379         return array('table' => $table, 'columns' => $columns);
380     }
383 /**
384  * helper extended file_logger class that implements some methods for testing
385  * Returns the, usually protected, handle
386  */
387 class mock_file_logger extends file_logger {
389     function get_fhandle() {
390         return $this->fhandle;
391     }