Merge "CSSMin: Skip #default#behaviorName when detecting local files"
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / rdbms / database / DatabaseTest.php
1 <?php
2
3 use Wikimedia\Rdbms\IDatabase;
4 use Wikimedia\Rdbms\TransactionProfiler;
5 use Wikimedia\TestingAccessWrapper;
6
7 class DatabaseTest extends PHPUnit_Framework_TestCase {
8
9 protected function setUp() {
10 $this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
11 }
12
13 public static function provideAddQuotes() {
14 return [
15 [ null, 'NULL' ],
16 [ 1234, "'1234'" ],
17 [ 1234.5678, "'1234.5678'" ],
18 [ 'string', "'string'" ],
19 [ 'string\'s cause trouble', "'string\'s cause trouble'" ],
20 ];
21 }
22
23 /**
24 * @dataProvider provideAddQuotes
25 * @covers Wikimedia\Rdbms\Database::addQuotes
26 */
27 public function testAddQuotes( $input, $expected ) {
28 $this->assertEquals( $expected, $this->db->addQuotes( $input ) );
29 }
30
31 public static function provideTableName() {
32 // Formatting is mostly ignored since addIdentifierQuotes is abstract.
33 // For testing of addIdentifierQuotes, see actual Database subclas tests.
34 return [
35 'local' => [
36 'tablename',
37 'tablename',
38 'quoted',
39 ],
40 'local-raw' => [
41 'tablename',
42 'tablename',
43 'raw',
44 ],
45 'shared' => [
46 'sharedb.tablename',
47 'tablename',
48 'quoted',
49 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
50 ],
51 'shared-raw' => [
52 'sharedb.tablename',
53 'tablename',
54 'raw',
55 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
56 ],
57 'shared-prefix' => [
58 'sharedb.sh_tablename',
59 'tablename',
60 'quoted',
61 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
62 ],
63 'shared-prefix-raw' => [
64 'sharedb.sh_tablename',
65 'tablename',
66 'raw',
67 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
68 ],
69 'foreign' => [
70 'databasename.tablename',
71 'databasename.tablename',
72 'quoted',
73 ],
74 'foreign-raw' => [
75 'databasename.tablename',
76 'databasename.tablename',
77 'raw',
78 ],
79 ];
80 }
81
82 /**
83 * @dataProvider provideTableName
84 * @covers Wikimedia\Rdbms\Database::tableName
85 */
86 public function testTableName( $expected, $table, $format, array $alias = null ) {
87 if ( $alias ) {
88 $this->db->setTableAliases( [ $table => $alias ] );
89 }
90 $this->assertEquals(
91 $expected,
92 $this->db->tableName( $table, $format ?: 'quoted' )
93 );
94 }
95
96 /**
97 * @covers Wikimedia\Rdbms\Database::onTransactionIdle
98 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
99 */
100 public function testTransactionIdle() {
101 $db = $this->db;
102
103 $db->setFlag( DBO_TRX );
104 $called = false;
105 $flagSet = null;
106 $db->onTransactionIdle(
107 function () use ( $db, &$flagSet, &$called ) {
108 $called = true;
109 $flagSet = $db->getFlag( DBO_TRX );
110 },
111 __METHOD__
112 );
113 $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
114 $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
115 $this->assertTrue( $called, 'Callback reached' );
116
117 $db->clearFlag( DBO_TRX );
118 $flagSet = null;
119 $db->onTransactionIdle(
120 function () use ( $db, &$flagSet ) {
121 $flagSet = $db->getFlag( DBO_TRX );
122 },
123 __METHOD__
124 );
125 $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
126 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
127
128 $db->clearFlag( DBO_TRX );
129 $db->onTransactionIdle(
130 function () use ( $db ) {
131 $db->setFlag( DBO_TRX );
132 },
133 __METHOD__
134 );
135 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
136 }
137
138 /**
139 * @covers Wikimedia\Rdbms\Database::onTransactionResolution
140 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
141 */
142 public function testTransactionResolution() {
143 $db = $this->db;
144
145 $db->clearFlag( DBO_TRX );
146 $db->begin( __METHOD__ );
147 $called = false;
148 $db->onTransactionResolution( function () use ( $db, &$called ) {
149 $called = true;
150 $db->setFlag( DBO_TRX );
151 } );
152 $db->commit( __METHOD__ );
153 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
154 $this->assertTrue( $called, 'Callback reached' );
155
156 $db->clearFlag( DBO_TRX );
157 $db->begin( __METHOD__ );
158 $called = false;
159 $db->onTransactionResolution( function () use ( $db, &$called ) {
160 $called = true;
161 $db->setFlag( DBO_TRX );
162 } );
163 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
164 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
165 $this->assertTrue( $called, 'Callback reached' );
166 }
167
168 /**
169 * @covers Wikimedia\Rdbms\Database::setTransactionListener
170 */
171 public function testTransactionListener() {
172 $db = $this->db;
173
174 $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
175 $called = true;
176 } );
177
178 $called = false;
179 $db->begin( __METHOD__ );
180 $db->commit( __METHOD__ );
181 $this->assertTrue( $called, 'Callback reached' );
182
183 $called = false;
184 $db->begin( __METHOD__ );
185 $db->commit( __METHOD__ );
186 $this->assertTrue( $called, 'Callback still reached' );
187
188 $called = false;
189 $db->begin( __METHOD__ );
190 $db->rollback( __METHOD__ );
191 $this->assertTrue( $called, 'Callback reached' );
192
193 $db->setTransactionListener( 'ping', null );
194 $called = false;
195 $db->begin( __METHOD__ );
196 $db->commit( __METHOD__ );
197 $this->assertFalse( $called, 'Callback not reached' );
198 }
199
200 /**
201 * Use this mock instead of DatabaseTestHelper for cases where
202 * DatabaseTestHelper is too inflexibile due to mocking too much
203 * or being too restrictive about fname matching (e.g. for tests
204 * that assert behaviour when the name is a mismatch, we need to
205 * catch the error here instead of there).
206 *
207 * @return Database
208 */
209 private function getMockDB( $methods = [] ) {
210 static $abstractMethods = [
211 'affectedRows',
212 'closeConnection',
213 'dataSeek',
214 'doQuery',
215 'fetchObject', 'fetchRow',
216 'fieldInfo', 'fieldName',
217 'getSoftwareLink', 'getServerVersion',
218 'getType',
219 'indexInfo',
220 'insertId',
221 'lastError', 'lastErrno',
222 'numFields', 'numRows',
223 'open',
224 'strencode',
225 ];
226 $db = $this->getMockBuilder( Database::class )
227 ->disableOriginalConstructor()
228 ->setMethods( array_values( array_unique( array_merge(
229 $abstractMethods,
230 $methods
231 ) ) ) )
232 ->getMock();
233 $wdb = TestingAccessWrapper::newFromObject( $db );
234 $wdb->trxProfiler = new TransactionProfiler();
235 $wdb->connLogger = new \Psr\Log\NullLogger();
236 $wdb->queryLogger = new \Psr\Log\NullLogger();
237 return $db;
238 }
239
240 /**
241 * @covers Wikimedia\Rdbms\Database::flushSnapshot
242 */
243 public function testFlushSnapshot() {
244 $db = $this->getMockDB( [ 'isOpen' ] );
245 $db->method( 'isOpen' )->willReturn( true );
246
247 $db->flushSnapshot( __METHOD__ ); // ok
248 $db->flushSnapshot( __METHOD__ ); // ok
249
250 $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
251 $db->query( 'SELECT 1', __METHOD__ );
252 $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
253 $db->flushSnapshot( __METHOD__ ); // ok
254 $db->restoreFlags( $db::RESTORE_PRIOR );
255
256 $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
257 }
258
259 public function testGetScopedLock() {
260 $db = $this->getMockDB( [ 'isOpen' ] );
261 $db->method( 'isOpen' )->willReturn( true );
262
263 $db->setFlag( DBO_TRX );
264 try {
265 $this->badLockingMethodImplicit( $db );
266 } catch ( RunTimeException $e ) {
267 $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
268 }
269 $db->clearFlag( DBO_TRX );
270 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
271 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
272
273 try {
274 $this->badLockingMethodExplicit( $db );
275 } catch ( RunTimeException $e ) {
276 $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
277 }
278 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
279 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
280 }
281
282 private function badLockingMethodImplicit( IDatabase $db ) {
283 $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
284 $db->query( "SELECT 1" ); // trigger DBO_TRX
285 throw new RunTimeException( "Uh oh!" );
286 }
287
288 private function badLockingMethodExplicit( IDatabase $db ) {
289 $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
290 $db->begin( __METHOD__ );
291 throw new RunTimeException( "Uh oh!" );
292 }
293
294 /**
295 * @covers Wikimedia\Rdbms\Database::getFlag
296 * @covers Wikimedia\Rdbms\Database::setFlag
297 * @covers Wikimedia\Rdbms\Database::restoreFlags
298 */
299 public function testFlagSetting() {
300 $db = $this->db;
301 $origTrx = $db->getFlag( DBO_TRX );
302 $origSsl = $db->getFlag( DBO_SSL );
303
304 $origTrx
305 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
306 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
307 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
308
309 $origSsl
310 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
311 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
312 $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
313
314 $db->restoreFlags( $db::RESTORE_INITIAL );
315 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
316 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
317
318 $origTrx
319 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
320 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
321 $origSsl
322 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
323 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
324
325 $db->restoreFlags();
326 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
327 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
328
329 $db->restoreFlags();
330 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
331 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
332 }
333
334 /**
335 * @covers Wikimedia\Rdbms\Database::tablePrefix
336 * @covers Wikimedia\Rdbms\Database::dbSchema
337 */
338 public function testMutators() {
339 $old = $this->db->tablePrefix();
340 $this->assertInternalType( 'string', $old, 'Prefix is string' );
341 $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
342 $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
343 $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
344 $this->db->tablePrefix( $old );
345 $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
346
347 $old = $this->db->dbSchema();
348 $this->assertInternalType( 'string', $old, 'Schema is string' );
349 $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
350 $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
351 $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
352 $this->db->dbSchema( $old );
353 $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
354 }
355 }