Import: Handle uploads with sha1 starting with 0 properly
[mediawiki.git] / tests / phpunit / includes / db / DatabaseMysqlBaseTest.php
blob6403905e4e18ece27177da787a2d7788a2399cf6
1 <?php
2 /**
3 * Holds tests for DatabaseMysqlBase MediaWiki class.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
20 * @file
21 * @author Antoine Musso
22 * @author Bryan Davis
23 * @copyright © 2013 Antoine Musso
24 * @copyright © 2013 Bryan Davis
25 * @copyright © 2013 Wikimedia Foundation Inc.
28 /**
29 * Fake class around abstract class so we can call concrete methods.
31 class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
32 // From DatabaseBase
33 function __construct() {
36 protected function closeConnection() {
39 protected function doQuery( $sql ) {
42 // From DatabaseMysql
43 protected function mysqlConnect( $realServer ) {
46 protected function mysqlSetCharset( $charset ) {
49 protected function mysqlFreeResult( $res ) {
52 protected function mysqlFetchObject( $res ) {
55 protected function mysqlFetchArray( $res ) {
58 protected function mysqlNumRows( $res ) {
61 protected function mysqlNumFields( $res ) {
64 protected function mysqlFieldName( $res, $n ) {
67 protected function mysqlFieldType( $res, $n ) {
70 protected function mysqlDataSeek( $res, $row ) {
73 protected function mysqlError( $conn = null ) {
76 protected function mysqlFetchField( $res, $n ) {
79 protected function mysqlPing() {
82 protected function mysqlRealEscapeString( $s ) {
86 // From interface DatabaseType
87 function insertId() {
90 function lastErrno() {
93 function affectedRows() {
96 function getServerVersion() {
100 class DatabaseMysqlBaseTest extends MediaWikiTestCase {
102 * @dataProvider provideDiapers
103 * @covers DatabaseMysqlBase::addIdentifierQuotes
105 public function testAddIdentifierQuotes( $expected, $in ) {
106 $db = new FakeDatabaseMysqlBase();
107 $quoted = $db->addIdentifierQuotes( $in );
108 $this->assertEquals( $expected, $quoted );
112 * Feeds testAddIdentifierQuotes
114 * Named per bug 20281 convention.
116 function provideDiapers() {
117 return array(
118 // Format: expected, input
119 array( '``', '' ),
121 // Yeah I really hate loosely typed PHP idiocies nowadays
122 array( '``', null ),
124 // Dear codereviewer, guess what addIdentifierQuotes()
125 // will return with thoses:
126 array( '``', false ),
127 array( '`1`', true ),
129 // We never know what could happen
130 array( '`0`', 0 ),
131 array( '`1`', 1 ),
133 // Whatchout! Should probably use something more meaningful
134 array( "`'`", "'" ), # single quote
135 array( '`"`', '"' ), # double quote
136 array( '````', '`' ), # backtick
137 array( '`’`', '’' ), # apostrophe (look at your encyclopedia)
139 // sneaky NUL bytes are lurking everywhere
140 array( '``', "\0" ),
141 array( '`xyzzy`', "\0x\0y\0z\0z\0y\0" ),
143 // unicode chars
144 array(
145 self::createUnicodeString( '`\u0001a\uFFFFb`' ),
146 self::createUnicodeString( '\u0001a\uFFFFb' )
148 array(
149 self::createUnicodeString( '`\u0001\uFFFF`' ),
150 self::createUnicodeString( '\u0001\u0000\uFFFF\u0000' )
152 array( '`☃`', '☃' ),
153 array( '`メインページ`', 'メインページ' ),
154 array( '`Басты_бет`', 'Басты_бет' ),
156 // Real world:
157 array( '`Alix`', 'Alix' ), # while( ! $recovered ) { sleep(); }
158 array( '`Backtick: ```', 'Backtick: `' ),
159 array( '`This is a test`', 'This is a test' ),
163 private static function createUnicodeString( $str ) {
164 return json_decode( '"' . $str . '"' );
167 function getMockForViews() {
168 $db = $this->getMockBuilder( 'DatabaseMysql' )
169 ->disableOriginalConstructor()
170 ->setMethods( array( 'fetchRow', 'query' ) )
171 ->getMock();
173 $db->expects( $this->any() )
174 ->method( 'query' )
175 ->with( $this->anything() )
176 ->will(
177 $this->returnValue( null )
180 $db->expects( $this->any() )
181 ->method( 'fetchRow' )
182 ->with( $this->anything() )
183 ->will( $this->onConsecutiveCalls(
184 array( 'Tables_in_' => 'view1' ),
185 array( 'Tables_in_' => 'view2' ),
186 array( 'Tables_in_' => 'myview' ),
187 false # no more rows
188 ) );
189 return $db;
192 * @covers DatabaseMysqlBase::listViews
194 function testListviews() {
195 $db = $this->getMockForViews();
197 // The first call populate an internal cache of views
198 $this->assertEquals( array( 'view1', 'view2', 'myview' ),
199 $db->listViews() );
200 $this->assertEquals( array( 'view1', 'view2', 'myview' ),
201 $db->listViews() );
203 // Prefix filtering
204 $this->assertEquals( array( 'view1', 'view2' ),
205 $db->listViews( 'view' ) );
206 $this->assertEquals( array( 'myview' ),
207 $db->listViews( 'my' ) );
208 $this->assertEquals( array(),
209 $db->listViews( 'UNUSED_PREFIX' ) );
210 $this->assertEquals( array( 'view1', 'view2', 'myview' ),
211 $db->listViews( '' ) );
215 * @covers DatabaseMysqlBase::isView
216 * @dataProvider provideViewExistanceChecks
218 function testIsView( $isView, $viewName ) {
219 $db = $this->getMockForViews();
221 switch ( $isView ) {
222 case true:
223 $this->assertTrue( $db->isView( $viewName ),
224 "$viewName should be considered a view" );
225 break;
227 case false:
228 $this->assertFalse( $db->isView( $viewName ),
229 "$viewName has not been defined as a view" );
230 break;
235 function provideViewExistanceChecks() {
236 return array(
237 // format: whether it is a view, view name
238 array( true, 'view1' ),
239 array( true, 'view2' ),
240 array( true, 'myview' ),
242 array( false, 'user' ),
244 array( false, 'view10' ),
245 array( false, 'my' ),
246 array( false, 'OH_MY_GOD' ), # they killed kenny!
250 function testMasterPos() {
251 $pos1 = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
252 $pos2 = new MySQLMasterPos( 'db1034-bin.000976', '843431248' );
254 $this->assertTrue( $pos1->hasReached( $pos1 ) );
255 $this->assertTrue( $pos2->hasReached( $pos2 ) );
256 $this->assertTrue( $pos2->hasReached( $pos1 ) );
257 $this->assertFalse( $pos1->hasReached( $pos2 ) );
261 * @dataProvider provideLagAmounts
263 function testPtHeartbeat( $lag ) {
264 $db = $this->getMockBuilder( 'DatabaseMysql' )
265 ->disableOriginalConstructor()
266 ->setMethods( array(
267 'getLagDetectionMethod', 'getHeartbeatData', 'getMasterServerInfo' ) )
268 ->getMock();
270 $db->expects( $this->any() )
271 ->method( 'getLagDetectionMethod' )
272 ->will( $this->returnValue( 'pt-heartbeat' ) );
274 $db->expects( $this->any() )
275 ->method( 'getMasterServerInfo' )
276 ->will( $this->returnValue( array( 'serverId' => 172, 'asOf' => time() ) ) );
278 // Fake the current time.
279 list( $nowSecFrac, $nowSec ) = explode( ' ', microtime() );
280 $now = (float)$nowSec + (float)$nowSecFrac;
281 // Fake the heartbeat time.
282 // Work arounds for weak DataTime microseconds support.
283 $ptTime = $now - $lag;
284 $ptSec = (int)$ptTime;
285 $ptSecFrac = ( $ptTime - $ptSec );
286 $ptDateTime = new DateTime( "@$ptSec" );
287 $ptTimeISO = $ptDateTime->format( 'Y-m-d\TH:i:s' );
288 $ptTimeISO .= ltrim( number_format( $ptSecFrac, 6 ), '0' );
290 $db->expects( $this->any() )
291 ->method( 'getHeartbeatData' )
292 ->with( 172 )
293 ->will( $this->returnValue( array( $ptTimeISO, $now ) ) );
295 $db->setLBInfo( 'clusterMasterHost', 'db1052' );
296 $lagEst = $db->getLag();
298 $this->assertGreaterThan( $lag - .010, $lagEst, "Correct heatbeat lag" );
299 $this->assertLessThan( $lag + .010, $lagEst, "Correct heatbeat lag" );
302 function provideLagAmounts() {
303 return array(
304 array( 0 ),
305 array( 0.3 ),
306 array( 6.5 ),
307 array( 10.1 ),
308 array( 200.2 ),
309 array( 400.7 ),
310 array( 600.22 ),
311 array( 1000.77 ),