'TestUserRegistry' => "$testDir/phpunit/includes/TestUserRegistry.php",
'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
'MediaWikiCoversValidator' => "$testDir/phpunit/MediaWikiCoversValidator.php",
+ 'PHPUnit4And6Compat' => "$testDir/phpunit/PHPUnit4And6Compat.php",
# tests/phpunit/includes
'RevisionDbTestBase' => "$testDir/phpunit/includes/RevisionDbTestBase.php",
'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
'ApiTestCaseUpload' => "$testDir/phpunit/includes/api/ApiTestCaseUpload.php",
'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestContext.php",
+ 'ApiUploadTestCase' => "$testDir/phpunit/includes/api/ApiUploadTestCase.php",
'MockApi' => "$testDir/phpunit/includes/api/MockApi.php",
'MockApiQueryBase' => "$testDir/phpunit/includes/api/MockApiQueryBase.php",
'UserWrapper' => "$testDir/phpunit/includes/api/UserWrapper.php",
'DummyContentForTesting' => "$testDir/phpunit/mocks/content/DummyContentForTesting.php",
'DummyNonTextContentHandler' => "$testDir/phpunit/mocks/content/DummyNonTextContentHandler.php",
'DummyNonTextContent' => "$testDir/phpunit/mocks/content/DummyNonTextContent.php",
+ 'DummySerializeErrorContentHandler' =>
+ "$testDir/phpunit/mocks/content/DummySerializeErrorContentHandler.php",
'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
'SpecialPageExecutor' => "$testDir/phpunit/includes/specials/SpecialPageExecutor.php",
+ # tests/phpunit/includes/Storage
+ 'MediaWiki\Tests\Storage\RevisionSlotsTest' => "$testDir/phpunit/includes/Storage/RevisionSlotsTest.php",
+
# tests/phpunit/languages
'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
'MediaWiki\\Session\\DummySessionBackend'
=> "$testDir/phpunit/mocks/session/DummySessionBackend.php",
'DummySessionProvider' => "$testDir/phpunit/mocks/session/DummySessionProvider.php",
+ 'MockMessageLocalizer' => "$testDir/phpunit/mocks/MockMessageLocalizer.php",
# tests/suites
'ParserTestFileSuite' => "$testDir/phpunit/suites/ParserTestFileSuite.php",
'ParserTestTopLevelSuite' => "$testDir/phpunit/suites/ParserTestTopLevelSuite.php",
];
// phpcs:enable
+
+ /**
+ * Alias any PHPUnit 4 era PHPUnit_... class
+ * to it's PHPUnit 6 replacement. For most classes
+ * this is a direct _ -> \ replacement, but for
+ * some others we might need to maintain a manual
+ * mapping. Once we drop support for PHPUnit 4 this
+ * should be considered deprecated and eventually removed.
+ */
+ spl_autoload_register( function ( $class ) {
+ if ( strpos( $class, 'PHPUnit_' ) !== 0 ) {
+ // Skip if it doesn't start with the old prefix
+ return;
+ }
+
+ // Classes that don't map 100%
+ $map = [
+ 'PHPUnit_Framework_TestSuite_DataProvider' => 'PHPUnit\Framework\DataProviderTestSuite'
+ ];
+
+ if ( isset( $map[$class] ) ) {
+ $newForm = $map[$class];
+ } else {
+ $newForm = str_replace( '_', '\\', $class );
+ }
+
+ if ( class_exists( $newForm ) ) {
+ // If the new class name exists, alias
+ // the old name to it.
+ class_alias( $newForm, $class );
+ }
+ } );
abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
use MediaWikiCoversValidator;
+ use PHPUnit4And6Compat;
/**
* The service locator created by prepareServices(). This service locator will
}
}
+ private static $schemaOverrideDefaults = [
+ 'scripts' => [],
+ 'create' => [],
+ 'drop' => [],
+ 'alter' => [],
+ ];
+
/**
* Stub. If a test suite needs to test against a specific database schema, it should
* override this method and return the appropriate information from it.
*
- * @return [ $tables, $scripts ] A tuple of two lists, with $tables being a list of tables
- * that will be re-created by the scripts, and $scripts being a list of SQL script
- * files for creating the tables listed.
+ * @param IMaintainableDatabase $db The DB connection to use for the mock schema.
+ * May be used to check the current state of the schema, to determine what
+ * overrides are needed.
+ *
+ * @return array An associative array with the following fields:
+ * - 'scripts': any SQL scripts to run. If empty or not present, schema overrides are skipped.
+ * - 'create': A list of tables created (may or may not exist in the original schema).
+ * - 'drop': A list of tables dropped (expected to be present in the original schema).
+ * - 'alter': A list of tables altered (expected to be present in the original schema).
*/
- protected function getSchemaOverrides() {
- return [ [], [] ];
+ protected function getSchemaOverrides( IMaintainableDatabase $db ) {
+ return [];
}
/**
- * Applies any schema changes requested by calling setDbSchema().
+ * Undoes the dpecified schema overrides..
+ * Called once per test class, just before addDataOnce().
+ *
+ * @param IMaintainableDatabase $db
+ * @param array $oldOverrides
+ */
+ private function undoSchemaOverrides( IMaintainableDatabase $db, $oldOverrides ) {
+ $this->ensureMockDatabaseConnection( $db );
+
+ $oldOverrides = $oldOverrides + self::$schemaOverrideDefaults;
+ $originalTables = $this->listOriginalTables( $db );
+
+ // Drop tables that need to be restored or removed.
+ $tablesToDrop = array_merge( $oldOverrides['create'], $oldOverrides['alter'] );
+
+ // Restore tables that have been dropped or created or altered,
+ // if they exist in the original schema.
+ $tablesToRestore = array_merge( $tablesToDrop, $oldOverrides['drop'] );
+ $tablesToRestore = array_intersect( $originalTables, $tablesToRestore );
+
+ if ( $tablesToDrop ) {
+ $this->dropMockTables( $db, $tablesToDrop );
+ }
+
+ if ( $tablesToRestore ) {
+ $this->recloneMockTables( $db, $tablesToRestore );
+ }
+ }
+
+ /**
+ * Applies the schema overrides returned by getSchemaOverrides(),
+ * after undoing any previously applied schema overrides.
* Called once per test class, just before addDataOnce().
*/
private function setUpSchema( IMaintainableDatabase $db ) {
- list( $tablesToAlter, $scriptsToRun ) = $this->getSchemaOverrides();
+ // Undo any active overrides.
+ $oldOverrides = isset( $db->_schemaOverrides ) ? $db->_schemaOverrides
+ : self::$schemaOverrideDefaults;
+
+ if ( $oldOverrides['alter'] || $oldOverrides['create'] || $oldOverrides['drop'] ) {
+ $this->undoSchemaOverrides( $db, $oldOverrides );
+ }
+
+ // Determine new overrides.
+ $overrides = $this->getSchemaOverrides( $db ) + self::$schemaOverrideDefaults;
+
+ $extraKeys = array_diff(
+ array_keys( $overrides ),
+ array_keys( self::$schemaOverrideDefaults )
+ );
- if ( $tablesToAlter && !$scriptsToRun ) {
+ if ( $extraKeys ) {
throw new InvalidArgumentException(
- 'No scripts supplied for applying the database schema.'
+ 'Schema override contains extra keys: ' . var_export( $extraKeys, true )
);
}
- if ( !$tablesToAlter && $scriptsToRun ) {
+ if ( !$overrides['scripts'] ) {
+ // no scripts to run
+ return;
+ }
+
+ if ( !$overrides['create'] && !$overrides['drop'] && !$overrides['alter'] ) {
throw new InvalidArgumentException(
- 'No tables declared to be altered by schema scripts.'
+ 'Schema override scripts given, but no tables are declared to be '
+ . 'created, dropped or altered.'
);
}
$this->ensureMockDatabaseConnection( $db );
- $previouslyAlteredTables = isset( $db->_alteredMockTables ) ? $db->_alteredMockTables : [];
-
- if ( !$tablesToAlter && !$previouslyAlteredTables ) {
- return; // nothing to do
- }
-
- $tablesToDrop = array_merge( $previouslyAlteredTables, $tablesToAlter );
- $tablesToRestore = array_diff( $previouslyAlteredTables, $tablesToAlter );
+ // Drop the tables that will be created by the schema scripts.
+ $originalTables = $this->listOriginalTables( $db );
+ $tablesToDrop = array_intersect( $originalTables, $overrides['create'] );
if ( $tablesToDrop ) {
$this->dropMockTables( $db, $tablesToDrop );
}
- if ( $tablesToRestore ) {
- $this->recloneMockTables( $db, $tablesToRestore );
- }
-
- foreach ( $scriptsToRun as $script ) {
+ // Run schema override scripts.
+ foreach ( $overrides['scripts'] as $script ) {
$db->sourceFile(
$script,
null,
);
}
- $db->_alteredMockTables = $tablesToAlter;
+ $db->_schemaOverrides = $overrides;
}
private function mungeSchemaUpdateQuery( $cmd ) {
}
}
+ /**
+ * Lists all tables in the live database schema.
+ *
+ * @param IMaintainableDatabase $db
+ * @return array
+ */
+ private function listOriginalTables( IMaintainableDatabase $db ) {
+ if ( !isset( $db->_originalTablePrefix ) ) {
+ throw new LogicException( 'No original table prefix know, cannot list tables!' );
+ }
+
+ $originalTables = $db->listTables( $db->_originalTablePrefix, __METHOD__ );
+ return $originalTables;
+ }
+
/**
* Re-clones the given mock tables to restore them based on the live database schema.
+ * The tables listed in $tables are expected to currently not exist, so dropMockTables()
+ * should be called first.
*
* @param IMaintainableDatabase $db
* @param array $tables
throw new LogicException( 'No original table prefix know, cannot restore tables!' );
}
- $originalTables = $db->listTables( $db->_originalTablePrefix, __METHOD__ );
+ $originalTables = $this->listOriginalTables( $db );
$tables = array_intersect( $tables, $originalTables );
$dbClone = new CloneDatabase( $db, $tables, $db->tablePrefix(), $db->_originalTablePrefix );
*/
private function resetDB( $db, $tablesUsed ) {
if ( $db ) {
- $userTables = [ 'user', 'user_groups', 'user_properties' ];
- $pageTables = [ 'page', 'revision', 'ip_changes', 'revision_comment_temp', 'comment' ];
+ $userTables = [ 'user', 'user_groups', 'user_properties', 'actor' ];
+ $pageTables = [ 'page', 'revision', 'ip_changes', 'revision_comment_temp',
+ 'revision_actor_temp', 'comment' ];
$coreDBDataTables = array_merge( $userTables, $pageTables );
// If any of the user or page tables were marked as used, we should clear all of them.
continue;
}
+ if ( !$db->tableExists( $tbl ) ) {
+ continue;
+ }
+
if ( $truncate ) {
$db->query( 'TRUNCATE TABLE ' . $db->tableName( $tbl ), __METHOD__ );
} else {
$db->delete( $tbl, '*', __METHOD__ );
}
+ if ( $db->getType() === 'postgres' ) {
+ // Reset the table's sequence too.
+ $db->resetSequenceForTable( $tbl, __METHOD__ );
+ }
+
if ( $tbl === 'page' ) {
// Forget about the pages since they don't
// exist in the DB.
}
}
- /**
- * @since 1.18
- *
- * @param string $func
- * @param array $args
- *
- * @return mixed
- * @throws MWException
- */
- public function __call( $func, $args ) {
- static $compatibility = [
- 'createMock' => 'createMock2',
- ];
-
- if ( isset( $compatibility[$func] ) ) {
- return call_user_func_array( [ $this, $compatibility[$func] ], $args );
- } else {
- throw new MWException( "Called non-existent $func method on " . static::class );
- }
- }
-
- /**
- * Return a test double for the specified class.
- *
- * @param string $originalClassName
- * @return PHPUnit_Framework_MockObject_MockObject
- * @throws Exception
- */
- private function createMock2( $originalClassName ) {
- return $this->getMockBuilder( $originalClassName )
- ->disableOriginalConstructor()
- ->disableOriginalClone()
- ->disableArgumentCloning()
- // New in phpunit-mock-objects 3.2 (phpunit 5.4.0)
- // ->disallowMockingUnknownTypes()
- ->getMock();
- }
-
private static function unprefixTable( &$tableName, $ind, $prefix ) {
$tableName = substr( $tableName, strlen( $prefix ) );
}