mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
698 lines
23 KiB
PHP
698 lines
23 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\Core\Tests\Manifest;
|
|
|
|
use SilverStripe\Control\Director;
|
|
use SilverStripe\Core\Config\Config;
|
|
use SilverStripe\Core\Manifest\ConfigManifest;
|
|
use SilverStripe\Dev\SapphireTest;
|
|
use ReflectionProperty;
|
|
use Zend_Cache_Core;
|
|
|
|
class ConfigManifestTest extends SapphireTest
|
|
{
|
|
/**
|
|
* This is a helper method for getting a new manifest
|
|
*
|
|
* @param string $name
|
|
* @return mixed
|
|
*/
|
|
protected function getConfigFixtureValue($name)
|
|
{
|
|
$manifest = new ConfigManifest(dirname(__FILE__).'/fixtures/configmanifest', true, true);
|
|
return $manifest->get(__CLASS__, $name);
|
|
}
|
|
|
|
/**
|
|
* This is a helper method for displaying a relevant message about a parsing failure
|
|
*/
|
|
protected function getParsedAsMessage($path)
|
|
{
|
|
return sprintf('Reference path "%s" failed to parse correctly', $path);
|
|
}
|
|
|
|
/**
|
|
* A helper method to return a mock of the cache in order to test expectations and reduce dependency
|
|
*
|
|
* @return Zend_Cache_Core
|
|
*/
|
|
protected function getCacheMock()
|
|
{
|
|
return $this->getMock(
|
|
'Zend_Cache_Core',
|
|
array('load', 'save'),
|
|
array(),
|
|
'',
|
|
false
|
|
);
|
|
}
|
|
|
|
/**
|
|
* A helper method to return a mock of the manifest in order to test expectations and reduce dependency
|
|
*
|
|
* @param $methods
|
|
* @return ConfigManifest
|
|
*/
|
|
protected function getManifestMock($methods)
|
|
{
|
|
return $this->getMock(
|
|
ConfigManifest::class,
|
|
$methods,
|
|
array(), // no constructor arguments
|
|
'', // default
|
|
false // don't call the constructor
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test the caching functionality when we are forcing regeneration
|
|
*
|
|
* 1. Test that regenerate is called in the default case and that cache->load isn't
|
|
* 2. Test that save is called correctly after the regeneration
|
|
*/
|
|
public function testCachingForceRegeneration()
|
|
{
|
|
// Test that regenerate is called correctly.
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
$manifest->expects($this->once()) // regenerate should be called once
|
|
->method('regenerate')
|
|
->with($this->equalTo(true)); // includeTests = true
|
|
|
|
// Set up a cache where we expect load to never be called
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->never())
|
|
->method('load');
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->__construct(dirname(__FILE__).'/fixtures/configmanifest', true, true);
|
|
|
|
// Test that save is called correctly
|
|
$manifest = $this->getManifestMock(array('getCache'));
|
|
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->atLeastOnce())
|
|
->method('save');
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->__construct(dirname(__FILE__).'/fixtures/configmanifest', true, true);
|
|
}
|
|
|
|
/**
|
|
* Test the caching functionality when we are not forcing regeneration
|
|
*
|
|
* 1. Test that load is called
|
|
* 2. Test the regenerate is called when the cache is unprimed
|
|
* 3. Test that when there is a value in the cache regenerate isn't called
|
|
*/
|
|
public function testCachingNotForceRegeneration()
|
|
{
|
|
// Test that load is called
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
// Load should be called twice
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->exactly(2))
|
|
->method('load');
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->__construct(dirname(__FILE__).'/fixtures/configmanifest', true, false);
|
|
|
|
|
|
// Now test that regenerate is called because the cache is unprimed
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->exactly(2))
|
|
->method('load')
|
|
->will($this->onConsecutiveCalls(false, false));
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->expects($this->once())
|
|
->method('regenerate')
|
|
->with($this->equalTo(false)); //includeTests = false
|
|
|
|
$manifest->__construct(dirname(__FILE__).'/fixtures/configmanifest', false, false);
|
|
|
|
// Now test that when there is a value in the cache that regenerate isn't called
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->exactly(2))
|
|
->method('load')
|
|
->will($this->onConsecutiveCalls(array(), array()));
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->expects($this->never())
|
|
->method('regenerate');
|
|
|
|
$manifest->__construct(dirname(__FILE__).'/fixtures/configmanifest', false, false);
|
|
}
|
|
|
|
/**
|
|
* Test cache regeneration if all or some of the cache files are missing
|
|
*
|
|
* 1. Test regeneration if all cache files are missing
|
|
* 2. Test regeneration if 'variant_key_spec' cache file is missing
|
|
* 3. Test regeneration if 'php_config_sources' cache file is missing
|
|
*/
|
|
public function testAutomaticCacheRegeneration()
|
|
{
|
|
$base = dirname(__FILE__) . '/fixtures/configmanifest';
|
|
|
|
// Test regeneration if all cache files are missing
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
$manifest->expects($this->once())// regenerate should be called once
|
|
->method('regenerate')
|
|
->with($this->equalTo(false)); // includeTests = false
|
|
|
|
// Set up a cache where we expect load to never be called
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->exactly(2))
|
|
->will($this->returnValue(false))
|
|
->method('load');
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->__construct($base);
|
|
|
|
// Test regeneration if 'variant_key_spec' cache file is missing
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
$manifest->expects($this->once())// regenerate should be called once
|
|
->method('regenerate')
|
|
->with($this->equalTo(false)); // includeTests = false
|
|
|
|
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->exactly(2))
|
|
->method('load')
|
|
->will($this->returnCallback(function ($parameter) {
|
|
if (strpos($parameter, 'variant_key_spec') !== false) {
|
|
return false;
|
|
}
|
|
return array();
|
|
}));
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->__construct($base);
|
|
|
|
// Test regeneration if 'php_config_sources' cache file is missing
|
|
$manifest = $this->getManifestMock(array('getCache', 'regenerate', 'buildYamlConfigVariant'));
|
|
|
|
$manifest->expects($this->once())// regenerate should be called once
|
|
->method('regenerate')
|
|
->with($this->equalTo(false)); // includeTests = false
|
|
|
|
$cache = $this->getCacheMock();
|
|
$cache->expects($this->exactly(2))
|
|
->method('load')
|
|
->will($this->returnCallback(function ($parameter) {
|
|
if (strpos($parameter, 'php_config_sources') !== false) {
|
|
return false;
|
|
}
|
|
return array();
|
|
}));
|
|
|
|
$manifest->expects($this->any())
|
|
->method('getCache')
|
|
->will($this->returnValue($cache));
|
|
|
|
$manifest->__construct($base);
|
|
}
|
|
|
|
/**
|
|
* This test checks the processing of before and after reference paths (module-name/filename#fragment)
|
|
* This method uses fixture/configmanifest/mysite/_config/addyamlconfigfile.yml as a fixture
|
|
*/
|
|
public function testAddYAMLConfigFileReferencePathParsing()
|
|
{
|
|
// Use a mock to avoid testing unrelated functionality
|
|
$manifest = $this->getManifestMock(array('addModule'));
|
|
|
|
// This tests that the addModule method is called with the correct value
|
|
$manifest->expects($this->once())
|
|
->method('addModule')
|
|
->with($this->equalTo(dirname(__FILE__).'/fixtures/configmanifest/mysite'));
|
|
|
|
// Call the method to be tested
|
|
$manifest->addYAMLConfigFile(
|
|
'addyamlconfigfile.yml',
|
|
dirname(__FILE__).'/fixtures/configmanifest/mysite/_config/addyamlconfigfile.yml',
|
|
false
|
|
);
|
|
|
|
// There is no getter for yamlConfigFragments
|
|
$property = new ReflectionProperty('SilverStripe\\Core\\Manifest\\ConfigManifest', 'yamlConfigFragments');
|
|
$property->setAccessible(true);
|
|
|
|
// Get the result back from the parsing
|
|
$result = $property->getValue($manifest);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => 'mysite',
|
|
'file' => 'testfile',
|
|
'name' => 'fragment',
|
|
),
|
|
),
|
|
@$result[0]['after'],
|
|
$this->getParsedAsMessage('mysite/testfile#fragment')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => 'test-module',
|
|
'file' => 'testfile',
|
|
'name' => 'fragment',
|
|
),
|
|
),
|
|
@$result[1]['after'],
|
|
$this->getParsedAsMessage('test-module/testfile#fragment')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => '*',
|
|
'file' => '*',
|
|
'name' => '*',
|
|
),
|
|
),
|
|
@$result[2]['after'],
|
|
$this->getParsedAsMessage('*')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => '*',
|
|
'file' => 'testfile',
|
|
'name' => '*'
|
|
),
|
|
),
|
|
@$result[3]['after'],
|
|
$this->getParsedAsMessage('*/testfile')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => '*',
|
|
'file' => '*',
|
|
'name' => 'fragment'
|
|
),
|
|
),
|
|
@$result[4]['after'],
|
|
$this->getParsedAsMessage('*/*#fragment')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => '*',
|
|
'file' => '*',
|
|
'name' => 'fragment'
|
|
),
|
|
),
|
|
@$result[5]['after'],
|
|
$this->getParsedAsMessage('#fragment')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => 'test-module',
|
|
'file' => '*',
|
|
'name' => 'fragment'
|
|
),
|
|
),
|
|
@$result[6]['after'],
|
|
$this->getParsedAsMessage('test-module#fragment')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => 'test-module',
|
|
'file' => '*',
|
|
'name' => '*'
|
|
),
|
|
),
|
|
@$result[7]['after'],
|
|
$this->getParsedAsMessage('test-module')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => 'test-module',
|
|
'file' => '*',
|
|
'name' => '*'
|
|
),
|
|
),
|
|
@$result[8]['after'],
|
|
$this->getParsedAsMessage('test-module/*')
|
|
);
|
|
|
|
$this->assertEquals(
|
|
array(
|
|
array(
|
|
'module' => 'test-module',
|
|
'file' => '*',
|
|
'name' => '*'
|
|
),
|
|
),
|
|
@$result[9]['after'],
|
|
$this->getParsedAsMessage('test-module/*/#*')
|
|
);
|
|
}
|
|
|
|
public function testClassRules()
|
|
{
|
|
$config = $this->getConfigFixtureValue('Class');
|
|
|
|
$this->assertEquals(
|
|
'Yes',
|
|
@$config['DirectorExists'],
|
|
'Only rule correctly detects existing class'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['NoSuchClassExists'],
|
|
'Except rule correctly detects missing class'
|
|
);
|
|
}
|
|
|
|
public function testModuleRules()
|
|
{
|
|
$config = $this->getConfigFixtureValue('Module');
|
|
|
|
$this->assertEquals(
|
|
'Yes',
|
|
@$config['MysiteExists'],
|
|
'Only rule correctly detects existing module'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['NoSuchModuleExists'],
|
|
'Except rule correctly detects missing module'
|
|
);
|
|
}
|
|
|
|
public function testEnvVarSetRules()
|
|
{
|
|
$loader = new \Dotenv\Loader(null);
|
|
|
|
$loader->setEnvironmentVariable('ENVVARSET_FOO', 1);
|
|
$config = $this->getConfigFixtureValue('EnvVarSet');
|
|
|
|
$this->assertEquals(
|
|
'Yes',
|
|
@$config['FooSet'],
|
|
'Only rule correctly detects set environment variable'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['BarSet'],
|
|
'Except rule correctly detects unset environment variable'
|
|
);
|
|
}
|
|
|
|
public function testConstantDefinedRules()
|
|
{
|
|
define('CONSTANTDEFINED_FOO', 1);
|
|
$config = $this->getConfigFixtureValue('ConstantDefined');
|
|
|
|
$this->assertEquals(
|
|
'Yes',
|
|
@$config['FooDefined'],
|
|
'Only rule correctly detects defined constant'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['BarDefined'],
|
|
'Except rule correctly detects undefined constant'
|
|
);
|
|
}
|
|
|
|
public function testEnvOrConstantMatchesValueRules()
|
|
{
|
|
$loader = new \Dotenv\Loader(null);
|
|
|
|
$loader->setEnvironmentVariable('ENVORCONSTANTMATCHESVALUE_FOO', 'Foo');
|
|
define('ENVORCONSTANTMATCHESVALUE_BAR', 'Bar');
|
|
$config = $this->getConfigFixtureValue('EnvOrConstantMatchesValue');
|
|
|
|
$this->assertEquals(
|
|
'Yes',
|
|
@$config['FooIsFoo'],
|
|
'Only rule correctly detects environment variable matches specified value'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'Yes',
|
|
@$config['BarIsBar'],
|
|
'Only rule correctly detects constant matches specified value'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['FooIsQux'],
|
|
'Except rule correctly detects environment variable that doesn\'t match specified value'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['BarIsQux'],
|
|
'Except rule correctly detects environment variable that doesn\'t match specified value'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'No',
|
|
@$config['BazIsBaz'],
|
|
'Except rule correctly detects undefined variable'
|
|
);
|
|
}
|
|
|
|
public function testEnvironmentRules()
|
|
{
|
|
foreach (array('dev', 'test', 'live') as $env) {
|
|
Config::nest();
|
|
|
|
Config::inst()->update('SilverStripe\\Control\\Director', 'environment_type', $env);
|
|
$config = $this->getConfigFixtureValue('Environment');
|
|
|
|
foreach (array('dev', 'test', 'live') as $check) {
|
|
$this->assertEquals(
|
|
$env == $check ? $check : 'not'.$check,
|
|
@$config[ucfirst($check).'Environment'],
|
|
'Only & except rules correctly detect environment'
|
|
);
|
|
}
|
|
|
|
Config::unnest();
|
|
}
|
|
}
|
|
|
|
public function testDynamicEnvironmentRules()
|
|
{
|
|
// First, make sure environment_type is live
|
|
Director::config()->update('environment_type', 'live');
|
|
$this->assertEquals('live', Director::config()->get('environment_type'));
|
|
|
|
// Then, load in a new manifest, which includes a _config.php that sets environment_type to dev
|
|
$manifest = new ConfigManifest(dirname(__FILE__).'/fixtures/configmanifest_dynamicenv', true, true);
|
|
Config::inst()->pushConfigYamlManifest($manifest);
|
|
|
|
// Make sure that stuck
|
|
$this->assertEquals('dev', Director::config()->get('environment_type'));
|
|
|
|
// And that the dynamic rule was calculated correctly
|
|
$this->assertEquals('dev', Config::inst()->get(__CLASS__, 'DynamicEnvironment'));
|
|
}
|
|
|
|
public function testMultipleRules()
|
|
{
|
|
$loader = new \Dotenv\Loader(null);
|
|
|
|
$loader->setEnvironmentVariable('MULTIPLERULES_ENVVARIABLESET', 1);
|
|
define('MULTIPLERULES_DEFINEDCONSTANT', 'defined');
|
|
$config = $this->getConfigFixtureValue('MultipleRules');
|
|
|
|
$this->assertFalse(
|
|
isset($config['TwoOnlyFail']),
|
|
'Fragment is not included if one of the Only rules fails.'
|
|
);
|
|
|
|
$this->assertTrue(
|
|
isset($config['TwoOnlySucceed']),
|
|
'Fragment is included if both Only rules succeed.'
|
|
);
|
|
|
|
$this->assertTrue(
|
|
isset($config['TwoExceptSucceed']),
|
|
'Fragment is included if one of the Except rules matches.'
|
|
);
|
|
|
|
$this->assertFalse(
|
|
isset($config['TwoExceptFail']),
|
|
'Fragment is not included if both of the Except rules fail.'
|
|
);
|
|
|
|
$this->assertFalse(
|
|
isset($config['TwoBlocksFail']),
|
|
'Fragment is not included if one block fails.'
|
|
);
|
|
|
|
$this->assertTrue(
|
|
isset($config['TwoBlocksSucceed']),
|
|
'Fragment is included if both blocks succeed.'
|
|
);
|
|
}
|
|
|
|
public function testRelativeOrder()
|
|
{
|
|
$accessor = new ConfigManifestTest\ConfigManifestAccess(BASE_PATH, true, false);
|
|
|
|
// A fragment with a fully wildcard before rule
|
|
$beforeWildcarded = array(
|
|
'module' => 'foo', 'file' => 'alpha', 'name' => '1',
|
|
'before' => array(array('module' => '*', 'file' => '*', 'name' => '*'))
|
|
);
|
|
// A fragment with a fully wildcard before rule and a fully explicit after rule
|
|
$beforeWildcardedAfterExplicit = array_merge(
|
|
$beforeWildcarded,
|
|
array(
|
|
'after' => array(array('module' => 'bar', 'file' => 'beta', 'name' => '2'))
|
|
)
|
|
);
|
|
// A fragment with a fully wildcard before rule and two fully explicit after rules
|
|
$beforeWildcardedAfterTwoExplicitRules = array_merge(
|
|
$beforeWildcarded,
|
|
array(
|
|
'after' => array(
|
|
array('module' => 'bar', 'file' => 'beta', 'name' => '2'),
|
|
array('module' => 'baz', 'file' => 'gamma', 'name' => '3')
|
|
)
|
|
)
|
|
);
|
|
// A fragment with a fully wildcard before rule and a partially explicit after rule
|
|
$beforeWildcardedAfterPartialWildcarded = array_merge(
|
|
$beforeWildcarded,
|
|
array(
|
|
'after' => array(array('module' => 'bar', 'file' => 'beta', 'name' => '*'))
|
|
)
|
|
);
|
|
|
|
// Wildcard should match any module
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcarded,
|
|
array('module' => 'qux', 'file' => 'delta', 'name' => '4')
|
|
),
|
|
'before'
|
|
);
|
|
|
|
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterExplicit,
|
|
array('module' => 'qux', 'file' => 'delta', 'name' => '4')
|
|
),
|
|
'before'
|
|
);
|
|
|
|
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match, no
|
|
// matter how many opposing rules
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterExplicit,
|
|
array('module' => 'qux', 'file' => 'delta', 'name' => '4')
|
|
),
|
|
'before'
|
|
);
|
|
|
|
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match
|
|
// (even if some portions do)
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterExplicit,
|
|
array('module' => 'bar', 'file' => 'beta', 'name' => 'nomatchy')
|
|
),
|
|
'before'
|
|
);
|
|
|
|
// When opposing rule matches, wildcard should be ignored
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterExplicit,
|
|
array('module' => 'bar', 'file' => 'beta', 'name' => '2')
|
|
),
|
|
'after'
|
|
);
|
|
|
|
// When any one of mutiple opposing rule exists, wildcard should be ignored
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterTwoExplicitRules,
|
|
array('module' => 'bar', 'file' => 'beta', 'name' => '2')
|
|
),
|
|
'after'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterTwoExplicitRules,
|
|
array('module' => 'baz', 'file' => 'gamma', 'name' => '3')
|
|
),
|
|
'after'
|
|
);
|
|
|
|
// When two opposed wildcard rules, and more specific one doesn't match, other should win
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterPartialWildcarded,
|
|
array('module' => 'qux', 'file' => 'delta', 'name' => '4')
|
|
),
|
|
'before'
|
|
);
|
|
|
|
// When two opposed wildcard rules, and more specific one does match, more specific one should win
|
|
$this->assertEquals(
|
|
$accessor->relativeOrder(
|
|
$beforeWildcardedAfterPartialWildcarded,
|
|
array('module' => 'bar', 'file' => 'beta', 'name' => 'wildcardmatchy')
|
|
),
|
|
'after'
|
|
);
|
|
}
|
|
}
|