Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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/>.
  16  
  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   */
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  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');
  36  
  37  
  38  /**
  39   * logger tests (all)
  40   */
  41  class backup_logger_testcase extends basic_testcase {
  42  
  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));
  55  
  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);
  62  
  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          }
  75  
  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          }
  91  
  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          $msg = 13;
  99          $this->assertFalse($lo1->process($msg, backup::LOG_ERROR));
 100  
 101          // Test checksum correct
 102          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 103          $lo1->is_checksum_correct(get_class($lo1) . '-' . backup::LOG_ERROR);
 104  
 105          // Test get_levelstr()
 106          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 107          $this->assertEquals($lo1->get_levelstr(backup::LOG_NONE), 'undefined');
 108          $this->assertEquals($lo1->get_levelstr(backup::LOG_ERROR), 'error');
 109          $this->assertEquals($lo1->get_levelstr(backup::LOG_WARNING), 'warn');
 110          $this->assertEquals($lo1->get_levelstr(backup::LOG_INFO), 'info');
 111          $this->assertEquals($lo1->get_levelstr(backup::LOG_DEBUG), 'debug');
 112  
 113          // Test destroy.
 114          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 115          $lo2 = new mock_base_logger2(backup::LOG_ERROR);
 116          $lo1->set_next($lo2);
 117          $this->assertInstanceOf('base_logger', $lo1->get_next());
 118          $this->assertNull($lo2->get_next());
 119          $lo1->destroy();
 120          $this->assertNull($lo1->get_next());
 121          $this->assertNull($lo2->get_next());
 122      }
 123  
 124      /**
 125       * test error_log_logger class
 126       */
 127      function test_error_log_logger() {
 128          // Not much really to test, just instantiate and execute, should return true
 129          $lo = new error_log_logger(backup::LOG_ERROR);
 130          $this->assertTrue($lo instanceof error_log_logger);
 131          $message = 'This log exists because you have run Moodle unit tests: Ignore it';
 132          $result = $lo->process($message, backup::LOG_ERROR);
 133          $this->assertTrue($result);
 134      }
 135  
 136      /**
 137       * test output_text_logger class
 138       */
 139      function test_output_text_logger() {
 140          // Instantiate without date nor level output
 141          $lo = new output_text_logger(backup::LOG_ERROR);
 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, $message) !== false);
 150  
 151          // Instantiate with date and level output
 152          $lo = new output_text_logger(backup::LOG_ERROR, true, true);
 153          $this->assertTrue($lo instanceof output_text_logger);
 154          $message = 'testing output_text_logger';
 155          ob_start(); // Capture output
 156          $result = $lo->process($message, backup::LOG_ERROR);
 157          $contents = ob_get_contents();
 158          ob_end_clean(); // End capture and discard
 159          $this->assertTrue($result);
 160          $this->assertTrue(strpos($contents,'[') === 0);
 161          $this->assertTrue(strpos($contents,'[error]') !== false);
 162          $this->assertTrue(strpos($contents, $message) !== false);
 163          $this->assertTrue(substr_count($contents , '] ') >= 2);
 164      }
 165  
 166      /**
 167       * test output_indented_logger class
 168       */
 169      function test_output_indented_logger() {
 170          // Instantiate without date nor level output
 171          $options = array('depth' => 2);
 172          $lo = new output_indented_logger(backup::LOG_ERROR);
 173          $this->assertTrue($lo instanceof output_indented_logger);
 174          $message = 'testing output_indented_logger';
 175          ob_start(); // Capture output
 176          $result = $lo->process($message, backup::LOG_ERROR, $options);
 177          $contents = ob_get_contents();
 178          ob_end_clean(); // End capture and discard
 179          $this->assertTrue($result);
 180          if (defined('STDOUT')) {
 181              $check = '  ';
 182          } else {
 183              $check = '&nbsp;&nbsp;';
 184          }
 185          $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
 186  
 187          // Instantiate with date and level output
 188          $options = array('depth' => 3);
 189          $lo = new output_indented_logger(backup::LOG_ERROR, true, true);
 190          $this->assertTrue($lo instanceof output_indented_logger);
 191          $message = 'testing output_indented_logger';
 192          ob_start(); // Capture output
 193          $result = $lo->process($message, backup::LOG_ERROR, $options);
 194          $contents = ob_get_contents();
 195          ob_end_clean(); // End capture and discard
 196          $this->assertTrue($result);
 197          $this->assertTrue(strpos($contents,'[') === 0);
 198          $this->assertTrue(strpos($contents,'[error]') !== false);
 199          $this->assertTrue(strpos($contents, $message) !== false);
 200          $this->assertTrue(substr_count($contents , '] ') >= 2);
 201          if (defined('STDOUT')) {
 202              $check = '  ';
 203          } else {
 204              $check = '&nbsp;&nbsp;';
 205          }
 206          $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
 207      }
 208  
 209      /**
 210       * test database_logger class
 211       */
 212      function test_database_logger() {
 213          // Instantiate with date and level output (and with specs from the global moodle "log" table so checks will pass
 214          $now = time();
 215          $datecol = 'time';
 216          $levelcol = 'action';
 217          $messagecol = 'info';
 218          $logtable = 'log';
 219          $columns = array('url' => 'http://127.0.0.1');
 220          $loglevel = backup::LOG_ERROR;
 221          $lo = new mock_database_logger(backup::LOG_ERROR, $datecol, $levelcol, $messagecol, $logtable, $columns);
 222          $this->assertTrue($lo instanceof database_logger);
 223          $message = 'testing database_logger';
 224          $result = $lo->process($message, $loglevel);
 225          // Check everything is ready to be inserted to DB
 226          $this->assertEquals($result['table'], $logtable);
 227          $this->assertTrue($result['columns'][$datecol] >= $now);
 228          $this->assertEquals($result['columns'][$levelcol], $loglevel);
 229          $this->assertEquals($result['columns'][$messagecol], $message);
 230          $this->assertEquals($result['columns']['url'], $columns['url']);
 231      }
 232  
 233      /**
 234       * test file_logger class
 235       */
 236      function test_file_logger() {
 237          global $CFG;
 238  
 239          $file = $CFG->tempdir . '/test/test_file_logger.txt';
 240          // Remove the test dir and any content
 241          @remove_dir(dirname($file));
 242          // Recreate test dir
 243          if (!check_dir_exists(dirname($file), true, true)) {
 244              throw new moodle_exception('error_creating_temp_dir', 'error', dirname($file));
 245          }
 246  
 247          // Instantiate with date and level output, and also use the depth option
 248          $options = array('depth' => 3);
 249          $lo1 = new file_logger(backup::LOG_ERROR, true, true, $file);
 250          $this->assertTrue($lo1 instanceof file_logger);
 251          $message1 = 'testing file_logger';
 252          $result = $lo1->process($message1, backup::LOG_ERROR, $options);
 253          $this->assertTrue($result);
 254  
 255          // Another file_logger is going towrite there too without closing
 256          $options = array();
 257          $lo2 = new file_logger(backup::LOG_WARNING, true, true, $file);
 258          $this->assertTrue($lo2 instanceof file_logger);
 259          $message2 = 'testing file_logger2';
 260          $result = $lo2->process($message2, backup::LOG_WARNING, $options);
 261          $this->assertTrue($result);
 262  
 263          // Destroy loggers.
 264          $lo1->destroy();
 265          $lo2->destroy();
 266  
 267          // Load file results to analyze them
 268          $fcontents = file_get_contents($file);
 269          $acontents = explode(PHP_EOL, $fcontents); // Split by line
 270          $this->assertTrue(strpos($acontents[0], $message1) !== false);
 271          $this->assertTrue(strpos($acontents[0], '[error]') !== false);
 272          $this->assertTrue(strpos($acontents[0], '      ') !== false);
 273          $this->assertTrue(substr_count($acontents[0] , '] ') >= 2);
 274          $this->assertTrue(strpos($acontents[1], $message2) !== false);
 275          $this->assertTrue(strpos($acontents[1], '[warn]') !== false);
 276          $this->assertTrue(strpos($acontents[1], '      ') === false);
 277          $this->assertTrue(substr_count($acontents[1] , '] ') >= 2);
 278          unlink($file); // delete file
 279  
 280          // Try one html file
 281          check_dir_exists($CFG->tempdir . '/test');
 282          $file = $CFG->tempdir . '/test/test_file_logger.html';
 283          $options = array('depth' => 1);
 284          $lo = new file_logger(backup::LOG_ERROR, true, true, $file);
 285          $this->assertTrue($lo instanceof file_logger);
 286          $this->assertTrue(file_exists($file));
 287          $message = 'testing file_logger';
 288          $result = $lo->process($message, backup::LOG_ERROR, $options);
 289          $lo->close(); // Closes logger.
 290          // Get file contents and inspect them
 291          $fcontents = file_get_contents($file);
 292          $this->assertTrue($result);
 293          $this->assertTrue(strpos($fcontents, $message) !== false);
 294          $this->assertTrue(strpos($fcontents, '[error]') !== false);
 295          $this->assertTrue(strpos($fcontents, '&nbsp;&nbsp;') !== false);
 296          $this->assertTrue(substr_count($fcontents , '] ') >= 2);
 297          unlink($file); // delete file
 298  
 299          // Instantiate, write something, force deletion, try to write again
 300          check_dir_exists($CFG->tempdir . '/test');
 301          $file = $CFG->tempdir . '/test/test_file_logger.html';
 302          $lo = new mock_file_logger(backup::LOG_ERROR, true, true, $file);
 303          $this->assertTrue(file_exists($file));
 304          $message = 'testing file_logger';
 305          $result = $lo->process($message, backup::LOG_ERROR);
 306          $lo->close();
 307          $this->assertNull($lo->get_fhandle());
 308          try {
 309              $result = @$lo->process($message, backup::LOG_ERROR); // Try to write again
 310              $this->assertTrue(false, 'base_logger_exception expected');
 311          } catch (exception $e) {
 312              $this->assertTrue($e instanceof base_logger_exception);
 313              $this->assertEquals($e->errorcode, 'error_writing_file');
 314          }
 315  
 316          // Instantiate without file
 317          try {
 318              $lo = new file_logger(backup::LOG_WARNING, true, true, '');
 319              $this->assertTrue(false, 'base_logger_exception expected');
 320          } catch (exception $e) {
 321              $this->assertTrue($e instanceof base_logger_exception);
 322              $this->assertEquals($e->errorcode, 'missing_fullpath_parameter');
 323          }
 324  
 325          // Instantiate in (near) impossible path
 326          $file =  $CFG->tempdir . '/test_azby/test_file_logger.txt';
 327          try {
 328              $lo = new file_logger(backup::LOG_WARNING, true, true, $file);
 329              $this->assertTrue(false, 'base_logger_exception expected');
 330          } catch (exception $e) {
 331              $this->assertTrue($e instanceof base_logger_exception);
 332              $this->assertEquals($e->errorcode, 'file_not_writable');
 333              $this->assertEquals($e->a, $file);
 334          }
 335  
 336          // Instantiate one file logger with level = backup::LOG_NONE
 337          $file =  $CFG->tempdir . '/test/test_file_logger.txt';
 338          $lo = new file_logger(backup::LOG_NONE, true, true, $file);
 339          $this->assertTrue($lo instanceof file_logger);
 340          $this->assertFalse(file_exists($file));
 341          $lo->close();
 342  
 343          // Remove the test dir and any content
 344          @remove_dir(dirname($file));
 345      }
 346  }
 347  
 348  
 349  /**
 350   * helper extended base_logger class that implements some methods for testing
 351   * Simply return the product of message and level
 352   */
 353  class mock_base_logger1 extends base_logger {
 354  
 355      protected function action($message, $level, $options = null) {
 356          return $message * $level; // Simply return that, for testing
 357      }
 358      public function get_levelstr($level) {
 359          return parent::get_levelstr($level);
 360      }
 361  }
 362  
 363  /**
 364   * helper extended base_logger class that implements some methods for testing
 365   * Simply return the sum of message and level
 366   */
 367  class mock_base_logger2 extends base_logger {
 368  
 369      protected function action($message, $level, $options = null) {
 370          return $message + $level; // Simply return that, for testing
 371      }
 372  }
 373  
 374  /**
 375   * helper extended base_logger class that implements some methods for testing
 376   * Simply return 8
 377   */
 378  class mock_base_logger3 extends base_logger {
 379  
 380      protected function action($message, $level, $options = null) {
 381          return false; // Simply return false, for testing stopper
 382      }
 383  }
 384  
 385  /**
 386   * helper extended database_logger class that implements some methods for testing
 387   * Returns the complete info that normally will be used by insert record calls
 388   */
 389  class mock_database_logger extends database_logger {
 390  
 391      protected function insert_log_record($table, $columns) {
 392          return array('table' => $table, 'columns' => $columns);
 393      }
 394  }
 395  
 396  /**
 397   * helper extended file_logger class that implements some methods for testing
 398   * Returns the, usually protected, handle
 399   */
 400  class mock_file_logger extends file_logger {
 401  
 402      function get_fhandle() {
 403          return $this->fhandle;
 404      }
 405  }