Merge "Add Special:Search sort parameter without ui"
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / rdbms / database / DatabaseTest.php
1 <?php
2
3 use Wikimedia\Rdbms\Database;
4 use Wikimedia\Rdbms\IDatabase;
5 use Wikimedia\Rdbms\DatabaseDomain;
6 use Wikimedia\Rdbms\DatabaseMysqli;
7 use Wikimedia\Rdbms\LBFactorySingle;
8 use Wikimedia\Rdbms\TransactionProfiler;
9 use Wikimedia\TestingAccessWrapper;
10 use Wikimedia\Rdbms\DatabaseSqlite;
11 use Wikimedia\Rdbms\DatabasePostgres;
12 use Wikimedia\Rdbms\DatabaseMssql;
13 use Wikimedia\Rdbms\DBUnexpectedError;
14
15 class DatabaseTest extends PHPUnit\Framework\TestCase {
16
17 use MediaWikiCoversValidator;
18
19 protected function setUp() {
20 $this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
21 }
22
23 /**
24 * @dataProvider provideAddQuotes
25 * @covers Wikimedia\Rdbms\Database::factory
26 */
27 public function testFactory() {
28 $m = Database::NEW_UNCONNECTED; // no-connect mode
29 $p = [ 'host' => 'localhost', 'user' => 'me', 'password' => 'myself', 'dbname' => 'i' ];
30
31 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysqli', $p, $m ) );
32 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySqli', $p, $m ) );
33 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQLi', $p, $m ) );
34 $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'postgres', $p, $m ) );
35 $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'Postgres', $p, $m ) );
36
37 $x = $p + [ 'port' => 10000, 'UseWindowsAuth' => false ];
38 $this->assertInstanceOf( DatabaseMssql::class, Database::factory( 'mssql', $x, $m ) );
39
40 $x = $p + [ 'dbFilePath' => 'some/file.sqlite' ];
41 $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
42 $x = $p + [ 'dbDirectory' => 'some/file' ];
43 $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
44 }
45
46 public static function provideAddQuotes() {
47 return [
48 [ null, 'NULL' ],
49 [ 1234, "'1234'" ],
50 [ 1234.5678, "'1234.5678'" ],
51 [ 'string', "'string'" ],
52 [ 'string\'s cause trouble', "'string\'s cause trouble'" ],
53 ];
54 }
55
56 /**
57 * @dataProvider provideAddQuotes
58 * @covers Wikimedia\Rdbms\Database::addQuotes
59 */
60 public function testAddQuotes( $input, $expected ) {
61 $this->assertEquals( $expected, $this->db->addQuotes( $input ) );
62 }
63
64 public static function provideTableName() {
65 // Formatting is mostly ignored since addIdentifierQuotes is abstract.
66 // For testing of addIdentifierQuotes, see actual Database subclas tests.
67 return [
68 'local' => [
69 'tablename',
70 'tablename',
71 'quoted',
72 ],
73 'local-raw' => [
74 'tablename',
75 'tablename',
76 'raw',
77 ],
78 'shared' => [
79 'sharedb.tablename',
80 'tablename',
81 'quoted',
82 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
83 ],
84 'shared-raw' => [
85 'sharedb.tablename',
86 'tablename',
87 'raw',
88 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
89 ],
90 'shared-prefix' => [
91 'sharedb.sh_tablename',
92 'tablename',
93 'quoted',
94 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
95 ],
96 'shared-prefix-raw' => [
97 'sharedb.sh_tablename',
98 'tablename',
99 'raw',
100 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
101 ],
102 'foreign' => [
103 'databasename.tablename',
104 'databasename.tablename',
105 'quoted',
106 ],
107 'foreign-raw' => [
108 'databasename.tablename',
109 'databasename.tablename',
110 'raw',
111 ],
112 ];
113 }
114
115 /**
116 * @dataProvider provideTableName
117 * @covers Wikimedia\Rdbms\Database::tableName
118 */
119 public function testTableName( $expected, $table, $format, array $alias = null ) {
120 if ( $alias ) {
121 $this->db->setTableAliases( [ $table => $alias ] );
122 }
123 $this->assertEquals(
124 $expected,
125 $this->db->tableName( $table, $format ?: 'quoted' )
126 );
127 }
128
129 public function provideTableNamesWithIndexClauseOrJOIN() {
130 return [
131 'one-element array' => [
132 [ 'table' ], [], 'table '
133 ],
134 'comma join' => [
135 [ 'table1', 'table2' ], [], 'table1,table2 '
136 ],
137 'real join' => [
138 [ 'table1', 'table2' ],
139 [ 'table2' => [ 'LEFT JOIN', 't1_id = t2_id' ] ],
140 'table1 LEFT JOIN table2 ON ((t1_id = t2_id))'
141 ],
142 'real join with multiple conditionals' => [
143 [ 'table1', 'table2' ],
144 [ 'table2' => [ 'LEFT JOIN', [ 't1_id = t2_id', 't2_x = \'X\'' ] ] ],
145 'table1 LEFT JOIN table2 ON ((t1_id = t2_id) AND (t2_x = \'X\'))'
146 ],
147 'join with parenthesized group' => [
148 [ 'table1', 'n' => [ 'table2', 'table3' ] ],
149 [
150 'table3' => [ 'JOIN', 't2_id = t3_id' ],
151 'n' => [ 'LEFT JOIN', 't1_id = t2_id' ],
152 ],
153 'table1 LEFT JOIN (table2 JOIN table3 ON ((t2_id = t3_id))) ON ((t1_id = t2_id))'
154 ],
155 'join with degenerate parenthesized group' => [
156 [ 'table1', 'n' => [ 't2' => 'table2' ] ],
157 [
158 'n' => [ 'LEFT JOIN', 't1_id = t2_id' ],
159 ],
160 'table1 LEFT JOIN table2 t2 ON ((t1_id = t2_id))'
161 ],
162 ];
163 }
164
165 /**
166 * @dataProvider provideTableNamesWithIndexClauseOrJOIN
167 * @covers Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN
168 */
169 public function testTableNamesWithIndexClauseOrJOIN( $tables, $join_conds, $expect ) {
170 $clause = TestingAccessWrapper::newFromObject( $this->db )
171 ->tableNamesWithIndexClauseOrJOIN( $tables, [], [], $join_conds );
172 $this->assertSame( $expect, $clause );
173 }
174
175 /**
176 * @covers Wikimedia\Rdbms\Database::onTransactionCommitOrIdle
177 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
178 */
179 public function testTransactionIdle() {
180 $db = $this->db;
181
182 $db->clearFlag( DBO_TRX );
183 $called = false;
184 $flagSet = null;
185 $callback = function ( $trigger, IDatabase $db ) use ( &$flagSet, &$called ) {
186 $called = true;
187 $flagSet = $db->getFlag( DBO_TRX );
188 };
189
190 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
191 $this->assertTrue( $called, 'Callback reached' );
192 $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
193 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
194
195 $flagSet = null;
196 $called = false;
197 $db->startAtomic( __METHOD__ );
198 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
199 $this->assertFalse( $called, 'Callback not reached during TRX' );
200 $db->endAtomic( __METHOD__ );
201
202 $this->assertTrue( $called, 'Callback reached after COMMIT' );
203 $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
204 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
205
206 $db->clearFlag( DBO_TRX );
207 $db->onTransactionCommitOrIdle(
208 function ( $trigger, IDatabase $db ) {
209 $db->setFlag( DBO_TRX );
210 },
211 __METHOD__
212 );
213 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
214 }
215
216 /**
217 * @covers Wikimedia\Rdbms\Database::onTransactionCommitOrIdle
218 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
219 */
220 public function testTransactionIdle_TRX() {
221 $db = $this->getMockDB( [ 'isOpen', 'ping' ] );
222 $db->method( 'isOpen' )->willReturn( true );
223 $db->method( 'ping' )->willReturn( true );
224 $db->setFlag( DBO_TRX );
225
226 $lbFactory = LBFactorySingle::newFromConnection( $db );
227 // Ask for the connection so that LB sets internal state
228 // about this connection being the master connection
229 $lb = $lbFactory->getMainLB();
230 $conn = $lb->openConnection( $lb->getWriterIndex() );
231 $this->assertSame( $db, $conn, 'Same DB instance' );
232 $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
233
234 $called = false;
235 $flagSet = null;
236 $callback = function () use ( $db, &$flagSet, &$called ) {
237 $called = true;
238 $flagSet = $db->getFlag( DBO_TRX );
239 };
240
241 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
242 $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
243 $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
244 $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
245
246 $called = false;
247 $lbFactory->beginMasterChanges( __METHOD__ );
248 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
249 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
250
251 $lbFactory->commitMasterChanges( __METHOD__ );
252 $this->assertTrue( $called, 'Called when lb-transaction is committed' );
253
254 $called = false;
255 $lbFactory->beginMasterChanges( __METHOD__ );
256 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
257 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
258
259 $lbFactory->rollbackMasterChanges( __METHOD__ );
260 $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
261
262 $lbFactory->commitMasterChanges( __METHOD__ );
263 $this->assertFalse( $called, 'Not called in next round commit' );
264
265 $db->setFlag( DBO_TRX );
266 try {
267 $db->onTransactionCommitOrIdle( function () {
268 throw new RuntimeException( 'test' );
269 } );
270 $this->fail( "Exception not thrown" );
271 } catch ( RuntimeException $e ) {
272 $this->assertTrue( $db->getFlag( DBO_TRX ) );
273 }
274 }
275
276 /**
277 * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
278 * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
279 */
280 public function testTransactionPreCommitOrIdle() {
281 $db = $this->getMockDB( [ 'isOpen' ] );
282 $db->method( 'isOpen' )->willReturn( true );
283 $db->clearFlag( DBO_TRX );
284
285 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX is not set' );
286
287 $called = false;
288 $db->onTransactionPreCommitOrIdle(
289 function ( IDatabase $db ) use ( &$called ) {
290 $called = true;
291 },
292 __METHOD__
293 );
294 $this->assertTrue( $called, 'Called when idle' );
295
296 $db->begin( __METHOD__ );
297 $called = false;
298 $db->onTransactionPreCommitOrIdle(
299 function ( IDatabase $db ) use ( &$called ) {
300 $called = true;
301 },
302 __METHOD__
303 );
304 $this->assertFalse( $called, 'Not called when transaction is active' );
305 $db->commit( __METHOD__ );
306 $this->assertTrue( $called, 'Called when transaction is committed' );
307 }
308
309 /**
310 * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
311 * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
312 */
313 public function testTransactionPreCommitOrIdle_TRX() {
314 $db = $this->getMockDB( [ 'isOpen', 'ping' ] );
315 $db->method( 'isOpen' )->willReturn( true );
316 $db->method( 'ping' )->willReturn( true );
317 $db->setFlag( DBO_TRX );
318
319 $lbFactory = LBFactorySingle::newFromConnection( $db );
320 // Ask for the connection so that LB sets internal state
321 // about this connection being the master connection
322 $lb = $lbFactory->getMainLB();
323 $conn = $lb->openConnection( $lb->getWriterIndex() );
324 $this->assertSame( $db, $conn, 'Same DB instance' );
325
326 $this->assertFalse( $lb->hasMasterChanges() );
327 $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
328 $called = false;
329 $callback = function ( IDatabase $db ) use ( &$called ) {
330 $called = true;
331 };
332 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
333 $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
334 $called = false;
335 $lbFactory->commitMasterChanges();
336 $this->assertFalse( $called );
337
338 $called = false;
339 $lbFactory->beginMasterChanges( __METHOD__ );
340 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
341 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
342 $lbFactory->commitMasterChanges( __METHOD__ );
343 $this->assertTrue( $called, 'Called when lb-transaction is committed' );
344
345 $called = false;
346 $lbFactory->beginMasterChanges( __METHOD__ );
347 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
348 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
349
350 $lbFactory->rollbackMasterChanges( __METHOD__ );
351 $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
352
353 $lbFactory->commitMasterChanges( __METHOD__ );
354 $this->assertFalse( $called, 'Not called in next round commit' );
355 }
356
357 /**
358 * @covers Wikimedia\Rdbms\Database::onTransactionResolution
359 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
360 */
361 public function testTransactionResolution() {
362 $db = $this->db;
363
364 $db->clearFlag( DBO_TRX );
365 $db->begin( __METHOD__ );
366 $called = false;
367 $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
368 $called = true;
369 $db->setFlag( DBO_TRX );
370 } );
371 $db->commit( __METHOD__ );
372 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
373 $this->assertTrue( $called, 'Callback reached' );
374
375 $db->clearFlag( DBO_TRX );
376 $db->begin( __METHOD__ );
377 $called = false;
378 $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
379 $called = true;
380 $db->setFlag( DBO_TRX );
381 } );
382 $db->rollback( __METHOD__ );
383 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
384 $this->assertTrue( $called, 'Callback reached' );
385 }
386
387 /**
388 * @covers Wikimedia\Rdbms\Database::setTransactionListener
389 */
390 public function testTransactionListener() {
391 $db = $this->db;
392
393 $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
394 $called = true;
395 } );
396
397 $called = false;
398 $db->begin( __METHOD__ );
399 $db->commit( __METHOD__ );
400 $this->assertTrue( $called, 'Callback reached' );
401
402 $called = false;
403 $db->begin( __METHOD__ );
404 $db->commit( __METHOD__ );
405 $this->assertTrue( $called, 'Callback still reached' );
406
407 $called = false;
408 $db->begin( __METHOD__ );
409 $db->rollback( __METHOD__ );
410 $this->assertTrue( $called, 'Callback reached' );
411
412 $db->setTransactionListener( 'ping', null );
413 $called = false;
414 $db->begin( __METHOD__ );
415 $db->commit( __METHOD__ );
416 $this->assertFalse( $called, 'Callback not reached' );
417 }
418
419 /**
420 * Use this mock instead of DatabaseTestHelper for cases where
421 * DatabaseTestHelper is too inflexibile due to mocking too much
422 * or being too restrictive about fname matching (e.g. for tests
423 * that assert behaviour when the name is a mismatch, we need to
424 * catch the error here instead of there).
425 *
426 * @return Database
427 */
428 private function getMockDB( $methods = [] ) {
429 static $abstractMethods = [
430 'fetchAffectedRowCount',
431 'closeConnection',
432 'dataSeek',
433 'doQuery',
434 'fetchObject', 'fetchRow',
435 'fieldInfo', 'fieldName',
436 'getSoftwareLink', 'getServerVersion',
437 'getType',
438 'indexInfo',
439 'insertId',
440 'lastError', 'lastErrno',
441 'numFields', 'numRows',
442 'open',
443 'strencode',
444 'tableExists'
445 ];
446 $db = $this->getMockBuilder( Database::class )
447 ->disableOriginalConstructor()
448 ->setMethods( array_values( array_unique( array_merge(
449 $abstractMethods,
450 $methods
451 ) ) ) )
452 ->getMock();
453 $wdb = TestingAccessWrapper::newFromObject( $db );
454 $wdb->trxProfiler = new TransactionProfiler();
455 $wdb->connLogger = new \Psr\Log\NullLogger();
456 $wdb->queryLogger = new \Psr\Log\NullLogger();
457 $wdb->currentDomain = DatabaseDomain::newUnspecified();
458 return $db;
459 }
460
461 /**
462 * @covers Wikimedia\Rdbms\Database::flushSnapshot
463 */
464 public function testFlushSnapshot() {
465 $db = $this->getMockDB( [ 'isOpen' ] );
466 $db->method( 'isOpen' )->willReturn( true );
467
468 $db->flushSnapshot( __METHOD__ ); // ok
469 $db->flushSnapshot( __METHOD__ ); // ok
470
471 $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
472 $db->query( 'SELECT 1', __METHOD__ );
473 $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
474 $db->flushSnapshot( __METHOD__ ); // ok
475 $db->restoreFlags( $db::RESTORE_PRIOR );
476
477 $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
478 }
479
480 /**
481 * @covers Wikimedia\Rdbms\Database::getScopedLockAndFlush
482 * @covers Wikimedia\Rdbms\Database::lock
483 * @covers Wikimedia\Rdbms\Database::unlock
484 * @covers Wikimedia\Rdbms\Database::lockIsFree
485 */
486 public function testGetScopedLock() {
487 $db = $this->getMockDB( [ 'isOpen' ] );
488 $db->method( 'isOpen' )->willReturn( true );
489
490 $this->assertEquals( 0, $db->trxLevel() );
491 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
492 $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
493 $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
494 $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
495 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
496 $this->assertEquals( 0, $db->trxLevel() );
497
498 $db->setFlag( DBO_TRX );
499 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
500 $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
501 $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
502 $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
503 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
504 $db->clearFlag( DBO_TRX );
505
506 // Pending writes with DBO_TRX
507 $this->assertEquals( 0, $db->trxLevel() );
508 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
509 $db->setFlag( DBO_TRX );
510 $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
511 try {
512 $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
513 $this->fail( "Exception not reached" );
514 } catch ( DBUnexpectedError $e ) {
515 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
516 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ), 'Lock not acquired' );
517 }
518 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
519 // Pending writes without DBO_TRX
520 $db->clearFlag( DBO_TRX );
521 $this->assertEquals( 0, $db->trxLevel() );
522 $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ) );
523 $db->begin( __METHOD__ );
524 $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
525 try {
526 $lock = $db->getScopedLockAndFlush( 'meow2', __METHOD__, 1 );
527 $this->fail( "Exception not reached" );
528 } catch ( DBUnexpectedError $e ) {
529 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
530 $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ), 'Lock not acquired' );
531 }
532 $db->rollback( __METHOD__ );
533 // No pending writes, with DBO_TRX
534 $db->setFlag( DBO_TRX );
535 $this->assertEquals( 0, $db->trxLevel() );
536 $this->assertTrue( $db->lockIsFree( 'wuff', __METHOD__ ) );
537 $db->query( "SELECT 1", __METHOD__ );
538 $this->assertEquals( 1, $db->trxLevel() );
539 $lock = $db->getScopedLockAndFlush( 'wuff', __METHOD__, 1 );
540 $this->assertEquals( 0, $db->trxLevel() );
541 $this->assertFalse( $db->lockIsFree( 'wuff', __METHOD__ ), 'Lock already acquired' );
542 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
543 // No pending writes, without DBO_TRX
544 $db->clearFlag( DBO_TRX );
545 $this->assertEquals( 0, $db->trxLevel() );
546 $this->assertTrue( $db->lockIsFree( 'wuff2', __METHOD__ ) );
547 $db->begin( __METHOD__ );
548 try {
549 $lock = $db->getScopedLockAndFlush( 'wuff2', __METHOD__, 1 );
550 $this->fail( "Exception not reached" );
551 } catch ( DBUnexpectedError $e ) {
552 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
553 $this->assertFalse( $db->lockIsFree( 'wuff2', __METHOD__ ), 'Lock not acquired' );
554 }
555 $db->rollback( __METHOD__ );
556 }
557
558 /**
559 * @covers Wikimedia\Rdbms\Database::getFlag
560 * @covers Wikimedia\Rdbms\Database::setFlag
561 * @covers Wikimedia\Rdbms\Database::restoreFlags
562 */
563 public function testFlagSetting() {
564 $db = $this->db;
565 $origTrx = $db->getFlag( DBO_TRX );
566 $origSsl = $db->getFlag( DBO_SSL );
567
568 $origTrx
569 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
570 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
571 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
572
573 $origSsl
574 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
575 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
576 $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
577
578 $db->restoreFlags( $db::RESTORE_INITIAL );
579 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
580 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
581
582 $origTrx
583 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
584 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
585 $origSsl
586 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
587 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
588
589 $db->restoreFlags();
590 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
591 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
592
593 $db->restoreFlags();
594 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
595 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
596 }
597
598 /**
599 * @expectedException UnexpectedValueException
600 * @covers Wikimedia\Rdbms\Database::setFlag
601 */
602 public function testDBOIgnoreSet() {
603 $db = $this->getMockBuilder( DatabaseMysqli::class )
604 ->disableOriginalConstructor()
605 ->setMethods( null )
606 ->getMock();
607
608 $db->setFlag( Database::DBO_IGNORE );
609 }
610
611 /**
612 * @expectedException UnexpectedValueException
613 * @covers Wikimedia\Rdbms\Database::clearFlag
614 */
615 public function testDBOIgnoreClear() {
616 $db = $this->getMockBuilder( DatabaseMysqli::class )
617 ->disableOriginalConstructor()
618 ->setMethods( null )
619 ->getMock();
620
621 $db->clearFlag( Database::DBO_IGNORE );
622 }
623
624 /**
625 * @covers Wikimedia\Rdbms\Database::tablePrefix
626 * @covers Wikimedia\Rdbms\Database::dbSchema
627 */
628 public function testMutators() {
629 $old = $this->db->tablePrefix();
630 $this->assertInternalType( 'string', $old, 'Prefix is string' );
631 $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
632 $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
633 $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
634 $this->db->tablePrefix( $old );
635 $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
636
637 $old = $this->db->dbSchema();
638 $this->assertInternalType( 'string', $old, 'Schema is string' );
639 $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
640 $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
641 $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
642 $this->db->dbSchema( $old );
643 $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
644 }
645 }