mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Refactor SapphireTest state management into SapphireTestState
API Remove Injector::unregisterAllObjects() API Remove FakeController
This commit is contained in:
parent
f26ae75c6e
commit
c66d433977
9
_config/tests.yml
Normal file
9
_config/tests.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
Name: sapphiretest
|
||||||
|
---
|
||||||
|
SilverStripe\Core\Injector\Injector:
|
||||||
|
SilverStripe\Dev\SapphireTestState:
|
||||||
|
properties:
|
||||||
|
States:
|
||||||
|
flushable: %$SilverStripe\Dev\FlushableTestState
|
||||||
|
requirements: %$SilverStripe\View\Dev\RequirementsTestState
|
@ -7,15 +7,14 @@ use SilverStripe\Core\Startup\OutputMiddleware;
|
|||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
|
|
||||||
require __DIR__ . '/src/includes/cli.php';
|
require __DIR__ . '/src/includes/cli.php';
|
||||||
$_SERVER['SCRIPT_FILENAME'] = __FILE__;
|
|
||||||
chdir(__DIR__);
|
|
||||||
|
|
||||||
|
|
||||||
require __DIR__ . '/src/includes/autoload.php';
|
require __DIR__ . '/src/includes/autoload.php';
|
||||||
|
|
||||||
// Default application
|
// Build request and detect flush
|
||||||
$request = HTTPRequest::createFromEnvironment();
|
$request = HTTPRequest::createFromEnvironment();
|
||||||
$kernel = new AppKernel();
|
$flush = $request->getVar('flush') || strpos($request->getURL(), 'dev/build') === 0;
|
||||||
|
|
||||||
|
// Default application
|
||||||
|
$kernel = new AppKernel($flush);
|
||||||
$app = new HTTPApplication($kernel);
|
$app = new HTTPApplication($kernel);
|
||||||
$app->addMiddleware(new OutputMiddleware());
|
$app->addMiddleware(new OutputMiddleware());
|
||||||
$app->handle($request);
|
$app->handle($request);
|
||||||
|
@ -1340,6 +1340,8 @@ After (`mysite/_config/config.yml`):
|
|||||||
#### <a name="overview-general-removed"></a>General and Core Removed API
|
#### <a name="overview-general-removed"></a>General and Core Removed API
|
||||||
|
|
||||||
* `CMSMain::buildbrokenlinks()` action is removed.
|
* `CMSMain::buildbrokenlinks()` action is removed.
|
||||||
|
* `Injector::unregisterAllObjects()` has been removed. Use `unregisterObjects` to unregister
|
||||||
|
groups of objects limited by type instead.
|
||||||
* `SS_Log` class has been removed. Use `Injector::inst()->get(LoggerInterface::class)` instead.
|
* `SS_Log` class has been removed. Use `Injector::inst()->get(LoggerInterface::class)` instead.
|
||||||
* Removed `CMSBatchAction_Delete`
|
* Removed `CMSBatchAction_Delete`
|
||||||
* Removed `CMSBatchAction_DeleteFromLive`
|
* Removed `CMSBatchAction_DeleteFromLive`
|
||||||
|
7
main.php
7
main.php
@ -8,9 +8,12 @@ use SilverStripe\Control\HTTPRequest;
|
|||||||
|
|
||||||
require __DIR__ . '/src/includes/autoload.php';
|
require __DIR__ . '/src/includes/autoload.php';
|
||||||
|
|
||||||
// Default application
|
// Build request and detect flush
|
||||||
$request = HTTPRequest::createFromEnvironment();
|
$request = HTTPRequest::createFromEnvironment();
|
||||||
$kernel = new AppKernel();
|
$flush = $request->getVar('flush') || strpos($request->getURL(), 'dev/build') === 0;
|
||||||
|
|
||||||
|
// Default application
|
||||||
|
$kernel = new AppKernel($flush);
|
||||||
$app = new HTTPApplication($kernel);
|
$app = new HTTPApplication($kernel);
|
||||||
$app->addMiddleware(new OutputMiddleware());
|
$app->addMiddleware(new OutputMiddleware());
|
||||||
$app->addMiddleware(new ErrorControlChainMiddleware($app, $request));
|
$app->addMiddleware(new ErrorControlChainMiddleware($app, $request));
|
||||||
|
@ -27,8 +27,15 @@ use SilverStripe\View\ThemeResourceLoader;
|
|||||||
|
|
||||||
class AppKernel extends CoreKernel
|
class AppKernel extends CoreKernel
|
||||||
{
|
{
|
||||||
public function __construct()
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $flush = false;
|
||||||
|
|
||||||
|
public function __construct($flush = false)
|
||||||
{
|
{
|
||||||
|
$this->flush = $flush;
|
||||||
|
|
||||||
// Initialise the dependency injector as soon as possible, as it is
|
// Initialise the dependency injector as soon as possible, as it is
|
||||||
// subsequently used by some of the following code
|
// subsequently used by some of the following code
|
||||||
$injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
|
$injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
|
||||||
@ -355,28 +362,30 @@ class AppKernel extends CoreKernel
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function getIncludeTests()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boot all manifests
|
* Boot all manifests
|
||||||
*/
|
*/
|
||||||
protected function bootManifests()
|
protected function bootManifests()
|
||||||
{
|
{
|
||||||
// Regenerate the manifest if ?flush is set, or if the database is being built.
|
|
||||||
// The coupling is a hack, but it removes an annoying bug where new classes
|
|
||||||
// referenced in _config.php files can be referenced during the build process.
|
|
||||||
$flush = isset($_GET['flush']) ||
|
|
||||||
trim($_GET['url'], '/') === trim(BASE_URL . '/dev/build', '/');
|
|
||||||
|
|
||||||
// Setup autoloader
|
// Setup autoloader
|
||||||
$this->getClassLoader()->init(false, $flush);
|
$this->getClassLoader()->init($this->getIncludeTests(), $this->flush);
|
||||||
|
|
||||||
// Find modules
|
// Find modules
|
||||||
$this->getModuleLoader()->init(false, $flush);
|
$this->getModuleLoader()->init($this->getIncludeTests(), $this->flush);
|
||||||
|
|
||||||
// Flush config
|
// Flush config
|
||||||
if ($flush) {
|
if ($this->flush) {
|
||||||
$config = $this->getConfigLoader()->getManifest();
|
$config = $this->getConfigLoader()->getManifest();
|
||||||
if ($config instanceof CachedConfigCollection) {
|
if ($config instanceof CachedConfigCollection) {
|
||||||
$config->setFlush($flush);
|
$config->setFlush(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +395,7 @@ class AppKernel extends CoreKernel
|
|||||||
// Find default templates
|
// Find default templates
|
||||||
$defaultSet = $this->getThemeResourceLoader()->getSet('$default');
|
$defaultSet = $this->getThemeResourceLoader()->getSet('$default');
|
||||||
if ($defaultSet instanceof ThemeManifest) {
|
if ($defaultSet instanceof ThemeManifest) {
|
||||||
$defaultSet->init(false, $flush);
|
$defaultSet->init($this->getIncludeTests(), $this->flush);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use InvalidArgumentException;
|
|||||||
use SilverStripe\Control\RequestHandler;
|
use SilverStripe\Control\RequestHandler;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\View\ViewableData;
|
use SilverStripe\View\ViewableData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -271,8 +272,8 @@ trait Extensible
|
|||||||
}
|
}
|
||||||
Config::modify()->set($class, 'extensions', $config);
|
Config::modify()->set($class, 'extensions', $config);
|
||||||
|
|
||||||
// unset singletons to avoid side-effects
|
// Unset singletons
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterObjects($class);
|
||||||
|
|
||||||
// unset some caches
|
// unset some caches
|
||||||
$subclasses = ClassInfo::subclassesFor($class);
|
$subclasses = ClassInfo::subclassesFor($class);
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Core\Injector;
|
namespace SilverStripe\Core\Injector;
|
||||||
|
|
||||||
|
use ArrayObject;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use ReflectionMethod;
|
||||||
|
use ReflectionObject;
|
||||||
|
use ReflectionProperty;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use ReflectionProperty;
|
|
||||||
use ArrayObject;
|
|
||||||
use ReflectionObject;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use Psr\Container\ContainerInterface;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,16 +232,10 @@ class Injector implements ContainerInterface
|
|||||||
protected $nestedFrom = null;
|
protected $nestedFrom = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a user wants to use the injector as a static reference
|
|
||||||
*
|
|
||||||
* @param array $config
|
|
||||||
* @return Injector
|
* @return Injector
|
||||||
*/
|
*/
|
||||||
public static function inst($config = null)
|
public static function inst()
|
||||||
{
|
{
|
||||||
if (!self::$instance) {
|
|
||||||
self::$instance = new Injector($config);
|
|
||||||
}
|
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,7 +399,7 @@ class Injector implements ContainerInterface
|
|||||||
|
|
||||||
// make sure the class is set...
|
// make sure the class is set...
|
||||||
if (empty($class)) {
|
if (empty($class)) {
|
||||||
throw new \InvalidArgumentException('Missing spec class');
|
throw new InvalidArgumentException('Missing spec class');
|
||||||
}
|
}
|
||||||
$spec['class'] = $class;
|
$spec['class'] = $class;
|
||||||
|
|
||||||
@ -651,21 +646,21 @@ class Injector implements ContainerInterface
|
|||||||
|
|
||||||
// Format validation
|
// Format validation
|
||||||
if (!is_array($method) || !isset($method[0]) || isset($method[2])) {
|
if (!is_array($method) || !isset($method[0]) || isset($method[2])) {
|
||||||
throw new \InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
"'calls' entries in service definition should be 1 or 2 element arrays."
|
"'calls' entries in service definition should be 1 or 2 element arrays."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!is_string($method[0])) {
|
if (!is_string($method[0])) {
|
||||||
throw new \InvalidArgumentException("1st element of a 'calls' entry should be a string");
|
throw new InvalidArgumentException("1st element of a 'calls' entry should be a string");
|
||||||
}
|
}
|
||||||
if (isset($method[1]) && !is_array($method[1])) {
|
if (isset($method[1]) && !is_array($method[1])) {
|
||||||
throw new \InvalidArgumentException("2nd element of a 'calls' entry should an arguments array");
|
throw new InvalidArgumentException("2nd element of a 'calls' entry should an arguments array");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the method exists and is callable
|
// Check that the method exists and is callable
|
||||||
$objectMethod = array($object, $method[0]);
|
$objectMethod = array($object, $method[0]);
|
||||||
if (!is_callable($objectMethod)) {
|
if (!is_callable($objectMethod)) {
|
||||||
throw new \InvalidArgumentException("'$method[0]' in 'calls' entry is not a public method");
|
throw new InvalidArgumentException("'$method[0]' in 'calls' entry is not a public method");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call it
|
// Call it
|
||||||
@ -847,16 +842,18 @@ class Injector implements ContainerInterface
|
|||||||
* @param object $service The object to register
|
* @param object $service The object to register
|
||||||
* @param string $replace The name of the object to replace (if different to the
|
* @param string $replace The name of the object to replace (if different to the
|
||||||
* class name of the object to register)
|
* class name of the object to register)
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function registerService($service, $replace = null)
|
public function registerService($service, $replace = null)
|
||||||
{
|
{
|
||||||
$registerAt = get_class($service);
|
$registerAt = get_class($service);
|
||||||
if ($replace != null) {
|
if ($replace !== null) {
|
||||||
$registerAt = $replace;
|
$registerAt = $replace;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->specs[$registerAt] = array('class' => get_class($service));
|
$this->specs[$registerAt] = array('class' => get_class($service));
|
||||||
$this->serviceCache[$registerAt] = $service;
|
$this->serviceCache[$registerAt] = $service;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -864,18 +861,40 @@ class Injector implements ContainerInterface
|
|||||||
* by the inject
|
* by the inject
|
||||||
*
|
*
|
||||||
* @param string $name The name to unregister
|
* @param string $name The name to unregister
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function unregisterNamedObject($name)
|
public function unregisterNamedObject($name)
|
||||||
{
|
{
|
||||||
unset($this->serviceCache[$name]);
|
unset($this->serviceCache[$name]);
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear out all objects that are managed by the injetor.
|
* Clear out objects of one or more types that are managed by the injetor.
|
||||||
|
*
|
||||||
|
* @param array|string $types Base class of object (not service name) to remove
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function unregisterAllObjects()
|
public function unregisterObjects($types)
|
||||||
{
|
{
|
||||||
$this->serviceCache = array('Injector' => $this);
|
if (!is_array($types)) {
|
||||||
|
$types = [ $types ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter all objects
|
||||||
|
foreach ($this->serviceCache as $key => $object) {
|
||||||
|
foreach ($types as $filterClass) {
|
||||||
|
// Prevent destructive flushing
|
||||||
|
if (strcasecmp($filterClass, 'object') === 0) {
|
||||||
|
throw new InvalidArgumentException("Global unregistration is not allowed");
|
||||||
|
}
|
||||||
|
if ($object instanceof $filterClass) {
|
||||||
|
unset($this->serviceCache[$key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
30
src/Core/TestKernel.php
Normal file
30
src/Core/TestKernel.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kernel for running unit tests
|
||||||
|
*/
|
||||||
|
class TestKernel extends AppKernel
|
||||||
|
{
|
||||||
|
public function __construct($flush = true)
|
||||||
|
{
|
||||||
|
parent::__construct($flush);
|
||||||
|
$this->setEnvironment(self::DEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset kernel between tests.
|
||||||
|
* Note: this avoids resetting services (See TestState for service specific reset)
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
$this->setEnvironment(self::DEV);
|
||||||
|
$this->bootPHP();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getIncludeTests()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
49
src/Dev/FlushableTestState.php
Normal file
49
src/Dev/FlushableTestState.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Dev;
|
||||||
|
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Flushable;
|
||||||
|
use SilverStripe\Core\Resettable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears flushable / resettable objects
|
||||||
|
*/
|
||||||
|
class FlushableTestState implements TestState
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $flushed = false;
|
||||||
|
|
||||||
|
public function setUp(SapphireTest $test)
|
||||||
|
{
|
||||||
|
// Reset all resettables
|
||||||
|
/** @var Resettable $resettable */
|
||||||
|
foreach (ClassInfo::implementorsOf(Resettable::class) as $resettable) {
|
||||||
|
$resettable::reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown(SapphireTest $test)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUpOnce($class)
|
||||||
|
{
|
||||||
|
if ($this->flushed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->flushed = true;
|
||||||
|
|
||||||
|
// Flush all flushable records
|
||||||
|
/** @var Flushable $class */
|
||||||
|
foreach (ClassInfo::implementorsOf(Flushable::class) as $class) {
|
||||||
|
$class::flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDownOnce($class)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -11,37 +11,31 @@ use SilverStripe\Control\Cookie;
|
|||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\Email\Email;
|
use SilverStripe\Control\Email\Email;
|
||||||
use SilverStripe\Control\Email\Mailer;
|
use SilverStripe\Control\Email\Mailer;
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\Session;
|
use SilverStripe\Control\Session;
|
||||||
use SilverStripe\Control\Tests\FakeController;
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Config\ConfigLoader;
|
|
||||||
use SilverStripe\Core\Config\CoreConfigFactory;
|
|
||||||
use SilverStripe\Core\Config\DefaultConfig;
|
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
use SilverStripe\Core\Flushable;
|
use SilverStripe\Core\HTTPApplication;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Manifest\ClassLoader;
|
use SilverStripe\Core\TestKernel;
|
||||||
use SilverStripe\Core\Manifest\ClassManifest;
|
|
||||||
use SilverStripe\Core\Resettable;
|
|
||||||
use SilverStripe\i18n\i18n;
|
use SilverStripe\i18n\i18n;
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
use SilverStripe\ORM\SS_List;
|
|
||||||
use SilverStripe\Security\IdentityStore;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
|
use SilverStripe\ORM\SS_List;
|
||||||
use SilverStripe\Security\Group;
|
use SilverStripe\Security\Group;
|
||||||
|
use SilverStripe\Security\IdentityStore;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\View\Requirements;
|
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
use SilverStripe\View\ThemeManifest;
|
|
||||||
use SilverStripe\View\ThemeResourceLoader;
|
if (!class_exists(PHPUnit_Framework_TestCase::class)) {
|
||||||
use Translatable;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test case class for the Sapphire framework.
|
* Test case class for the Sapphire framework.
|
||||||
@ -50,12 +44,6 @@ use Translatable;
|
|||||||
*/
|
*/
|
||||||
class SapphireTest extends PHPUnit_Framework_TestCase
|
class SapphireTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
/** @config */
|
|
||||||
private static $dependencies = array(
|
|
||||||
'fixtureFactory' => '%$FixtureFactory',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path to fixture data for this test run.
|
* Path to fixture data for this test run.
|
||||||
* If passed as an array, multiple fixture files will be loaded.
|
* If passed as an array, multiple fixture files will be loaded.
|
||||||
@ -77,36 +65,18 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
* {@link $fixture_file}, which always forces a database build.
|
* {@link $fixture_file}, which always forces a database build.
|
||||||
*/
|
*/
|
||||||
protected $usesDatabase = null;
|
protected $usesDatabase = null;
|
||||||
protected $originalMemberPasswordValidator;
|
|
||||||
protected $originalRequirements;
|
|
||||||
protected $originalIsRunningTest;
|
|
||||||
protected $originalNestedURLsState;
|
|
||||||
protected $originalMemoryLimit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var TestMailer
|
* @var bool
|
||||||
*/
|
|
||||||
protected $mailer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pointer to the manifest that isn't a test manifest
|
|
||||||
*/
|
|
||||||
protected static $regular_manifest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var boolean
|
|
||||||
*/
|
*/
|
||||||
protected static $is_running_test = false;
|
protected static $is_running_test = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ClassManifest
|
|
||||||
*/
|
|
||||||
protected static $test_class_manifest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By default, setUp() does not require default records. Pass
|
* By default, setUp() does not require default records. Pass
|
||||||
* class names in here, and the require/augment default records
|
* class names in here, and the require/augment default records
|
||||||
* function will be called on them.
|
* function will be called on them.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $requireDefaultRecordsFrom = array();
|
protected $requireDefaultRecordsFrom = array();
|
||||||
|
|
||||||
@ -173,44 +143,35 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
protected static $flushedFlushables = false;
|
protected static $flushedFlushables = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if unit tests are currently run, flag set during test bootstrap.
|
* Test application kernel.
|
||||||
* This is used as a cheap replacement for fully mockable state
|
* Note: This is always the root kernel. Use Injector to get the current kernel
|
||||||
* in certain contiditions (e.g. access checks).
|
* if nested.
|
||||||
* Caution: When set to FALSE, certain controllers might bypass
|
|
||||||
* access checks, so this is a very security sensitive setting.
|
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @var TestKernel
|
||||||
|
*/
|
||||||
|
protected static $kernel = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if test bootstrapping has been performed. Must not be relied on
|
||||||
|
* outside of unit tests.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected static function is_running_test()
|
protected static function is_running_test()
|
||||||
{
|
{
|
||||||
return self::$is_running_test;
|
return self::$is_running_test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set test running state
|
||||||
|
*
|
||||||
|
* @param bool $bool
|
||||||
|
*/
|
||||||
protected static function set_is_running_test($bool)
|
protected static function set_is_running_test($bool)
|
||||||
{
|
{
|
||||||
self::$is_running_test = $bool;
|
self::$is_running_test = $bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the manifest to be used to look up test classes by helper functions
|
|
||||||
*
|
|
||||||
* @param ClassManifest $manifest
|
|
||||||
*/
|
|
||||||
public static function set_test_class_manifest($manifest)
|
|
||||||
{
|
|
||||||
self::$test_class_manifest = $manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the manifest being used to look up test classes by helper functions
|
|
||||||
*
|
|
||||||
* @return ClassManifest
|
|
||||||
*/
|
|
||||||
public static function get_test_class_manifest()
|
|
||||||
{
|
|
||||||
return self::$test_class_manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
@ -219,27 +180,30 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
return static::$fixture_file;
|
return static::$fixture_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $model;
|
/**
|
||||||
|
* @var TestState
|
||||||
|
*/
|
||||||
|
protected static $state = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State of Versioned before this test is run
|
* Setup the test.
|
||||||
|
* Always sets up in order:
|
||||||
|
* - Reset php state
|
||||||
|
* - Nest
|
||||||
|
* - Custom state helpers
|
||||||
*
|
*
|
||||||
* @var string
|
* User code should call parent::setUp() before custom setup code
|
||||||
*/
|
*/
|
||||||
protected $originalReadingMode = null;
|
|
||||||
|
|
||||||
protected $originalEnv = null;
|
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
|
self::$kernel->reset();
|
||||||
|
|
||||||
//nest config and injector for each test so they are effectively sandboxed per test
|
//nest config and injector for each test so they are effectively sandboxed per test
|
||||||
Config::nest();
|
Config::nest();
|
||||||
Injector::nest();
|
Injector::nest();
|
||||||
|
|
||||||
$this->originalEnv = Director::get_environment_type();
|
// Call state helpers
|
||||||
if (class_exists(Versioned::class)) {
|
static::$state->setUp($this);
|
||||||
$this->originalReadingMode = Versioned::get_reading_mode();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We cannot run the tests on this abstract class.
|
// We cannot run the tests on this abstract class.
|
||||||
if (static::class == __CLASS__) {
|
if (static::class == __CLASS__) {
|
||||||
@ -247,31 +211,18 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark test as being run
|
|
||||||
$this->originalIsRunningTest = self::$is_running_test;
|
|
||||||
self::$is_running_test = true;
|
|
||||||
|
|
||||||
// i18n needs to be set to the defaults or tests fail
|
// i18n needs to be set to the defaults or tests fail
|
||||||
i18n::set_locale(i18n::config()->uninherited('default_locale'));
|
i18n::set_locale(i18n::config()->uninherited('default_locale'));
|
||||||
|
|
||||||
// Set default timezone consistently to avoid NZ-specific dependencies
|
// Set default timezone consistently to avoid NZ-specific dependencies
|
||||||
date_default_timezone_set('UTC');
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
// Remove password validation
|
|
||||||
$this->originalMemberPasswordValidator = Member::password_validator();
|
|
||||||
$this->originalRequirements = Requirements::backend();
|
|
||||||
Member::set_password_validator(null);
|
Member::set_password_validator(null);
|
||||||
Cookie::config()->update('report_errors', false);
|
Cookie::config()->update('report_errors', false);
|
||||||
if (class_exists(RootURLController::class)) {
|
if (class_exists(RootURLController::class)) {
|
||||||
RootURLController::reset();
|
RootURLController::reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all resettables
|
|
||||||
/** @var Resettable $resettable */
|
|
||||||
foreach (ClassInfo::implementorsOf(Resettable::class) as $resettable) {
|
|
||||||
$resettable::reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
Security::clear_database_is_ready();
|
Security::clear_database_is_ready();
|
||||||
|
|
||||||
// Set up test routes
|
// Set up test routes
|
||||||
@ -307,18 +258,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->logInWithPermission("ADMIN");
|
$this->logInWithPermission("ADMIN");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve memory settings
|
|
||||||
$this->originalMemoryLimit = ini_get('memory_limit');
|
|
||||||
|
|
||||||
// turn off template debugging
|
// turn off template debugging
|
||||||
SSViewer::config()->update('source_file_comments', false);
|
SSViewer::config()->update('source_file_comments', false);
|
||||||
|
|
||||||
// Clear requirements
|
|
||||||
Requirements::clear();
|
|
||||||
|
|
||||||
// Set up the test mailer
|
// Set up the test mailer
|
||||||
$this->mailer = new TestMailer();
|
Injector::inst()->registerService(new TestMailer(), Mailer::class);
|
||||||
Injector::inst()->registerService($this->mailer, Mailer::class);
|
|
||||||
Email::config()->remove('send_all_emails_to');
|
Email::config()->remove('send_all_emails_to');
|
||||||
Email::config()->remove('send_all_emails_from');
|
Email::config()->remove('send_all_emails_from');
|
||||||
Email::config()->remove('cc_all_emails_to');
|
Email::config()->remove('cc_all_emails_to');
|
||||||
@ -332,20 +276,25 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
* don't change state for any called method inside the test,
|
* don't change state for any called method inside the test,
|
||||||
* e.g. dynamically adding an extension. See {@link teardownAfterClass()}
|
* e.g. dynamically adding an extension. See {@link teardownAfterClass()}
|
||||||
* for tearing down the state again.
|
* for tearing down the state again.
|
||||||
|
*
|
||||||
|
* Always sets up in order:
|
||||||
|
* - Reset php state
|
||||||
|
* - Nest
|
||||||
|
* - Custom state helpers
|
||||||
|
*
|
||||||
|
* User code should call parent::setUpBeforeClass() before custom setup code
|
||||||
*/
|
*/
|
||||||
public static function setUpBeforeClass()
|
public static function setUpBeforeClass()
|
||||||
{
|
{
|
||||||
static::start();
|
static::start();
|
||||||
|
|
||||||
|
static::$kernel->reset();
|
||||||
|
|
||||||
//nest config and injector for each suite so they are effectively sandboxed
|
//nest config and injector for each suite so they are effectively sandboxed
|
||||||
Config::nest();
|
Config::nest();
|
||||||
Injector::nest();
|
Injector::nest();
|
||||||
$isAltered = false;
|
$isAltered = false;
|
||||||
|
|
||||||
if (!Director::isDev()) {
|
|
||||||
user_error('Tests can only run in "dev" mode', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove any illegal extensions that are present
|
// Remove any illegal extensions that are present
|
||||||
foreach (static::$illegal_extensions as $class => $extensions) {
|
foreach (static::$illegal_extensions as $class => $extensions) {
|
||||||
if (!class_exists($class)) {
|
if (!class_exists($class)) {
|
||||||
@ -401,26 +350,30 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
// clear singletons, they're caching old extension info
|
// clear singletons, they're caching old extension info
|
||||||
// which is used in DatabaseAdmin->doBuild()
|
// which is used in DatabaseAdmin->doBuild()
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterObjects(DataObject::class);
|
||||||
|
|
||||||
// Set default timezone consistently to avoid NZ-specific dependencies
|
// Set default timezone consistently to avoid NZ-specific dependencies
|
||||||
date_default_timezone_set('UTC');
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
// Flush all flushable records
|
// Call state helpers
|
||||||
$flush = !empty($_GET['flush']);
|
static::$state->setUpOnce(static::class);
|
||||||
if (!self::$flushedFlushables && $flush) {
|
|
||||||
self::$flushedFlushables = true;
|
|
||||||
foreach (ClassInfo::implementorsOf(Flushable::class) as $class) {
|
|
||||||
$class::flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tearDown method that's called once per test class rather once per test method.
|
* tearDown method that's called once per test class rather once per test method.
|
||||||
|
*
|
||||||
|
* Always sets up in order:
|
||||||
|
* - Custom state helpers
|
||||||
|
* - Unnest
|
||||||
|
* - Reset php state
|
||||||
|
*
|
||||||
|
* User code should call parent::tearDownAfterClass() after custom tear down code
|
||||||
*/
|
*/
|
||||||
public static function tearDownAfterClass()
|
public static function tearDownAfterClass()
|
||||||
{
|
{
|
||||||
|
// Call state helpers
|
||||||
|
static::$state->tearDownOnce(static::class);
|
||||||
|
|
||||||
// If we have made changes to the extensions present, then migrate the database schema.
|
// If we have made changes to the extensions present, then migrate the database schema.
|
||||||
if (self::$extensions_to_reapply || self::$extensions_to_remove) {
|
if (self::$extensions_to_reapply || self::$extensions_to_remove) {
|
||||||
// @todo: This isn't strictly necessary to restore extensions, but only to ensure that
|
// @todo: This isn't strictly necessary to restore extensions, but only to ensure that
|
||||||
@ -451,6 +404,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
if (!empty(self::$extensions_to_reapply) || !empty(self::$extensions_to_remove) || !empty($extraDataObjects)) {
|
if (!empty(self::$extensions_to_reapply) || !empty(self::$extensions_to_remove) || !empty($extraDataObjects)) {
|
||||||
static::resetDBSchema();
|
static::resetDBSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static::$kernel->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -464,6 +419,12 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
return $this->fixtureFactory;
|
return $this->fixtureFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new fixture factory
|
||||||
|
*
|
||||||
|
* @param FixtureFactory $factory
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function setFixtureFactory(FixtureFactory $factory)
|
public function setFixtureFactory(FixtureFactory $factory)
|
||||||
{
|
{
|
||||||
$this->fixtureFactory = $factory;
|
$this->fixtureFactory = $factory;
|
||||||
@ -552,11 +513,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* Useful for writing unit tests without hardcoding folder structures.
|
* Useful for writing unit tests without hardcoding folder structures.
|
||||||
*
|
*
|
||||||
* @return String Absolute path to current class.
|
* @return string Absolute path to current class.
|
||||||
*/
|
*/
|
||||||
protected function getCurrentAbsolutePath()
|
protected function getCurrentAbsolutePath()
|
||||||
{
|
{
|
||||||
$filename = self::$test_class_manifest->getItemPath(static::class);
|
$filename = static::$kernel->getClassLoader()->getItemPath(static::class);
|
||||||
if (!$filename) {
|
if (!$filename) {
|
||||||
throw new LogicException("getItemPath returned null for " . static::class);
|
throw new LogicException("getItemPath returned null for " . static::class);
|
||||||
}
|
}
|
||||||
@ -564,7 +525,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return String File path relative to webroot
|
* @return string File path relative to webroot
|
||||||
*/
|
*/
|
||||||
protected function getCurrentRelativePath()
|
protected function getCurrentRelativePath()
|
||||||
{
|
{
|
||||||
@ -576,28 +537,17 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
return $path;
|
return $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the test.
|
||||||
|
* Always sets up in order:
|
||||||
|
* - Custom state helpers
|
||||||
|
* - Unnest
|
||||||
|
* - Reset php state
|
||||||
|
*
|
||||||
|
* User code should call parent::tearDown() after custom tear down code
|
||||||
|
*/
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
// Preserve memory settings
|
|
||||||
ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1);
|
|
||||||
|
|
||||||
// Restore email configuration
|
|
||||||
$this->mailer = null;
|
|
||||||
|
|
||||||
// Restore password validation
|
|
||||||
if ($this->originalMemberPasswordValidator) {
|
|
||||||
Member::set_password_validator($this->originalMemberPasswordValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore requirements
|
|
||||||
if ($this->originalRequirements) {
|
|
||||||
Requirements::set_backend($this->originalRequirements);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark test as no longer being run - we use originalIsRunningTest to allow for nested SapphireTest calls
|
|
||||||
self::$is_running_test = $this->originalIsRunningTest;
|
|
||||||
$this->originalIsRunningTest = null;
|
|
||||||
|
|
||||||
// Reset mocked datetime
|
// Reset mocked datetime
|
||||||
DBDatetime::clear_mock_now();
|
DBDatetime::clear_mock_now();
|
||||||
|
|
||||||
@ -610,14 +560,15 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
$response->removeHeader('Location');
|
$response->removeHeader('Location');
|
||||||
}
|
}
|
||||||
|
|
||||||
Director::set_environment_type($this->originalEnv);
|
// Call state helpers
|
||||||
if (class_exists(Versioned::class)) {
|
static::$state->setUp($this);
|
||||||
Versioned::set_reading_mode($this->originalReadingMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
//unnest injector / config now that tests are over
|
//unnest injector / config now that tests are over
|
||||||
Injector::unnest();
|
Injector::unnest();
|
||||||
Config::unnest();
|
Config::unnest();
|
||||||
|
|
||||||
|
// Reset state
|
||||||
|
self::$kernel->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function assertContains(
|
public static function assertContains(
|
||||||
@ -650,36 +601,48 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the log of emails sent
|
* Clear the log of emails sent
|
||||||
|
*
|
||||||
|
* @return bool True if emails cleared
|
||||||
*/
|
*/
|
||||||
public function clearEmails()
|
public function clearEmails()
|
||||||
{
|
{
|
||||||
return $this->mailer->clearEmails();
|
/** @var Mailer $mailer */
|
||||||
|
$mailer = Injector::inst()->get(Mailer::class);
|
||||||
|
if ($mailer instanceof TestMailer) {
|
||||||
|
$mailer->clearEmails();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for an email that was sent.
|
* Search for an email that was sent.
|
||||||
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
||||||
* @param $to
|
* @param string $to
|
||||||
* @param $from
|
* @param string $from
|
||||||
* @param $subject
|
* @param string $subject
|
||||||
* @param $content
|
* @param string $content
|
||||||
* @return array Contains keys: 'type', 'to', 'from', 'subject','content', 'plainContent', 'attachedFiles',
|
* @return array Contains keys: 'type', 'to', 'from', 'subject','content', 'plainContent', 'attachedFiles',
|
||||||
* 'customHeaders', 'htmlContent', 'inlineImages'
|
* 'customHeaders', 'htmlContent', 'inlineImages'
|
||||||
*/
|
*/
|
||||||
public function findEmail($to, $from = null, $subject = null, $content = null)
|
public function findEmail($to, $from = null, $subject = null, $content = null)
|
||||||
{
|
{
|
||||||
return $this->mailer->findEmail($to, $from, $subject, $content);
|
/** @var Mailer $mailer */
|
||||||
|
$mailer = Injector::inst()->get(Mailer::class);
|
||||||
|
if ($mailer instanceof TestMailer) {
|
||||||
|
return $mailer->findEmail($to, $from, $subject, $content);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert that the matching email was sent since the last call to clearEmails()
|
* Assert that the matching email was sent since the last call to clearEmails()
|
||||||
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
||||||
* @param $to
|
*
|
||||||
* @param $from
|
* @param string $to
|
||||||
* @param $subject
|
* @param string $from
|
||||||
* @param $content
|
* @param string $subject
|
||||||
* @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles',
|
* @param string $content
|
||||||
* 'customHeaders', 'htmlContent', inlineImages'
|
|
||||||
*/
|
*/
|
||||||
public function assertEmailSent($to, $from = null, $subject = null, $content = null)
|
public function assertEmailSent($to, $from = null, $subject = null, $content = null)
|
||||||
{
|
{
|
||||||
@ -1013,43 +976,39 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public static function start()
|
public static function start()
|
||||||
{
|
{
|
||||||
if (!static::is_running_test()) {
|
if (static::is_running_test()) {
|
||||||
new FakeController();
|
return;
|
||||||
static::use_test_manifest();
|
}
|
||||||
|
// Health check
|
||||||
|
if (Injector::inst() || static::$kernel) {
|
||||||
|
throw new LogicException("SapphireTest::start() cannot be called within another application");
|
||||||
|
}
|
||||||
static::set_is_running_test(true);
|
static::set_is_running_test(true);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Mock request
|
||||||
* Pushes a class and template manifest instance that include tests onto the
|
$session = new Session(isset($_SESSION) ? $_SESSION : array());
|
||||||
* top of the loader stacks.
|
$request = new HTTPRequest('GET', '/');
|
||||||
*/
|
$request->setSession($session);
|
||||||
protected static function use_test_manifest()
|
|
||||||
{
|
|
||||||
$flush = !empty($_GET['flush']);
|
|
||||||
$classManifest = new ClassManifest(
|
|
||||||
BASE_PATH,
|
|
||||||
true,
|
|
||||||
$flush
|
|
||||||
);
|
|
||||||
|
|
||||||
ClassLoader::inst()->pushManifest($classManifest, false);
|
// Test application
|
||||||
static::set_test_class_manifest($classManifest);
|
static::$kernel = new TestKernel();
|
||||||
|
$app = new HTTPApplication(static::$kernel);
|
||||||
ThemeResourceLoader::inst()->addSet('$default', new ThemeManifest(
|
|
||||||
BASE_PATH,
|
|
||||||
project(),
|
|
||||||
true,
|
|
||||||
$flush
|
|
||||||
));
|
|
||||||
|
|
||||||
// Once new class loader is registered, push a new uncached config
|
|
||||||
$config = CoreConfigFactory::inst()->createCore();
|
|
||||||
ConfigLoader::inst()->pushManifest($config);
|
|
||||||
|
|
||||||
|
// Custom application
|
||||||
|
$app->execute(function () use ($request) {
|
||||||
// Invalidate classname spec since the test manifest will now pull out new subclasses for each internal class
|
// Invalidate classname spec since the test manifest will now pull out new subclasses for each internal class
|
||||||
// (e.g. Member will now have various subclasses of DataObjects that implement TestOnly)
|
// (e.g. Member will now have various subclasses of DataObjects that implement TestOnly)
|
||||||
DataObject::reset();
|
DataObject::reset();
|
||||||
|
|
||||||
|
// Set dummy controller
|
||||||
|
$controller = Controller::create();
|
||||||
|
$controller->setRequest($request);
|
||||||
|
$controller->pushCurrent();
|
||||||
|
$controller->doInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register state
|
||||||
|
static::$state = SapphireTestState::singleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1160,7 +1119,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
|||||||
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()
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterObjects(DataObject::class);
|
||||||
|
|
||||||
$dataClasses = ClassInfo::subclassesFor(DataObject::class);
|
$dataClasses = ClassInfo::subclassesFor(DataObject::class);
|
||||||
array_shift($dataClasses);
|
array_shift($dataClasses);
|
||||||
|
70
src/Dev/SapphireTestState.php
Normal file
70
src/Dev/SapphireTestState.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Dev;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
|
|
||||||
|
class SapphireTestState implements TestState
|
||||||
|
{
|
||||||
|
use Injectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TestState[]
|
||||||
|
*/
|
||||||
|
protected $states = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return TestState[]
|
||||||
|
*/
|
||||||
|
public function getStates()
|
||||||
|
{
|
||||||
|
return $this->states;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TestState[] $states
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setStates(array $states)
|
||||||
|
{
|
||||||
|
$this->states = $states;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp(SapphireTest $test)
|
||||||
|
{
|
||||||
|
foreach ($this->states as $state) {
|
||||||
|
$state->setUp($test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown(SapphireTest $test)
|
||||||
|
{
|
||||||
|
// Tear down in reverse order
|
||||||
|
/** @var TestState $state */
|
||||||
|
foreach (array_reverse($this->states) as $state) {
|
||||||
|
$state->tearDown($test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUpOnce($class)
|
||||||
|
{
|
||||||
|
foreach ($this->states as $state) {
|
||||||
|
$state->setUpOnce($class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once on tear down
|
||||||
|
*
|
||||||
|
* @param string $class Class being torn down
|
||||||
|
*/
|
||||||
|
public function tearDownOnce($class)
|
||||||
|
{
|
||||||
|
// Tear down in reverse order
|
||||||
|
/** @var TestState $state */
|
||||||
|
foreach (array_reverse($this->states) as $state) {
|
||||||
|
$state->tearDownOnce($class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
src/Dev/TestState.php
Normal file
39
src/Dev/TestState.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Dev;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for resetting, booting, or cleaning up test state.
|
||||||
|
*
|
||||||
|
* SapphireTest will detect all implementors of this interface during test execution
|
||||||
|
*/
|
||||||
|
interface TestState extends TestOnly
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called on setup
|
||||||
|
*
|
||||||
|
* @param SapphireTest $test
|
||||||
|
*/
|
||||||
|
public function setUp(SapphireTest $test);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on tear down
|
||||||
|
*
|
||||||
|
* @param SapphireTest $test
|
||||||
|
*/
|
||||||
|
public function tearDown(SapphireTest $test);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once on setup
|
||||||
|
*
|
||||||
|
* @param string $class Class being setup
|
||||||
|
*/
|
||||||
|
public function setUpOnce($class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once on tear down
|
||||||
|
*
|
||||||
|
* @param string $class Class being torn down
|
||||||
|
*/
|
||||||
|
public function tearDownOnce($class);
|
||||||
|
}
|
@ -10,6 +10,7 @@ use SilverStripe\Control\Controller;
|
|||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\Email\Email;
|
use SilverStripe\Control\Email\Email;
|
||||||
use SilverStripe\Control\Email\Mailer;
|
use SilverStripe\Control\Email\Mailer;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
@ -176,14 +177,6 @@ class Member extends DataObject
|
|||||||
*/
|
*/
|
||||||
private static $unique_identifier_field = 'Email';
|
private static $unique_identifier_field = 'Email';
|
||||||
|
|
||||||
/**
|
|
||||||
* Object for validating user's password
|
|
||||||
*
|
|
||||||
* @config
|
|
||||||
* @var PasswordValidator
|
|
||||||
*/
|
|
||||||
private static $password_validator = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
* The number of days that a password should be valid for.
|
* The number of days that a password should be valid for.
|
||||||
@ -369,23 +362,31 @@ class Member extends DataObject
|
|||||||
/**
|
/**
|
||||||
* Set a {@link PasswordValidator} object to use to validate member's passwords.
|
* Set a {@link PasswordValidator} object to use to validate member's passwords.
|
||||||
*
|
*
|
||||||
* @param PasswordValidator $pv
|
* @param PasswordValidator $validator
|
||||||
*/
|
*/
|
||||||
public static function set_password_validator($pv)
|
public static function set_password_validator(PasswordValidator $validator = null)
|
||||||
{
|
{
|
||||||
self::$password_validator = $pv;
|
// Override existing config
|
||||||
|
Config::modify()->remove(Injector::class, PasswordValidator::class);
|
||||||
|
if ($validator) {
|
||||||
|
Injector::inst()->registerService($validator, PasswordValidator::class);
|
||||||
|
} else {
|
||||||
|
Injector::inst()->unregisterNamedObject(PasswordValidator::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current {@link PasswordValidator}
|
* Returns the default {@link PasswordValidator}
|
||||||
*
|
*
|
||||||
* @return PasswordValidator
|
* @return PasswordValidator
|
||||||
*/
|
*/
|
||||||
public static function password_validator()
|
public static function password_validator()
|
||||||
{
|
{
|
||||||
return self::$password_validator;
|
if (Injector::inst()->has(PasswordValidator::class)) {
|
||||||
|
return Injector::inst()->get(PasswordValidator::class);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function isPasswordExpired()
|
public function isPasswordExpired()
|
||||||
{
|
{
|
||||||
@ -1605,16 +1606,17 @@ class Member extends DataObject
|
|||||||
public function validate()
|
public function validate()
|
||||||
{
|
{
|
||||||
$valid = parent::validate();
|
$valid = parent::validate();
|
||||||
|
$validator = static::password_validator();
|
||||||
|
|
||||||
if (!$this->ID || $this->isChanged('Password')) {
|
if (!$this->ID || $this->isChanged('Password')) {
|
||||||
if ($this->Password && self::$password_validator) {
|
if ($this->Password && $validator) {
|
||||||
$valid->combineAnd(self::$password_validator->validate($this->Password, $this));
|
$valid->combineAnd($validator->validate($this->Password, $this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!$this->ID && $this->SetPassword) || $this->isChanged('SetPassword')) {
|
if ((!$this->ID && $this->SetPassword) || $this->isChanged('SetPassword')) {
|
||||||
if ($this->SetPassword && self::$password_validator) {
|
if ($this->SetPassword && $validator) {
|
||||||
$valid->combineAnd(self::$password_validator->validate($this->SetPassword, $this));
|
$valid->combineAnd($validator->validate($this->SetPassword, $this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
src/View/Dev/RequirementsTestState.php
Normal file
39
src/View/Dev/RequirementsTestState.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace SilverStripe\View\Dev;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Dev\TestState;
|
||||||
|
use SilverStripe\View\Requirements;
|
||||||
|
use SilverStripe\View\Requirements_Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets requirements for test state
|
||||||
|
*/
|
||||||
|
class RequirementsTestState implements TestState
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Requirements_Backend
|
||||||
|
*/
|
||||||
|
protected $backend = null;
|
||||||
|
|
||||||
|
public function setUp(SapphireTest $test)
|
||||||
|
{
|
||||||
|
$this->backend = Requirements::backend();
|
||||||
|
Requirements::set_backend(Requirements_Backend::create());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown(SapphireTest $test)
|
||||||
|
{
|
||||||
|
Requirements::set_backend($this->backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUpOnce($class)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDownOnce($class)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
namespace SilverStripe\View;
|
namespace SilverStripe\View;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Flushable;
|
use SilverStripe\Core\Flushable;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requirements tracker for JavaScript and CSS.
|
* Requirements tracker for JavaScript and CSS.
|
||||||
@ -104,7 +103,7 @@ class Requirements implements Flushable
|
|||||||
public static function backend()
|
public static function backend()
|
||||||
{
|
{
|
||||||
if (!self::$backend) {
|
if (!self::$backend) {
|
||||||
self::$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
|
self::$backend = Requirements_Backend::create();
|
||||||
}
|
}
|
||||||
return self::$backend;
|
return self::$backend;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace SilverStripe\View;
|
namespace SilverStripe\View;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
use SilverStripe\Assets\File;
|
use SilverStripe\Assets\File;
|
||||||
use SilverStripe\Assets\Storage\GeneratedAssetHandler;
|
use SilverStripe\Assets\Storage\GeneratedAssetHandler;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
@ -13,7 +13,6 @@ use SilverStripe\Core\Convert;
|
|||||||
use SilverStripe\Core\Injector\Injectable;
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
use SilverStripe\Dev\Debug;
|
use SilverStripe\Dev\Debug;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
use SilverStripe\i18n\i18n;
|
use SilverStripe\i18n\i18n;
|
||||||
|
|
||||||
class Requirements_Backend
|
class Requirements_Backend
|
||||||
|
@ -3,4 +3,3 @@
|
|||||||
require __DIR__ . '/bootstrap/init.php';
|
require __DIR__ . '/bootstrap/init.php';
|
||||||
require __DIR__ . '/bootstrap/cli.php';
|
require __DIR__ . '/bootstrap/cli.php';
|
||||||
require __DIR__ . '/bootstrap/environment.php';
|
require __DIR__ . '/bootstrap/environment.php';
|
||||||
require __DIR__ . '/bootstrap/phpunit.php';
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// Default database settings
|
|
||||||
global $project;
|
|
||||||
$project = 'mysite';
|
|
||||||
|
|
||||||
global $database;
|
|
||||||
$database = '';
|
|
||||||
|
|
||||||
require_once('conf/ConfigureFromEnv.php');
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// Bootstrap for running SapphireTests
|
|
||||||
|
|
||||||
// Connect to database
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../../src/Core/functions.php';
|
|
||||||
require_once __DIR__ . '/../php/Control/FakeController.php';
|
|
||||||
|
|
||||||
// Bootstrap a mock project configuration
|
|
||||||
require __DIR__ . '/mysite.php';
|
|
||||||
|
|
||||||
global $databaseConfig;
|
|
||||||
DB::connect($databaseConfig);
|
|
||||||
|
|
||||||
// Now set a fake REQUEST_URI
|
|
||||||
$_SERVER['REQUEST_URI'] = BASE_URL;
|
|
||||||
|
|
||||||
// Fake a session
|
|
||||||
$_SESSION = null;
|
|
||||||
|
|
||||||
// Remove the error handler so that PHPUnit can add its own
|
|
||||||
restore_error_handler();
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Control\Tests;
|
|
||||||
|
|
||||||
use SilverStripe\Control\Session;
|
|
||||||
use SilverStripe\Core\Injector\Injector;
|
|
||||||
use SilverStripe\Control\HTTPRequest;
|
|
||||||
use SilverStripe\Control\HTTPResponse;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
|
|
||||||
// Fake a current controller. Way harder than it should be
|
|
||||||
class FakeController extends Controller
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
$this->pushCurrent();
|
|
||||||
$session = new Session(isset($_SESSION) ? $_SESSION : array());
|
|
||||||
$request = new HTTPRequest('GET', '/');
|
|
||||||
$request->setSession($session);
|
|
||||||
$this->setRequest($request);
|
|
||||||
$this->setResponse(new HTTPResponse());
|
|
||||||
|
|
||||||
$this->doInit();
|
|
||||||
}
|
|
||||||
}
|
|
@ -967,7 +967,10 @@ class InjectorTest extends SapphireTest
|
|||||||
// Test that nested injector values can be overridden
|
// Test that nested injector values can be overridden
|
||||||
Injector::nest();
|
Injector::nest();
|
||||||
$this->nestingLevel++;
|
$this->nestingLevel++;
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterObjects([
|
||||||
|
TestStaticInjections::class,
|
||||||
|
MyParentClass::class,
|
||||||
|
]);
|
||||||
$newsi = Injector::inst()->get(TestStaticInjections::class);
|
$newsi = Injector::inst()->get(TestStaticInjections::class);
|
||||||
$newsi->backend = new InjectorTest\OriginalRequirementsBackend();
|
$newsi->backend = new InjectorTest\OriginalRequirementsBackend();
|
||||||
Injector::inst()->registerService($newsi, TestStaticInjections::class);
|
Injector::inst()->registerService($newsi, TestStaticInjections::class);
|
||||||
@ -990,7 +993,10 @@ class InjectorTest extends SapphireTest
|
|||||||
$this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
|
$this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
|
||||||
|
|
||||||
// Test reset of cache
|
// Test reset of cache
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterObjects([
|
||||||
|
TestStaticInjections::class,
|
||||||
|
MyParentClass::class,
|
||||||
|
]);
|
||||||
$si = Injector::inst()->get(TestStaticInjections::class);
|
$si = Injector::inst()->get(TestStaticInjections::class);
|
||||||
$this->assertInstanceOf(TestStaticInjections::class, $si);
|
$this->assertInstanceOf(TestStaticInjections::class, $si);
|
||||||
$this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
|
$this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
|
||||||
|
@ -6,11 +6,9 @@ use SilverStripe\Dev\TestOnly;
|
|||||||
|
|
||||||
class TestStaticInjections implements TestOnly
|
class TestStaticInjections implements TestOnly
|
||||||
{
|
{
|
||||||
|
|
||||||
public $backend;
|
public $backend;
|
||||||
/**
|
|
||||||
* @config
|
/** @config */
|
||||||
*/
|
|
||||||
private static $dependencies = array(
|
private static $dependencies = array(
|
||||||
'backend' => '%$SilverStripe\\Core\\Tests\\Injector\\InjectorTest\\NewRequirementsBackend'
|
'backend' => '%$SilverStripe\\Core\\Tests\\Injector\\InjectorTest\\NewRequirementsBackend'
|
||||||
);
|
);
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Core\Tests;
|
namespace SilverStripe\Core\Tests;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Extension;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Core\Tests\ObjectTest\BaseObject;
|
||||||
use SilverStripe\Core\Tests\ObjectTest\ExtendTest1;
|
use SilverStripe\Core\Tests\ObjectTest\ExtendTest1;
|
||||||
use SilverStripe\Core\Tests\ObjectTest\ExtendTest2;
|
use SilverStripe\Core\Tests\ObjectTest\ExtendTest2;
|
||||||
use SilverStripe\Core\Tests\ObjectTest\ExtendTest3;
|
use SilverStripe\Core\Tests\ObjectTest\ExtendTest3;
|
||||||
@ -16,7 +19,6 @@ use SilverStripe\Core\Tests\ObjectTest\MyObject;
|
|||||||
use SilverStripe\Core\Tests\ObjectTest\MySubObject;
|
use SilverStripe\Core\Tests\ObjectTest\MySubObject;
|
||||||
use SilverStripe\Core\Tests\ObjectTest\TestExtension;
|
use SilverStripe\Core\Tests\ObjectTest\TestExtension;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +32,10 @@ class ObjectTest extends SapphireTest
|
|||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
Injector::inst()->unregisterAllObjects();
|
Injector::inst()->unregisterObjects([
|
||||||
|
Extension::class,
|
||||||
|
BaseObject::class,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHasmethodBehaviour()
|
public function testHasmethodBehaviour()
|
||||||
|
Loading…
Reference in New Issue
Block a user