rdbms: clean up DBO_TRX behavior for onTransaction* callbacks
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / rdbms / database / DatabaseTest.php
index b42a367..94b4e0d 100644 (file)
@@ -1,11 +1,16 @@
 <?php
 
+use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\DatabaseMysqli;
 use Wikimedia\Rdbms\LBFactorySingle;
 use Wikimedia\Rdbms\TransactionProfiler;
 use Wikimedia\TestingAccessWrapper;
+use Wikimedia\Rdbms\DatabaseSqlite;
+use Wikimedia\Rdbms\DatabasePostgres;
+use Wikimedia\Rdbms\DatabaseMssql;
 
-class DatabaseTest extends PHPUnit_Framework_TestCase {
+class DatabaseTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
@@ -13,6 +18,29 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
        }
 
+       /**
+        * @dataProvider provideAddQuotes
+        * @covers Wikimedia\Rdbms\Database::factory
+        */
+       public function testFactory() {
+               $m = Database::NEW_UNCONNECTED; // no-connect mode
+               $p = [ 'host' => 'localhost', 'user' => 'me', 'password' => 'myself', 'dbname' => 'i' ];
+
+               $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysqli', $p, $m ) );
+               $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySqli', $p, $m ) );
+               $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQLi', $p, $m ) );
+               $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'postgres', $p, $m ) );
+               $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'Postgres', $p, $m ) );
+
+               $x = $p + [ 'port' => 10000, 'UseWindowsAuth' => false ];
+               $this->assertInstanceOf( DatabaseMssql::class, Database::factory( 'mssql', $x, $m ) );
+
+               $x = $p + [ 'dbFilePath' => 'some/file.sqlite' ];
+               $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
+               $x = $p + [ 'dbDirectory' => 'some/file' ];
+               $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
+       }
+
        public static function provideAddQuotes() {
                return [
                        [ null, 'NULL' ],
@@ -149,28 +177,27 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
        public function testTransactionIdle() {
                $db = $this->db;
 
-               $db->setFlag( DBO_TRX );
+               $db->clearFlag( DBO_TRX );
                $called = false;
                $flagSet = null;
-               $db->onTransactionIdle(
-                       function () use ( $db, &$flagSet, &$called ) {
-                               $called = true;
-                               $flagSet = $db->getFlag( DBO_TRX );
-                       },
-                       __METHOD__
-               );
-               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
-               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+               $callback = function () use ( $db, &$flagSet, &$called ) {
+                       $called = true;
+                       $flagSet = $db->getFlag( DBO_TRX );
+               };
+
+               $db->onTransactionIdle( $callback, __METHOD__ );
                $this->assertTrue( $called, 'Callback reached' );
+               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
 
-               $db->clearFlag( DBO_TRX );
                $flagSet = null;
-               $db->onTransactionIdle(
-                       function () use ( $db, &$flagSet ) {
-                               $flagSet = $db->getFlag( DBO_TRX );
-                       },
-                       __METHOD__
-               );
+               $called = false;
+               $db->startAtomic( __METHOD__ );
+               $db->onTransactionIdle( $callback, __METHOD__ );
+               $this->assertFalse( $called, 'Callback not reached during TRX' );
+               $db->endAtomic( __METHOD__ );
+
+               $this->assertTrue( $called, 'Callback reached after COMMIT' );
                $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
                $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
 
@@ -184,6 +211,56 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
        }
 
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionIdle
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
+        */
+       public function testTransactionIdle_TRX() {
+               $db = $this->getMockDB( [ 'isOpen', 'ping' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+               $db->method( 'ping' )->willReturn( true );
+               $db->setFlag( DBO_TRX );
+
+               $lbFactory = LBFactorySingle::newFromConnection( $db );
+               // Ask for the connection so that LB sets internal state
+               // about this connection being the master connection
+               $lb = $lbFactory->getMainLB();
+               $conn = $lb->openConnection( $lb->getWriterIndex() );
+               $this->assertSame( $db, $conn, 'Same DB instance' );
+               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
+
+               $called = false;
+               $flagSet = null;
+               $callback = function () use ( $db, &$flagSet, &$called ) {
+                       $called = true;
+                       $flagSet = $db->getFlag( DBO_TRX );
+               };
+
+               $db->onTransactionIdle( $callback, __METHOD__ );
+               $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
+               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
+               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
+
+               $called = false;
+               $lbFactory->beginMasterChanges( __METHOD__ );
+               $db->onTransactionIdle( $callback, __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is active' );
+
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $this->assertTrue( $called, 'Called when lb-transaction is committed' );
+
+               $called = false;
+               $lbFactory->beginMasterChanges( __METHOD__ );
+               $db->onTransactionIdle( $callback, __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is active' );
+
+               $lbFactory->rollbackMasterChanges( __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
+
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $this->assertFalse( $called, 'Not called in next round commit' );
+       }
+
        /**
         * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
         * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
@@ -222,12 +299,13 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
         * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
         */
        public function testTransactionPreCommitOrIdle_TRX() {
-               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db = $this->getMockDB( [ 'isOpen', 'ping' ] );
                $db->method( 'isOpen' )->willReturn( true );
+               $db->method( 'ping' )->willReturn( true );
                $db->setFlag( DBO_TRX );
 
                $lbFactory = LBFactorySingle::newFromConnection( $db );
-               // Ask for the connectin so that LB sets internal state
+               // Ask for the connection so that LB sets internal state
                // about this connection being the master connection
                $lb = $lbFactory->getMainLB();
                $conn = $lb->openConnection( $lb->getWriterIndex() );
@@ -235,18 +313,29 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
 
                $called = false;
-               $db->onTransactionPreCommitOrIdle(
-                       function () use ( &$called ) {
-                               $called = true;
-                       }
-               );
-               $this->assertFalse( $called, 'Not called when idle if DBO_TRX is set' );
+               $callback = function () use ( &$called ) {
+                       $called = true;
+               };
+               $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
+               $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
 
+               $called = false;
                $lbFactory->beginMasterChanges( __METHOD__ );
+               $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
                $this->assertFalse( $called, 'Not called when lb-transaction is active' );
-
                $lbFactory->commitMasterChanges( __METHOD__ );
                $this->assertTrue( $called, 'Called when lb-transaction is committed' );
+
+               $called = false;
+               $lbFactory->beginMasterChanges( __METHOD__ );
+               $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is active' );
+
+               $lbFactory->rollbackMasterChanges( __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
+
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $this->assertFalse( $called, 'Not called in next round commit' );
        }
 
        /**
@@ -469,6 +558,32 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
        }
 
+       /**
+        * @expectedException UnexpectedValueException
+        * @covers Wikimedia\Rdbms\Database::setFlag
+        */
+       public function testDBOIgnoreSet() {
+               $db = $this->getMockBuilder( DatabaseMysqli::class )
+                       ->disableOriginalConstructor()
+                       ->setMethods( null )
+                       ->getMock();
+
+               $db->setFlag( Database::DBO_IGNORE );
+       }
+
+       /**
+        * @expectedException UnexpectedValueException
+        * @covers Wikimedia\Rdbms\Database::clearFlag
+        */
+       public function testDBOIgnoreClear() {
+               $db = $this->getMockBuilder( DatabaseMysqli::class )
+                       ->disableOriginalConstructor()
+                       ->setMethods( null )
+                       ->getMock();
+
+               $db->clearFlag( Database::DBO_IGNORE );
+       }
+
        /**
         * @covers Wikimedia\Rdbms\Database::tablePrefix
         * @covers Wikimedia\Rdbms\Database::dbSchema