mirror of
https://github.com/silverstripe/silverstripe-testsession
synced 2024-10-22 14:06:00 +02:00
Merge branch '2'
This commit is contained in:
commit
4f68d5bae2
@ -20,6 +20,11 @@ is a random token stored in the browser session, in order to make the
|
|||||||
test session specific to the executing browser, and allow multiple
|
test session specific to the executing browser, and allow multiple
|
||||||
people using their own test session in the same webroot.
|
people using their own test session in the same webroot.
|
||||||
|
|
||||||
|
The module also keeps some metadata about the session state in the database,
|
||||||
|
so that it may be available for the clients as well.
|
||||||
|
E.g. the silverstripe-behat-extension may use it through this module APIs,
|
||||||
|
allowing us to introduce some grey-box testing techniques.
|
||||||
|
|
||||||
The module also serves as an initializer for the
|
The module also serves as an initializer for the
|
||||||
[SilverStripe Behat Extension](https://github.com/silverstripe-labs/silverstripe-behat-extension/).
|
[SilverStripe Behat Extension](https://github.com/silverstripe-labs/silverstripe-behat-extension/).
|
||||||
It is required for Behat because the Behat CLI test runner needs to persist
|
It is required for Behat because the Behat CLI test runner needs to persist
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
Name: testsessionroutes
|
Name: testsessionroutes
|
||||||
---
|
---
|
||||||
SilverStripe\Control\Director:
|
SilverStripe\Dev\DevelopmentAdmin:
|
||||||
rules:
|
registered_controllers:
|
||||||
dev/testsession: SilverStripe\TestSession\TestSessionController
|
testsession:
|
||||||
|
controller: SilverStripe\TestSession\TestSessionController
|
||||||
|
@ -283,26 +283,7 @@ class TestSessionEnvironment
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ensure we have a connection to the database
|
// ensure we have a connection to the database
|
||||||
if (isset($state->database) && $state->database) {
|
$this->connectToDatabase($state);
|
||||||
if (!DB::get_conn()) {
|
|
||||||
// No connection, so try and connect to tmpdb if it exists
|
|
||||||
if (isset($state->database)) {
|
|
||||||
$this->oldDatabaseName = $databaseConfig['database'];
|
|
||||||
$databaseConfig['database'] = $state->database;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to database
|
|
||||||
DB::connect($databaseConfig);
|
|
||||||
} else {
|
|
||||||
// We've already connected to the database, do a fast check to see what database we're currently using
|
|
||||||
$db = DB::get_conn()->getSelectedDatabase();
|
|
||||||
if (isset($state->database) && $db != $state->database) {
|
|
||||||
$this->oldDatabaseName = $databaseConfig['database'];
|
|
||||||
$databaseConfig['database'] = $state->database;
|
|
||||||
DB::connect($databaseConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
if (!$this->isRunningTests()) {
|
if (!$this->isRunningTests()) {
|
||||||
@ -334,6 +315,8 @@ class TestSessionEnvironment
|
|||||||
// Connect to the new database, overwriting the old DB connection (if any)
|
// Connect to the new database, overwriting the old DB connection (if any)
|
||||||
DB::connect($databaseConfig);
|
DB::connect($databaseConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestSessionState::create()->write(); // initialize the session state
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mailer
|
// Mailer
|
||||||
@ -362,6 +345,7 @@ class TestSessionEnvironment
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->saveState($state);
|
$this->saveState($state);
|
||||||
|
|
||||||
$this->extend('onAfterApplyState');
|
$this->extend('onAfterApplyState');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,4 +541,69 @@ class TestSessionEnvironment
|
|||||||
{
|
{
|
||||||
return PUBLIC_PATH . DIRECTORY_SEPARATOR . 'assets_backup';
|
return PUBLIC_PATH . DIRECTORY_SEPARATOR . 'assets_backup';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that there is a connection to the database
|
||||||
|
*
|
||||||
|
* @param mixed $state
|
||||||
|
*/
|
||||||
|
public function connectToDatabase($state = null) {
|
||||||
|
if ($state == null) {
|
||||||
|
$state = $this->getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
$databaseConfig = DB::getConfig();
|
||||||
|
|
||||||
|
if (isset($state->database) && $state->database) {
|
||||||
|
if (!DB::get_conn()) {
|
||||||
|
// No connection, so try and connect to tmpdb if it exists
|
||||||
|
if (isset($state->database)) {
|
||||||
|
$this->oldDatabaseName = $databaseConfig['database'];
|
||||||
|
$databaseConfig['database'] = $state->database;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to database
|
||||||
|
DB::connect($databaseConfig);
|
||||||
|
} else {
|
||||||
|
// We've already connected to the database, do a fast check to see what database we're currently using
|
||||||
|
$db = DB::get_conn()->getSelectedDatabase();
|
||||||
|
if (isset($state->database) && $db != $state->database) {
|
||||||
|
$this->oldDatabaseName = $databaseConfig['database'];
|
||||||
|
$databaseConfig['database'] = $state->database;
|
||||||
|
DB::connect($databaseConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for pending requests
|
||||||
|
*
|
||||||
|
* @param int $await Time to wait (in ms) after the last response (to allow the browser react)
|
||||||
|
* @param int $timeout For how long (in ms) do we wait before giving up
|
||||||
|
*
|
||||||
|
* @return bool Whether there are no more pending requests
|
||||||
|
*/
|
||||||
|
public function waitForPendingRequests($await = 700, $timeout = 10000)
|
||||||
|
{
|
||||||
|
$timeout = TestSessionState::millitime() + $timeout;
|
||||||
|
$interval = max(300, $await);
|
||||||
|
|
||||||
|
do {
|
||||||
|
$now = TestSessionState::millitime();
|
||||||
|
|
||||||
|
if ($timeout < $now) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = TestSessionState::get()->byID(1);
|
||||||
|
|
||||||
|
$pendingRequests = $model->PendingRequests > 0;
|
||||||
|
$lastRequestAwait = ($model->LastResponseTimestamp + $await) > $now;
|
||||||
|
|
||||||
|
$pending = $pendingRequests || $lastRequestAwait;
|
||||||
|
} while ($pending && (usleep($interval * 1000) || true));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,12 +39,14 @@ class TestSessionHTTPMiddleware implements HTTPMiddleware
|
|||||||
|
|
||||||
// Load test state
|
// Load test state
|
||||||
$this->loadTestState($request);
|
$this->loadTestState($request);
|
||||||
|
TestSessionState::incrementState();
|
||||||
|
|
||||||
// Call with safe teardown
|
// Call with safe teardown
|
||||||
try {
|
try {
|
||||||
return $delegate($request);
|
return $delegate($request);
|
||||||
} finally {
|
} finally {
|
||||||
$this->restoreTestState($request);
|
$this->restoreTestState($request);
|
||||||
|
TestSessionState::decrementState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +71,9 @@ class TestSessionHTTPMiddleware implements HTTPMiddleware
|
|||||||
Email::config()->set("send_all_emails_to", null);
|
Email::config()->set("send_all_emails_to", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect to the test session database
|
||||||
|
$this->testSessionEnvironment->connectToDatabase();
|
||||||
|
|
||||||
// Allows inclusion of a PHP file, usually with procedural commands
|
// Allows inclusion of a PHP file, usually with procedural commands
|
||||||
// to set up required test state. The file can be generated
|
// to set up required test state. The file can be generated
|
||||||
// through {@link TestSessionStubCodeWriter}, and the session state
|
// through {@link TestSessionStubCodeWriter}, and the session state
|
||||||
@ -77,11 +82,6 @@ class TestSessionHTTPMiddleware implements HTTPMiddleware
|
|||||||
if (isset($testState->stubfile)) {
|
if (isset($testState->stubfile)) {
|
||||||
$file = $testState->stubfile;
|
$file = $testState->stubfile;
|
||||||
if (!Director::isLive() && $file && file_exists($file)) {
|
if (!Director::isLive() && $file && file_exists($file)) {
|
||||||
// Connect to the database so the included code can interact with it
|
|
||||||
$databaseConfig = DB::getConfig();
|
|
||||||
if ($databaseConfig) {
|
|
||||||
DB::connect($databaseConfig);
|
|
||||||
}
|
|
||||||
include_once($file);
|
include_once($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
70
src/TestSessionState.php
Normal file
70
src/TestSessionState.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\TestSession;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\ORM\Queries\SQLUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The session state keeps some metadata about the current test session.
|
||||||
|
* This may allow the client (Behat) to get some insight into the
|
||||||
|
* server side affairs (e.g. if the server is handling some number requests at the moment).
|
||||||
|
*
|
||||||
|
* The client side (Behat) must not use this class straightforwardly, but rather
|
||||||
|
* rely on the API of {@see TestSessionEnvironment} or {@see TestSessionController}.
|
||||||
|
*
|
||||||
|
* @property int PendingRequests keeps information about how many requests are in progress
|
||||||
|
* @property float LastResponseTimestamp microtime of the last response made by the server
|
||||||
|
*/
|
||||||
|
class TestSessionState extends DataObject
|
||||||
|
{
|
||||||
|
private static $table_name = 'TestSessionState';
|
||||||
|
|
||||||
|
private static $db = [
|
||||||
|
'PendingRequests' => 'Int',
|
||||||
|
'LastResponseTimestamp' => 'Decimal(14, 0)'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments TestSessionState.PendingRequests number by 1
|
||||||
|
* to indicate we have one more request in progress
|
||||||
|
*/
|
||||||
|
public static function incrementState()
|
||||||
|
{
|
||||||
|
$schema = DataObject::getSchema();
|
||||||
|
|
||||||
|
$update = SQLUpdate::create(sprintf('"%s"', $schema->tableName(self::class)))
|
||||||
|
->addWhere(['ID' => 1])
|
||||||
|
->assignSQL('"PendingRequests"', '"PendingRequests" + 1');
|
||||||
|
|
||||||
|
$update->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements TestSessionState.PendingRequests number by 1
|
||||||
|
* to indicate we have one more request in progress.
|
||||||
|
* Also updates TestSessionState.LastResponseTimestamp
|
||||||
|
* to the current timestamp.
|
||||||
|
*/
|
||||||
|
public static function decrementState()
|
||||||
|
{
|
||||||
|
$schema = DataObject::getSchema();
|
||||||
|
|
||||||
|
$update = SQLUpdate::create(sprintf('"%s"', $schema->tableName(self::class)))
|
||||||
|
->addWhere(['ID' => 1])
|
||||||
|
->assignSQL('"PendingRequests"', '"PendingRequests" - 1')
|
||||||
|
->assign('"LastResponseTimestamp"', self::millitime());
|
||||||
|
|
||||||
|
$update->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns unix timestamp in milliseconds
|
||||||
|
*
|
||||||
|
* @return float milliseconds since 1970
|
||||||
|
*/
|
||||||
|
public static function millitime()
|
||||||
|
{
|
||||||
|
return round(microtime(true) * 1000);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user