Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 39 and 401]

   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   * Database driver test case.
  19   *
  20   * @package    core
  21   * @category   phpunit
  22   * @copyright  2012 Petr Skoda {@link http://skodak.org}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  
  27  /**
  28   * Special test case for testing of DML drivers and DDL layer.
  29   *
  30   * Note: Use only 'test_table*' names when creating new tables.
  31   *
  32   * For DML/DDL developers: you can add following settings to config.php if you want to test different driver than the main one,
  33   *                         the reason is to allow testing of incomplete drivers that do not allow full PHPUnit environment
  34   *                         initialisation (the database can be empty).
  35   * $CFG->phpunit_extra_drivers = array(
  36   *      1=>array('dbtype'=>'mysqli', 'dbhost'=>'localhost', 'dbname'=>'moodle', 'dbuser'=>'root', 'dbpass'=>'', 'prefix'=>'phpu2_'),
  37   *      2=>array('dbtype'=>'pgsql', 'dbhost'=>'localhost', 'dbname'=>'moodle', 'dbuser'=>'postgres', 'dbpass'=>'', 'prefix'=>'phpu2_'),
  38   *      3=>array('dbtype'=>'sqlsrv', 'dbhost'=>'127.0.0.1', 'dbname'=>'moodle', 'dbuser'=>'sa', 'dbpass'=>'', 'prefix'=>'phpu2_'),
  39   *      4=>array('dbtype'=>'oci', 'dbhost'=>'127.0.0.1', 'dbname'=>'XE', 'dbuser'=>'sa', 'dbpass'=>'', 'prefix'=>'t_'),
  40   * );
  41   * define('PHPUNIT_TEST_DRIVER')=1; //number is index in the previous array
  42   *
  43   * @package    core
  44   * @category   phpunit
  45   * @copyright  2012 Petr Skoda {@link http://skodak.org}
  46   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  47   */
  48  abstract class database_driver_testcase extends base_testcase {
  49      /** @var moodle_database connection to extra database */
  50      private static $extradb = null;
  51  
  52      /** @var moodle_database used in these tests*/
  53      protected $tdb;
  54  
  55      /**
  56       * Constructs a test case with the given name.
  57       *
  58       * @param string $name
  59       * @param array  $data
  60       * @param string $dataName
  61       */
  62      final public function __construct($name = null, array $data = array(), $dataName = '') {
  63          parent::__construct($name, $data, $dataName);
  64  
  65          $this->setBackupGlobals(false);
  66          $this->setBackupStaticAttributes(false);
  67          $this->setRunTestInSeparateProcess(false);
  68      }
  69  
  70      public static function setUpBeforeClass(): void {
  71          global $CFG;
  72          parent::setUpBeforeClass();
  73  
  74          if (!defined('PHPUNIT_TEST_DRIVER')) {
  75              // use normal $DB
  76              return;
  77          }
  78  
  79          if (!isset($CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER])) {
  80              throw new exception('Can not find driver configuration options with index: '.PHPUNIT_TEST_DRIVER);
  81          }
  82  
  83          $dblibrary = empty($CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dblibrary']) ? 'native' : $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dblibrary'];
  84          $dbtype = $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dbtype'];
  85          $dbhost = $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dbhost'];
  86          $dbname = $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dbname'];
  87          $dbuser = $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dbuser'];
  88          $dbpass = $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dbpass'];
  89          $prefix = $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['prefix'];
  90          $dboptions = empty($CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dboptions']) ? array() : $CFG->phpunit_extra_drivers[PHPUNIT_TEST_DRIVER]['dboptions'];
  91  
  92          $classname = "{$dbtype}_{$dblibrary}_moodle_database";
  93          require_once("$CFG->libdir/dml/$classname.php");
  94          $d = new $classname();
  95          if (!$d->driver_installed()) {
  96              throw new exception('Database driver for '.$classname.' is not installed');
  97          }
  98  
  99          $d->connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions);
 100  
 101          self::$extradb = $d;
 102      }
 103  
 104      protected function setUp(): void {
 105          global $DB;
 106          parent::setUp();
 107  
 108          if (self::$extradb) {
 109              $this->tdb = self::$extradb;
 110          } else {
 111              $this->tdb = $DB;
 112          }
 113      }
 114  
 115      protected function tearDown(): void {
 116          // delete all test tables
 117          $dbman = $this->tdb->get_manager();
 118          $tables = $this->tdb->get_tables(false);
 119          foreach($tables as $tablename) {
 120              if (strpos($tablename, 'test_table') === 0) {
 121                  $table = new xmldb_table($tablename);
 122                  $dbman->drop_table($table);
 123              }
 124          }
 125          parent::tearDown();
 126      }
 127  
 128      public static function tearDownAfterClass(): void {
 129          if (self::$extradb) {
 130              self::$extradb->dispose();
 131              self::$extradb = null;
 132          }
 133          phpunit_util::reset_all_data(null);
 134          parent::tearDownAfterClass();
 135      }
 136  
 137      /**
 138       * Runs the bare test sequence.
 139       * @return void
 140       */
 141      public function runBare(): void {
 142          try {
 143              parent::runBare();
 144  
 145          } catch (Exception $ex) {
 146              $e = $ex;
 147          } catch (Throwable $ex) {
 148              // Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5).
 149              $e = $ex;
 150          }
 151  
 152          if (isset($e)) {
 153              if ($this->tdb->is_transaction_started()) {
 154                  $this->tdb->force_transaction_rollback();
 155              }
 156              $this->tearDown();
 157              throw $e;
 158          }
 159      }
 160  
 161      /**
 162       * Return debugging messages from the current test.
 163       * @return array with instances having 'message', 'level' and 'stacktrace' property.
 164       */
 165      public function getDebuggingMessages() {
 166          return phpunit_util::get_debugging_messages();
 167      }
 168  
 169      /**
 170       * Clear all previous debugging messages in current test.
 171       */
 172      public function resetDebugging() {
 173          phpunit_util::reset_debugging();
 174      }
 175  
 176      /**
 177       * Assert that exactly debugging was just called once.
 178       *
 179       * Discards the debugging message if successful.
 180       *
 181       * @param null|string $debugmessage null means any
 182       * @param null|string $debuglevel null means any
 183       * @param string $message
 184       */
 185      public function assertDebuggingCalled($debugmessage = null, $debuglevel = null, $message = '') {
 186          $debugging = $this->getDebuggingMessages();
 187          $count = count($debugging);
 188  
 189          if ($count == 0) {
 190              if ($message === '') {
 191                  $message = 'Expectation failed, debugging() not triggered.';
 192              }
 193              $this->fail($message);
 194          }
 195          if ($count > 1) {
 196              if ($message === '') {
 197                  $message = 'Expectation failed, debugging() triggered '.$count.' times.';
 198              }
 199              $this->fail($message);
 200          }
 201          $this->assertEquals(1, $count);
 202  
 203          $debug = reset($debugging);
 204          if ($debugmessage !== null) {
 205              $this->assertSame($debugmessage, $debug->message, $message);
 206          }
 207          if ($debuglevel !== null) {
 208              $this->assertSame($debuglevel, $debug->level, $message);
 209          }
 210  
 211          $this->resetDebugging();
 212      }
 213  
 214      /**
 215       * Call when no debugging() messages expected.
 216       * @param string $message
 217       */
 218      public function assertDebuggingNotCalled($message = '') {
 219          $debugging = $this->getDebuggingMessages();
 220          $count = count($debugging);
 221  
 222          if ($message === '') {
 223              $message = 'Expectation failed, debugging() was triggered.';
 224          }
 225          $this->assertEquals(0, $count, $message);
 226      }
 227  }