mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 17:02:03 +02:00
MDL-32323 rework db reset once more, now with tests
This commit is contained in:
parent
50be93d1d7
commit
8b5413ccf2
@ -167,6 +167,86 @@ class phpunit_util {
|
||||
return self::$tablestructure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all database sequences
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function reset_all_database_sequences() {
|
||||
global $DB;
|
||||
|
||||
// reset all sequences as fast as possible, it is usually faster to find out current value than to update all,
|
||||
// please note we really must verify all tables because sometimes records are added and later removed,
|
||||
// but in the next run we really want to start again from MAX(id)+1
|
||||
|
||||
if (!$data = self::get_tabledata()) {
|
||||
// not initialised yet
|
||||
return false;
|
||||
}
|
||||
if (!$structure = self::get_tablestructure()) {
|
||||
// not initialised yet
|
||||
return false;
|
||||
}
|
||||
|
||||
$dbfamily = $DB->get_dbfamily();
|
||||
if ($dbfamily === 'postgres') {
|
||||
$queries = array();
|
||||
$prefix = $DB->get_prefix();
|
||||
foreach ($data as $table=>$records) {
|
||||
if (isset($structure[$table]['id']) and $structure[$table]['id']->primary_key) {
|
||||
if (empty($records)) {
|
||||
$nextid = 1;
|
||||
} else {
|
||||
$lastrecord = end($records);
|
||||
$nextid = $lastrecord->id + 1;
|
||||
}
|
||||
$queries[] = "ALTER SEQUENCE {$prefix}{$table}_id_seq RESTART WITH $nextid";
|
||||
}
|
||||
}
|
||||
if ($queries) {
|
||||
$DB->change_database_structure(implode(';', $queries));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$sequences = array();
|
||||
if ($dbfamily === 'mysql') {
|
||||
$prefix = $DB->get_prefix();
|
||||
$rs = $DB->get_recordset_sql("SHOW TABLE STATUS LIKE ?", array($prefix.'%'));
|
||||
foreach ($rs as $info) {
|
||||
$table = strtolower($info->name);
|
||||
if (strpos($table, $prefix) !== 0) {
|
||||
// incorrect table match caused by _
|
||||
continue;
|
||||
}
|
||||
if (!is_null($info->auto_increment)) {
|
||||
$table = preg_replace('/^'.preg_quote($prefix).'/', '', $table);
|
||||
$sequences[$table] = $info->auto_increment;
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
}
|
||||
|
||||
foreach ($data as $table=>$records) {
|
||||
if (isset($structure[$table]['id']) and $structure[$table]['id']->primary_key) {
|
||||
if (isset($sequences[$table])) {
|
||||
if (empty($records)) {
|
||||
$lastid = 0;
|
||||
} else {
|
||||
$lastrecord = end($records);
|
||||
$lastid = $lastrecord->id;
|
||||
}
|
||||
if ($sequences[$table] != $lastid +1) {
|
||||
$DB->get_manager()->reset_sequence($table);
|
||||
}
|
||||
|
||||
} else {
|
||||
$DB->get_manager()->reset_sequence($table);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all database tables to default values.
|
||||
* @static
|
||||
@ -230,53 +310,8 @@ class phpunit_util {
|
||||
}
|
||||
}
|
||||
|
||||
// reset all sequences as fast as possible, it is usually faster to find out current value than to update all,
|
||||
// please note we really must verify all tables because sometimes records are added and later removed,
|
||||
// but in the next run we really want to start again from MAX(id)+1
|
||||
$dbfamily = $DB->get_dbfamily();
|
||||
$sequences = array();
|
||||
if ($dbfamily === 'mysql') {
|
||||
$prefix = $DB->get_prefix();
|
||||
$rs = $DB->get_recordset_sql("SHOW TABLE STATUS LIKE ?", array($prefix.'%'));
|
||||
foreach ($rs as $info) {
|
||||
$table = strtolower($info->name);
|
||||
if (strpos($table, $prefix) !== 0) {
|
||||
// incorrect table match caused by _
|
||||
continue;
|
||||
}
|
||||
if (!is_null($info->auto_increment)) {
|
||||
$table = preg_replace('/^'.preg_quote($prefix).'/', '', $table);
|
||||
$sequences[$table] = $info->auto_increment;
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
} else if ($dbfamily === 'postgres') {
|
||||
foreach ($data as $table=>$records) {
|
||||
if (isset($structure[$table]['id']) and $structure[$table]['id']->primary_key) {
|
||||
$sequences[$table] = $DB->get_field_sql("SELECT last_value FROM {{$table}}_id_seq;");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $table=>$records) {
|
||||
if (isset($structure[$table]['id']) and $structure[$table]['id']->primary_key) {
|
||||
if (isset($sequences[$table])) {
|
||||
if (empty($records)) {
|
||||
$lastid = 0;
|
||||
} else {
|
||||
$lastrecord = end($records);
|
||||
$lastid = $lastrecord->id;
|
||||
}
|
||||
if ($sequences[$table] != $lastid +1) {
|
||||
$DB->get_manager()->reset_sequence($table);
|
||||
}
|
||||
|
||||
} else {
|
||||
$DB->get_manager()->reset_sequence($table);
|
||||
}
|
||||
}
|
||||
}
|
||||
// reset all next record ids - aka sequences
|
||||
self::reset_all_database_sequences();
|
||||
|
||||
// remove extra tables
|
||||
foreach ($tables as $table) {
|
||||
@ -394,18 +429,18 @@ class phpunit_util {
|
||||
// restore original config once more in case resetting of caches changes CFG
|
||||
$CFG = self::get_global_backup('CFG');
|
||||
|
||||
// verify db writes just in case something goes wrong in reset
|
||||
if (self::$lastdbwrites != $DB->perf_get_writes()) {
|
||||
error_log('Unexpected DB writes in reset_all_data.');
|
||||
self::$lastdbwrites = $DB->perf_get_writes();
|
||||
}
|
||||
|
||||
// inform data generator
|
||||
self::get_data_generator()->reset();
|
||||
|
||||
// fix PHP settings
|
||||
error_reporting($CFG->debug);
|
||||
|
||||
// verify db writes just in case something goes wrong in reset
|
||||
if (self::$lastdbwrites != $DB->perf_get_writes()) {
|
||||
error_log('Unexpected DB writes in reset_all_data.');
|
||||
self::$lastdbwrites = $DB->perf_get_writes();
|
||||
}
|
||||
|
||||
if ($warnings) {
|
||||
$warnings = implode("\n", $warnings);
|
||||
trigger_error($warnings, E_USER_WARNING);
|
||||
@ -903,10 +938,10 @@ class basic_testcase extends PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
class advanced_testcase extends PHPUnit_Framework_TestCase {
|
||||
/** @var bool automatically reset everything? null means log changes */
|
||||
protected $resetAfterTest = null;
|
||||
protected $resetAfterTest;
|
||||
|
||||
/** @var moodle_transaction */
|
||||
protected $testdbtransaction = null;
|
||||
protected $testdbtransaction;
|
||||
|
||||
/**
|
||||
* Constructs a test case with the given name.
|
||||
@ -932,7 +967,11 @@ class advanced_testcase extends PHPUnit_Framework_TestCase {
|
||||
public function runBare() {
|
||||
global $DB;
|
||||
|
||||
if ($DB->get_dbfamily() === 'postgres') {
|
||||
if (phpunit_util::$lastdbwrites != $DB->perf_get_writes()) {
|
||||
// this happens when previous test does not reset, we can not use transactions
|
||||
$this->testdbtransaction = null;
|
||||
|
||||
} else if ($DB->get_dbfamily() === 'postgres') {
|
||||
// database must allow rollback of DDL, so no mysql here
|
||||
$this->testdbtransaction = $DB->start_delegated_transaction();
|
||||
}
|
||||
@ -942,28 +981,43 @@ class advanced_testcase extends PHPUnit_Framework_TestCase {
|
||||
if (!$this->testdbtransaction or $this->testdbtransaction->is_disposed()) {
|
||||
$this->testdbtransaction = null;
|
||||
}
|
||||
$trans = $this->testdbtransaction;
|
||||
|
||||
if ($this->resetAfterTest === true) {
|
||||
if ($trans) {
|
||||
if ($this->testdbtransaction) {
|
||||
$DB->force_transaction_rollback();
|
||||
phpunit_util::reset_all_database_sequences();
|
||||
phpunit_util::$lastdbwrites = $DB->perf_get_writes(); // no db reset necessary
|
||||
}
|
||||
phpunit_util::reset_all_data();
|
||||
|
||||
} else if ($this->resetAfterTest === false) {
|
||||
if ($trans) {
|
||||
$trans->allow_commit();
|
||||
if ($this->testdbtransaction) {
|
||||
$this->testdbtransaction->allow_commit();
|
||||
}
|
||||
// keep all data untouched for other tests
|
||||
|
||||
} else {
|
||||
// reset but log what changed
|
||||
if ($trans) {
|
||||
$trans->allow_commit();
|
||||
if ($this->testdbtransaction) {
|
||||
$this->testdbtransaction->allow_commit();
|
||||
}
|
||||
phpunit_util::reset_all_data(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method from test if you want to make sure that
|
||||
* the resetting of database is done the slow way without transaction
|
||||
* rollback.
|
||||
* @return void
|
||||
*/
|
||||
public function preventResetByRollback() {
|
||||
if ($this->testdbtransaction and !$this->testdbtransaction->is_disposed()) {
|
||||
$this->testdbtransaction->allow_commit();
|
||||
$this->testdbtransaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset everything after current test.
|
||||
* @param bool $reset true means reset state back, false means keep all data for the next test,
|
||||
|
@ -192,6 +192,69 @@ class core_phpunit_advanced_testcase extends advanced_testcase {
|
||||
$this->assertSame($_SESSION['USER'], $USER);
|
||||
}
|
||||
|
||||
public function test_database_reset_repeated() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$this->preventResetByRollback();
|
||||
|
||||
$this->assertEquals(1, $DB->count_records('course')); // only frontpage in new site
|
||||
|
||||
// this is weird table - id is NOT a sequence here
|
||||
$this->assertEquals(0, $DB->count_records('context_temp'));
|
||||
$DB->import_record('context_temp', array('id'=>5, 'path'=>'/1/2', 'depth'=>2));
|
||||
$record = $DB->get_record('context_temp', array());
|
||||
$this->assertEquals(5, $record->id);
|
||||
|
||||
$this->assertEquals(0, $DB->count_records('course_display'));
|
||||
$originaldisplayid = $DB->insert_record('course_display', array('userid'=>2, 'course'=>1, 'display'=>1));
|
||||
$this->assertEquals(1, $originaldisplayid);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$this->assertEquals(2, $course->id);
|
||||
|
||||
$this->assertEquals(2, $DB->count_records('user'));
|
||||
$DB->delete_records('user', array('id'=>1));
|
||||
$this->assertEquals(1, $DB->count_records('user'));
|
||||
|
||||
//=========
|
||||
|
||||
$this->resetAllData();
|
||||
|
||||
$this->assertEquals(1, $DB->count_records('course')); // only frontpage in new site
|
||||
$this->assertEquals(0, $DB->count_records('context_temp')); // only frontpage in new site
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$this->assertEquals(2, $course->id);
|
||||
|
||||
$displayid = $DB->insert_record('course_display', array('userid'=>2, 'course'=>1, 'display'=>1));
|
||||
$this->assertEquals($originaldisplayid, $displayid);
|
||||
|
||||
$this->assertEquals(2, $DB->count_records('user'));
|
||||
$DB->delete_records('user', array('id'=>2));
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->assertEquals(3, $user->id);
|
||||
|
||||
// =========
|
||||
|
||||
$this->resetAllData();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$this->assertEquals(2, $course->id);
|
||||
|
||||
$this->assertEquals(2, $DB->count_records('user'));
|
||||
$DB->delete_records('user', array('id'=>2));
|
||||
|
||||
//==========
|
||||
|
||||
$this->resetAllData();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$this->assertEquals(2, $course->id);
|
||||
|
||||
$this->assertEquals(2, $DB->count_records('user'));
|
||||
}
|
||||
|
||||
public function test_getDataGenerator() {
|
||||
$generator = $this->getDataGenerator();
|
||||
$this->assertInstanceOf('phpunit_data_generator', $generator);
|
||||
|
Loading…
x
Reference in New Issue
Block a user