Merge "RCLFilters: reword target placeholder"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 5 Jan 2018 17:18:46 +0000 (17:18 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 5 Jan 2018 17:18:46 +0000 (17:18 +0000)
.mailmap
CREDITS
includes/db/CloneDatabase.php
includes/libs/rdbms/database/Database.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/tests/MediaWikiTestCaseSchema1Test.php [new file with mode: 0644]
tests/phpunit/tests/MediaWikiTestCaseSchema2Test.php [new file with mode: 0644]
tests/phpunit/tests/MediaWikiTestCaseSchemaTest.sql [new file with mode: 0644]
tests/phpunit/tests/MediaWikiTestCaseTest.php

index c4f8604..c101be3 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -281,6 +281,7 @@ Matthew Walker <mwalker@wikimedia.org>
 Matthias Mullie <git@mullie.eu>
 Matthias Mullie <git@mullie.eu> <mmullie@wikimedia.org>
 Matěj Grabovský <mgrabovsky@yahoo.com> <mgrabovsky@users.mediawiki.org>
+Matěj Suchánek <matejsuchanek97@gmail.com>
 Max Semenik <maxsem.wiki@gmail.com>
 Max Semenik <maxsem.wiki@gmail.com> <maxsem@users.mediawiki.org>
 Max Semenik <maxsem.wiki@gmail.com> <semenik@gmail.com>
@@ -383,7 +384,7 @@ Sam Reed <reedy@wikimedia.org> <reedy@users.mediawiki.org>
 Sam Smith <git@samsmith.io>
 Santhosh Thottingal <santhosh.thottingal@gmail.com>
 Santhosh Thottingal <santhosh.thottingal@gmail.com> <santhosh@users.mediawiki.org>
-Schnark <listenleser@gmail.com>
+Schnark (Michael M.) <listenleser@gmail.com>
 Scimonster <tehalmightyscimonster@gmail.com>
 Sean Colombo <sean.colombo@gmail.com> <sean_colombo@users.mediawiki.org>
 Sean Pringle <springle@wikimedia.org>
diff --git a/CREDITS b/CREDITS
index 6ab4ad3..ea011e8 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -394,7 +394,6 @@ The following list can be found parsed under Special:Version/Credits -->
 * Massaf
 * Matěj Grabovský
 * Matěj Suchánek
-* matejsuchanek
 * Mathias Ertl
 * mati
 * Matt Fitzpatrick
@@ -420,7 +419,6 @@ The following list can be found parsed under Special:Version/Credits -->
 * Michael Dale
 * Michael De La Rue
 * Michael Holloway
-* Michael M.
 * Michael Newton
 * Michael Walsh
 * Michał Łazowik
@@ -573,7 +571,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Sam Wilson
 * Santhosh Thottingal
 * saptaks
-* Schnark
+* Schnark (Michael M.)
 * Scimonster
 * scnd
 * Scott Colcord
@@ -670,7 +668,6 @@ The following list can be found parsed under Special:Version/Credits -->
 * Victor Barbu
 * Victor Porton
 * Victor Vasiliev
-* victorbarbu
 * Ville Stadista
 * vishnu
 * Vitaliy Filippov
index 16d10d1..edb54ae 100644 (file)
@@ -50,12 +50,12 @@ class CloneDatabase {
         * @param bool $dropCurrentTables
         */
        public function __construct( IMaintainableDatabase $db, array $tablesToClone,
-               $newTablePrefix, $oldTablePrefix = '', $dropCurrentTables = true
+               $newTablePrefix, $oldTablePrefix = null, $dropCurrentTables = true
        ) {
                $this->db = $db;
                $this->tablesToClone = $tablesToClone;
                $this->newTablePrefix = $newTablePrefix;
-               $this->oldTablePrefix = $oldTablePrefix ? $oldTablePrefix : $this->db->tablePrefix();
+               $this->oldTablePrefix = $oldTablePrefix !== null ? $oldTablePrefix : $this->db->tablePrefix();
                $this->dropCurrentTables = $dropCurrentTables;
        }
 
index 15e02ad..0ee10ed 100644 (file)
@@ -3390,6 +3390,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $fname = __METHOD__,
                callable $inputCallback = null
        ) {
+               $delimiterReset = new ScopedCallback(
+                       function ( $delimiter ) {
+                               $this->delimiter = $delimiter;
+                       },
+                       [ $this->delimiter ]
+               );
                $cmd = '';
 
                while ( !feof( $fp ) ) {
@@ -3418,7 +3424,15 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        if ( $done || feof( $fp ) ) {
                                $cmd = $this->replaceVars( $cmd );
 
-                               if ( !$inputCallback || call_user_func( $inputCallback, $cmd ) ) {
+                               if ( $inputCallback ) {
+                                       $callbackResult = call_user_func( $inputCallback, $cmd );
+
+                                       if ( is_string( $callbackResult ) || !$callbackResult ) {
+                                               $cmd = $callbackResult;
+                                       }
+                               }
+
+                               if ( $cmd ) {
                                        $res = $this->query( $cmd, $fname );
 
                                        if ( $resultCallback ) {
@@ -3435,6 +3449,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        }
                }
 
+               ScopedCallback::consume( $delimiterReset );
                return true;
        }
 
index d542826..10f5d41 100644 (file)
@@ -5,8 +5,10 @@ use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\Logger\MonologSpi;
 use MediaWiki\MediaWikiServices;
 use Psr\Log\LoggerInterface;
+use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\IMaintainableDatabase;
 use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\LBFactory;
 use Wikimedia\TestingAccessWrapper;
 
 /**
@@ -408,6 +410,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        // 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->setUpSchema( $this->db );
                                $this->addDBDataOnce();
                        }
 
@@ -1152,6 +1155,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix );
                $dbClone->useTemporaryTables( self::$useTemporaryTables );
 
+               $db->_originalTablePrefix = $db->tablePrefix();
+
                if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
                        CloneDatabase::changePrefix( $prefix );
 
@@ -1295,6 +1300,133 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                return false;
        }
 
+       /**
+        * @throws LogicException if the given database connection is not a set up to use
+        * mock tables.
+        */
+       private function ensureMockDatabaseConnection( IDatabase $db ) {
+               if ( $db->tablePrefix() !== $this->dbPrefix() ) {
+                       throw new LogicException(
+                               'Trying to delete mock tables, but table prefix does not indicate a mock database.'
+                       );
+               }
+       }
+
+       /**
+        * 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.
+        */
+       protected function getSchemaOverrides() {
+               return [ [], [] ];
+       }
+
+       /**
+        * Applies any schema changes requested by calling setDbSchema().
+        * Called once per test class, just before addDataOnce().
+        */
+       private function setUpSchema( IMaintainableDatabase $db ) {
+               list( $tablesToAlter, $scriptsToRun ) = $this->getSchemaOverrides();
+
+               if ( $tablesToAlter && !$scriptsToRun ) {
+                       throw new InvalidArgumentException(
+                               'No scripts supplied for applying the database schema.'
+                       );
+               }
+
+               if ( !$tablesToAlter && $scriptsToRun ) {
+                       throw new InvalidArgumentException(
+                               'No tables declared to be altered by schema scripts.'
+                       );
+               }
+
+               $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 );
+
+               if ( $tablesToDrop ) {
+                       $this->dropMockTables( $db, $tablesToDrop );
+               }
+
+               if ( $tablesToRestore ) {
+                       $this->recloneMockTables( $db, $tablesToRestore );
+               }
+
+               foreach ( $scriptsToRun as $script ) {
+                       $db->sourceFile(
+                               $script,
+                               null,
+                               null,
+                               __METHOD__,
+                               function ( $cmd ) {
+                                       return $this->mungeSchemaUpdateQuery( $cmd );
+                               }
+                       );
+               }
+
+               $db->_alteredMockTables = $tablesToAlter;
+       }
+
+       private function mungeSchemaUpdateQuery( $cmd ) {
+               return self::$useTemporaryTables
+                       ? preg_replace( '/\bCREATE\s+TABLE\b/i', 'CREATE TEMPORARY TABLE', $cmd )
+                       : $cmd;
+       }
+
+       /**
+        * Drops the given mock tables.
+        *
+        * @param IMaintainableDatabase $db
+        * @param array $tables
+        */
+       private function dropMockTables( IMaintainableDatabase $db, array $tables ) {
+               $this->ensureMockDatabaseConnection( $db );
+
+               foreach ( $tables as $tbl ) {
+                       $tmp = self::$useTemporaryTables ? ' TEMPORARY ' : '';
+                       $tbl = $db->tableName( $tbl );
+                       $db->query( "DROP $tmp TABLE IF EXISTS $tbl", __METHOD__ );
+
+                       if ( $tbl === 'page' ) {
+                               // Forget about the pages since they don't
+                               // exist in the DB.
+                               LinkCache::singleton()->clear();
+                       }
+               }
+       }
+
+       /**
+        * Re-clones the given mock tables to restore them based on the live database schema.
+        *
+        * @param IMaintainableDatabase $db
+        * @param array $tables
+        */
+       private function recloneMockTables( IMaintainableDatabase $db, array $tables ) {
+               $this->ensureMockDatabaseConnection( $db );
+
+               if ( !isset( $db->_originalTablePrefix ) ) {
+                       throw new LogicException( 'No original table prefix know, cannot restore tables!' );
+               }
+
+               $originalTables = $db->listTables( $db->_originalTablePrefix, __METHOD__ );
+               $tables = array_intersect( $tables, $originalTables );
+
+               $dbClone = new CloneDatabase( $db, $tables, $db->tablePrefix(), $db->_originalTablePrefix );
+               $dbClone->useTemporaryTables( self::$useTemporaryTables );
+
+               $dbClone->cloneTableStructure();
+       }
+
        /**
         * Empty all tables so they can be repopulated for tests
         *
@@ -1386,7 +1518,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        }
 
        private static function isNotUnittest( $table ) {
-               return strpos( $table, 'unittest_' ) !== 0;
+               return strpos( $table, self::DB_PREFIX ) !== 0;
        }
 
        /**
diff --git a/tests/phpunit/tests/MediaWikiTestCaseSchema1Test.php b/tests/phpunit/tests/MediaWikiTestCaseSchema1Test.php
new file mode 100644 (file)
index 0000000..4b0e0bf
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @covers MediaWikiTestCase
+ *
+ * @group Database
+ * @group MediaWikiTestCaseTest
+ */
+class MediaWikiTestCaseSchema1Test extends MediaWikiTestCase {
+
+       public function getSchemaOverrides() {
+               return [
+                       [ 'imagelinks', 'MediaWikiTestCaseTestTable' ],
+                       [ __DIR__ . '/MediaWikiTestCaseSchemaTest.sql' ]
+               ];
+       }
+
+       public function testSchemaExtension() {
+               // make sure we can use the MediaWikiTestCaseTestTable table
+
+               $input = [ 'id' => '5', 'name' => 'Test' ];
+
+               $this->db->insert(
+                       'MediaWikiTestCaseTestTable',
+                       $input
+               );
+
+               $output = $this->db->selectRow( 'MediaWikiTestCaseTestTable', array_keys( $input ), [] );
+               $this->assertEquals( (object)$input, $output );
+       }
+
+       public function testSchemaOverride() {
+               // make sure we can use the il_frobniz field
+
+               $input = [
+                       'il_from' => '7',
+                       'il_from_namespace' => '0',
+                       'il_to' => 'Foo.jpg',
+                       'il_frobniz' => 'Xyzzy',
+               ];
+
+               $this->db->insert(
+                       'imagelinks',
+                       $input
+               );
+
+               $output = $this->db->selectRow( 'imagelinks', array_keys( $input ), [] );
+               $this->assertEquals( (object)$input, $output );
+       }
+
+}
diff --git a/tests/phpunit/tests/MediaWikiTestCaseSchema2Test.php b/tests/phpunit/tests/MediaWikiTestCaseSchema2Test.php
new file mode 100644 (file)
index 0000000..b1c65ee
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @covers MediaWikiTestCase
+ *
+ * @group Database
+ * @group MediaWikiTestCaseTest
+ *
+ * This test is intended to be executed AFTER MediaWikiTestCaseSchema1Test to ensure
+ * that any schema modifications have been cleaned up between test cases.
+ * As there seems to be no way to force execution order, we currently rely on
+ * test classes getting run in anpha-numerical order.
+ */
+class MediaWikiTestCaseSchema2Test extends MediaWikiTestCase {
+
+       public function testSchemaExtension() {
+               // Make sure MediaWikiTestCaseTestTable created by MediaWikiTestCaseSchema1Test
+               // was dropped before executing MediaWikiTestCaseSchema2Test.
+               $this->assertFalse( $this->db->tableExists( 'MediaWikiTestCaseTestTable' ) );
+       }
+
+       public function testSchemaOverride() {
+               // Make sure imagelinks modified by MediaWikiTestCaseSchema1Test
+               // was restored to the original schema before executing MediaWikiTestCaseSchema2Test.
+               $this->assertTrue( $this->db->tableExists( 'imagelinks' ) );
+               $this->assertFalse( $this->db->fieldExists( 'imagelinks', 'il_frobniz' ) );
+       }
+
+}
diff --git a/tests/phpunit/tests/MediaWikiTestCaseSchemaTest.sql b/tests/phpunit/tests/MediaWikiTestCaseSchemaTest.sql
new file mode 100644 (file)
index 0000000..e5ef5c6
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE TABLE /*_*/MediaWikiTestCaseTestTable (
+  id INT NOT NULL,
+  name VARCHAR(20) NOT NULL,
+  PRIMARY KEY (id)
+) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*_*/imagelinks (
+  il_from int(10) unsigned NOT NULL DEFAULT 0,
+  il_from_namespace int(11) NOT NULL DEFAULT 0,
+  il_to varbinary(255) NOT NULL DEFAULT '',
+  il_frobniz varchar(255) NOT NULL DEFAULT 'FROB',
+  PRIMARY KEY (il_from,il_to)
+) /*$wgDBTableOptions*/;
index 7d75ffe..fb2957b 100644 (file)
@@ -2,9 +2,12 @@
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
 use Psr\Log\LoggerInterface;
+use Wikimedia\Rdbms\LoadBalancer;
 
 /**
  * @covers MediaWikiTestCase
+ * @group MediaWikiTestCaseTest
+ *
  * @author Addshore
  */
 class MediaWikiTestCaseTest extends MediaWikiTestCase {