Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 39 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   * Logger tests (all).
  19   * @package    core_backup
  20   * @category   test
  21   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_backup;
  26  
  27  use backup;
  28  use base_logger;
  29  use base_logger_exception;
  30  use database_logger;
  31  use error_log_logger;
  32  use file_logger;
  33  use output_indented_logger;
  34  use output_text_logger;
  35  
  36  defined('MOODLE_INTERNAL') || die();
  37  
  38  // Include all the needed stuff
  39  global $CFG;
  40  require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php');
  41  require_once($CFG->dirroot . '/backup/backup.class.php');
  42  require_once($CFG->dirroot . '/backup/util/loggers/base_logger.class.php');
  43  require_once($CFG->dirroot . '/backup/util/loggers/error_log_logger.class.php');
  44  require_once($CFG->dirroot . '/backup/util/loggers/output_text_logger.class.php');
  45  require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
  46  require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
  47  require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
  48  
  49  
  50  /**
  51   * Logger tests (all).
  52   *
  53   * @package    core_backup
  54   * @category   test
  55   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  56   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  57   */
  58  class logger_test extends \basic_testcase {
  59  
  60      /**
  61       * test base_logger class
  62       */
  63      function test_base_logger() {
  64          // Test logger with simple action (message * level)
  65          $lo = new mock_base_logger1(backup::LOG_ERROR);
  66          $msg = 13;
  67          $this->assertEquals($lo->process($msg, backup::LOG_ERROR), $msg * backup::LOG_ERROR);
  68          // With lowest level must return true
  69          $lo = new mock_base_logger1(backup::LOG_ERROR);
  70          $msg = 13;
  71          $this->assertTrue($lo->process($msg, backup::LOG_DEBUG));
  72  
  73          // Chain 2 loggers, we must get as result the result of the inner one
  74          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
  75          $lo2 = new mock_base_logger2(backup::LOG_ERROR);
  76          $lo1->set_next($lo2);
  77          $msg = 13;
  78          $this->assertEquals($lo1->process($msg, backup::LOG_ERROR), $msg + backup::LOG_ERROR);
  79  
  80          // Try circular reference
  81          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
  82          try {
  83              $lo1->set_next($lo1); //self
  84              $this->assertTrue(false, 'base_logger_exception expected');
  85          } catch (\Exception $e) {
  86              $this->assertTrue($e instanceof base_logger_exception);
  87              $this->assertEquals($e->errorcode, 'logger_circular_reference');
  88              $this->assertTrue($e->a instanceof \stdClass);
  89              $this->assertEquals($e->a->main, get_class($lo1));
  90              $this->assertEquals($e->a->alreadyinchain, get_class($lo1));
  91          }
  92  
  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          try {
  99              $lo3->set_next($lo1);
 100              $this->assertTrue(false, 'base_logger_exception expected');
 101          } catch (\Exception $e) {
 102              $this->assertTrue($e instanceof base_logger_exception);
 103              $this->assertEquals($e->errorcode, 'logger_circular_reference');
 104              $this->assertTrue($e->a instanceof \stdClass);
 105              $this->assertEquals($e->a->main, get_class($lo1));
 106              $this->assertEquals($e->a->alreadyinchain, get_class($lo3));
 107          }
 108  
 109          // Test stopper logger
 110          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 111          $lo2 = new mock_base_logger2(backup::LOG_ERROR);
 112          $lo3 = new mock_base_logger3(backup::LOG_ERROR);
 113          $lo1->set_next($lo2);
 114          $lo2->set_next($lo3);
 115          $msg = 13;
 116          $this->assertFalse($lo1->process($msg, backup::LOG_ERROR));
 117  
 118          // Test checksum correct
 119          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 120          $lo1->is_checksum_correct(get_class($lo1) . '-' . backup::LOG_ERROR);
 121  
 122          // Test get_levelstr()
 123          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 124          $this->assertEquals($lo1->get_levelstr(backup::LOG_NONE), 'undefined');
 125          $this->assertEquals($lo1->get_levelstr(backup::LOG_ERROR), 'error');
 126          $this->assertEquals($lo1->get_levelstr(backup::LOG_WARNING), 'warn');
 127          $this->assertEquals($lo1->get_levelstr(backup::LOG_INFO), 'info');
 128          $this->assertEquals($lo1->get_levelstr(backup::LOG_DEBUG), 'debug');
 129  
 130          // Test destroy.
 131          $lo1 = new mock_base_logger1(backup::LOG_ERROR);
 132          $lo2 = new mock_base_logger2(backup::LOG_ERROR);
 133          $lo1->set_next($lo2);
 134          $this->assertInstanceOf('base_logger', $lo1->get_next());
 135          $this->assertNull($lo2->get_next());
 136          $lo1->destroy();
 137          $this->assertNull($lo1->get_next());
 138          $this->assertNull($lo2->get_next());
 139      }
 140  
 141      /**
 142       * test error_log_logger class
 143       */
 144      function test_error_log_logger() {
 145          // Not much really to test, just instantiate and execute, should return true
 146          $lo = new error_log_logger(backup::LOG_ERROR);
 147          $this->assertTrue($lo instanceof error_log_logger);
 148          $message = 'This log exists because you have run Moodle unit tests: Ignore it';
 149          $result = $lo->process($message, backup::LOG_ERROR);
 150          $this->assertTrue($result);
 151      }
 152  
 153      /**
 154       * test output_text_logger class
 155       */
 156      function test_output_text_logger() {
 157          // Instantiate without date nor level output
 158          $lo = new output_text_logger(backup::LOG_ERROR);
 159          $this->assertTrue($lo instanceof output_text_logger);
 160          $message = 'testing output_text_logger';
 161          ob_start(); // Capture output
 162          $result = $lo->process($message, backup::LOG_ERROR);
 163          $contents = ob_get_contents();
 164          ob_end_clean(); // End capture and discard
 165          $this->assertTrue($result);
 166          $this->assertTrue(strpos($contents, $message) !== false);
 167  
 168          // Instantiate with date and level output
 169          $lo = new output_text_logger(backup::LOG_ERROR, true, true);
 170          $this->assertTrue($lo instanceof output_text_logger);
 171          $message = 'testing output_text_logger';
 172          ob_start(); // Capture output
 173          $result = $lo->process($message, backup::LOG_ERROR);
 174          $contents = ob_get_contents();
 175          ob_end_clean(); // End capture and discard
 176          $this->assertTrue($result);
 177          $this->assertTrue(strpos($contents,'[') === 0);
 178          $this->assertTrue(strpos($contents,'[error]') !== false);
 179          $this->assertTrue(strpos($contents, $message) !== false);
 180          $this->assertTrue(substr_count($contents , '] ') >= 2);
 181      }
 182  
 183      /**
 184       * test output_indented_logger class
 185       */
 186      function test_output_indented_logger() {
 187          // Instantiate without date nor level output
 188          $options = array('depth' => 2);
 189          $lo = new output_indented_logger(backup::LOG_ERROR);
 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          if (defined('STDOUT')) {
 198              $check = '  ';
 199          } else {
 200              $check = '&nbsp;&nbsp;';
 201          }
 202          $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
 203  
 204          // Instantiate with date and level output
 205          $options = array('depth' => 3);
 206          $lo = new output_indented_logger(backup::LOG_ERROR, true, true);
 207          $this->assertTrue($lo instanceof output_indented_logger);
 208          $message = 'testing output_indented_logger';
 209          ob_start(); // Capture output
 210          $result = $lo->process($message, backup::LOG_ERROR, $options);
 211          $contents = ob_get_contents();
 212          ob_end_clean(); // End capture and discard
 213          $this->assertTrue($result);
 214          $this->assertTrue(strpos($contents,'[') === 0);
 215          $this->assertTrue(strpos($contents,'[error]') !== false);
 216          $this->assertTrue(strpos($contents, $message) !== false);
 217          $this->assertTrue(substr_count($contents , '] ') >= 2);
 218          if (defined('STDOUT')) {
 219              $check = '  ';
 220          } else {
 221              $check = '&nbsp;&nbsp;';
 222          }
 223          $this->assertTrue(strpos($contents, str_repeat($check, $options['depth']) . $message) !== false);
 224      }
 225  
 226      /**
 227       * test database_logger class
 228       */
 229      function test_database_logger() {
 230          // Instantiate with date and level output (and with specs from the global moodle "log" table so checks will pass
 231          $now = time();
 232          $datecol = 'time';
 233          $levelcol = 'action';
 234          $messagecol = 'info';
 235          $logtable = 'log';
 236          $columns = array('url' => 'http://127.0.0.1');
 237          $loglevel = backup::LOG_ERROR;
 238          $lo = new mock_database_logger(backup::LOG_ERROR, $datecol, $levelcol, $messagecol, $logtable, $columns);
 239          $this->assertTrue($lo instanceof database_logger);
 240          $message = 'testing database_logger';
 241          $result = $lo->process($message, $loglevel);
 242          // Check everything is ready to be inserted to DB
 243          $this->assertEquals($result['table'], $logtable);
 244          $this->assertTrue($result['columns'][$datecol] >= $now);
 245          $this->assertEquals($result['columns'][$levelcol], $loglevel);
 246          $this->assertEquals($result['columns'][$messagecol], $message);
 247          $this->assertEquals($result['columns']['url'], $columns['url']);
 248      }
 249  
 250      /**
 251       * test file_logger class
 252       */
 253      function test_file_logger() {
 254          global $CFG;
 255  
 256          $file = $CFG->tempdir . '/test/test_file_logger.txt';
 257          // Remove the test dir and any content
 258          @remove_dir(dirname($file));
 259          // Recreate test dir
 260          if (!check_dir_exists(dirname($file), true, true)) {
 261              throw new \moodle_exception('error_creating_temp_dir', 'error', dirname($file));
 262          }
 263  
 264          // Instantiate with date and level output, and also use the depth option
 265          $options = array('depth' => 3);
 266          $lo1 = new file_logger(backup::LOG_ERROR, true, true, $file);
 267          $this->assertTrue($lo1 instanceof file_logger);
 268          $message1 = 'testing file_logger';
 269          $result = $lo1->process($message1, backup::LOG_ERROR, $options);
 270          $this->assertTrue($result);
 271  
 272          // Another file_logger is going towrite there too without closing
 273          $options = array();
 274          $lo2 = new file_logger(backup::LOG_WARNING, true, true, $file);
 275          $this->assertTrue($lo2 instanceof file_logger);
 276          $message2 = 'testing file_logger2';
 277          $result = $lo2->process($message2, backup::LOG_WARNING, $options);
 278          $this->assertTrue($result);
 279  
 280          // Destroy loggers.
 281          $lo1->destroy();
 282          $lo2->destroy();
 283  
 284          // Load file results to analyze them
 285          $fcontents = file_get_contents($file);
 286          $acontents = explode(PHP_EOL, $fcontents); // Split by line
 287          $this->assertTrue(strpos($acontents[0], $message1) !== false);
 288          $this->assertTrue(strpos($acontents[0], '[error]') !== false);
 289          $this->assertTrue(strpos($acontents[0], '      ') !== false);
 290          $this->assertTrue(substr_count($acontents[0] , '] ') >= 2);
 291          $this->assertTrue(strpos($acontents[1], $message2) !== false);
 292          $this->assertTrue(strpos($acontents[1], '[warn]') !== false);
 293          $this->assertTrue(strpos($acontents[1], '      ') === false);
 294          $this->assertTrue(substr_count($acontents[1] , '] ') >= 2);
 295          unlink($file); // delete file
 296  
 297          // Try one html file
 298          check_dir_exists($CFG->tempdir . '/test');
 299          $file = $CFG->tempdir . '/test/test_file_logger.html';
 300          $options = array('depth' => 1);
 301          $lo = new file_logger(backup::LOG_ERROR, true, true, $file);
 302          $this->assertTrue($lo instanceof file_logger);
 303          $this->assertTrue(file_exists($file));
 304          $message = 'testing file_logger';
 305          $result = $lo->process($message, backup::LOG_ERROR, $options);
 306          $lo->close(); // Closes logger.
 307          // Get file contents and inspect them
 308          $fcontents = file_get_contents($file);
 309          $this->assertTrue($result);
 310          $this->assertTrue(strpos($fcontents, $message) !== false);
 311          $this->assertTrue(strpos($fcontents, '[error]') !== false);
 312          $this->assertTrue(strpos($fcontents, '&nbsp;&nbsp;') !== false);
 313          $this->assertTrue(substr_count($fcontents , '] ') >= 2);
 314          unlink($file); // delete file
 315  
 316          // Instantiate, write something, force deletion, try to write again
 317          check_dir_exists($CFG->tempdir . '/test');
 318          $file = $CFG->tempdir . '/test/test_file_logger.html';
 319          $lo = new mock_file_logger(backup::LOG_ERROR, true, true, $file);
 320          $this->assertTrue(file_exists($file));
 321          $message = 'testing file_logger';
 322          $result = $lo->process($message, backup::LOG_ERROR);
 323          $lo->close();
 324          $this->assertNull($lo->get_fhandle());
 325          try {
 326              $result = @$lo->process($message, backup::LOG_ERROR); // Try to write again
 327              $this->assertTrue(false, 'base_logger_exception expected');
 328          } catch (\Exception $e) {
 329              $this->assertTrue($e instanceof base_logger_exception);
 330              $this->assertEquals($e->errorcode, 'error_writing_file');
 331          }
 332  
 333          // Instantiate without file
 334          try {
 335              $lo = new file_logger(backup::LOG_WARNING, true, true, '');
 336              $this->assertTrue(false, 'base_logger_exception expected');
 337          } catch (\Exception $e) {
 338              $this->assertTrue($e instanceof base_logger_exception);
 339              $this->assertEquals($e->errorcode, 'missing_fullpath_parameter');
 340          }
 341  
 342          // Instantiate in (near) impossible path
 343          $file =  $CFG->tempdir . '/test_azby/test_file_logger.txt';
 344          try {
 345              $lo = new file_logger(backup::LOG_WARNING, true, true, $file);
 346              $this->assertTrue(false, 'base_logger_exception expected');
 347          } catch (\Exception $e) {
 348              $this->assertTrue($e instanceof base_logger_exception);
 349              $this->assertEquals($e->errorcode, 'file_not_writable');
 350              $this->assertEquals($e->a, $file);
 351          }
 352  
 353          // Instantiate one file logger with level = backup::LOG_NONE
 354          $file =  $CFG->tempdir . '/test/test_file_logger.txt';
 355          $lo = new file_logger(backup::LOG_NONE, true, true, $file);
 356          $this->assertTrue($lo instanceof file_logger);
 357          $this->assertFalse(file_exists($file));
 358          $lo->close();
 359  
 360          // Remove the test dir and any content
 361          @remove_dir(dirname($file));
 362      }
 363  }
 364  
 365  
 366  /**
 367   * helper extended base_logger class that implements some methods for testing
 368   * Simply return the product of message and level
 369   */
 370  class mock_base_logger1 extends base_logger {
 371  
 372      protected function action($message, $level, $options = null) {
 373          return $message * $level; // Simply return that, for testing
 374      }
 375      public function get_levelstr($level) {
 376          return parent::get_levelstr($level);
 377      }
 378  }
 379  
 380  /**
 381   * helper extended base_logger class that implements some methods for testing
 382   * Simply return the sum of message and level
 383   */
 384  class mock_base_logger2 extends base_logger {
 385  
 386      protected function action($message, $level, $options = null) {
 387          return $message + $level; // Simply return that, for testing
 388      }
 389  }
 390  
 391  /**
 392   * helper extended base_logger class that implements some methods for testing
 393   * Simply return 8
 394   */
 395  class mock_base_logger3 extends base_logger {
 396  
 397      protected function action($message, $level, $options = null) {
 398          return false; // Simply return false, for testing stopper
 399      }
 400  }
 401  
 402  /**
 403   * helper extended database_logger class that implements some methods for testing
 404   * Returns the complete info that normally will be used by insert record calls
 405   */
 406  class mock_database_logger extends database_logger {
 407  
 408      protected function insert_log_record($table, $columns) {
 409          return array('table' => $table, 'columns' => $columns);
 410      }
 411  }
 412  
 413  /**
 414   * helper extended file_logger class that implements some methods for testing
 415   * Returns the, usually protected, handle
 416   */
 417  class mock_file_logger extends file_logger {
 418  
 419      function get_fhandle() {
 420          return $this->fhandle;
 421      }
 422  }