mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 00:46:50 +02:00
MDL-16486 Implemented the proxy DB class. Sort of working, but still some issues to sort out, like fix_course_sort_order, which updates a record NOT inserted by the unit test.
This commit is contained in:
parent
aa41944f23
commit
5d1381c2c3
5 changed files with 212 additions and 30 deletions
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
$string['all'] = 'ALL';
|
$string['all'] = 'ALL';
|
||||||
$string['addconfigprefix'] = 'Add prefix to config file';
|
$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['exception'] = 'Exception';
|
||||||
$string['fail'] = 'Fail';
|
$string['fail'] = 'Fail';
|
||||||
$string['ignorefile'] = 'Ignore tests in the file';
|
$string['ignorefile'] = 'Ignore tests in the file';
|
||||||
|
@ -29,7 +31,9 @@ $string['showsearch'] = 'Show the search for test files.';
|
||||||
$string['stacktrace'] = 'Stack trace:';
|
$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['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['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['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['uncaughtexception'] = 'Uncaught exception [{$a->getMessage()}] in [{$a->getFile()}:{$a->getLine()}] TESTS ABORTED.';
|
||||||
$string['unittests'] = 'Unit tests';
|
$string['unittests'] = 'Unit tests';
|
||||||
$string['version'] = 'Using <a href=\"http://sourceforge.net/projects/simpletest/\">SimpleTest</a> version $a.';
|
$string['version'] = 'Using <a href=\"http://sourceforge.net/projects/simpletest/\">SimpleTest</a> version $a.';
|
||||||
|
|
|
@ -150,7 +150,6 @@ class CheckSpecifiedFieldsExpectation extends SimpleExpectation {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoodleUnitTestCase extends UnitTestCase {
|
class MoodleUnitTestCase extends UnitTestCase {
|
||||||
public $real_db;
|
|
||||||
public $tables = array();
|
public $tables = array();
|
||||||
public $pkfile;
|
public $pkfile;
|
||||||
public $cfg;
|
public $cfg;
|
||||||
|
@ -189,7 +188,9 @@ class MoodleUnitTestCase extends UnitTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!file_put_contents($this->pkfile, $tabledata)) {
|
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) {
|
foreach ($tables as $table) {
|
||||||
if ($table != 'sessions2' && isset($tabledata[$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;
|
return $tabledata;
|
||||||
} else {
|
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,22 +242,8 @@ class MoodleUnitTestCase extends UnitTestCase {
|
||||||
* TODO Improve detection of incorrectly built DB test tables (e.g. detect version discrepancy and offer to upgrade/rebuild)
|
* TODO Improve detection of incorrectly built DB test tables (e.g. detect version discrepancy and offer to upgrade/rebuild)
|
||||||
*/
|
*/
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
global $CFG, $DB;
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
UnitTestDB::instantiate();
|
||||||
$this->real_db = $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();
|
|
||||||
|
|
||||||
if (!$manager->table_exists('user')) {
|
|
||||||
print_error('tablesnotsetup', 'simpletest');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -261,9 +251,8 @@ class MoodleUnitTestCase extends UnitTestCase {
|
||||||
*/
|
*/
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
$DB->cleanup();
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
|
|
||||||
$DB = $this->real_db;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,12 +260,204 @@ class MoodleUnitTestCase extends UnitTestCase {
|
||||||
* It should also detect if data is missing from the original tables.
|
* It should also detect if data is missing from the original tables.
|
||||||
*/
|
*/
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
global $CFG;
|
global $CFG, $DB;
|
||||||
|
|
||||||
$CFG = $this->cfg;
|
$CFG = $this->cfg;
|
||||||
$this->truncate_test_tables($this->get_table_data($this->pkfile));
|
|
||||||
fulldelete($this->pkfile);
|
|
||||||
$this->tearDown();
|
$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;
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overriding insert_record to keep track of the ids inserted during unit tests, so that they can be deleted afterwards
|
||||||
|
*/
|
||||||
|
public function insert_record($table, $dataobject, $returnid=true, $bulk=false) {
|
||||||
|
global $DB;
|
||||||
|
$id = UnitTestDB::$DB->insert_record($table, $dataobject, $returnid, $bulk);
|
||||||
|
$this->table_data[$table][] = $id;
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -32,10 +32,6 @@ class testChatPortfolioCallers extends portfoliolib_test {
|
||||||
$this->caller = parent::setup_caller('chat_portfolio_caller', array('id' => $cm->id), $userid);
|
$this->caller = parent::setup_caller('chat_portfolio_caller', array('id' => $cm->id), $userid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
parent::tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_caller_sha1() {
|
public function test_caller_sha1() {
|
||||||
$sha1 = $this->caller->get_sha1();
|
$sha1 = $this->caller->get_sha1();
|
||||||
$this->caller->prepare_package();
|
$this->caller->prepare_package();
|
||||||
|
|
|
@ -728,6 +728,7 @@ class resource_portfolio_caller extends portfolio_module_caller_base {
|
||||||
require_once($this->resourcefile);
|
require_once($this->resourcefile);
|
||||||
$this->resource= new $resourceclass($this->cm->id);
|
$this->resource= new $resourceclass($this->cm->id);
|
||||||
if (!is_callable(array($this->resource, 'portfolio_prepare_package')) || !is_callable(array($this->resource, 'portfolio_get_sha1'))) {
|
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);
|
throw new portfolio_exception('portfolionotimplemented', 'resource', null, $this->cm->type);
|
||||||
}
|
}
|
||||||
$this->supportedformats = array(self::type_to_format($this->cm->type));
|
$this->supportedformats = array(self::type_to_format($this->cm->type));
|
||||||
|
|
|
@ -19,7 +19,7 @@ class testResourcePortfolioCallers extends portfoliolib_test {
|
||||||
|
|
||||||
$resource_type = new stdClass();
|
$resource_type = new stdClass();
|
||||||
$resource_type->type = GENERATOR_SEQUENCE;
|
$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,
|
$settings = array('quiet' => 1, 'pre_cleanup' => 1,
|
||||||
'modules_list' => array($this->module_type),
|
'modules_list' => array($this->module_type),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue