From 3ee5b24898a63e4be973e25847344da4f949880d Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Fri, 6 Mar 2015 22:37:06 +0000 Subject: [PATCH 1/3] Nest and unnest Config and Controller for each test and test suite --- dev/SapphireTest.php | 258 +++++++++--------- tests/control/DirectorTest.php | 130 +++++---- tests/core/ConfigTest.php | 16 +- tests/core/manifest/ConfigManifestTest.php | 8 +- tests/dev/DevAdminControllerTest.php | 61 ++--- .../model/DataObjectSchemaGenerationTest.php | 52 ++-- tests/model/MySQLDatabaseTest.php | 6 +- tests/oembed/OembedTest.php | 12 +- tests/security/BasicAuthTest.php | 52 ++-- 9 files changed, 275 insertions(+), 320 deletions(-) diff --git a/dev/SapphireTest.php b/dev/SapphireTest.php index e2cc95a9c..c0de44d29 100644 --- a/dev/SapphireTest.php +++ b/dev/SapphireTest.php @@ -5,12 +5,12 @@ require_once 'TestRunner.php'; * Test case class for the Sapphire framework. * Sapphire unit testing is based on PHPUnit, but provides a number of hooks into our data model that make it easier * to work with. - * + * * @package framework * @subpackage testing */ class SapphireTest extends PHPUnit_Framework_TestCase { - + /** @config */ private static $dependencies = array( 'fixtureFactory' => '%$FixtureFactory', @@ -21,7 +21,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { * If passed as an array, multiple fixture files will be loaded. * Please note that you won't be able to refer with "=>" notation * between the fixtures, they act independent of each other. - * + * * @var string|array */ protected static $fixture_file = null; @@ -30,19 +30,19 @@ class SapphireTest extends PHPUnit_Framework_TestCase { * @var FixtureFactory */ protected $fixtureFactory; - + /** * @var bool Set whether to include this test in the TestRunner or to skip this. */ protected $skipTest = false; - + /** * @var Boolean If set to TRUE, this will force a test database to be generated - * in {@link setUp()}. Note that this flag is overruled by the presence of a + * in {@link setUp()}. Note that this flag is overruled by the presence of a * {@link $fixture_file}, which always forces a database build. */ protected $usesDatabase = null; - + protected $originalMailer; protected $originalMemberPasswordValidator; protected $originalRequirements; @@ -50,33 +50,33 @@ class SapphireTest extends PHPUnit_Framework_TestCase { protected $originalTheme; protected $originalNestedURLsState; protected $originalMemoryLimit; - + 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 $test_class_manifest; - + /** * By default, setUp() does not require default records. Pass * class names in here, and the require/augment default records * function will be called on them. */ protected $requireDefaultRecordsFrom = array(); - - + + /** * A list of extensions that can't be applied during the execution of this run. If they are * applied, they will be temporarily removed and a database migration called. - * + * * The keys of the are the classes that the extensions can't be applied the extensions to, and * the values are an array of illegal extensions on that class. */ @@ -86,10 +86,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase { /** * A list of extensions that must be applied during the execution of this run. If they are * not applied, they will be temporarily added and a database migration called. - * + * * The keys of the are the classes to apply the extensions to, and the values are an array * of required extensions on that class. - * + * * Example: * * array("MyTreeDataObject" => array("Versioned", "Hierarchy")) @@ -97,35 +97,35 @@ class SapphireTest extends PHPUnit_Framework_TestCase { */ protected $requiredExtensions = array( ); - + /** * By default, the test database won't contain any DataObjects that have the interface TestOnly. * This variable lets you define additional TestOnly DataObjects to set up for this test. * Set it to an array of DataObject subclass names. */ protected $extraDataObjects = array(); - + /** * We need to disabling backing up of globals to avoid overriding * the few globals SilverStripe relies on, like $lang for the i18n subsystem. - * + * * @see http://sebastian-bergmann.de/archives/797-Global-Variables-and-PHPUnit.html */ protected $backupGlobals = FALSE; - /** + /** * Helper arrays for illegalExtensions/requiredExtensions code */ private $extensionsToReapply = array(), $extensionsToRemove = array(); - + /** * Determines if unit tests are currently run (via {@link TestRunner}). * This is used as a cheap replacement for fully mockable state * in certain contiditions (e.g. access checks). * Caution: When set to FALSE, certain controllers might bypass * access checks, so this is a very security sensitive setting. - * + * * @return boolean */ public static function is_running_test() { @@ -133,7 +133,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { } public static function set_is_running_test($bool) { - self::$is_running_test = $bool; + self::$is_running_test = $bool; } /** @@ -161,22 +161,27 @@ class SapphireTest extends PHPUnit_Framework_TestCase { * @var array $fixtures Array of {@link YamlFixture} instances * @deprecated 3.1 Use $fixtureFactory instad */ - protected $fixtures = array(); - + protected $fixtures = array(); + protected $model; - + public function setUp() { + + //nest config and injector for each test so they are effectively sandboxed per test + Config::nest(); + Injector::nest(); + // We cannot run the tests on this abstract class. if(get_class($this) == "SapphireTest") $this->skipTest = true; - + if($this->skipTest) { $this->markTestSkipped(sprintf( 'Skipping %s ', get_class($this) )); - + return; } - + // Mark test as being run $this->originalIsRunningTest = self::$is_running_test; self::$is_running_test = true; @@ -185,16 +190,16 @@ class SapphireTest extends PHPUnit_Framework_TestCase { i18n::set_locale(i18n::default_locale()); i18n::config()->date_format = null; i18n::config()->time_format = null; - + // Set default timezone consistently to avoid NZ-specific dependencies date_default_timezone_set('UTC'); - + // Remove password validation $this->originalMemberPasswordValidator = Member::password_validator(); $this->originalRequirements = Requirements::backend(); Member::set_password_validator(null); Config::inst()->update('Cookie', 'report_errors', false); - + if(class_exists('RootURLController')) RootURLController::reset(); if(class_exists('Translatable')) Translatable::reset(); Versioned::reset(); @@ -203,7 +208,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { Hierarchy::reset(); if(Controller::has_curr()) Controller::curr()->setSession(Injector::inst()->create('Session', array())); Security::$database_is_ready = null; - + $fixtureFile = static::get_fixture_file(); $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; @@ -213,13 +218,13 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $this->mailer = new TestMailer(); Email::set_mailer($this->mailer); Config::inst()->remove('Email', 'send_all_emails_to'); - + // Todo: this could be a special test model $this->model = DataModel::inst(); // Set up fixture if($fixtureFile || $this->usesDatabase || !self::using_temp_db()) { - if(substr(DB::getConn()->currentDatabase(), 0, strlen($prefix) + 5) + if(substr(DB::getConn()->currentDatabase(), 0, strlen($prefix) + 5) != strtolower(sprintf('%stmpdb', $prefix))) { //echo "Re-creating temp database... "; @@ -228,9 +233,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase { } singleton('DataObject')->flushCache(); - + self::empty_temp_db(); - + foreach($this->requireDefaultRecordsFrom as $className) { $instance = singleton($className); if (method_exists($instance, 'requireDefaultRecords')) $instance->requireDefaultRecords(); @@ -245,14 +250,14 @@ class SapphireTest extends PHPUnit_Framework_TestCase { foreach($fixtureFiles as $fixtureFilePath) { // Support fixture paths relative to the test class, rather than relative to webroot // String checking is faster than file_exists() calls. - $isRelativeToFile = (strpos('/', $fixtureFilePath) === false + $isRelativeToFile = (strpos('/', $fixtureFilePath) === false || preg_match('/^\.\./', $fixtureFilePath)); if($isRelativeToFile) { $resolvedPath = realpath($pathForClass . '/' . $fixtureFilePath); if($resolvedPath) $fixtureFilePath = $resolvedPath; } - + $fixture = Injector::inst()->create('YamlFixture', $fixtureFilePath); $fixture->writeInto($this->getFixtureFactory()); $this->fixtures[] = $fixture; @@ -262,20 +267,20 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $i++; } } - + $this->logInWithPermission("ADMIN"); } - + // Preserve memory settings $this->originalMemoryLimit = ini_get('memory_limit'); - + // turn off template debugging Config::inst()->update('SSViewer', 'source_file_comments', false); - + // Clear requirements Requirements::clear(); } - + /** * Called once per test case ({@link SapphireTest} subclass). * This is different to {@link setUp()}, which gets called once @@ -285,6 +290,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase { * for tearing down the state again. */ public function setUpOnce() { + + //nest config and injector for each suite so they are effectively sandboxed + Config::nest(); + Injector::nest(); $isAltered = false; if(!Director::isDev()) { @@ -315,46 +324,34 @@ class SapphireTest extends PHPUnit_Framework_TestCase { } } } - + // If we have made changes to the extensions present, then migrate the database schema. if($isAltered || $this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) { if(!self::using_temp_db()) self::create_temp_db(); $this->resetDBSchema(true); } - // clear singletons, they're caching old extension info + // clear singletons, they're caching old extension info // which is used in DatabaseAdmin->doBuild() Injector::inst()->unregisterAllObjects(); // Set default timezone consistently to avoid NZ-specific dependencies date_default_timezone_set('UTC'); } - + /** * tearDown method that's called once per test class rather once per test method. */ public function tearDownOnce() { - // If we have made changes to the extensions present, then migrate the database schema. - if($this->extensionsToReapply || $this->extensionsToRemove) { - // Remove extensions added for testing - foreach($this->extensionsToRemove as $class => $extensions) { - foreach($extensions as $extension) { - $class::remove_extension($extension); - } - } - - // Reapply ones removed - foreach($this->extensionsToReapply as $class => $extensions) { - foreach($extensions as $extension) { - $class::add_extension($extension); - } - } - } + //unnest injector / config now that the test suite is over + // this will reset all the extensions on the object too (see setUpOnce) + Injector::unnest(); + Config::unnest(); if($this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) { $this->resetDBSchema(); } } - + /** * @return FixtureFactory */ @@ -367,10 +364,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $this->fixtureFactory = $factory; return $this; } - + /** * Get the ID of an object from the fixture. - * + * * @param $className The data class, as specified in your fixture file. Parent classes won't work * @param $identifier The identifier string, as provided in your fixture file * @return int @@ -416,12 +413,12 @@ class SapphireTest extends PHPUnit_Framework_TestCase { "Couldn't find object '%s' (class: %s)", $identifier, $className - ), E_USER_ERROR); + ), E_USER_ERROR); } - + return $obj; } - + /** * Load a YAML fixture file into the database. * Once loaded, you can use idFromFixture() and objFromFixture() to get items from the fixture. @@ -434,19 +431,19 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $fixture->writeInto($this->getFixtureFactory()); $this->fixtures[] = $fixture; } - + /** * Clear all fixtures which were previously loaded through - * {@link loadFixture()} + * {@link loadFixture()} */ public function clearFixtures() { $this->fixtures = array(); $this->getFixtureFactory()->clear(); } - + /** * Useful for writing unit tests without hardcoding folder structures. - * + * * @return String Absolute path to current class. */ protected function getCurrentAbsolutePath() { @@ -454,7 +451,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { if(!$filename) throw new LogicException("getItemPath returned null for " . get_class($this)); return dirname($filename); } - + /** * @return String File path relative to webroot */ @@ -464,7 +461,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { if(substr($path,0,strlen($base)) == $base) $path = preg_replace('/^\/*/', '', substr($path,strlen($base))); return $path; } - + public function tearDown() { // Preserve memory settings ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1); @@ -474,16 +471,16 @@ class SapphireTest extends PHPUnit_Framework_TestCase { Email::set_mailer($this->originalMailer); $this->originalMailer = null; } - $this->mailer = null; + $this->mailer = null; // Restore password validation if($this->originalMemberPasswordValidator) { - Member::set_password_validator($this->originalMemberPasswordValidator); + Member::set_password_validator($this->originalMemberPasswordValidator); } - + // Restore requirements if($this->originalRequirements) { - Requirements::set_backend($this->originalRequirements); + Requirements::set_backend($this->originalRequirements); } // Mark test as no longer being run - we use originalIsRunningTest to allow for nested SapphireTest calls @@ -492,15 +489,18 @@ class SapphireTest extends PHPUnit_Framework_TestCase { // Reset mocked datetime SS_Datetime::clear_mock_now(); - + // Stop the redirection that might have been requested in the test. - // Note: Ideally a clean Controller should be created for each test. + // Note: Ideally a clean Controller should be created for each test. // Now all tests executed in a batch share the same controller. $controller = Controller::has_curr() ? Controller::curr() : null; if ( $controller && $controller->response && $controller->response->getHeader('Location') ) { $controller->response->setStatusCode(200); $controller->response->removeHeader('Location'); } + //unnest injector / config now that tests are over + Injector::unnest(); + Config::unnest(); } public static function assertContains( @@ -547,7 +547,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { public function findEmail($to, $from = null, $subject = null, $content = null) { return $this->mailer->findEmail($to, $from, $subject, $content); } - + /** * 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. @@ -579,7 +579,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { /** * Assert that the given {@link SS_List} includes DataObjects matching the given key-value * pairs. Each match must correspond to 1 distinct record. - * + * * @param $matches The patterns to match. Each pattern is a map of key-value pairs. You can * either pass a single pattern or an array of patterns. * @param $dataObjectSet The {@link SS_List} to test. @@ -587,19 +587,19 @@ class SapphireTest extends PHPUnit_Framework_TestCase { * Examples * -------- * Check that $members includes an entry with Email = sam@example.com: - * $this->assertDOSContains(array('Email' => '...@example.com'), $members); - * - * Check that $members includes entries with Email = sam@example.com and with + * $this->assertDOSContains(array('Email' => '...@example.com'), $members); + * + * Check that $members includes entries with Email = sam@example.com and with * Email = ingo@example.com: - * $this->assertDOSContains(array( - * array('Email' => '...@example.com'), - * array('Email' => 'i...@example.com'), - * ), $members); + * $this->assertDOSContains(array( + * array('Email' => '...@example.com'), + * array('Email' => 'i...@example.com'), + * ), $members); */ public function assertDOSContains($matches, $dataObjectSet) { $extracted = array(); foreach($dataObjectSet as $item) $extracted[] = $item->toMap(); - + foreach($matches as $match) { $matched = false; foreach($extracted as $i => $item) { @@ -615,35 +615,35 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $this->assertTrue( $matched, "Failed asserting that the SS_List contains an item matching " - . var_export($match, true) . "\n\nIn the following SS_List:\n" + . var_export($match, true) . "\n\nIn the following SS_List:\n" . $this->DOSSummaryForMatch($dataObjectSet, $match) ); } - } - + } + /** - * Assert that the given {@link SS_List} includes only DataObjects matching the given + * Assert that the given {@link SS_List} includes only DataObjects matching the given * key-value pairs. Each match must correspond to 1 distinct record. - * + * * @param $matches The patterns to match. Each pattern is a map of key-value pairs. You can * either pass a single pattern or an array of patterns. * @param $dataObjectSet The {@link SS_List} to test. * * Example * -------- - * Check that *only* the entries Sam Minnee and Ingo Schommer exist in $members. Order doesn't + * Check that *only* the entries Sam Minnee and Ingo Schommer exist in $members. Order doesn't * matter: - * $this->assertDOSEquals(array( - * array('FirstName' =>'Sam', 'Surname' => 'Minnee'), - * array('FirstName' => 'Ingo', 'Surname' => 'Schommer'), - * ), $members); + * $this->assertDOSEquals(array( + * array('FirstName' =>'Sam', 'Surname' => 'Minnee'), + * array('FirstName' => 'Ingo', 'Surname' => 'Schommer'), + * ), $members); */ public function assertDOSEquals($matches, $dataObjectSet) { if(!$dataObjectSet) return false; - + $extracted = array(); foreach($dataObjectSet as $item) $extracted[] = $item->toMap(); - + foreach($matches as $match) { $matched = false; foreach($extracted as $i => $item) { @@ -659,11 +659,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $this->assertTrue( $matched, "Failed asserting that the SS_List contains an item matching " - . var_export($match, true) . "\n\nIn the following SS_List:\n" + . var_export($match, true) . "\n\nIn the following SS_List:\n" . $this->DOSSummaryForMatch($dataObjectSet, $match) ); } - + // If we have leftovers than the DOS has extra data that shouldn't be there $this->assertTrue( (count($extracted) == 0), @@ -671,19 +671,19 @@ class SapphireTest extends PHPUnit_Framework_TestCase { "Failed asserting that the SS_List contained only the given items, the " . "following items were left over:\n" . var_export($extracted, true) ); - } + } /** * Assert that the every record in the given {@link SS_List} matches the given key-value * pairs. - * + * * @param $match The pattern to match. The pattern is a map of key-value pairs. * @param $dataObjectSet The {@link SS_List} to test. * * Example * -------- * Check that every entry in $members has a Status of 'Active': - * $this->assertDOSAllMatch(array('Status' => 'Active'), $members); + * $this->assertDOSAllMatch(array('Status' => 'Active'), $members); */ public function assertDOSAllMatch($match, $dataObjectSet) { $extracted = array(); @@ -692,12 +692,12 @@ class SapphireTest extends PHPUnit_Framework_TestCase { foreach($extracted as $i => $item) { $this->assertTrue( $this->dataObjectArrayMatch($item, $match), - "Failed asserting that the the following item matched " + "Failed asserting that the the following item matched " . var_export($match, true) . ": " . var_export($item, true) ); } - } - + } + /** * Helper function for the DOS matchers */ @@ -723,17 +723,17 @@ class SapphireTest extends PHPUnit_Framework_TestCase { public static function using_temp_db() { $dbConn = DB::getConn(); $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; - return $dbConn && (substr($dbConn->currentDatabase(), 0, strlen($prefix) + 5) + return $dbConn && (substr($dbConn->currentDatabase(), 0, strlen($prefix) + 5) == strtolower(sprintf('%stmpdb', $prefix))); } - + public static function kill_temp_db() { // Delete our temporary database if(self::using_temp_db()) { $dbConn = DB::getConn(); $dbName = $dbConn->currentDatabase(); if($dbName && DB::getConn()->databaseExists($dbName)) { - // Some DataExtensions keep a static cache of information that needs to + // Some DataExtensions keep a static cache of information that needs to // be reset whenever the database is killed foreach(ClassInfo::subclassesFor('DataExtension') as $class) { $toCall = array($class, 'on_db_reset'); @@ -745,7 +745,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { } } } - + /** * Remove all content from the temporary database. */ @@ -753,8 +753,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase { if(self::using_temp_db()) { $dbadmin = new DatabaseAdmin(); $dbadmin->clearAllData(); - - // Some DataExtensions keep a static cache of information that needs to + + // Some DataExtensions keep a static cache of information that needs to // be reset whenever the database is cleaned out $classes = array_merge(ClassInfo::subclassesFor('DataExtension'), ClassInfo::subclassesFor('DataObject')); foreach($classes as $class) { @@ -763,7 +763,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { } } } - + public static function create_temp_db() { // Disable PHPUnit error handling restore_error_handler(); @@ -784,13 +784,13 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $st = Injector::inst()->create('SapphireTest'); $st->resetDBSchema(); - + // Reinstate PHPUnit error handling set_error_handler(array('PHPUnit_Util_ErrorHandler', 'handleError')); - + return $dbname; } - + public static function delete_all_temp_dbs() { $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; foreach(DB::getConn()->allDatabaseNames() as $dbName) { @@ -805,7 +805,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { } } } - + /** * Reset the testing database's schema. * @param $includeExtraDataObjects If true, the extraDataObjects tables will also be included @@ -846,7 +846,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { singleton('DataObject')->flushCache(); } } - + /** * Create a member and group with the given permission code, and log in with it. * Returns the member ID. @@ -861,23 +861,23 @@ class SapphireTest extends PHPUnit_Framework_TestCase { $permission->Code = $permCode; $permission->write(); $group->Permissions()->add($permission); - + $member = DataObject::get_one('Member', sprintf('"Email" = \'%s\'', "$permCode@example.org")); if(!$member) $member = Injector::inst()->create('Member'); - + $member->FirstName = $permCode; $member->Surname = "User"; $member->Email = "$permCode@example.org"; $member->write(); $group->Members()->add($member); - + $this->cache_generatedMembers[$permCode] = $member; } - + $this->cache_generatedMembers[$permCode]->logIn(); return $this->cache_generatedMembers[$permCode]->ID; } - + /** * Cache for logInWithPermission() */ diff --git a/tests/control/DirectorTest.php b/tests/control/DirectorTest.php index b96d321be..1d5c6830f 100644 --- a/tests/control/DirectorTest.php +++ b/tests/control/DirectorTest.php @@ -2,7 +2,7 @@ /** * @package framework * @subpackage tests - * + * * @todo test Director::alternateBaseFolder() */ class DirectorTest extends SapphireTest { @@ -10,27 +10,24 @@ class DirectorTest extends SapphireTest { protected static $originalRequestURI; protected $originalProtocolHeaders = array(); - + protected $originalGet = array(); - + protected $originalSession = array(); public function setUp() { parent::setUp(); - - // Required for testRequestFilterInDirectorTest - Injector::nest(); // Hold the original request URI once so it doesn't get overwritten if(!self::$originalRequestURI) { self::$originalRequestURI = $_SERVER['REQUEST_URI']; } $_SERVER['REQUEST_URI'] = 'http://www.mysite.com'; - + $this->originalGet = $_GET; $this->originalSession = $_SESSION; $_SESSION = array(); - + Config::inst()->update('Director', 'rules', array( 'DirectorTestRule/$Action/$ID/$OtherID' => 'DirectorTestRequest_Controller', 'en-nz/$Action/$ID/$OtherID' => array( @@ -49,16 +46,13 @@ class DirectorTest extends SapphireTest { } } } - + public function tearDown() { // TODO Remove director rule, currently API doesnt allow this - - // Remove base URL override (setting to false reverts to default behaviour) - Config::inst()->update('Director', 'alternate_base_url', false); - + $_GET = $this->originalGet; $_SESSION = $this->originalSession; - + // Reinstate the original REQUEST_URI after it was modified by some tests $_SERVER['REQUEST_URI'] = self::$originalRequestURI; @@ -67,34 +61,32 @@ class DirectorTest extends SapphireTest { $_SERVER[$header] = $value; } } - - Injector::unnest(); parent::tearDown(); } - + public function testFileExists() { $tempFileName = 'DirectorTest_testFileExists.tmp'; $tempFilePath = TEMP_FOLDER . '/' . $tempFileName; - + // create temp file file_put_contents($tempFilePath, ''); - + $this->assertTrue( - Director::fileExists($tempFilePath), + Director::fileExists($tempFilePath), 'File exist check with absolute path' ); - + $this->assertTrue( - Director::fileExists($tempFilePath . '?queryparams=1&foo[bar]=bar'), + Director::fileExists($tempFilePath . '?queryparams=1&foo[bar]=bar'), 'File exist check with query params ignored' ); - + unlink($tempFilePath); } - + public function testAbsoluteURL() { - + $rootURL = Director::protocolAndHost(); $_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/"; Config::inst()->update('Director', 'alternate_base_url', '/mysite/'); @@ -116,20 +108,20 @@ class DirectorTest extends SapphireTest { $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', true)); $this->assertEquals("$rootURL/test", Director::absoluteURL("$rootURL/test")); $this->assertEquals("$rootURL/test", Director::absoluteURL("$rootURL/test", true)); - + // Test relative to base $this->assertEquals("$rootURL/mysite/test", Director::absoluteURL("test", true)); $this->assertEquals("$rootURL/mysite/test/url", Director::absoluteURL("test/url", true)); $this->assertEquals("$rootURL/root", Director::absoluteURL("/root", true)); $this->assertEquals("$rootURL/root/url", Director::absoluteURL("/root/url", true)); - + // Test relative to requested page $this->assertEquals("$rootURL/mysite/sub-page/test", Director::absoluteURL("test")); // Legacy behaviour resolves this to $rootURL/mysite/test/url //$this->assertEquals("$rootURL/mysite/sub-page/test/url", Director::absoluteURL("test/url")); $this->assertEquals("$rootURL/root", Director::absoluteURL("/root")); $this->assertEquals("$rootURL/root/url", Director::absoluteURL("/root/url")); - + // Test that javascript links are not left intact $this->assertStringStartsNotWith('javascript', Director::absoluteURL('javascript:alert("attack")')); $this->assertStringStartsNotWith('alert', Director::absoluteURL('javascript:alert("attack")')); @@ -140,7 +132,7 @@ class DirectorTest extends SapphireTest { public function testAlternativeBaseURL() { // Get original protocol and hostname $rootURL = Director::protocolAndHost(); - + // relative base URLs - you should end them in a / Config::inst()->update('Director', 'alternate_base_url', '/relativebase/'); $_SERVER['REQUEST_URI'] = "$rootURL/relativebase/sub-page/"; @@ -180,7 +172,7 @@ class DirectorTest extends SapphireTest { Director::absoluteURL('subfolder/test') ); } - + /** * Tests that {@link Director::is_absolute()} works under different environment types */ @@ -196,12 +188,12 @@ class DirectorTest extends SapphireTest { 'folder' => false, 'a/c:/' => false ); - + foreach($expected as $path => $result) { $this->assertEquals(Director::is_absolute($path), $result, "Test result for $path"); } } - + public function testIsAbsoluteUrl() { $this->assertTrue(Director::is_absolute_url('http://test.com/testpage')); $this->assertTrue(Director::is_absolute_url('ftp://test.com')); @@ -218,7 +210,7 @@ class DirectorTest extends SapphireTest { $this->assertTrue(Director::is_absolute_url('http:test.com')); $this->assertTrue(Director::is_absolute_url('//http://test.com')); } - + public function testIsRelativeUrl() { $siteUrl = Director::absoluteBaseURL(); $this->assertFalse(Director::is_relative_url('http://test.com')); @@ -231,18 +223,18 @@ class DirectorTest extends SapphireTest { $this->assertTrue(Director::is_relative_url('/relative/?url=http://test.com')); $this->assertTrue(Director::is_relative_url('/relative/#=http://test.com')); } - + public function testMakeRelative() { $siteUrl = Director::absoluteBaseURL(); $siteUrlNoProtocol = preg_replace('/https?:\/\//', '', $siteUrl); - + $this->assertEquals(Director::makeRelative("$siteUrl"), ''); $this->assertEquals(Director::makeRelative("https://$siteUrlNoProtocol"), ''); $this->assertEquals(Director::makeRelative("http://$siteUrlNoProtocol"), ''); $this->assertEquals(Director::makeRelative(" $siteUrl/testpage "), 'testpage'); $this->assertEquals(Director::makeRelative("$siteUrlNoProtocol/testpage"), 'testpage'); - + $this->assertEquals(Director::makeRelative('ftp://test.com'), 'ftp://test.com'); $this->assertEquals(Director::makeRelative('http://test.com'), 'http://test.com'); @@ -252,7 +244,7 @@ class DirectorTest extends SapphireTest { $this->assertEquals("test", Director::makeRelative("https://".$siteUrlNoProtocol."/test")); $this->assertEquals("test", Director::makeRelative("http://".$siteUrlNoProtocol."/test")); } - + /** * Mostly tested by {@link testIsRelativeUrl()}, * just adding the host name matching aspect here. @@ -264,7 +256,7 @@ class DirectorTest extends SapphireTest { $this->assertFalse(Director::is_site_url("http://test.com?url=" . urlencode(Director::absoluteBaseURL()))); $this->assertFalse(Director::is_site_url("//test.com?url=" . Director::absoluteBaseURL())); } - + /** * Tests isDev, isTest, isLive set from querystring */ @@ -274,32 +266,32 @@ class DirectorTest extends SapphireTest { unset($_SESSION['isLive']); unset($_GET['isTest']); unset($_GET['isDev']); - + // Test isDev=1 $_GET['isDev'] = '1'; $this->assertTrue(Director::isDev()); $this->assertFalse(Director::isTest()); $this->assertFalse(Director::isLive()); - + // Test persistence unset($_GET['isDev']); $this->assertTrue(Director::isDev()); $this->assertFalse(Director::isTest()); $this->assertFalse(Director::isLive()); - + // Test change to isTest $_GET['isTest'] = '1'; $this->assertFalse(Director::isDev()); $this->assertTrue(Director::isTest()); $this->assertFalse(Director::isLive()); - + // Test persistence unset($_GET['isTest']); $this->assertFalse(Director::isDev()); $this->assertTrue(Director::isTest()); $this->assertFalse(Director::isLive()); } - + public function testResetGlobalsAfterTestRequest() { $_GET = array('somekey' => 'getvalue'); $_POST = array('somekey' => 'postvalue'); @@ -315,7 +307,7 @@ class DirectorTest extends SapphireTest { $this->assertEquals('cookievalue', $_COOKIE['somekey'], '$_COOKIE reset to original value after Director::test()'); } - + public function testTestRequestCarriesGlobals() { $fixture = array('somekey' => 'sometestvalue'); foreach(array('get', 'post') as $method) { @@ -329,20 +321,20 @@ class DirectorTest extends SapphireTest { } } } - + /** - * Tests that additional parameters specified in the routing table are - * saved in the request + * Tests that additional parameters specified in the routing table are + * saved in the request */ public function testRouteParams() { Director::test('en-nz/myaction/myid/myotherid', null, null, null, null, null, null, $request); - + $this->assertEquals( - $request->params(), + $request->params(), array( 'Controller' => 'DirectorTestRequest_Controller', - 'Action' => 'myaction', - 'ID' => 'myid', + 'Action' => 'myaction', + 'ID' => 'myid', 'OtherID' => 'myotherid', 'Locale' => 'en_NZ' ) @@ -406,7 +398,7 @@ class DirectorTest extends SapphireTest { 'CONTENT_TYPE' => 'text/xml', 'CONTENT_LENGTH' => 10 ); - + $headers = array( 'Host' => 'host', 'User-Agent' => 'User Agent', @@ -416,7 +408,7 @@ class DirectorTest extends SapphireTest { 'Content-Type' => 'text/xml', 'Content-Length' => '10' ); - + $this->assertEquals($headers, Director::extract_request_headers($request)); } @@ -483,34 +475,34 @@ class DirectorTest extends SapphireTest { $_SERVER = $origServer; } - + public function testRequestFilterInDirectorTest() { $filter = new TestRequestFilter; - + $processor = new RequestProcessor(array($filter)); - + Injector::inst()->registerService($processor, 'RequestProcessor'); - + $response = Director::test('some-dummy-url'); - + $this->assertEquals(1, $filter->preCalls); $this->assertEquals(1, $filter->postCalls); - + $filter->failPost = true; - + $this->setExpectedException('SS_HTTPResponse_Exception'); - + $response = Director::test('some-dummy-url'); - + $this->assertEquals(2, $filter->preCalls); $this->assertEquals(2, $filter->postCalls); - + $filter->failPre = true; - + $response = Director::test('some-dummy-url'); - + $this->assertEquals(3, $filter->preCalls); - + // preCall 'false' will trigger an exception and prevent post call execution $this->assertEquals(2, $filter->postCalls); } @@ -519,13 +511,13 @@ class DirectorTest extends SapphireTest { class TestRequestFilter implements RequestFilter, TestOnly { public $preCalls = 0; public $postCalls = 0; - + public $failPre = false; public $failPost = false; public function preRequest(\SS_HTTPRequest $request, \Session $session, \DataModel $model) { ++$this->preCalls; - + if ($this->failPre) { return false; } @@ -533,7 +525,7 @@ class TestRequestFilter implements RequestFilter, TestOnly { public function postRequest(\SS_HTTPRequest $request, \SS_HTTPResponse $response, \DataModel $model) { ++$this->postCalls; - + if ($this->failPost) { return false; } diff --git a/tests/core/ConfigTest.php b/tests/core/ConfigTest.php index 21ae3bf43..07b3490c2 100644 --- a/tests/core/ConfigTest.php +++ b/tests/core/ConfigTest.php @@ -82,23 +82,23 @@ class ConfigTest_TestNest extends Object implements TestOnly { } class ConfigTest extends SapphireTest { - + public function testNest() { - + // Check basic config $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); - + // Test nest copies data Config::nest(); $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); - + // Test nested data can be updated Config::inst()->update('ConfigTest_TestNest', 'foo', 4); $this->assertEquals(4, Config::inst()->get('ConfigTest_TestNest', 'foo')); $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); - + // Test unnest restores data Config::unnest(); $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); @@ -193,8 +193,8 @@ class ConfigTest extends SapphireTest { Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED)); // Subclasses that don't have the static explicitly defined should allow definition, also - // This also checks that set can be called after the first uninherited get() - // call (which can be buggy due to caching) + // This also checks that set can be called after the first uninherited get() + // call (which can be buggy due to caching) Config::inst()->update('ConfigStaticTest_Fourth', 'first', array('test_4b')); $this->assertContains('test_4b', Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED)); } @@ -227,7 +227,7 @@ class ConfigTest extends SapphireTest { $result = array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2), 'D' => 3); Config::merge_array_low_into_high($result, array('C' => array('Bar' => 3, 'Baz' => 4))); - $this->assertEquals($result, + $this->assertEquals($result, array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2, 'Baz' => 4), 'D' => 3)); $result = array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2), 'D' => 3); diff --git a/tests/core/manifest/ConfigManifestTest.php b/tests/core/manifest/ConfigManifestTest.php index e43dcf344..a1fac8424 100644 --- a/tests/core/manifest/ConfigManifestTest.php +++ b/tests/core/manifest/ConfigManifestTest.php @@ -391,7 +391,7 @@ class ConfigManifestTest extends SapphireTest { public function testEnvironmentRules() { foreach (array('dev', 'test', 'live') as $env) { - Config::inst()->nest(); + Config::nest(); Config::inst()->update('Director', 'environment_type', $env); $config = $this->getConfigFixtureValue('Environment'); @@ -403,13 +403,11 @@ class ConfigManifestTest extends SapphireTest { ); } - Config::inst()->unnest(); + Config::unnest(); } } public function testDynamicEnvironmentRules() { - Config::inst()->nest(); - // First, make sure environment_type is live Config::inst()->update('Director', 'environment_type', 'live'); $this->assertEquals('live', Config::inst()->get('Director', 'environment_type')); @@ -423,8 +421,6 @@ class ConfigManifestTest extends SapphireTest { // And that the dynamic rule was calculated correctly $this->assertEquals('dev', Config::inst()->get('ConfigManifestTest', 'DynamicEnvironment')); - - Config::inst()->unnest(); } public function testMultipleRules() { diff --git a/tests/dev/DevAdminControllerTest.php b/tests/dev/DevAdminControllerTest.php index c09b14684..8425e038e 100644 --- a/tests/dev/DevAdminControllerTest.php +++ b/tests/dev/DevAdminControllerTest.php @@ -1,17 +1,17 @@ -update('DevelopmentAdmin', 'registered_controllers', array( + Config::inst()->update('DevelopmentAdmin', 'registered_controllers', array( 'x1' => array( 'controller' => 'DevAdminControllerTest_Controller1', 'links' => array( @@ -27,45 +27,40 @@ class DevAdminControllerTest extends FunctionalTest { ) )); } - - public function tearDown(){ - parent::tearDown(); - Config::unnest(); - } - - + + public function testGoodRegisteredControllerOutput(){ - // Check for the controller running from the registered url above + // Check for the controller running from the registered url above // (we use contains rather than equals because sometimes you get Warning: You probably want to define an entry in $_FILE_TO_URL_MAPPING) $this->assertContains(DevAdminControllerTest_Controller1::OK_MSG, $this->getCapture('/dev/x1')); $this->assertContains(DevAdminControllerTest_Controller1::OK_MSG, $this->getCapture('/dev/x1/y1')); } - + public function testGoodRegisteredControllerStatus(){ // Check response code is 200/OK $this->assertEquals(false, $this->getAndCheckForError('/dev/x1')); $this->assertEquals(false, $this->getAndCheckForError('/dev/x1/y1')); - + // Check response code is 500/ some sort of error $this->assertEquals(true, $this->getAndCheckForError('/dev/x2')); } - - - + + + protected function getCapture($url){ $this->logInWithPermission('ADMIN'); - + ob_start(); $this->get($url); $r = ob_get_contents(); ob_end_clean(); - + return $r; } - + protected function getAndCheckForError($url){ $this->logInWithPermission('ADMIN'); - + if(Director::is_cli()){ // when in CLI the admin controller throws exceptions ob_start(); @@ -75,10 +70,10 @@ class DevAdminControllerTest extends FunctionalTest { ob_end_clean(); return true; } - + ob_end_clean(); return false; - + }else{ // when in http the admin controller sets a response header ob_start(); @@ -87,30 +82,30 @@ class DevAdminControllerTest extends FunctionalTest { return $resp->isError(); } } - + } class DevAdminControllerTest_Controller1 extends Controller { - + const OK_MSG = 'DevAdminControllerTest_Controller1 TEST OK'; - + private static $url_handlers = array( '' => 'index', 'y1' => 'y1Action' ); - + private static $allowed_actions = array( 'index', 'y1Action', ); - - + + public function index(){ echo self::OK_MSG; } - + public function y1Action(){ echo self::OK_MSG; - } - -} \ No newline at end of file + } + +} diff --git a/tests/model/DataObjectSchemaGenerationTest.php b/tests/model/DataObjectSchemaGenerationTest.php index c33cb7e36..76ca294eb 100644 --- a/tests/model/DataObjectSchemaGenerationTest.php +++ b/tests/model/DataObjectSchemaGenerationTest.php @@ -5,13 +5,13 @@ class DataObjectSchemaGenerationTest extends SapphireTest { 'DataObjectSchemaGenerationTest_DO', 'DataObjectSchemaGenerationTest_IndexDO' ); - + public function setUpOnce() { - + // enable fulltext option on this table Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'create_table_options', array('MySQLDatabase' => 'ENGINE=MyISAM')); - + parent::setUpOnce(); } @@ -23,7 +23,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest { DB::quiet(); // Table will have been initially created by the $extraDataObjects setting - + // Verify that it doesn't need to be recreated $db->beginSchemaUpdate(); $obj = new DataObjectSchemaGenerationTest_DO(); @@ -41,9 +41,8 @@ class DataObjectSchemaGenerationTest extends SapphireTest { DB::quiet(); // Table will have been initially created by the $extraDataObjects setting - + // Let's insert a new field here - Config::nest(); Config::inst()->update('DataObjectSchemaGenerationTest_DO', 'db', array( 'SecretField' => 'Varchar(100)' )); @@ -55,20 +54,17 @@ class DataObjectSchemaGenerationTest extends SapphireTest { $needsUpdating = $db->doesSchemaNeedUpdating(); $db->cancelSchemaUpdate(); $this->assertTrue($needsUpdating); - - // Restore db configuration - Config::unnest(); } - + /** - * Check that indexes on a newly generated class do not subsequently request modification + * Check that indexes on a newly generated class do not subsequently request modification */ public function testIndexesDontRerequestChanges() { $db = DB::getConn(); DB::quiet(); - + // Table will have been initially created by the $extraDataObjects setting - + // Verify that it doesn't need to be recreated $db->beginSchemaUpdate(); $obj = new DataObjectSchemaGenerationTest_IndexDO(); @@ -76,9 +72,8 @@ class DataObjectSchemaGenerationTest extends SapphireTest { $needsUpdating = $db->doesSchemaNeedUpdating(); $db->cancelSchemaUpdate(); $this->assertFalse($needsUpdating); - + // Test with alternate index format, although these indexes are the same - Config::nest(); Config::inst()->remove('DataObjectSchemaGenerationTest_IndexDO', 'indexes'); Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', Config::inst()->get('DataObjectSchemaGenerationTest_IndexDO', 'indexes_alt') @@ -91,22 +86,18 @@ class DataObjectSchemaGenerationTest extends SapphireTest { $needsUpdating = $db->doesSchemaNeedUpdating(); $db->cancelSchemaUpdate(); $this->assertFalse($needsUpdating); - - // Restore old index format - Config::unnest(); } - + /** * Check that updates to a dataobject's indexes are reflected in DDL */ public function testIndexesRerequestChanges() { $db = DB::getConn(); DB::quiet(); - + // Table will have been initially created by the $extraDataObjects setting - + // Update the SearchFields index here - Config::nest(); Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', array( 'SearchFields' => array( 'value' => 'Title' @@ -120,17 +111,14 @@ class DataObjectSchemaGenerationTest extends SapphireTest { $needsUpdating = $db->doesSchemaNeedUpdating(); $db->cancelSchemaUpdate(); $this->assertTrue($needsUpdating); - - // Restore old indexes - Config::unnest(); } - + /** * Tests the generation of the ClassName spec and ensure it's not unnecessarily influenced * by the order of classnames of existing records */ public function testClassNameSpecGeneration() { - + // Test with blank entries DataObject::clear_classname_spec_cache(); $fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO'); @@ -138,7 +126,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest { "Enum('DataObjectSchemaGenerationTest_DO, DataObjectSchemaGenerationTest_IndexDO')", $fields['ClassName'] ); - + // Test with instance of subclass $item1 = new DataObjectSchemaGenerationTest_IndexDO(); $item1->write(); @@ -149,7 +137,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest { $fields['ClassName'] ); $item1->delete(); - + // Test with instance of main class $item2 = new DataObjectSchemaGenerationTest_DO(); $item2->write(); @@ -160,7 +148,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest { $fields['ClassName'] ); $item2->delete(); - + // Test with instances of both classes $item1 = new DataObjectSchemaGenerationTest_IndexDO(); $item1->write(); @@ -199,7 +187,7 @@ class DataObjectSchemaGenerationTest_IndexDO extends DataObjectSchemaGenerationT 'value' => '"Title","Content"' ) ); - + /** @config */ private static $indexes_alt = array( 'NameIndex' => array( @@ -209,4 +197,4 @@ class DataObjectSchemaGenerationTest_IndexDO extends DataObjectSchemaGenerationT ), 'SearchFields' => 'fulltext ("Title","Content")' ); -} \ No newline at end of file +} diff --git a/tests/model/MySQLDatabaseTest.php b/tests/model/MySQLDatabaseTest.php index 83f7fb8f4..9b113fd5f 100644 --- a/tests/model/MySQLDatabaseTest.php +++ b/tests/model/MySQLDatabaseTest.php @@ -8,8 +8,9 @@ class MySQLDatabaseTest extends SapphireTest { protected $extraDataObjects = array( 'MySQLDatabaseTest_DO', ); - + public function setUp() { + parent::setUp(); if(DB::getConn() instanceof MySQLDatabase) { MySQLDatabaseTest_DO::config()->db = array( 'MultiEnum1' => 'MultiEnum("A, B, C, D","")', @@ -18,9 +19,8 @@ class MySQLDatabaseTest extends SapphireTest { ); } $this->markTestSkipped('This test requires the Config API to be immutable'); - parent::setUp(); } - + /** * Check that once a schema has been generated, then it doesn't need any more updating */ diff --git a/tests/oembed/OembedTest.php b/tests/oembed/OembedTest.php index 3570f7bb6..73d94c49b 100644 --- a/tests/oembed/OembedTest.php +++ b/tests/oembed/OembedTest.php @@ -1,16 +1,6 @@ update('Oembed', 'providers', array( 'http://*.silverstripe.com/watch*'=>'http://www.silverstripe.com/oembed/' @@ -35,7 +25,7 @@ class OembedTest extends SapphireTest { $result = Oembed::get_oembed_from_url('http://www.silverstripe.com/watch12345', false, array('foo'=>'bar')); $this->assertTrue($result!=false); $this->assertEquals($result->getOembedURL(), 'http://www.silverstripe.com/oembed/?format=json&url=' - . urlencode('http://www.silverstripe.com/watch12345').'&foo=bar', + . urlencode('http://www.silverstripe.com/watch12345').'&foo=bar', 'Includes options'); // Test magic. diff --git a/tests/security/BasicAuthTest.php b/tests/security/BasicAuthTest.php index 8be7ce73e..62b2479fa 100644 --- a/tests/security/BasicAuthTest.php +++ b/tests/security/BasicAuthTest.php @@ -14,46 +14,40 @@ class BasicAuthTest extends FunctionalTest { parent::setUp(); // Fixtures assume Email is the field used to identify the log in identity - Config::nest(); Member::config()->unique_identifier_field = 'Email'; Member::config()->lock_out_after_incorrect_logins = 10; } - public function tearDown() { - Config::unnest(); - parent::tearDown(); - } - public function testBasicAuthEnabledWithoutLogin() { $origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; $origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; - + unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_PW']); - + $response = Director::test('BasicAuthTest_ControllerSecuredWithPermission'); $this->assertEquals(401, $response->getStatusCode()); - + $_SERVER['PHP_AUTH_USER'] = $origUser; $_SERVER['PHP_AUTH_PW'] = $origPw; } - + public function testBasicAuthDoesntCallActionOrFurtherInitOnAuthFailure() { $origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; $origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; - + unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_PW']); $response = Director::test('BasicAuthTest_ControllerSecuredWithPermission'); $this->assertFalse(BasicAuthTest_ControllerSecuredWithPermission::$index_called); $this->assertFalse(BasicAuthTest_ControllerSecuredWithPermission::$post_init_called); - + $_SERVER['PHP_AUTH_USER'] = 'user-in-mygroup@test.com'; $_SERVER['PHP_AUTH_PW'] = 'test'; $response = Director::test('BasicAuthTest_ControllerSecuredWithPermission'); $this->assertTrue(BasicAuthTest_ControllerSecuredWithPermission::$index_called); $this->assertTrue(BasicAuthTest_ControllerSecuredWithPermission::$post_init_called); - + $_SERVER['PHP_AUTH_USER'] = $origUser; $_SERVER['PHP_AUTH_PW'] = $origPw; } @@ -61,45 +55,45 @@ class BasicAuthTest extends FunctionalTest { public function testBasicAuthEnabledWithPermission() { $origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; $origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; - + $_SERVER['PHP_AUTH_USER'] = 'user-in-mygroup@test.com'; $_SERVER['PHP_AUTH_PW'] = 'wrongpassword'; $response = Director::test('BasicAuthTest_ControllerSecuredWithPermission'); $this->assertEquals(401, $response->getStatusCode(), 'Invalid users dont have access'); - + $_SERVER['PHP_AUTH_USER'] = 'user-without-groups@test.com'; $_SERVER['PHP_AUTH_PW'] = 'test'; $response = Director::test('BasicAuthTest_ControllerSecuredWithPermission'); $this->assertEquals(401, $response->getStatusCode(), 'Valid user without required permission has no access'); - + $_SERVER['PHP_AUTH_USER'] = 'user-in-mygroup@test.com'; $_SERVER['PHP_AUTH_PW'] = 'test'; $response = Director::test('BasicAuthTest_ControllerSecuredWithPermission'); $this->assertEquals(200, $response->getStatusCode(), 'Valid user with required permission has access'); - + $_SERVER['PHP_AUTH_USER'] = $origUser; $_SERVER['PHP_AUTH_PW'] = $origPw; } - + public function testBasicAuthEnabledWithoutPermission() { $origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; $origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; - + $_SERVER['PHP_AUTH_USER'] = 'user-without-groups@test.com'; $_SERVER['PHP_AUTH_PW'] = 'wrongpassword'; $response = Director::test('BasicAuthTest_ControllerSecuredWithoutPermission'); $this->assertEquals(401, $response->getStatusCode(), 'Invalid users dont have access'); - + $_SERVER['PHP_AUTH_USER'] = 'user-without-groups@test.com'; $_SERVER['PHP_AUTH_PW'] = 'test'; $response = Director::test('BasicAuthTest_ControllerSecuredWithoutPermission'); $this->assertEquals(200, $response->getStatusCode(), 'All valid users have access'); - + $_SERVER['PHP_AUTH_USER'] = 'user-in-mygroup@test.com'; $_SERVER['PHP_AUTH_PW'] = 'test'; $response = Director::test('BasicAuthTest_ControllerSecuredWithoutPermission'); $this->assertEquals(200, $response->getStatusCode(), 'All valid users have access'); - + $_SERVER['PHP_AUTH_USER'] = $origUser; $_SERVER['PHP_AUTH_PW'] = $origPw; } @@ -131,23 +125,23 @@ class BasicAuthTest extends FunctionalTest { } class BasicAuthTest_ControllerSecuredWithPermission extends Controller implements TestOnly { - + static $post_init_called = false; - + static $index_called = false; protected $template = 'BlankPage'; - + public function init() { self::$post_init_called = false; self::$index_called = false; - + BasicAuth::protect_entire_site(true, 'MYCODE'); parent::init(); - + self::$post_init_called = true; } - + public function index() { self::$index_called = true; } @@ -164,5 +158,5 @@ class BasicAuthTest_ControllerSecuredWithoutPermission extends Controller implem BasicAuth::protect_entire_site(true, null); parent::init(); } - + } From 28be51cab0b567b692632503e0f440d30a2fe09e Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Sat, 25 Oct 2014 22:28:28 +0100 Subject: [PATCH 2/3] FIX: Config state leaking between unit tests --- tests/core/ConfigTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/core/ConfigTest.php b/tests/core/ConfigTest.php index 07b3490c2..e0abcaa8a 100644 --- a/tests/core/ConfigTest.php +++ b/tests/core/ConfigTest.php @@ -189,8 +189,7 @@ class ConfigTest extends SapphireTest { // But it won't affect subclasses - this is *uninherited* static $this->assertNotContains('test_2b', Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED)); - $this->assertNotContains('test_2b', - Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED)); + $this->assertNull(Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED)); // Subclasses that don't have the static explicitly defined should allow definition, also // This also checks that set can be called after the first uninherited get() From f21427d7fa309dcc6229cca79c9f73f60564a7b3 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Sat, 7 Mar 2015 10:32:17 +0000 Subject: [PATCH 3/3] DOCS Explaining test suite nesting --- dev/SapphireTest.php | 2 +- .../06_Testing/00_Unit_Testing.md | 38 ++++++++++++++----- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/dev/SapphireTest.php b/dev/SapphireTest.php index c0de44d29..aacb86c18 100644 --- a/dev/SapphireTest.php +++ b/dev/SapphireTest.php @@ -347,7 +347,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase { Injector::unnest(); Config::unnest(); - if($this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) { + if(!empty($this->extensionsToReapply) || !empty($this->extensionsToRemove) || !empty($this->extraDataObjects)) { $this->resetDBSchema(); } } diff --git a/docs/en/02_Developer_Guides/06_Testing/00_Unit_Testing.md b/docs/en/02_Developer_Guides/06_Testing/00_Unit_Testing.md index 1771e640b..7afd1894c 100644 --- a/docs/en/02_Developer_Guides/06_Testing/00_Unit_Testing.md +++ b/docs/en/02_Developer_Guides/06_Testing/00_Unit_Testing.md @@ -182,18 +182,10 @@ end of each test. $page->publish('Stage', 'Live'); } - // reset configuration for the test. - Config::nest(); + // set custom configuration for the test. Config::inst()->update('Foo', 'bar', 'Hello!'); } - public function tearDown() { - // restores the config variables - Config::unnest(); - - parent::tearDown(); - } - public function testMyMethod() { // .. } @@ -223,6 +215,32 @@ individual test case. // .. } } + +### Config and Injector Nesting + +A powerful feature of both [`Config`](/developer_guides/configuration/configuration/) and [`Injector`](/developer_guides/extending/injector/) is the ability to "nest" them so that you can make changes that can easily be discarded without having to manage previous values. + +The testing suite makes use of this to "sandbox" each of the unit tests as well as each suite to prevent leakage between tests. + +If you need to make changes to `Config` (or `Injector) for each test (or the whole suite) you can safely update `Config` (or `Injector`) settings in the `setUp` or `tearDown` functions. + +It's important to remember that the `parent::setUp();` functions will need to be called first to ensure the nesting feature works as expected. + + :::php + function setUpOnce() { + parent::setUpOnce(); + //this will remain for the whole suite and be removed for any other tests + Config::inst()->update('ClassName', 'var_name', 'var_value'); + } + + function testFeatureDoesAsExpected() { + //this will be reset to 'var_value' at the end of this test function + Config::inst()->update('ClassName', 'var_name', 'new_var_value'); + } + + function testAnotherFeatureDoesAsExpected() { + Config::inst()->get('ClassName', 'var_name'); // this will be 'var_value' + } ## Generating a Coverage Report @@ -266,4 +284,4 @@ some `thirdparty/` directories add the following to the `phpunit.xml` configurat * [api:TestRunner] * [api:SapphireTest] -* [api:FunctionalTest] \ No newline at end of file +* [api:FunctionalTest]