function connectDatabase() { $this->enum_map = array(); $parameters = $this->parameters; $dbName = !isset($this->database) ? $parameters['database'] : ($dbName = $this->database); //assumes that the path to dbname will always be provided: $file = $parameters['path'] . '/' . $dbName; // use the very lightspeed SQLite In-Memory feature for testing if (SapphireTest::using_temp_db() && $parameters['memory']) { $file = ':memory:'; $this->lives_in_memory = true; } else { $this->lives_in_memory = false; } if (!file_exists($parameters['path'])) { SQLiteDatabaseConfigurationHelper::create_db_dir($parameters['path']); SQLiteDatabaseConfigurationHelper::secure_db_dir($parameters['path']); } $this->dbConn = new PDO("sqlite:{$file}"); //By virtue of getting here, the connection is active: $this->active = true; $this->database = $dbName; if (!$this->dbConn) { $this->databaseError("Couldn't connect to SQLite3 database"); return false; } foreach (self::$default_pragma as $pragma => $value) { $this->pragma($pragma, $value); } if (empty(self::$default_pragma['locking_mode'])) { self::$default_pragma['locking_mode'] = $this->pragma('locking_mode'); } return true; }
function sessionloadyml() { // Load incremental YAML fixtures // TODO: We will probably have to filter out the admin member here, // as it is supplied by Bare.yml if (Director::isLive()) { return "<p>sessionloadyml can only be used on dev and test sites</p>"; } if (!SapphireTest::using_temp_db()) { return "<p>Please load /dev/tests/startsession first</p>"; } $fixtureFile = isset($_GET['fixture']) ? $_GET['fixture'] : null; if (empty($fixtureFile)) { $me = Director::baseURL() . "/dev/tests/sessionloadyml"; return <<<HTML \t\t\t\t<form action="{$me}"> \t\t\t\t\t<p>Enter a fixture file name to load a new YAML fixture into the session.</p> \t\t\t\t\t<p>Fixture file <input id="fixture-file" name="fixture" /></p> \t\t\t\t\t<input type="hidden" name="flush" value="1"> \t\t\t\t\t<p><input id="session-load-yaml" value="Load yml fixture" type="submit" /></p> \t\t\t\t</form> HTML; } // Validate fixture file $realFile = realpath(BASE_PATH . '/' . $fixtureFile); $baseDir = realpath(Director::baseFolder()); if (!$realFile || !file_exists($realFile)) { return "<p>Fixture file doesn't exist</p>"; } else { if (substr($realFile, 0, strlen($baseDir)) != $baseDir) { return "<p>Fixture file must be inside {$baseDir}</p>"; } else { if (substr($realFile, -4) != '.yml') { return "<p>Fixture file must be a .yml file</p>"; } else { if (!preg_match('/^([^\\/.][^\\/]+)\\/tests\\//', $fixtureFile)) { return "<p>Fixture file must be inside the tests subfolder of one of your modules.</p>"; } } } } // Fixture $fixture = new YamlFixture($fixtureFile); $fixture->saveIntoDatabase(); return "<p>Loaded fixture '{$fixtureFile}' into session</p>"; }
/** * Cleans up the test session state by restoring the normal database connect (for the rest of this request, if any) * and removes the {@link self::$test_state_file} so that future requests don't use this test state. * * Can be extended by implementing either onBeforeEndTestSession() or onAfterEndTestSession(). * * This should implement itself cleanly in case it is called twice (e.g. don't throw errors when the state file * doesn't exist anymore because it's already been cleaned up etc.) This is because during behat test runs where * a queueing system (e.g. silverstripe-resque) is used, the behat module may call this first, and then the forked * worker will call it as well - but there is only one state file that is created. */ public function endTestSession() { $this->extend('onBeforeEndTestSession'); if (SapphireTest::using_temp_db()) { $state = $this->getState(); $dbConn = DB::getConn(); $dbExists = $dbConn->databaseExists($state->database); if ($dbExists) { // Clean up temp database $dbConn->dropDatabase(); file_put_contents('php://stdout', "Deleted temp database: {$state->database}" . PHP_EOL); } // End test session mode $this->resetDatabaseName(); SapphireTest::set_is_running_test(false); } $this->removeStateFile(); $this->extend('onAfterEndTestSession'); }
/** * Start a test session. * Usage: visit dev/tests/startsession?fixture=(fixturefile). A test database will be constructed, and your browser session will be amended * to use this database. This can only be run on dev and test sites. */ function startsession() { if(!Director::isLive()) { if(SapphireTest::using_temp_db()) { $endLink = Director::baseURL() . "/dev/tests/endsession"; return "<p><a id=\"end-session\" href=\"$endLink\">You're in the middle of a test session; click here to end it.</a></p>"; } else if(!isset($_GET['fixture'])) { $me = Director::baseURL() . "/dev/tests/startsession"; return <<<HTML <form action="$me"> <p>Enter a fixture file name to start a new test session. Don't forget to visit dev/tests/endsession when you're done!</p> <p>Fixture file: <input id="fixture-file" name="fixture" /></p> <input type="hidden" name="flush" value="1"> <p><input id="start-session" value="Start test session" type="submit" /></p> </form> HTML; } else { $fixtureFile = $_GET['fixture']; // Validate fixture file $realFile = realpath('../' . $fixtureFile); $baseDir = realpath(Director::baseFolder()); if(!$realFile || !file_exists($realFile)) { return "<p>Fixture file doesn't exist</p>"; } else if(substr($realFile,0,strlen($baseDir)) != $baseDir) { return "<p>Fixture file must be inside $baseDir</p>"; } else if(substr($realFile,-4) != '.yml') { return "<p>Fixture file must be a .yml file</p>"; } else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) { return "<p>Fixture file must be inside the tests subfolder of one of your modules.</p>"; } $dbname = SapphireTest::create_temp_db(); DB::set_alternative_database_name($dbname); $fixture = new YamlFixture($_GET['fixture']); $fixture->saveIntoDatabase(); return "<p>Started testing session with fixture '$fixtureFile'. Time to start testing; where would you like to start?</p> <ul> <li><a id=\"home-link\" href=\"" .Director::baseURL() . "\">Homepage - published site</a></li> <li><a id=\"draft-link\" href=\"" .Director::baseURL() . "?stage=Stage\">Homepage - draft site</a></li> <li><a id=\"admin-link\" href=\"" .Director::baseURL() . "admin/\">CMS Admin</a></li> </ul>"; } } else { return "<p>startession can only be used on dev and test sites</p>"; } }
/** * Cleans up the test session state by restoring the normal database connect (for the rest of this request, if any) * and removes the {@link self::$test_state_file} so that future requests don't use this test state. * * Can be extended by implementing either onBeforeEndTestSession() or onAfterEndTestSession(). * * This should implement itself cleanly in case it is called twice (e.g. don't throw errors when the state file * doesn't exist anymore because it's already been cleaned up etc.) This is because during behat test runs where * a queueing system (e.g. silverstripe-resque) is used, the behat module may call this first, and then the forked * worker will call it as well - but there is only one state file that is created. */ public function endTestSession() { $this->extend('onBeforeEndTestSession'); if (SapphireTest::using_temp_db()) { $this->resetDatabaseName(); SapphireTest::set_is_running_test(false); } $this->removeStateFile(); $this->extend('onAfterEndTestSession'); }
/** * @return boolean */ public function isTesting() { return SapphireTest::using_temp_db(); }
public function checkAndRepairTable($tableName = null) { $ok = true; if (!SapphireTest::using_temp_db() && !self::$checked_and_repaired) { $this->alterationMessage("Checking database integrity", "repaired"); // Check for any tables with failed integrity if ($messages = $this->query('PRAGMA integrity_check')) { foreach ($messages as $message) { if ($message['integrity_check'] != 'ok') { Debug::show($message['integrity_check']); $ok = false; } } } // If enabled vacuum (clean and rebuild) the database if (self::$vacuum) { $this->query('VACUUM', E_USER_NOTICE); $message = $this->database->getConnector()->getLastError(); if (preg_match('/authoriz/', $message)) { $this->alterationMessage("VACUUM | {$message}", "error"); } else { $this->alterationMessage("VACUUMing", "repaired"); } } self::$checked_and_repaired = true; } return $ok; }
/** * Repairs and reindexes the table. This might take a long time on a very large table. * @var string $tableName The name of the table. * @return boolean Return true if the table has integrity after the method is complete. */ public function checkAndRepairTable($tableName = null) { $ok = true; if (!SapphireTest::using_temp_db() && !self::$checked_and_repaired) { $this->alterationMessage("Checking database integrity", "repaired"); if ($msgs = $this->query('PRAGMA integrity_check')) { foreach ($msgs as $msg) { if ($msg['integrity_check'] != 'ok') { Debug::show($msg['integrity_check']); $ok = false; } } } if (self::$vacuum) { $this->query('VACUUM', E_USER_NOTICE); if ($this instanceof SQLitePDODatabase) { $msg = $this->dbConn->errorInfo(); $msg = isset($msg[2]) ? $msg[2] : 'no errors'; } else { $msg = $this->dbConn->lastErrorMsg(); } if (preg_match('/authoriz/', $msg)) { $this->alterationMessage('VACUUM | ' . $msg, "error"); } else { $this->alterationMessage("VACUUMing", "repaired"); } } self::$checked_and_repaired = true; } return $ok; }