mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Maybe fix it?
This commit is contained in:
parent
225e61dc67
commit
8ea3bb36a0
@ -19,6 +19,13 @@ class FixtureTestState implements TestState
|
|||||||
*/
|
*/
|
||||||
private $fixtureFactories = [];
|
private $fixtureFactories = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if fixtures have been loaded
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $loaded = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on setup
|
* Called on setup
|
||||||
*
|
*
|
||||||
@ -29,6 +36,8 @@ class FixtureTestState implements TestState
|
|||||||
if (!$this->testNeedsDB($test)) {
|
if (!$this->testNeedsDB($test)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure DB is built
|
||||||
$tmpDB = $test::tempDB();
|
$tmpDB = $test::tempDB();
|
||||||
if (!$tmpDB->isUsed()) {
|
if (!$tmpDB->isUsed()) {
|
||||||
// Build base db
|
// Build base db
|
||||||
@ -40,10 +49,11 @@ class FixtureTestState implements TestState
|
|||||||
$tmpDB->resetDBSchema($extraObjects);
|
$tmpDB->resetDBSchema($extraObjects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataObject::singleton()->flushCache();
|
DataObject::singleton()->flushCache();
|
||||||
|
|
||||||
// Ensure DB is built and populated
|
// Ensure DB is built and populated
|
||||||
if (!$tmpDB->hasStarted()) {
|
if (!$this->getIsLoaded(get_class($test))) {
|
||||||
foreach ($test->getRequireDefaultRecordsFrom() as $className) {
|
foreach ($test->getRequireDefaultRecordsFrom() as $className) {
|
||||||
$instance = singleton($className);
|
$instance = singleton($className);
|
||||||
if (method_exists($instance, 'requireDefaultRecords')) {
|
if (method_exists($instance, 'requireDefaultRecords')) {
|
||||||
@ -155,6 +165,8 @@ class FixtureTestState implements TestState
|
|||||||
foreach ($paths as $fixtureFile) {
|
foreach ($paths as $fixtureFile) {
|
||||||
$this->loadFixture($fixtureFile, $test);
|
$this->loadFixture($fixtureFile, $test);
|
||||||
}
|
}
|
||||||
|
// Flag as loaded
|
||||||
|
$this->loaded[get_class($test)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -254,5 +266,17 @@ class FixtureTestState implements TestState
|
|||||||
protected function resetFixtureFactory($class)
|
protected function resetFixtureFactory($class)
|
||||||
{
|
{
|
||||||
$this->fixtureFactories[strtolower($class)] = Injector::inst()->create(FixtureFactory::class);
|
$this->fixtureFactories[strtolower($class)] = Injector::inst()->create(FixtureFactory::class);
|
||||||
|
$this->loaded[$class] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if fixtures need to be loaded for this class
|
||||||
|
*
|
||||||
|
* @param string $class Name of test to check
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function getIsLoaded($class)
|
||||||
|
{
|
||||||
|
return !empty($this->loaded[$class]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,6 +668,20 @@ abstract class Database
|
|||||||
*/
|
*/
|
||||||
abstract public function transactionEnd($chain = false);
|
abstract public function transactionEnd($chain = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return depth of current transaction
|
||||||
|
*
|
||||||
|
* @return int Nesting level, or 0 if not in a transaction
|
||||||
|
*/
|
||||||
|
public function transactionDepth()
|
||||||
|
{
|
||||||
|
// Placeholder error for transactional DBs that don't expose depth
|
||||||
|
if ($this->supportsTransactions()) {
|
||||||
|
user_error(get_class($this) . " does not support transactionDepth", E_USER_WARNING);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the used database supports application-level locks,
|
* Determines if the used database supports application-level locks,
|
||||||
* which is different from table- or row-level locking.
|
* which is different from table- or row-level locking.
|
||||||
|
@ -348,6 +348,11 @@ class MySQLDatabase extends Database
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function transactionDepth()
|
||||||
|
{
|
||||||
|
return $this->transactionNesting;
|
||||||
|
}
|
||||||
|
|
||||||
public function transactionEnd($chain = false)
|
public function transactionEnd($chain = false)
|
||||||
{
|
{
|
||||||
// Fail if transaction isn't available
|
// Fail if transaction isn't available
|
||||||
@ -365,7 +370,7 @@ class MySQLDatabase extends Database
|
|||||||
/**
|
/**
|
||||||
* In error condition, set transactionNesting to zero
|
* In error condition, set transactionNesting to zero
|
||||||
*/
|
*/
|
||||||
protected function discardTransactions()
|
protected function resetTransactionNesting()
|
||||||
{
|
{
|
||||||
$this->transactionNesting = 0;
|
$this->transactionNesting = 0;
|
||||||
}
|
}
|
||||||
@ -394,7 +399,7 @@ class MySQLDatabase extends Database
|
|||||||
// on why we need to be over-eager
|
// on why we need to be over-eager
|
||||||
$isDDL = $this->getConnector()->isQueryDDL($sql);
|
$isDDL = $this->getConnector()->isQueryDDL($sql);
|
||||||
if ($isDDL) {
|
if ($isDDL) {
|
||||||
$this->discardTransactions();
|
$this->resetTransactionNesting();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,11 +23,6 @@ class TempDatabase
|
|||||||
*/
|
*/
|
||||||
protected $name = null;
|
protected $name = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool If a transaction has been started
|
|
||||||
*/
|
|
||||||
protected $hasStarted = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new temp database
|
* Create a new temp database
|
||||||
*
|
*
|
||||||
@ -73,14 +68,6 @@ class TempDatabase
|
|||||||
return $this->isDBTemp($selected);
|
return $this->isDBTemp($selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasStarted()
|
|
||||||
{
|
|
||||||
return $this->hasStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -94,7 +81,6 @@ class TempDatabase
|
|||||||
*/
|
*/
|
||||||
public function startTransaction()
|
public function startTransaction()
|
||||||
{
|
{
|
||||||
$this->hasStarted = true;
|
|
||||||
if (static::getConn()->supportsTransactions()) {
|
if (static::getConn()->supportsTransactions()) {
|
||||||
static::getConn()->transactionStart();
|
static::getConn()->transactionStart();
|
||||||
}
|
}
|
||||||
@ -110,32 +96,36 @@ class TempDatabase
|
|||||||
*/
|
*/
|
||||||
public function rollbackTransaction()
|
public function rollbackTransaction()
|
||||||
{
|
{
|
||||||
$success = $this->hasStarted() && static::getConn()->supportsTransactions();
|
// Ensure a rollback can be performed
|
||||||
if ($success) {
|
$success = static::getConn()->supportsTransactions()
|
||||||
|
&& static::getConn()->transactionDepth();
|
||||||
|
if (!$success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// Explicit false = gnostic error from transactionRollback
|
// Explicit false = gnostic error from transactionRollback
|
||||||
if (static::getConn()->transactionRollback() === false) {
|
if (static::getConn()->transactionRollback() === false) {
|
||||||
$success = false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} catch (DatabaseException $ex) {
|
} catch (DatabaseException $ex) {
|
||||||
$success = false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the current temp database
|
* Destroy the current temp database
|
||||||
*/
|
*/
|
||||||
public function kill()
|
public function kill()
|
||||||
{
|
{
|
||||||
$this->hasStarted = false;
|
// Nothing to kill
|
||||||
|
|
||||||
// Delete our temporary database
|
|
||||||
if (!$this->isUsed()) {
|
if (!$this->isUsed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rollback any transactions (note: Success ignored)
|
||||||
|
$this->rollbackTransaction();
|
||||||
|
|
||||||
// Check the database actually exists
|
// Check the database actually exists
|
||||||
$dbConn = $this->getConn();
|
$dbConn = $this->getConn();
|
||||||
$dbName = $dbConn->getSelectedDatabase();
|
$dbName = $dbConn->getSelectedDatabase();
|
||||||
@ -160,7 +150,6 @@ class TempDatabase
|
|||||||
*/
|
*/
|
||||||
public function clearAllData()
|
public function clearAllData()
|
||||||
{
|
{
|
||||||
$this->hasStarted = false;
|
|
||||||
if (!$this->isUsed()) {
|
if (!$this->isUsed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -218,44 +207,12 @@ class TempDatabase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all temp DBs on this connection
|
* Rebuild all database tables
|
||||||
*
|
*
|
||||||
* Note: This will output results to stdout unless suppressOutput
|
* @param array $extraDataObjects
|
||||||
* is set on the current db schema
|
|
||||||
*/
|
*/
|
||||||
public function deleteAll()
|
protected function rebuildTables($extraDataObjects = [])
|
||||||
{
|
{
|
||||||
$schema = $this->getConn()->getSchemaManager();
|
|
||||||
foreach ($schema->databaseList() as $dbName) {
|
|
||||||
if ($this->isDBTemp($dbName)) {
|
|
||||||
$schema->dropDatabase($dbName);
|
|
||||||
$schema->alterationMessage("Dropped database \"$dbName\"", 'deleted');
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the testing database's schema.
|
|
||||||
*
|
|
||||||
* @param array $extraDataObjects List of extra dataobjects to build
|
|
||||||
*/
|
|
||||||
public function resetDBSchema(array $extraDataObjects = [])
|
|
||||||
{
|
|
||||||
// pgsql doesn't allow schema updates inside transactions
|
|
||||||
// so we need to rollback any transactions before commencing a schema reset
|
|
||||||
if ($this->hasStarted()) {
|
|
||||||
// Sometimes transactions fail, rebuild
|
|
||||||
$success = $this->rollbackTransaction();
|
|
||||||
if (!$success) {
|
|
||||||
$this->kill();
|
|
||||||
$this->build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$this->isUsed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataObject::reset();
|
DataObject::reset();
|
||||||
|
|
||||||
// clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild()
|
// clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild()
|
||||||
@ -291,4 +248,45 @@ class TempDatabase
|
|||||||
ClassInfo::reset_db_cache();
|
ClassInfo::reset_db_cache();
|
||||||
DataObject::singleton()->flushCache();
|
DataObject::singleton()->flushCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all temp DBs on this connection
|
||||||
|
*
|
||||||
|
* Note: This will output results to stdout unless suppressOutput
|
||||||
|
* is set on the current db schema
|
||||||
|
*/
|
||||||
|
public function deleteAll()
|
||||||
|
{
|
||||||
|
$schema = $this->getConn()->getSchemaManager();
|
||||||
|
foreach ($schema->databaseList() as $dbName) {
|
||||||
|
if ($this->isDBTemp($dbName)) {
|
||||||
|
$schema->dropDatabase($dbName);
|
||||||
|
$schema->alterationMessage("Dropped database \"$dbName\"", 'deleted');
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the testing database's schema.
|
||||||
|
*
|
||||||
|
* @param array $extraDataObjects List of extra dataobjects to build
|
||||||
|
*/
|
||||||
|
public function resetDBSchema(array $extraDataObjects = [])
|
||||||
|
{
|
||||||
|
// Skip if no DB
|
||||||
|
if (!$this->isUsed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->rebuildTables($extraDataObjects);
|
||||||
|
} catch (DatabaseException $ex) {
|
||||||
|
// In case of error during build force a hard reset
|
||||||
|
// e.g. pgsql doesn't allow schema updates inside transactions
|
||||||
|
$this->kill();
|
||||||
|
$this->build();
|
||||||
|
$this->rebuildTables($extraDataObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,11 @@ class GroupTest extends FunctionalTest
|
|||||||
TestMember::class
|
TestMember::class
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
public function testGroupCodeDefaultsToTitle()
|
public function testGroupCodeDefaultsToTitle()
|
||||||
{
|
{
|
||||||
$g1 = new Group();
|
$g1 = new Group();
|
||||||
|
Loading…
Reference in New Issue
Block a user