# tests/phpunit/includes/changes
'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",
+ # tests/phpunit/includes/config
+ 'TestAllServiceOptionsUsed' => "$testDir/phpunit/includes/config/TestAllServiceOptionsUsed.php",
+ 'LoggedServiceOptions' => "$testDir/phpunit/includes/config/LoggedServiceOptions.php",
+
# tests/phpunit/includes/content
'DummyContentHandlerForTesting' =>
"$testDir/phpunit/mocks/content/DummyContentHandlerForTesting.php",
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Block\CompositeBlock;
use MediaWiki\Block\SystemBlock;
-use MediaWiki\Config\ServiceOptions;
use MediaWiki\MediaWikiServices;
use Wikimedia\TestingAccessWrapper;
* @coversDefaultClass \MediaWiki\Block\BlockManager
*/
class BlockManagerTest extends MediaWikiTestCase {
+ use TestAllServiceOptionsUsed;
/** @var User */
protected $user;
$this->setMwGlobals( $blockManagerConfig );
$this->overrideMwServices();
return [
- new ServiceOptions(
+ new LoggedServiceOptions(
+ self::$serviceOptionsAccessLog,
BlockManager::$constructorOptions,
MediaWikiServices::getInstance()->getMainConfig()
),
];
}
+ /**
+ * @coversNothing
+ */
+ public function testAllServiceOptionsUsed() {
+ $this->assertAllServiceOptionsUsed( [ 'ApplyIpBlocksToXff', 'SoftBlockRanges' ] );
+ }
}
--- /dev/null
+<?php
+
+use MediaWiki\Config\ServiceOptions;
+
+/**
+ * Helper for TestAllServiceOptionsUsed.
+ */
+class LoggedServiceOptions extends ServiceOptions {
+ /** @var array */
+ private $accessLog;
+
+ /**
+ * @param array &$accessLog Pass self::$serviceOptionsAccessLog from the class implementing
+ * TestAllServiceOptionsUsed.
+ * @param string[] $keys
+ * @param mixed ...$args Forwarded to parent as-is.
+ */
+ public function __construct( array &$accessLog, array $keys, ...$args ) {
+ $this->accessLog = &$accessLog;
+ if ( !$accessLog ) {
+ $accessLog = [ $keys, [] ];
+ }
+
+ parent::__construct( $keys, ...$args );
+ }
+
+ /**
+ * @param string $key
+ * @return mixed
+ */
+ public function get( $key ) {
+ $this->accessLog[1][$key] = true;
+
+ return parent::get( $key );
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * Use this trait to check that code run by tests accesses every key declared for this class'
+ * ServiceOptions, e.g., in a $constructorOptions member variable. To use this trait, you need to do
+ * two things (other than use-ing it):
+ *
+ * 1) Don't use the regular ServiceOptions when constructing your objects, but rather
+ * LoggedServiceOptions. These are used the same as ServiceOptions, except in the constructor, pass
+ * self::$serviceOptionsAccessLog before the regular arguments.
+ *
+ * 2) Make a test that calls assertAllServiceOptionsUsed(). If some ServiceOptions keys are not yet
+ * accessed in tests but actually are used by the class, pass their names as an argument.
+ *
+ * Currently we support only one ServiceOptions per test class.
+ */
+trait TestAllServiceOptionsUsed {
+ /** @var array [ expected keys (as list), keys accessed so far (as dictionary) ] */
+ private static $serviceOptionsAccessLog = [];
+
+ /**
+ * @param string[] $expectedUnused Options that we know are not yet tested
+ */
+ public function assertAllServiceOptionsUsed( array $expectedUnused = [] ) {
+ $this->assertNotEmpty( self::$serviceOptionsAccessLog,
+ 'You need to pass LoggedServiceOptions to your class instead of ServiceOptions ' .
+ 'for TestAllServiceOptionsUsed to work.'
+ );
+
+ list( $expected, $actual ) = self::$serviceOptionsAccessLog;
+
+ $expected = array_diff( $expected, $expectedUnused );
+
+ $this->assertSame(
+ [],
+ array_diff( $expected, array_keys( $actual ) ),
+ "Some ServiceOptions keys were not accessed in tests. If they really aren't used, " .
+ "remove them from the class' option list. If they are used, add tests to cover them, " .
+ "or ignore the problem for now by passing them to assertAllServiceOptionsUsed() in " .
+ "its \$expectedUnused argument."
+ );
+
+ if ( $expectedUnused ) {
+ $this->markTestIncomplete( 'Some ServiceOptions keys are not yet accessed by tests: ' .
+ implode( ', ', $expectedUnused ) );
+ }
+ }
+}
<?php
use MediaWiki\Auth\AuthManager;
-use MediaWiki\Config\ServiceOptions;
use MediaWiki\MediaWikiServices;
use MediaWiki\Preferences\DefaultPreferencesFactory;
use Wikimedia\TestingAccessWrapper;
* @group Preferences
*/
class DefaultPreferencesFactoryTest extends \MediaWikiTestCase {
+ use TestAllServiceOptionsUsed;
/** @var IContextSource */
protected $context;
->method( $this->anythingBut( 'getValidNamespaces', '__destruct' ) );
return new DefaultPreferencesFactory(
- new ServiceOptions( DefaultPreferencesFactory::$constructorOptions, $this->config ),
+ new LoggedServiceOptions( self::$serviceOptionsAccessLog,
+ DefaultPreferencesFactory::$constructorOptions, $this->config ),
new Language(),
AuthManager::singleton(),
MediaWikiServices::getInstance()->getLinkRenderer(),
$form->trySubmit();
$this->assertEquals( 12, $user->getOption( 'rclimit' ) );
}
+
+ /**
+ * @coversNothing
+ */
+ public function testAllServiceOptionsUsed() {
+ $this->assertAllServiceOptionsUsed( [ 'EnotifMinorEdits', 'EnotifRevealEditorAddress' ] );
+ }
}
use MediaWiki\Linker\LinkTarget;
class NamespaceInfoTest extends MediaWikiTestCase {
+ use TestAllServiceOptionsUsed;
+
/**********************************************************************************************
* Shared code
* %{
];
private function newObj( array $options = [] ) : NamespaceInfo {
- return new NamespaceInfo( new ServiceOptions( NamespaceInfo::$constructorOptions,
- $options, self::$defaultOptions ) );
+ return new NamespaceInfo( new LoggedServiceOptions(
+ self::$serviceOptionsAccessLog,
+ NamespaceInfo::$constructorOptions,
+ $options, self::$defaultOptions
+ ) );
}
// %} End shared code
}
// %} End restriction levels
+
+ /**
+ * @coversNothing
+ */
+ public function testAllServiceOptionsUsed() {
+ $this->assertAllServiceOptionsUsed();
+ }
}
/**