jenkins-bot has submitted this change and it was merged.

Change subject: Add support for External Store for unit tests
......................................................................


Add support for External Store for unit tests

This allow running PHP tests with External Store DB configured.
This means it needs to do the unittest_ prefixing similar to the main
tables.

It could do resetting (truncating the tables) similar to the tablesUsed
functionality.  However, if External Store is truncated unconditionally,
revision, etc. also needs to be.  This can cause performance issues.
I was thinking of making resetting ES optional, but it wasn't needed
to pass any of the core tests, so I just left it out entirely for now.

Bug: T95870
Change-Id: If27435210d5a2165795dd759b17a7e51fe9bba10
---
M tests/phpunit/MediaWikiTestCase.php
1 file changed, 146 insertions(+), 39 deletions(-)

Approvals:
  Matthias Mullie: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/tests/phpunit/MediaWikiTestCase.php 
b/tests/phpunit/MediaWikiTestCase.php
index b9cf51d..f3306be 100644
--- a/tests/phpunit/MediaWikiTestCase.php
+++ b/tests/phpunit/MediaWikiTestCase.php
@@ -29,6 +29,8 @@
        public static $users;
 
        /**
+        * Primary database
+        *
         * @var DatabaseBase
         * @since 1.18
         */
@@ -132,17 +134,16 @@
                        $this->checkDbIsSupported();
 
                        if ( !self::$dbSetup ) {
-                               // switch to a temporary clone of the database
-                               self::setupTestDB( $this->db, $this->dbPrefix() 
);
+                               $this->setupAllTestDBs();
                                $this->addCoreDBData();
 
                                if ( ( $this->db->getType() == 'oracle' || 
!self::$useTemporaryTables ) && self::$reuseDB ) {
-                                       $this->resetDB();
+                                       $this->resetDB( $this->db, 
$this->tablesUsed );
                                }
                        }
 
                        // TODO: the DB setup should be done in 
setUpBeforeClass(), so the test DB
-                       // is available in subclasse's setUpBeforeClass() and 
setUp() methods.
+                       // is available in subclass's setUpBeforeClass() and 
setUp() methods.
                        // This would also remove the need for the HACK that is 
oncePerClass().
                        if ( $this->oncePerClass() ) {
                                $this->addDBDataOnce();
@@ -155,7 +156,7 @@
                parent::run( $result );
 
                if ( $needsResetDB ) {
-                       $this->resetDB();
+                       $this->resetDB( $this->db, $this->tablesUsed );
                }
        }
 
@@ -564,10 +565,6 @@
                $user = User::newFromName( 'UTSysop' );
                $comment = __METHOD__ . ': Sample page for unit test.';
 
-               // Avoid memory leak...?
-               // LinkCache::singleton()->clear();
-               // Maybe.  But doing this absolutely breaks 
$title->isRedirect() when called during unit tests....
-
                $page = WikiPage::factory( $title );
                $page->doEditContent( ContentHandler::makeContent( $text, 
$title ), $comment, 0, false, $user );
 
@@ -691,6 +688,52 @@
        }
 
        /**
+        * Setups a database with the given prefix.
+        *
+        * If reuseDB is true and certain conditions apply, it will just change 
the prefix.
+        * Otherwise, it will clone the tables and change the prefix.
+        *
+        * Clones all tables in the given database (whatever database that 
connection has
+        * open), to versions with the test prefix.
+        *
+        * @param DatabaseBase $db Database to use
+        * @param string $prefix Prefix to use for test tables
+        * @return bool True if tables were cloned, false if only the prefix 
was changed
+        */
+       protected static function setupDatabaseWithTestPrefix( DatabaseBase 
$db, $prefix ) {
+               $tablesCloned = self::listTables( $db );
+               $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix );
+               $dbClone->useTemporaryTables( self::$useTemporaryTables );
+
+               if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables 
) && self::$reuseDB ) {
+                       CloneDatabase::changePrefix( $prefix );
+
+                       return false;
+               } else {
+                       $dbClone->cloneTableStructure();
+                       return true;
+               }
+       }
+
+       /**
+        * Set up all test DBs
+        */
+       public function setupAllTestDBs() {
+               global $wgDBprefix;
+
+               self::$oldTablePrefix = $wgDBprefix;
+
+               $testPrefix = $this->dbPrefix();
+
+               // switch to a temporary clone of the database
+               self::setupTestDB( $this->db, $testPrefix );
+
+               if ( self::isUsingExternalStoreDB() ) {
+                       self::setupExternalStoreTestDBs( $testPrefix );
+               }
+       }
+
+       /**
         * Creates an empty skeleton of the wiki database by cloning its 
structure
         * to equivalent tables using the given $prefix. Then sets MediaWiki to
         * use the new set of tables (aka schema) instead of the original set.
@@ -712,8 +755,7 @@
         * @throws MWException If the database table prefix is already $prefix
         */
        public static function setupTestDB( DatabaseBase $db, $prefix ) {
-               global $wgDBprefix;
-               if ( $wgDBprefix === $prefix ) {
+               if ( $db->tablePrefix() === $prefix ) {
                        throw new MWException(
                                'Cannot run unit tests, the database prefix is 
already "' . $prefix . '"' );
                }
@@ -722,42 +764,110 @@
                        return;
                }
 
-               $tablesCloned = self::listTables( $db );
-               $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix );
-               $dbClone->useTemporaryTables( self::$useTemporaryTables );
-
-               self::$dbSetup = true;
-               self::$oldTablePrefix = $wgDBprefix;
-
-               if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables 
) && self::$reuseDB ) {
-                       CloneDatabase::changePrefix( $prefix );
-
+               if ( !self::setupDatabaseWithTestPrefix( $db, $prefix ) ) {
                        return;
-               } else {
-                       $dbClone->cloneTableStructure();
                }
 
+               // Assuming this isn't needed for External Store database, and 
not sure if the procedure
+               // would be available there.
                if ( $db->getType() == 'oracle' ) {
                        $db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+               }
+
+               self::$dbSetup = true;
+       }
+
+       /**
+        * Clones the External Store database(s) for testing
+        *
+        * @param string $testPrefix Prefix for test tables
+        */
+       protected static function setupExternalStoreTestDBs( $testPrefix ) {
+               $connections = self::getExternalStoreDatabaseConnections();
+               foreach ( $connections as $dbw ) {
+                       // Hack: cloneTableStructure sets $wgDBprefix to the 
unit test
+                       // prefix,.  Even though listTables now uses 
tablePrefix, that
+                       // itself is populated from $wgDBprefix by default.
+
+                       // We have to set it back, or we won't find the 
original 'blobs'
+                       // table to copy.
+
+                       $dbw->tablePrefix( self::$oldTablePrefix );
+                       self::setupDatabaseWithTestPrefix( $dbw, $testPrefix );
                }
        }
 
        /**
-        * Empty all tables so they can be repopulated for tests
+        * Gets master database connections for all of the ExternalStoreDB
+        * stores configured in $wgDefaultExternalStore.
+        *
+        * @return array Array of DatabaseBase master connections
         */
-       private function resetDB() {
-               if ( $this->db ) {
-                       $truncate = in_array( $this->db->getType(), [ 'oracle', 
'mysql' ] );
-                       foreach ( $this->tablesUsed as $tbl ) {
+
+       protected static function getExternalStoreDatabaseConnections() {
+               global $wgDefaultExternalStore;
+
+               $externalStoreDB = ExternalStore::getStoreObject( 'DB' );
+               $defaultArray = (array) $wgDefaultExternalStore;
+               $dbws = [];
+               foreach ( $defaultArray as $url ) {
+                       if ( strpos( $url, 'DB://' ) === 0 ) {
+                               list( $proto, $cluster ) = explode( '://', 
$url, 2 );
+                               $dbw = $externalStoreDB->getMaster( $cluster );
+                               $dbws[] = $dbw;
+                       }
+               }
+
+               return $dbws;
+       }
+
+       /**
+        * Check whether ExternalStoreDB is being used
+        *
+        * @return bool True if it's being used
+        */
+       protected static function isUsingExternalStoreDB() {
+               global $wgDefaultExternalStore;
+               if ( !$wgDefaultExternalStore ) {
+                       return false;
+               }
+
+               $defaultArray = (array) $wgDefaultExternalStore;
+               foreach ( $defaultArray as $url ) {
+                       if ( strpos( $url, 'DB://' ) === 0 ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Empty all tables so they can be repopulated for tests
+        *
+        * @param DatabaseBase $db|null Database to reset
+        * @param array $tablesUsed Tables to reset
+        */
+       private function resetDB( $db, $tablesUsed ) {
+               if ( $db ) {
+                       $truncate = in_array( $db->getType(), [ 'oracle', 
'mysql' ] );
+                       foreach ( $tablesUsed as $tbl ) {
                                // TODO: reset interwiki and user tables to 
their original content.
                                if ( $tbl == 'interwiki' || $tbl == 'user' ) {
                                        continue;
                                }
 
                                if ( $truncate ) {
-                                       $this->db->query( 'TRUNCATE TABLE ' . 
$this->db->tableName( $tbl ), __METHOD__ );
+                                       $db->query( 'TRUNCATE TABLE ' . 
$db->tableName( $tbl ), __METHOD__ );
                                } else {
-                                       $this->db->delete( $tbl, '*', 
__METHOD__ );
+
+                                       $db->delete( $tbl, '*', __METHOD__ );
+                               }
+
+                               if ( $tbl === 'page' ) {
+                                       // Forget about the pages since they 
don't
+                                       // exist in the DB.
+                                       LinkCache::singleton()->clear();
                                }
                        }
                }
@@ -794,10 +904,8 @@
                $this->assertTrue( $value == '', $msg );
        }
 
-       private static function unprefixTable( $tableName ) {
-               global $wgDBprefix;
-
-               return substr( $tableName, strlen( $wgDBprefix ) );
+       private static function unprefixTable( &$tableName, $ind, $prefix ) {
+               $tableName = substr( $tableName, strlen( $prefix ) );
        }
 
        private static function isNotUnittest( $table ) {
@@ -812,16 +920,15 @@
         * @return array
         */
        public static function listTables( $db ) {
-               global $wgDBprefix;
-
-               $tables = $db->listTables( $wgDBprefix, __METHOD__ );
+               $prefix = $db->tablePrefix();
+               $tables = $db->listTables( $prefix, __METHOD__ );
 
                if ( $db->getType() === 'mysql' ) {
                        # bug 43571: cannot clone VIEWs under MySQL
-                       $views = $db->listViews( $wgDBprefix, __METHOD__ );
+                       $views = $db->listViews( $prefix, __METHOD__ );
                        $tables = array_diff( $tables, $views );
                }
-               $tables = array_map( [ __CLASS__, 'unprefixTable' ], $tables );
+               array_walk( $tables, [ __CLASS__, 'unprefixTable' ], $prefix );
 
                // Don't duplicate test tables from the previous fataled run
                $tables = array_filter( $tables, [ __CLASS__, 'isNotUnittest' ] 
);

-- 
To view, visit https://gerrit.wikimedia.org/r/274643
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: If27435210d5a2165795dd759b17a7e51fe9bba10
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Mattflaschen <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Mattflaschen <[email protected]>
Gerrit-Reviewer: Matthias Mullie <[email protected]>
Gerrit-Reviewer: Parent5446 <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to