]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-16486 Implemented the proxy DB class. Sort of working, but still some issues...
authornicolasconnault <nicolasconnault>
Fri, 19 Sep 2008 14:28:22 +0000 (14:28 +0000)
committernicolasconnault <nicolasconnault>
Fri, 19 Sep 2008 14:28:22 +0000 (14:28 +0000)
lang/en_utf8/simpletest.php
lib/simpletestlib.php
mod/chat/simpletest/test_chat_portfolio_callers.php
mod/resource/lib.php
mod/resource/simpletest/test_resource_portfolio_callers.php

index 27a15097d14905cd008b582281c76986f58495ab..5eab9a4cde550b12ee6728036e687e5267f9128e 100644 (file)
@@ -4,6 +4,8 @@
 
 $string['all'] = 'ALL';
 $string['addconfigprefix'] = 'Add prefix to config file';
+$string['deletingnoninsertedrecord'] = 'Trying to delete a record that was not inserted by these unit tests (id $a->id in table $a->table).';
+$string['deletingnoninsertedrecords'] = 'Trying to delete records that were not inserted by these unit tests (from table $a->table).';
 $string['exception'] = 'Exception';
 $string['fail'] = 'Fail';
 $string['ignorefile'] = 'Ignore tests in the file';
@@ -29,7 +31,9 @@ $string['showsearch'] = 'Show the search for test files.';
 $string['stacktrace'] = 'Stack trace:';
 $string['summary'] = '{$a->run}/{$a->total} test cases complete: <strong>{$a->passes}</strong> passes, <strong>{$a->fails}</strong> fails and <strong>{$a->exceptions}</strong> exceptions.';
 $string['tablesnotsetup'] = 'Unit test tables are not yet built. Do you want to build them now?.';
+$string['testtablescsvfileunwritable'] = 'The test tables CSV file is not writable ($a->filename)';
 $string['thorough'] = 'Run a thorough test (may be slow).';
+$string['updatingnoninsertedrecord'] = 'Trying to update a record that was not inserted by these unit tests (id $a->id in table $a->table).';
 $string['uncaughtexception'] = 'Uncaught exception [{$a->getMessage()}] in [{$a->getFile()}:{$a->getLine()}] TESTS ABORTED.';
 $string['unittests'] = 'Unit tests';
 $string['version'] = 'Using <a href=\"http://sourceforge.net/projects/simpletest/\">SimpleTest</a> version $a.';
index b83bdd38f35b4fe99483ccfd35973e457107c095..1db52cff4900fa843ca7fa8930dad1eead7f1cf7 100644 (file)
@@ -150,7 +150,6 @@ class CheckSpecifiedFieldsExpectation extends SimpleExpectation {
 }
 
 class MoodleUnitTestCase extends UnitTestCase {
-    public $real_db;
     public $tables = array();
     public $pkfile;
     public $cfg;
@@ -189,7 +188,9 @@ class MoodleUnitTestCase extends UnitTestCase {
                 }
             }
             if (!file_put_contents($this->pkfile, $tabledata)) {
-                throw new moodle_exception('testtablescsvfileunwritable', 'error');
+                $a = new stdClass();
+                $a->filename = $this->pkfile;
+                throw new moodle_exception('testtablescsvfileunwritable', 'simpletest', '', $a);
             }
         }
     }
@@ -205,7 +206,7 @@ class MoodleUnitTestCase extends UnitTestCase {
 
         foreach ($tables as $table) {
             if ($table != 'sessions2' && isset($tabledata[$table])) {
-                $DB->delete_records_select($table, "id > ?", array($tabledata[$table]));
+                // $DB->delete_records_select($table, "id > ?", array($tabledata[$table]));
             }
         }
     }
@@ -227,7 +228,10 @@ class MoodleUnitTestCase extends UnitTestCase {
             }
             return $tabledata;
         } else {
-            throw new moodle_exception('testtablescsvfilemissing', 'error');
+            $a = new stdClass();
+            $a->filename = $this->pkfile;
+            debug_print_backtrace();
+            throw new moodle_exception('testtablescsvfilemissing', 'simpletest', '', $a);
             return false;
         }
     }
@@ -238,45 +242,222 @@ class MoodleUnitTestCase extends UnitTestCase {
      * TODO Improve detection of incorrectly built DB test tables (e.g. detect version discrepancy and offer to upgrade/rebuild)
      */
     public function setUp() {
-        global $CFG, $DB;
         parent::setUp();
+        UnitTestDB::instantiate();
+    }
+
+    /**
+     * Method called after each test method. Doesn't do anything extraordinary except restore the global $DB to the real one.
+     */
+    public function tearDown() {
+        global $DB;
+        $DB->cleanup();
+        parent::tearDown();
+    }
+
+    /**
+     * This will execute once all the tests have been run. It should delete the text file holding info about database contents prior to the tests
+     * It should also detect if data is missing from the original tables.
+     */
+    public function __destruct() {
+        global $CFG, $DB;
+
+        $CFG = $this->cfg;
+        $this->tearDown();
+        UnitTestDB::restore();
+        fulldelete($this->pkfile);
+    }
+}
+
+/**
+ * This is a Database Engine proxy class: It replaces the global object $DB with itself through a call to the
+ * static instantiate() method, and restores the original global $DB through restore().
+ * Internally, it routes all calls to $DB to a real instance of the database engine (aggregated as a member variable),
+ * except those that are defined in this proxy class. This makes it possible to add extra code to the database engine
+ * without subclassing it.
+ */
+class UnitTestDB {
+    public static $DB;
+    private static $real_db;
 
-        $this->real_db = $DB;
+    public $table_data = array();
+
+    public function __construct() {
+
+    }
+
+    /**
+     * Call this statically to connect to the DB using the unittest prefix, instantiate
+     * the unit test db, store it as a member variable, instantiate $this and use it as the new global $DB.
+     */
+    public static function instantiate() {
+        global $CFG, $DB;
+        UnitTestDB::$real_db = clone($DB);
 
         if (empty($CFG->unittestprefix)) {
             print_error("prefixnotset", 'simpletest');
         }
 
-        $DB = moodle_database::get_driver_instance($CFG->dbtype, $CFG->dblibrary);
-        $DB->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->dbpersist, $CFG->unittestprefix);
-        $manager = $DB->get_manager();
+        UnitTestDB::$DB = moodle_database::get_driver_instance($CFG->dbtype, $CFG->dblibrary);
+        UnitTestDB::$DB->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->dbpersist, $CFG->unittestprefix);
+        $manager = UnitTestDB::$DB->get_manager();
 
         if (!$manager->table_exists('user')) {
             print_error('tablesnotsetup', 'simpletest');
         }
+
+        $DB = new UnitTestDB();
+    }
+
+    public function __call($method, $args) {
+        // Set args to null if they don't exist (up to 10 args should do)
+        if (!method_exists($this, $method)) {
+            return call_user_func_array(array(UnitTestDB::$DB, $method), $args);
+        } else {
+            call_user_func_array(array($this, $method), $args);
+        }
+    }
+
+    public function __get($variable) {
+        return UnitTestDB::$DB->$variable;
+    }
+
+    public function __set($variable, $value) {
+        UnitTestDB::$DB->$variable = $value;
+    }
+
+    public function __isset($variable) {
+        return isset(UnitTestDB::$DB->$variable);
+    }
+
+    public function __unset($variable) {
+        unset(UnitTestDB::$DB->$variable);
     }
 
     /**
-     * Method called after each test method. Doesn't do anything extraordinary except restore the global $DB to the real one.
+     * Overriding insert_record to keep track of the ids inserted during unit tests, so that they can be deleted afterwards
      */
-    public function tearDown() {
+    public function insert_record($table, $dataobject, $returnid=true, $bulk=false) {
         global $DB;
-        parent::tearDown();
+        $id = UnitTestDB::$DB->insert_record($table, $dataobject, $returnid, $bulk);
+        $this->table_data[$table][] = $id;
+        return $id;
+    }
 
-        $DB = $this->real_db;
+    /**
+     * Overriding update_record: If we are updating a record that was NOT inserted by unit tests,
+     * throw an exception and cancel update.
+     * @throws moodle_exception If trying to update a record not inserted by unit tests.
+     */
+    public function update_record($table, $dataobject, $bulk=false) {
+        global $DB;
+        if (empty($this->table_data[$table]) || !in_array($dataobject->id, $this->table_data[$table])) {
+            return UnitTestDB::$DB->update_record($table, $dataobject, $bulk);
+            // $a = new stdClass();
+            // $a->id = $dataobject->id;
+            // $a->table = $table;
+            // debug_print_backtrace();
+            // throw new moodle_exception('updatingnoninsertedrecord', 'simpletest', '', $a);
+        } else {
+            return UnitTestDB::$DB->update_record($table, $dataobject, $bulk);
+        }
     }
 
     /**
-     * This will execute once all the tests have been run. It should delete the text file holding info about database contents prior to the tests
-     * It should also detect if data is missing from the original tables.
+     * Overriding delete_record: If we are deleting a record that was NOT inserted by unit tests,
+     * throw an exception and cancel delete.
+     * @throws moodle_exception If trying to delete a record not inserted by unit tests.
      */
-    public function __destruct() {
-        global $CFG;
-        $CFG = $this->cfg;
-        $this->truncate_test_tables($this->get_table_data($this->pkfile));
-        fulldelete($this->pkfile);
-        $this->tearDown();
+    public function delete_records($table, array $conditions=null) {
+        global $DB;
+        $a = new stdClass();
+        $a->table = $table;
+
+        // Get ids matching conditions
+        if (!$ids_to_delete = $DB->get_field($table, 'id', $conditions)) {
+            return UnitTestDB::$DB->delete_records($table, $conditions);
+        }
+
+        $proceed_with_delete = true;
+
+        if (!is_array($ids_to_delete)) {
+            $ids_to_delete = array($ids_to_delete);
+        }
+
+        foreach ($ids_to_delete as $id) {
+            if (!in_array($id, $this->table_data[$table])) {
+                $proceed_with_delete = false;
+                $a->id = $id;
+                break;
+            }
+        }
+
+        if ($proceed_with_delete) {
+            return UnitTestDB::$DB->delete_records($table, $conditions);
+        } else {
+            debug_print_backtrace();
+            throw new moodle_exception('deletingnoninsertedrecord', 'simpletest', '', $a);
+        }
     }
-}
 
+    /**
+     * Overriding delete_records_select: If we are deleting a record that was NOT inserted by unit tests,
+     * throw an exception and cancel delete.
+     * @throws moodle_exception If trying to delete a record not inserted by unit tests.
+     */
+    public function delete_records_select($table, $select, array $params=null) {
+        global $DB;
+        $a = new stdClass();
+        $a->table = $table;
+
+        // Get ids matching conditions
+        if (!$ids_to_delete = $DB->get_field_select($table, 'id', $select, $params)) {
+            return UnitTestDB::$DB->delete_records_select($table, $select, $params);
+        }
+
+        $proceed_with_delete = true;
+
+        foreach ($ids_to_delete as $id) {
+            if (!in_array($id, $this->table_data[$table])) {
+                $proceed_with_delete = false;
+                $a->id = $id;
+                break;
+            }
+        }
+
+        if ($proceed_with_delete) {
+            return UnitTestDB::$DB->delete_records_select($table, $select, $params);
+        } else {
+            debug_print_backtrace();
+            throw new moodle_exception('deletingnoninsertedrecord', 'simpletest', '', $a);
+        }
+    }
+
+    /**
+     * Removes from the test DB all the records that were inserted during unit tests,
+     */
+    public function cleanup() {
+        global $DB;
+        foreach ($this->table_data as $table => $ids) {
+            foreach ($ids as $id) {
+                $DB->delete_records($table, array('id' => $id));
+            }
+        }
+    }
+
+    /**
+     * Restores the global $DB object.
+     */
+    public static function restore() {
+        global $DB;
+        $DB = UnitTestDB::$real_db;
+    }
+
+    public function get_field($table, $return, array $conditions) {
+        if (!is_array($conditions)) {
+            debug_print_backtrace();
+        }
+        return UnitTestDB::$DB->get_field($table, $return, $conditions);
+    }
+}
 ?>
index 48333c4a475ddb8a24761f4033dbc9021074bce9..14e9565267d9e68112f3890f4fc2420bbc38dc25 100644 (file)
@@ -32,10 +32,6 @@ class testChatPortfolioCallers extends portfoliolib_test {
         $this->caller = parent::setup_caller('chat_portfolio_caller', array('id' => $cm->id), $userid);
     }
 
-    public function tearDown() {
-        parent::tearDown();
-    }
-
     public function test_caller_sha1() {
         $sha1 = $this->caller->get_sha1();
         $this->caller->prepare_package();
index 6eb9dee09d623a55e1bd1d919f32d8f4aab8e070..7e43f659d88b34be1d8ee9be1d274843cd43c32f 100644 (file)
@@ -103,7 +103,7 @@ class resource_base {
         $morenavlinks = array($this->strresources   => 'index.php?id='.$this->course->id,
                                  $this->resource->name => '');
 
-        $PAGE->print_header($this->course->shortname.': %fullname%', $morenavlinks, "", "", 
+        $PAGE->print_header($this->course->shortname.': %fullname%', $morenavlinks, "", "",
                             update_module_button($this->cm->id, $this->course->id, $this->strresource));
 
         echo '<table id="layout-table"><tr>';
@@ -728,6 +728,7 @@ class resource_portfolio_caller extends portfolio_module_caller_base {
         require_once($this->resourcefile);
         $this->resource= new $resourceclass($this->cm->id);
         if (!is_callable(array($this->resource, 'portfolio_prepare_package')) || !is_callable(array($this->resource, 'portfolio_get_sha1'))) {
+            debug_print_backtrace();
             throw new portfolio_exception('portfolionotimplemented', 'resource', null, $this->cm->type);
         }
         $this->supportedformats = array(self::type_to_format($this->cm->type));
index 992ba35740c6d8af6b4463ca67e831a3e6cb10fa..1ad23ca76702952310d15820de4ef9712f5a9cda 100644 (file)
@@ -19,7 +19,7 @@ class testResourcePortfolioCallers extends portfoliolib_test {
 
         $resource_type = new stdClass();
         $resource_type->type = GENERATOR_SEQUENCE;
-        $resource_type->options = array('file', 'text', 'html');
+        $resource_type->options = array('text', 'html');
 
         $settings = array('quiet' => 1, 'pre_cleanup' => 1,
                           'modules_list' => array($this->module_type),