This shows a warning message when maintenance scripts are run directly. maintenance/run.php should be used instead. Change-Id: Ibd47e3f29f93238ccd3e607774927e639ba74ace
615 lines
18 KiB
PHP
615 lines
18 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Tests\Structure;
|
|
|
|
use ExtensionRegistry;
|
|
use MediaWiki\MainConfigNames;
|
|
use MediaWiki\MainConfigSchema;
|
|
use MediaWiki\Settings\Config\ArrayConfigBuilder;
|
|
use MediaWiki\Settings\Config\PhpIniSink;
|
|
use MediaWiki\Settings\SettingsBuilder;
|
|
use MediaWiki\Settings\Source\FileSource;
|
|
use MediaWiki\Settings\Source\JsonSchemaTrait;
|
|
use MediaWiki\Settings\Source\PhpSettingsSource;
|
|
use MediaWiki\Settings\Source\ReflectionSchemaSource;
|
|
use MediaWiki\Settings\Source\SettingsSource;
|
|
use MediaWiki\Shell\Shell;
|
|
use MediaWikiIntegrationTestCase;
|
|
|
|
/**
|
|
* @coversNothing
|
|
*/
|
|
class SettingsTest extends MediaWikiIntegrationTestCase {
|
|
use JsonSchemaTrait;
|
|
|
|
/**
|
|
* Returns the main configuration schema as a settings array.
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getSchemaData(): array {
|
|
$source = new ReflectionSchemaSource( MainConfigSchema::class, true );
|
|
$settings = $source->load();
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* @return SettingsBuilder
|
|
*/
|
|
private function getSettingsBuilderWithSchema(): SettingsBuilder {
|
|
$configBuilder = new ArrayConfigBuilder();
|
|
$settingsBuilder = new SettingsBuilder(
|
|
__DIR__ . '/../../..',
|
|
$this->createNoOpMock( ExtensionRegistry::class ),
|
|
$configBuilder,
|
|
$this->createNoOpMock( PhpIniSink::class )
|
|
);
|
|
$settingsBuilder->loadArray( self::getSchemaData() );
|
|
return $settingsBuilder;
|
|
}
|
|
|
|
public function testConfigSchemaIsLoadable() {
|
|
$settingsBuilder = $this->getSettingsBuilderWithSchema();
|
|
$settingsBuilder->apply();
|
|
|
|
// Assert we've read some random config value
|
|
$this->assertTrue( $settingsBuilder->getConfig()->has( MainConfigNames::Server ) );
|
|
}
|
|
|
|
/**
|
|
* Check that core default settings validate against the schema
|
|
*/
|
|
public function testConfigSchemaDefaultsValidate() {
|
|
$settingsBuilder = $this->getSettingsBuilderWithSchema();
|
|
$validationResult = $settingsBuilder->apply()->validate();
|
|
$this->assertStatusOK( $validationResult );
|
|
}
|
|
|
|
/**
|
|
* Check that currently loaded settings validate against the schema.
|
|
*/
|
|
public function testCurrentSettingsValidate() {
|
|
$validationResult = SettingsBuilder::getInstance()->validate();
|
|
$this->assertStatusOK( $validationResult );
|
|
}
|
|
|
|
/**
|
|
* Check that currently loaded config does not use deprecated settings.
|
|
*/
|
|
public function testCurrentSettingsNotDeprecated() {
|
|
$deprecations = SettingsBuilder::getInstance()->detectDeprecatedConfig();
|
|
$this->assertEquals( [], $deprecations );
|
|
}
|
|
|
|
/**
|
|
* Check that currently loaded config does not use obsolete settings.
|
|
*/
|
|
public function testCurrentSettingsNotObsolete() {
|
|
$obsolete = SettingsBuilder::getInstance()->detectObsoleteConfig();
|
|
$this->assertEquals( [], $obsolete );
|
|
}
|
|
|
|
/**
|
|
* Check that currently loaded config does not have warnings.
|
|
*/
|
|
public function testCurrentSettingsHaveNoWarnings() {
|
|
$deprecations = SettingsBuilder::getInstance()->getWarnings();
|
|
$this->assertEquals( [], $deprecations );
|
|
}
|
|
|
|
public function provideConfigGeneration() {
|
|
yield 'includes/config-schema.php' => [
|
|
'option' => '--schema',
|
|
'expectedFile' => MW_INSTALL_PATH . '/includes/config-schema.php',
|
|
];
|
|
yield 'includes/config-vars.php' => [
|
|
'option' => '--vars',
|
|
'expectedFile' => MW_INSTALL_PATH . '/includes/config-vars.php',
|
|
];
|
|
yield 'docs/config-schema.yaml' => [
|
|
'option' => '--yaml',
|
|
'expectedFile' => MW_INSTALL_PATH . '/docs/config-schema.yaml',
|
|
];
|
|
yield 'includes/MainConfigNames.php' => [
|
|
'option' => '--names',
|
|
'expectedFile' => MW_INSTALL_PATH . '/includes/MainConfigNames.php',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConfigGeneration
|
|
*/
|
|
public function testConfigGeneration( string $option, string $expectedFile ) {
|
|
$script = MW_INSTALL_PATH . '/maintenance/run.php';
|
|
$schemaGenerator = Shell::makeScriptCommand( $script, [ 'GenerateConfigSchema', $option, 'php://stdout' ] );
|
|
$result = $schemaGenerator->execute();
|
|
$this->assertSame(
|
|
0,
|
|
$result->getExitCode(),
|
|
'Config generation must finish successfully.' . "\n" . $result->getStderr()
|
|
);
|
|
|
|
$errors = $result->getStderr();
|
|
$errors = preg_replace( '/^Xdebug:.*\n/m', '', $errors );
|
|
$this->assertSame( '', $errors, 'Config generation must not have errors' );
|
|
|
|
$oldGeneratedSchema = file_get_contents( $expectedFile );
|
|
$relativePath = wfRelativePath( $script, MW_INSTALL_PATH );
|
|
|
|
$this->assertEquals(
|
|
$oldGeneratedSchema,
|
|
$result->getStdout(),
|
|
"Configuration schema was changed. Rerun $relativePath script!"
|
|
);
|
|
}
|
|
|
|
public function provideDefaultSettingsConsistency() {
|
|
yield 'YAML' => [ new FileSource( MW_INSTALL_PATH . '/docs/config-schema.yaml' ) ];
|
|
yield 'PHP' => [ new PhpSettingsSource( MW_INSTALL_PATH . '/includes/config-schema.php' ) ];
|
|
}
|
|
|
|
/**
|
|
* Check that the result of loading config-schema.yaml is the same as DefaultSettings.php
|
|
* This test can be removed when DefaultSettings.php is removed.
|
|
* @dataProvider provideDefaultSettingsConsistency
|
|
*/
|
|
public function testDefaultSettingsConsistency( SettingsSource $source ) {
|
|
$this->expectDeprecationAndContinue( '/DefaultSettings\\.php/' );
|
|
$defaultSettingsProps = ( static function () {
|
|
require MW_INSTALL_PATH . '/includes/DefaultSettings.php';
|
|
$vars = get_defined_vars();
|
|
unset( $vars['input'] );
|
|
$result = [];
|
|
foreach ( $vars as $key => $value ) {
|
|
$result[substr( $key, 2 )] = $value;
|
|
}
|
|
return $result;
|
|
} )();
|
|
|
|
$configBuilder = new ArrayConfigBuilder();
|
|
$settingsBuilder = new SettingsBuilder(
|
|
__DIR__ . '/../../..',
|
|
$this->createNoOpMock( ExtensionRegistry::class ),
|
|
$configBuilder,
|
|
$this->createNoOpMock( PhpIniSink::class )
|
|
);
|
|
$settingsBuilder->load( $source );
|
|
$defaults = iterator_to_array( $settingsBuilder->getDefaultConfig() );
|
|
|
|
foreach ( $defaultSettingsProps as $key => $value ) {
|
|
if ( in_array( $key, [
|
|
'Version', // deprecated alias to MW_VERSION
|
|
'Conf', // instance of SiteConfiguration
|
|
'AutoloadClasses', // conditionally initialized
|
|
] ) ) {
|
|
continue;
|
|
}
|
|
$this->assertArrayHasKey( $key, $defaults, "Missing $key from $source" );
|
|
$this->assertEquals( $value, $defaults[ $key ], "Wrong value for $key\n" );
|
|
}
|
|
|
|
$missingKeys = array_diff_key( $defaults, $defaultSettingsProps );
|
|
$this->assertSame( [], $missingKeys, 'Keys missing from DefaultSettings.php' );
|
|
}
|
|
|
|
public function provideArraysHaveMergeStrategy() {
|
|
[ 'config-schema' => $allSchemas ] = self::getSchemaData();
|
|
|
|
foreach ( $allSchemas as $name => $schema ) {
|
|
yield "Schema for $name" => [ $schema ];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that the schema for each config variable contains all necessary information.
|
|
* @dataProvider provideArraysHaveMergeStrategy
|
|
*/
|
|
public function testSchemaCompleteness( $schema ) {
|
|
$type = $schema['type'] ?? null;
|
|
$type = (array)$type;
|
|
|
|
$this->assertArrayNotHasKey( 'obsolete', $schema, 'Obsolete schemas should have been filtered out' );
|
|
|
|
if ( isset( $schema['properties'] ) ) {
|
|
$this->assertContains(
|
|
'object', $type,
|
|
'must be of type "object", since it defines properties'
|
|
);
|
|
|
|
$defaults = $schema['default'] ?? [];
|
|
foreach ( $schema['properties'] as $key => $sch ) {
|
|
// must have a default in the schema, or in the top level default
|
|
if ( !array_key_exists( 'default', $sch ) ) {
|
|
$this->assertArrayHasKey( $key, $defaults, "property $key must have a default" );
|
|
} else {
|
|
$defaults[$key] = $sch['default'];
|
|
}
|
|
}
|
|
} else {
|
|
$this->assertArrayHasKey(
|
|
'default',
|
|
$schema,
|
|
'should specify a default value'
|
|
);
|
|
$defaults = $schema['default'];
|
|
}
|
|
|
|
// If the default is an array, the type must be declared, so we know whether
|
|
// it's a list (JS "array") or a map (JS "object").
|
|
if ( is_array( $defaults ) ) {
|
|
$this->assertTrue(
|
|
in_array( 'array', $type ) || in_array( 'object', $type ),
|
|
'must be of type "array" or "object", since the default is an array'
|
|
);
|
|
}
|
|
|
|
// If the default value of a list is not empty, check that it is an indexed array,
|
|
// not an associative array.
|
|
if ( in_array( 'array', $type ) && !empty( $defaults ) ) {
|
|
$this->assertArrayHasKey(
|
|
0,
|
|
$schema['default'],
|
|
'should have a default value starting with index 0, since its type is "array".'
|
|
);
|
|
}
|
|
|
|
$mergeStrategy = $schema['mergeStrategy'] ?? null;
|
|
|
|
// If a merge strategy is defined, make sure it makes sense for the given type.
|
|
if ( $mergeStrategy ) {
|
|
if ( in_array( 'array', $type ) ) {
|
|
$this->assertNotSame(
|
|
'array_merge',
|
|
$mergeStrategy,
|
|
'should not specify redundant mergeStrategy "array_merge" since '
|
|
. 'it is implied by the type being "array"'
|
|
);
|
|
|
|
$this->assertNotSame(
|
|
'array_plus',
|
|
$mergeStrategy,
|
|
'should not specify mergeStrategy "array_plus" since its type is "array"'
|
|
);
|
|
|
|
$this->assertNotSame(
|
|
'array_plus_2d',
|
|
$mergeStrategy,
|
|
'should not specify mergeStrategy "array_plus_2d" since its type is "array"'
|
|
);
|
|
} elseif ( in_array( 'object', $type ) ) {
|
|
$this->assertNotSame(
|
|
'array_plus',
|
|
$mergeStrategy,
|
|
'should not specify redundant mergeStrategy "array_plus" since '
|
|
. 'it is implied by the type being "object"'
|
|
);
|
|
|
|
$this->assertNotSame(
|
|
'array_merge',
|
|
$mergeStrategy,
|
|
'should not specify mergeStrategy "array_merge" since its type is "object"'
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( isset( $schema['items'] ) ) {
|
|
$this->assertContains(
|
|
'array',
|
|
$type,
|
|
'should be declared to be an array if an "items" schema is defined'
|
|
);
|
|
}
|
|
|
|
if ( isset( $schema['additionalProperties'] ) || isset( $schema['properties'] ) ) {
|
|
$this->assertContains(
|
|
'object',
|
|
$type,
|
|
'should be declared to be an object if schemas are defined for "properties" ' .
|
|
'or "additionalProperties"'
|
|
);
|
|
}
|
|
}
|
|
|
|
public function provideConfigStructureHandling() {
|
|
yield 'NamespacesWithSubpages' => [
|
|
MainConfigNames::NamespacesWithSubpages,
|
|
[ 0 => true, 1 => false,
|
|
2 => true, 3 => true, 4 => true, 5 => true, 7 => true,
|
|
8 => true, 9 => true, 10 => true, 11 => true, 12 => true,
|
|
13 => true, 15 => true
|
|
],
|
|
[ 0 => true, 1 => false ]
|
|
];
|
|
yield 'InterwikiCache array' => [
|
|
MainConfigNames::InterwikiCache,
|
|
[ 'x' => [ 'foo' => 1 ] ],
|
|
[ 'x' => [ 'foo' => 1 ] ],
|
|
];
|
|
yield 'InterwikiCache string' => [
|
|
MainConfigNames::InterwikiCache,
|
|
'interwiki.map',
|
|
'interwiki.map',
|
|
];
|
|
yield 'InterwikiCache string over array' => [
|
|
MainConfigNames::InterwikiCache,
|
|
'interwiki.map',
|
|
[ 'x' => [ 'foo' => 1 ] ],
|
|
'interwiki.map',
|
|
];
|
|
yield 'ProxyList array' => [
|
|
MainConfigNames::ProxyList,
|
|
[ 'a', 'b', 'c' ],
|
|
[ 'a', 'b', 'c' ],
|
|
];
|
|
yield 'ProxyList string' => [
|
|
MainConfigNames::ProxyList,
|
|
'interwiki.map',
|
|
'interwiki.map',
|
|
];
|
|
yield 'ProxyList string over array' => [
|
|
MainConfigNames::ProxyList,
|
|
'interwiki.map',
|
|
[ 'a', 'b', 'c' ],
|
|
'interwiki.map',
|
|
];
|
|
yield 'ProxyList array over array' => [
|
|
MainConfigNames::ProxyList,
|
|
[ 'a', 'b', 'c', 'd' ],
|
|
[ 'a', 'b' ],
|
|
[ 'c', 'd' ],
|
|
];
|
|
yield 'Logos' => [
|
|
MainConfigNames::Logos,
|
|
[ '1x' => 'Logo1', '2x' => 'Logo2' ],
|
|
[ '1x' => 'Logo1', '2x' => 'Logo2' ],
|
|
];
|
|
yield 'Logos clear' => [
|
|
MainConfigNames::Logos,
|
|
false,
|
|
[ '1x' => 'Logo1', '2x' => 'Logo2' ],
|
|
false
|
|
];
|
|
yield 'RevokePermissions' => [
|
|
MainConfigNames::RevokePermissions,
|
|
[ '*' => [ 'read' => true, 'edit' => true, ] ],
|
|
[ '*' => [ 'edit' => true ] ],
|
|
[ '*' => [ 'read' => true ] ]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Ensure that some of the more complex/problematic config structures are handled
|
|
* correctly.
|
|
*
|
|
* @dataProvider provideConfigStructureHandling
|
|
*/
|
|
public function testConfigStructureHandling( $key, $expected, $value, $value2 = null ) {
|
|
$settingsBuilder = $this->getSettingsBuilderWithSchema();
|
|
$settingsBuilder->apply();
|
|
|
|
$settingsBuilder->putConfigValue( $key, $value );
|
|
|
|
if ( $value2 !== null ) {
|
|
$settingsBuilder->putConfigValue( $key, $value2 );
|
|
}
|
|
|
|
$config = $settingsBuilder->getConfig();
|
|
|
|
$this->assertSame( $expected, $config->get( $key ) );
|
|
}
|
|
|
|
public function provideConfigStructurePartialReplacement() {
|
|
yield 'ObjectCaches' => [
|
|
'ObjectCaches',
|
|
[ // the spec for each cache should be replaced entirely
|
|
1 => [ 'factory' => 'ObjectCache::newAnything' ],
|
|
'test' => [ 'factory' => 'Testing' ]
|
|
],
|
|
[
|
|
1 => [ 'factory' => 'ObjectCache::newAnything' ],
|
|
'test' => [ 'factory' => 'Testing' ]
|
|
],
|
|
];
|
|
yield 'GroupPermissions' => [
|
|
'GroupPermissions',
|
|
[ // permissions for each group should be merged
|
|
'autoconfirmed' => [
|
|
'autoconfirmed' => true,
|
|
'editsemiprotected' => false,
|
|
'patrol' => true,
|
|
],
|
|
'mygroup' => [ 'test' => true ],
|
|
],
|
|
[
|
|
'autoconfirmed' => [
|
|
'patrol' => true,
|
|
'editsemiprotected' => false
|
|
],
|
|
'mygroup' => [ 'test' => true ],
|
|
],
|
|
];
|
|
yield 'RateLimits' => [
|
|
'RateLimits',
|
|
[ // limits for each action should be merged, limits for each group get replaced
|
|
'move' => [ 'newbie' => [ 1, 80 ], 'user' => [ 8, 60 ], 'ip' => [ 1, 60 ] ],
|
|
'test' => [ 'ip' => [ 1, 60 ] ],
|
|
],
|
|
[
|
|
'move' => [ 'ip' => [ 1, 60 ], 'newbie' => [ 1, 80 ], 'user' => [ 8, 60 ] ],
|
|
'test' => [ 'ip' => [ 1, 60 ] ],
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Ensure that some of the more complex/problematic config structures are
|
|
* correctly replacing parts of a complex default.
|
|
*
|
|
* @dataProvider provideConfigStructurePartialReplacement
|
|
*/
|
|
public function testConfigStructurePartialReplacement( $key, $expectedValue, $newValue ) {
|
|
$settingsBuilder = $this->getSettingsBuilderWithSchema();
|
|
$defaultValue = $settingsBuilder->getConfig()->get( $key );
|
|
|
|
$settingsBuilder->putConfigValue( $key, $newValue );
|
|
$mergedValue = $settingsBuilder->getConfig()->get( $key );
|
|
|
|
// Check that the keys in $mergedValue that are also present
|
|
// in $newValue now match $expectedValue.
|
|
$updatedValue = array_intersect_key( $mergedValue, $newValue );
|
|
$this->assertArrayEquals( $expectedValue, $updatedValue, false, true );
|
|
|
|
// Check that the other keys in $mergedValue are still the same
|
|
// as in $defaultValue.
|
|
$mergedValue = array_diff_key( $mergedValue, $newValue );
|
|
$defaultValue = array_diff_key( $defaultValue, $newValue );
|
|
$this->assertArrayEquals( $defaultValue, $mergedValue, false, true );
|
|
}
|
|
|
|
/**
|
|
* Ensure that hook handlers are merged correctly.
|
|
*/
|
|
public function testHooksMerge() {
|
|
$settingsBuilder = $this->getSettingsBuilderWithSchema();
|
|
|
|
$f1 = static function () {
|
|
// noop
|
|
};
|
|
|
|
$hooks = [
|
|
'TestHook' => [
|
|
'TestHookHandler1',
|
|
[ 'TestHookHandler1', 'handler data' ],
|
|
$f1,
|
|
]
|
|
];
|
|
$settingsBuilder->putConfigValue( MainConfigNames::Hooks, $hooks );
|
|
|
|
$f2 = static function () {
|
|
// noop
|
|
};
|
|
|
|
$hooks = [
|
|
'TestHook' => [
|
|
'TestHookHandler2',
|
|
[ 'TestHookHandler2', 'more handler data' ],
|
|
$f2,
|
|
]
|
|
];
|
|
$settingsBuilder->putConfigValue( MainConfigNames::Hooks, $hooks );
|
|
|
|
$config = $settingsBuilder->getConfig();
|
|
|
|
$hooks = [
|
|
'TestHook' => [
|
|
'TestHookHandler1',
|
|
[ 'TestHookHandler1', 'handler data' ],
|
|
$f1,
|
|
'TestHookHandler2',
|
|
[ 'TestHookHandler2', 'more handler data' ],
|
|
$f2,
|
|
]
|
|
];
|
|
$this->assertSame( $hooks, $config->get( MainConfigNames::Hooks ) );
|
|
}
|
|
|
|
/**
|
|
* Ensure that PasswordPolicy are merged correctly.
|
|
*/
|
|
public function testPasswordPolicyMerge() {
|
|
$settingsBuilder = $this->getSettingsBuilderWithSchema();
|
|
$defaultPolicies = $settingsBuilder->getConfig()->get( MainConfigNames::PasswordPolicy );
|
|
|
|
$newPolicies = [
|
|
'policies' => [
|
|
'sysop' => [
|
|
'MinimalPasswordLength' => [
|
|
'value' => 10,
|
|
'suggestChangeOnLogin' => false,
|
|
],
|
|
],
|
|
'bot' => [
|
|
'MinimumPasswordLengthToLogin' => 2,
|
|
],
|
|
],
|
|
'checks' => [
|
|
'MinimalPasswordLength' => 'myLengthCheck',
|
|
'SomeOtherCheck' => 'myOtherCheck',
|
|
]
|
|
];
|
|
$settingsBuilder->putConfigValue( MainConfigNames::PasswordPolicy, $newPolicies );
|
|
$mergedPolicies = $settingsBuilder->getConfig()->get( MainConfigNames::PasswordPolicy );
|
|
|
|
// check that the new policies have been applied
|
|
$this->assertSame(
|
|
[
|
|
'MinimalPasswordLength' => [
|
|
'value' => 10,
|
|
'suggestChangeOnLogin' => false,
|
|
],
|
|
'MinimumPasswordLengthToLogin' => 1, // from defaults
|
|
],
|
|
$mergedPolicies['policies']['sysop']
|
|
);
|
|
$this->assertSame(
|
|
[
|
|
'MinimalPasswordLength' => 10, // from defaults
|
|
'MinimumPasswordLengthToLogin' => 2,
|
|
],
|
|
$mergedPolicies['policies']['bot']
|
|
);
|
|
$this->assertSame(
|
|
'myLengthCheck',
|
|
$mergedPolicies['checks']['MinimalPasswordLength']
|
|
);
|
|
$this->assertSame(
|
|
'myOtherCheck',
|
|
$mergedPolicies['checks']['SomeOtherCheck']
|
|
);
|
|
|
|
// check that other stuff wasn't changed
|
|
$this->assertSame(
|
|
$defaultPolicies['checks']['PasswordCannotMatchDefaults'],
|
|
$mergedPolicies['checks']['PasswordCannotMatchDefaults']
|
|
);
|
|
$this->assertSame(
|
|
$defaultPolicies['policies']['bureaucrat'],
|
|
$mergedPolicies['policies']['bureaucrat']
|
|
);
|
|
$this->assertSame(
|
|
$defaultPolicies['policies']['default'],
|
|
$mergedPolicies['policies']['default']
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\MainConfigSchema::listDefaultValues
|
|
* @covers \MediaWiki\MainConfigSchema::getDefaultValue
|
|
*/
|
|
public function testMainConfigSchemaDefaults() {
|
|
$defaults = iterator_to_array( MainConfigSchema::listDefaultValues() );
|
|
$prefixed = iterator_to_array( MainConfigSchema::listDefaultValues( 'wg' ) );
|
|
|
|
$schema = $this->getSchemaData();
|
|
foreach ( $schema['config-schema'] as $name => $sch ) {
|
|
$this->assertArrayHasKey( $name, $defaults );
|
|
$this->assertArrayHasKey( "wg$name", $prefixed );
|
|
|
|
$expected = self::getDefaultFromJsonSchema( $sch );
|
|
|
|
$this->assertSame( $expected, $defaults[$name] );
|
|
$this->assertSame( $expected, $prefixed["wg$name"] );
|
|
|
|
$this->assertSame( $expected, MainConfigSchema::getDefaultValue( $name ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @coversNothing Only covers code in global scope, no way to annotate that?
|
|
*/
|
|
public function testSetLocaltimezone(): void {
|
|
// Make sure the configured timezone ewas applied to the PHP runtime.
|
|
$tz = $this->getServiceContainer()->getMainConfig()->get( 'Localtimezone' );
|
|
$this->assertSame( $tz, date_default_timezone_get() );
|
|
}
|
|
}
|