diff options
Diffstat (limited to 'tests/phpunit/includes')
189 files changed, 9869 insertions, 2055 deletions
diff --git a/tests/phpunit/includes/BlockTest.php b/tests/phpunit/includes/BlockTest.php index b248d24e..19741621 100644 --- a/tests/phpunit/includes/BlockTest.php +++ b/tests/phpunit/includes/BlockTest.php @@ -133,6 +133,7 @@ class BlockTest extends MediaWikiLangTestCase { $username = 'BlockedUserToCreateAccountWith'; $u = User::newFromName( $username ); $u->setPassword( 'NotRandomPass' ); + $u->setId( 14146 ); $u->addToDatabase(); unset( $u ); @@ -200,6 +201,12 @@ class BlockTest extends MediaWikiLangTestCase { $oldBlock->delete(); } + // Local perspective (blockee on current wiki)... + $user = User::newFromName( 'UserOnForeignWiki' ); + $user->addToDatabase(); + // Set user ID to match the test value + $this->db->update( 'user', array( 'user_id' => 14146 ), array( 'user_id' => $user->getId() ) ); + // Foreign perspective (blockee not on current wiki)... $block = new Block( /* $address */ 'UserOnForeignWiki', @@ -221,11 +228,6 @@ class BlockTest extends MediaWikiLangTestCase { $res = $block->insert( $this->db ); $this->assertTrue( (bool)$res['id'], 'Block succeeded' ); - // Local perspective (blockee on current wiki)... - $user = User::newFromName( 'UserOnForeignWiki' ); - $user->addToDatabase(); - // Set user ID to match the test value - $this->db->update( 'user', array( 'user_id' => 14146 ), array( 'user_id' => $user->getId() ) ); $user = null; // clear $block = Block::newFromID( $res['id'] ); diff --git a/tests/phpunit/includes/EditPageTest.php b/tests/phpunit/includes/EditPageTest.php index 702fce4c..15778e40 100644 --- a/tests/phpunit/includes/EditPageTest.php +++ b/tests/phpunit/includes/EditPageTest.php @@ -217,7 +217,8 @@ class EditPageTest extends MediaWikiLangTestCase { EditPage::AS_SUCCESS_NEW_ARTICLE, '' ), - array( 'expected registered MediaWiki: page whose default content is empty not being created if empty', + array( 'expected registered MediaWiki: page whose default content is empty' + . ' not being created if empty', 'MediaWiki:Ipb-default-expiry', 'UTSysop', '', @@ -246,7 +247,9 @@ class EditPageTest extends MediaWikiLangTestCase { * @dataProvider provideCreatePages * @covers EditPage */ - public function testCreatePage( $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false ) { + public function testCreatePage( + $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false + ) { $edit = array( 'wpTextbox1' => $editText ); if ( $ignoreBlank ) { $edit['wpIgnoreBlankArticle'] = 1; diff --git a/tests/phpunit/includes/GitInfoTest.php b/tests/phpunit/includes/GitInfoTest.php index e22f5050..c3539d0e 100644 --- a/tests/phpunit/includes/GitInfoTest.php +++ b/tests/phpunit/includes/GitInfoTest.php @@ -10,7 +10,7 @@ class GitInfoTest extends MediaWikiTestCase { } public function testValidJsonData() { - $dir = $GLOBALS['IP'] . '/testValidJsonData'; + $dir = $GLOBALS['IP'] . DIRECTORY_SEPARATOR . 'testValidJsonData'; $fixture = new GitInfo( $dir ); $this->assertTrue( $fixture->cacheIsComplete() ); diff --git a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php index 3acc48e2..1e30273e 100644 --- a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php +++ b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php @@ -7,7 +7,7 @@ class GlobalTest extends MediaWikiTestCase { protected function setUp() { parent::setUp(); - $readOnlyFile = tempnam( wfTempDir(), "mwtest_readonly" ); + $readOnlyFile = $this->getNewTempFile(); unlink( $readOnlyFile ); $this->setMwGlobals( array( @@ -22,16 +22,6 @@ class GlobalTest extends MediaWikiTestCase { ) ); } - protected function tearDown() { - global $wgReadOnlyFile; - - if ( file_exists( $wgReadOnlyFile ) ) { - unlink( $wgReadOnlyFile ); - } - - parent::tearDown(); - } - /** * @dataProvider provideForWfArrayDiff2 * @covers ::wfArrayDiff2 @@ -312,46 +302,42 @@ class GlobalTest extends MediaWikiTestCase { * @covers ::wfDebugMem */ public function testDebugFunctionTest() { + $debugLogFile = $this->getNewTempFile(); - global $wgDebugLogFile, $wgDebugTimestamps; - - $old_log_file = $wgDebugLogFile; - $wgDebugLogFile = tempnam( wfTempDir(), 'mw-' ); - # @todo FIXME: $wgDebugTimestamps should be tested - $old_wgDebugTimestamps = $wgDebugTimestamps; - $wgDebugTimestamps = false; + $this->setMwGlobals( array( + 'wgDebugLogFile' => $debugLogFile, + # @todo FIXME: $wgDebugTimestamps should be tested + 'wgDebugTimestamps' => false + ) ); wfDebug( "This is a normal string" ); - $this->assertEquals( "This is a normal string", file_get_contents( $wgDebugLogFile ) ); - unlink( $wgDebugLogFile ); + $this->assertEquals( "This is a normal string\n", file_get_contents( $debugLogFile ) ); + unlink( $debugLogFile ); wfDebug( "This is nöt an ASCII string" ); - $this->assertEquals( "This is nöt an ASCII string", file_get_contents( $wgDebugLogFile ) ); - unlink( $wgDebugLogFile ); + $this->assertEquals( "This is nöt an ASCII string\n", file_get_contents( $debugLogFile ) ); + unlink( $debugLogFile ); wfDebug( "\00305This has böth UTF and control chars\003" ); $this->assertEquals( - " 05This has böth UTF and control chars ", - file_get_contents( $wgDebugLogFile ) + " 05This has böth UTF and control chars \n", + file_get_contents( $debugLogFile ) ); - unlink( $wgDebugLogFile ); + unlink( $debugLogFile ); wfDebugMem(); $this->assertGreaterThan( 1000, - preg_replace( '/\D/', '', file_get_contents( $wgDebugLogFile ) ) + preg_replace( '/\D/', '', file_get_contents( $debugLogFile ) ) ); - unlink( $wgDebugLogFile ); + unlink( $debugLogFile ); wfDebugMem( true ); $this->assertGreaterThan( 1000000, - preg_replace( '/\D/', '', file_get_contents( $wgDebugLogFile ) ) + preg_replace( '/\D/', '', file_get_contents( $debugLogFile ) ) ); - unlink( $wgDebugLogFile ); - - $wgDebugLogFile = $old_log_file; - $wgDebugTimestamps = $old_wgDebugTimestamps; + unlink( $debugLogFile ); } /** @@ -389,24 +375,6 @@ class GlobalTest extends MediaWikiTestCase { } /** - * @covers ::swap - */ - public function testSwapVarsTest() { - $this->hideDeprecated( 'swap' ); - - $var1 = 1; - $var2 = 2; - - $this->assertEquals( $var1, 1, 'var1 is set originally' ); - $this->assertEquals( $var2, 2, 'var1 is set originally' ); - - swap( $var1, $var2 ); - - $this->assertEquals( $var1, 2, 'var1 is swapped' ); - $this->assertEquals( $var2, 1, 'var2 is swapped' ); - } - - /** * @covers ::wfPercent */ public function testWfPercentTest() { @@ -705,21 +673,21 @@ class GlobalTest extends MediaWikiTestCase { } /** - * @dataProvider provideWfShellMaintenanceCmdList - * @covers ::wfShellMaintenanceCmd + * @dataProvider provideWfShellWikiCmdList + * @covers ::wfShellWikiCmd */ - public function testWfShellMaintenanceCmd( $script, $parameters, $options, + public function testWfShellWikiCmd( $script, $parameters, $options, $expected, $description ) { if ( wfIsWindows() ) { // Approximation that's good enough for our purposes just now $expected = str_replace( "'", '"', $expected ); } - $actual = wfShellMaintenanceCmd( $script, $parameters, $options ); + $actual = wfShellWikiCmd( $script, $parameters, $options ); $this->assertEquals( $expected, $actual, $description ); } - public static function provideWfShellMaintenanceCmdList() { + public static function provideWfShellWikiCmdList() { global $wgPhpCli; return array( diff --git a/tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php b/tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php new file mode 100644 index 00000000..54e1f896 --- /dev/null +++ b/tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php @@ -0,0 +1,67 @@ +<?php + +/** + * @group GlobalFunctions + * @covers ::wfAppendQuery + */ +class WfAppendQueryTest extends MediaWikiTestCase { + /** + * @dataProvider provideAppendQuery + */ + public function testAppendQuery( $url, $query, $expected, $message = null ) { + $this->assertEquals( $expected, wfAppendQuery( $url, $query ), $message ); + } + + public static function provideAppendQuery() { + return array( + array( + 'http://www.example.org/index.php', + '', + 'http://www.example.org/index.php', + 'No query' + ), + array( + 'http://www.example.org/index.php', + array( 'foo' => 'bar' ), + 'http://www.example.org/index.php?foo=bar', + 'Set query array' + ), + array( + 'http://www.example.org/index.php?foz=baz', + 'foo=bar', + 'http://www.example.org/index.php?foz=baz&foo=bar', + 'Set query string' + ), + array( + 'http://www.example.org/index.php?foo=bar', + '', + 'http://www.example.org/index.php?foo=bar', + 'Empty string with query' + ), + array( + 'http://www.example.org/index.php?foo=bar', + array( 'baz' => 'quux' ), + 'http://www.example.org/index.php?foo=bar&baz=quux', + 'Add query array' + ), + array( + 'http://www.example.org/index.php?foo=bar', + 'baz=quux', + 'http://www.example.org/index.php?foo=bar&baz=quux', + 'Add query string' + ), + array( + 'http://www.example.org/index.php?foo=bar', + array( 'baz' => 'quux', 'foo' => 'baz' ), + 'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz', + 'Modify query array' + ), + array( + 'http://www.example.org/index.php?foo=bar', + 'baz=quux&foo=baz', + 'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz', + 'Modify query string' + ) + ); + } +} diff --git a/tests/phpunit/includes/GlobalFunctions/wfEscapeShellArgTest.php b/tests/phpunit/includes/GlobalFunctions/wfEscapeShellArgTest.php new file mode 100644 index 00000000..cb334d2f --- /dev/null +++ b/tests/phpunit/includes/GlobalFunctions/wfEscapeShellArgTest.php @@ -0,0 +1,43 @@ +<?php + +/** + * @group GlobalFunctions + * @covers ::wfEscapeShellArg + */ +class wfEscapeShellArgTest extends MediaWikiTestCase { + public function testSingleInput() { + if ( wfIsWindows() ) { + $expected = '"blah"'; + } else { + $expected = "'blah'"; + } + + $actual = wfEscapeShellArg( 'blah' ); + + $this->assertEquals( $expected, $actual ); + } + + public function testMultipleArgs() { + if ( wfIsWindows() ) { + $expected = '"foo" "bar" "baz"'; + } else { + $expected = "'foo' 'bar' 'baz'"; + } + + $actual = wfEscapeShellArg( 'foo', 'bar', 'baz' ); + + $this->assertEquals( $expected, $actual ); + } + + public function testMultipleArgsAsArray() { + if ( wfIsWindows() ) { + $expected = '"foo" "bar" "baz"'; + } else { + $expected = "'foo' 'bar' 'baz'"; + } + + $actual = wfEscapeShellArg( array( 'foo', 'bar', 'baz' ) ); + + $this->assertEquals( $expected, $actual ); + } +} diff --git a/tests/phpunit/includes/GlobalFunctions/wfThumbIsStandardTest.php b/tests/phpunit/includes/GlobalFunctions/wfThumbIsStandardTest.php new file mode 100644 index 00000000..448250a6 --- /dev/null +++ b/tests/phpunit/includes/GlobalFunctions/wfThumbIsStandardTest.php @@ -0,0 +1,104 @@ +<?php + +/** + * @group GlobalFunctions + * @covers ::wfThumbIsStandard + */ +class WfThumbIsStandardTest extends MediaWikiTestCase { + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgThumbLimits' => array( + 100, + 401 + ), + 'wgImageLimits' => array( + array( 300, 225 ), + array( 800, 600 ), + ), + 'wgMediaHandlers' => array( + 'unknown/unknown' => 'MockBitmapHandler', + ), + ) ); + } + + public static function provideThumbParams() { + return array( + // Thumb limits + array( + 'Standard thumb width', + true, + array( 'width' => 100 ), + ), + array( + 'Standard thumb width', + true, + array( 'width' => 401 ), + ), + // wfThumbIsStandard should match Linker::processResponsiveImages + // in its rounding behaviour. + array( + 'Standard thumb width (HiDPI 1.5x) - incorrect rounding', + false, + array( 'width' => 601 ), + ), + array( + 'Standard thumb width (HiDPI 1.5x)', + true, + array( 'width' => 602 ), + ), + array( + 'Standard thumb width (HiDPI 2x)', + true, + array( 'width' => 802 ), + ), + array( + 'Non-standard thumb width', + false, + array( 'width' => 300 ), + ), + // Image limits + // Note: Image limits are measured as pairs. Individual values + // may be non-standard based on the aspect ratio. + array( + 'Standard image width/height pair', + true, + array( 'width' => 250, 'height' => 225 ), + ), + array( + 'Standard image width/height pair', + true, + array( 'width' => 667, 'height' => 600 ), + ), + array( + 'Standard image width where image does not fit aspect ratio', + false, + array( 'width' => 300 ), + ), + array( + 'Implicit width from image width/height pair aspect ratio fit', + true, + // 2000x1800 fit inside 300x225 makes w=250 + array( 'width' => 250 ), + ), + array( + 'Height-only is always non-standard', + false, + array( 'height' => 225 ), + ), + ); + } + + /** + * @dataProvider provideThumbParams + */ + public function testIsStandard( $message, $expected, $params ) { + $this->assertSame( + $expected, + wfThumbIsStandard( new FakeDimensionFile( array( 2000, 1800 ) ), $params ), + $message + ); + } +} diff --git a/tests/phpunit/includes/HtmlFormatterTest.php b/tests/phpunit/includes/HtmlFormatterTest.php index 9dbfa452..1c3e8539 100644 --- a/tests/phpunit/includes/HtmlFormatterTest.php +++ b/tests/phpunit/includes/HtmlFormatterTest.php @@ -4,6 +4,20 @@ * @group HtmlFormatter */ class HtmlFormatterTest extends MediaWikiTestCase { + + /** + * Use TidySupport to check whether we should use $wgTidyInternal. + * + * The Tidy extension in HHVM does not support error text return, so it is + * nominally usable, but does not pass tests which require error text from + * Tidy. + */ + protected function setUp() { + parent::setUp(); + $tidySupport = new TidySupport(); + $this->setMwGlobals( 'wgTidyInternal', $tidySupport->isInternal() ); + } + /** * @dataProvider getHtmlData * diff --git a/tests/phpunit/includes/HtmlTest.php b/tests/phpunit/includes/HtmlTest.php index a8829cd8..c5797c4f 100644 --- a/tests/phpunit/includes/HtmlTest.php +++ b/tests/phpunit/includes/HtmlTest.php @@ -637,7 +637,7 @@ class HtmlTest extends MediaWikiTestCase { . 'Depending on compatibility mode IE might use "button", instead.', ); - # <select> specifc handling + # <select> specific handling $cases[] = array( '<select multiple></select>', 'select', array( 'size' => '4', 'multiple' => true ), ); @@ -715,7 +715,7 @@ class HtmlTest extends MediaWikiTestCase { 'Input wrapper with type and value.' ); $this->assertEquals( - '<input name=testname class=mw-ui-input>', + '<input name=testname>', Html::input( 'testname' ), 'Input wrapper with all default values.' ); @@ -764,6 +764,30 @@ class HtmlTest extends MediaWikiTestCase { 'Label wrapper' ); } + + public static function provideSrcSetImages() { + return array( + array( array(), '', 'when there are no images, return empty string' ), + array( + array( '1x' => '1x.png', '1.5x' => '1_5x.png', '2x' => '2x.png' ), + '1x.png 1x, 1_5x.png 1.5x, 2x.png 2x', + 'pixel depth keys may include a trailing "x"' + ), + array( + array( '1' => '1x.png', '1.5' => '1_5x.png', '2' => '2x.png' ), + '1x.png 1x, 1_5x.png 1.5x, 2x.png 2x', + 'pixel depth keys may omit a trailing "x"' + ), + ); + } + + /** + * @dataProvider provideSrcSetImages + * @covers Html::srcSet + */ + public function testSrcSet( $images, $expected, $message ) { + $this->assertEquals( Html::srcSet( $images ), $expected, $message ); + } } class HtmlTestValue { diff --git a/tests/phpunit/includes/HttpTest.php b/tests/phpunit/includes/HttpTest.php index 9b53381e..8a0dff78 100644 --- a/tests/phpunit/includes/HttpTest.php +++ b/tests/phpunit/includes/HttpTest.php @@ -1,6 +1,7 @@ <?php + /** - * @group Broken + * @group Http */ class HttpTest extends MediaWikiTestCase { /** @@ -92,8 +93,10 @@ class HttpTest extends MediaWikiTestCase { array( true, 'http://user:pass@host', 'Username and password provided' ), # (\S+) - host part is made of anything not whitespaces - array( false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ), - array( false, 'http://exam:ple.org/', 'hostname can not use colons!' ), + // commented these out in order to remove @group Broken + // @todo are these valid tests? if so, fix Http::isValidURI so it can handle them + //array( false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ), + //array( false, 'http://exam:ple.org/', 'hostname can not use colons!' ), # (:[0-9]+)? - port number array( true, 'http://example.org:80/' ), @@ -135,7 +138,7 @@ class HttpTest extends MediaWikiTestCase { * HTTP redirects). */ public function testRelativeRedirections() { - $h = MWHttpRequestTester::factory( 'http://oldsite/file.ext' ); + $h = MWHttpRequestTester::factory( 'http://oldsite/file.ext', array(), __METHOD__ ); # Forge a Location header $h->setRespHeaders( 'location', array( @@ -171,6 +174,310 @@ class HttpTest extends MediaWikiTestCase { $h->getFinalUrl( "Relative file path Location: should keep the latest host and scheme!" ) ); } + + /** + * Constant values are from PHP 5.3.28 using cURL 7.24.0 + * @see http://php.net/manual/en/curl.constants.php + * + * All constant values are present so that developers don’t need to remember + * to add them if added at a later date. The commented out constants were + * not found anywhere in the MediaWiki core code. + * + * Commented out constants that were not available in: + * HipHop VM 3.3.0 (rel) + * Compiler: heads/master-0-g08810d920dfff59e0774cf2d651f92f13a637175 + * Repo schema: 3214fc2c684a4520485f715ee45f33f2182324b1 + * Extension API: 20140829 + * + * Commented out constants that were removed in PHP 5.6.0 + * + * @covers CurlHttpRequest::execute + */ + public function provideCurlConstants() { + return array( + array( 'CURLAUTH_ANY' ), + array( 'CURLAUTH_ANYSAFE' ), + array( 'CURLAUTH_BASIC' ), + array( 'CURLAUTH_DIGEST' ), + array( 'CURLAUTH_GSSNEGOTIATE' ), + array( 'CURLAUTH_NTLM' ), + // array( 'CURLCLOSEPOLICY_CALLBACK' ), // removed in PHP 5.6.0 + // array( 'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' ), // removed in PHP 5.6.0 + // array( 'CURLCLOSEPOLICY_LEAST_TRAFFIC' ), // removed in PHP 5.6.0 + // array( 'CURLCLOSEPOLICY_OLDEST' ), // removed in PHP 5.6.0 + // array( 'CURLCLOSEPOLICY_SLOWEST' ), // removed in PHP 5.6.0 + array( 'CURLE_ABORTED_BY_CALLBACK' ), + array( 'CURLE_BAD_CALLING_ORDER' ), + array( 'CURLE_BAD_CONTENT_ENCODING' ), + array( 'CURLE_BAD_FUNCTION_ARGUMENT' ), + array( 'CURLE_BAD_PASSWORD_ENTERED' ), + array( 'CURLE_COULDNT_CONNECT' ), + array( 'CURLE_COULDNT_RESOLVE_HOST' ), + array( 'CURLE_COULDNT_RESOLVE_PROXY' ), + array( 'CURLE_FAILED_INIT' ), + array( 'CURLE_FILESIZE_EXCEEDED' ), + array( 'CURLE_FILE_COULDNT_READ_FILE' ), + array( 'CURLE_FTP_ACCESS_DENIED' ), + array( 'CURLE_FTP_BAD_DOWNLOAD_RESUME' ), + array( 'CURLE_FTP_CANT_GET_HOST' ), + array( 'CURLE_FTP_CANT_RECONNECT' ), + array( 'CURLE_FTP_COULDNT_GET_SIZE' ), + array( 'CURLE_FTP_COULDNT_RETR_FILE' ), + array( 'CURLE_FTP_COULDNT_SET_ASCII' ), + array( 'CURLE_FTP_COULDNT_SET_BINARY' ), + array( 'CURLE_FTP_COULDNT_STOR_FILE' ), + array( 'CURLE_FTP_COULDNT_USE_REST' ), + array( 'CURLE_FTP_PORT_FAILED' ), + array( 'CURLE_FTP_QUOTE_ERROR' ), + array( 'CURLE_FTP_SSL_FAILED' ), + array( 'CURLE_FTP_USER_PASSWORD_INCORRECT' ), + array( 'CURLE_FTP_WEIRD_227_FORMAT' ), + array( 'CURLE_FTP_WEIRD_PASS_REPLY' ), + array( 'CURLE_FTP_WEIRD_PASV_REPLY' ), + array( 'CURLE_FTP_WEIRD_SERVER_REPLY' ), + array( 'CURLE_FTP_WEIRD_USER_REPLY' ), + array( 'CURLE_FTP_WRITE_ERROR' ), + array( 'CURLE_FUNCTION_NOT_FOUND' ), + array( 'CURLE_GOT_NOTHING' ), + array( 'CURLE_HTTP_NOT_FOUND' ), + array( 'CURLE_HTTP_PORT_FAILED' ), + array( 'CURLE_HTTP_POST_ERROR' ), + array( 'CURLE_HTTP_RANGE_ERROR' ), + array( 'CURLE_LDAP_CANNOT_BIND' ), + array( 'CURLE_LDAP_INVALID_URL' ), + array( 'CURLE_LDAP_SEARCH_FAILED' ), + array( 'CURLE_LIBRARY_NOT_FOUND' ), + array( 'CURLE_MALFORMAT_USER' ), + array( 'CURLE_OBSOLETE' ), + array( 'CURLE_OK' ), + array( 'CURLE_OPERATION_TIMEOUTED' ), + array( 'CURLE_OUT_OF_MEMORY' ), + array( 'CURLE_PARTIAL_FILE' ), + array( 'CURLE_READ_ERROR' ), + array( 'CURLE_RECV_ERROR' ), + array( 'CURLE_SEND_ERROR' ), + array( 'CURLE_SHARE_IN_USE' ), + // array( 'CURLE_SSH' ), // not present in HHVM 3.3.0-dev + array( 'CURLE_SSL_CACERT' ), + array( 'CURLE_SSL_CERTPROBLEM' ), + array( 'CURLE_SSL_CIPHER' ), + array( 'CURLE_SSL_CONNECT_ERROR' ), + array( 'CURLE_SSL_ENGINE_NOTFOUND' ), + array( 'CURLE_SSL_ENGINE_SETFAILED' ), + array( 'CURLE_SSL_PEER_CERTIFICATE' ), + array( 'CURLE_TELNET_OPTION_SYNTAX' ), + array( 'CURLE_TOO_MANY_REDIRECTS' ), + array( 'CURLE_UNKNOWN_TELNET_OPTION' ), + array( 'CURLE_UNSUPPORTED_PROTOCOL' ), + array( 'CURLE_URL_MALFORMAT' ), + array( 'CURLE_URL_MALFORMAT_USER' ), + array( 'CURLE_WRITE_ERROR' ), + array( 'CURLFTPAUTH_DEFAULT' ), + array( 'CURLFTPAUTH_SSL' ), + array( 'CURLFTPAUTH_TLS' ), + // array( 'CURLFTPMETHOD_MULTICWD' ), // not present in HHVM 3.3.0-dev + // array( 'CURLFTPMETHOD_NOCWD' ), // not present in HHVM 3.3.0-dev + // array( 'CURLFTPMETHOD_SINGLECWD' ), // not present in HHVM 3.3.0-dev + array( 'CURLFTPSSL_ALL' ), + array( 'CURLFTPSSL_CONTROL' ), + array( 'CURLFTPSSL_NONE' ), + array( 'CURLFTPSSL_TRY' ), + // array( 'CURLINFO_CERTINFO' ), // not present in HHVM 3.3.0-dev + array( 'CURLINFO_CONNECT_TIME' ), + array( 'CURLINFO_CONTENT_LENGTH_DOWNLOAD' ), + array( 'CURLINFO_CONTENT_LENGTH_UPLOAD' ), + array( 'CURLINFO_CONTENT_TYPE' ), + array( 'CURLINFO_EFFECTIVE_URL' ), + array( 'CURLINFO_FILETIME' ), + array( 'CURLINFO_HEADER_OUT' ), + array( 'CURLINFO_HEADER_SIZE' ), + array( 'CURLINFO_HTTP_CODE' ), + array( 'CURLINFO_NAMELOOKUP_TIME' ), + array( 'CURLINFO_PRETRANSFER_TIME' ), + array( 'CURLINFO_PRIVATE' ), + array( 'CURLINFO_REDIRECT_COUNT' ), + array( 'CURLINFO_REDIRECT_TIME' ), + // array( 'CURLINFO_REDIRECT_URL' ), // not present in HHVM 3.3.0-dev + array( 'CURLINFO_REQUEST_SIZE' ), + array( 'CURLINFO_SIZE_DOWNLOAD' ), + array( 'CURLINFO_SIZE_UPLOAD' ), + array( 'CURLINFO_SPEED_DOWNLOAD' ), + array( 'CURLINFO_SPEED_UPLOAD' ), + array( 'CURLINFO_SSL_VERIFYRESULT' ), + array( 'CURLINFO_STARTTRANSFER_TIME' ), + array( 'CURLINFO_TOTAL_TIME' ), + array( 'CURLMSG_DONE' ), + array( 'CURLM_BAD_EASY_HANDLE' ), + array( 'CURLM_BAD_HANDLE' ), + array( 'CURLM_CALL_MULTI_PERFORM' ), + array( 'CURLM_INTERNAL_ERROR' ), + array( 'CURLM_OK' ), + array( 'CURLM_OUT_OF_MEMORY' ), + array( 'CURLOPT_AUTOREFERER' ), + array( 'CURLOPT_BINARYTRANSFER' ), + array( 'CURLOPT_BUFFERSIZE' ), + array( 'CURLOPT_CAINFO' ), + array( 'CURLOPT_CAPATH' ), + // array( 'CURLOPT_CERTINFO' ), // not present in HHVM 3.3.0-dev + // array( 'CURLOPT_CLOSEPOLICY' ), // removed in PHP 5.6.0 + array( 'CURLOPT_CONNECTTIMEOUT' ), + array( 'CURLOPT_CONNECTTIMEOUT_MS' ), + array( 'CURLOPT_COOKIE' ), + array( 'CURLOPT_COOKIEFILE' ), + array( 'CURLOPT_COOKIEJAR' ), + array( 'CURLOPT_COOKIESESSION' ), + array( 'CURLOPT_CRLF' ), + array( 'CURLOPT_CUSTOMREQUEST' ), + array( 'CURLOPT_DNS_CACHE_TIMEOUT' ), + array( 'CURLOPT_DNS_USE_GLOBAL_CACHE' ), + array( 'CURLOPT_EGDSOCKET' ), + array( 'CURLOPT_ENCODING' ), + array( 'CURLOPT_FAILONERROR' ), + array( 'CURLOPT_FILE' ), + array( 'CURLOPT_FILETIME' ), + array( 'CURLOPT_FOLLOWLOCATION' ), + array( 'CURLOPT_FORBID_REUSE' ), + array( 'CURLOPT_FRESH_CONNECT' ), + array( 'CURLOPT_FTPAPPEND' ), + array( 'CURLOPT_FTPLISTONLY' ), + array( 'CURLOPT_FTPPORT' ), + array( 'CURLOPT_FTPSSLAUTH' ), + array( 'CURLOPT_FTP_CREATE_MISSING_DIRS' ), + // array( 'CURLOPT_FTP_FILEMETHOD' ), // not present in HHVM 3.3.0-dev + // array( 'CURLOPT_FTP_SKIP_PASV_IP' ), // not present in HHVM 3.3.0-dev + array( 'CURLOPT_FTP_SSL' ), + array( 'CURLOPT_FTP_USE_EPRT' ), + array( 'CURLOPT_FTP_USE_EPSV' ), + array( 'CURLOPT_HEADER' ), + array( 'CURLOPT_HEADERFUNCTION' ), + array( 'CURLOPT_HTTP200ALIASES' ), + array( 'CURLOPT_HTTPAUTH' ), + array( 'CURLOPT_HTTPGET' ), + array( 'CURLOPT_HTTPHEADER' ), + array( 'CURLOPT_HTTPPROXYTUNNEL' ), + array( 'CURLOPT_HTTP_VERSION' ), + array( 'CURLOPT_INFILE' ), + array( 'CURLOPT_INFILESIZE' ), + array( 'CURLOPT_INTERFACE' ), + array( 'CURLOPT_IPRESOLVE' ), + // array( 'CURLOPT_KEYPASSWD' ), // not present in HHVM 3.3.0-dev + array( 'CURLOPT_KRB4LEVEL' ), + array( 'CURLOPT_LOW_SPEED_LIMIT' ), + array( 'CURLOPT_LOW_SPEED_TIME' ), + array( 'CURLOPT_MAXCONNECTS' ), + array( 'CURLOPT_MAXREDIRS' ), + // array( 'CURLOPT_MAX_RECV_SPEED_LARGE' ), // not present in HHVM 3.3.0-dev + // array( 'CURLOPT_MAX_SEND_SPEED_LARGE' ), // not present in HHVM 3.3.0-dev + array( 'CURLOPT_NETRC' ), + array( 'CURLOPT_NOBODY' ), + array( 'CURLOPT_NOPROGRESS' ), + array( 'CURLOPT_NOSIGNAL' ), + array( 'CURLOPT_PORT' ), + array( 'CURLOPT_POST' ), + array( 'CURLOPT_POSTFIELDS' ), + array( 'CURLOPT_POSTQUOTE' ), + array( 'CURLOPT_POSTREDIR' ), + array( 'CURLOPT_PRIVATE' ), + array( 'CURLOPT_PROGRESSFUNCTION' ), + // array( 'CURLOPT_PROTOCOLS' ), // not present in HHVM 3.3.0-dev + array( 'CURLOPT_PROXY' ), + array( 'CURLOPT_PROXYAUTH' ), + array( 'CURLOPT_PROXYPORT' ), + array( 'CURLOPT_PROXYTYPE' ), + array( 'CURLOPT_PROXYUSERPWD' ), + array( 'CURLOPT_PUT' ), + array( 'CURLOPT_QUOTE' ), + array( 'CURLOPT_RANDOM_FILE' ), + array( 'CURLOPT_RANGE' ), + array( 'CURLOPT_READDATA' ), + array( 'CURLOPT_READFUNCTION' ), + // array( 'CURLOPT_REDIR_PROTOCOLS' ), // not present in HHVM 3.3.0-dev + array( 'CURLOPT_REFERER' ), + array( 'CURLOPT_RESUME_FROM' ), + array( 'CURLOPT_RETURNTRANSFER' ), + // array( 'CURLOPT_SSH_AUTH_TYPES' ), // not present in HHVM 3.3.0-dev + // array( 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5' ), // not present in HHVM 3.3.0-dev + // array( 'CURLOPT_SSH_PRIVATE_KEYFILE' ), // not present in HHVM 3.3.0-dev + // array( 'CURLOPT_SSH_PUBLIC_KEYFILE' ), // not present in HHVM 3.3.0-dev + array( 'CURLOPT_SSLCERT' ), + array( 'CURLOPT_SSLCERTPASSWD' ), + array( 'CURLOPT_SSLCERTTYPE' ), + array( 'CURLOPT_SSLENGINE' ), + array( 'CURLOPT_SSLENGINE_DEFAULT' ), + array( 'CURLOPT_SSLKEY' ), + array( 'CURLOPT_SSLKEYPASSWD' ), + array( 'CURLOPT_SSLKEYTYPE' ), + array( 'CURLOPT_SSLVERSION' ), + array( 'CURLOPT_SSL_CIPHER_LIST' ), + array( 'CURLOPT_SSL_VERIFYHOST' ), + array( 'CURLOPT_SSL_VERIFYPEER' ), + array( 'CURLOPT_STDERR' ), + array( 'CURLOPT_TCP_NODELAY' ), + array( 'CURLOPT_TIMECONDITION' ), + array( 'CURLOPT_TIMEOUT' ), + array( 'CURLOPT_TIMEOUT_MS' ), + array( 'CURLOPT_TIMEVALUE' ), + array( 'CURLOPT_TRANSFERTEXT' ), + array( 'CURLOPT_UNRESTRICTED_AUTH' ), + array( 'CURLOPT_UPLOAD' ), + array( 'CURLOPT_URL' ), + array( 'CURLOPT_USERAGENT' ), + array( 'CURLOPT_USERPWD' ), + array( 'CURLOPT_VERBOSE' ), + array( 'CURLOPT_WRITEFUNCTION' ), + array( 'CURLOPT_WRITEHEADER' ), + // array( 'CURLPROTO_ALL' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_DICT' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_FILE' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_FTP' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_FTPS' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_HTTP' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_HTTPS' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_LDAP' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_LDAPS' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_SCP' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_SFTP' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_TELNET' ), // not present in HHVM 3.3.0-dev + // array( 'CURLPROTO_TFTP' ), // not present in HHVM 3.3.0-dev + array( 'CURLPROXY_HTTP' ), + // array( 'CURLPROXY_SOCKS4' ), // not present in HHVM 3.3.0-dev + array( 'CURLPROXY_SOCKS5' ), + // array( 'CURLSSH_AUTH_DEFAULT' ), // not present in HHVM 3.3.0-dev + // array( 'CURLSSH_AUTH_HOST' ), // not present in HHVM 3.3.0-dev + // array( 'CURLSSH_AUTH_KEYBOARD' ), // not present in HHVM 3.3.0-dev + // array( 'CURLSSH_AUTH_NONE' ), // not present in HHVM 3.3.0-dev + // array( 'CURLSSH_AUTH_PASSWORD' ), // not present in HHVM 3.3.0-dev + // array( 'CURLSSH_AUTH_PUBLICKEY' ), // not present in HHVM 3.3.0-dev + array( 'CURLVERSION_NOW' ), + array( 'CURL_HTTP_VERSION_1_0' ), + array( 'CURL_HTTP_VERSION_1_1' ), + array( 'CURL_HTTP_VERSION_NONE' ), + array( 'CURL_IPRESOLVE_V4' ), + array( 'CURL_IPRESOLVE_V6' ), + array( 'CURL_IPRESOLVE_WHATEVER' ), + array( 'CURL_NETRC_IGNORED' ), + array( 'CURL_NETRC_OPTIONAL' ), + array( 'CURL_NETRC_REQUIRED' ), + array( 'CURL_TIMECOND_IFMODSINCE' ), + array( 'CURL_TIMECOND_IFUNMODSINCE' ), + array( 'CURL_TIMECOND_LASTMOD' ), + array( 'CURL_VERSION_IPV6' ), + array( 'CURL_VERSION_KERBEROS4' ), + array( 'CURL_VERSION_LIBZ' ), + array( 'CURL_VERSION_SSL' ), + ); + } + + /** + * Added this test based on an issue experienced with HHVM 3.3.0-dev + * where it did not define a cURL constant. + * + * @bug 70570 + * @dataProvider provideCurlConstants + */ + public function testCurlConstants( $value ) { + $this->assertTrue( defined( $value ), $value . ' not defined' ); + } } /** @@ -179,7 +486,7 @@ class HttpTest extends MediaWikiTestCase { class MWHttpRequestTester extends MWHttpRequest { // function derived from the MWHttpRequest factory function but // returns appropriate tester class here - public static function factory( $url, $options = null ) { + public static function factory( $url, $options = null, $caller = __METHOD__ ) { if ( !Http::$httpEngine ) { Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php'; } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) { @@ -189,7 +496,7 @@ class MWHttpRequestTester extends MWHttpRequest { switch ( Http::$httpEngine ) { case 'curl': - return new CurlHttpRequestTester( $url, $options ); + return new CurlHttpRequestTester( $url, $options, $caller ); case 'php': if ( !wfIniGetBool( 'allow_url_fopen' ) ) { throw new MWException( __METHOD__ . @@ -197,7 +504,7 @@ class MWHttpRequestTester extends MWHttpRequest { . 'If possible, curl should be used instead. See http://php.net/curl.' ); } - return new PhpHttpRequestTester( $url, $options ); + return new PhpHttpRequestTester( $url, $options, $caller ); default: } } diff --git a/tests/phpunit/includes/ImportTest.php b/tests/phpunit/includes/ImportTest.php index 2fce6bfb..ea753e81 100644 --- a/tests/phpunit/includes/ImportTest.php +++ b/tests/phpunit/includes/ImportTest.php @@ -28,13 +28,14 @@ class ImportTest extends MediaWikiLangTestCase { $source = $this->getInputStreamSource( $xml ); $redirect = null; - $callback = function ( $title, $origTitle, $revCount, $sRevCount, $pageInfo ) use ( &$redirect ) { + $callback = function ( Title $title, ForeignTitle $foreignTitle, $revCount, + $sRevCount, $pageInfo ) use ( &$redirect ) { if ( array_key_exists( 'redirect', $pageInfo ) ) { $redirect = $pageInfo['redirect']; } }; - $importer = new WikiImporter( $source ); + $importer = new WikiImporter( $source, ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) ); $importer->setPageOutCallback( $callback ); $importer->doImport(); @@ -45,7 +46,7 @@ class ImportTest extends MediaWikiLangTestCase { return array( array( <<< EOF -<mediawiki> +<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en"> <page> <title>Test</title> <ns>0</ns> @@ -59,10 +60,10 @@ class ImportTest extends MediaWikiLangTestCase { <id>10</id> </contributor> <comment>Admin moved page [[Test]] to [[Test22]]</comment> - <text xml:space="preserve" bytes="20">#REDIRECT [[Test22]]</text> - <sha1>tq456o9x3abm7r9ozi6km8yrbbc56o6</sha1> <model>wikitext</model> <format>text/x-wiki</format> + <text xml:space="preserve" bytes="20">#REDIRECT [[Test22]]</text> + <sha1>tq456o9x3abm7r9ozi6km8yrbbc56o6</sha1> </revision> </page> </mediawiki> @@ -72,7 +73,7 @@ EOF ), array( <<< EOF -<mediawiki> +<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.9/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.9/ http://www.mediawiki.org/xml/export-0.9.xsd" version="0.9" xml:lang="en"> <page> <title>Test</title> <ns>0</ns> @@ -98,4 +99,59 @@ EOF ); } + /** + * @covers WikiImporter::handleSiteInfo + * @dataProvider getSiteInfoXML + * @param string $xml + * @param array|null $namespaces + */ + public function testSiteInfoContainsNamespaces( $xml, $namespaces ) { + $source = $this->getInputStreamSource( $xml ); + + $importNamespaces = null; + $callback = function ( array $siteinfo, $innerImporter ) use ( &$importNamespaces ) { + $importNamespaces = $siteinfo['_namespaces']; + }; + + $importer = new WikiImporter( $source, ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) ); + $importer->setSiteInfoCallback( $callback ); + $importer->doImport(); + + $this->assertEquals( $importNamespaces, $namespaces ); + } + + public function getSiteInfoXML() { + return array( + array( + <<< EOF +<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en"> + <siteinfo> + <namespaces> + <namespace key="-2" case="first-letter">Media</namespace> + <namespace key="-1" case="first-letter">Special</namespace> + <namespace key="0" case="first-letter" /> + <namespace key="1" case="first-letter">Talk</namespace> + <namespace key="2" case="first-letter">User</namespace> + <namespace key="3" case="first-letter">User talk</namespace> + <namespace key="100" case="first-letter">Portal</namespace> + <namespace key="101" case="first-letter">Portal talk</namespace> + </namespaces> + </siteinfo> +</mediawiki> +EOF + , + array( + '-2' => 'Media', + '-1' => 'Special', + '0' => '', + '1' => 'Talk', + '2' => 'User', + '3' => 'User talk', + '100' => 'Portal', + '101' => 'Portal talk', + ) + ), + ); + } + } diff --git a/tests/phpunit/includes/LanguageConverterTest.php b/tests/phpunit/includes/LanguageConverterTest.php deleted file mode 100644 index d4ccca99..00000000 --- a/tests/phpunit/includes/LanguageConverterTest.php +++ /dev/null @@ -1,187 +0,0 @@ -<?php - -class LanguageConverterTest extends MediaWikiLangTestCase { - /** @var LanguageToTest */ - protected $lang = null; - /** @var TestConverter */ - protected $lc = null; - - protected function setUp() { - parent::setUp(); - - $this->setMwGlobals( array( - 'wgContLang' => Language::factory( 'tg' ), - 'wgLanguageCode' => 'tg', - 'wgDefaultLanguageVariant' => false, - 'wgMemc' => new EmptyBagOStuff, - 'wgRequest' => new FauxRequest( array() ), - 'wgUser' => new User, - ) ); - - $this->lang = new LanguageToTest(); - $this->lc = new TestConverter( - $this->lang, 'tg', - array( 'tg', 'tg-latn' ) - ); - } - - protected function tearDown() { - unset( $this->lc ); - unset( $this->lang ); - - parent::tearDown(); - } - - /** - * @covers LanguageConverter::getPreferredVariant - */ - public function testGetPreferredVariantDefaults() { - $this->assertEquals( 'tg', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getHeaderVariant - */ - public function testGetPreferredVariantHeaders() { - global $wgRequest; - $wgRequest->setHeader( 'Accept-Language', 'tg-latn' ); - - $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getHeaderVariant - */ - public function testGetPreferredVariantHeaderWeight() { - global $wgRequest; - $wgRequest->setHeader( 'Accept-Language', 'tg;q=1' ); - - $this->assertEquals( 'tg', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getHeaderVariant - */ - public function testGetPreferredVariantHeaderWeight2() { - global $wgRequest; - $wgRequest->setHeader( 'Accept-Language', 'tg-latn;q=1' ); - - $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getHeaderVariant - */ - public function testGetPreferredVariantHeaderMulti() { - global $wgRequest; - $wgRequest->setHeader( 'Accept-Language', 'en, tg-latn;q=1' ); - - $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - */ - public function testGetPreferredVariantUserOption() { - global $wgUser; - - $wgUser = new User; - $wgUser->load(); // from 'defaults' - $wgUser->mId = 1; - $wgUser->mDataLoaded = true; - $wgUser->mOptionsLoaded = true; - $wgUser->setOption( 'variant', 'tg-latn' ); - - $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getUserVariant - */ - public function testGetPreferredVariantUserOptionForForeignLanguage() { - global $wgContLang, $wgUser; - - $wgContLang = Language::factory( 'en' ); - $wgUser = new User; - $wgUser->load(); // from 'defaults' - $wgUser->mId = 1; - $wgUser->mDataLoaded = true; - $wgUser->mOptionsLoaded = true; - $wgUser->setOption( 'variant-tg', 'tg-latn' ); - - $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getUserVariant - * @covers LanguageConverter::getURLVariant - */ - public function testGetPreferredVariantHeaderUserVsUrl() { - global $wgContLang, $wgRequest, $wgUser; - - $wgContLang = Language::factory( 'tg-latn' ); - $wgRequest->setVal( 'variant', 'tg' ); - $wgUser = User::newFromId( "admin" ); - $wgUser->setId( 1 ); - $wgUser->mFrom = 'defaults'; - $wgUser->mOptionsLoaded = true; - // The user's data is ignored because the variant is set in the URL. - $wgUser->setOption( 'variant', 'tg-latn' ); - $this->assertEquals( 'tg', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - */ - public function testGetPreferredVariantDefaultLanguageVariant() { - global $wgDefaultLanguageVariant; - - $wgDefaultLanguageVariant = 'tg-latn'; - $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() ); - } - - /** - * @covers LanguageConverter::getPreferredVariant - * @covers LanguageConverter::getURLVariant - */ - public function testGetPreferredVariantDefaultLanguageVsUrlVariant() { - global $wgDefaultLanguageVariant, $wgRequest, $wgContLang; - - $wgContLang = Language::factory( 'tg-latn' ); - $wgDefaultLanguageVariant = 'tg'; - $wgRequest->setVal( 'variant', null ); - $this->assertEquals( 'tg', $this->lc->getPreferredVariant() ); - } -} - -/** - * Test converter (from Tajiki to latin orthography) - */ -class TestConverter extends LanguageConverter { - private $table = array( - 'б' => 'b', - 'в' => 'v', - 'г' => 'g', - ); - - function loadDefaultTables() { - $this->mTables = array( - 'tg-latn' => new ReplacementArray( $this->table ), - 'tg' => new ReplacementArray() - ); - } -} - -class LanguageToTest extends Language { - function __construct() { - parent::__construct(); - $variants = array( 'tg', 'tg-latn' ); - $this->mConverter = new TestConverter( $this, 'tg', $variants ); - } -} diff --git a/tests/phpunit/includes/LinkerTest.php b/tests/phpunit/includes/LinkerTest.php index 7b84107e..823c9330 100644 --- a/tests/phpunit/includes/LinkerTest.php +++ b/tests/phpunit/includes/LinkerTest.php @@ -149,10 +149,14 @@ class LinkerTest extends MediaWikiLangTestCase { "pre /* autocomment */ post", ), array( - '/* autocomment */ multiple? <a href="/wiki/Special:BlankPage#autocomment2" title="Special:BlankPage">→</a><span dir="auto"><span class="autocomment">autocomment2: </span> </span>', + '<a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→</a><span dir="auto"><span class="autocomment">autocomment: </span> multiple? <a href="/wiki/Special:BlankPage#autocomment2" title="Special:BlankPage">→</a><span dir="auto"><span class="autocomment">autocomment2: </span> </span></span>', "/* autocomment */ multiple? /* autocomment2 */ ", ), array( + '<a href="/wiki/Special:BlankPage#autocomment_containing_.2F.2A" title="Special:BlankPage">→</a><span dir="auto"><span class="autocomment">autocomment containing /*: </span> T70361</span>', + "/* autocomment containing /* */ T70361" + ), + array( '<a href="#autocomment">→</a><span dir="auto"><span class="autocomment">autocomment</span></span>', "/* autocomment */", false, true @@ -189,4 +193,54 @@ class LinkerTest extends MediaWikiLangTestCase { ), ); } + + /** + * @covers Linker::formatLinksInComment + * @dataProvider provideCasesForFormatLinksInComment + */ + public function testFormatLinksInComment( $expected, $input, $wiki ) { + + $conf = new SiteConfiguration(); + $conf->settings = array( + 'wgServer' => array( + 'enwiki' => '//en.example.org' + ), + 'wgArticlePath' => array( + 'enwiki' => '/w/$1', + ), + ); + $conf->suffixes = array( 'wiki' ); + $this->setMwGlobals( array( + 'wgScript' => '/wiki/index.php', + 'wgArticlePath' => '/wiki/$1', + 'wgWellFormedXml' => true, + 'wgCapitalLinks' => true, + 'wgConf' => $conf, + ) ); + + $this->assertEquals( + $expected, + Linker::formatLinksInComment( $input, Title::newFromText( 'Special:BlankPage' ), false, $wiki ) + ); + } + + public static function provideCasesForFormatLinksInComment() { + return array( + array( + 'foo bar <a href="/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>', + 'foo bar [[Special:BlankPage]]', + null, + ), + array( + '<a class="external" rel="nofollow" href="//en.example.org/w/Foo%27bar">Foo\'bar</a>', + "[[Foo'bar]]", + 'enwiki', + ), + array( + 'foo bar <a class="external" rel="nofollow" href="//en.example.org/w/Special:BlankPage">Special:BlankPage</a>', + 'foo bar [[Special:BlankPage]]', + 'enwiki', + ), + ); + } } diff --git a/tests/phpunit/includes/MWTimestampTest.php b/tests/phpunit/includes/MWTimestampTest.php index dcb98563..36562545 100644 --- a/tests/phpunit/includes/MWTimestampTest.php +++ b/tests/phpunit/includes/MWTimestampTest.php @@ -8,6 +8,9 @@ class MWTimestampTest extends MediaWikiLangTestCase { protected function setUp() { parent::setUp(); + // Avoid 'GetHumanTimestamp' hook and others + $this->setMwGlobals( 'wgHooks', array() ); + RequestContext::getMain()->setLanguage( Language::factory( 'en' ) ); } @@ -79,6 +82,17 @@ class MWTimestampTest extends MediaWikiLangTestCase { } /** + * Test an out of range timestamp + * @dataProvider provideOutOfRangeTimestamps + * @expectedException TimestampException + * @covers MWTimestamp + */ + public function testOutOfRangeTimestamps( $format, $input ) { + $timestamp = new MWTimestamp( $input ); + $timestamp->getTimestamp( $format ); + } + + /** * Test requesting an invalid output format. * @expectedException TimestampException * @covers MWTimestamp::getTimestamp @@ -111,6 +125,18 @@ class MWTimestampTest extends MediaWikiLangTestCase { } /** + * Returns a list of out of range timestamps in the format: + * array( type, timestamp_of_type ) + */ + public static function provideOutOfRangeTimestamps() { + return array( + // Various formats + array( TS_MW, '-62167219201' ), // -0001-12-31T23:59:59Z + array( TS_MW, '253402300800' ), // 10000-01-01T00:00:00Z + ); + } + + /** * @dataProvider provideHumanTimestampTests * @covers MWTimestamp::getHumanTimestamp */ diff --git a/tests/phpunit/includes/MediaWikiVersionFetcherTest.php b/tests/phpunit/includes/MediaWikiVersionFetcherTest.php index e548f817..fa59ef29 100644 --- a/tests/phpunit/includes/MediaWikiVersionFetcherTest.php +++ b/tests/phpunit/includes/MediaWikiVersionFetcherTest.php @@ -8,7 +8,6 @@ * * @group ComposerHooks * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class MediaWikiVersionFetcherTest extends PHPUnit_Framework_TestCase { diff --git a/tests/phpunit/includes/MessageTest.php b/tests/phpunit/includes/MessageTest.php index f3d2a84a..99ec2e42 100644 --- a/tests/phpunit/includes/MessageTest.php +++ b/tests/phpunit/includes/MessageTest.php @@ -16,22 +16,11 @@ class MessageTest extends MediaWikiLangTestCase { * @dataProvider provideConstructor */ public function testConstructor( $expectedLang, $key, $params, $language ) { - $reflection = new ReflectionClass( 'Message' ); - - $keyProperty = $reflection->getProperty( 'key' ); - $keyProperty->setAccessible( true ); - - $paramsProperty = $reflection->getProperty( 'parameters' ); - $paramsProperty->setAccessible( true ); - - $langProperty = $reflection->getProperty( 'language' ); - $langProperty->setAccessible( true ); - $message = new Message( $key, $params, $language ); - $this->assertEquals( $key, $keyProperty->getValue( $message ) ); - $this->assertEquals( $params, $paramsProperty->getValue( $message ) ); - $this->assertEquals( $expectedLang, $langProperty->getValue( $message ) ); + $this->assertEquals( $key, $message->getKey() ); + $this->assertEquals( $params, $message->getParams() ); + $this->assertEquals( $expectedLang, $message->getLanguage() ); } public static function provideConstructor() { @@ -45,21 +34,62 @@ class MessageTest extends MediaWikiLangTestCase { ); } - public static function provideTestParams() { + public static function provideConstructorParams() { return array( - array( array() ), - array( array( 'foo' ), 'foo' ), - array( array( 'foo', 'bar' ), 'foo', 'bar' ), - array( array( 'baz' ), array( 'baz' ) ), - array( array( 'baz', 'foo' ), array( 'baz', 'foo' ) ), - array( array( 'baz', 'foo' ), array( 'baz', 'foo' ), 'hhh' ), - array( array( 'baz', 'foo' ), array( 'baz', 'foo' ), 'hhh', array( 'ahahahahha' ) ), - array( array( 'baz', 'foo' ), array( 'baz', 'foo' ), array( 'ahahahahha' ) ), - array( array( 'baz' ), array( 'baz' ), array( 'ahahahahha' ) ), + array( + array(), + array(), + ), + array( + array( 'foo' ), + array( 'foo' ), + ), + array( + array( 'foo', 'bar' ), + array( 'foo', 'bar' ), + ), + array( + array( 'baz' ), + array( array( 'baz' ) ), + ), + array( + array( 'baz', 'foo' ), + array( array( 'baz', 'foo' ) ), + ), + array( + array( 'baz', 'foo' ), + array( array( 'baz', 'foo' ), 'hhh' ), + ), + array( + array( 'baz', 'foo' ), + array( array( 'baz', 'foo' ), 'hhh', array( 'ahahahahha' ) ), + ), + array( + array( 'baz', 'foo' ), + array( array( 'baz', 'foo' ), array( 'ahahahahha' ) ), + ), + array( + array( 'baz' ), + array( array( 'baz' ), array( 'ahahahahha' ) ), + ), ); } - public function getLanguageProvider() { + /** + * @covers Message::__construct + * @covers Message::getParams + * @dataProvider provideConstructorParams + */ + public function testConstructorParams( $expected, $args ) { + $msg = new Message( 'imasomething' ); + + $returned = call_user_func_array( array( $msg, 'params' ), $args ); + + $this->assertSame( $msg, $returned ); + $this->assertEquals( $expected, $msg->getParams() ); + } + + public static function provideConstructorLanguage() { return array( array( 'foo', array( 'bar' ), 'en' ), array( 'foo', array( 'bar' ), 'de' ) @@ -67,27 +97,98 @@ class MessageTest extends MediaWikiLangTestCase { } /** + * @covers Message::__construct * @covers Message::getLanguage - * @dataProvider getLanguageProvider + * @dataProvider provideConstructorLanguage */ - public function testGetLanguageCode( $key, $params, $languageCode ) { + public function testConstructorLanguage( $key, $params, $languageCode ) { $language = Language::factory( $languageCode ); $message = new Message( $key, $params, $language ); $this->assertEquals( $language, $message->getLanguage() ); } + public static function provideKeys() { + return array( + 'string' => array( + 'key' => 'mainpage', + 'expected' => array( 'mainpage' ), + ), + 'single' => array( + 'key' => array( 'mainpage' ), + 'expected' => array( 'mainpage' ), + ), + 'multi' => array( + 'key' => array( 'mainpage-foo', 'mainpage-bar', 'mainpage' ), + 'expected' => array( 'mainpage-foo', 'mainpage-bar', 'mainpage' ), + ), + 'empty' => array( + 'key' => array(), + 'expected' => null, + 'exception' => 'InvalidArgumentException', + ), + 'null' => array( + 'key' => null, + 'expected' => null, + 'exception' => 'InvalidArgumentException', + ), + 'bad type' => array( + 'key' => 123, + 'expected' => null, + 'exception' => 'InvalidArgumentException', + ), + ); + } + /** - * @covers Message::params - * @dataProvider provideTestParams + * @covers Message::__construct + * @covers Message::getKey + * @covers Message::isMultiKey + * @covers Message::getKeysToTry + * @dataProvider provideKeys */ - public function testParams( $expected ) { - $msg = new Message( 'imasomething' ); + public function testKeys( $key, $expected, $exception = null ) { + if ( $exception ) { + $this->setExpectedException( $exception ); + } + + $msg = new Message( $key ); + $this->assertContains( $msg->getKey(), $expected ); + $this->assertEquals( $expected, $msg->getKeysToTry() ); + $this->assertEquals( count( $expected ) > 1, $msg->isMultiKey() ); + } - $returned = call_user_func_array( array( $msg, 'params' ), array_slice( func_get_args(), 1 ) ); + /** + * @covers ::wfMessage + */ + public function testWfMessage() { + $this->assertInstanceOf( 'Message', wfMessage( 'mainpage' ) ); + $this->assertInstanceOf( 'Message', wfMessage( 'i-dont-exist-evar' ) ); + } - $this->assertSame( $msg, $returned ); - $this->assertEquals( $expected, $msg->getParams() ); + /** + * @covers Message::newFromKey + */ + public function testNewFromKey() { + $this->assertInstanceOf( 'Message', Message::newFromKey( 'mainpage' ) ); + $this->assertInstanceOf( 'Message', Message::newFromKey( 'i-dont-exist-evar' ) ); + } + + /** + * @covers ::wfMessage + * @covers Message::__construct + */ + public function testWfMessageParams() { + $this->assertEquals( 'Return to $1.', wfMessage( 'returnto' )->text() ); + $this->assertEquals( 'Return to $1.', wfMessage( 'returnto', array() )->text() ); + $this->assertEquals( + 'You have foo (bar).', + wfMessage( 'youhavenewmessages', 'foo', 'bar' )->text() + ); + $this->assertEquals( + 'You have foo (bar).', + wfMessage( 'youhavenewmessages', array( 'foo', 'bar' ) )->text() + ); } /** @@ -104,14 +205,42 @@ class MessageTest extends MediaWikiLangTestCase { /** * @covers Message::__construct + * @covers Message::text + * @covers Message::plain + * @covers Message::escaped + * @covers Message::toString */ - public function testKey() { - $this->assertInstanceOf( 'Message', wfMessage( 'mainpage' ) ); - $this->assertInstanceOf( 'Message', wfMessage( 'i-dont-exist-evar' ) ); + public function testToStringKey() { $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->text() ); - $this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->text() ); + $this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->text() ); + $this->assertEquals( '<i<dont>exist-evar>', wfMessage( 'i<dont>exist-evar' )->text() ); $this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->plain() ); + $this->assertEquals( '<i<dont>exist-evar>', wfMessage( 'i<dont>exist-evar' )->plain() ); $this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->escaped() ); + $this->assertEquals( + '<i<dont>exist-evar>', + wfMessage( 'i<dont>exist-evar' )->escaped() + ); + } + + public static function provideToString() { + return array( + array( 'mainpage', 'Main Page' ), + array( 'i-dont-exist-evar', '<i-dont-exist-evar>' ), + array( 'i-dont-exist-evar', '<i-dont-exist-evar>', 'escaped' ), + ); + } + + /** + * @covers Message::toString + * @covers Message::__toString + * @dataProvider provideToString + */ + public function testToString( $key, $expect, $format = 'plain' ) { + $msg = new Message( $key ); + $msg->$format(); + $this->assertEquals( $expect, $msg->toString() ); + $this->assertEquals( $expect, $msg->__toString() ); } /** @@ -132,26 +261,10 @@ class MessageTest extends MediaWikiLangTestCase { } /** - * @covers Message::__construct - */ - public function testMessageParams() { - $this->assertEquals( 'Return to $1.', wfMessage( 'returnto' )->text() ); - $this->assertEquals( 'Return to $1.', wfMessage( 'returnto', array() )->text() ); - $this->assertEquals( - 'You have foo (bar).', - wfMessage( 'youhavenewmessages', 'foo', 'bar' )->text() - ); - $this->assertEquals( - 'You have foo (bar).', - wfMessage( 'youhavenewmessages', array( 'foo', 'bar' ) )->text() - ); - } - - /** - * @covers Message::__construct + * @covers Message::rawParam * @covers Message::rawParams */ - public function testMessageParamSubstitution() { + public function testRawParams() { $this->assertEquals( '(Заглавная страница)', wfMessage( 'parentheses', 'Заглавная страница' )->plain() @@ -171,10 +284,21 @@ class MessageTest extends MediaWikiLangTestCase { } /** - * @covers Message::__construct + * @covers RawMessage::__construct + * @covers RawMessage::fetchMessage + */ + public function testRawMessage() { + $msg = new RawMessage( 'example &' ); + $this->assertEquals( 'example &', $msg->plain() ); + $this->assertEquals( 'example &', $msg->escaped() ); + } + + /** * @covers Message::params + * @covers Message::toString + * @covers Message::replaceParameters */ - public function testDeliciouslyManyParams() { + public function testReplaceManyParams() { $msg = new RawMessage( '$1$2$3$4$5$6$7$8$9$10$11$12' ); // One less than above has placeholders $params = array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' ); @@ -183,12 +307,20 @@ class MessageTest extends MediaWikiLangTestCase { $msg->params( $params )->plain(), 'Params > 9 are replaced correctly' ); + + $msg = new RawMessage( 'Params$*' ); + $params = array( 'ab', 'bc', 'cd' ); + $this->assertEquals( + 'Params: ab, bc, cd', + $msg->params( $params )->text() + ); } /** + * @covers Message::numParam * @covers Message::numParams */ - public function testMessageNumParams() { + public function testNumParams() { $lang = Language::factory( 'en' ); $msg = new RawMessage( '$1' ); @@ -200,9 +332,10 @@ class MessageTest extends MediaWikiLangTestCase { } /** + * @covers Message::durationParam * @covers Message::durationParams */ - public function testMessageDurationParams() { + public function testDurationParams() { $lang = Language::factory( 'en' ); $msg = new RawMessage( '$1' ); @@ -216,9 +349,10 @@ class MessageTest extends MediaWikiLangTestCase { /** * FIXME: This should not need database, but Language#formatExpiry does (bug 55912) * @group Database + * @covers Message::expiryParam * @covers Message::expiryParams */ - public function testMessageExpiryParams() { + public function testExpiryParams() { $lang = Language::factory( 'en' ); $msg = new RawMessage( '$1' ); @@ -230,9 +364,10 @@ class MessageTest extends MediaWikiLangTestCase { } /** + * @covers Message::timeperiodParam * @covers Message::timeperiodParams */ - public function testMessageTimeperiodParams() { + public function testTimeperiodParams() { $lang = Language::factory( 'en' ); $msg = new RawMessage( '$1' ); @@ -244,9 +379,10 @@ class MessageTest extends MediaWikiLangTestCase { } /** + * @covers Message::sizeParam * @covers Message::sizeParams */ - public function testMessageSizeParams() { + public function testSizeParams() { $lang = Language::factory( 'en' ); $msg = new RawMessage( '$1' ); @@ -258,9 +394,10 @@ class MessageTest extends MediaWikiLangTestCase { } /** + * @covers Message::bitrateParam * @covers Message::bitrateParams */ - public function testMessageBitrateParams() { + public function testBitrateParams() { $lang = Language::factory( 'en' ); $msg = new RawMessage( '$1' ); @@ -271,6 +408,100 @@ class MessageTest extends MediaWikiLangTestCase { ); } + public static function providePlaintextParams() { + return array( + array( + 'one $2 <div>foo</div> [[Bar]] {{Baz}} <', + 'plain', + ), + + array( + // expect + 'one $2 <div>foo</div> [[Bar]] {{Baz}} <', + // format + 'text', + ), + array( + 'one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;', + 'escaped', + ), + + array( + 'one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;', + 'parse', + ), + + array( + "<p>one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;\n</p>", + 'parseAsBlock', + ), + ); + } + + /** + * @covers Message::plaintextParam + * @covers Message::plaintextParams + * @covers Message::formatPlaintext + * @covers Message::toString + * @covers Message::parse + * @covers Message::parseAsBlock + * @dataProvider providePlaintextParams + */ + public function testPlaintextParams( $expect, $format ) { + $lang = Language::factory( 'en' ); + + $msg = new RawMessage( '$1 $2' ); + $params = array( + 'one $2', + '<div>foo</div> [[Bar]] {{Baz}} <', + ); + $this->assertEquals( + $expect, + $msg->inLanguage( $lang )->plaintextParams( $params )->$format(), + "Fail formatting for $format" + ); + } + + public static function provideParser() { + return array( + array( + "''&'' <x><!-- x -->", + 'plain', + ), + + array( + "''&'' <x><!-- x -->", + 'text', + ), + array( + '<i>&</i> <x>', + 'parse', + ), + + array( + "<p><i>&</i> <x>\n</p>", + 'parseAsBlock', + ), + ); + } + + /** + * @covers Message::text + * @covers Message::parse + * @covers Message::parseAsBlock + * @covers Message::toString + * @covers Message::transformText + * @covers Message::parseText + * @dataProvider provideParser + */ + public function testParser( $expect, $format ) { + $msg = new RawMessage( "''&'' <x><!-- x -->" ); + $this->assertEquals( + $expect, + $msg->inLanguage( 'en' )->$format() + ); + } + /** * @covers Message::inContentLanguage */ @@ -317,52 +548,4 @@ class MessageTest extends MediaWikiLangTestCase { public function testInLanguageThrows() { wfMessage( 'foo' )->inLanguage( 123 ); } - - public function keyProvider() { - return array( - 'string' => array( - 'key' => 'mainpage', - 'expected' => array( 'mainpage' ), - ), - 'single' => array( - 'key' => array( 'mainpage' ), - 'expected' => array( 'mainpage' ), - ), - 'multi' => array( - 'key' => array( 'mainpage-foo', 'mainpage-bar', 'mainpage' ), - 'expected' => array( 'mainpage-foo', 'mainpage-bar', 'mainpage' ), - ), - 'empty' => array( - 'key' => array(), - 'expected' => null, - 'exception' => 'InvalidArgumentException', - ), - 'null' => array( - 'key' => null, - 'expected' => null, - 'exception' => 'InvalidArgumentException', - ), - 'bad type' => array( - 'key' => 17, - 'expected' => null, - 'exception' => 'InvalidArgumentException', - ), - ); - } - - /** - * @dataProvider keyProvider() - * - * @covers Message::getKey - */ - public function testGetKey( $key, $expected, $exception = null ) { - if ( $exception ) { - $this->setExpectedException( $exception ); - } - - $msg = new Message( $key ); - $this->assertEquals( $expected, $msg->getKeysToTry() ); - $this->assertEquals( count( $expected ) > 1, $msg->isMultiKey() ); - $this->assertContains( $msg->getKey(), $expected ); - } } diff --git a/tests/phpunit/includes/MovePageTest.php b/tests/phpunit/includes/MovePageTest.php new file mode 100644 index 00000000..9501e452 --- /dev/null +++ b/tests/phpunit/includes/MovePageTest.php @@ -0,0 +1,63 @@ +<?php + +/** + * @group Database + */ +class MovePageTest extends MediaWikiTestCase { + + /** + * @dataProvider provideIsValidMove + * @covers MovePage::isValidMove + * @covers MovePage::isValidFileMove + */ + public function testIsValidMove( $old, $new, $error ) { + $this->setMwGlobals( 'wgContentHandlerUseDB', false ); + $mp = new MovePage( + Title::newFromText( $old ), + Title::newFromText( $new ) + ); + $status = $mp->isValidMove(); + if ( $error === true ) { + $this->assertTrue( $status->isGood() ); + } else { + $this->assertTrue( $status->hasMessage( $error ) ); + } + } + + /** + * This should be kept in sync with TitleTest::provideTestIsValidMoveOperation + */ + public static function provideIsValidMove() { + return array( + // for MovePage::isValidMove + array( 'Test', 'Test', 'selfmove' ), + array( 'Special:FooBar', 'Test', 'immobile-source-namespace' ), + array( 'Test', 'Special:FooBar', 'immobile-target-namespace' ), + array( 'MediaWiki:Common.js', 'Help:Some wikitext page', 'bad-target-model' ), + array( 'Page', 'File:Test.jpg', 'nonfile-cannot-move-to-file' ), + // for MovePage::isValidFileMove + array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' ), + ); + } + + /** + * Integration test to catch regressions like T74870. Taken and modified + * from SemanticMediaWiki + */ + public function testTitleMoveCompleteIntegrationTest() { + $oldTitle = Title::newFromText( 'Help:Some title' ); + WikiPage::factory( $oldTitle )->doEditContent( new WikitextContent( 'foo' ), 'bar' ); + $newTitle = Title::newFromText( 'Help:Some other title' ); + $this->assertNull( + WikiPage::factory( $newTitle )->getRevision() + ); + + $this->assertTrue( $oldTitle->moveTo( $newTitle, false, 'test1', true ) ); + $this->assertNotNull( + WikiPage::factory( $oldTitle )->getRevision() + ); + $this->assertNotNull( + WikiPage::factory( $newTitle)->getRevision() + ); + } +} diff --git a/tests/phpunit/includes/OutputPageTest.php b/tests/phpunit/includes/OutputPageTest.php index d7e8cd31..6c6d95ee 100644 --- a/tests/phpunit/includes/OutputPageTest.php +++ b/tests/phpunit/includes/OutputPageTest.php @@ -172,18 +172,18 @@ mw.test.baz({token:123});mw.loader.state({"test.quux":"ready"}); array( array( 'test.quux', ResourceLoaderModule::TYPE_COMBINED ), '<script>if(window.mw){ -mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"css":[".mw-icon{transition:none}\n"]},{}); +mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"css":[".mw-icon{transition:none}\n"]}); }</script> ' ), - // Load module script with with ESI + // Load module script with ESI array( array( 'test.foo', ResourceLoaderModule::TYPE_SCRIPTS, true ), '<script><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&lang=en&modules=test.foo&only=scripts&skin=fallback&*" /></script> ' ), - // Load module styles with with ESI + // Load module styles with ESI array( array( 'test.foo', ResourceLoaderModule::TYPE_STYLES, true ), '<style><esi:include src="http://127.0.0.1:8080/w/load.php?debug=false&lang=en&modules=test.foo&only=styles&skin=fallback&*" /></style> @@ -203,9 +203,13 @@ mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{" // Load two modules in separate groups array( array( array( 'test.group.foo', 'test.group.bar' ), ResourceLoaderModule::TYPE_COMBINED ), - '<script src="http://127.0.0.1:8080/w/load.php?debug=false&lang=en&modules=test.group.bar&skin=fallback&*"></script> -<script src="http://127.0.0.1:8080/w/load.php?debug=false&lang=en&modules=test.group.foo&skin=fallback&*"></script> -', + '<script>if(window.mw){ +document.write("\u003Cscript src=\"http://127.0.0.1:8080/w/load.php?debug=false\u0026amp;lang=en\u0026amp;modules=test.group.bar\u0026amp;skin=fallback\u0026amp;*\"\u003E\u003C/script\u003E"); +}</script> +<script>if(window.mw){ +document.write("\u003Cscript src=\"http://127.0.0.1:8080/w/load.php?debug=false\u0026amp;lang=en\u0026amp;modules=test.group.foo\u0026amp;skin=fallback\u0026amp;*\"\u003E\u003C/script\u003E"); +}</script> +' ), ); } @@ -213,6 +217,11 @@ mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{" /** * @dataProvider provideMakeResourceLoaderLink * @covers OutputPage::makeResourceLoaderLink + * @covers ResourceLoader::makeLoaderImplementScript + * @covers ResourceLoader::makeModuleResponse + * @covers ResourceLoader::makeInlineScript + * @covers ResourceLoader::makeLoaderStateScript + * @covers ResourceLoader::createLoaderURL */ public function testMakeResourceLoaderLink( $args, $expectedHtml ) { $this->setMwGlobals( array( @@ -230,6 +239,7 @@ mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{" $ctx->setLanguage( 'en' ); $out = new OutputPage( $ctx ); $rl = $out->getResourceLoader(); + $rl->setMessageBlobStore( new NullMessageBlobStore() ); $rl->register( array( 'test.foo' => new ResourceLoaderTestModule( array( 'script' => 'mw.test.foo( { a: true } );', @@ -271,3 +281,26 @@ mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{" $this->assertEquals( $expectedHtml, $actualHtml ); } } + +/** + * MessageBlobStore that doesn't do anything + */ +class NullMessageBlobStore extends MessageBlobStore { + public function get ( ResourceLoader $resourceLoader, $modules, $lang ) { + return array(); + } + + public function insertMessageBlob ( $name, ResourceLoaderModule $module, $lang ) { + return false; + } + + public function updateModule ( $name, ResourceLoaderModule $module, $lang ) { + return; + } + + public function updateMessage ( $key ) { + } + public function clear() { + } +} + diff --git a/tests/phpunit/includes/PrefixSearchTest.php b/tests/phpunit/includes/PrefixSearchTest.php new file mode 100644 index 00000000..d63541b7 --- /dev/null +++ b/tests/phpunit/includes/PrefixSearchTest.php @@ -0,0 +1,306 @@ +<?php +/** + * @group Search + * @group Database + */ +class PrefixSearchTest extends MediaWikiLangTestCase { + + public function addDBData() { + $this->insertPage( 'Sandbox' ); + $this->insertPage( 'Bar' ); + $this->insertPage( 'Example' ); + $this->insertPage( 'Example Bar' ); + $this->insertPage( 'Example Foo' ); + $this->insertPage( 'Example Foo/Bar' ); + $this->insertPage( 'Example/Baz' ); + $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' ); + $this->insertPage( 'Redirect Test' ); + $this->insertPage( 'Redirect Test Worse Result' ); + $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' ); + $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' ); + $this->insertPage( 'Redirect Test2' ); + $this->insertPage( 'Redirect Test2 Worse Result' ); + + $this->insertPage( 'Talk:Sandbox' ); + $this->insertPage( 'Talk:Example' ); + + $this->insertPage( 'User:Example' ); + } + + protected function setUp() { + parent::setUp(); + + if ( !$this->isWikitextNS( NS_MAIN ) ) { + $this->markTestSkipped( 'Main namespace does not support wikitext.' ); + } + + // Avoid special pages from extensions interferring with the tests + $this->setMwGlobals( 'wgSpecialPages', array() ); + } + + protected function searchProvision( Array $results = null ) { + if ( $results === null ) { + $this->setMwGlobals( 'wgHooks', array() ); + } else { + $this->setMwGlobals( 'wgHooks', array( + 'PrefixSearchBackend' => array( + function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) { + $srchres = $results; + return false; + } + ), + ) ); + } + } + + public static function provideSearch() { + return array( + array( array( + 'Empty string', + 'query' => '', + 'results' => array(), + ) ), + array( array( + 'Main namespace with title prefix', + 'query' => 'Ex', + 'results' => array( + 'Example', + 'Example/Baz', + 'Example Bar', + ), + // Third result when testing offset + 'offsetresult' => array( + 'Example Foo', + ), + ) ), + array( array( + 'Talk namespace prefix', + 'query' => 'Talk:', + 'results' => array( + 'Talk:Example', + 'Talk:Sandbox', + ), + ) ), + array( array( + 'User namespace prefix', + 'query' => 'User:', + 'results' => array( + 'User:Example', + ), + ) ), + array( array( + 'Special namespace prefix', + 'query' => 'Special:', + 'results' => array( + 'Special:ActiveUsers', + 'Special:AllMessages', + 'Special:AllMyFiles', + ), + // Third result when testing offset + 'offsetresult' => array( + 'Special:AllMyUploads', + ), + ) ), + array( array( + 'Special namespace with prefix', + 'query' => 'Special:Un', + 'results' => array( + 'Special:Unblock', + 'Special:UncategorizedCategories', + 'Special:UncategorizedFiles', + ), + // Third result when testing offset + 'offsetresult' => array( + 'Special:UncategorizedImages', + ), + ) ), + array( array( + 'Special page name', + 'query' => 'Special:EditWatchlist', + 'results' => array( + 'Special:EditWatchlist', + ), + ) ), + array( array( + 'Special page subpages', + 'query' => 'Special:EditWatchlist/', + 'results' => array( + 'Special:EditWatchlist/clear', + 'Special:EditWatchlist/raw', + ), + ) ), + array( array( + 'Special page subpages with prefix', + 'query' => 'Special:EditWatchlist/cl', + 'results' => array( + 'Special:EditWatchlist/clear', + ), + ) ), + ); + } + + /** + * @dataProvider provideSearch + * @covers PrefixSearch::search + * @covers PrefixSearch::searchBackend + */ + public function testSearch( Array $case ) { + $this->searchProvision( null ); + $searcher = new StringPrefixSearch; + $results = $searcher->search( $case['query'], 3 ); + $this->assertEquals( + $case['results'], + $results, + $case[0] + ); + } + + /** + * @dataProvider provideSearch + * @covers PrefixSearch::search + * @covers PrefixSearch::searchBackend + */ + public function testSearchWithOffset( Array $case ) { + $this->searchProvision( null ); + $searcher = new StringPrefixSearch; + $results = $searcher->search( $case['query'], 3, array(), 1 ); + + // We don't expect the first result when offsetting + array_shift( $case['results'] ); + // And sometimes we expect a different last result + $expected = isset( $case['offsetresult'] ) ? + array_merge( $case['results'], $case['offsetresult'] ) : + $case['results']; + + $this->assertEquals( + $expected, + $results, + $case[0] + ); + } + + public static function provideSearchBackend() { + return array( + array( array( + 'Simple case', + 'provision' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + 'query' => 'Bar', + 'results' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + ) ), + array( array( + 'Exact match not on top (bug 70958)', + 'provision' => array( + 'Barcelona', + 'Bar', + 'Barbara', + ), + 'query' => 'Bar', + 'results' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + ) ), + array( array( + 'Exact match missing (bug 70958)', + 'provision' => array( + 'Barcelona', + 'Barbara', + 'Bart', + ), + 'query' => 'Bar', + 'results' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + ) ), + array( array( + 'Exact match missing and not existing', + 'provision' => array( + 'Exile', + 'Exist', + 'External', + ), + 'query' => 'Ex', + 'results' => array( + 'Exile', + 'Exist', + 'External', + ), + ) ), + array( array( + "Exact match shouldn't override already found match if " . + "exact is redirect and found isn't", + 'provision' => array( + // Target of the exact match is low in the list + 'Redirect Test Worse Result', + 'Redirect Test', + ), + 'query' => 'redirect test', + 'results' => array( + // Redirect target is pulled up and exact match isn't added + 'Redirect Test', + 'Redirect Test Worse Result', + ), + ) ), + array( array( + "Exact match shouldn't override already found match if " . + "both exact match and found match are redirect", + 'provision' => array( + // Another redirect to the same target as the exact match + // is low in the list + 'Redirect Test2 Worse Result', + 'Redirect test2', + ), + 'query' => 'redirect TEST2', + 'results' => array( + // Found redirect is pulled to the top and exact match isn't + // added + 'Redirect test2', + 'Redirect Test2 Worse Result', + ), + ) ), + array( array( + "Exact match should override any already found matches that " . + "are redirects to it", + 'provision' => array( + // Another redirect to the same target as the exact match + // is low in the list + 'Redirect Test Worse Result', + 'Redirect test', + ), + 'query' => 'Redirect Test', + 'results' => array( + // Found redirect is pulled to the top and exact match isn't + // added + 'Redirect Test', + 'Redirect Test Worse Result', + ), + ) ), + ); + } + + /** + * @dataProvider provideSearchBackend + * @covers PrefixSearch::searchBackend + */ + public function testSearchBackend( Array $case ) { + $this->searchProvision( $case['provision'] ); + $searcher = new StringPrefixSearch; + $results = $searcher->search( $case['query'], 3 ); + $this->assertEquals( + $case['results'], + $results, + $case[0] + ); + } +} diff --git a/tests/phpunit/includes/SampleTest.php b/tests/phpunit/includes/SampleTest.php index 25858110..c5944d16 100644 --- a/tests/phpunit/includes/SampleTest.php +++ b/tests/phpunit/includes/SampleTest.php @@ -97,7 +97,7 @@ class TestSample extends MediaWikiLangTestCase { // @codingStandardsIgnoreStart Ignore long line warning /** - * @expectedException MWException object + * @expectedException InvalidArgumentException * See http://phpunit.de/manual/3.7/en/appendixes.annotations.html#appendixes.annotations.expectedException */ // @codingStandardsIgnoreEnd diff --git a/tests/phpunit/includes/SanitizerTest.php b/tests/phpunit/includes/SanitizerTest.php index 50c1e509..c615c460 100644 --- a/tests/phpunit/includes/SanitizerTest.php +++ b/tests/phpunit/includes/SanitizerTest.php @@ -6,12 +6,6 @@ */ class SanitizerTest extends MediaWikiTestCase { - protected function setUp() { - parent::setUp(); - - AutoLoader::loadClass( 'Sanitizer' ); - } - /** * @covers Sanitizer::decodeCharReferences */ @@ -85,7 +79,7 @@ class SanitizerTest extends MediaWikiTestCase { */ public function testInvalidNumberedEntities() { $this->assertEquals( - UTF8_REPLACEMENT, + UtfNormal\Constants::UTF8_REPLACEMENT, Sanitizer::decodeCharReferences( "�" ), 'Invalid numbered entity' ); @@ -346,4 +340,25 @@ class SanitizerTest extends MediaWikiTestCase { $message ); } + + /** + * @dataProvider provideEscapeHtmlAllowEntities + * @covers Sanitizer::escapeHtmlAllowEntities + */ + public function testEscapeHtmlAllowEntities( $expected, $html ) { + $this->assertEquals( + $expected, + Sanitizer::escapeHtmlAllowEntities( $html ) + ); + } + + public static function provideEscapeHtmlAllowEntities() { + return array( + array( 'foo', 'foo' ), + array( 'a¡b', 'a¡b' ), + array( 'foo'bar', "foo'bar" ), + array( '<script>foo</script>', '<script>foo</script>' ), + ); + } + } diff --git a/tests/phpunit/includes/StatusTest.php b/tests/phpunit/includes/StatusTest.php index 628c59b6..c013f4fc 100644 --- a/tests/phpunit/includes/StatusTest.php +++ b/tests/phpunit/includes/StatusTest.php @@ -57,6 +57,17 @@ class StatusTest extends MediaWikiLangTestCase { } /** + * + */ + public function testOkAndErrors() { + $status = Status::newGood( 'foo' ); + $this->assertTrue( $status->ok ); + $status = Status::newFatal( 'foo', 1, 2 ); + $this->assertFalse( $status->ok ); + $this->assertArrayEquals( array( array( 'type' => 'error', 'message' => 'foo', 'params' => array( 1, 2 ) ) ), $status->errors ); + } + + /** * @dataProvider provideSetResult * @covers Status::setResult */ @@ -109,7 +120,9 @@ class StatusTest extends MediaWikiLangTestCase { public function testIsGood( $ok, $errors, $expected ) { $status = new Status(); $status->ok = $ok; - $status->errors = $errors; + foreach ( $errors as $error ) { + $status->warning( $error ); + } $this->assertEquals( $expected, $status->isGood() ); } diff --git a/tests/phpunit/includes/TemplateParserTest.php b/tests/phpunit/includes/TemplateParserTest.php new file mode 100644 index 00000000..81854ff3 --- /dev/null +++ b/tests/phpunit/includes/TemplateParserTest.php @@ -0,0 +1,63 @@ +<?php + +/** + * @group Templates + */ +class TemplateParserTest extends MediaWikiTestCase { + + protected $templateDir; + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgSecretKey' => 'foo', + 'wgMemc' => new EmptyBagOStuff(), + ) ); + + $this->templateDir = dirname( __DIR__ ) . '/data/templates/'; + } + + /** + * @dataProvider provideProcessTemplate + * @covers TemplateParser::processTemplate + * @covers TemplateParser::getTemplate + * @covers TemplateParser::getTemplateFilename + */ + public function testProcessTemplate( $name, $args, $result, $exception = false ) { + if ( $exception ) { + $this->setExpectedException( $exception ); + } + $tp = new TemplateParser( $this->templateDir ); + $this->assertEquals( $result, $tp->processTemplate( $name, $args ) ); + } + + public static function provideProcessTemplate() { + return array( + array( + 'foobar', + array(), + "hello world!\n" + ), + array( + 'foobar_args', + array( + 'planet' => 'world', + ), + "hello world!\n", + ), + array( + '../foobar', + array(), + false, + 'UnexpectedValueException' + ), + array( + 'nonexistenttemplate', + array(), + false, + 'RuntimeException', + ) + ); + } +} diff --git a/tests/phpunit/includes/TestUser.php b/tests/phpunit/includes/TestUser.php index 610a6acd..754568d0 100644 --- a/tests/phpunit/includes/TestUser.php +++ b/tests/phpunit/includes/TestUser.php @@ -5,24 +5,41 @@ * like password if we log in via the API. */ class TestUser { + /** + * @deprecated Since 1.25. Use TestUser::getUser()->getName() + * @private + * @var string + */ public $username; + + /** + * @deprecated Since 1.25. Use TestUser::getPassword() + * @private + * @var string + */ public $password; - public $email; - public $groups; + + /** + * @deprecated Since 1.25. Use TestUser::getUser() + * @private + * @var User + */ public $user; + private function assertNotReal() { + global $wgDBprefix; + if ( $wgDBprefix !== MediaWikiTestCase::DB_PREFIX && $wgDBprefix !== MediaWikiTestCase::ORA_DB_PREFIX ) { + throw new MWException( "Can't create user on real database" ); + } + } + public function __construct( $username, $realname = 'Real Name', $email = 'sample@example.com', $groups = array() ) { - $this->username = $username; - $this->realname = $realname; - $this->email = $email; - $this->groups = $groups; + $this->assertNotReal(); - // don't allow user to hardcode or select passwords -- people sometimes run tests - // on live wikis. Sometimes we create sysop users in these tests. A sysop user with - // a known password would be a Bad Thing. - $this->password = User::randomPassword(); + $this->username = $username; + $this->password = 'TestUser'; $this->user = User::newFromName( $this->username ); $this->user->load(); @@ -31,32 +48,99 @@ class TestUser { // But for now, we just need to create or update the user with the desired properties. // we particularly need the new password, since we just generated it randomly. // In core MediaWiki, there is no functionality to delete users, so this is the best we can do. - if ( !$this->user->getID() ) { + if ( !$this->user->isLoggedIn() ) { // create the user $this->user = User::createNew( $this->username, array( - "email" => $this->email, - "real_name" => $this->realname + "email" => $email, + "real_name" => $realname ) ); + if ( !$this->user ) { - throw new Exception( "error creating user" ); + throw new MWException( "Error creating TestUser " . $username ); } } - // update the user to use the new random password and other details - $this->user->setPassword( $this->password ); - $this->user->setEmail( $this->email ); - $this->user->setRealName( $this->realname ); + // Update the user to use the password and other details + $change = $this->setPassword( $this->password ) || + $this->setEmail( $email ) || + $this->setRealName( $realname ); // Adjust groups by adding any missing ones and removing any extras $currentGroups = $this->user->getGroups(); - foreach ( array_diff( $this->groups, $currentGroups ) as $group ) { + foreach ( array_diff( $groups, $currentGroups ) as $group ) { $this->user->addGroup( $group ); } - foreach ( array_diff( $currentGroups, $this->groups ) as $group ) { + foreach ( array_diff( $currentGroups, $groups ) as $group ) { $this->user->removeGroup( $group ); } - $this->user->saveSettings(); + if ( $change ) { + $this->user->saveSettings(); + } + } + + /** + * @param string $realname + * @return bool + */ + private function setRealName( $realname ) { + if ( $this->user->getRealName() !== $realname ) { + $this->user->setRealName( $realname ); + return true; + } + + return false; + } + + /** + * @param string $email + * @return bool + */ + private function setEmail( $email ) { + if ( $this->user->getEmail() !== $email ) { + $this->user->setEmail( $email ); + return true; + } + + return false; + } + + /** + * @param string $password + * @return bool + */ + private function setPassword( $password ) { + $passwordFactory = $this->user->getPasswordFactory(); + $oldDefaultType = $passwordFactory->getDefaultType(); + + // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only + $passwordFactory->setDefaultType( 'A' ); + $newPassword = $passwordFactory->newFromPlaintext( $password, $this->user->getPassword() ); + + $change = false; + if ( !$this->user->getPassword()->equals( $newPassword ) ) { + // Password changed + $this->user->setPassword( $password ); + $change = true; + } + + $passwordFactory->setDefaultType( $oldDefaultType ); + + return $change; + } + + /** + * @return User + */ + public function getUser() { + return $this->user; + } + + /** + * @return string + */ + public function getPassword() { + return $this->password; } } diff --git a/tests/phpunit/includes/TestingAccessWrapper.php b/tests/phpunit/includes/TestingAccessWrapper.php new file mode 100644 index 00000000..84c0f9b5 --- /dev/null +++ b/tests/phpunit/includes/TestingAccessWrapper.php @@ -0,0 +1,50 @@ +<?php +/** + * Circumvent access restrictions on object internals + * + * This can be helpful for writing tests that can probe object internals, + * without having to modify the class under test to accomodate. + * + * Wrap an object with private methods as follows: + * $title = TestingAccessWrapper::newFromObject( Title::newFromDBkey( $key ) ); + * + * You can access private and protected instance methods and variables: + * $formatter = $title->getTitleFormatter(); + * + * TODO: + * - Provide access to static methods and properties. + * - Organize other helper classes in tests/testHelpers.inc into a directory. + */ +class TestingAccessWrapper { + public $object; + + /** + * Return the same object, without access restrictions. + */ + public static function newFromObject( $object ) { + $wrapper = new TestingAccessWrapper(); + $wrapper->object = $object; + return $wrapper; + } + + public function __call( $method, $args ) { + $classReflection = new ReflectionClass( $this->object ); + $methodReflection = $classReflection->getMethod( $method ); + $methodReflection->setAccessible( true ); + return $methodReflection->invokeArgs( $this->object, $args ); + } + + public function __set( $name, $value ) { + $classReflection = new ReflectionClass( $this->object ); + $propertyReflection = $classReflection->getProperty( $name ); + $propertyReflection->setAccessible( true ); + $propertyReflection->setValue( $this->object, $value ); + } + + public function __get( $name ) { + $classReflection = new ReflectionClass( $this->object ); + $propertyReflection = $classReflection->getProperty( $name ); + $propertyReflection->setAccessible( true ); + return $propertyReflection->getValue( $this->object ); + } +} diff --git a/tests/phpunit/includes/TestingAccessWrapperTest.php b/tests/phpunit/includes/TestingAccessWrapperTest.php new file mode 100644 index 00000000..7e5b91a1 --- /dev/null +++ b/tests/phpunit/includes/TestingAccessWrapperTest.php @@ -0,0 +1,34 @@ +<?php + +class TestingAccessWrapperTest extends MediaWikiTestCase { + protected $raw; + protected $wrapped; + + function setUp() { + parent::setUp(); + + require_once __DIR__ . '/../data/helpers/WellProtectedClass.php'; + $this->raw = new WellProtectedClass(); + $this->wrapped = TestingAccessWrapper::newFromObject( $this->raw ); + } + + function testGetProperty() { + $this->assertSame( 1, $this->wrapped->property ); + } + + function testSetProperty() { + $this->wrapped->property = 10; + $this->assertSame( 10, $this->wrapped->property ); + $this->assertSame( 10, $this->raw->getProperty() ); + } + + function testCallMethod() { + $this->wrapped->incrementPropertyValue(); + $this->assertSame( 2, $this->wrapped->property ); + $this->assertSame( 2, $this->raw->getProperty() ); + } + + function testCallMethodTwoArgs() { + $this->assertSame( 'two', $this->wrapped->whatSecondArg( 'one', 'two' ) ); + } +} diff --git a/tests/phpunit/includes/TitleMethodsTest.php b/tests/phpunit/includes/TitleMethodsTest.php index 5904facd..e186438b 100644 --- a/tests/phpunit/includes/TitleMethodsTest.php +++ b/tests/phpunit/includes/TitleMethodsTest.php @@ -7,7 +7,7 @@ * @note We don't make assumptions about the main namespace. * But we do expect the Help namespace to contain Wikitext. */ -class TitleMethodsTest extends MediaWikiTestCase { +class TitleMethodsTest extends MediaWikiLangTestCase { protected function setUp() { global $wgContLang; @@ -297,4 +297,43 @@ class TitleMethodsTest extends MediaWikiTestCase { $title = Title::newFromText( $title ); $this->assertEquals( $expectedBool, $title->isWikitextPage() ); } + + public static function provideGetOtherPage() { + return array( + array( 'Main Page', 'Talk:Main Page' ), + array( 'Talk:Main Page', 'Main Page' ), + array( 'Help:Main Page', 'Help talk:Main Page' ), + array( 'Help talk:Main Page', 'Help:Main Page' ), + array( 'Special:FooBar', null ), + ); + } + + /** + * @dataProvider provideGetOtherpage + * @covers Title::getOtherPage + * + * @param string $text + * @param string|null $expected + */ + public function testGetOtherPage( $text, $expected ) { + if ( $expected === null ) { + $this->setExpectedException( 'MWException' ); + } + + $title = Title::newFromText( $text ); + $this->assertEquals( $expected, $title->getOtherPage()->getPrefixedText() ); + } + + public function testClearCaches() { + $linkCache = LinkCache::singleton(); + + $title1 = Title::newFromText( 'Foo' ); + $linkCache->addGoodLinkObj( 23, $title1 ); + + Title::clearCaches(); + + $title2 = Title::newFromText( 'Foo' ); + $this->assertNotSame( $title1, $title2, 'title cache should be empty' ); + $this->assertEquals( 0, $linkCache->getGoodLinkID( 'Foo' ), 'link cache should be empty' ); + } } diff --git a/tests/phpunit/includes/TitlePermissionTest.php b/tests/phpunit/includes/TitlePermissionTest.php index d2400b3f..022c7d53 100644 --- a/tests/phpunit/includes/TitlePermissionTest.php +++ b/tests/phpunit/includes/TitlePermissionTest.php @@ -326,6 +326,10 @@ class TitlePermissionTest extends MediaWikiLangTestCase { $this->setUserPerm( null ); $this->assertEquals( $check[$action][0], $this->title->getUserPermissionsErrors( $action, $this->user, true ) ); + $this->assertEquals( $check[$action][0], + $this->title->getUserPermissionsErrors( $action, $this->user, 'full' ) ); + $this->assertEquals( $check[$action][0], + $this->title->getUserPermissionsErrors( $action, $this->user, 'secure' ) ); global $wgGroupPermissions; $old = $wgGroupPermissions; @@ -333,11 +337,19 @@ class TitlePermissionTest extends MediaWikiLangTestCase { $this->assertEquals( $check[$action][1], $this->title->getUserPermissionsErrors( $action, $this->user, true ) ); + $this->assertEquals( $check[$action][1], + $this->title->getUserPermissionsErrors( $action, $this->user, 'full' ) ); + $this->assertEquals( $check[$action][1], + $this->title->getUserPermissionsErrors( $action, $this->user, 'secure' ) ); $wgGroupPermissions = $old; $this->setUserPerm( $action ); $this->assertEquals( $check[$action][2], $this->title->getUserPermissionsErrors( $action, $this->user, true ) ); + $this->assertEquals( $check[$action][2], + $this->title->getUserPermissionsErrors( $action, $this->user, 'full' ) ); + $this->assertEquals( $check[$action][2], + $this->title->getUserPermissionsErrors( $action, $this->user, 'secure' ) ); $this->setUserPerm( $action ); $this->assertEquals( $check[$action][3], @@ -403,7 +415,8 @@ class TitlePermissionTest extends MediaWikiLangTestCase { $this->setTitle( NS_USER ); $this->setUserPerm( '' ); - $this->assertEquals( array( array( 'badaccess-group0' ), array( 'namespaceprotected', 'User', 'bogus' ) ), + $this->assertEquals( array( array( 'badaccess-group0' ), + array( 'namespaceprotected', 'User', 'bogus' ) ), $this->title->getUserPermissionsErrors( 'bogus', $this->user ) ); $this->setTitle( NS_MEDIAWIKI ); @@ -630,7 +643,8 @@ class TitlePermissionTest extends MediaWikiLangTestCase { $this->assertEquals( false, $this->title->userCan( 'bogus', $this->user ) ); - $this->assertEquals( array( array( "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n", 'bogus' ), + $this->assertEquals( array( + array( "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n", 'bogus' ), array( "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n", 'bogus' ), array( "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n", 'bogus' ) ), $this->title->getUserPermissionsErrors( 'bogus', $this->user ) ); @@ -648,10 +662,10 @@ class TitlePermissionTest extends MediaWikiLangTestCase { public function testActionPermissions() { $this->setUserPerm( array( "createpage" ) ); $this->setTitle( NS_MAIN, "test page" ); - $this->title->mTitleProtection['pt_create_perm'] = ''; - $this->title->mTitleProtection['pt_user'] = $this->user->getID(); - $this->title->mTitleProtection['pt_expiry'] = wfGetDB( DB_SLAVE )->getInfinity(); - $this->title->mTitleProtection['pt_reason'] = 'test'; + $this->title->mTitleProtection['permission'] = ''; + $this->title->mTitleProtection['user'] = $this->user->getID(); + $this->title->mTitleProtection['expiry'] = wfGetDB( DB_SLAVE )->getInfinity(); + $this->title->mTitleProtection['reason'] = 'test'; $this->title->mCascadeRestriction = false; $this->assertEquals( array( array( 'titleprotected', 'Useruser', 'test' ) ), @@ -659,7 +673,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase { $this->assertEquals( false, $this->title->userCan( 'create', $this->user ) ); - $this->title->mTitleProtection['pt_create_perm'] = 'sysop'; + $this->title->mTitleProtection['permission'] = 'editprotected'; $this->setUserPerm( array( 'createpage', 'protect' ) ); $this->assertEquals( array( array( 'titleprotected', 'Useruser', 'test' ) ), $this->title->getUserPermissionsErrors( 'create', $this->user ) ); diff --git a/tests/phpunit/includes/TitleTest.php b/tests/phpunit/includes/TitleTest.php index fb58381f..d55f958b 100644 --- a/tests/phpunit/includes/TitleTest.php +++ b/tests/phpunit/includes/TitleTest.php @@ -14,6 +14,7 @@ class TitleTest extends MediaWikiTestCase { 'wgLang' => Language::factory( 'en' ), 'wgAllowUserJs' => false, 'wgDefaultLanguageVariant' => false, + 'wgMetaNamespace' => 'Project', ) ); } @@ -324,36 +325,19 @@ class TitleTest extends MediaWikiTestCase { $whitelistRegexp = array( $whitelistRegexp ); } + $this->setMwGlobals( array( + // So User::isEveryoneAllowed( 'read' ) === false + 'wgGroupPermissions' => array( '*' => array( 'read' => false ) ), + 'wgWhitelistRead' => array( 'some random non sense title' ), + 'wgWhitelistReadRegexp' => $whitelistRegexp, + ) ); + $title = Title::newFromDBkey( $source ); - global $wgGroupPermissions; - $oldPermissions = $wgGroupPermissions; - // Disallow all so we can ensure our regex works - $wgGroupPermissions = array(); - $wgGroupPermissions['*']['read'] = false; - - global $wgWhitelistRead; - $oldWhitelist = $wgWhitelistRead; - // Undo any LocalSettings explicite whitelists so they won't cause a - // failing test to succeed. Set it to some random non sense just - // to make sure we properly test Title::checkReadPermissions() - $wgWhitelistRead = array( 'some random non sense title' ); - - global $wgWhitelistReadRegexp; - $oldWhitelistRegexp = $wgWhitelistReadRegexp; - $wgWhitelistReadRegexp = $whitelistRegexp; - - // Just use $wgUser which in test is a user object for '127.0.0.1' - global $wgUser; - // Invalidate user rights cache to take in account $wgGroupPermissions - // change above. - $wgUser->clearInstanceCache(); - $errors = $title->userCan( $action, $wgUser ); - - // Restore globals - $wgGroupPermissions = $oldPermissions; - $wgWhitelistRead = $oldWhitelist; - $wgWhitelistReadRegexp = $oldWhitelistRegexp; + // New anonymous user with no rights + $user = new User; + $user->mRights = array(); + $errors = $title->userCan( $action, $user ); if ( is_bool( $expected ) ) { # Forge the assertion message depending on the assertion expectation @@ -428,14 +412,14 @@ class TitleTest extends MediaWikiTestCase { public function testGetPageViewLanguage( $expected, $titleText, $contLang, $lang, $variant, $msg = '' ) { - global $wgLanguageCode, $wgContLang, $wgLang, $wgDefaultLanguageVariant, $wgAllowUserJs; - // Setup environnement for this test - $wgLanguageCode = $contLang; - $wgContLang = Language::factory( $contLang ); - $wgLang = Language::factory( $lang ); - $wgDefaultLanguageVariant = $variant; - $wgAllowUserJs = true; + $this->setMwGlobals( array( + 'wgLanguageCode' => $contLang, + 'wgContLang' => Language::factory( $contLang ), + 'wgLang' => Language::factory( $lang ), + 'wgDefaultLanguageVariant' => $variant, + 'wgAllowUserJs' => true, + ) ); $title = Title::newFromText( $titleText ); $this->assertInstanceOf( 'Title', $title, diff --git a/tests/phpunit/includes/UserTest.php b/tests/phpunit/includes/UserTest.php index af95a721..b74a7ead 100644 --- a/tests/phpunit/includes/UserTest.php +++ b/tests/phpunit/includes/UserTest.php @@ -214,8 +214,10 @@ class UserTest extends MediaWikiTestCase { */ public function testEditCount() { $user = User::newFromName( 'UnitTestUser' ); - $user->loadDefaults(); - $user->addToDatabase(); + + if ( !$user->getId() ) { + $user->addToDatabase(); + } // let the user have a few (3) edits $page = WikiPage::factory( Title::newFromText( 'Help:UserTest_EditCount' ) ); @@ -248,14 +250,17 @@ class UserTest extends MediaWikiTestCase { */ public function testOptions() { $user = User::newFromName( 'UnitTestUser' ); - $user->addToDatabase(); - $user->setOption( 'someoption', 'test' ); + if ( !$user->getId() ) { + $user->addToDatabase(); + } + + $user->setOption( 'userjs-someoption', 'test' ); $user->setOption( 'cols', 200 ); $user->saveSettings(); $user = User::newFromName( 'UnitTestUser' ); - $this->assertEquals( 'test', $user->getOption( 'someoption' ) ); + $this->assertEquals( 'test', $user->getOption( 'userjs-someoption' ) ); $this->assertEquals( 200, $user->getOption( 'cols' ) ); } @@ -266,9 +271,9 @@ class UserTest extends MediaWikiTestCase { */ public function testAnonOptions() { global $wgDefaultUserOptions; - $this->user->setOption( 'someoption', 'test' ); + $this->user->setOption( 'userjs-someoption', 'test' ); $this->assertEquals( $wgDefaultUserOptions['cols'], $this->user->getOption( 'cols' ) ); - $this->assertEquals( 'test', $this->user->getOption( 'someoption' ) ); + $this->assertEquals( 'test', $this->user->getOption( 'userjs-someoption' ) ); } /** @@ -276,12 +281,10 @@ class UserTest extends MediaWikiTestCase { * @covers User::getPasswordExpired() */ public function testPasswordExpire() { - global $wgPasswordExpireGrace; - $wgTemp = $wgPasswordExpireGrace; - $wgPasswordExpireGrace = 3600 * 24 * 7; // 7 days + $this->setMwGlobals( 'wgPasswordExpireGrace', 3600 * 24 * 7 ); // 7 days $user = User::newFromName( 'UnitTestUser' ); - $user->loadDefaults(); + $user->loadDefaults( 'UnitTestUser' ); $this->assertEquals( false, $user->getPasswordExpired() ); $ts = time() - ( 3600 * 24 * 1 ); // 1 day ago @@ -291,8 +294,6 @@ class UserTest extends MediaWikiTestCase { $ts = time() - ( 3600 * 24 * 10 ); // 10 days ago $user->expirePassword( $ts ); $this->assertEquals( 'hard', $user->getPasswordExpired() ); - - $wgPasswordExpireGrace = $wgTemp; } /** @@ -343,8 +344,8 @@ class UserTest extends MediaWikiTestCase { public function testGetCanonicalName( $name, $expectedArray, $msg ) { foreach ( $expectedArray as $validate => $expected ) { $this->assertEquals( - User::getCanonicalName( $name, $validate === 'false' ? false : $validate ), $expected, + User::getCanonicalName( $name, $validate === 'false' ? false : $validate ), $msg . ' (' . $validate . ')' ); } @@ -352,8 +353,8 @@ class UserTest extends MediaWikiTestCase { public static function provideGetCanonicalName() { return array( - array( ' trailing space ', array( 'creatable' => 'Trailing space' ), 'Trailing spaces' ), - // @todo FIXME: Maybe the createable name should be 'Talk:Username' or false to reject? + array( ' Trailing space ', array( 'creatable' => 'Trailing space' ), 'Trailing spaces' ), + // @todo FIXME: Maybe the creatable name should be 'Talk:Username' or false to reject? array( 'Talk:Username', array( 'creatable' => 'Username', 'usable' => 'Username', 'valid' => 'Username', 'false' => 'Talk:Username' ), 'Namespace prefix' ), array( ' name with # hash', array( 'creatable' => false, 'usable' => false ), 'With hash' ), @@ -366,4 +367,62 @@ class UserTest extends MediaWikiTestCase { 'false' => 'With / slash' ), 'With slash' ), ); } + + /** + * @covers User::equals + */ + public function testEquals() { + $first = User::newFromName( 'EqualUser' ); + $second = User::newFromName( 'EqualUser' ); + + $this->assertTrue( $first->equals( $first ) ); + $this->assertTrue( $first->equals( $second ) ); + $this->assertTrue( $second->equals( $first ) ); + + $third = User::newFromName( '0' ); + $fourth = User::newFromName( '000' ); + + $this->assertFalse( $third->equals( $fourth ) ); + $this->assertFalse( $fourth->equals( $third ) ); + + // Test users loaded from db with id + $user = User::newFromName( 'EqualUnitTestUser' ); + if ( !$user->getId() ) { + $user->addToDatabase(); + } + + $id = $user->getId(); + + $fifth = User::newFromId( $id ); + $sixth = User::newFromName( 'EqualUnitTestUser' ); + $this->assertTrue( $fifth->equals( $sixth ) ); + } + + /** + * @covers User::getId + */ + public function testGetId() { + $user = User::newFromName( 'UTSysop' ); + $this->assertTrue( $user->getId() > 0 ); + + } + + /** + * @covers User::isLoggedIn + * @covers User::isAnon + */ + public function testLoggedIn() { + $user = User::newFromName( 'UTSysop' ); + $this->assertTrue( $user->isLoggedIn() ); + $this->assertFalse( $user->isAnon() ); + + // Non-existent users are perceived as anonymous + $user = User::newFromName( 'UTNonexistent' ); + $this->assertFalse( $user->isLoggedIn() ); + $this->assertTrue( $user->isAnon() ); + + $user = new User; + $this->assertFalse( $user->isLoggedIn() ); + $this->assertTrue( $user->isAnon() ); + } } diff --git a/tests/phpunit/includes/XmlSelectTest.php b/tests/phpunit/includes/XmlSelectTest.php index 9f154bb7..0e03add4 100644 --- a/tests/phpunit/includes/XmlSelectTest.php +++ b/tests/phpunit/includes/XmlSelectTest.php @@ -166,7 +166,7 @@ class XmlSelectTest extends MediaWikiTestCase { 'razor' ); - # inexistant keys should give us 'null' + # inexistent keys should give us 'null' $this->assertEquals( $this->select->getAttribute( 'I DO NOT EXIT' ), null diff --git a/tests/phpunit/includes/XmlTest.php b/tests/phpunit/includes/XmlTest.php index e6558819..382e3d89 100644 --- a/tests/phpunit/includes/XmlTest.php +++ b/tests/phpunit/includes/XmlTest.php @@ -81,7 +81,7 @@ class XmlTest extends MediaWikiTestCase { */ public function testElementInputCanHaveAValueOfZero() { $this->assertEquals( - '<input name="name" value="0" class="mw-ui-input" />', + '<input name="name" value="0" />', Xml::input( 'name', false, 0 ), 'Input with a value of 0 (bug 23797)' ); @@ -152,7 +152,7 @@ class XmlTest extends MediaWikiTestCase { $this->assertEquals( '<label for="year">From year (and earlier):</label> ' . - '<input id="year" maxlength="4" size="7" type="number" value="2011" name="year" class="mw-ui-input" /> ' . + '<input id="year" maxlength="4" size="7" type="number" value="2011" name="year" /> ' . '<label for="month">From month (and earlier):</label> ' . '<select id="month" name="month" class="mw-month-selector">' . '<option value="-1">all</option>' . "\n" . @@ -173,7 +173,7 @@ class XmlTest extends MediaWikiTestCase { ); $this->assertEquals( '<label for="year">From year (and earlier):</label> ' . - '<input id="year" maxlength="4" size="7" type="number" value="2011" name="year" class="mw-ui-input" /> ' . + '<input id="year" maxlength="4" size="7" type="number" value="2011" name="year" /> ' . '<label for="month">From month (and earlier):</label> ' . '<select id="month" name="month" class="mw-month-selector">' . '<option value="-1">all</option>' . "\n" . @@ -207,7 +207,7 @@ class XmlTest extends MediaWikiTestCase { $this->assertEquals( '<label for="year">From year (and earlier):</label> ' . - '<input id="year" maxlength="4" size="7" type="number" name="year" class="mw-ui-input" /> ' . + '<input id="year" maxlength="4" size="7" type="number" name="year" /> ' . '<label for="month">From month (and earlier):</label> ' . '<select id="month" name="month" class="mw-month-selector">' . '<option value="-1">all</option>' . "\n" . @@ -233,7 +233,7 @@ class XmlTest extends MediaWikiTestCase { */ public function testTextareaNoContent() { $this->assertEquals( - '<textarea name="name" id="name" cols="40" rows="5" class="mw-ui-input"></textarea>', + '<textarea name="name" id="name" cols="40" rows="5"></textarea>', Xml::textarea( 'name', '' ), 'textarea() with not content' ); @@ -244,7 +244,7 @@ class XmlTest extends MediaWikiTestCase { */ public function testTextareaAttribs() { $this->assertEquals( - '<textarea name="name" id="name" cols="20" rows="10" class="mw-ui-input"><txt></textarea>', + '<textarea name="name" id="name" cols="20" rows="10"><txt></textarea>', Xml::textarea( 'name', '<txt>', 20, 10 ), 'textarea() with custom attribs' ); diff --git a/tests/phpunit/includes/actions/ActionTest.php b/tests/phpunit/includes/actions/ActionTest.php index cc6fb11a..3babb97f 100644 --- a/tests/phpunit/includes/actions/ActionTest.php +++ b/tests/phpunit/includes/actions/ActionTest.php @@ -3,7 +3,6 @@ /** * @covers Action * - * @licence GNU GPL v2+ * @author Thiemo Mättig * * @group Action @@ -20,7 +19,7 @@ class ActionTest extends MediaWikiTestCase { 'disabled' => false, 'view' => true, 'edit' => true, - 'revisiondelete' => true, + 'revisiondelete' => 'SpecialPageAction', 'dummy' => true, 'string' => 'NamedDummyAction', 'declared' => 'NonExistingClassName', @@ -117,6 +116,15 @@ class ActionTest extends MediaWikiTestCase { $this->assertEquals( 'revisiondelete', $actionName ); } + public function testGetActionName_whenCanNotUseWikiPage_defaultsToView() { + $request = new FauxRequest( array( 'action' => 'edit' ) ); + $context = new DerivativeContext( RequestContext::getMain() ); + $context->setRequest( $request ); + $actionName = Action::getActionName( $context ); + + $this->assertEquals( 'view', $actionName ); + } + /** * @dataProvider actionProvider * @param string $requestedAction diff --git a/tests/phpunit/includes/api/ApiContinuationManagerTest.php b/tests/phpunit/includes/api/ApiContinuationManagerTest.php new file mode 100644 index 00000000..2edf0c6f --- /dev/null +++ b/tests/phpunit/includes/api/ApiContinuationManagerTest.php @@ -0,0 +1,195 @@ +<?php + +/** + * @covers ApiContinuationManager + * @group API + */ +class ApiContinuationManagerTest extends MediaWikiTestCase { + + private static function getManager( $continue, $allModules, $generatedModules ) { + $context = new DerivativeContext( RequestContext::getMain() ); + $context->setRequest( new FauxRequest( array( 'continue' => $continue ) ) ); + $main = new ApiMain( $context ); + return new ApiContinuationManager( $main, $allModules, $generatedModules ); + } + + public function testContinuation() { + $allModules = array( + new MockApiQueryBase( 'mock1' ), + new MockApiQueryBase( 'mock2' ), + new MockApiQueryBase( 'mocklist' ), + ); + $generator = new MockApiQueryBase( 'generator' ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( 'ApiMain', $manager->getSource() ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $manager->addContinueParam( $allModules[2], 'mlcontinue', 2 ); + $manager->addGeneratorContinueParam( $generator, 'gcontinue', 3 ); + $this->assertSame( array( array( + 'mlcontinue' => 2, + 'm1continue' => '1|2', + 'continue' => '||mock2', + ), false ), $manager->getContinuation() ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + 'mocklist' => array( 'mlcontinue' => 2 ), + 'generator' => array( 'gcontinue' => 3 ), + ), $manager->getRawContinuation() ); + + $result = new ApiResult( 0 ); + $manager->setContinuationIntoResult( $result ); + $this->assertSame( array( + 'mlcontinue' => 2, + 'm1continue' => '1|2', + 'continue' => '||mock2', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( null, $result->getResultData( 'batchcomplete' ) ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $manager->addGeneratorContinueParam( $generator, 'gcontinue', array( 3, 4 ) ); + $this->assertSame( array( array( + 'm1continue' => '1|2', + 'continue' => '||mock2|mocklist', + ), false ), $manager->getContinuation() ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + 'generator' => array( 'gcontinue' => '3|4' ), + ), $manager->getRawContinuation() ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addContinueParam( $allModules[2], 'mlcontinue', 2 ); + $manager->addGeneratorContinueParam( $generator, 'gcontinue', 3 ); + $this->assertSame( array( array( + 'mlcontinue' => 2, + 'gcontinue' => 3, + 'continue' => 'gcontinue||', + ), true ), $manager->getContinuation() ); + $this->assertSame( array( + 'mocklist' => array( 'mlcontinue' => 2 ), + 'generator' => array( 'gcontinue' => 3 ), + ), $manager->getRawContinuation() ); + + $result = new ApiResult( 0 ); + $manager->setContinuationIntoResult( $result ); + $this->assertSame( array( + 'mlcontinue' => 2, + 'gcontinue' => 3, + 'continue' => 'gcontinue||', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( true, $result->getResultData( 'batchcomplete' ) ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addGeneratorContinueParam( $generator, 'gcontinue', 3 ); + $this->assertSame( array( array( + 'gcontinue' => 3, + 'continue' => 'gcontinue||mocklist', + ), true ), $manager->getContinuation() ); + $this->assertSame( array( + 'generator' => array( 'gcontinue' => 3 ), + ), $manager->getRawContinuation() ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $manager->addContinueParam( $allModules[2], 'mlcontinue', 2 ); + $this->assertSame( array( array( + 'mlcontinue' => 2, + 'm1continue' => '1|2', + 'continue' => '||mock2', + ), false ), $manager->getContinuation() ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + 'mocklist' => array( 'mlcontinue' => 2 ), + ), $manager->getRawContinuation() ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $this->assertSame( array( array( + 'm1continue' => '1|2', + 'continue' => '||mock2|mocklist', + ), false ), $manager->getContinuation() ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + ), $manager->getRawContinuation() ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $manager->addContinueParam( $allModules[2], 'mlcontinue', 2 ); + $this->assertSame( array( array( + 'mlcontinue' => 2, + 'continue' => '-||mock1|mock2', + ), true ), $manager->getContinuation() ); + $this->assertSame( array( + 'mocklist' => array( 'mlcontinue' => 2 ), + ), $manager->getRawContinuation() ); + + $manager = self::getManager( '', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( $allModules, $manager->getRunModules() ); + $this->assertSame( array( array(), true ), $manager->getContinuation() ); + $this->assertSame( array(), $manager->getRawContinuation() ); + + $manager = self::getManager( '||mock2', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( false, $manager->isGeneratorDone() ); + $this->assertSame( + array_values( array_diff_key( $allModules, array( 1 => 1 ) ) ), + $manager->getRunModules() + ); + + $manager = self::getManager( '-||', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( true, $manager->isGeneratorDone() ); + $this->assertSame( + array_values( array_diff_key( $allModules, array( 0 => 0, 1 => 1 ) ) ), + $manager->getRunModules() + ); + + try { + self::getManager( 'foo', $allModules, array( 'mock1', 'mock2' ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UsageException $ex ) { + $this->assertSame( + 'Invalid continue param. You should pass the original value returned by the previous query', + $ex->getMessage(), + 'Expected exception' + ); + } + + $manager = self::getManager( '||mock2', array_slice( $allModules, 0, 2 ), array( 'mock1', 'mock2' ) ); + try { + $manager->addContinueParam( $allModules[1], 'm2continue', 1 ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UnexpectedValueException $ex ) { + $this->assertSame( + 'Module \'mock2\' was not supposed to have been executed, but it was executed anyway', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $manager->addContinueParam( $allModules[2], 'mlcontinue', 1 ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UnexpectedValueException $ex ) { + $this->assertSame( + 'Module \'mocklist\' called ApiContinuationManager::addContinueParam but was not passed to ApiContinuationManager::__construct', + $ex->getMessage(), + 'Expected exception' + ); + } + + } + +} diff --git a/tests/phpunit/includes/api/ApiErrorFormatterTest.php b/tests/phpunit/includes/api/ApiErrorFormatterTest.php new file mode 100644 index 00000000..8ebdf60f --- /dev/null +++ b/tests/phpunit/includes/api/ApiErrorFormatterTest.php @@ -0,0 +1,351 @@ +<?php + +/** + * @group API + */ +class ApiErrorFormatterTest extends MediaWikiTestCase { + + /** + * @covers ApiErrorFormatter + * @dataProvider provideErrorFormatter + */ + public function testErrorFormatter( $format, $lang, $useDB, + $expect1, $expect2, $expect3 + ) { + $result = new ApiResult( 8388608 ); + $formatter = new ApiErrorFormatter( $result, Language::factory( $lang ), $format, $useDB ); + + // Add default type + $expect1[ApiResult::META_TYPE] = 'assoc'; + $expect2[ApiResult::META_TYPE] = 'assoc'; + $expect3[ApiResult::META_TYPE] = 'assoc'; + + $formatter->addWarning( 'string', 'mainpage' ); + $formatter->addError( 'err', 'mainpage' ); + $this->assertSame( $expect1, $result->getResultData(), 'Simple test' ); + + $result->reset(); + $formatter->addWarning( 'foo', 'mainpage' ); + $formatter->addWarning( 'foo', 'mainpage' ); + $formatter->addWarning( 'foo', array( 'parentheses', 'foobar' ) ); + $msg1 = wfMessage( 'mainpage' ); + $formatter->addWarning( 'message', $msg1 ); + $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', array( 'overriddenData' => true ) ); + $formatter->addWarning( 'messageWithData', $msg2 ); + $formatter->addError( 'errWithData', $msg2 ); + $this->assertSame( $expect2, $result->getResultData(), 'Complex test' ); + + $result->reset(); + $status = Status::newGood(); + $status->warning( 'mainpage' ); + $status->warning( 'parentheses', 'foobar' ); + $status->warning( $msg1 ); + $status->warning( $msg2 ); + $status->error( 'mainpage' ); + $status->error( 'parentheses', 'foobar' ); + $formatter->addMessagesFromStatus( 'status', $status ); + $this->assertSame( $expect3, $result->getResultData(), 'Status test' ); + + $this->assertSame( + $expect3['errors']['status'], + $formatter->arrayFromStatus( $status, 'error' ), + 'arrayFromStatus test for error' + ); + $this->assertSame( + $expect3['warnings']['status'], + $formatter->arrayFromStatus( $status, 'warning' ), + 'arrayFromStatus test for warning' + ); + } + + public static function provideErrorFormatter() { + $mainpagePlain = wfMessage( 'mainpage' )->useDatabase( false )->plain(); + $parensPlain = wfMessage( 'parentheses', 'foobar' )->useDatabase( false )->plain(); + $mainpageText = wfMessage( 'mainpage' )->inLanguage( 'de' )->text(); + $parensText = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'de' )->text(); + $C = ApiResult::META_CONTENT; + $I = ApiResult::META_INDEXED_TAG_NAME; + + return array( + array( 'wikitext', 'de', true, + array( + 'errors' => array( + 'err' => array( + array( 'code' => 'mainpage', 'text' => $mainpageText, $C => 'text' ), + $I => 'error', + ), + ), + 'warnings' => array( + 'string' => array( + array( 'code' => 'mainpage', 'text' => $mainpageText, $C => 'text' ), + $I => 'warning', + ), + ), + ), + array( + 'errors' => array( + 'errWithData' => array( + array( 'code' => 'overriddenCode', 'text' => $mainpageText, + 'overriddenData' => true, $C => 'text' ), + $I => 'error', + ), + ), + 'warnings' => array( + 'messageWithData' => array( + array( 'code' => 'overriddenCode', 'text' => $mainpageText, + 'overriddenData' => true, $C => 'text' ), + $I => 'warning', + ), + 'message' => array( + array( 'code' => 'mainpage', 'text' => $mainpageText, $C => 'text' ), + $I => 'warning', + ), + 'foo' => array( + array( 'code' => 'mainpage', 'text' => $mainpageText, $C => 'text' ), + array( 'code' => 'parentheses', 'text' => $parensText, $C => 'text' ), + $I => 'warning', + ), + ), + ), + array( + 'errors' => array( + 'status' => array( + array( 'code' => 'mainpage', 'text' => $mainpageText, $C => 'text' ), + array( 'code' => 'parentheses', 'text' => $parensText, $C => 'text' ), + $I => 'error', + ), + ), + 'warnings' => array( + 'status' => array( + array( 'code' => 'mainpage', 'text' => $mainpageText, $C => 'text' ), + array( 'code' => 'parentheses', 'text' => $parensText, $C => 'text' ), + array( 'code' => 'overriddenCode', 'text' => $mainpageText, + 'overriddenData' => true, $C => 'text' ), + $I => 'warning', + ), + ), + ), + ), + array( 'raw', 'fr', true, + array( + 'errors' => array( + 'err' => array( + array( 'code' => 'mainpage', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + $I => 'error', + ), + ), + 'warnings' => array( + 'string' => array( + array( 'code' => 'mainpage', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + $I => 'warning', + ), + ), + ), + array( + 'errors' => array( + 'errWithData' => array( + array( 'code' => 'overriddenCode', 'message' => 'mainpage', 'params' => array( $I => 'param' ), + 'overriddenData' => true ), + $I => 'error', + ), + ), + 'warnings' => array( + 'messageWithData' => array( + array( 'code' => 'overriddenCode', 'message' => 'mainpage', 'params' => array( $I => 'param' ), + 'overriddenData' => true ), + $I => 'warning', + ), + 'message' => array( + array( 'code' => 'mainpage', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + $I => 'warning', + ), + 'foo' => array( + array( 'code' => 'mainpage', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + array( 'code' => 'parentheses', 'message' => 'parentheses', 'params' => array( 'foobar', $I => 'param' ) ), + $I => 'warning', + ), + ), + ), + array( + 'errors' => array( + 'status' => array( + array( 'code' => 'mainpage', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + array( 'code' => 'parentheses', 'message' => 'parentheses', 'params' => array( 'foobar', $I => 'param' ) ), + $I => 'error', + ), + ), + 'warnings' => array( + 'status' => array( + array( 'code' => 'mainpage', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + array( 'code' => 'parentheses', 'message' => 'parentheses', 'params' => array( 'foobar', $I => 'param' ) ), + array( 'code' => 'overriddenCode', 'message' => 'mainpage', 'params' => array( $I => 'param' ), + 'overriddenData' => true ), + $I => 'warning', + ), + ), + ), + ), + array( 'none', 'fr', true, + array( + 'errors' => array( + 'err' => array( + array( 'code' => 'mainpage' ), + $I => 'error', + ), + ), + 'warnings' => array( + 'string' => array( + array( 'code' => 'mainpage' ), + $I => 'warning', + ), + ), + ), + array( + 'errors' => array( + 'errWithData' => array( + array( 'code' => 'overriddenCode', 'overriddenData' => true ), + $I => 'error', + ), + ), + 'warnings' => array( + 'messageWithData' => array( + array( 'code' => 'overriddenCode', 'overriddenData' => true ), + $I => 'warning', + ), + 'message' => array( + array( 'code' => 'mainpage' ), + $I => 'warning', + ), + 'foo' => array( + array( 'code' => 'mainpage' ), + array( 'code' => 'parentheses' ), + $I => 'warning', + ), + ), + ), + array( + 'errors' => array( + 'status' => array( + array( 'code' => 'mainpage' ), + array( 'code' => 'parentheses' ), + $I => 'error', + ), + ), + 'warnings' => array( + 'status' => array( + array( 'code' => 'mainpage' ), + array( 'code' => 'parentheses' ), + array( 'code' => 'overriddenCode', 'overriddenData' => true ), + $I => 'warning', + ), + ), + ), + ), + ); + } + + /** + * @covers ApiErrorFormatter_BackCompat + */ + public function testErrorFormatterBC() { + $mainpagePlain = wfMessage( 'mainpage' )->useDatabase( false )->plain(); + $parensPlain = wfMessage( 'parentheses', 'foobar' )->useDatabase( false )->plain(); + + $result = new ApiResult( 8388608 ); + $formatter = new ApiErrorFormatter_BackCompat( $result ); + + $formatter->addWarning( 'string', 'mainpage' ); + $formatter->addError( 'err', 'mainpage' ); + $this->assertSame( array( + 'error' => array( + 'code' => 'mainpage', + 'info' => $mainpagePlain, + ), + 'warnings' => array( + 'string' => array( + 'warnings' => $mainpagePlain, + ApiResult::META_CONTENT => 'warnings', + ), + ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData(), 'Simple test' ); + + $result->reset(); + $formatter->addWarning( 'foo', 'mainpage' ); + $formatter->addWarning( 'foo', 'mainpage' ); + $formatter->addWarning( 'foo', array( 'parentheses', 'foobar' ) ); + $msg1 = wfMessage( 'mainpage' ); + $formatter->addWarning( 'message', $msg1 ); + $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', array( 'overriddenData' => true ) ); + $formatter->addWarning( 'messageWithData', $msg2 ); + $formatter->addError( 'errWithData', $msg2 ); + $this->assertSame( array( + 'error' => array( + 'code' => 'overriddenCode', + 'info' => $mainpagePlain, + 'overriddenData' => true, + ), + 'warnings' => array( + 'messageWithData' => array( + 'warnings' => $mainpagePlain, + ApiResult::META_CONTENT => 'warnings', + ), + 'message' => array( + 'warnings' => $mainpagePlain, + ApiResult::META_CONTENT => 'warnings', + ), + 'foo' => array( + 'warnings' => "$mainpagePlain\n$parensPlain", + ApiResult::META_CONTENT => 'warnings', + ), + ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData(), 'Complex test' ); + + $result->reset(); + $status = Status::newGood(); + $status->warning( 'mainpage' ); + $status->warning( 'parentheses', 'foobar' ); + $status->warning( $msg1 ); + $status->warning( $msg2 ); + $status->error( 'mainpage' ); + $status->error( 'parentheses', 'foobar' ); + $formatter->addMessagesFromStatus( 'status', $status ); + $this->assertSame( array( + 'error' => array( + 'code' => 'parentheses', + 'info' => $parensPlain, + ), + 'warnings' => array( + 'status' => array( + 'warnings' => "$mainpagePlain\n$parensPlain", + ApiResult::META_CONTENT => 'warnings', + ), + ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData(), 'Status test' ); + + $I = ApiResult::META_INDEXED_TAG_NAME; + $this->assertSame( + array( + array( 'type' => 'error', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + array( 'type' => 'error', 'message' => 'parentheses', 'params' => array( 'foobar', $I => 'param' ) ), + $I => 'error', + ), + $formatter->arrayFromStatus( $status, 'error' ), + 'arrayFromStatus test for error' + ); + $this->assertSame( + array( + array( 'type' => 'warning', 'message' => 'mainpage', 'params' => array( $I => 'param' ) ), + array( 'type' => 'warning', 'message' => 'parentheses', 'params' => array( 'foobar', $I => 'param' ) ), + array( 'message' => 'mainpage', 'params' => array( $I => 'param' ), 'type' => 'warning' ), + array( 'message' => 'mainpage', 'params' => array( $I => 'param' ), 'type' => 'warning' ), + $I => 'warning', + ), + $formatter->arrayFromStatus( $status, 'warning' ), + 'arrayFromStatus test for warning' + ); + } + +} diff --git a/tests/phpunit/includes/api/ApiLoginTest.php b/tests/phpunit/includes/api/ApiLoginTest.php index 67a75f36..88a99e9b 100644 --- a/tests/phpunit/includes/api/ApiLoginTest.php +++ b/tests/phpunit/includes/api/ApiLoginTest.php @@ -123,7 +123,8 @@ class ApiLoginTest extends ApiTestCase { "lgname" => $user->username, "lgpassword" => $user->password ) - ) + ), + __METHOD__ ); $req->execute(); diff --git a/tests/phpunit/includes/api/ApiMainTest.php b/tests/phpunit/includes/api/ApiMainTest.php index 780cf9ed..e8ef1804 100644 --- a/tests/phpunit/includes/api/ApiMainTest.php +++ b/tests/phpunit/includes/api/ApiMainTest.php @@ -2,7 +2,6 @@ /** * @group API - * @group Database * @group medium * * @covers ApiMain @@ -10,41 +9,25 @@ class ApiMainTest extends ApiTestCase { /** - * Test that the API will accept a FauxRequest and execute. The help action - * (default) throws a UsageException. Just validate we're getting proper XML - * - * @expectedException UsageException + * Test that the API will accept a FauxRequest and execute. */ public function testApi() { $api = new ApiMain( - new FauxRequest( array( 'action' => 'help', 'format' => 'xml' ) ) + new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) ) ); $api->execute(); - $api->getPrinter()->setBufferResult( true ); - $api->printResult( false ); - $resp = $api->getPrinter()->getBuffer(); - - libxml_use_internal_errors( true ); - $sxe = simplexml_load_string( $resp ); - $this->assertNotInternalType( "bool", $sxe ); - $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) ); + $data = $api->getResult()->getResultData(); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'query', $data ); } public static function provideAssert() { - $anon = new User(); - $bot = new User(); - $bot->setName( 'Bot' ); - $bot->addToDatabase(); - $bot->addGroup( 'bot' ); - $user = new User(); - $user->setName( 'User' ); - $user->addToDatabase(); return array( - array( $anon, 'user', 'assertuserfailed' ), - array( $user, 'user', false ), - array( $user, 'bot', 'assertbotfailed' ), - array( $bot, 'user', false ), - array( $bot, 'bot', false ), + array( false, array(), 'user', 'assertuserfailed' ), + array( true, array(), 'user', false ), + array( true, array(), 'bot', 'assertbotfailed' ), + array( true, array( 'bot' ), 'user', false ), + array( true, array( 'bot' ), 'bot', false ), ); } @@ -53,11 +36,17 @@ class ApiMainTest extends ApiTestCase { * * @covers ApiMain::checkAsserts * @dataProvider provideAssert - * @param User $user + * @param bool $registered + * @param array $rights * @param string $assert * @param string|bool $error False if no error expected */ - public function testAssert( $user, $assert, $error ) { + public function testAssert( $registered, $rights, $assert, $error ) { + $user = new User(); + if ( $registered ) { + $user->setId( 1 ); + } + $user->mRights = $rights; try { $this->doApiRequest( array( 'action' => 'query', @@ -69,4 +58,25 @@ class ApiMainTest extends ApiTestCase { } } + /** + * Test if all classes in the main module manager exists + */ + public function testClassNamesInModuleManager() { + global $wgAutoloadLocalClasses, $wgAutoloadClasses; + + // wgAutoloadLocalClasses has precedence, just like in includes/AutoLoader.php + $classes = $wgAutoloadLocalClasses + $wgAutoloadClasses; + + $api = new ApiMain( + new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) ) + ); + $modules = $api->getModuleManager()->getNamesWithClasses(); + foreach( $modules as $name => $class ) { + $this->assertArrayHasKey( + $class, + $classes, + 'Class ' . $class . ' for api module ' . $name . ' not in autoloader (with exact case)' + ); + } + } } diff --git a/tests/phpunit/includes/api/ApiMessageTest.php b/tests/phpunit/includes/api/ApiMessageTest.php new file mode 100644 index 00000000..6c3ce60d --- /dev/null +++ b/tests/phpunit/includes/api/ApiMessageTest.php @@ -0,0 +1,103 @@ +<?php + +/** + * @group API + */ +class ApiMessageTest extends MediaWikiTestCase { + + private function compareMessages( $msg, $msg2 ) { + $this->assertSame( $msg->getKey(), $msg2->getKey(), 'getKey' ); + $this->assertSame( $msg->getKeysToTry(), $msg2->getKeysToTry(), 'getKeysToTry' ); + $this->assertSame( $msg->getParams(), $msg2->getParams(), 'getParams' ); + $this->assertSame( $msg->getFormat(), $msg2->getFormat(), 'getFormat' ); + $this->assertSame( $msg->getLanguage(), $msg2->getLanguage(), 'getLanguage' ); + + $msg = TestingAccessWrapper::newFromObject( $msg ); + $msg2 = TestingAccessWrapper::newFromObject( $msg2 ); + foreach ( array( 'interface', 'useDatabase', 'title' ) as $key ) { + $this->assertSame( $msg->$key, $msg2->$key, $key ); + } + } + + /** + * @covers ApiMessage + */ + public function testApiMessage() { + $msg = new Message( array( 'foo', 'bar' ), array( 'baz' ) ); + $msg->inLanguage( 'de' )->title( Title::newMainPage() ); + $msg2 = new ApiMessage( $msg, 'code', array( 'data' ) ); + $this->compareMessages( $msg, $msg2 ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + + $msg = new Message( array( 'foo', 'bar' ), array( 'baz' ) ); + $msg2 = new ApiMessage( array( array( 'foo', 'bar' ), 'baz' ), 'code', array( 'data' ) ); + $this->compareMessages( $msg, $msg2 ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + + $msg = new Message( 'foo' ); + $msg2 = new ApiMessage( 'foo' ); + $this->compareMessages( $msg, $msg2 ); + $this->assertEquals( 'foo', $msg2->getApiCode() ); + $this->assertEquals( array(), $msg2->getApiData() ); + + $msg2->setApiCode( 'code', array( 'data' ) ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + $msg2->setApiCode( null ); + $this->assertEquals( 'foo', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + $msg2->setApiData( array( 'data2' ) ); + $this->assertEquals( array( 'data2' ), $msg2->getApiData() ); + } + + /** + * @covers ApiRawMessage + */ + public function testApiRawMessage() { + $msg = new RawMessage( 'foo', array( 'baz' ) ); + $msg->inLanguage( 'de' )->title( Title::newMainPage() ); + $msg2 = new ApiRawMessage( $msg, 'code', array( 'data' ) ); + $this->compareMessages( $msg, $msg2 ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + + $msg = new RawMessage( 'foo', array( 'baz' ) ); + $msg2 = new ApiRawMessage( array( 'foo', 'baz' ), 'code', array( 'data' ) ); + $this->compareMessages( $msg, $msg2 ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + + $msg = new RawMessage( 'foo' ); + $msg2 = new ApiRawMessage( 'foo', 'code', array( 'data' ) ); + $this->compareMessages( $msg, $msg2 ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + + $msg2->setApiCode( 'code', array( 'data' ) ); + $this->assertEquals( 'code', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + $msg2->setApiCode( null ); + $this->assertEquals( 'foo', $msg2->getApiCode() ); + $this->assertEquals( array( 'data' ), $msg2->getApiData() ); + $msg2->setApiData( array( 'data2' ) ); + $this->assertEquals( array( 'data2' ), $msg2->getApiData() ); + } + + /** + * @covers ApiMessage::create + */ + public function testApiMessageCreate() { + $this->assertInstanceOf( 'ApiMessage', ApiMessage::create( new Message( 'mainpage' ) ) ); + $this->assertInstanceOf( 'ApiRawMessage', ApiMessage::create( new RawMessage( 'mainpage' ) ) ); + $this->assertInstanceOf( 'ApiMessage', ApiMessage::create( 'mainpage' ) ); + + $msg = new ApiMessage( 'mainpage' ); + $this->assertSame( $msg, ApiMessage::create( $msg ) ); + + $msg = new ApiRawMessage( 'mainpage' ); + $this->assertSame( $msg, ApiMessage::create( $msg ) ); + } + +} diff --git a/tests/phpunit/includes/api/ApiOptionsTest.php b/tests/phpunit/includes/api/ApiOptionsTest.php index 5f955bbc..51154ae3 100644 --- a/tests/phpunit/includes/api/ApiOptionsTest.php +++ b/tests/phpunit/includes/api/ApiOptionsTest.php @@ -17,8 +17,6 @@ class ApiOptionsTest extends MediaWikiLangTestCase { /** @var DerivativeContext */ private $mContext; - private $mOldGetPreferencesHooks; - private static $Success = array( 'options' => 'success' ); protected function setUp() { @@ -50,21 +48,11 @@ class ApiOptionsTest extends MediaWikiLangTestCase { $this->mTested = new ApiOptions( $main, 'options' ); - global $wgHooks; - if ( !isset( $wgHooks['GetPreferences'] ) ) { - $wgHooks['GetPreferences'] = array(); - } - $this->mOldGetPreferencesHooks = $wgHooks['GetPreferences']; - $wgHooks['GetPreferences'][] = array( $this, 'hookGetPreferences' ); - } - - protected function tearDown() { - global $wgHooks; - - $wgHooks['GetPreferences'] = $this->mOldGetPreferencesHooks; - $this->mOldGetPreferencesHooks = false; - - parent::tearDown(); + $this->mergeMwGlobalArrayValue( 'wgHooks', array( + 'GetPreferences' => array( + array( $this, 'hookGetPreferences' ) + ) + ) ); } public function hookGetPreferences( $user, &$preferences ) { @@ -150,7 +138,7 @@ class ApiOptionsTest extends MediaWikiLangTestCase { $this->mContext->setRequest( new FauxRequest( $request, true, $this->mSession ) ); $this->mTested->execute(); - return $this->mTested->getResult()->getData(); + return $this->mTested->getResult()->getResultData( null, array( 'Strip' => 'all' ) ); } /** @@ -408,7 +396,7 @@ class ApiOptionsTest extends MediaWikiLangTestCase { 'options' => 'success', 'warnings' => array( 'options' => array( - '*' => "Validation error for 'special': cannot be set by this module" + 'warnings' => "Validation error for 'special': cannot be set by this module" ) ) ), $response ); @@ -431,7 +419,7 @@ class ApiOptionsTest extends MediaWikiLangTestCase { 'options' => 'success', 'warnings' => array( 'options' => array( - '*' => "Validation error for 'unknownOption': not a valid preference" + 'warnings' => "Validation error for 'unknownOption': not a valid preference" ) ) ), $response ); diff --git a/tests/phpunit/includes/api/ApiResultTest.php b/tests/phpunit/includes/api/ApiResultTest.php new file mode 100644 index 00000000..f0d84552 --- /dev/null +++ b/tests/phpunit/includes/api/ApiResultTest.php @@ -0,0 +1,1563 @@ +<?php + +/** + * @covers ApiResult + * @group API + */ +class ApiResultTest extends MediaWikiTestCase { + + /** + * @covers ApiResult + */ + public function testStaticDataMethods() { + $arr = array(); + + ApiResult::setValue( $arr, 'setValue', '1' ); + + ApiResult::setValue( $arr, null, 'unnamed 1' ); + ApiResult::setValue( $arr, null, 'unnamed 2' ); + + ApiResult::setValue( $arr, 'deleteValue', '2' ); + ApiResult::unsetValue( $arr, 'deleteValue' ); + + ApiResult::setContentValue( $arr, 'setContentValue', '3' ); + + $this->assertSame( array( + 'setValue' => '1', + 'unnamed 1', + 'unnamed 2', + ApiResult::META_CONTENT => 'setContentValue', + 'setContentValue' => '3', + ), $arr ); + + try { + ApiResult::setValue( $arr, 'setValue', '99' ); + $this->fail( 'Expected exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( + 'Attempting to add element setValue=99, existing value is 1', + $ex->getMessage(), + 'Expected exception' + ); + } + + try { + ApiResult::setContentValue( $arr, 'setContentValue2', '99' ); + $this->fail( 'Expected exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( + 'Attempting to set content element as setContentValue2 when setContentValue ' . + 'is already set as the content element', + $ex->getMessage(), + 'Expected exception' + ); + } + + ApiResult::setValue( $arr, 'setValue', '99', ApiResult::OVERRIDE ); + $this->assertSame( '99', $arr['setValue'] ); + + ApiResult::setContentValue( $arr, 'setContentValue2', '99', ApiResult::OVERRIDE ); + $this->assertSame( 'setContentValue2', $arr[ApiResult::META_CONTENT] ); + + $arr = array( 'foo' => 1, 'bar' => 1 ); + ApiResult::setValue( $arr, 'top', '2', ApiResult::ADD_ON_TOP ); + ApiResult::setValue( $arr, null, '2', ApiResult::ADD_ON_TOP ); + ApiResult::setValue( $arr, 'bottom', '2' ); + ApiResult::setValue( $arr, 'foo', '2', ApiResult::OVERRIDE ); + ApiResult::setValue( $arr, 'bar', '2', ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP ); + $this->assertSame( array( 0, 'top', 'foo', 'bar', 'bottom' ), array_keys( $arr ) ); + + $arr = array(); + ApiResult::setValue( $arr, 'sub', array( 'foo' => 1 ) ); + ApiResult::setValue( $arr, 'sub', array( 'bar' => 1 ) ); + $this->assertSame( array( 'sub' => array( 'foo' => 1, 'bar' => 1 ) ), $arr ); + + try { + ApiResult::setValue( $arr, 'sub', array( 'foo' => 2, 'baz' => 2 ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( + 'Conflicting keys (foo) when attempting to merge element sub', + $ex->getMessage(), + 'Expected exception' + ); + } + + $arr = array(); + $title = Title::newFromText( "MediaWiki:Foobar" ); + $obj = new stdClass; + $obj->foo = 1; + $obj->bar = 2; + ApiResult::setValue( $arr, 'title', $title ); + ApiResult::setValue( $arr, 'obj', $obj ); + $this->assertSame( array( + 'title' => (string)$title, + 'obj' => array( 'foo' => 1, 'bar' => 2, ApiResult::META_TYPE => 'assoc' ), + ), $arr ); + + $fh = tmpfile(); + try { + ApiResult::setValue( $arr, 'file', $fh ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + ApiResult::setValue( $arr, null, $fh ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $obj->file = $fh; + ApiResult::setValue( $arr, 'sub', $obj ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $obj->file = $fh; + ApiResult::setValue( $arr, null, $obj ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + fclose( $fh ); + + try { + ApiResult::setValue( $arr, 'inf', INF ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + ApiResult::setValue( $arr, null, INF ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + ApiResult::setValue( $arr, 'nan', NAN ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + ApiResult::setValue( $arr, null, NAN ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + + $arr = array(); + $result2 = new ApiResult( 8388608 ); + $result2->addValue( null, 'foo', 'bar' ); + ApiResult::setValue( $arr, 'baz', $result2 ); + $this->assertSame( array( + 'baz' => array( + ApiResult::META_TYPE => 'assoc', + 'foo' => 'bar', + ) + ), $arr ); + + $arr = array(); + ApiResult::setValue( $arr, 'foo', "foo\x80bar" ); + ApiResult::setValue( $arr, 'bar', "a\xcc\x81" ); + ApiResult::setValue( $arr, 'baz', 74 ); + ApiResult::setValue( $arr, null, "foo\x80bar" ); + ApiResult::setValue( $arr, null, "a\xcc\x81" ); + $this->assertSame( array( + 'foo' => "foo\xef\xbf\xbdbar", + 'bar' => "\xc3\xa1", + 'baz' => 74, + 0 => "foo\xef\xbf\xbdbar", + 1 => "\xc3\xa1", + ), $arr ); + } + + /** + * @covers ApiResult + */ + public function testInstanceDataMethods() { + $result = new ApiResult( 8388608 ); + + $result->addValue( null, 'setValue', '1' ); + + $result->addValue( null, null, 'unnamed 1' ); + $result->addValue( null, null, 'unnamed 2' ); + + $result->addValue( null, 'deleteValue', '2' ); + $result->removeValue( null, 'deleteValue' ); + + $result->addValue( array( 'a', 'b' ), 'deleteValue', '3' ); + $result->removeValue( array( 'a', 'b', 'deleteValue' ), null, '3' ); + + $result->addContentValue( null, 'setContentValue', '3' ); + + $this->assertSame( array( + 'setValue' => '1', + 'unnamed 1', + 'unnamed 2', + 'a' => array( 'b' => array() ), + 'setContentValue' => '3', + ApiResult::META_TYPE => 'assoc', + ApiResult::META_CONTENT => 'setContentValue', + ), $result->getResultData() ); + $this->assertSame( 20, $result->getSize() ); + + try { + $result->addValue( null, 'setValue', '99' ); + $this->fail( 'Expected exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( + 'Attempting to add element setValue=99, existing value is 1', + $ex->getMessage(), + 'Expected exception' + ); + } + + try { + $result->addContentValue( null, 'setContentValue2', '99' ); + $this->fail( 'Expected exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( + 'Attempting to set content element as setContentValue2 when setContentValue ' . + 'is already set as the content element', + $ex->getMessage(), + 'Expected exception' + ); + } + + $result->addValue( null, 'setValue', '99', ApiResult::OVERRIDE ); + $this->assertSame( '99', $result->getResultData( array( 'setValue' ) ) ); + + $result->addContentValue( null, 'setContentValue2', '99', ApiResult::OVERRIDE ); + $this->assertSame( 'setContentValue2', + $result->getResultData( array( ApiResult::META_CONTENT ) ) ); + + $result->reset(); + $this->assertSame( array( + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + $this->assertSame( 0, $result->getSize() ); + + $result->addValue( null, 'foo', 1 ); + $result->addValue( null, 'bar', 1 ); + $result->addValue( null, 'top', '2', ApiResult::ADD_ON_TOP ); + $result->addValue( null, null, '2', ApiResult::ADD_ON_TOP ); + $result->addValue( null, 'bottom', '2' ); + $result->addValue( null, 'foo', '2', ApiResult::OVERRIDE ); + $result->addValue( null, 'bar', '2', ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP ); + $this->assertSame( array( 0, 'top', 'foo', 'bar', 'bottom', ApiResult::META_TYPE ), + array_keys( $result->getResultData() ) ); + + $result->reset(); + $result->addValue( null, 'foo', array( 'bar' => 1 ) ); + $result->addValue( array( 'foo', 'top' ), 'x', 2, ApiResult::ADD_ON_TOP ); + $result->addValue( array( 'foo', 'bottom' ), 'x', 2 ); + $this->assertSame( array( 'top', 'bar', 'bottom' ), + array_keys( $result->getResultData( array( 'foo' ) ) ) ); + + $result->reset(); + $result->addValue( null, 'sub', array( 'foo' => 1 ) ); + $result->addValue( null, 'sub', array( 'bar' => 1 ) ); + $this->assertSame( array( + 'sub' => array( 'foo' => 1, 'bar' => 1 ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + + try { + $result->addValue( null, 'sub', array( 'foo' => 2, 'baz' => 2 ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( + 'Conflicting keys (foo) when attempting to merge element sub', + $ex->getMessage(), + 'Expected exception' + ); + } + + $result->reset(); + $title = Title::newFromText( "MediaWiki:Foobar" ); + $obj = new stdClass; + $obj->foo = 1; + $obj->bar = 2; + $result->addValue( null, 'title', $title ); + $result->addValue( null, 'obj', $obj ); + $this->assertSame( array( + 'title' => (string)$title, + 'obj' => array( 'foo' => 1, 'bar' => 2, ApiResult::META_TYPE => 'assoc' ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + + $fh = tmpfile(); + try { + $result->addValue( null, 'file', $fh ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $result->addValue( null, null, $fh ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $obj->file = $fh; + $result->addValue( null, 'sub', $obj ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $obj->file = $fh; + $result->addValue( null, null, $obj ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add resource(stream) to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + fclose( $fh ); + + try { + $result->addValue( null, 'inf', INF ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $result->addValue( null, null, INF ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $result->addValue( null, 'nan', NAN ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $result->addValue( null, null, NAN ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + + $result->reset(); + $result->addParsedLimit( 'foo', 12 ); + $this->assertSame( array( + 'limits' => array( 'foo' => 12 ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + $result->addParsedLimit( 'foo', 13 ); + $this->assertSame( array( + 'limits' => array( 'foo' => 13 ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + $this->assertSame( null, $result->getResultData( array( 'foo', 'bar', 'baz' ) ) ); + $this->assertSame( 13, $result->getResultData( array( 'limits', 'foo' ) ) ); + try { + $result->getResultData( array( 'limits', 'foo', 'bar' ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( InvalidArgumentException $ex ) { + $this->assertSame( + 'Path limits.foo is not an array', + $ex->getMessage(), + 'Expected exception' + ); + } + + $result = new ApiResult( 10 ); + $formatter = new ApiErrorFormatter( $result, Language::factory( 'en' ), 'none', false ); + $result->setErrorFormatter( $formatter ); + $this->assertFalse( $result->addValue( null, 'foo', '12345678901' ) ); + $this->assertTrue( $result->addValue( null, 'foo', '12345678901', ApiResult::NO_SIZE_CHECK ) ); + $this->assertSame( 0, $result->getSize() ); + $result->reset(); + $this->assertTrue( $result->addValue( null, 'foo', '1234567890' ) ); + $this->assertFalse( $result->addValue( null, 'foo', '1' ) ); + $result->removeValue( null, 'foo' ); + $this->assertTrue( $result->addValue( null, 'foo', '1' ) ); + + $result = new ApiResult( 8388608 ); + $result2 = new ApiResult( 8388608 ); + $result2->addValue( null, 'foo', 'bar' ); + $result->addValue( null, 'baz', $result2 ); + $this->assertSame( array( + 'baz' => array( + 'foo' => 'bar', + ApiResult::META_TYPE => 'assoc', + ), + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + + $result = new ApiResult( 8388608 ); + $result->addValue( null, 'foo', "foo\x80bar" ); + $result->addValue( null, 'bar', "a\xcc\x81" ); + $result->addValue( null, 'baz', 74 ); + $result->addValue( null, null, "foo\x80bar" ); + $result->addValue( null, null, "a\xcc\x81" ); + $this->assertSame( array( + 'foo' => "foo\xef\xbf\xbdbar", + 'bar' => "\xc3\xa1", + 'baz' => 74, + 0 => "foo\xef\xbf\xbdbar", + 1 => "\xc3\xa1", + ApiResult::META_TYPE => 'assoc', + ), $result->getResultData() ); + } + + /** + * @covers ApiResult + */ + public function testMetadata() { + $arr = array( 'foo' => array( 'bar' => array() ) ); + $result = new ApiResult( 8388608 ); + $result->addValue( null, 'foo', array( 'bar' => array() ) ); + + $expect = array( + 'foo' => array( + 'bar' => array( + ApiResult::META_INDEXED_TAG_NAME => 'ritn', + ApiResult::META_TYPE => 'default', + ), + ApiResult::META_INDEXED_TAG_NAME => 'ritn', + ApiResult::META_TYPE => 'default', + ), + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar' ), + ApiResult::META_TYPE => 'array', + ); + + ApiResult::setSubelementsList( $arr, 'foo' ); + ApiResult::setSubelementsList( $arr, array( 'bar', 'baz' ) ); + ApiResult::unsetSubelementsList( $arr, 'baz' ); + ApiResult::setIndexedTagNameRecursive( $arr, 'ritn' ); + ApiResult::setIndexedTagName( $arr, 'itn' ); + ApiResult::setPreserveKeysList( $arr, 'foo' ); + ApiResult::setPreserveKeysList( $arr, array( 'bar', 'baz' ) ); + ApiResult::unsetPreserveKeysList( $arr, 'baz' ); + ApiResult::setArrayTypeRecursive( $arr, 'default' ); + ApiResult::setArrayType( $arr, 'array' ); + $this->assertSame( $expect, $arr ); + + $result->addSubelementsList( null, 'foo' ); + $result->addSubelementsList( null, array( 'bar', 'baz' ) ); + $result->removeSubelementsList( null, 'baz' ); + $result->addIndexedTagNameRecursive( null, 'ritn' ); + $result->addIndexedTagName( null, 'itn' ); + $result->addPreserveKeysList( null, 'foo' ); + $result->addPreserveKeysList( null, array( 'bar', 'baz' ) ); + $result->removePreserveKeysList( null, 'baz' ); + $result->addArrayTypeRecursive( null, 'default' ); + $result->addArrayType( null, 'array' ); + $this->assertEquals( $expect, $result->getResultData() ); + + $arr = array( 'foo' => array( 'bar' => array() ) ); + $expect = array( + 'foo' => array( + 'bar' => array( + ApiResult::META_TYPE => 'kvp', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + ApiResult::META_TYPE => 'kvp', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + ApiResult::META_TYPE => 'BCkvp', + ApiResult::META_KVP_KEY_NAME => 'bc', + ); + ApiResult::setArrayTypeRecursive( $arr, 'kvp', 'key' ); + ApiResult::setArrayType( $arr, 'BCkvp', 'bc' ); + $this->assertSame( $expect, $arr ); + } + + /** + * @covers ApiResult + */ + public function testUtilityFunctions() { + $arr = array( + 'foo' => array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'bar2' => (object)array( '_dummy' => 'foobaz' ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + 'foo2' => (object)array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'bar2' => (object)array( '_dummy' => 'foobaz' ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ), + ApiResult::META_TYPE => 'array', + '_dummy' => 'foobaz', + '_dummy2' => 'foobaz!', + ); + $this->assertEquals( array( + 'foo' => array( + 'bar' => array(), + 'bar2' => (object)array(), + 'x' => 'ok', + ), + 'foo2' => (object)array( + 'bar' => array(), + 'bar2' => (object)array(), + 'x' => 'ok', + ), + '_dummy2' => 'foobaz!', + ), ApiResult::stripMetadata( $arr ), 'ApiResult::stripMetadata' ); + + $metadata = array(); + $data = ApiResult::stripMetadataNonRecursive( $arr, $metadata ); + $this->assertEquals( array( + 'foo' => array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'bar2' => (object)array( '_dummy' => 'foobaz' ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + 'foo2' => (object)array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'bar2' => (object)array( '_dummy' => 'foobaz' ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + '_dummy2' => 'foobaz!', + ), $data, 'ApiResult::stripMetadataNonRecursive ($data)' ); + $this->assertEquals( array( + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ), + ApiResult::META_TYPE => 'array', + '_dummy' => 'foobaz', + ), $metadata, 'ApiResult::stripMetadataNonRecursive ($metadata)' ); + + $metadata = null; + $data = ApiResult::stripMetadataNonRecursive( (object)$arr, $metadata ); + $this->assertEquals( (object)array( + 'foo' => array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'bar2' => (object)array( '_dummy' => 'foobaz' ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + 'foo2' => (object)array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'bar2' => (object)array( '_dummy' => 'foobaz' ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + '_dummy2' => 'foobaz!', + ), $data, 'ApiResult::stripMetadataNonRecursive on object ($data)' ); + $this->assertEquals( array( + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ), + ApiResult::META_TYPE => 'array', + '_dummy' => 'foobaz', + ), $metadata, 'ApiResult::stripMetadataNonRecursive on object ($metadata)' ); + } + + /** + * @covers ApiResult + * @dataProvider provideTransformations + * @param string $label + * @param array $input + * @param array $transforms + * @param array|Exception $expect + */ + public function testTransformations( $label, $input, $transforms, $expect ) { + $result = new ApiResult( false ); + $result->addValue( null, 'test', $input ); + + if ( $expect instanceof Exception ) { + try { + $output = $result->getResultData( 'test', $transforms ); + $this->fail( 'Expected exception not thrown', $label ); + } catch ( Exception $ex ) { + $this->assertEquals( $ex, $expect, $label ); + } + } else { + $output = $result->getResultData( 'test', $transforms ); + $this->assertEquals( $expect, $output, $label ); + } + } + + public function provideTransformations() { + $kvp = function ( $keyKey, $key, $valKey, $value ) { + return array( + $keyKey => $key, + $valKey => $value, + ApiResult::META_PRESERVE_KEYS => array( $keyKey ), + ApiResult::META_CONTENT => $valKey, + ApiResult::META_TYPE => 'assoc', + ); + }; + $typeArr = array( + 'defaultArray' => array( 2 => 'a', 0 => 'b', 1 => 'c' ), + 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c' ), + 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c' ), + 'array' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'array' ), + 'BCarray' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'BCarray' ), + 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'BCassoc' ), + 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'kvp' => array( 'x' => 'a', 'y' => 'b', 'z' => array( 'c' ), ApiResult::META_TYPE => 'kvp' ), + 'BCkvp' => array( 'x' => 'a', 'y' => 'b', + ApiResult::META_TYPE => 'BCkvp', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + 'emptyDefault' => array( '_dummy' => 1 ), + 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ), + '_dummy' => 1, + ApiResult::META_PRESERVE_KEYS => array( '_dummy' ), + ); + $stripArr = array( + 'foo' => array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'baz' => array( + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ), + ApiResult::META_TYPE => 'array', + ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ), + ApiResult::META_TYPE => 'array', + '_dummy' => 'foobaz', + '_dummy2' => 'foobaz!', + ); + + return array( + array( + 'BC: META_BC_BOOLS', + array( + 'BCtrue' => true, + 'BCfalse' => false, + 'true' => true, + 'false' => false, + ApiResult::META_BC_BOOLS => array( 0, 'true', 'false' ), + ), + array( 'BC' => array() ), + array( + 'BCtrue' => '', + 'true' => true, + 'false' => false, + ApiResult::META_BC_BOOLS => array( 0, 'true', 'false' ), + ) + ), + array( + 'BC: META_BC_SUBELEMENTS', + array( + 'bc' => 'foo', + 'nobc' => 'bar', + ApiResult::META_BC_SUBELEMENTS => array( 'bc' ), + ), + array( 'BC' => array() ), + array( + 'bc' => array( + '*' => 'foo', + ApiResult::META_CONTENT => '*', + ApiResult::META_TYPE => 'assoc', + ), + 'nobc' => 'bar', + ApiResult::META_BC_SUBELEMENTS => array( 'bc' ), + ), + ), + array( + 'BC: META_CONTENT', + array( + 'content' => '!!!', + ApiResult::META_CONTENT => 'content', + ), + array( 'BC' => array() ), + array( + '*' => '!!!', + ApiResult::META_CONTENT => '*', + ), + ), + array( + 'BC: BCkvp type', + array( + 'foo' => 'foo value', + 'bar' => 'bar value', + '_baz' => 'baz value', + ApiResult::META_TYPE => 'BCkvp', + ApiResult::META_KVP_KEY_NAME => 'key', + ApiResult::META_PRESERVE_KEYS => array( '_baz' ), + ), + array( 'BC' => array() ), + array( + $kvp( 'key', 'foo', '*', 'foo value' ), + $kvp( 'key', 'bar', '*', 'bar value' ), + $kvp( 'key', '_baz', '*', 'baz value' ), + ApiResult::META_TYPE => 'array', + ApiResult::META_KVP_KEY_NAME => 'key', + ApiResult::META_PRESERVE_KEYS => array( '_baz' ), + ), + ), + array( + 'BC: BCarray type', + array( + ApiResult::META_TYPE => 'BCarray', + ), + array( 'BC' => array() ), + array( + ApiResult::META_TYPE => 'default', + ), + ), + array( + 'BC: BCassoc type', + array( + ApiResult::META_TYPE => 'BCassoc', + ), + array( 'BC' => array() ), + array( + ApiResult::META_TYPE => 'default', + ), + ), + array( + 'BC: BCkvp exception', + array( + ApiResult::META_TYPE => 'BCkvp', + ), + array( 'BC' => array() ), + new UnexpectedValueException( + 'Type "BCkvp" used without setting ApiResult::META_KVP_KEY_NAME metadata item' + ), + ), + array( + 'BC: nobool, no*, nosub', + array( + 'true' => true, + 'false' => false, + 'content' => 'content', + ApiResult::META_CONTENT => 'content', + 'bc' => 'foo', + ApiResult::META_BC_SUBELEMENTS => array( 'bc' ), + 'BCarray' => array( ApiResult::META_TYPE => 'BCarray' ), + 'BCassoc' => array( ApiResult::META_TYPE => 'BCassoc' ), + 'BCkvp' => array( + 'foo' => 'foo value', + 'bar' => 'bar value', + '_baz' => 'baz value', + ApiResult::META_TYPE => 'BCkvp', + ApiResult::META_KVP_KEY_NAME => 'key', + ApiResult::META_PRESERVE_KEYS => array( '_baz' ), + ), + ), + array( 'BC' => array( 'nobool', 'no*', 'nosub' ) ), + array( + 'true' => true, + 'false' => false, + 'content' => 'content', + 'bc' => 'foo', + 'BCarray' => array( ApiResult::META_TYPE => 'default' ), + 'BCassoc' => array( ApiResult::META_TYPE => 'default' ), + 'BCkvp' => array( + $kvp( 'key', 'foo', '*', 'foo value' ), + $kvp( 'key', 'bar', '*', 'bar value' ), + $kvp( 'key', '_baz', '*', 'baz value' ), + ApiResult::META_TYPE => 'array', + ApiResult::META_KVP_KEY_NAME => 'key', + ApiResult::META_PRESERVE_KEYS => array( '_baz' ), + ), + ApiResult::META_CONTENT => 'content', + ApiResult::META_BC_SUBELEMENTS => array( 'bc' ), + ), + ), + + array( + 'Types: Normal transform', + $typeArr, + array( 'Types' => array() ), + array( + 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ), + 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ), + 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'kvp' => array( 'x' => 'a', 'y' => 'b', + 'z' => array( 'c', ApiResult::META_TYPE => 'array' ), + ApiResult::META_TYPE => 'assoc' + ), + 'BCkvp' => array( 'x' => 'a', 'y' => 'b', + ApiResult::META_TYPE => 'assoc', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ), + 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ), + '_dummy' => 1, + ApiResult::META_PRESERVE_KEYS => array( '_dummy' ), + ApiResult::META_TYPE => 'assoc', + ), + ), + array( + 'Types: AssocAsObject', + $typeArr, + array( 'Types' => array( 'AssocAsObject' => true ) ), + (object)array( + 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ), + 'defaultAssoc' => (object)array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'defaultAssoc2' => (object)array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCassoc' => (object)array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ), + 'assoc' => (object)array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'kvp' => (object)array( 'x' => 'a', 'y' => 'b', + 'z' => array( 'c', ApiResult::META_TYPE => 'array' ), + ApiResult::META_TYPE => 'assoc' + ), + 'BCkvp' => (object)array( 'x' => 'a', 'y' => 'b', + ApiResult::META_TYPE => 'assoc', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ), + 'emptyAssoc' => (object)array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ), + '_dummy' => 1, + ApiResult::META_PRESERVE_KEYS => array( '_dummy' ), + ApiResult::META_TYPE => 'assoc', + ), + ), + array( + 'Types: ArmorKVP', + $typeArr, + array( 'Types' => array( 'ArmorKVP' => 'name' ) ), + array( + 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ), + 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ), + 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'kvp' => array( + $kvp( 'name', 'x', 'value', 'a' ), + $kvp( 'name', 'y', 'value', 'b' ), + $kvp( 'name', 'z', 'value', array( 'c', ApiResult::META_TYPE => 'array' ) ), + ApiResult::META_TYPE => 'array' + ), + 'BCkvp' => array( + $kvp( 'key', 'x', 'value', 'a' ), + $kvp( 'key', 'y', 'value', 'b' ), + ApiResult::META_TYPE => 'array', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ), + 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ), + '_dummy' => 1, + ApiResult::META_PRESERVE_KEYS => array( '_dummy' ), + ApiResult::META_TYPE => 'assoc', + ), + ), + array( + 'Types: ArmorKVP + BC', + $typeArr, + array( 'BC' => array(), 'Types' => array( 'ArmorKVP' => 'name' ) ), + array( + 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ), + 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCarray' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'array' ), + 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'kvp' => array( + $kvp( 'name', 'x', '*', 'a' ), + $kvp( 'name', 'y', '*', 'b' ), + $kvp( 'name', 'z', '*', array( 'c', ApiResult::META_TYPE => 'array' ) ), + ApiResult::META_TYPE => 'array' + ), + 'BCkvp' => array( + $kvp( 'key', 'x', '*', 'a' ), + $kvp( 'key', 'y', '*', 'b' ), + ApiResult::META_TYPE => 'array', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ), + 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ), + '_dummy' => 1, + ApiResult::META_PRESERVE_KEYS => array( '_dummy' ), + ApiResult::META_TYPE => 'assoc', + ), + ), + array( + 'Types: ArmorKVP + AssocAsObject', + $typeArr, + array( 'Types' => array( 'ArmorKVP' => 'name', 'AssocAsObject' => true ) ), + (object)array( + 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ), + 'defaultAssoc' => (object)array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'defaultAssoc2' => (object)array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ), + 'BCassoc' => (object)array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ), + 'assoc' => (object)array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ), + 'kvp' => array( + (object)$kvp( 'name', 'x', 'value', 'a' ), + (object)$kvp( 'name', 'y', 'value', 'b' ), + (object)$kvp( 'name', 'z', 'value', array( 'c', ApiResult::META_TYPE => 'array' ) ), + ApiResult::META_TYPE => 'array' + ), + 'BCkvp' => array( + (object)$kvp( 'key', 'x', 'value', 'a' ), + (object)$kvp( 'key', 'y', 'value', 'b' ), + ApiResult::META_TYPE => 'array', + ApiResult::META_KVP_KEY_NAME => 'key', + ), + 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ), + 'emptyAssoc' => (object)array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ), + '_dummy' => 1, + ApiResult::META_PRESERVE_KEYS => array( '_dummy' ), + ApiResult::META_TYPE => 'assoc', + ), + ), + array( + 'Types: BCkvp exception', + array( + ApiResult::META_TYPE => 'BCkvp', + ), + array( 'Types' => array() ), + new UnexpectedValueException( + 'Type "BCkvp" used without setting ApiResult::META_KVP_KEY_NAME metadata item' + ), + ), + + array( + 'Strip: With ArmorKVP + AssocAsObject transforms', + $typeArr, + array( 'Types' => array( 'ArmorKVP' => 'name', 'AssocAsObject' => true ), 'Strip' => 'all' ), + (object)array( + 'defaultArray' => array( 'b', 'c', 'a' ), + 'defaultAssoc' => (object)array( 'x' => 'a', 1 => 'b', 0 => 'c' ), + 'defaultAssoc2' => (object)array( 2 => 'a', 3 => 'b', 0 => 'c' ), + 'array' => array( 'a', 'c', 'b' ), + 'BCarray' => array( 'a', 'c', 'b' ), + 'BCassoc' => (object)array( 'a', 'b', 'c' ), + 'assoc' => (object)array( 2 => 'a', 0 => 'b', 1 => 'c' ), + 'kvp' => array( + (object)array( 'name' => 'x', 'value' => 'a' ), + (object)array( 'name' => 'y', 'value' => 'b' ), + (object)array( 'name' => 'z', 'value' => array( 'c' ) ), + ), + 'BCkvp' => array( + (object)array( 'key' => 'x', 'value' => 'a' ), + (object)array( 'key' => 'y', 'value' => 'b' ), + ), + 'emptyDefault' => array(), + 'emptyAssoc' => (object)array(), + '_dummy' => 1, + ), + ), + + array( + 'Strip: all', + $stripArr, + array( 'Strip' => 'all' ), + array( + 'foo' => array( + 'bar' => array(), + 'baz' => array(), + 'x' => 'ok', + ), + '_dummy2' => 'foobaz!', + ), + ), + array( + 'Strip: base', + $stripArr, + array( 'Strip' => 'base' ), + array( + 'foo' => array( + 'bar' => array( '_dummy' => 'foobaz' ), + 'baz' => array( + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ), + ApiResult::META_TYPE => 'array', + ), + 'x' => 'ok', + '_dummy' => 'foobaz', + ), + '_dummy2' => 'foobaz!', + ), + ), + array( + 'Strip: bc', + $stripArr, + array( 'Strip' => 'bc' ), + array( + 'foo' => array( + 'bar' => array(), + 'baz' => array( + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ), + 'x' => 'ok', + ), + '_dummy2' => 'foobaz!', + ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ), + ApiResult::META_INDEXED_TAG_NAME => 'itn', + ), + ), + + array( + 'Custom transform', + array( + 'foo' => '?', + 'bar' => '?', + '_dummy' => '?', + '_dummy2' => '?', + '_dummy3' => '?', + ApiResult::META_CONTENT => 'foo', + ApiResult::META_PRESERVE_KEYS => array( '_dummy2', '_dummy3' ), + ), + array( + 'Custom' => array( $this, 'customTransform' ), + 'BC' => array(), + 'Types' => array(), + 'Strip' => 'all' + ), + array( + '*' => 'FOO', + 'bar' => 'BAR', + 'baz' => array( 'a', 'b' ), + '_dummy2' => '_DUMMY2', + '_dummy3' => '_DUMMY3', + ApiResult::META_CONTENT => 'bar', + ), + ), + ); + + } + + /** + * Custom transformer for testTransformations + * @param array &$data + * @param array &$metadata + */ + public function customTransform( &$data, &$metadata ) { + // Prevent recursion + if ( isset( $metadata['_added'] ) ) { + $metadata[ApiResult::META_TYPE] = 'array'; + return; + } + + foreach ( $data as $k => $v ) { + $data[$k] = strtoupper( $k ); + } + $data['baz'] = array( '_added' => 1, 'z' => 'b', 'y' => 'a' ); + $metadata[ApiResult::META_PRESERVE_KEYS][0] = '_dummy'; + $data[ApiResult::META_CONTENT] = 'bar'; + } + + /** + * @covers ApiResult + */ + public function testDeprecatedFunctions() { + // Ignore ApiResult deprecation warnings during this test + set_error_handler( function ( $errno, $errstr ) use ( &$warnings ) { + if ( preg_match( '/Use of ApiResult::\S+ was deprecated in MediaWiki \d+.\d+\./', $errstr ) ) { + return true; + } + if ( preg_match( '/Use of ApiMain to ApiResult::__construct was deprecated in MediaWiki \d+.\d+\./', $errstr ) ) { + return true; + } + return false; + } ); + $reset = new ScopedCallback( 'restore_error_handler' ); + + $context = new DerivativeContext( RequestContext::getMain() ); + $context->setConfig( new HashConfig( array( + 'APIModules' => array(), + 'APIFormatModules' => array(), + 'APIMaxResultSize' => 42, + ) ) ); + $main = new ApiMain( $context ); + $result = TestingAccessWrapper::newFromObject( new ApiResult( $main ) ); + $this->assertSame( 42, $result->maxSize ); + $this->assertSame( $main->getErrorFormatter(), $result->errorFormatter ); + $this->assertSame( $main, $result->mainForContinuation ); + + $result = new ApiResult( 8388608 ); + + $result->addContentValue( null, 'test', 'content' ); + $result->addContentValue( array( 'foo', 'bar' ), 'test', 'content' ); + $result->addIndexedTagName( null, 'itn' ); + $result->addSubelementsList( null, array( 'sub' ) ); + $this->assertSame( array( + 'foo' => array( + 'bar' => array( + '*' => 'content', + ), + ), + '*' => 'content', + ), $result->getData() ); + $result->setRawMode(); + $this->assertSame( array( + 'foo' => array( + 'bar' => array( + '*' => 'content', + ), + ), + '*' => 'content', + '_element' => 'itn', + '_subelements' => array( 'sub' ), + ), $result->getData() ); + + $arr = array(); + ApiResult::setContent( $arr, 'value' ); + ApiResult::setContent( $arr, 'value2', 'foobar' ); + $this->assertSame( array( + ApiResult::META_CONTENT => 'content', + 'content' => 'value', + 'foobar' => array( + ApiResult::META_CONTENT => 'content', + 'content' => 'value2', + ), + ), $arr ); + + $result = new ApiResult( 3 ); + $formatter = new ApiErrorFormatter_BackCompat( $result ); + $result->setErrorFormatter( $formatter ); + $result->disableSizeCheck(); + $this->assertTrue( $result->addValue( null, 'foo', '1234567890' ) ); + $result->enableSizeCheck(); + $this->assertSame( 0, $result->getSize() ); + $this->assertFalse( $result->addValue( null, 'foo', '1234567890' ) ); + + $arr = array( 'foo' => array( 'bar' => 1 ) ); + $result->setIndexedTagName_recursive( $arr, 'itn' ); + $this->assertSame( array( + 'foo' => array( + 'bar' => 1, + ApiResult::META_INDEXED_TAG_NAME => 'itn' + ), + ), $arr ); + + $status = Status::newGood(); + $status->fatal( 'parentheses', '1' ); + $status->fatal( 'parentheses', '2' ); + $status->warning( 'parentheses', '3' ); + $status->warning( 'parentheses', '4' ); + $this->assertSame( array( + array( + 'type' => 'error', + 'message' => 'parentheses', + 'params' => array( + 0 => '1', + ApiResult::META_INDEXED_TAG_NAME => 'param', + ), + ), + array( + 'type' => 'error', + 'message' => 'parentheses', + 'params' => array( + 0 => '2', + ApiResult::META_INDEXED_TAG_NAME => 'param', + ), + ), + ApiResult::META_INDEXED_TAG_NAME => 'error', + ), $result->convertStatusToArray( $status, 'error' ) ); + $this->assertSame( array( + array( + 'type' => 'warning', + 'message' => 'parentheses', + 'params' => array( + 0 => '3', + ApiResult::META_INDEXED_TAG_NAME => 'param', + ), + ), + array( + 'type' => 'warning', + 'message' => 'parentheses', + 'params' => array( + 0 => '4', + ApiResult::META_INDEXED_TAG_NAME => 'param', + ), + ), + ApiResult::META_INDEXED_TAG_NAME => 'warning', + ), $result->convertStatusToArray( $status, 'warning' ) ); + } + + /** + * @covers ApiResult + */ + public function testDeprecatedContinuation() { + // Ignore ApiResult deprecation warnings during this test + set_error_handler( function ( $errno, $errstr ) use ( &$warnings ) { + if ( preg_match( '/Use of ApiResult::\S+ was deprecated in MediaWiki \d+.\d+\./', $errstr ) ) { + return true; + } + return false; + } ); + + $reset = new ScopedCallback( 'restore_error_handler' ); + $allModules = array( + new MockApiQueryBase( 'mock1' ), + new MockApiQueryBase( 'mock2' ), + new MockApiQueryBase( 'mocklist' ), + ); + $generator = new MockApiQueryBase( 'generator' ); + + $main = new ApiMain( RequestContext::getMain() ); + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $result->setContinueParam( $allModules[2], 'mlcontinue', 2 ); + $result->setGeneratorContinueParam( $generator, 'gcontinue', 3 ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'mlcontinue' => 2, + 'm1continue' => '1|2', + 'continue' => '||mock2', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( null, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + 'mocklist' => array( 'mlcontinue' => 2 ), + 'generator' => array( 'gcontinue' => 3 ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $result->setGeneratorContinueParam( $generator, 'gcontinue', array( 3, 4 ) ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'm1continue' => '1|2', + 'continue' => '||mock2|mocklist', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( null, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + 'generator' => array( 'gcontinue' => '3|4' ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setContinueParam( $allModules[2], 'mlcontinue', 2 ); + $result->setGeneratorContinueParam( $generator, 'gcontinue', 3 ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'mlcontinue' => 2, + 'gcontinue' => 3, + 'continue' => 'gcontinue||', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( true, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'mocklist' => array( 'mlcontinue' => 2 ), + 'generator' => array( 'gcontinue' => 3 ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setGeneratorContinueParam( $generator, 'gcontinue', 3 ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'gcontinue' => 3, + 'continue' => 'gcontinue||mocklist', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( true, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'generator' => array( 'gcontinue' => 3 ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $result->setContinueParam( $allModules[2], 'mlcontinue', 2 ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'mlcontinue' => 2, + 'm1continue' => '1|2', + 'continue' => '||mock2', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( null, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + 'mocklist' => array( 'mlcontinue' => 2 ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'm1continue' => '1|2', + 'continue' => '||mock2|mocklist', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( null, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'mock1' => array( 'm1continue' => '1|2' ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->setContinueParam( $allModules[2], 'mlcontinue', 2 ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( array( + 'mlcontinue' => 2, + 'continue' => '-||mock1|mock2', + ), $result->getResultData( 'continue' ) ); + $this->assertSame( true, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( array( + 'mocklist' => array( 'mlcontinue' => 2 ), + ), $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( array( false, $allModules ), $ret ); + $result->endContinuation( 'raw' ); + $result->endContinuation( 'standard' ); + $this->assertSame( null, $result->getResultData( 'continue' ) ); + $this->assertSame( true, $result->getResultData( 'batchcomplete' ) ); + $this->assertSame( null, $result->getResultData( 'query-continue' ) ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( '||mock2', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( + array( false, array_values( array_diff_key( $allModules, array( 1 => 1 ) ) ) ), + $ret + ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $ret = $result->beginContinuation( '-||', $allModules, array( 'mock1', 'mock2' ) ); + $this->assertSame( + array( true, array_values( array_diff_key( $allModules, array( 0 => 0, 1 => 1 ) ) ) ), + $ret + ); + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + try { + $result->beginContinuation( 'foo', $allModules, array( 'mock1', 'mock2' ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UsageException $ex ) { + $this->assertSame( + 'Invalid continue param. You should pass the original value returned by the previous query', + $ex->getMessage(), + 'Expected exception' + ); + } + $main->setContinuationManager( null ); + + $result = new ApiResult( 8388608 ); + $result->setMainForContinuation( $main ); + $result->beginContinuation( '||mock2', array_slice( $allModules, 0, 2 ), array( 'mock1', 'mock2' ) ); + try { + $result->setContinueParam( $allModules[1], 'm2continue', 1 ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UnexpectedValueException $ex ) { + $this->assertSame( + 'Module \'mock2\' was not supposed to have been executed, but it was executed anyway', + $ex->getMessage(), + 'Expected exception' + ); + } + try { + $result->setContinueParam( $allModules[2], 'mlcontinue', 1 ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UnexpectedValueException $ex ) { + $this->assertSame( + 'Module \'mocklist\' called ApiContinuationManager::addContinueParam but was not passed to ApiContinuationManager::__construct', + $ex->getMessage(), + 'Expected exception' + ); + } + $main->setContinuationManager( null ); + + } + + public function testObjectSerialization() { + $arr = array(); + ApiResult::setValue( $arr, 'foo', (object)array( 'a' => 1, 'b' => 2 ) ); + $this->assertSame( array( + 'a' => 1, + 'b' => 2, + ApiResult::META_TYPE => 'assoc', + ), $arr['foo'] ); + + $arr = array(); + ApiResult::setValue( $arr, 'foo', new ApiResultTestStringifiableObject() ); + $this->assertSame( 'Ok', $arr['foo'] ); + + $arr = array(); + ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject( 'Ok' ) ); + $this->assertSame( 'Ok', $arr['foo'] ); + + try { + $arr = array(); + ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject( + new ApiResultTestStringifiableObject() + ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UnexpectedValueException $ex ) { + $this->assertSame( + 'ApiResultTestSerializableObject::serializeForApiResult() returned an object of class ApiResultTestStringifiableObject', + $ex->getMessage(), + 'Expected exception' + ); + } + + try { + $arr = array(); + ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject( NAN ) ); + $this->fail( 'Expected exception not thrown' ); + } catch ( UnexpectedValueException $ex ) { + $this->assertSame( + 'ApiResultTestSerializableObject::serializeForApiResult() returned an invalid value: Cannot add non-finite floats to ApiResult', + $ex->getMessage(), + 'Expected exception' + ); + } + + $arr = array(); + ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject( + array( + 'one' => new ApiResultTestStringifiableObject( '1' ), + 'two' => new ApiResultTestSerializableObject( 2 ), + ) + ) ); + $this->assertSame( array( + 'one' => '1', + 'two' => 2, + ), $arr['foo'] ); + } + +} + +class ApiResultTestStringifiableObject { + private $ret; + + public function __construct( $ret = 'Ok' ) { + $this->ret = $ret; + } + + public function __toString() { + return $this->ret; + } +} + +class ApiResultTestSerializableObject { + private $ret; + + public function __construct( $ret ) { + $this->ret = $ret; + } + + public function __toString() { + return "Fail"; + } + + public function serializeForApiResult() { + return $this->ret; + } +} diff --git a/tests/phpunit/includes/api/ApiTestCase.php b/tests/phpunit/includes/api/ApiTestCase.php index cd141947..da62bb0a 100644 --- a/tests/phpunit/includes/api/ApiTestCase.php +++ b/tests/phpunit/includes/api/ApiTestCase.php @@ -8,6 +8,11 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { */ protected $apiContext; + /** + * @var array + */ + protected $tablesUsed = array( 'user', 'user_groups', 'user_properties' ); + protected function setUp() { global $wgServer; @@ -41,6 +46,17 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { $this->apiContext = new ApiTestContext(); } + protected function tearDown() { + // Avoid leaking session over tests + if ( session_id() != '' ) { + global $wgUser; + $wgUser->logout(); + session_destroy(); + } + + parent::tearDown(); + } + /** * Edits or creates a page/revision * @param string $pageName Page title @@ -100,7 +116,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { // construct result $results = array( - $module->getResultData(), + $module->getResult()->getResultData( null, array( 'Strip' => 'all' ) ), $context->getRequest(), $context->getRequest()->getSessionArray() ); @@ -134,10 +150,14 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { } if ( isset( $session['wsToken'] ) && $session['wsToken'] ) { + // @todo Why does this directly mess with the session? Fix that. // add edit token to fake session $session['wsEditToken'] = $session['wsToken']; // add token to request parameters - $params['token'] = md5( $session['wsToken'] ) . User::EDIT_TOKEN_SUFFIX; + $timestamp = wfTimestamp(); + $params['token'] = hash_hmac( 'md5', $timestamp, $session['wsToken'] ) . + dechex( $timestamp ) . + User::EDIT_TOKEN_SUFFIX; return $this->doApiRequest( $params, $session, false, $user ); } else { diff --git a/tests/phpunit/includes/api/ApiTestCaseUpload.php b/tests/phpunit/includes/api/ApiTestCaseUpload.php index 7e513394..87f794c1 100644 --- a/tests/phpunit/includes/api/ApiTestCaseUpload.php +++ b/tests/phpunit/includes/api/ApiTestCaseUpload.php @@ -1,9 +1,8 @@ <?php /** - * * Abstract class to support upload tests + * Abstract class to support upload tests */ - abstract class ApiTestCaseUpload extends ApiTestCase { /** * Fixture -- run before every test @@ -21,12 +20,6 @@ abstract class ApiTestCaseUpload extends ApiTestCase { $this->clearFakeUploads(); } - protected function tearDown() { - $this->clearTempUpload(); - - parent::tearDown(); - } - /** * Helper function -- remove files and associated articles by Title * @@ -105,7 +98,7 @@ abstract class ApiTestCaseUpload extends ApiTestCase { * @return bool */ function fakeUploadFile( $fieldName, $fileName, $type, $filePath ) { - $tmpName = tempnam( wfTempDir(), "" ); + $tmpName = $this->getNewTempFile(); if ( !file_exists( $filePath ) ) { throw new Exception( "$filePath doesn't exist!" ); } @@ -132,7 +125,7 @@ abstract class ApiTestCaseUpload extends ApiTestCase { } function fakeUploadChunk( $fieldName, $fileName, $type, & $chunkData ) { - $tmpName = tempnam( wfTempDir(), "" ); + $tmpName = $this->getNewTempFile(); // copy the chunk data to temp location: if ( !file_put_contents( $tmpName, $chunkData ) ) { throw new Exception( "couldn't copy chunk data to $tmpName" ); @@ -153,15 +146,6 @@ abstract class ApiTestCaseUpload extends ApiTestCase { ); } - function clearTempUpload() { - if ( isset( $_FILES['file']['tmp_name'] ) ) { - $tmp = $_FILES['file']['tmp_name']; - if ( file_exists( $tmp ) ) { - unlink( $tmp ); - } - } - } - /** * Remove traces of previous fake uploads */ diff --git a/tests/phpunit/includes/api/ApiUploadTest.php b/tests/phpunit/includes/api/ApiUploadTest.php index 8ea761f8..f74fc354 100644 --- a/tests/phpunit/includes/api/ApiUploadTest.php +++ b/tests/phpunit/includes/api/ApiUploadTest.php @@ -1,31 +1,24 @@ <?php /** - * @group API - * @group Database - * @group medium - */ - -/** * n.b. Ensure that you can write to the images/ directory as the * user that will run tests. - */ - -// Note for reviewers: this intentionally duplicates functionality already in -// "ApiSetup" and so on. This framework works better IMO and has less -// strangeness (such as test cases inheriting from "ApiSetup"...) (and in the -// case of the other Upload tests, this flat out just actually works... ) - -// @todo Port the other Upload tests, and other API tests to this framework - -require_once 'ApiTestCaseUpload.php'; - -/** - * @group Database - * @group Broken - * Broken test, reports false errors from time to time. + * + * Note for reviewers: this intentionally duplicates functionality already in + * "ApiSetup" and so on. This framework works better IMO and has less + * strangeness (such as test cases inheriting from "ApiSetup"...) (and in the + * case of the other Upload tests, this flat out just actually works... ) + * + * @todo Port the other Upload tests, and other API tests to this framework + * + * @todo Broken test, reports false errors from time to time. * See https://bugzilla.wikimedia.org/26169 * - * This is pretty sucky... needs to be prettified. + * @todo This is pretty sucky... needs to be prettified. + * + * @group API + * @group Database + * @group medium + * @group Broken */ class ApiUploadTest extends ApiTestCaseUpload { /** @@ -105,7 +98,7 @@ class ApiUploadTest extends ApiTestCaseUpload { try { $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() ); + $filePaths = $randomImageGenerator->writeImages( 1, $extension, $this->getNewTempDirectory() ); } catch ( Exception $e ) { $this->markTestIncomplete( $e->getMessage() ); } @@ -145,7 +138,6 @@ class ApiUploadTest extends ApiTestCaseUpload { // clean up $this->deleteFileByFilename( $fileName ); - unlink( $filePath ); } /** @@ -154,7 +146,7 @@ class ApiUploadTest extends ApiTestCaseUpload { public function testUploadZeroLength( $session ) { $mimeType = 'image/png'; - $filePath = tempnam( wfTempDir(), "" ); + $filePath = $this->getNewTempFile(); $fileName = "apiTestUploadZeroLength.png"; $this->deleteFileByFileName( $fileName ); @@ -182,7 +174,6 @@ class ApiUploadTest extends ApiTestCaseUpload { // clean up $this->deleteFileByFilename( $fileName ); - unlink( $filePath ); } /** @@ -194,7 +185,7 @@ class ApiUploadTest extends ApiTestCaseUpload { try { $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 2, $extension, wfTempDir() ); + $filePaths = $randomImageGenerator->writeImages( 2, $extension, $this->getNewTempDirectory() ); } catch ( Exception $e ) { $this->markTestIncomplete( $e->getMessage() ); } @@ -253,8 +244,6 @@ class ApiUploadTest extends ApiTestCaseUpload { // clean up $this->deleteFileByFilename( $fileName ); - unlink( $filePaths[0] ); - unlink( $filePaths[1] ); } /** @@ -266,7 +255,7 @@ class ApiUploadTest extends ApiTestCaseUpload { try { $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() ); + $filePaths = $randomImageGenerator->writeImages( 1, $extension, $this->getNewTempDirectory() ); } catch ( Exception $e ) { $this->markTestIncomplete( $e->getMessage() ); } @@ -335,7 +324,6 @@ class ApiUploadTest extends ApiTestCaseUpload { // clean up $this->deleteFileByFilename( $fileNames[0] ); $this->deleteFileByFilename( $fileNames[1] ); - unlink( $filePaths[0] ); } /** @@ -351,7 +339,7 @@ class ApiUploadTest extends ApiTestCaseUpload { try { $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() ); + $filePaths = $randomImageGenerator->writeImages( 1, $extension, $this->getNewTempDirectory() ); } catch ( Exception $e ) { $this->markTestIncomplete( $e->getMessage() ); } @@ -419,7 +407,6 @@ class ApiUploadTest extends ApiTestCaseUpload { // clean up $this->deleteFileByFilename( $fileName ); - unlink( $filePath ); } /** @@ -433,16 +420,14 @@ class ApiUploadTest extends ApiTestCaseUpload { $chunkSize = 1048576; // Download a large image file - // ( using RandomImageGenerator for large files is not stable ) + // (using RandomImageGenerator for large files is not stable) + // @todo Don't download files from wikimedia.org $mimeType = 'image/jpeg'; $url = 'http://upload.wikimedia.org/wikipedia/commons/' . 'e/ed/Oberaargletscher_from_Oberaar%2C_2010_07.JPG'; - $filePath = wfTempDir() . '/Oberaargletscher_from_Oberaar.jpg'; + $filePath = $this->getNewTempDirectory() . '/Oberaargletscher_from_Oberaar.jpg'; try { - // Only download if the file is not avaliable in the temp location: - if ( !is_file( $filePath ) ) { - copy( $url, $filePath ); - } + copy( $url, $filePath ); } catch ( Exception $e ) { $this->markTestIncomplete( $e->getMessage() ); } @@ -566,7 +551,5 @@ class ApiUploadTest extends ApiTestCaseUpload { // clean up $this->deleteFileByFilename( $fileName ); - // don't remove downloaded temporary file for fast subquent tests. - //unlink( $filePath ); } } diff --git a/tests/phpunit/includes/api/MockApi.php b/tests/phpunit/includes/api/MockApi.php index d94aa2cd..516da0c8 100644 --- a/tests/phpunit/includes/api/MockApi.php +++ b/tests/phpunit/includes/api/MockApi.php @@ -4,9 +4,6 @@ class MockApi extends ApiBase { public function execute() { } - public function getVersion() { - } - public function __construct() { } diff --git a/tests/phpunit/includes/api/MockApiQueryBase.php b/tests/phpunit/includes/api/MockApiQueryBase.php index 4bede519..f5b50e5a 100644 --- a/tests/phpunit/includes/api/MockApiQueryBase.php +++ b/tests/phpunit/includes/api/MockApiQueryBase.php @@ -1,11 +1,15 @@ <?php class MockApiQueryBase extends ApiQueryBase { + private $name; + public function execute() { } - public function getVersion() { + public function __construct( $name = 'mock' ) { + $this->name = $name; } - public function __construct() { + public function getModuleName() { + return $this->name; } } diff --git a/tests/phpunit/includes/api/PrefixUniquenessTest.php b/tests/phpunit/includes/api/PrefixUniquenessTest.php index 13da33c7..d04766be 100644 --- a/tests/phpunit/includes/api/PrefixUniquenessTest.php +++ b/tests/phpunit/includes/api/PrefixUniquenessTest.php @@ -20,7 +20,7 @@ class PrefixUniquenessTest extends MediaWikiTestCase { $class = get_class( $module ); $prefix = $module->getModulePrefix(); - if ( isset( $prefixes[$prefix] ) ) { + if ( $prefix !== '' && isset( $prefixes[$prefix] ) ) { $this->fail( "Module prefix '{$prefix}' is shared between {$class} and {$prefixes[$prefix]}" ); } $prefixes[$module->getModulePrefix()] = $class; diff --git a/tests/phpunit/includes/api/format/ApiFormatDbgTest.php b/tests/phpunit/includes/api/format/ApiFormatDbgTest.php new file mode 100644 index 00000000..3fcfc73f --- /dev/null +++ b/tests/phpunit/includes/api/format/ApiFormatDbgTest.php @@ -0,0 +1,55 @@ +<?php + +/** + * @group API + * @covers ApiFormatDbg + */ +class ApiFormatDbgTest extends ApiFormatTestBase { + + protected $printerName = 'dbg'; + + public static function provideGeneralEncoding() { + $warning = "\n 'warnings' => \n array (\n 'dbg' => \n array (\n" . + " '*' => 'format=dbg has been deprecated. Please use format=json instead.',\n" . + " ),\n ),"; + + return array( + // Basic types + array( array( null ), "array ({$warning}\n 0 => NULL,\n)" ), + array( array( true ), "array ({$warning}\n 0 => '',\n)" ), + array( array( false ), "array ({$warning}\n)" ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), + "array ({$warning}\n 0 => true,\n)" ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), + "array ({$warning}\n 0 => false,\n)" ), + array( array( 42 ), "array ({$warning}\n 0 => 42,\n)" ), + array( array( 42.5 ), "array ({$warning}\n 0 => 42.5,\n)" ), + array( array( 1e42 ), "array ({$warning}\n 0 => 1.0E+42,\n)" ), + array( array( 'foo' ), "array ({$warning}\n 0 => 'foo',\n)" ), + array( array( 'fóo' ), "array ({$warning}\n 0 => 'fóo',\n)" ), + + // Arrays and objects + array( array( array() ), "array ({$warning}\n 0 => \n array (\n ),\n)" ), + array( array( array( 1 ) ), "array ({$warning}\n 0 => \n array (\n 0 => 1,\n ),\n)" ), + array( array( array( 'x' => 1 ) ), "array ({$warning}\n 0 => \n array (\n 'x' => 1,\n ),\n)" ), + array( array( array( 2 => 1 ) ), "array ({$warning}\n 0 => \n array (\n 2 => 1,\n ),\n)" ), + array( array( (object)array() ), "array ({$warning}\n 0 => \n array (\n ),\n)" ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), "array ({$warning}\n 0 => \n array (\n 0 => 1,\n ),\n)" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), "array ({$warning}\n 0 => \n array (\n 0 => 1,\n ),\n)" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), "array ({$warning}\n 0 => \n array (\n 'x' => 1,\n ),\n)" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + "array ({$warning}\n 0 => \n array (\n 0 => \n array (\n 'key' => 'x',\n '*' => 1,\n ),\n ),\n)" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), "array ({$warning}\n 0 => \n array (\n 'x' => 1,\n ),\n)" ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), "array ({$warning}\n 0 => \n array (\n 0 => 'a',\n 1 => 'b',\n ),\n)" ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + "array ({$warning}\n '*' => 'foo',\n)" ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + "array ({$warning}\n 'foo' => \n array (\n '*' => 'foo',\n ),\n)" ), + ); + } + +} diff --git a/tests/phpunit/includes/api/format/ApiFormatDumpTest.php b/tests/phpunit/includes/api/format/ApiFormatDumpTest.php new file mode 100644 index 00000000..c0f67f8d --- /dev/null +++ b/tests/phpunit/includes/api/format/ApiFormatDumpTest.php @@ -0,0 +1,63 @@ +<?php + +/** + * @group API + * @covers ApiFormatDump + */ +class ApiFormatDumpTest extends ApiFormatTestBase { + + protected $printerName = 'dump'; + + public static function provideGeneralEncoding() { + // Sigh. Docs claim it's a boolean, but can have values 0, 1, or 2. + // Fortunately wfIniGetBool does the right thing. + if ( wfIniGetBool( 'xdebug.overload_var_dump' ) ) { + return array( + array( array(), 'Cannot test ApiFormatDump when xDebug overloads var_dump', array( 'SKIP' => true ) ), + ); + } + + $warning = "\n [\"warnings\"]=>\n array(1) {\n [\"dump\"]=>\n array(1) {\n [\"*\"]=>\n" . + " string(64) \"format=dump has been deprecated. Please use format=json instead.\"\n" . + " }\n }"; + + return array( + // Basic types + array( array( null ), "array(2) {{$warning}\n [0]=>\n NULL\n}\n" ), + array( array( true ), "array(2) {{$warning}\n [0]=>\n string(0) \"\"\n}\n" ), + array( array( false ), "array(1) {{$warning}\n}\n" ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), + "array(2) {{$warning}\n [0]=>\n bool(true)\n}\n" ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), + "array(2) {{$warning}\n [0]=>\n bool(false)\n}\n" ), + array( array( 42 ), "array(2) {{$warning}\n [0]=>\n int(42)\n}\n" ), + array( array( 42.5 ), "array(2) {{$warning}\n [0]=>\n float(42.5)\n}\n" ), + array( array( 1e42 ), "array(2) {{$warning}\n [0]=>\n float(1.0E+42)\n}\n" ), + array( array( 'foo' ), "array(2) {{$warning}\n [0]=>\n string(3) \"foo\"\n}\n" ), + array( array( 'fóo' ), "array(2) {{$warning}\n [0]=>\n string(4) \"fóo\"\n}\n" ), + + // Arrays + array( array( array() ), "array(2) {{$warning}\n [0]=>\n array(0) {\n }\n}\n" ), + array( array( array( 1 ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [0]=>\n int(1)\n }\n}\n" ), + array( array( array( 'x' => 1 ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [\"x\"]=>\n int(1)\n }\n}\n" ), + array( array( array( 2 => 1 ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [2]=>\n int(1)\n }\n}\n" ), + array( array( (object)array() ), "array(2) {{$warning}\n [0]=>\n array(0) {\n }\n}\n" ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [0]=>\n int(1)\n }\n}\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [0]=>\n int(1)\n }\n}\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [\"x\"]=>\n int(1)\n }\n}\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + "array(2) {{$warning}\n [0]=>\n array(1) {\n [0]=>\n array(2) {\n [\"key\"]=>\n string(1) \"x\"\n [\"*\"]=>\n int(1)\n }\n }\n}\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), "array(2) {{$warning}\n [0]=>\n array(1) {\n [\"x\"]=>\n int(1)\n }\n}\n" ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), "array(2) {{$warning}\n [0]=>\n array(2) {\n [0]=>\n string(1) \"a\"\n [1]=>\n string(1) \"b\"\n }\n}\n" ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + "array(2) {{$warning}\n [\"*\"]=>\n string(3) \"foo\"\n}\n" ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + "array(2) {{$warning}\n [\"foo\"]=>\n array(1) {\n [\"*\"]=>\n string(3) \"foo\"\n }\n}\n" ), + ); + } + +} diff --git a/tests/phpunit/includes/api/format/ApiFormatJsonTest.php b/tests/phpunit/includes/api/format/ApiFormatJsonTest.php index fc1f9021..3dfcaf0f 100644 --- a/tests/phpunit/includes/api/format/ApiFormatJsonTest.php +++ b/tests/phpunit/includes/api/format/ApiFormatJsonTest.php @@ -2,21 +2,109 @@ /** * @group API - * @group Database - * @group medium * @covers ApiFormatJson */ class ApiFormatJsonTest extends ApiFormatTestBase { - public function testValidSyntax( ) { - $data = $this->apiRequest( 'json', array( 'action' => 'query', 'meta' => 'siteinfo' ) ); + protected $printerName = 'json'; - $this->assertInternalType( 'array', json_decode( $data, true ) ); - $this->assertGreaterThan( 0, count( (array)$data ) ); + private static function addFormatVersion( $format, $arr ) { + foreach ( $arr as &$p ) { + if ( !isset( $p[2] ) ) { + $p[2] = array( 'formatversion' => $format ); + } else { + $p[2]['formatversion'] = $format; + } + } + return $arr; } - public function testJsonpInjection( ) { - $data = $this->apiRequest( 'json', array( 'action' => 'query', 'meta' => 'siteinfo', 'callback' => 'myCallback' ) ); - $this->assertEquals( '/**/myCallback(', substr( $data, 0, 15 ) ); + public static function provideGeneralEncoding() { + return array_merge( + self::addFormatVersion( 1, array( + // Basic types + array( array( null ), '[null]' ), + array( array( true ), '[""]' ), + array( array( false ), '[]' ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), '[true]' ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), '[false]' ), + array( array( 42 ), '[42]' ), + array( array( 42.5 ), '[42.5]' ), + array( array( 1e42 ), '[1.0e+42]' ), + array( array( 'foo' ), '["foo"]' ), + array( array( 'fóo' ), '["f\u00f3o"]' ), + array( array( 'fóo' ), '["fóo"]', array( 'utf8' => 1 ) ), + + // Arrays and objects + array( array( array() ), '[[]]' ), + array( array( array( 1 ) ), '[[1]]' ), + array( array( array( 'x' => 1 ) ), '[{"x":1}]' ), + array( array( array( 2 => 1 ) ), '[{"2":1}]' ), + array( array( (object)array() ), '[{}]' ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), '[{"0":1}]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), '[[1]]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), '[{"x":1}]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + '[[{"key":"x","*":1}]]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), '[{"x":1}]' ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), '[["a","b"]]' ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + '{"*":"foo"}' ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + '{"foo":{"*":"foo"}}' ), + + // Callbacks + array( array( 1 ), '/**/myCallback([1])', array( 'callback' => 'myCallback' ) ), + + // Cross-domain mangling + array( array( '< Cross-Domain-Policy >' ), '["\u003C Cross-Domain-Policy \u003E"]' ), + ) ), + self::addFormatVersion( 2, array( + // Basic types + array( array( null ), '[null]' ), + array( array( true ), '[true]' ), + array( array( false ), '[false]' ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), '[true]' ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), '[false]' ), + array( array( 42 ), '[42]' ), + array( array( 42.5 ), '[42.5]' ), + array( array( 1e42 ), '[1.0e+42]' ), + array( array( 'foo' ), '["foo"]' ), + array( array( 'fóo' ), '["fóo"]' ), + array( array( 'fóo' ), '["f\u00f3o"]', array( 'ascii' => 1 ) ), + + // Arrays and objects + array( array( array() ), '[[]]' ), + array( array( array( 'x' => 1 ) ), '[{"x":1}]' ), + array( array( array( 2 => 1 ) ), '[{"2":1}]' ), + array( array( (object)array() ), '[{}]' ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), '[{"0":1}]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), '[[1]]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), '[{"x":1}]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + '[{"x":1}]' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), '[[1]]' ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), '[{"0":"a","1":"b"}]' ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + '{"content":"foo"}' ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + '{"foo":"foo"}' ), + + // Callbacks + array( array( 1 ), '/**/myCallback([1])', array( 'callback' => 'myCallback' ) ), + + // Cross-domain mangling + array( array( '< Cross-Domain-Policy >' ), '["\u003C Cross-Domain-Policy \u003E"]' ), + ) ) + ); } + } diff --git a/tests/phpunit/includes/api/format/ApiFormatNoneTest.php b/tests/phpunit/includes/api/format/ApiFormatNoneTest.php index cabd750b..8f81a411 100644 --- a/tests/phpunit/includes/api/format/ApiFormatNoneTest.php +++ b/tests/phpunit/includes/api/format/ApiFormatNoneTest.php @@ -2,15 +2,43 @@ /** * @group API - * @group Database - * @group medium * @covers ApiFormatNone */ class ApiFormatNoneTest extends ApiFormatTestBase { - public function testValidSyntax( ) { - $data = $this->apiRequest( 'none', array( 'action' => 'query', 'meta' => 'siteinfo' ) ); + protected $printerName = 'none'; - $this->assertEquals( '', $data ); // No output! + public static function provideGeneralEncoding() { + return array( + // Basic types + array( array( null ), '' ), + array( array( true ), '' ), + array( array( false ), '' ), + array( array( 42 ), '' ), + array( array( 42.5 ), '' ), + array( array( 1e42 ), '' ), + array( array( 'foo' ), '' ), + array( array( 'fóo' ), '' ), + + // Arrays and objects + array( array( array() ), '' ), + array( array( array( 1 ) ), '' ), + array( array( array( 'x' => 1 ) ), '' ), + array( array( array( 2 => 1 ) ), '' ), + array( array( (object)array() ), '' ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), '' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), '' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), '' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), '' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), '' ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), '' ), + + // Content + array( array( '*' => 'foo' ), '' ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), '' ), + ); } + } diff --git a/tests/phpunit/includes/api/format/ApiFormatPhpTest.php b/tests/phpunit/includes/api/format/ApiFormatPhpTest.php index 54f447a9..0cb44e92 100644 --- a/tests/phpunit/includes/api/format/ApiFormatPhpTest.php +++ b/tests/phpunit/includes/api/format/ApiFormatPhpTest.php @@ -2,16 +2,143 @@ /** * @group API - * @group Database - * @group medium * @covers ApiFormatPhp */ class ApiFormatPhpTest extends ApiFormatTestBase { - public function testValidSyntax( ) { - $data = $this->apiRequest( 'php', array( 'action' => 'query', 'meta' => 'siteinfo' ) ); + protected $printerName = 'php'; - $this->assertInternalType( 'array', unserialize( $data ) ); - $this->assertGreaterThan( 0, count( (array)$data ) ); + private static function addFormatVersion( $format, $arr ) { + foreach ( $arr as &$p ) { + if ( !isset( $p[2] ) ) { + $p[2] = array( 'formatversion' => $format ); + } else { + $p[2]['formatversion'] = $format; + } + } + return $arr; } + + public static function provideGeneralEncoding() { + return array_merge( + self::addFormatVersion( 1, array( + // Basic types + array( array( null ), 'a:1:{i:0;N;}' ), + array( array( true ), 'a:1:{i:0;s:0:"";}' ), + array( array( false ), 'a:0:{}' ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), + 'a:1:{i:0;b:1;}' ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), + 'a:1:{i:0;b:0;}' ), + array( array( 42 ), 'a:1:{i:0;i:42;}' ), + array( array( 42.5 ), 'a:1:{i:0;d:42.5;}' ), + array( array( 1e42 ), 'a:1:{i:0;d:1.0E+42;}' ), + array( array( 'foo' ), 'a:1:{i:0;s:3:"foo";}' ), + array( array( 'fóo' ), 'a:1:{i:0;s:4:"fóo";}' ), + + // Arrays and objects + array( array( array() ), 'a:1:{i:0;a:0:{}}' ), + array( array( array( 1 ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'x' => 1 ) ), 'a:1:{i:0;a:1:{s:1:"x";i:1;}}' ), + array( array( array( 2 => 1 ) ), 'a:1:{i:0;a:1:{i:2;i:1;}}' ), + array( array( (object)array() ), 'a:1:{i:0;a:0:{}}' ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), 'a:1:{i:0;a:1:{s:1:"x";i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + 'a:1:{i:0;a:1:{i:0;a:2:{s:3:"key";s:1:"x";s:1:"*";i:1;}}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), 'a:1:{i:0;a:1:{s:1:"x";i:1;}}' ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), 'a:1:{i:0;a:2:{i:0;s:1:"a";i:1;s:1:"b";}}' ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + 'a:1:{s:1:"*";s:3:"foo";}' ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + 'a:1:{s:3:"foo";a:1:{s:1:"*";s:3:"foo";}}' ), + ) ), + self::addFormatVersion( 2, array( + // Basic types + array( array( null ), 'a:1:{i:0;N;}' ), + array( array( true ), 'a:1:{i:0;b:1;}' ), + array( array( false ), 'a:1:{i:0;b:0;}' ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), + 'a:1:{i:0;b:1;}' ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), + 'a:1:{i:0;b:0;}' ), + array( array( 42 ), 'a:1:{i:0;i:42;}' ), + array( array( 42.5 ), 'a:1:{i:0;d:42.5;}' ), + array( array( 1e42 ), 'a:1:{i:0;d:1.0E+42;}' ), + array( array( 'foo' ), 'a:1:{i:0;s:3:"foo";}' ), + array( array( 'fóo' ), 'a:1:{i:0;s:4:"fóo";}' ), + + // Arrays and objects + array( array( array() ), 'a:1:{i:0;a:0:{}}' ), + array( array( array( 1 ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'x' => 1 ) ), 'a:1:{i:0;a:1:{s:1:"x";i:1;}}' ), + array( array( array( 2 => 1 ) ), 'a:1:{i:0;a:1:{i:2;i:1;}}' ), + array( array( (object)array() ), 'a:1:{i:0;a:0:{}}' ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), 'a:1:{i:0;a:1:{s:1:"x";i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + 'a:1:{i:0;a:1:{s:1:"x";i:1;}}' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), 'a:1:{i:0;a:1:{i:0;i:1;}}' ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), 'a:1:{i:0;a:2:{i:0;s:1:"a";i:1;s:1:"b";}}' ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + 'a:1:{s:7:"content";s:3:"foo";}' ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + 'a:1:{s:3:"foo";s:3:"foo";}' ), + ) ) + ); + } + + public function testCrossDomainMangling() { + $config = new HashConfig( array( 'MangleFlashPolicy' => false ) ); + $context = new RequestContext; + $context->setConfig( new MultiConfig( array( + $config, + $context->getConfig(), + ) ) ); + $main = new ApiMain( $context ); + $main->getResult()->addValue( null, null, '< Cross-Domain-Policy >' ); + + if ( !function_exists( 'wfOutputHandler' ) ) { + function wfOutputHandler( $s ) { + return $s; + } + } + + $printer = $main->createPrinterByName( 'php' ); + ob_start( 'wfOutputHandler' ); + $printer->initPrinter(); + $printer->execute(); + $printer->closePrinter(); + $ret = ob_get_clean(); + $this->assertSame( 'a:1:{i:0;s:23:"< Cross-Domain-Policy >";}', $ret ); + + $config->set( 'MangleFlashPolicy', true ); + $printer = $main->createPrinterByName( 'php' ); + ob_start( 'wfOutputHandler' ); + try { + $printer->initPrinter(); + $printer->execute(); + $printer->closePrinter(); + ob_end_clean(); + $this->fail( 'Expected exception not thrown' ); + } catch ( UsageException $ex ) { + ob_end_clean(); + $this->assertSame( + 'This response cannot be represented using format=php. See https://bugzilla.wikimedia.org/show_bug.cgi?id=66776', + $ex->getMessage(), + 'Expected exception' + ); + } + } + } diff --git a/tests/phpunit/includes/api/format/ApiFormatTestBase.php b/tests/phpunit/includes/api/format/ApiFormatTestBase.php index 5f6d53ce..cabf62b1 100644 --- a/tests/phpunit/includes/api/format/ApiFormatTestBase.php +++ b/tests/phpunit/includes/api/format/ApiFormatTestBase.php @@ -1,32 +1,64 @@ <?php -abstract class ApiFormatTestBase extends ApiTestCase { +abstract class ApiFormatTestBase extends MediaWikiTestCase { /** - * @param string $format - * @param array $params - * @param array $data - * - * @return string + * Name of the formatter being tested + * @var string */ - protected function apiRequest( $format, $params, $data = null ) { - $data = parent::doApiRequest( $params, $data, true ); - - /** @var ApiMain $module */ - $module = $data[3]; + protected $printerName; - $printer = $module->createPrinterByName( $format ); - $printer->setUnescapeAmps( false ); + /** + * Return general data to be encoded for testing + * @return array See self::testGeneralEncoding + * @throws Exception + */ + public static function provideGeneralEncoding() { + throw new Exception( 'Subclass must implement ' . __METHOD__ ); + } - $printer->initPrinter( false ); + /** + * Get the formatter output for the given input data + * @param array $params Query parameters + * @param array $data Data to encode + * @param string $class Printer class to use instead of the normal one + * @return string + * @throws Exception + */ + protected function encodeData( array $params, array $data, $class = null ) { + $context = new RequestContext; + $context->setRequest( new FauxRequest( $params, true ) ); + $main = new ApiMain( $context ); + if ( $class !== null ) { + $main->getModuleManager()->addModule( $this->printerName, 'format', $class ); + } + $result = $main->getResult(); + $result->addArrayType( null, 'default' ); + foreach ( $data as $k => $v ) { + $result->addValue( null, $k, $v ); + } - ob_start(); + $printer = $main->createPrinterByName( $this->printerName ); + $printer->initPrinter(); $printer->execute(); - $out = ob_get_clean(); - - $printer->closePrinter(); + ob_start(); + try { + $printer->closePrinter(); + return ob_get_clean(); + } catch ( Exception $ex ) { + ob_end_clean(); + throw $ex; + } + } - return $out; + /** + * @dataProvider provideGeneralEncoding + */ + public function testGeneralEncoding( array $data, $expect, array $params = array() ) { + if ( isset( $params['SKIP'] ) ) { + $this->markTestSkipped( $expect ); + } + $this->assertSame( $expect, $this->encodeData( $params, $data ) ); } } diff --git a/tests/phpunit/includes/api/format/ApiFormatTxtTest.php b/tests/phpunit/includes/api/format/ApiFormatTxtTest.php new file mode 100644 index 00000000..b0a2a960 --- /dev/null +++ b/tests/phpunit/includes/api/format/ApiFormatTxtTest.php @@ -0,0 +1,55 @@ +<?php + +/** + * @group API + * @covers ApiFormatTxt + */ +class ApiFormatTxtTest extends ApiFormatTestBase { + + protected $printerName = 'txt'; + + public static function provideGeneralEncoding() { + $warning = "\n [warnings] => Array\n (\n [txt] => Array\n (\n" . + " [*] => format=txt has been deprecated. Please use format=json instead.\n" . + " )\n\n )\n"; + + return array( + // Basic types + array( array( null ), "Array\n({$warning}\n [0] => \n)\n" ), + array( array( true ), "Array\n({$warning}\n [0] => \n)\n" ), + array( array( false ), "Array\n({$warning}\n)\n" ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), + "Array\n({$warning}\n [0] => 1\n)\n" ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), + "Array\n({$warning}\n [0] => \n)\n" ), + array( array( 42 ), "Array\n({$warning}\n [0] => 42\n)\n" ), + array( array( 42.5 ), "Array\n({$warning}\n [0] => 42.5\n)\n" ), + array( array( 1e42 ), "Array\n({$warning}\n [0] => 1.0E+42\n)\n" ), + array( array( 'foo' ), "Array\n({$warning}\n [0] => foo\n)\n" ), + array( array( 'fóo' ), "Array\n({$warning}\n [0] => fóo\n)\n" ), + + // Arrays and objects + array( array( array() ), "Array\n({$warning}\n [0] => Array\n (\n )\n\n)\n" ), + array( array( array( 1 ) ), "Array\n({$warning}\n [0] => Array\n (\n [0] => 1\n )\n\n)\n" ), + array( array( array( 'x' => 1 ) ), "Array\n({$warning}\n [0] => Array\n (\n [x] => 1\n )\n\n)\n" ), + array( array( array( 2 => 1 ) ), "Array\n({$warning}\n [0] => Array\n (\n [2] => 1\n )\n\n)\n" ), + array( array( (object)array() ), "Array\n({$warning}\n [0] => Array\n (\n )\n\n)\n" ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), "Array\n({$warning}\n [0] => Array\n (\n [0] => 1\n )\n\n)\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), "Array\n({$warning}\n [0] => Array\n (\n [0] => 1\n )\n\n)\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), "Array\n({$warning}\n [0] => Array\n (\n [x] => 1\n )\n\n)\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + "Array\n({$warning}\n [0] => Array\n (\n [0] => Array\n (\n [key] => x\n [*] => 1\n )\n\n )\n\n)\n" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), "Array\n({$warning}\n [0] => Array\n (\n [x] => 1\n )\n\n)\n" ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), "Array\n({$warning}\n [0] => Array\n (\n [0] => a\n [1] => b\n )\n\n)\n" ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + "Array\n({$warning}\n [*] => foo\n)\n" ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + "Array\n({$warning}\n [foo] => Array\n (\n [*] => foo\n )\n\n)\n" ), + ); + } + +} diff --git a/tests/phpunit/includes/api/format/ApiFormatWddxTest.php b/tests/phpunit/includes/api/format/ApiFormatWddxTest.php index d075f547..07111300 100644 --- a/tests/phpunit/includes/api/format/ApiFormatWddxTest.php +++ b/tests/phpunit/includes/api/format/ApiFormatWddxTest.php @@ -2,19 +2,79 @@ /** * @group API - * @group Database - * @group medium * @covers ApiFormatWddx */ class ApiFormatWddxTest extends ApiFormatTestBase { + protected $printerName = 'wddx'; + + public static function provideGeneralEncoding() { + if ( ApiFormatWddx::useSlowPrinter() ) { + return array( + array( array(), 'Fast Wddx printer is unavailable', array( 'SKIP' => true ) ) + ); + } + return self::provideEncoding(); + } + + public static function provideEncoding() { + $p = '<wddxPacket version=\'1.0\'><header/><data><struct><var name=\'warnings\'><struct><var name=\'wddx\'><struct><var name=\'*\'><string>format=wddx has been deprecated. Please use format=json instead.</string></var></struct></var></struct></var>'; + $s = '</struct></data></wddxPacket>'; + + return array( + // Basic types + array( array( null ), "{$p}<var name='0'><null/></var>{$s}" ), + array( array( true ), "{$p}<var name='0'><string></string></var>{$s}" ), + array( array( false ), "{$p}{$s}" ), + array( array( true, ApiResult::META_BC_BOOLS => array( 0 ) ), + "{$p}<var name='0'><boolean value='true'/></var>{$s}" ), + array( array( false, ApiResult::META_BC_BOOLS => array( 0 ) ), + "{$p}<var name='0'><boolean value='false'/></var>{$s}" ), + array( array( 42 ), "{$p}<var name='0'><number>42</number></var>{$s}" ), + array( array( 42.5 ), "{$p}<var name='0'><number>42.5</number></var>{$s}" ), + array( array( 1e42 ), "{$p}<var name='0'><number>1.0E+42</number></var>{$s}" ), + array( array( 'foo' ), "{$p}<var name='0'><string>foo</string></var>{$s}" ), + array( array( 'fóo' ), "{$p}<var name='0'><string>fóo</string></var>{$s}" ), + + // Arrays and objects + array( array( array() ), "{$p}<var name='0'><array length='0'></array></var>{$s}" ), + array( array( array( 1 ) ), "{$p}<var name='0'><array length='1'><number>1</number></array></var>{$s}" ), + array( array( array( 'x' => 1 ) ), "{$p}<var name='0'><struct><var name='x'><number>1</number></var></struct></var>{$s}" ), + array( array( array( 2 => 1 ) ), "{$p}<var name='0'><struct><var name='2'><number>1</number></var></struct></var>{$s}" ), + array( array( (object)array() ), "{$p}<var name='0'><struct></struct></var>{$s}" ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), "{$p}<var name='0'><struct><var name='0'><number>1</number></var></struct></var>{$s}" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), "{$p}<var name='0'><array length='1'><number>1</number></array></var>{$s}" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp' ) ), "{$p}<var name='0'><struct><var name='x'><number>1</number></var></struct></var>{$s}" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + "{$p}<var name='0'><array length='1'><struct><var name='key'><string>x</string></var><var name='*'><number>1</number></var></struct></array></var>{$s}" ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), "{$p}<var name='0'><struct><var name='x'><number>1</number></var></struct></var>{$s}" ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), "{$p}<var name='0'><array length='2'><string>a</string><string>b</string></array></var>{$s}" ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + "{$p}<var name='*'><string>foo</string></var>{$s}" ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + "{$p}<var name='foo'><struct><var name='*'><string>foo</string></var></struct></var>{$s}" ), + ); + } + /** - * @requires function wddx_deserialize + * @dataProvider provideEncoding */ - public function testValidSyntax( ) { - $data = $this->apiRequest( 'wddx', array( 'action' => 'query', 'meta' => 'siteinfo' ) ); + public function testSlowEncoding( array $data, $expect, array $params = array() ) { + // Adjust expectation for differences between fast and slow printers. + $expect = str_replace( '\'', '"', $expect ); + $expect = str_replace( '/>', ' />', $expect ); + $expect = '<?xml version="1.0"?>' . $expect; + + $this->assertSame( $expect, $this->encodeData( $params, $data, 'ApiFormatWddxTest_SlowWddx' ) ); + } +} - $this->assertInternalType( 'array', wddx_deserialize( $data ) ); - $this->assertGreaterThan( 0, count( (array)$data ) ); +class ApiFormatWddxTest_SlowWddx extends ApiFormatWddx { + public static function useSlowPrinter() { + return true; } } diff --git a/tests/phpunit/includes/api/format/ApiFormatXmlTest.php b/tests/phpunit/includes/api/format/ApiFormatXmlTest.php new file mode 100644 index 00000000..7babaedb --- /dev/null +++ b/tests/phpunit/includes/api/format/ApiFormatXmlTest.php @@ -0,0 +1,119 @@ +<?php + +/** + * @group API + * @group Database + * @covers ApiFormatXml + */ +class ApiFormatXmlTest extends ApiFormatTestBase { + + protected $printerName = 'xml'; + + public static function setUpBeforeClass() { + parent::setUpBeforeClass(); + $page = WikiPage::factory( Title::newFromText( 'MediaWiki:ApiFormatXmlTest.xsl' ) ); + $page->doEditContent( new WikitextContent( + '<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" />' + ), 'Summary' ); + $page = WikiPage::factory( Title::newFromText( 'MediaWiki:ApiFormatXmlTest' ) ); + $page->doEditContent( new WikitextContent( 'Bogus' ), 'Summary' ); + $page = WikiPage::factory( Title::newFromText( 'ApiFormatXmlTest' ) ); + $page->doEditContent( new WikitextContent( 'Bogus' ), 'Summary' ); + } + + public static function provideGeneralEncoding() { + return array( + // Basic types + array( array( null, 'a' => null ), '<?xml version="1.0"?><api><_v _idx="0" /></api>' ), + array( array( true, 'a' => true ), '<?xml version="1.0"?><api a=""><_v _idx="0">true</_v></api>' ), + array( array( false, 'a' => false ), '<?xml version="1.0"?><api><_v _idx="0">false</_v></api>' ), + array( array( true, 'a' => true, ApiResult::META_BC_BOOLS => array( 0, 'a' ) ), + '<?xml version="1.0"?><api a=""><_v _idx="0">1</_v></api>' ), + array( array( false, 'a' => false, ApiResult::META_BC_BOOLS => array( 0, 'a' ) ), + '<?xml version="1.0"?><api><_v _idx="0"></_v></api>' ), + array( array( 42, 'a' => 42 ), '<?xml version="1.0"?><api a="42"><_v _idx="0">42</_v></api>' ), + array( array( 42.5, 'a' => 42.5 ), '<?xml version="1.0"?><api a="42.5"><_v _idx="0">42.5</_v></api>' ), + array( array( 1e42, 'a' => 1e42 ), '<?xml version="1.0"?><api a="1.0E+42"><_v _idx="0">1.0E+42</_v></api>' ), + array( array( 'foo', 'a' => 'foo' ), '<?xml version="1.0"?><api a="foo"><_v _idx="0">foo</_v></api>' ), + array( array( 'fóo', 'a' => 'fóo' ), '<?xml version="1.0"?><api a="fóo"><_v _idx="0">fóo</_v></api>' ), + + // Arrays and objects + array( array( array() ), '<?xml version="1.0"?><api><_v /></api>' ), + array( array( array( 'x' => 1 ) ), '<?xml version="1.0"?><api><_v x="1" /></api>' ), + array( array( array( 2 => 1 ) ), '<?xml version="1.0"?><api><_v><_v _idx="2">1</_v></_v></api>' ), + array( array( (object)array() ), '<?xml version="1.0"?><api><_v /></api>' ), + array( array( array( 1, ApiResult::META_TYPE => 'assoc' ) ), '<?xml version="1.0"?><api><_v><_v _idx="0">1</_v></_v></api>' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'array' ) ), '<?xml version="1.0"?><api><_v><_v>1</_v></_v></api>' ), + array( array( array( 'x' => 1, 'y' => array( 'z' => 1 ), ApiResult::META_TYPE => 'kvp' ) ), + '<?xml version="1.0"?><api><_v><_v _name="x" xml:space="preserve">1</_v><_v _name="y"><z xml:space="preserve">1</z></_v></_v></api>' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'kvp', ApiResult::META_INDEXED_TAG_NAME => 'i', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + '<?xml version="1.0"?><api><_v><i key="x" xml:space="preserve">1</i></_v></api>' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCkvp', ApiResult::META_KVP_KEY_NAME => 'key' ) ), + '<?xml version="1.0"?><api><_v><_v key="x" xml:space="preserve">1</_v></_v></api>' ), + array( array( array( 'x' => 1, ApiResult::META_TYPE => 'BCarray' ) ), '<?xml version="1.0"?><api><_v x="1" /></api>' ), + array( array( array( 'a', 'b', ApiResult::META_TYPE => 'BCassoc' ) ), '<?xml version="1.0"?><api><_v><_v _idx="0">a</_v><_v _idx="1">b</_v></_v></api>' ), + + // Content + array( array( 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + '<?xml version="1.0"?><api xml:space="preserve">foo</api>' ), + + // Specified element name + array( array( 'foo', 'bar', ApiResult::META_INDEXED_TAG_NAME => 'itn' ), + '<?xml version="1.0"?><api><itn>foo</itn><itn>bar</itn></api>' ), + + // Subelements + array( array( 'a' => 1, 's' => 1, '_subelements' => array( 's' ) ), + '<?xml version="1.0"?><api a="1"><s xml:space="preserve">1</s></api>' ), + + // Content and subelement + array( array( 'a' => 1, 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + '<?xml version="1.0"?><api a="1" xml:space="preserve">foo</api>' ), + array( array( 's' => array(), 'content' => 'foo', ApiResult::META_CONTENT => 'content' ), + '<?xml version="1.0"?><api><s /><content xml:space="preserve">foo</content></api>' ), + array( + array( + 's' => 1, + 'content' => 'foo', + ApiResult::META_CONTENT => 'content', + ApiResult::META_SUBELEMENTS => array( 's' ) + ), + '<?xml version="1.0"?><api><s xml:space="preserve">1</s><content xml:space="preserve">foo</content></api>' + ), + + // BC Subelements + array( array( 'foo' => 'foo', ApiResult::META_BC_SUBELEMENTS => array( 'foo' ) ), + '<?xml version="1.0"?><api><foo xml:space="preserve">foo</foo></api>' ), + + // Name mangling + array( array( 'foo.bar' => 1 ), '<?xml version="1.0"?><api foo.bar="1" />' ), + array( array( '' => 1 ), '<?xml version="1.0"?><api _="1" />' ), + array( array( 'foo bar' => 1 ), '<?xml version="1.0"?><api _foo.20.bar="1" />' ), + array( array( 'foo:bar' => 1 ), '<?xml version="1.0"?><api _foo.3A.bar="1" />' ), + array( array( 'foo%.bar' => 1 ), '<?xml version="1.0"?><api _foo.25..2E.bar="1" />' ), + array( array( '4foo' => 1, 'foo4' => 1 ), '<?xml version="1.0"?><api _4foo="1" foo4="1" />' ), + array( array( "foo\xe3\x80\x80bar" => 1 ), '<?xml version="1.0"?><api _foo.3000.bar="1" />' ), + array( array( 'foo:bar' => 1, ApiResult::META_PRESERVE_KEYS => array( 'foo:bar' ) ), + '<?xml version="1.0"?><api foo:bar="1" />' ), + array( array( 'a', 'b', ApiResult::META_INDEXED_TAG_NAME => 'foo bar' ), + '<?xml version="1.0"?><api><_foo.20.bar>a</_foo.20.bar><_foo.20.bar>b</_foo.20.bar></api>' ), + + // includenamespace param + array( array( 'x' => 'foo' ), '<?xml version="1.0"?><api x="foo" xmlns="http://www.mediawiki.org/xml/api/" />', + array( 'includexmlnamespace' => 1 ) ), + + // xslt param + array( array(), '<?xml version="1.0"?><api><warnings><xml xml:space="preserve">Invalid or non-existent stylesheet specified</xml></warnings></api>', + array( 'xslt' => 'DoesNotExist' ) ), + array( array(), '<?xml version="1.0"?><api><warnings><xml xml:space="preserve">Stylesheet should be in the MediaWiki namespace.</xml></warnings></api>', + array( 'xslt' => 'ApiFormatXmlTest' ) ), + array( array(), '<?xml version="1.0"?><api><warnings><xml xml:space="preserve">Stylesheet should have .xsl extension.</xml></warnings></api>', + array( 'xslt' => 'MediaWiki:ApiFormatXmlTest' ) ), + array( array(), + '<?xml version="1.0"?><?xml-stylesheet href="' . + htmlspecialchars( Title::newFromText( 'MediaWiki:ApiFormatXmlTest.xsl' )->getLocalURL( 'action=raw' ) ) . + '" type="text/xsl" ?><api />', + array( 'xslt' => 'MediaWiki:ApiFormatXmlTest.xsl' ) ), + ); + } + +} diff --git a/tests/phpunit/includes/api/query/ApiQueryBasicTest.php b/tests/phpunit/includes/api/query/ApiQueryBasicTest.php index e486c4f4..fa0e4cb5 100644 --- a/tests/phpunit/includes/api/query/ApiQueryBasicTest.php +++ b/tests/phpunit/includes/api/query/ApiQueryBasicTest.php @@ -23,8 +23,6 @@ * @file */ -require_once 'ApiQueryTestBase.php'; - /** * These tests validate basic functionality of the api query module * diff --git a/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php b/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php index 347cd6f8..cd735223 100644 --- a/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php +++ b/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php @@ -18,8 +18,6 @@ * http://www.gnu.org/copyleft/gpl.html */ -require_once 'ApiQueryContinueTestBase.php'; - /** * @group API * @group Database @@ -62,7 +60,8 @@ class ApiQueryContinue2Test extends ApiQueryContinueTestBase { ); }; // generator + 1 prop + 1 list - $data = $this->query( $mk( 99, 99, true ), 1, 'g1p', false ); + $data = $this->query( $mk( 99, 99, true ), 1, 'g1p', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, 1, true ), 6, 'g1p-11t' ); $this->checkC( $data, $mk( 2, 2, true ), 3, 'g1p-22t' ); $this->checkC( $data, $mk( 1, 1, false ), 6, 'g1p-11f' ); diff --git a/tests/phpunit/includes/api/query/ApiQueryContinueTest.php b/tests/phpunit/includes/api/query/ApiQueryContinueTest.php index 03797901..d441f4c4 100644 --- a/tests/phpunit/includes/api/query/ApiQueryContinueTest.php +++ b/tests/phpunit/includes/api/query/ApiQueryContinueTest.php @@ -18,8 +18,6 @@ * http://www.gnu.org/copyleft/gpl.html */ -require_once 'ApiQueryContinueTestBase.php'; - /** * These tests validate the new continue functionality of the api query module by * doing multiple requests with varying parameters, merging the results, and checking @@ -68,7 +66,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { 'aplimit' => "$l", ); }; - $data = $this->query( $mk( 99 ), 1, '1L', false ); + $data = $this->query( $mk( 99 ), 1, '1L', false ) + + array( 'batchcomplete' => true ); // 1 list $this->checkC( $data, $mk( 1 ), 5, '1L-1' ); @@ -95,7 +94,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // 2 lists - $data = $this->query( $mk( 99, 99 ), 1, '2L', false ); + $data = $this->query( $mk( 99, 99 ), 1, '2L', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, 1 ), 5, '2L-11' ); $this->checkC( $data, $mk( 2, 2 ), 3, '2L-22' ); $this->checkC( $data, $mk( 3, 3 ), 2, '2L-33' ); @@ -119,7 +119,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // generator + 1 prop - $data = $this->query( $mk( 99, 99 ), 1, 'G1P', false ); + $data = $this->query( $mk( 99, 99 ), 1, 'G1P', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, 1 ), 11, 'G1P-11' ); $this->checkC( $data, $mk( 2, 2 ), 6, 'G1P-22' ); $this->checkC( $data, $mk( 3, 3 ), 4, 'G1P-33' ); @@ -144,7 +145,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // generator + 2 props - $data = $this->query( $mk( 99, 99, 99 ), 1, 'G2P', false ); + $data = $this->query( $mk( 99, 99, 99 ), 1, 'G2P', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, 1, 1 ), 16, 'G2P-111' ); $this->checkC( $data, $mk( 2, 2, 2 ), 9, 'G2P-222' ); $this->checkC( $data, $mk( 3, 3, 3 ), 6, 'G2P-333' ); @@ -177,7 +179,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // generator + 1 prop + 1 list - $data = $this->query( $mk( 99, 99, 99 ), 1, 'G1P1L', false ); + $data = $this->query( $mk( 99, 99, 99 ), 1, 'G1P1L', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, 1, 1 ), 11, 'G1P1L-111' ); $this->checkC( $data, $mk( 2, 2, 2 ), 6, 'G1P1L-222' ); $this->checkC( $data, $mk( 3, 3, 3 ), 4, 'G1P1L-333' ); @@ -214,7 +217,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // generator + 1 prop + 1 list - $data = $this->query( $mk( 99, 99, 99, 99, 99 ), 1, 'G2P2L1M', false ); + $data = $this->query( $mk( 99, 99, 99, 99, 99 ), 1, 'G2P2L1M', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, 1, 1, 1, 1 ), 16, 'G2P2L1M-11111' ); $this->checkC( $data, $mk( 2, 2, 2, 2, 2 ), 9, 'G2P2L1M-22222' ); $this->checkC( $data, $mk( 3, 3, 3, 3, 3 ), 6, 'G2P2L1M-33333' ); @@ -244,7 +248,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // generator + 1 prop - $data = $this->query( $mk( 99, true, 99, true ), 1, 'G=P', false ); + $data = $this->query( $mk( 99, true, 99, true ), 1, 'G=P', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, true, 1, true ), 4, 'G=P-1t1t' ); $this->checkC( $data, $mk( 2, true, 2, true ), 2, 'G=P-2t2t' ); @@ -290,7 +295,8 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase { ); }; // generator + 1 list - $data = $this->query( $mk( 99, true, 99, true ), 1, 'G=L', false ); + $data = $this->query( $mk( 99, true, 99, true ), 1, 'G=L', false ) + + array( 'batchcomplete' => true ); $this->checkC( $data, $mk( 1, true, 1, true ), 5, 'G=L-1t1t' ); $this->checkC( $data, $mk( 2, true, 2, true ), 3, 'G=L-2t2t' ); diff --git a/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php b/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php index bce62685..ce2f70de 100644 --- a/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php +++ b/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php @@ -21,9 +21,6 @@ * * @file */ - -require_once 'ApiQueryTestBase.php'; - abstract class ApiQueryContinueTestBase extends ApiQueryTestBase { /** @@ -62,6 +59,8 @@ abstract class ApiQueryContinueTestBase extends ApiQueryTestBase { } if ( $useContinue && !isset( $params['continue'] ) ) { $params['continue'] = ''; + } else { + $params['rawcontinue'] = '1'; } $count = 0; $result = array(); diff --git a/tests/phpunit/includes/api/query/ApiQueryTest.php b/tests/phpunit/includes/api/query/ApiQueryTest.php index bba22c77..5f061b50 100644 --- a/tests/phpunit/includes/api/query/ApiQueryTest.php +++ b/tests/phpunit/includes/api/query/ApiQueryTest.php @@ -7,32 +7,21 @@ * @covers ApiQuery */ class ApiQueryTest extends ApiTestCase { - /** - * @var array Storage for $wgHooks - */ - protected $hooks; - protected function setUp() { - global $wgHooks; - parent::setUp(); $this->doLogin(); - // Setup en: as interwiki prefix - $this->hooks = $wgHooks; - $wgHooks['InterwikiLoadPrefix'][] = function ( $prefix, &$data ) { - if ( $prefix == 'apiquerytestiw' ) { - $data = array( 'iw_url' => 'wikipedia' ); - } - return false; - }; - } - - protected function tearDown() { - global $wgHooks; - $wgHooks = $this->hooks; - - parent::tearDown(); + // Setup apiquerytestiw: as interwiki prefix + $this->setMwGlobals( 'wgHooks', array( + 'InterwikiLoadPrefix' => array( + function ( $prefix, &$data ) { + if ( $prefix == 'apiquerytestiw' ) { + $data = array( 'iw_url' => 'wikipedia' ); + } + return false; + } + ) + ) ); } public function testTitlesGetNormalized() { @@ -127,4 +116,27 @@ class ApiQueryTest extends ApiTestCase { array( 'apiquerytestiw:foo', NS_MAIN, null, true ), ); } + + /** + * Test if all classes in the query module manager exists + */ + public function testClassNamesInModuleManager() { + global $wgAutoloadLocalClasses, $wgAutoloadClasses; + + // wgAutoloadLocalClasses has precedence, just like in includes/AutoLoader.php + $classes = $wgAutoloadLocalClasses + $wgAutoloadClasses; + + $api = new ApiMain( + new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) ) + ); + $queryApi = new ApiQuery( $api, 'query' ); + $modules = $queryApi->getModuleManager()->getNamesWithClasses(); + foreach( $modules as $name => $class ) { + $this->assertArrayHasKey( + $class, + $classes, + 'Class ' . $class . ' for api module ' . $name . ' not in autoloader (with exact case)' + ); + } + } } diff --git a/tests/phpunit/includes/api/query/ApiQueryTestBase.php b/tests/phpunit/includes/api/query/ApiQueryTestBase.php index 56c15b23..dabf72e0 100644 --- a/tests/phpunit/includes/api/query/ApiQueryTestBase.php +++ b/tests/phpunit/includes/api/query/ApiQueryTestBase.php @@ -88,19 +88,26 @@ STR; /** * Checks that the request's result matches the expected results. * @param array $values Array is a two element array( request, expected_results ) - * @throws Exception + * @param array $session + * @param bool $appendModule + * @param User $user */ - protected function check( $values ) { + protected function check( $values, array $session = null, + $appendModule = false, User $user = null + ) { list( $req, $exp ) = $this->validateRequestExpectedPair( $values ); if ( !array_key_exists( 'action', $req ) ) { $req['action'] = 'query'; } + if ( !array_key_exists( 'continue', $req ) ) { + $req['rawcontinue'] = '1'; + } foreach ( $req as &$val ) { if ( is_array( $val ) ) { $val = implode( '|', array_unique( $val ) ); } } - $result = $this->doApiRequest( $req ); + $result = $this->doApiRequest( $req, $session, $appendModule, $user ); $this->assertResult( array( 'query' => $exp ), $result[0], $req ); } @@ -113,9 +120,16 @@ STR; if ( is_array( $message ) ) { $message = http_build_query( $message ); } + + // FIXME: once we migrate to phpunit 4.1+, hardcode ComparisonFailure exception use + $compEx = 'SebastianBergmann\Comparator\ComparisonFailure'; + if ( !class_exists( $compEx ) ) { + $compEx = 'PHPUnit_Framework_ComparisonFailure'; + } + throw new PHPUnit_Framework_ExpectationFailedException( $e->getMessage() . "\nRequest: $message", - new PHPUnit_Framework_ComparisonFailure( + new $compEx( $exp, $result, print_r( $exp, true ), diff --git a/tests/phpunit/includes/cache/GenderCacheTest.php b/tests/phpunit/includes/cache/GenderCacheTest.php index ce2db5d7..04fb00d9 100644 --- a/tests/phpunit/includes/cache/GenderCacheTest.php +++ b/tests/phpunit/includes/cache/GenderCacheTest.php @@ -6,14 +6,10 @@ */ class GenderCacheTest extends MediaWikiLangTestCase { - protected function setUp() { - global $wgDefaultUserOptions; - parent::setUp(); + function addDBData() { //ensure the correct default gender - $wgDefaultUserOptions['gender'] = 'unknown'; - } + $this->mergeMwGlobalArrayValue( 'wgDefaultUserOptions', array( 'gender' => 'unknown' ) ); - function addDBData() { $user = User::newFromName( 'UTMale' ); if ( $user->getID() == 0 ) { $user->addToDatabase(); diff --git a/tests/phpunit/includes/cache/LocalisationCacheTest.php b/tests/phpunit/includes/cache/LocalisationCacheTest.php index fc06a501..a0d308aa 100644 --- a/tests/phpunit/includes/cache/LocalisationCacheTest.php +++ b/tests/phpunit/includes/cache/LocalisationCacheTest.php @@ -7,18 +7,32 @@ */ class LocalisationCacheTest extends MediaWikiTestCase { protected function setUp() { - global $IP; - parent::setUp(); $this->setMwGlobals( array( - 'wgMessagesDirs' => array( "$IP/tests/phpunit/data/localisationcache" ), 'wgExtensionMessagesFiles' => array(), 'wgHooks' => array(), ) ); } + /** + * @return PHPUnit_Framework_MockObject_MockObject|LocalisationCache + */ + protected function getMockLocalisationCache() { + global $IP; + $lc = $this->getMockBuilder( 'LocalisationCache' ) + ->setConstructorArgs( array( array( 'store' => 'detect' ) ) ) + ->setMethods( array( 'getMessagesDirs' ) ) + ->getMock(); + $lc->expects( $this->any() )->method( 'getMessagesDirs' ) + ->will( $this->returnValue( + array( "$IP/tests/phpunit/data/localisationcache" ) + ) ); + + return $lc; + } + public function testPuralRulesFallback() { - $cache = new LocalisationCache( array( 'store' => 'detect' ) ); + $cache = $this->getMockLocalisationCache(); $this->assertEquals( $cache->getItem( 'ar', 'pluralRules' ), @@ -46,7 +60,7 @@ class LocalisationCacheTest extends MediaWikiTestCase { } public function testRecacheFallbacks() { - $lc = new LocalisationCache( array( 'store' => 'detect' ) ); + $lc = $this->getMockLocalisationCache(); $lc->recache( 'uk' ); $this->assertEquals( array( @@ -60,23 +74,25 @@ class LocalisationCacheTest extends MediaWikiTestCase { } public function testRecacheFallbacksWithHooks() { - global $wgHooks; - // Use hook to provide updates for messages. This is what the // LocalisationUpdate extension does. See bug 68781. - $wgHooks['LocalisationCacheRecacheFallback'][] = function ( - LocalisationCache $lc, - $code, - array &$cache - ) { - if ( $code === 'ru' ) { - $cache['messages']['present-uk'] = 'ru-override'; - $cache['messages']['present-ru'] = 'ru-override'; - $cache['messages']['present-en'] = 'ru-override'; - } - }; + $this->mergeMwGlobalArrayValue( 'wgHooks', array( + 'LocalisationCacheRecacheFallback' => array( + function ( + LocalisationCache $lc, + $code, + array &$cache + ) { + if ( $code === 'ru' ) { + $cache['messages']['present-uk'] = 'ru-override'; + $cache['messages']['present-ru'] = 'ru-override'; + $cache['messages']['present-en'] = 'ru-override'; + } + } + ) + ) ); - $lc = new LocalisationCache( array( 'store' => 'detect' ) ); + $lc = $this->getMockLocalisationCache(); $lc->recache( 'uk' ); $this->assertEquals( array( diff --git a/tests/phpunit/includes/cache/RedisBloomCacheTest.php b/tests/phpunit/includes/cache/RedisBloomCacheTest.php deleted file mode 100644 index 3d491e90..00000000 --- a/tests/phpunit/includes/cache/RedisBloomCacheTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * Test for BloomCacheRedis class. - * - * @TODO: some generic base "redis test server conf" for all testing? - * - * @covers BloomCacheRedis - * @group Cache - */ -class BloomCacheRedisTest extends MediaWikiTestCase { - private static $suffix; - - protected function setUp() { - parent::setUp(); - - self::$suffix = self::$suffix ? : mt_rand(); - - $fcache = BloomCache::get( 'main' ); - if ( $fcache instanceof BloomCacheRedis ) { - $fcache->delete( "unit-testing-" . self::$suffix ); - } else { - $this->markTestSkipped( 'The main bloom cache is not redis.' ); - } - } - - public function testBloomCache() { - $key = "unit-testing-" . self::$suffix; - $fcache = BloomCache::get( 'main' ); - $count = 1500; - - $this->assertTrue( $fcache->delete( $key ), "OK delete of filter '$key'." ); - $this->assertTrue( $fcache->init( $key, $count, .001 ), "OK init of filter '$key'." ); - - $members = array(); - for ( $i = 0; $i < $count; ++$i ) { - $members[] = "$i-value-$i"; - } - $this->assertTrue( $fcache->add( $key, $members ), "Addition of members to '$key' OK." ); - - for ( $i = 0; $i < $count; ++$i ) { - $this->assertTrue( $fcache->isHit( $key, "$i-value-$i" ), "Hit on member '$i-value-$i'." ); - } - - $falsePositives = array(); - for ( $i = $count; $i < 2 * $count; ++$i ) { - if ( $fcache->isHit( $key, "value$i" ) ) { - $falsePositives[] = "value$i"; - } - } - - $eFalsePositives = array( - 'value1763', - 'value2245', - 'value2353', - 'value2791', - 'value2898', - 'value2975' - ); - $this->assertEquals( $eFalsePositives, $falsePositives, "Correct number of false positives found." ); - } - - protected function tearDown() { - parent::tearDown(); - - $fcache = BloomCache::get( 'main' ); - if ( $fcache instanceof BloomCacheRedis ) { - $fcache->delete( "unit-testing-" . self::$suffix ); - } - } -} diff --git a/tests/phpunit/includes/changes/EnhancedChangesListTest.php b/tests/phpunit/includes/changes/EnhancedChangesListTest.php index 40a11d2d..a14a50d2 100644 --- a/tests/phpunit/includes/changes/EnhancedChangesListTest.php +++ b/tests/phpunit/includes/changes/EnhancedChangesListTest.php @@ -5,7 +5,6 @@ * * @group Database * - * @licence GNU GPL v2+ * @author Katie Filbert < aude.wiki@gmail.com > */ class EnhancedChangesListTest extends MediaWikiLangTestCase { @@ -31,7 +30,7 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase { 'mediawiki.special.changeslist', $styleModules, 'has mediawiki.special.changeslist' - ); + ); $this->assertContains( 'mediawiki.special.changeslist.enhanced', @@ -75,10 +74,10 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase { $this->assertEquals( '', $html ); } - /** - * @todo more tests for actual formatting, this is more of a smoke test - */ - public function testEndRecentChangesList() { + /** + * @todo more tests for actual formatting, this is more of a smoke test + */ + public function testEndRecentChangesList() { $enhancedChangesList = $this->newEnhancedChangesList(); $enhancedChangesList->beginRecentChangesList(); @@ -92,7 +91,7 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase { preg_match_all( '/td class="mw-enhanced-rc-nested"/', $html, $matches ); $this->assertCount( 2, $matches[0] ); - } + } /** * @return EnhancedChangesList diff --git a/tests/phpunit/includes/changes/OldChangesListTest.php b/tests/phpunit/includes/changes/OldChangesListTest.php index 2ea9f33e..311ad89c 100644 --- a/tests/phpunit/includes/changes/OldChangesListTest.php +++ b/tests/phpunit/includes/changes/OldChangesListTest.php @@ -9,7 +9,6 @@ * * @group Database * - * @licence GNU GPL v2+ * @author Katie Filbert < aude.wiki@gmail.com > */ class OldChangesListTest extends MediaWikiLangTestCase { diff --git a/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php b/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php index ee1a4d0e..0b877275 100644 --- a/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php +++ b/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php @@ -5,7 +5,6 @@ * * @group Database * - * @licence GNU GPL v2+ * @author Katie Filbert < aude.wiki@gmail.com > */ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase { diff --git a/tests/phpunit/includes/changes/RecentChangeTest.php b/tests/phpunit/includes/changes/RecentChangeTest.php index 98903f1e..b3cb7b52 100644 --- a/tests/phpunit/includes/changes/RecentChangeTest.php +++ b/tests/phpunit/includes/changes/RecentChangeTest.php @@ -35,6 +35,7 @@ class RecentChangeTest extends MediaWikiTestCase { * Should cover the following log actions (which are most commonly used by bots): * - block/block * - block/unblock + * - block/reblock * - delete/delete * - delete/restore * - newusers/create @@ -46,6 +47,9 @@ class RecentChangeTest extends MediaWikiTestCase { * - protect/modifyprotect * - protect/unprotect * - upload/upload + * - merge/merge + * - import/upload + * - import/interwiki * * As well as the following Auto Edit Summaries: * - blank @@ -62,9 +66,13 @@ class RecentChangeTest extends MediaWikiTestCase { # block/block $this->assertIRCComment( - $this->context->msg( 'blocklogentry', 'SomeTitle' )->plain() . $sep . $this->user_comment, + $this->context->msg( 'blocklogentry', 'SomeTitle', 'duration', '(flags)' )->plain() + . $sep . $this->user_comment, 'block', 'block', - array(), + array( + '5::duration' => 'duration', + '6::flags' => 'flags', + ), $this->user_comment ); # block/unblock @@ -74,6 +82,17 @@ class RecentChangeTest extends MediaWikiTestCase { array(), $this->user_comment ); + # block/reblock + $this->assertIRCComment( + $this->context->msg( 'reblock-logentry', 'SomeTitle', 'duration', '(flags)' )->plain() + . $sep . $this->user_comment, + 'block', 'reblock', + array( + '5::duration' => 'duration', + '6::flags' => 'flags', + ), + $this->user_comment + ); } /** @@ -230,6 +249,48 @@ class RecentChangeTest extends MediaWikiTestCase { } /** + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeMerge() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # merge/merge + $this->assertIRCComment( + $this->context->msg( 'pagemerge-logentry', 'SomeTitle', 'Dest', 'timestamp' )->plain() + . $sep . $this->user_comment, + 'merge', 'merge', + array( + '4::dest' => 'Dest', + '5::mergepoint' => 'timestamp', + ), + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeImport() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # import/upload + $this->assertIRCComment( + $this->context->msg( 'import-logentry-upload', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'import', 'upload', + array(), + $this->user_comment + ); + + # import/interwiki + $this->assertIRCComment( + $this->context->msg( 'import-logentry-interwiki', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'import', 'interwiki', + array(), + $this->user_comment + ); + } + + /** * @todo Emulate these edits somehow and extract * raw edit summary from RecentChange object * -- diff --git a/tests/phpunit/includes/changes/TestRecentChangesHelper.php b/tests/phpunit/includes/changes/TestRecentChangesHelper.php index ad643274..2506087b 100644 --- a/tests/phpunit/includes/changes/TestRecentChangesHelper.php +++ b/tests/phpunit/includes/changes/TestRecentChangesHelper.php @@ -3,7 +3,6 @@ /** * Helper for generating test recent changes entries. * - * @licence GNU GPL v2+ * @author Katie Filbert < aude.wiki@gmail.com > */ class TestRecentChangesHelper { diff --git a/tests/phpunit/includes/composer/ComposerVersionNormalizerTest.php b/tests/phpunit/includes/composer/ComposerVersionNormalizerTest.php index 3f887dc0..2fa11eaf 100644 --- a/tests/phpunit/includes/composer/ComposerVersionNormalizerTest.php +++ b/tests/phpunit/includes/composer/ComposerVersionNormalizerTest.php @@ -5,7 +5,6 @@ * * @group ComposerHooks * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class ComposerVersionNormalizerTest extends PHPUnit_Framework_TestCase { diff --git a/tests/phpunit/includes/config/GlobalVarConfigTest.php b/tests/phpunit/includes/config/GlobalVarConfigTest.php index 70b9e684..28068d5e 100644 --- a/tests/phpunit/includes/config/GlobalVarConfigTest.php +++ b/tests/phpunit/includes/config/GlobalVarConfigTest.php @@ -87,34 +87,10 @@ class GlobalVarConfigTest extends MediaWikiTestCase { $this->assertEquals( $config->get( $name ), $expected ); } - public static function provideSet() { - return array( - array( 'Foo', 'wg', 'wgFoo' ), - array( 'SomethingRandom', 'wg', 'wgSomethingRandom' ), - array( 'FromAnExtension', 'eg', 'egFromAnExtension' ), - array( 'NoPrefixHere', '', 'NoPrefixHere' ), - ); - } - private function maybeStashGlobal( $var ) { if ( array_key_exists( $var, $GLOBALS ) ) { // Will be reset after this test is over $this->stashMwGlobals( $var ); } } - - /** - * @dataProvider provideSet - * @covers GlobalVarConfig::set - * @covers GlobalVarConfig::setWithPrefix - */ - public function testSet( $name, $prefix, $var ) { - $this->hideDeprecated( 'GlobalVarConfig::set' ); - $this->maybeStashGlobal( $var ); - $config = new GlobalVarConfig( $prefix ); - $random = wfRandomString(); - $config->set( $name, $random ); - $this->assertArrayHasKey( $var, $GLOBALS ); - $this->assertEquals( $random, $GLOBALS[$var] ); - } } diff --git a/tests/phpunit/includes/config/HashConfigTest.php b/tests/phpunit/includes/config/HashConfigTest.php index 3ad3bfbd..06973b09 100644 --- a/tests/phpunit/includes/config/HashConfigTest.php +++ b/tests/phpunit/includes/config/HashConfigTest.php @@ -60,4 +60,4 @@ class HashConfigTest extends MediaWikiTestCase { $conf->set( 'one', '3' ); $this->assertEquals( '3', $conf->get( 'one' ) ); } -}
\ No newline at end of file +} diff --git a/tests/phpunit/includes/content/ContentHandlerTest.php b/tests/phpunit/includes/content/ContentHandlerTest.php index f7449734..988a59ee 100644 --- a/tests/phpunit/includes/content/ContentHandlerTest.php +++ b/tests/phpunit/includes/content/ContentHandlerTest.php @@ -2,11 +2,6 @@ /** * @group ContentHandler - * @group Database - * - * @note Declare that we are using the database, because otherwise we'll fail in - * the "databaseless" test run. This is because the LinkHolderArray used by the - * parser needs database access. */ class ContentHandlerTest extends MediaWikiTestCase { @@ -36,6 +31,8 @@ class ContentHandlerTest extends MediaWikiTestCase { // Reset namespace cache MWNamespace::getCanonicalNamespaces( true ); $wgContLang->resetNamespaces(); + // And LinkCache + LinkCache::destroySingleton(); } protected function tearDown() { @@ -44,6 +41,8 @@ class ContentHandlerTest extends MediaWikiTestCase { // Reset namespace cache MWNamespace::getCanonicalNamespaces( true ); $wgContLang->resetNamespaces(); + // And LinkCache + LinkCache::destroySingleton(); parent::tearDown(); } @@ -83,6 +82,7 @@ class ContentHandlerTest extends MediaWikiTestCase { */ public function testGetForTitle( $title, $expectedContentModel ) { $title = Title::newFromText( $title ); + LinkCache::singleton()->addBadLinkObj( $title ); $handler = ContentHandler::getForTitle( $title ); $this->assertEquals( $expectedContentModel, $handler->getModelID() ); } @@ -139,6 +139,7 @@ class ContentHandlerTest extends MediaWikiTestCase { public function testGetPageLanguage( $title, $expected ) { if ( is_string( $title ) ) { $title = Title::newFromText( $title ); + LinkCache::singleton()->addBadLinkObj( $title ); } $expected = wfGetLangObj( $expected ); @@ -292,7 +293,7 @@ class ContentHandlerTest extends MediaWikiTestCase { $expectedModelId, $expectedNativeData, $shouldFail ) { $title = Title::newFromText( $title ); - + LinkCache::singleton()->addBadLinkObj( $title ); try { $content = ContentHandler::makeContent( $data, $title, $modelId, $format ); @@ -317,6 +318,8 @@ class ContentHandlerTest extends MediaWikiTestCase { * page. */ public function testGetAutosummary() { + $this->setMwGlobals( 'wgContLang', Language::factory( 'en' ) ); + $content = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT ); $title = Title::newFromText( 'Help:Test' ); // Create a new content object with no content diff --git a/tests/phpunit/includes/content/JsonContentTest.php b/tests/phpunit/includes/content/JsonContentTest.php index 77b542f4..cccfe7b1 100644 --- a/tests/phpunit/includes/content/JsonContentTest.php +++ b/tests/phpunit/includes/content/JsonContentTest.php @@ -6,48 +6,85 @@ */ class JsonContentTest extends MediaWikiLangTestCase { - /** - * @dataProvider provideValidConstruction - */ - public function testValidConstruct( $text, $modelId, $isValid, $expected ) { - $obj = new JsonContent( $text, $modelId ); - $this->assertEquals( $isValid, $obj->isValid() ); - $this->assertEquals( $expected, $obj->getJsonData() ); + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( 'wgWellFormedXml', true ); } public static function provideValidConstruction() { return array( - array( 'foo', CONTENT_MODEL_JSON, false, null ), - array( FormatJson::encode( array() ), CONTENT_MODEL_JSON, true, array() ), - array( FormatJson::encode( array( 'foo' ) ), CONTENT_MODEL_JSON, true, array( 'foo' ) ), + array( 'foo', false, null ), + array( '[]', true, array() ), + array( '{}', true, (object)array() ), + array( '""', true, '' ), + array( '"0"', true, '0' ), + array( '"bar"', true, 'bar' ), + array( '0', true, '0' ), + array( '{ "0": "bar" }', true, (object)array( 'bar' ) ), ); } /** - * @dataProvider provideDataToEncode + * @dataProvider provideValidConstruction */ - public function testBeautifyUsesFormatJson( $data ) { - $obj = new JsonContent( FormatJson::encode( $data ) ); - $this->assertEquals( FormatJson::encode( $data, true ), $obj->beautifyJSON() ); + public function testIsValid( $text, $isValid, $expected ) { + $obj = new JsonContent( $text, CONTENT_MODEL_JSON ); + $this->assertEquals( $isValid, $obj->isValid() ); + $this->assertEquals( $expected, $obj->getData()->getValue() ); } public static function provideDataToEncode() { return array( - array( array() ), - array( array( 'foo' ) ), - array( array( 'foo', 'bar' ) ), - array( array( 'baz' => 'foo', 'bar' ) ), - array( array( 'baz' => 1000, 'bar' ) ), + array( + // Round-trip empty array + '[]', + '[]', + ), + array( + // Round-trip empty object + '{}', + '{}', + ), + array( + // Round-trip empty array/object (nested) + '{ "foo": {}, "bar": [] }', + "{\n \"foo\": {},\n \"bar\": []\n}", + ), + array( + '{ "foo": "bar" }', + "{\n \"foo\": \"bar\"\n}", + ), + array( + '{ "foo": 1000 }', + "{\n \"foo\": 1000\n}", + ), + array( + '{ "foo": 1000, "0": "bar" }', + "{\n \"foo\": 1000,\n \"0\": \"bar\"\n}", + ), ); } /** * @dataProvider provideDataToEncode */ - public function testPreSaveTransform( $data ) { - $obj = new JsonContent( FormatJson::encode( $data ) ); - $newObj = $obj->preSaveTransform( $this->getMockTitle(), $this->getMockUser(), $this->getMockParserOptions() ); - $this->assertTrue( $newObj->equals( new JsonContent( FormatJson::encode( $data, true ) ) ) ); + public function testBeautifyJson( $input, $beautified ) { + $obj = new JsonContent( $input ); + $this->assertEquals( $beautified, $obj->beautifyJSON() ); + } + + /** + * @dataProvider provideDataToEncode + */ + public function testPreSaveTransform( $input, $transformed ) { + $obj = new JsonContent( $input ); + $newObj = $obj->preSaveTransform( + $this->getMockTitle(), + $this->getMockUser(), + $this->getMockParserOptions() + ); + $this->assertTrue( $newObj->equals( new JsonContent( $transformed ) ) ); } private function getMockTitle() { @@ -67,48 +104,55 @@ class JsonContentTest extends MediaWikiLangTestCase { ->getMock(); } - /** - * @dataProvider provideDataAndParserText - */ - public function testFillParserOutput( $data, $expected ) { - $obj = new JsonContent( FormatJson::encode( $data ) ); - $parserOutput = $obj->getParserOutput( $this->getMockTitle(), null, null, true ); - $this->assertInstanceOf( 'ParserOutput', $parserOutput ); - $this->assertEquals( $expected, $parserOutput->getText() ); - } - public static function provideDataAndParserText() { return array( array( array(), - '<table class="mw-json"><tbody></tbody></table>' + '<table class="mw-json"><tbody><tr><td>' . + '<table class="mw-json"><tbody><tr><td class="mw-json-empty">Empty array</td></tr>' + . '</tbody></table></td></tr></tbody></table>' ), array( - array( 'foo' ), - '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"foo"</td></tr></tbody></table>' + (object)array(), + '<table class="mw-json"><tbody><tr><td class="mw-json-empty">Empty object</td></tr>' . + '</tbody></table>' ), array( - array( 'foo', 'bar' ), - '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"foo"</td></tr>' . - "\n" . - '<tr><th>1</th><td class="value">"bar"</td></tr></tbody></table>' + (object)array( 'foo' ), + '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"foo"</td></tr>' . + '</tbody></table>' ), array( - array( 'baz' => 'foo', 'bar' ), - '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">"foo"</td></tr>' . - "\n" . - '<tr><th>0</th><td class="value">"bar"</td></tr></tbody></table>' + (object)array( 'foo', 'bar' ), + '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"foo"</td></tr>' . + '<tr><th>1</th><td class="value">"bar"</td></tr></tbody></table>' ), array( - array( 'baz' => 1000, 'bar' ), + (object)array( 'baz' => 'foo', 'bar' ), + '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">"foo"</td></tr>' . + '<tr><th>0</th><td class="value">"bar"</td></tr></tbody></table>' + ), + array( + (object)array( 'baz' => 1000, 'bar' ), '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">1000</td></tr>' . - "\n" . - '<tr><th>0</th><td class="value">"bar"</td></tr></tbody></table>' + '<tr><th>0</th><td class="value">"bar"</td></tr></tbody></table>' ), array( - array( '<script>alert("evil!")</script>'), - '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"<script>alert("evil!")</script>"</td></tr></tbody></table>', + (object)array( '<script>alert("evil!")</script>'), + '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"' . + '<script>alert("evil!")</script>"' . + '</td></tr></tbody></table>', ), ); } + + /** + * @dataProvider provideDataAndParserText + */ + public function testFillParserOutput( $data, $expected ) { + $obj = new JsonContent( FormatJson::encode( $data ) ); + $parserOutput = $obj->getParserOutput( $this->getMockTitle(), null, null, true ); + $this->assertInstanceOf( 'ParserOutput', $parserOutput ); + $this->assertEquals( $expected, $parserOutput->getText() ); + } } diff --git a/tests/phpunit/includes/content/TextContentTest.php b/tests/phpunit/includes/content/TextContentTest.php index 2f811094..dd61f85b 100644 --- a/tests/phpunit/includes/content/TextContentTest.php +++ b/tests/phpunit/includes/content/TextContentTest.php @@ -7,11 +7,8 @@ */ class TextContentTest extends MediaWikiLangTestCase { protected $context; - protected $savedContentGetParserOutput; protected function setUp() { - global $wgHooks; - parent::setUp(); // Anon user @@ -32,24 +29,8 @@ class TextContentTest extends MediaWikiLangTestCase { 'wgUseTidy' => false, 'wgAlwaysUseTidy' => false, 'wgCapitalLinks' => true, + 'wgHooks' => array(), // bypass hook ContentGetParserOutput that force custom rendering ) ); - - // bypass hooks that force custom rendering - if ( isset( $wgHooks['ContentGetParserOutput'] ) ) { - $this->savedContentGetParserOutput = $wgHooks['ContentGetParserOutput']; - unset( $wgHooks['ContentGetParserOutput'] ); - } - } - - public function teardown() { - global $wgHooks; - - // restore hooks that force custom rendering - if ( $this->savedContentGetParserOutput !== null ) { - $wgHooks['ContentGetParserOutput'] = $this->savedContentGetParserOutput; - } - - parent::teardown(); } public function newContent( $text ) { diff --git a/tests/phpunit/includes/RequestContextTest.php b/tests/phpunit/includes/context/RequestContextTest.php index cae0e52e..a9e5be24 100644 --- a/tests/phpunit/includes/RequestContextTest.php +++ b/tests/phpunit/includes/context/RequestContextTest.php @@ -88,9 +88,9 @@ class RequestContextTest extends MediaWikiTestCase { unset( $sc ); // restore previous context $info = $context->exportSession(); - $this->assertEquals( $oInfo['ip'], $info['ip'], "Correct initial IP address." ); - $this->assertEquals( $oInfo['headers'], $info['headers'], "Correct initial headers." ); - $this->assertEquals( $oInfo['sessionId'], $info['sessionId'], "Correct initial session ID." ); - $this->assertEquals( $oInfo['userId'], $info['userId'], "Correct initial user ID." ); + $this->assertEquals( $oInfo['ip'], $info['ip'], "Correct restored IP address." ); + $this->assertEquals( $oInfo['headers'], $info['headers'], "Correct restored headers." ); + $this->assertEquals( $oInfo['sessionId'], $info['sessionId'], "Correct restored session ID." ); + $this->assertEquals( $oInfo['userId'], $info['userId'], "Correct restored user ID." ); } } diff --git a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php index 55e48d13..b4292a60 100644 --- a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php +++ b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php @@ -2,7 +2,6 @@ /** * Holds tests for DatabaseMysqlBase MediaWiki class. * - * @section LICENSE * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/tests/phpunit/includes/db/DatabaseSQLTest.php b/tests/phpunit/includes/db/DatabaseSQLTest.php index 5c2d4b70..b13751f3 100644 --- a/tests/phpunit/includes/db/DatabaseSQLTest.php +++ b/tests/phpunit/includes/db/DatabaseSQLTest.php @@ -722,4 +722,84 @@ class DatabaseSQLTest extends MediaWikiTestCase { $this->database->dropTable( 'non_existing', __METHOD__ ) ); } + + /** + * @dataProvider provideMakeList + * @covers DatabaseBase::makeList + */ + public function testMakeList( $list, $mode, $sqlText ) { + $this->assertEquals( trim( $this->database->makeList( + $list, $mode + ) ), $sqlText ); + } + + public static function provideMakeList() { + return array( + array( + array( 'value', 'value2' ), + LIST_COMMA, + "'value','value2'" + ), + array( + array( 'field', 'field2' ), + LIST_NAMES, + "field,field2" + ), + array( + array( 'field' => 'value', 'field2' => 'value2' ), + LIST_AND, + "field = 'value' AND field2 = 'value2'" + ), + array( + array( 'field' => null, "field2 != 'value2'" ), + LIST_AND, + "field IS NULL AND (field2 != 'value2')" + ), + array( + array( 'field' => array( 'value', null, 'value2' ), 'field2' => 'value2' ), + LIST_AND, + "(field IN ('value','value2') OR field IS NULL) AND field2 = 'value2'" + ), + array( + array( 'field' => array( null ), 'field2' => null ), + LIST_AND, + "field IS NULL AND field2 IS NULL" + ), + array( + array( 'field' => 'value', 'field2' => 'value2' ), + LIST_OR, + "field = 'value' OR field2 = 'value2'" + ), + array( + array( 'field' => 'value', 'field2' => null ), + LIST_OR, + "field = 'value' OR field2 IS NULL" + ), + array( + array( 'field' => array( 'value', 'value2' ), 'field2' => array( 'value' ) ), + LIST_OR, + "field IN ('value','value2') OR field2 = 'value'" + ), + array( + array( 'field' => array( null, 'value', null, 'value2' ), "field2 != 'value2'" ), + LIST_OR, + "(field IN ('value','value2') OR field IS NULL) OR (field2 != 'value2')" + ), + array( + array( 'field' => 'value', 'field2' => 'value2' ), + LIST_SET, + "field = 'value',field2 = 'value2'" + ), + array( + array( 'field' => 'value', 'field2' => null ), + LIST_SET, + "field = 'value',field2 = NULL" + ), + array( + array( 'field' => 'value', "field2 != 'value2'" ), + LIST_SET, + "field = 'value',field2 != 'value2'" + ), + ); + } } diff --git a/tests/phpunit/includes/db/DatabaseSqliteTest.php b/tests/phpunit/includes/db/DatabaseSqliteTest.php index 98b4ca04..645baf1f 100644 --- a/tests/phpunit/includes/db/DatabaseSqliteTest.php +++ b/tests/phpunit/includes/db/DatabaseSqliteTest.php @@ -1,10 +1,12 @@ <?php -class MockDatabaseSqlite extends DatabaseSqliteStandalone { +class MockDatabaseSqlite extends DatabaseSqlite { private $lastQuery; - function __construct() { - parent::__construct( ':memory:' ); + public static function newInstance( array $p = array() ) { + $p['dbFilePath'] = ':memory:'; + + return new self( $p ); } function query( $sql, $fname = '', $tempIgnore = false ) { @@ -36,7 +38,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { if ( !Sqlite::isPresent() ) { $this->markTestSkipped( 'No SQLite support detected' ); } - $this->db = new MockDatabaseSqlite(); + $this->db = MockDatabaseSqlite::newInstance(); if ( version_compare( $this->db->getServerVersion(), '3.6.0', '<' ) ) { $this->markTestSkipped( "SQLite at least 3.6 required, {$this->db->getServerVersion()} found" ); } @@ -89,7 +91,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { */ public function testAddQuotes( $value, $expected ) { // check quoting - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $this->assertEquals( $expected, $db->addQuotes( $value ), 'string not quoted as expected' ); // ok, quoting works as expected, now try a round trip. @@ -172,7 +174,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { */ public function testTableName() { // @todo Moar! - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $this->assertEquals( 'foo', $db->tableName( 'foo' ) ); $this->assertEquals( 'sqlite_master', $db->tableName( 'sqlite_master' ) ); $db->tablePrefix( 'foo' ); @@ -184,7 +186,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { * @covers DatabaseSqlite::duplicateTableStructure */ public function testDuplicateTableStructure() { - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $db->query( 'CREATE TABLE foo(foo, barfoo)' ); $db->duplicateTableStructure( 'foo', 'bar' ); @@ -208,7 +210,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { * @covers DatabaseSqlite::duplicateTableStructure */ public function testDuplicateTableStructureVirtual() { - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); if ( $db->getFulltextSearchModule() != 'FTS3' ) { $this->markTestSkipped( 'FTS3 not supported, cannot create virtual tables' ); } @@ -231,7 +233,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { * @covers DatabaseSqlite::deleteJoin */ public function testDeleteJoin() { - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $db->query( 'CREATE TABLE a (a_1)', __METHOD__ ); $db->query( 'CREATE TABLE b (b_1, b_2)', __METHOD__ ); $db->insert( 'a', array( @@ -272,7 +274,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { * @todo Currently only checks list of tables */ public function testUpgrades() { - global $IP, $wgVersion, $wgProfileToDatabase; + global $IP, $wgVersion, $wgProfiler; // Versions tested $versions = array( @@ -289,9 +291,20 @@ class DatabaseSqliteTest extends MediaWikiTestCase { 'user_newtalk.user_last_timestamp', // r84185 ); - $currentDB = new DatabaseSqliteStandalone( ':memory:' ); + $currentDB = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $currentDB->sourceFile( "$IP/maintenance/tables.sql" ); - if ( $wgProfileToDatabase ) { + + $profileToDb = false; + if ( isset( $wgProfiler['output'] ) ) { + $out = $wgProfiler['output']; + if ( $out === 'db' ) { + $profileToDb = true; + } elseif ( is_array( $out ) && in_array( 'db', $out ) ) { + $profileToDb = true; + } + } + + if ( $profileToDb ) { $currentDB->sourceFile( "$IP/maintenance/sqlite/archives/patch-profiling.sql" ); } $currentTables = $this->getTables( $currentDB ); @@ -346,7 +359,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { * @covers DatabaseSqlite::insertId */ public function testInsertIdType() { - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $databaseCreation = $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ ); $this->assertInstanceOf( 'ResultWrapper', $databaseCreation, "Database creation" ); @@ -366,7 +379,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { } global $IP; - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $db->sourceFile( "$IP/tests/phpunit/data/db/sqlite/tables-$version.sql" ); $updater = DatabaseUpdater::newForDB( $db, false, $maint ); $updater->doUpdates( array( 'core' ) ); @@ -429,7 +442,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { public function testCaseInsensitiveLike() { // TODO: Test this for all databases - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $res = $db->query( 'SELECT "a" LIKE "A" AS a' ); $row = $res->fetchRow(); $this->assertFalse( (bool)$row['a'] ); @@ -439,7 +452,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase { * @covers DatabaseSqlite::numFields */ public function testNumFields() { - $db = new DatabaseSqliteStandalone( ':memory:' ); + $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $databaseCreation = $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ ); $this->assertInstanceOf( 'ResultWrapper', $databaseCreation, "Failed to create table a" ); diff --git a/tests/phpunit/includes/db/LBFactoryTest.php b/tests/phpunit/includes/db/LBFactoryTest.php index 4c59f474..81d6840b 100644 --- a/tests/phpunit/includes/db/LBFactoryTest.php +++ b/tests/phpunit/includes/db/LBFactoryTest.php @@ -2,7 +2,6 @@ /** * Holds tests for LBFactory abstract MediaWiki class. * - * @section LICENSE * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/tests/phpunit/includes/db/ORMRowTest.php b/tests/phpunit/includes/db/ORMRowTest.php index 447bf219..807bd14e 100644 --- a/tests/phpunit/includes/db/ORMRowTest.php +++ b/tests/phpunit/includes/db/ORMRowTest.php @@ -34,7 +34,6 @@ * that hold the first tests in a pending state awaiting access to the database. * @group medium * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ abstract class ORMRowTest extends \MediaWikiTestCase { diff --git a/tests/phpunit/includes/db/ORMTableTest.php b/tests/phpunit/includes/db/ORMTableTest.php index 7171ee59..338d931f 100644 --- a/tests/phpunit/includes/db/ORMTableTest.php +++ b/tests/phpunit/includes/db/ORMTableTest.php @@ -25,14 +25,12 @@ * @group ORM * @group Database * - * @licence GNU GPL v2+ + * @covers PageORMTableForTesting + * * @author Jeroen De Dauw < jeroendedauw@gmail.com > * @author Daniel Kinzler */ -/** - * @covers PageORMTableForTesting - */ class ORMTableTest extends MediaWikiTestCase { /** @@ -99,6 +97,10 @@ class ORMTableTest extends MediaWikiTestCase { class PageORMTableForTesting extends ORMTable { + public function __construct() { + $this->fieldPrefix = 'page_'; + } + /** * @see ORMTable::getName * @@ -138,13 +140,4 @@ class PageORMTableForTesting extends ORMTable { 'title' => 'str', ); } - - /** - * @see ORMTable::getFieldPrefix - * - * @return string - */ - protected function getFieldPrefix() { - return 'page_'; - } } diff --git a/tests/phpunit/includes/db/TestORMRowTest.php b/tests/phpunit/includes/db/TestORMRowTest.php index c9459c90..04bb9f38 100644 --- a/tests/phpunit/includes/db/TestORMRowTest.php +++ b/tests/phpunit/includes/db/TestORMRowTest.php @@ -20,27 +20,23 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @since 1.20 - * * @ingroup Test - * - * @group ORM - * + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + */ + +/** * The database group has as a side effect that temporal database tables are created. This makes * it possible to test without poisoning a production database. - * @group Database * * Some of the tests takes more time, and needs therefor longer time before they can be aborted * as non-functional. The reason why tests are aborted is assumed to be set up of temporal databases * that hold the first tests in a pending state awaiting access to the database. - * @group medium * - * @licence GNU GPL v2+ - * @author Jeroen De Dauw < jeroendedauw@gmail.com > - */ -require_once __DIR__ . "/ORMRowTest.php"; - -/** + * @since 1.20 + * + * @group ORM + * @group Database + * @group medium * @covers TestORMRow */ class TestORMRowTest extends ORMRowTest { @@ -150,6 +146,10 @@ class TestORMRow extends ORMRow { class TestORMTable extends ORMTable { + public function __construct() { + $this->fieldPrefix = 'test_'; + } + /** * Returns the name of the database table objects of this type are stored in. * @@ -204,15 +204,4 @@ class TestORMTable extends ORMTable { 'time' => 'str', // TS_MW ); } - - /** - * Gets the db field prefix. - * - * @since 1.20 - * - * @return string - */ - protected function getFieldPrefix() { - return 'test_'; - } } diff --git a/tests/phpunit/includes/debug/MWDebugTest.php b/tests/phpunit/includes/debug/MWDebugTest.php index 6e41de75..1abb47e7 100644 --- a/tests/phpunit/includes/debug/MWDebugTest.php +++ b/tests/phpunit/includes/debug/MWDebugTest.php @@ -96,16 +96,15 @@ class MWDebugTest extends MediaWikiTestCase { $apiMain = new ApiMain( $context ); $result = new ApiResult( $apiMain ); - $result->setRawMode( true ); MWDebug::appendDebugInfoToApiResult( $context, $result ); $this->assertInstanceOf( 'ApiResult', $result ); - $data = $result->getData(); + $data = $result->getResultData(); $expectedKeys = array( 'mwVersion', 'phpEngine', 'phpVersion', 'gitRevision', 'gitBranch', 'gitViewUrl', 'time', 'log', 'debugLog', 'queries', 'request', 'memory', - 'memoryPeak', 'includes', 'profile', '_element' ); + 'memoryPeak', 'includes', '_element' ); foreach ( $expectedKeys as $expectedKey ) { $this->assertArrayHasKey( $expectedKey, $data['debuginfo'], "debuginfo has $expectedKey" ); diff --git a/tests/phpunit/includes/debug/logging/LegacyLoggerTest.php b/tests/phpunit/includes/debug/logging/LegacyLoggerTest.php new file mode 100644 index 00000000..415fa045 --- /dev/null +++ b/tests/phpunit/includes/debug/logging/LegacyLoggerTest.php @@ -0,0 +1,122 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +namespace MediaWiki\Logger; + +use MediaWikiTestCase; +use Psr\Log\LogLevel; + +class LegacyLoggerTest extends MediaWikiTestCase { + + /** + * @covers LegacyLogger::interpolate + * @dataProvider provideInterpolate + */ + public function testInterpolate( $message, $context, $expect ) { + $this->assertEquals( + $expect, LegacyLogger::interpolate( $message, $context ) ); + } + + public function provideInterpolate() { + return array( + array( + 'no-op', + array(), + 'no-op', + ), + array( + 'Hello {world}!', + array( + 'world' => 'World', + ), + 'Hello World!', + ), + array( + '{greeting} {user}', + array( + 'greeting' => 'Goodnight', + 'user' => 'Moon', + ), + 'Goodnight Moon', + ), + array( + 'Oops {key_not_set}', + array(), + 'Oops {key_not_set}', + ), + array( + '{ not interpolated }', + array( + 'not interpolated' => 'This should NOT show up in the message', + ), + '{ not interpolated }', + ), + ); + } + + /** + * @covers LegacyLogger::shouldEmit + * @dataProvider provideShouldEmit + */ + public function testShouldEmit( $level, $config, $expected ) { + $this->setMwGlobals( 'wgDebugLogGroups', array( 'fakechannel' => $config ) ); + $this->assertEquals( + $expected, + LegacyLogger::shouldEmit( 'fakechannel', 'some message', $level, array() ) + ); + } + + public static function provideShouldEmit() { + $dest = array( 'destination' => 'foobar' ); + $tests = array( + array( + LogLevel::DEBUG, + $dest, + true + ), + array( + LogLevel::WARNING, + $dest + array( 'level' => LogLevel::INFO ), + true, + ), + array( + LogLevel::INFO, + $dest + array( 'level' => LogLevel::CRITICAL ), + false, + ), + ); + + if ( class_exists( '\Monolog\Logger' ) ) { + $tests[] = array( + \Monolog\Logger::INFO, + $dest + array( 'level' => LogLevel::INFO ), + true, + ); + $tests[] = array( + \Monolog\Logger::WARNING, + $dest + array( 'level' => LogLevel::EMERGENCY ), + false, + ); + } + + return $tests; + } + +} diff --git a/tests/phpunit/includes/LinksUpdateTest.php b/tests/phpunit/includes/deferred/LinksUpdateTest.php index 02f6b2ab..02f6b2ab 100644 --- a/tests/phpunit/includes/LinksUpdateTest.php +++ b/tests/phpunit/includes/deferred/LinksUpdateTest.php diff --git a/tests/phpunit/includes/search/SearchUpdateTest.php b/tests/phpunit/includes/deferred/SearchUpdateTest.php index c6275371..90438a04 100644 --- a/tests/phpunit/includes/search/SearchUpdateTest.php +++ b/tests/phpunit/includes/deferred/SearchUpdateTest.php @@ -17,7 +17,6 @@ class MockSearch extends SearchEngine { /** * @group Search - * @group Database */ class SearchUpdateTest extends MediaWikiTestCase { @@ -67,9 +66,10 @@ EOT /** * @covers SearchUpdate::updateText - * @todo give this test a real name explaining what is being tested here + * Test bug 32712 + * Test if unicode quotes in article links make its search index empty */ - public function testBug32712() { + public function testUnicodeLinkSearchIndexError() { $text = "text „http://example.com“ text"; $result = $this->updateText( $text ); $processed = preg_replace( '/Q/u', 'Q', $result ); diff --git a/tests/phpunit/includes/diff/ArrayDiffFormatterTest.php b/tests/phpunit/includes/diff/ArrayDiffFormatterTest.php index 188ad3fd..3bea9b31 100644 --- a/tests/phpunit/includes/diff/ArrayDiffFormatterTest.php +++ b/tests/phpunit/includes/diff/ArrayDiffFormatterTest.php @@ -1,7 +1,6 @@ <?php /** - * @licence GNU GPL v2+ * @author Adam Shorland * * @group Diff diff --git a/tests/phpunit/includes/diff/DiffOpTest.php b/tests/phpunit/includes/diff/DiffOpTest.php index d89b89fe..cbe05732 100644 --- a/tests/phpunit/includes/diff/DiffOpTest.php +++ b/tests/phpunit/includes/diff/DiffOpTest.php @@ -1,10 +1,5 @@ <?php - -//Load our FakeDiffOp -require_once __DIR__ . DIRECTORY_SEPARATOR . 'FakeDiffOp.php'; - /** - * @licence GNU GPL v2+ * @author Adam Shorland * * @group Diff diff --git a/tests/phpunit/includes/diff/DiffTest.php b/tests/phpunit/includes/diff/DiffTest.php index 1911c82a..e0d79157 100644 --- a/tests/phpunit/includes/diff/DiffTest.php +++ b/tests/phpunit/includes/diff/DiffTest.php @@ -1,7 +1,6 @@ <?php /** - * @licence GNU GPL v2+ * @author Adam Shorland * * @group Diff diff --git a/tests/phpunit/includes/diff/DifferenceEngineTest.php b/tests/phpunit/includes/diff/DifferenceEngineTest.php index 5474b963..0f599af1 100644 --- a/tests/phpunit/includes/diff/DifferenceEngineTest.php +++ b/tests/phpunit/includes/diff/DifferenceEngineTest.php @@ -8,7 +8,6 @@ * @group Database * @group Diff * - * @licence GNU GPL v2+ * @author Katie Filbert < aude.wiki@gmail.com > */ class DifferenceEngineTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/exception/BadTitleErrorTest.php b/tests/phpunit/includes/exception/BadTitleErrorTest.php index 003efd27..500b7e48 100644 --- a/tests/phpunit/includes/exception/BadTitleErrorTest.php +++ b/tests/phpunit/includes/exception/BadTitleErrorTest.php @@ -5,23 +5,8 @@ */ class BadTitleErrorTest extends MediaWikiTestCase { - protected $wgOut; - - protected function setUp() { - parent::setUp(); - global $wgOut; - $this->wgOut = clone $wgOut; - } - - protected function tearDown() { - parent::tearDown(); - global $wgOut; - $wgOut = $this->wgOut; - } - public function testExceptionSetsStatusCode() { - global $wgOut; - $wgOut = $this->getMockWgOut(); + $this->setMwGlobals( 'wgOut', $this->getMockWgOut() ); try { throw new BadTitleError(); } catch ( BadTitleError $e ) { diff --git a/tests/phpunit/includes/exception/ErrorPageErrorTest.php b/tests/phpunit/includes/exception/ErrorPageErrorTest.php index 13dcf33b..9c4e4a0b 100644 --- a/tests/phpunit/includes/exception/ErrorPageErrorTest.php +++ b/tests/phpunit/includes/exception/ErrorPageErrorTest.php @@ -6,20 +6,6 @@ */ class ErrorPageErrorTest extends MediaWikiTestCase { - private $wgOut; - - protected function setUp() { - parent::setUp(); - global $wgOut; - $this->wgOut = clone $wgOut; - } - - protected function tearDown() { - global $wgOut; - $wgOut = $this->wgOut; - parent::tearDown(); - } - private function getMockMessage() { $mockMessage = $this->getMockBuilder( 'Message' ) ->disableOriginalConstructor() @@ -48,20 +34,18 @@ class ErrorPageErrorTest extends MediaWikiTestCase { $title = 'Foo'; $params = array( 'Baz' ); - global $wgOut; - $wgOut = $this->getMockBuilder( 'OutputPage' ) + $mock = $this->getMockBuilder( 'OutputPage' ) ->disableOriginalConstructor() ->getMock(); - $wgOut->expects( $this->once() ) + $mock->expects( $this->once() ) ->method( 'showErrorPage' ) ->with( $title, $mockMessage, $params ); - $wgOut->expects( $this->once() ) + $mock->expects( $this->once() ) ->method( 'output' ); + $this->setMwGlobals( 'wgOut', $mock ); $e = new ErrorPageError( $title, $mockMessage, $params ); $e->report(); } - - } diff --git a/tests/phpunit/includes/exception/MWExceptionHandlerTest.php b/tests/phpunit/includes/exception/MWExceptionHandlerTest.php index dc5dc6aa..d73f17d9 100644 --- a/tests/phpunit/includes/exception/MWExceptionHandlerTest.php +++ b/tests/phpunit/includes/exception/MWExceptionHandlerTest.php @@ -15,7 +15,7 @@ class MWExceptionHandlerTest extends MediaWikiTestCase { $refvar = 'value'; try { $array = array( 'a', 'b' ); - $object = new StdClass(); + $object = new stdClass(); self::helperThrowAnException( $array, $object, $refvar ); } catch ( Exception $e ) { } diff --git a/tests/phpunit/includes/exception/ThrottledErrorTest.php b/tests/phpunit/includes/exception/ThrottledErrorTest.php index bdb143fa..a1cf84bc 100644 --- a/tests/phpunit/includes/exception/ThrottledErrorTest.php +++ b/tests/phpunit/includes/exception/ThrottledErrorTest.php @@ -6,23 +6,8 @@ */ class ThrottledErrorTest extends MediaWikiTestCase { - protected $wgOut; - - protected function setUp() { - parent::setUp(); - global $wgOut; - $this->wgOut = clone $wgOut; - } - - protected function tearDown() { - parent::tearDown(); - global $wgOut; - $wgOut = $this->wgOut; - } - public function testExceptionSetsStatusCode() { - global $wgOut; - $wgOut = $this->getMockWgOut(); + $this->setMwGlobals( 'wgOut', $this->getMockWgOut() ); try { throw new ThrottledError(); } catch ( ThrottledError $e ) { diff --git a/tests/phpunit/includes/ExternalStoreTest.php b/tests/phpunit/includes/externalstore/ExternalStoreTest.php index 07c2957c..07c2957c 100644 --- a/tests/phpunit/includes/ExternalStoreTest.php +++ b/tests/phpunit/includes/externalstore/ExternalStoreTest.php diff --git a/tests/phpunit/includes/filebackend/FileBackendTest.php b/tests/phpunit/includes/filebackend/FileBackendTest.php index 9558cc7d..bfca75ad 100644 --- a/tests/phpunit/includes/filebackend/FileBackendTest.php +++ b/tests/phpunit/includes/filebackend/FileBackendTest.php @@ -13,14 +13,13 @@ class FileBackendTest extends MediaWikiTestCase { private $multiBackend; /** @var FSFileBackend */ public $singleBackend; - private $filesToPrune = array(); private static $backendToUse; protected function setUp() { global $wgFileBackends; parent::setUp(); $uniqueId = time() . '-' . mt_rand(); - $tmpPrefix = wfTempDir() . '/filebackend-unittest-' . $uniqueId; + $tmpDir = $this->getNewTempDirectory(); if ( $this->getCliArg( 'use-filebackend' ) ) { if ( self::$backendToUse ) { $this->singleBackend = self::$backendToUse; @@ -51,8 +50,8 @@ class FileBackendTest extends MediaWikiTestCase { 'lockManager' => LockManagerGroup::singleton()->get( 'fsLockManager' ), 'wikiId' => wfWikiID(), 'containerPaths' => array( - 'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1", - 'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" ) + 'unittest-cont1' => "{$tmpDir}/localtesting-cont1", + 'unittest-cont2' => "{$tmpDir}/localtesting-cont2" ) ) ); } $this->multiBackend = new FileBackendMultiWrite( array( @@ -65,21 +64,20 @@ class FileBackendTest extends MediaWikiTestCase { 'name' => 'localmultitesting1', 'class' => 'FSFileBackend', 'containerPaths' => array( - 'unittest-cont1' => "{$tmpPrefix}-localtestingmulti1-cont1", - 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti1-cont2" ), + 'unittest-cont1' => "{$tmpDir}/localtestingmulti1-cont1", + 'unittest-cont2' => "{$tmpDir}/localtestingmulti1-cont2" ), 'isMultiMaster' => false ), array( 'name' => 'localmultitesting2', 'class' => 'FSFileBackend', 'containerPaths' => array( - 'unittest-cont1' => "{$tmpPrefix}-localtestingmulti2-cont1", - 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti2-cont2" ), + 'unittest-cont1' => "{$tmpDir}/localtestingmulti2-cont1", + 'unittest-cont2' => "{$tmpDir}/localtestingmulti2-cont2" ), 'isMultiMaster' => true ) ) ) ); - $this->filesToPrune = array(); } private static function baseStorePath() { @@ -214,7 +212,7 @@ class FileBackendTest extends MediaWikiTestCase { * @dataProvider provider_testStore */ public function testStore( $op ) { - $this->filesToPrune[] = $op['src']; + $this->addTmpFiles( $op['src'] ); $this->backend = $this->singleBackend; $this->tearDownFiles(); @@ -224,7 +222,6 @@ class FileBackendTest extends MediaWikiTestCase { $this->backend = $this->multiBackend; $this->tearDownFiles(); $this->doTestStore( $op ); - $this->filesToPrune[] = $op['src']; # avoid file leaking $this->tearDownFiles(); } @@ -275,27 +272,15 @@ class FileBackendTest extends MediaWikiTestCase { $tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath(); $toPath = self::baseStorePath() . '/unittest-cont1/e/fun/obj1.txt'; $op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath ); - $cases[] = array( - $op, // operation - $tmpName, // source - $toPath, // dest - ); + $cases[] = array( $op ); $op2 = $op; $op2['overwrite'] = true; - $cases[] = array( - $op2, // operation - $tmpName, // source - $toPath, // dest - ); + $cases[] = array( $op2 ); - $op2 = $op; - $op2['overwriteSame'] = true; - $cases[] = array( - $op2, // operation - $tmpName, // source - $toPath, // dest - ); + $op3 = $op; + $op3['overwriteSame'] = true; + $cases[] = array( $op3 ); return $cases; } @@ -948,18 +933,14 @@ class FileBackendTest extends MediaWikiTestCase { * @dataProvider provider_testConcatenate */ public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) { - $this->filesToPrune[] = $op['dst']; - $this->backend = $this->singleBackend; $this->tearDownFiles(); $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ); - $this->filesToPrune[] = $op['dst']; # avoid file leaking $this->tearDownFiles(); $this->backend = $this->multiBackend; $this->tearDownFiles(); $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ); - $this->filesToPrune[] = $op['dst']; # avoid file leaking $this->tearDownFiles(); } @@ -983,7 +964,7 @@ class FileBackendTest extends MediaWikiTestCase { $this->assertGoodStatus( $status, "Creation of source files succeeded ($backendName)." ); - $dest = $params['dst']; + $dest = $params['dst'] = $this->getNewTempFile(); if ( $alreadyExists ) { $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false; $this->assertEquals( true, $ok, @@ -1029,8 +1010,6 @@ class FileBackendTest extends MediaWikiTestCase { public static function provider_testConcatenate() { $cases = array(); - $rand = mt_rand( 0, 2000000000 ) . time(); - $dest = wfTempDir() . "/randomfile!$rand.txt"; $srcs = array( self::baseStorePath() . '/unittest-cont1/e/file1.txt', self::baseStorePath() . '/unittest-cont1/e/file2.txt', @@ -1055,7 +1034,7 @@ class FileBackendTest extends MediaWikiTestCase { 'lkaem;a', 'legma' ); - $params = array( 'srcs' => $srcs, 'dst' => $dest ); + $params = array( 'srcs' => $srcs ); $cases[] = array( $params, // operation @@ -1499,7 +1478,7 @@ class FileBackendTest extends MediaWikiTestCase { $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) ); if ( $url !== null ) { // supported - $data = Http::request( "GET", $url ); + $data = Http::request( "GET", $url, array(), __METHOD__ ); $this->assertEquals( $content, $data, "HTTP GET of URL has right contents ($backendName)." ); } @@ -1761,16 +1740,13 @@ class FileBackendTest extends MediaWikiTestCase { $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag'; $tmpNameA = TempFSFile::factory( "unittests_", 'txt' )->getPath(); - file_put_contents( $tmpNameA, $fileAContents ); $tmpNameB = TempFSFile::factory( "unittests_", 'txt' )->getPath(); - file_put_contents( $tmpNameB, $fileBContents ); $tmpNameC = TempFSFile::factory( "unittests_", 'txt' )->getPath(); + $this->addTmpFiles( array( $tmpNameA, $tmpNameB, $tmpNameC ) ); + file_put_contents( $tmpNameA, $fileAContents ); + file_put_contents( $tmpNameB, $fileBContents ); file_put_contents( $tmpNameC, $fileCContents ); - $this->filesToPrune[] = $tmpNameA; # avoid file leaking - $this->filesToPrune[] = $tmpNameB; # avoid file leaking - $this->filesToPrune[] = $tmpNameC; # avoid file leaking - $fileA = "$base/unittest-cont1/e/a/b/fileA.txt"; $fileB = "$base/unittest-cont1/e/a/b/fileB.txt"; $fileC = "$base/unittest-cont1/e/a/b/fileC.txt"; @@ -2434,16 +2410,10 @@ class FileBackendTest extends MediaWikiTestCase { } function tearDownFiles() { - foreach ( $this->filesToPrune as $file ) { - if ( is_file( $file ) ) { - unlink( $file ); - } - } $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont-bad' ); foreach ( $containers as $container ) { $this->deleteFiles( $container ); } - $this->filesToPrune = array(); } private function deleteFiles( $container ) { diff --git a/tests/phpunit/includes/filerepo/StoreBatchTest.php b/tests/phpunit/includes/filerepo/StoreBatchTest.php index 9cc2efbf..86bfe123 100644 --- a/tests/phpunit/includes/filerepo/StoreBatchTest.php +++ b/tests/phpunit/includes/filerepo/StoreBatchTest.php @@ -16,7 +16,7 @@ class StoreBatchTest extends MediaWikiTestCase { parent::setUp(); # Forge a FSRepo object to not have to rely on local wiki settings - $tmpPrefix = wfTempDir() . '/storebatch-test-' . time() . '-' . mt_rand(); + $tmpPrefix = $this->getNewTempDirectory(); if ( $this->getCliArg( 'use-filebackend' ) ) { $name = $this->getCliArg( 'use-filebackend' ); $useConfig = array(); @@ -35,10 +35,10 @@ class StoreBatchTest extends MediaWikiTestCase { 'name' => 'local-testing', 'wikiId' => wfWikiID(), 'containerPaths' => array( - 'unittests-public' => "{$tmpPrefix}-public", - 'unittests-thumb' => "{$tmpPrefix}-thumb", - 'unittests-temp' => "{$tmpPrefix}-temp", - 'unittests-deleted' => "{$tmpPrefix}-deleted", + 'unittests-public' => "{$tmpPrefix}/public", + 'unittests-thumb' => "{$tmpPrefix}/thumb", + 'unittests-temp' => "{$tmpPrefix}/temp", + 'unittests-deleted' => "{$tmpPrefix}/deleted", ) ) ); } @@ -52,13 +52,8 @@ class StoreBatchTest extends MediaWikiTestCase { } protected function tearDown() { - $this->repo->cleanupBatch( $this->createdFiles ); // delete files - foreach ( $this->createdFiles as $tmp ) { // delete dirs - $tmp = $this->repo->resolveVirtualUrl( $tmp ); - while ( $tmp = FileBackend::parentStoragePath( $tmp ) ) { - $this->repo->getBackend()->clean( array( 'dir' => $tmp ) ); - } - } + // Delete files + $this->repo->cleanupBatch( $this->createdFiles ); parent::tearDown(); } diff --git a/tests/phpunit/includes/LocalFileTest.php b/tests/phpunit/includes/filerepo/file/LocalFileTest.php index 5c5052e4..3c5754bf 100644 --- a/tests/phpunit/includes/LocalFileTest.php +++ b/tests/phpunit/includes/filerepo/file/LocalFileTest.php @@ -2,7 +2,6 @@ /** * These tests should work regardless of $wgCapitalLinks - * @group Database * @todo Split tests into providers and test methods */ diff --git a/tests/phpunit/includes/installer/DatabaseUpdaterTest.php b/tests/phpunit/includes/installer/DatabaseUpdaterTest.php new file mode 100644 index 00000000..abff3e68 --- /dev/null +++ b/tests/phpunit/includes/installer/DatabaseUpdaterTest.php @@ -0,0 +1,279 @@ +<?php + +class DatabaseUpdaterTest extends MediaWikiTestCase { + + public function testSetAppliedUpdates() { + $db = new FakeDatabase(); + $dbu = new FakeDatabaseUpdater( $db ); + $dbu->setAppliedUpdates( "test", array() ); + $expected = "updatelist-test-" . time() . "0"; + $actual = $db->lastInsertData['ul_key']; + $this->assertEquals( $expected, $actual, var_export( $db->lastInsertData, true ) ); + $dbu->setAppliedUpdates( "test", array() ); + $expected = "updatelist-test-" . time() . "1"; + $actual = $db->lastInsertData['ul_key']; + $this->assertEquals( $expected, $actual, var_export( $db->lastInsertData, true ) ); + } +} + +class FakeDatabase extends DatabaseBase { + public $lastInsertTable; + public $lastInsertData; + + function __construct() { + } + + function clearFlag( $arg ) { + } + + function setFlag( $arg ) { + } + + public function insert( $table, $a, $fname = __METHOD__, $options = array() ) { + $this->lastInsertTable = $table; + $this->lastInsertData = $a; + } + + /** + * Get the type of the DBMS, as it appears in $wgDBtype. + * + * @return string + */ + function getType() { + // TODO: Implement getType() method. + } + + /** + * Open a connection to the database. Usually aborts on failure + * + * @param string $server Database server host + * @param string $user Database user name + * @param string $password Database user password + * @param string $dbName Database name + * @return bool + * @throws DBConnectionError + */ + function open( $server, $user, $password, $dbName ) { + // TODO: Implement open() method. + } + + /** + * Fetch the next row from the given result object, in object form. + * Fields can be retrieved with $row->fieldname, with fields acting like + * member variables. + * If no more rows are available, false is returned. + * + * @param ResultWrapper|stdClass $res Object as returned from DatabaseBase::query(), etc. + * @return stdClass|bool + * @throws DBUnexpectedError Thrown if the database returns an error + */ + function fetchObject( $res ) { + // TODO: Implement fetchObject() method. + } + + /** + * Fetch the next row from the given result object, in associative array + * form. Fields are retrieved with $row['fieldname']. + * If no more rows are available, false is returned. + * + * @param ResultWrapper $res Result object as returned from DatabaseBase::query(), etc. + * @return array|bool + * @throws DBUnexpectedError Thrown if the database returns an error + */ + function fetchRow( $res ) { + // TODO: Implement fetchRow() method. + } + + /** + * Get the number of rows in a result object + * + * @param mixed $res A SQL result + * @return int + */ + function numRows( $res ) { + // TODO: Implement numRows() method. + } + + /** + * Get the number of fields in a result object + * @see http://www.php.net/mysql_num_fields + * + * @param mixed $res A SQL result + * @return int + */ + function numFields( $res ) { + // TODO: Implement numFields() method. + } + + /** + * Get a field name in a result object + * @see http://www.php.net/mysql_field_name + * + * @param mixed $res A SQL result + * @param int $n + * @return string + */ + function fieldName( $res, $n ) { + // TODO: Implement fieldName() method. + } + + /** + * Get the inserted value of an auto-increment row + * + * The value inserted should be fetched from nextSequenceValue() + * + * Example: + * $id = $dbw->nextSequenceValue( 'page_page_id_seq' ); + * $dbw->insert( 'page', array( 'page_id' => $id ) ); + * $id = $dbw->insertId(); + * + * @return int + */ + function insertId() { + // TODO: Implement insertId() method. + } + + /** + * Change the position of the cursor in a result object + * @see http://www.php.net/mysql_data_seek + * + * @param mixed $res A SQL result + * @param int $row + */ + function dataSeek( $res, $row ) { + // TODO: Implement dataSeek() method. + } + + /** + * Get the last error number + * @see http://www.php.net/mysql_errno + * + * @return int + */ + function lastErrno() { + // TODO: Implement lastErrno() method. + } + + /** + * Get a description of the last error + * @see http://www.php.net/mysql_error + * + * @return string + */ + function lastError() { + // TODO: Implement lastError() method. + } + + /** + * mysql_fetch_field() wrapper + * Returns false if the field doesn't exist + * + * @param string $table Table name + * @param string $field Field name + * + * @return Field + */ + function fieldInfo( $table, $field ) { + // TODO: Implement fieldInfo() method. + } + + /** + * Get information about an index into an object + * @param string $table Table name + * @param string $index Index name + * @param string $fname Calling function name + * @return mixed Database-specific index description class or false if the index does not exist + */ + function indexInfo( $table, $index, $fname = __METHOD__ ) { + // TODO: Implement indexInfo() method. + } + + /** + * Get the number of rows affected by the last write query + * @see http://www.php.net/mysql_affected_rows + * + * @return int + */ + function affectedRows() { + // TODO: Implement affectedRows() method. + } + + /** + * Wrapper for addslashes() + * + * @param string $s String to be slashed. + * @return string Slashed string. + */ + function strencode( $s ) { + // TODO: Implement strencode() method. + } + + /** + * Returns a wikitext link to the DB's website, e.g., + * return "[http://www.mysql.com/ MySQL]"; + * Should at least contain plain text, if for some reason + * your database has no website. + * + * @return string Wikitext of a link to the server software's web site + */ + function getSoftwareLink() { + // TODO: Implement getSoftwareLink() method. + } + + /** + * A string describing the current software version, like from + * mysql_get_server_info(). + * + * @return string Version information from the database server. + */ + function getServerVersion() { + // TODO: Implement getServerVersion() method. + } + + /** + * Closes underlying database connection + * @since 1.20 + * @return bool Whether connection was closed successfully + */ + protected function closeConnection() { + // TODO: Implement closeConnection() method. + } + + /** + * The DBMS-dependent part of query() + * + * @param string $sql SQL query. + * @return ResultWrapper|bool Result object to feed to fetchObject, + * fetchRow, ...; or false on failure + */ + protected function doQuery( $sql ) { + // TODO: Implement doQuery() method. + } +} + +class FakeDatabaseUpdater extends DatabaseUpdater { + function __construct( $db ) { + $this->db = $db; + self::$updateCounter = 0; + } + + /** + * Get an array of updates to perform on the database. Should return a + * multi-dimensional array. The main key is the MediaWiki version (1.12, + * 1.13...) with the values being arrays of updates, identical to how + * updaters.inc did it (for now) + * + * @return array + */ + protected function getCoreUpdateList() { + return array(); + } + + public function canUseNewUpdatelog() { + return true; + } + + public function setAppliedUpdates( $version, $updates = array() ) { + parent::setAppliedUpdates( $version, $updates ); + } +} diff --git a/tests/phpunit/includes/installer/InstallDocFormatterTest.php b/tests/phpunit/includes/installer/InstallDocFormatterTest.php index 064d5185..724f0c88 100644 --- a/tests/phpunit/includes/installer/InstallDocFormatterTest.php +++ b/tests/phpunit/includes/installer/InstallDocFormatterTest.php @@ -34,6 +34,21 @@ class InstallDocFormatterTest extends MediaWikiTestCase { array( ':One indentation', "\tOne indentation", 'Replacing a single \t' ), array( '::Two indentations', "\t\tTwo indentations", 'Replacing 2 x \t' ), + # Transform 'T123' links + array( + '<span class="config-plainlink">[https://phabricator.wikimedia.org/T123 T123]</span>', + 'T123', 'Testing T123 links' ), + array( + 'bug <span class="config-plainlink">[https://phabricator.wikimedia.org/T123 T123]</span>', + 'bug T123', 'Testing bug T123 links' ), + array( + '(<span class="config-plainlink">[https://phabricator.wikimedia.org/T987654 T987654]</span>)', + '(T987654)', 'Testing (T987654) links' ), + + # "Tabc" shouldn't work + array( 'Tfoobar', 'Tfoobar', "Don't match T followed by non-digits" ), + array( 'T!!fakefake!!', 'T!!fakefake!!', "Don't match T followed by non-digits" ), + # Transform 'bug 123' links array( '<span class="config-plainlink">[https://bugzilla.wikimedia.org/123 bug 123]</span>', diff --git a/tests/phpunit/includes/jobqueue/JobQueueTest.php b/tests/phpunit/includes/jobqueue/JobQueueTest.php index 69e40068..ea1a4f63 100644 --- a/tests/phpunit/includes/jobqueue/JobQueueTest.php +++ b/tests/phpunit/includes/jobqueue/JobQueueTest.php @@ -247,8 +247,13 @@ class JobQueueTest extends MediaWikiTestCase { $this->assertNull( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" ); } $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) ); - sleep( 1 ); // roo job timestamp will increase - $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp + + $root2 = $root1; + # Add a second to UNIX epoch and format back to TS_MW + $root2_ts = strtotime( $root2['rootJobTimestamp'] ); + $root2_ts++; + $root2['rootJobTimestamp'] = wfTimestamp( TS_MW, $root2_ts ); + $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'], "Root job signatures have different timestamps." ); for ( $i = 0; $i < 5; ++$i ) { diff --git a/tests/phpunit/includes/jobqueue/JobTest.php b/tests/phpunit/includes/jobqueue/JobTest.php new file mode 100644 index 00000000..93069d2e --- /dev/null +++ b/tests/phpunit/includes/jobqueue/JobTest.php @@ -0,0 +1,67 @@ +<?php + +/** + * @author Adam Shorland + */ +class JobTest extends MediaWikiTestCase { + + /** + * @dataProvider provideTestToString + * + * @param Job $job + * @param string $expected + * + * @covers Job::toString + */ + public function testToString( $job, $expected ) { + $this->assertEquals( $expected, $job->toString() ); + } + + public function provideTestToString() { + $mockToStringObj = $this->getMock( 'stdClass', array( '__toString' ) ); + $mockToStringObj->expects( $this->any() ) + ->method( '__toString' ) + ->will( $this->returnValue( '{STRING_OBJ_VAL}' ) ); + + return array( + array( + $this->getMockJob( false ), + 'someCommand ' + ), + array( + $this->getMockJob( array( 'key' => 'val' ) ), + 'someCommand key=val' + ), + array( + $this->getMockJob( array( 'key' => array( 'inkey' => 'inval' ) ) ), + 'someCommand key={"inkey":"inval"}' + ), + array( + $this->getMockJob( array( 'val1' ) ), + 'someCommand 0=val1' + ), + array( + $this->getMockJob( array( 'val1', 'val2' ) ), + 'someCommand 0=val1 1=val2' + ), + array( + $this->getMockJob( array( new stdClass() ) ), + 'someCommand 0=object(stdClass)' + ), + array( + $this->getMockJob( array( $mockToStringObj ) ), + 'someCommand 0={STRING_OBJ_VAL}' + ), + ); + } + + public function getMockJob( $params ) { + $mock = $this->getMockForAbstractClass( + 'Job', + array( 'someCommand', new Title(), $params ), + 'SomeJob' + ); + return $mock; + } + +} diff --git a/tests/phpunit/includes/json/FormatJsonTest.php b/tests/phpunit/includes/json/FormatJsonTest.php index af68ab03..f0ac6acc 100644 --- a/tests/phpunit/includes/json/FormatJsonTest.php +++ b/tests/phpunit/includes/json/FormatJsonTest.php @@ -169,12 +169,30 @@ class FormatJsonTest extends MediaWikiTestCase { $this->assertEquals( $value, $st->getValue() ); } + /** + * Test data for testParseTryFixing. + * + * Some PHP interpreters use json-c rather than the JSON.org cannonical + * parser to avoid being encumbered by the "shall be used for Good, not + * Evil" clause of the JSON.org parser's license. By default, json-c + * parses in a non-strict mode which allows trailing commas for array and + * object delarations among other things, so our JSON_ERROR_SYNTAX rescue + * block is not always triggered. It however isn't lenient in exactly the + * same ways as our TRY_FIXING mode, so the assertions in this test are + * a bit more complicated than they ideally would be: + * + * Optional third argument: true if json-c parses the value without + * intervention, false otherwise. Defaults to true. + * + * Optional fourth argument: expected cannonical JSON serialization of + * json-c parsed result. Defaults to the second argument's value. + */ public static function provideParseTryFixing() { return array( - array( "[,]", '[]' ), - array( "[ , ]", '[]' ), + array( "[,]", '[]', false ), + array( "[ , ]", '[]', false ), array( "[ , }", false ), - array( '[1],', false ), + array( '[1],', false, true, '[1]' ), array( "[1,]", '[1]' ), array( "[1\n,]", '[1]' ), array( "[1,\n]", '[1]' ), @@ -182,24 +200,44 @@ class FormatJsonTest extends MediaWikiTestCase { array( "[1\n,\n]\n", '[1]' ), array( '["a,",]', '["a,"]' ), array( "[[1,]\n,[2,\n],[3\n,]]", '[[1],[2],[3]]' ), - array( '[[1,],[2,],[3,]]', false ), // I wish we could parse this, but would need quote parsing - array( '[1,,]', false ), + // I wish we could parse this, but would need quote parsing + array( '[[1,],[2,],[3,]]', false, true, '[[1],[2],[3]]' ), + array( '[1,,]', false, false, '[1]' ), ); } /** * @dataProvider provideParseTryFixing * @param string $value - * @param string|bool $expected + * @param string|bool $expected Expected result with strict parser + * @param bool $jsoncParses Will json-c parse this value without TRY_FIXING? + * @param string|bool $expectedJsonc Expected result with lenient parser + * if different from the strict expectation */ - public function testParseTryFixing( $value, $expected ) { + public function testParseTryFixing( + $value, $expected, + $jsoncParses = true, $expectedJsonc = null + ) { + // PHP5 results are always expected to have isGood() === false + $expectedGoodStatus = false; + + // Check to see if json parser allows trailing commas + if ( json_decode( '[1,]' ) !== null ) { + // Use json-c specific expected result if provided + $expected = ( $expectedJsonc === null ) ? $expected : $expectedJsonc; + // If json-c parses the value natively, expect isGood() === true + $expectedGoodStatus = $jsoncParses; + } + $st = FormatJson::parse( $value, FormatJson::TRY_FIXING ); $this->assertType( 'Status', $st ); if ( $expected === false ) { - $this->assertFalse( $st->isOK() ); + $this->assertFalse( $st->isOK(), 'Expected isOK() == false' ); } else { - $this->assertFalse( $st->isGood() ); - $this->assertTrue( $st->isOK() ); + $this->assertSame( $expectedGoodStatus, $st->isGood(), + 'Expected isGood() == ' . ( $expectedGoodStatus ? 'true' : 'false' ) + ); + $this->assertTrue( $st->isOK(), 'Expected isOK == true' ); $val = FormatJson::encode( $st->getValue(), false, FormatJson::ALL_OK ); $this->assertEquals( $expected, $val ); } @@ -222,6 +260,64 @@ class FormatJsonTest extends MediaWikiTestCase { $this->assertFalse( $st->isOK() ); } + public function provideStripComments() { + return array( + array( '{"a":"b"}', '{"a":"b"}' ), + array( "{\"a\":\"b\"}\n", "{\"a\":\"b\"}\n" ), + array( '/*c*/{"c":"b"}', '{"c":"b"}' ), + array( '{"a":"c"}/*c*/', '{"a":"c"}' ), + array( '/*c//d*/{"c":"b"}', '{"c":"b"}' ), + array( '{/*c*/"c":"b"}', '{"c":"b"}' ), + array( "/*\nc\r\n*/{\"c\":\"b\"}", '{"c":"b"}' ), + array( "//c\n{\"c\":\"b\"}", '{"c":"b"}' ), + array( "//c\r\n{\"c\":\"b\"}", '{"c":"b"}' ), + array( '{"a":"c"}//c', '{"a":"c"}' ), + array( "{\"a-c\"://c\n\"b\"}", '{"a-c":"b"}' ), + array( '{"/*a":"b"}', '{"/*a":"b"}' ), + array( '{"a":"//b"}', '{"a":"//b"}' ), + array( '{"a":"b/*c*/"}', '{"a":"b/*c*/"}' ), + array( "{\"\\\"/*a\":\"b\"}", "{\"\\\"/*a\":\"b\"}" ), + array( '', '' ), + array( '/*c', '' ), + array( '//c', '' ), + array( '"http://example.com"', '"http://example.com"' ), + array( "\0", "\0" ), + array( '"Blåbærsyltetøy"', '"Blåbærsyltetøy"' ), + ); + } + + /** + * @covers FormatJson::stripComments + * @dataProvider provideStripComments + * @param string $json + * @param string $expect + */ + public function testStripComments( $json, $expect ) { + $this->assertSame( $expect, FormatJson::stripComments( $json ) ); + } + + public function provideParseStripComments() { + return array( + array( '/* blah */true', true ), + array( "// blah \ntrue", true ), + array( '[ "a" , /* blah */ "b" ]', array( 'a', 'b' ) ), + ); + } + + /** + * @covers FormatJson::parse + * @covers FormatJson::stripComments + * @dataProvider provideParseStripComments + * @param string $json + * @param mixed $expect + */ + public function testParseStripComments( $json, $expect ) { + $st = FormatJson::parse( $json, FormatJson::STRIP_COMMENTS ); + $this->assertType( 'Status', $st ); + $this->assertTrue( $st->isGood() ); + $this->assertEquals( $expect, $st->getValue() ); + } + /** * Generate a set of test cases for a particular combination of encoder options. * diff --git a/tests/phpunit/includes/ArrayUtilsTest.php b/tests/phpunit/includes/libs/ArrayUtilsTest.php index 7bdb1ca4..b5ea7b72 100644 --- a/tests/phpunit/includes/ArrayUtilsTest.php +++ b/tests/phpunit/includes/libs/ArrayUtilsTest.php @@ -5,7 +5,7 @@ * @group Database */ -class ArrayUtilsTest extends MediaWikiTestCase { +class ArrayUtilsTest extends PHPUnit_Framework_TestCase { private $search; /** diff --git a/tests/phpunit/includes/libs/CSSMinTest.php b/tests/phpunit/includes/libs/CSSMinTest.php index 43c50869..6142f967 100644 --- a/tests/phpunit/includes/libs/CSSMinTest.php +++ b/tests/phpunit/includes/libs/CSSMinTest.php @@ -141,15 +141,57 @@ class CSSMinTest extends MediaWikiTestCase { ); } + public static function provideIsRemoteUrl() { + return array( + array( true, 'http://localhost/w/red.gif?123' ), + array( true, 'https://example.org/x.png' ), + array( true, '//example.org/x.y.z/image.png' ), + array( true, '//localhost/styles.css?query=yes' ), + array( true, 'data:image/gif;base64,R0lGODlhAQABAIAAAP8AADAAACwAAAAAAQABAAACAkQBADs=' ), + array( false, 'x.gif' ), + array( false, '/x.gif' ), + array( false, './x.gif' ), + array( false, '../x.gif' ), + ); + } + + /** + * @dataProvider provideIsRemoteUrl + * @cover CSSMin::isRemoteUrl + */ + public function testIsRemoteUrl( $expect, $url ) { + $this->assertEquals( CSSMin::isRemoteUrl( $url ), $expect ); + } + + public static function provideIsLocalUrls() { + return array( + array( false, 'x.gif' ), + array( true, '/x.gif' ), + array( false, './x.gif' ), + array( false, '../x.gif' ), + ); + } + + /** + * @dataProvider provideIsLocalUrls + * @cover CSSMin::isLocalUrl + */ + public function testIsLocalUrl( $expect, $url ) { + $this->assertEquals( CSSMin::isLocalUrl( $url ), $expect ); + } + public static function provideRemapRemappingCases() { // red.gif and green.gif are one-pixel 35-byte GIFs. // large.png is a 35K PNG that should be non-embeddable. // Full paths start with http://localhost/w/. // Timestamps in output are replaced with 'timestamp'. - // data: URIs for red.gif and green.gif + // data: URIs for red.gif, green.gif, circle.svg $red = 'data:image/gif;base64,R0lGODlhAQABAIAAAP8AADAAACwAAAAAAQABAAACAkQBADs='; $green = 'data:image/gif;base64,R0lGODlhAQABAIAAAACAADAAACwAAAAAAQABAAACAkQBADs='; + $svg = 'data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A' + . '%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%228%22%20height%3D' + . '%228%22%3E%0A%3Ccircle%20cx%3D%224%22%20cy%3D%224%22%20r%3D%222%22%2F%3E%0A%3C%2Fsvg%3E%0A'; return array( array( @@ -234,6 +276,11 @@ class CSSMinTest extends MediaWikiTestCase { "foo { background: url(http://localhost/w/large.png?timestamp); }", ), array( + 'SVG files are embedded without base64 encoding and unnecessary IE 6 and 7 fallback', + 'foo { /* @embed */ background: url(circle.svg); }', + "foo { background: url($svg); }", + ), + array( 'Two regular files in one rule', 'foo { background: url(red.gif), url(green.gif); }', 'foo { background: url(http://localhost/w/red.gif?timestamp), ' diff --git a/tests/phpunit/includes/libs/DeferredStringifierTest.php b/tests/phpunit/includes/libs/DeferredStringifierTest.php new file mode 100644 index 00000000..8b7610ae --- /dev/null +++ b/tests/phpunit/includes/libs/DeferredStringifierTest.php @@ -0,0 +1,50 @@ +<?php + +class DeferredStringifierTest extends PHPUnit_Framework_TestCase { + + /** + * @covers DeferredStringifier + * @dataProvider provideToString + */ + public function testToString( $params, $expected ) { + $class = new ReflectionClass( 'DeferredStringifier' ); + $ds = $class->newInstanceArgs( $params ); + $this->assertEquals( $expected, (string)$ds ); + } + + public static function provideToString() { + return array( + // No args + array( + array( + function() { + return 'foo'; + } + ), + 'foo' + ), + // Has args + array( + array( + function( $i ) { + return $i; + }, + 'bar' + ), + 'bar' + ), + ); + } + + /** + * Verify that the callback is not called if + * it is never converted to a string + */ + public function testCallbackNotCalled() { + $ds = new DeferredStringifier( function() { + throw new Exception( 'This should not be reached!' ); + } ); + // No exception was thrown + $this->assertTrue( true ); + } +} diff --git a/tests/phpunit/includes/libs/GenericArrayObjectTest.php b/tests/phpunit/includes/libs/GenericArrayObjectTest.php index 4911f73a..315bc7ed 100644 --- a/tests/phpunit/includes/libs/GenericArrayObjectTest.php +++ b/tests/phpunit/includes/libs/GenericArrayObjectTest.php @@ -24,10 +24,9 @@ * @ingroup Test * @group GenericArrayObject * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ -abstract class GenericArrayObjectTest extends MediaWikiTestCase { +abstract class GenericArrayObjectTest extends PHPUnit_Framework_TestCase { /** * Returns objects that can serve as elements in the concrete diff --git a/tests/phpunit/includes/libs/HashRingTest.php b/tests/phpunit/includes/libs/HashRingTest.php index 68dfea1f..b51eb3f4 100644 --- a/tests/phpunit/includes/libs/HashRingTest.php +++ b/tests/phpunit/includes/libs/HashRingTest.php @@ -3,7 +3,7 @@ /** * @group HashRing */ -class HashRingTest extends MediaWikiTestCase { +class HashRingTest extends PHPUnit_Framework_TestCase { /** * @covers HashRing */ diff --git a/tests/phpunit/includes/libs/IEUrlExtensionTest.php b/tests/phpunit/includes/libs/IEUrlExtensionTest.php index b7071230..e96953ee 100644 --- a/tests/phpunit/includes/libs/IEUrlExtensionTest.php +++ b/tests/phpunit/includes/libs/IEUrlExtensionTest.php @@ -5,7 +5,7 @@ * @todo tests below for findIE6Extension should be split into... * ...a dataprovider and test method. */ -class IEUrlExtensionTest extends MediaWikiTestCase { +class IEUrlExtensionTest extends PHPUnit_Framework_TestCase { /** * @covers IEUrlExtension::findIE6Extension */ diff --git a/tests/phpunit/includes/libs/IPSetTest.php b/tests/phpunit/includes/libs/IPSetTest.php index d4e5214a..5bbacef4 100644 --- a/tests/phpunit/includes/libs/IPSetTest.php +++ b/tests/phpunit/includes/libs/IPSetTest.php @@ -3,7 +3,7 @@ /** * @group IPSet */ -class IPSetTest extends MediaWikiTestCase { +class IPSetTest extends PHPUnit_Framework_TestCase { /** * Provides test cases for IPSetTest::testIPSet * diff --git a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php index c8795b2e..149a28c1 100644 --- a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php +++ b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php @@ -1,6 +1,6 @@ <?php -class JavaScriptMinifierTest extends MediaWikiTestCase { +class JavaScriptMinifierTest extends PHPUnit_Framework_TestCase { public static function provideCases() { return array( @@ -164,7 +164,7 @@ class JavaScriptMinifierTest extends MediaWikiTestCase { ); } - public static function provideBug32548() { + public static function provideExponentLineBreaking() { return array( array( // This one gets interpreted all together by the prior code; @@ -183,14 +183,13 @@ class JavaScriptMinifierTest extends MediaWikiTestCase { } /** - * @dataProvider provideBug32548 + * @dataProvider provideExponentLineBreaking * @covers JavaScriptMinifier::minify - * @todo give this test a real name explaining what is being tested here */ - public function testBug32548Exponent( $num ) { + public function testExponentLineBreaking( $num ) { // Long line breaking was being incorrectly done between the base and // exponent part of a number, causing a syntax error. The line should - // instead break at the start of the number. + // instead break at the start of the number. (T34548) $prefix = 'var longVarName' . str_repeat( '_', 973 ) . '='; $suffix = ',shortVarName=0;'; diff --git a/tests/phpunit/includes/libs/MWMessagePackTest.php b/tests/phpunit/includes/libs/MWMessagePackTest.php index f80f78df..ec145836 100644 --- a/tests/phpunit/includes/libs/MWMessagePackTest.php +++ b/tests/phpunit/includes/libs/MWMessagePackTest.php @@ -3,7 +3,7 @@ * PHP Unit tests for MWMessagePack * @covers MWMessagePack */ -class MWMessagePackTest extends MediaWikiTestCase { +class MWMessagePackTest extends PHPUnit_Framework_TestCase { /** * Provides test cases for MWMessagePackTest::testMessagePack diff --git a/tests/phpunit/includes/libs/ObjectFactoryTest.php b/tests/phpunit/includes/libs/ObjectFactoryTest.php new file mode 100644 index 00000000..92207325 --- /dev/null +++ b/tests/phpunit/includes/libs/ObjectFactoryTest.php @@ -0,0 +1,60 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +class ObjectFactoryTest extends PHPUnit_Framework_TestCase { + + /** + * @covers ObjectFactory::getObjectFromSpec + */ + public function testClosureExpansionDisabled() { + $obj = ObjectFactory::getObjectFromSpec( array( + 'class' => 'ObjectFactoryTest_Fixture', + 'args' => array( function (){ return 'unwrapped'; }, ), + 'closure_expansion' => false, + ) ); + $this->assertInstanceOf( 'Closure', $obj->args[0] ); + $this->assertSame( 'unwrapped', $obj->args[0]() ); + } + + /** + * @covers ObjectFactory::getObjectFromSpec + */ + public function testClosureExpansionEnabled() { + $obj = ObjectFactory::getObjectFromSpec( array( + 'class' => 'ObjectFactoryTest_Fixture', + 'args' => array( function (){ return 'unwrapped'; }, ), + 'closure_expansion' => true, + ) ); + $this->assertInternalType( 'string', $obj->args[0] ); + $this->assertSame( 'unwrapped', $obj->args[0] ); + + $obj = ObjectFactory::getObjectFromSpec( array( + 'class' => 'ObjectFactoryTest_Fixture', + 'args' => array( function (){ return 'unwrapped'; }, ), + ) ); + $this->assertInternalType( 'string', $obj->args[0] ); + $this->assertSame( 'unwrapped', $obj->args[0] ); + } +} + +class ObjectFactoryTest_Fixture { + public $args; + public function __construct( /*...*/ ) { $this->args = func_get_args(); } +} diff --git a/tests/phpunit/includes/libs/ProcessCacheLRUTest.php b/tests/phpunit/includes/libs/ProcessCacheLRUTest.php index 1a8a1e56..43001979 100644 --- a/tests/phpunit/includes/libs/ProcessCacheLRUTest.php +++ b/tests/phpunit/includes/libs/ProcessCacheLRUTest.php @@ -9,20 +9,20 @@ * * @group Cache */ -class ProcessCacheLRUTest extends MediaWikiTestCase { +class ProcessCacheLRUTest extends PHPUnit_Framework_TestCase { /** * Helper to verify emptiness of a cache object. * Compare against an array so we get the cache content difference. */ - function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) { + protected function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) { $this->assertAttributeEquals( array(), 'cache', $cache, $msg ); } /** * Helper to fill a cache object passed by reference */ - function fillCache( &$cache, $numEntries ) { + protected function fillCache( &$cache, $numEntries ) { // Fill cache with three values for ( $i = 1; $i <= $numEntries; $i++ ) { $cache->set( "cache-key-$i", "prop-$i", "value-$i" ); @@ -33,17 +33,17 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { * Generates an array of what would be expected in cache for a given cache * size and a number of entries filled in sequentially */ - function getExpectedCache( $cacheMaxEntries, $entryToFill ) { + protected function getExpectedCache( $cacheMaxEntries, $entryToFill ) { $expected = array(); if ( $entryToFill === 0 ) { - # The cache is empty! + // The cache is empty! return array(); } elseif ( $entryToFill <= $cacheMaxEntries ) { - # Cache is not fully filled + // Cache is not fully filled $firstKey = 1; } else { - # Cache overflowed + // Cache overflowed $firstKey = 1 + $entryToFill - $cacheMaxEntries; } @@ -62,13 +62,16 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { public function testPhpUnitArrayEquality() { $one = array( 'A' => 1, 'B' => 2 ); $two = array( 'B' => 2, 'A' => 1 ); - $this->assertEquals( $one, $two ); // == - $this->assertNotSame( $one, $two ); // === + // == + $this->assertEquals( $one, $two ); + // === + $this->assertNotSame( $one, $two ); } /** * @dataProvider provideInvalidConstructorArg * @expectedException UnexpectedValueException + * @covers ProcessCacheLRU::__construct */ public function testConstructorGivenInvalidValue( $maxSize ) { new ProcessCacheLRUTestable( $maxSize ); @@ -88,6 +91,11 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { ); } + /** + * @covers ProcessCacheLRU::get + * @covers ProcessCacheLRU::set + * @covers ProcessCacheLRU::het + */ public function testAddAndGetAKey() { $oneCache = new ProcessCacheLRUTestable( 1 ); $this->assertCacheEmpty( $oneCache ); @@ -99,6 +107,10 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { $this->assertEquals( 'value1', $oneCache->get( 'cache-key', 'prop1' ) ); } + /** + * @covers ProcessCacheLRU::set + * @covers ProcessCacheLRU::get + */ public function testDeleteOldKey() { $oneCache = new ProcessCacheLRUTestable( 1 ); $this->assertCacheEmpty( $oneCache ); @@ -113,6 +125,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { * a sequence of always different cache-keys. Meant to verify we correclty * delete the older key. * + * @covers ProcessCacheLRU::set * @dataProvider provideCacheFilling * @param int $cacheMaxEntries Maximum entry the created cache will hold * @param int $entryToFill Number of entries to insert in the created cache. @@ -136,14 +149,18 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { return array( array( 1, 0 ), array( 1, 1 ), - array( 1, 2 ), # overflow - array( 5, 33 ), # overflow + // overflow + array( 1, 2 ), + // overflow + array( 5, 33 ), ); } /** * Create a cache with only one remaining entry then update * the first inserted entry. Should bump it to the top. + * + * @covers ProcessCacheLRU::set */ public function testReplaceExistingKeyShouldBumpEntryToTop() { $maxEntries = 3; @@ -164,6 +181,11 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { ); } + /** + * @covers ProcessCacheLRU::get + * @covers ProcessCacheLRU::set + * @covers ProcessCacheLRU::het + */ public function testRecentlyAccessedKeyStickIn() { $cache = new ProcessCacheLRUTestable( 2 ); $cache->set( 'first', 'prop1', 'value1' ); @@ -182,6 +204,9 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { * filled entry. * Given a cache having 1,2,3 as key, updating 2 should bump 2 to * the top of the queue with the new value: 1,3,2* (* = updated). + * + * @covers ProcessCacheLRU::set + * @covers ProcessCacheLRU::get */ public function testReplaceExistingKeyInAFullCacheShouldBumpToTop() { $maxEntries = 3; @@ -204,6 +229,9 @@ class ProcessCacheLRUTest extends MediaWikiTestCase { ); } + /** + * @covers ProcessCacheLRU::set + */ public function testBumpExistingKeyToTop() { $cache = new ProcessCacheLRUTestable( 3 ); $this->fillCache( $cache, 3 ); diff --git a/tests/phpunit/includes/libs/RunningStatTest.php b/tests/phpunit/includes/libs/RunningStatTest.php index dc5db82c..edfaf162 100644 --- a/tests/phpunit/includes/libs/RunningStatTest.php +++ b/tests/phpunit/includes/libs/RunningStatTest.php @@ -3,7 +3,7 @@ * PHP Unit tests for RunningStat class. * @covers RunningStat */ -class RunningStatTest extends MediaWikiTestCase { +class RunningStatTest extends PHPUnit_Framework_TestCase { public $points = array( 49.7168, 74.3804, 7.0115, 96.5769, 34.9458, diff --git a/tests/phpunit/includes/utils/StringUtilsTest.php b/tests/phpunit/includes/libs/StringUtilsTest.php index 0fdb8e15..7c24fae6 100644 --- a/tests/phpunit/includes/utils/StringUtilsTest.php +++ b/tests/phpunit/includes/libs/StringUtilsTest.php @@ -1,6 +1,6 @@ <?php -class StringUtilsTest extends MediaWikiTestCase { +class StringUtilsTest extends PHPUnit_Framework_TestCase { /** * This tests StringUtils::isUtf8 whenever we have the mbstring extension diff --git a/tests/phpunit/includes/libs/XhprofTest.php b/tests/phpunit/includes/libs/XhprofTest.php new file mode 100644 index 00000000..2440fc08 --- /dev/null +++ b/tests/phpunit/includes/libs/XhprofTest.php @@ -0,0 +1,320 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * @uses Xhprof + * @uses AutoLoader + * @author Bryan Davis <bd808@wikimedia.org> + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. + * @since 1.25 + */ +class XhprofTest extends PHPUnit_Framework_TestCase { + + public function setUp() { + if ( !function_exists( 'xhprof_enable' ) ) { + $this->markTestSkipped( 'No xhprof support detected.' ); + } + } + + /** + * @covers Xhprof::splitKey + * @dataProvider provideSplitKey + */ + public function testSplitKey( $key, $expect ) { + $this->assertSame( $expect, Xhprof::splitKey( $key ) ); + } + + public function provideSplitKey() { + return array( + array( 'main()', array( null, 'main()' ) ), + array( 'foo==>bar', array( 'foo', 'bar' ) ), + array( 'bar@1==>bar@2', array( 'bar@1', 'bar@2' ) ), + array( 'foo==>bar==>baz', array( 'foo', 'bar==>baz' ) ), + array( '==>bar', array( '', 'bar' ) ), + array( '', array( null, '' ) ), + ); + } + + /** + * @covers Xhprof::__construct + * @covers Xhprof::stop + * @covers Xhprof::getRawData + * @dataProvider provideRawData + */ + public function testRawData( $flags, $keys ) { + $xhprof = new Xhprof( array( 'flags' => $flags ) ); + $raw = $xhprof->getRawData(); + $this->assertArrayHasKey( 'main()', $raw ); + foreach ( $keys as $key ) { + $this->assertArrayHasKey( $key, $raw['main()'] ); + } + } + + public function provideRawData() { + $tests = array( + array( 0, array( 'ct', 'wt' ) ), + ); + + if ( defined( 'XHPROF_FLAGS_CPU' ) && defined( 'XHPROF_FLAGS_CPU' ) ) { + $tests[] = array( XHPROF_FLAGS_MEMORY, array( + 'ct', 'wt', 'mu', 'pmu', + ) ); + $tests[] = array( XHPROF_FLAGS_CPU, array( + 'ct', 'wt', 'cpu', + ) ); + $tests[] = array( XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU, array( + 'ct', 'wt', 'mu', 'pmu', 'cpu', + ) ); + } + + return $tests; + } + + /** + * @covers Xhprof::pruneData + */ + public function testInclude() { + $xhprof = $this->getXhprofFixture( array( + 'include' => array( 'main()' ), + ) ); + $raw = $xhprof->getRawData(); + $this->assertArrayHasKey( 'main()', $raw ); + $this->assertArrayHasKey( 'main()==>foo', $raw ); + $this->assertArrayHasKey( 'main()==>xhprof_disable', $raw ); + $this->assertSame( 3, count( $raw ) ); + } + + /** + * Validate the structure of data returned by + * Xhprof::getInclusiveMetrics(). This acts as a guard against unexpected + * structural changes to the returned data in lieu of using a more heavy + * weight typed response object. + * + * @covers Xhprof::getInclusiveMetrics + */ + public function testInclusiveMetricsStructure() { + $metricStruct = array( + 'ct' => 'int', + 'wt' => 'array', + 'cpu' => 'array', + 'mu' => 'array', + 'pmu' => 'array', + ); + $statStruct = array( + 'total' => 'numeric', + 'min' => 'numeric', + 'mean' => 'numeric', + 'max' => 'numeric', + 'variance' => 'numeric', + 'percent' => 'numeric', + ); + + $xhprof = $this->getXhprofFixture(); + $metrics = $xhprof->getInclusiveMetrics(); + + foreach ( $metrics as $name => $metric ) { + $this->assertArrayStructure( $metricStruct, $metric ); + + foreach ( $metricStruct as $key => $type ) { + if ( $type === 'array' ) { + $this->assertArrayStructure( $statStruct, $metric[$key] ); + if ( $name === 'main()' ) { + $this->assertEquals( 100, $metric[$key]['percent'] ); + } + } + } + } + } + + /** + * Validate the structure of data returned by + * Xhprof::getCompleteMetrics(). This acts as a guard against unexpected + * structural changes to the returned data in lieu of using a more heavy + * weight typed response object. + * + * @covers Xhprof::getCompleteMetrics + */ + public function testCompleteMetricsStructure() { + $metricStruct = array( + 'ct' => 'int', + 'wt' => 'array', + 'cpu' => 'array', + 'mu' => 'array', + 'pmu' => 'array', + 'calls' => 'array', + 'subcalls' => 'array', + ); + $statsMetrics = array( 'wt', 'cpu', 'mu', 'pmu' ); + $statStruct = array( + 'total' => 'numeric', + 'min' => 'numeric', + 'mean' => 'numeric', + 'max' => 'numeric', + 'variance' => 'numeric', + 'percent' => 'numeric', + 'exclusive' => 'numeric', + ); + + $xhprof = $this->getXhprofFixture(); + $metrics = $xhprof->getCompleteMetrics(); + + foreach ( $metrics as $name => $metric ) { + $this->assertArrayStructure( $metricStruct, $metric, $name ); + + foreach ( $metricStruct as $key => $type ) { + if ( in_array( $key, $statsMetrics ) ) { + $this->assertArrayStructure( + $statStruct, $metric[$key], $key + ); + $this->assertLessThanOrEqual( + $metric[$key]['total'], $metric[$key]['exclusive'] + ); + } + } + } + } + + /** + * @covers Xhprof::getCallers + * @covers Xhprof::getCallees + * @uses Xhprof + */ + public function testEdges() { + $xhprof = $this->getXhprofFixture(); + $this->assertSame( array(), $xhprof->getCallers( 'main()' ) ); + $this->assertSame( array( 'foo', 'xhprof_disable' ), + $xhprof->getCallees( 'main()' ) + ); + $this->assertSame( array( 'main()' ), + $xhprof->getCallers( 'foo' ) + ); + $this->assertSame( array(), $xhprof->getCallees( 'strlen' ) ); + } + + /** + * @covers Xhprof::getCriticalPath + * @uses Xhprof + */ + public function testCriticalPath() { + $xhprof = $this->getXhprofFixture(); + $path = $xhprof->getCriticalPath(); + + $last = null; + foreach ( $path as $key => $value ) { + list( $func, $call ) = Xhprof::splitKey( $key ); + $this->assertSame( $last, $func ); + $last = $call; + } + $this->assertSame( $last, 'bar@1' ); + } + + /** + * Get an Xhprof instance that has been primed with a set of known testing + * data. Tests for the Xhprof class should laregly be concerned with + * evaluating the manipulations of the data collected by xhprof rather + * than the data collection process itself. + * + * The returned Xhprof instance primed will be with a data set created by + * running this trivial program using the PECL xhprof implementation: + * @code + * function bar( $x ) { + * if ( $x > 0 ) { + * bar($x - 1); + * } + * } + * function foo() { + * for ( $idx = 0; $idx < 2; $idx++ ) { + * bar( $idx ); + * $x = strlen( 'abc' ); + * } + * } + * xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY ); + * foo(); + * $x = xhprof_disable(); + * var_export( $x ); + * @endcode + * + * @return Xhprof + */ + protected function getXhprofFixture( array $opts = array() ) { + $xhprof = new Xhprof( $opts ); + $xhprof->loadRawData( array ( + 'foo==>bar' => array ( + 'ct' => 2, + 'wt' => 57, + 'cpu' => 92, + 'mu' => 1896, + 'pmu' => 0, + ), + 'foo==>strlen' => array ( + 'ct' => 2, + 'wt' => 21, + 'cpu' => 141, + 'mu' => 752, + 'pmu' => 0, + ), + 'bar==>bar@1' => array ( + 'ct' => 1, + 'wt' => 18, + 'cpu' => 19, + 'mu' => 752, + 'pmu' => 0, + ), + 'main()==>foo' => array ( + 'ct' => 1, + 'wt' => 304, + 'cpu' => 307, + 'mu' => 4008, + 'pmu' => 0, + ), + 'main()==>xhprof_disable' => array ( + 'ct' => 1, + 'wt' => 8, + 'cpu' => 10, + 'mu' => 768, + 'pmu' => 392, + ), + 'main()' => array ( + 'ct' => 1, + 'wt' => 353, + 'cpu' => 351, + 'mu' => 6112, + 'pmu' => 1424, + ), + ) ); + return $xhprof; + } + + /** + * Assert that the given array has the described structure. + * + * @param array $struct Array of key => type mappings + * @param array $actual Array to check + * @param string $label + */ + protected function assertArrayStructure( $struct, $actual, $label = null ) { + $this->assertInternalType( 'array', $actual, $label ); + $this->assertCount( count($struct), $actual, $label ); + foreach ( $struct as $key => $type ) { + $this->assertArrayHasKey( $key, $actual ); + $this->assertInternalType( $type, $actual[$key] ); + } + } +} diff --git a/tests/phpunit/includes/XmlTypeCheckTest.php b/tests/phpunit/includes/libs/XmlTypeCheckTest.php index 6ad97fd4..f0ba934e 100644 --- a/tests/phpunit/includes/XmlTypeCheckTest.php +++ b/tests/phpunit/includes/libs/XmlTypeCheckTest.php @@ -5,7 +5,7 @@ * @group Xml * @covers XMLTypeCheck */ -class XmlTypeCheckTest extends MediaWikiTestCase { +class XmlTypeCheckTest extends PHPUnit_Framework_TestCase { const WELL_FORMED_XML = "<root><child /></root>"; const MAL_FORMED_XML = "<root><child /></error>"; const XML_WITH_PIH = '<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="/w/index.php"?><svg><child /></svg>'; diff --git a/tests/phpunit/includes/libs/composer/ComposerJsonTest.php b/tests/phpunit/includes/libs/composer/ComposerJsonTest.php new file mode 100644 index 00000000..0c58b65a --- /dev/null +++ b/tests/phpunit/includes/libs/composer/ComposerJsonTest.php @@ -0,0 +1,57 @@ +<?php + +class ComposerJsonTest extends MediaWikiTestCase { + + private $json, $json2; + + public function setUp() { + parent::setUp(); + global $IP; + $this->json = "$IP/tests/phpunit/data/composer/composer.json"; + $this->json2 = "$IP/tests/phpunit/data/composer/new-composer.json"; + } + + public static function provideGetHash() { + return array( + array( 'json', 'cc6e7fc565b246cb30b0cac103a2b31e' ), + array( 'json2', '19921dd1fc457f1b00561da932432001' ), + ); + } + + /** + * @dataProvider provideGetHash + * @covers ComposerJson::getHash + */ + public function testIsHashUpToDate( $file, $expected ) { + $json = new ComposerJson( $this->$file ); + $this->assertEquals( $expected, $json->getHash() ); + } + + /** + * @covers ComposerJson::getRequiredDependencies + */ + public function testGetRequiredDependencies() { + $json = new ComposerJson( $this->json ); + $this->assertArrayEquals( array( + 'cdb/cdb' => '1.0.0', + 'cssjanus/cssjanus' => '1.1.1', + 'leafo/lessphp' => '0.5.0', + 'psr/log' => '1.0.0', + ), $json->getRequiredDependencies(), false, true ); + } + + public static function provideNormalizeVersion() { + return array( + array( 'v1.0.0', '1.0.0' ), + array( '0.0.5', '0.0.5' ), + ); + } + + /** + * @dataProvider provideNormalizeVersion + * @covers ComposerJson::normalizeVersion + */ + public function testNormalizeVersion( $input, $expected ) { + $this->assertEquals( $expected, ComposerJson::normalizeVersion( $input ) ); + } +} diff --git a/tests/phpunit/includes/libs/composer/ComposerLockTest.php b/tests/phpunit/includes/libs/composer/ComposerLockTest.php new file mode 100644 index 00000000..b5fd5f6e --- /dev/null +++ b/tests/phpunit/includes/libs/composer/ComposerLockTest.php @@ -0,0 +1,62 @@ +<?php + +class ComposerLockTest extends MediaWikiTestCase { + + private $lock; + + public function setUp() { + parent::setUp(); + global $IP; + $this->lock = "$IP/tests/phpunit/data/composer/composer.lock"; + } + + /** + * @covers ComposerLock::getHash + */ + public function testGetHash() { + $lock = new ComposerLock( $this->lock ); + $this->assertEquals( 'a3bb80b0ac4c4a31e52574d48c032923', $lock->getHash() ); + } + + /** + * @covers ComposerLock::getInstalledDependencies + */ + public function testGetInstalledDependencies() { + $lock = new ComposerLock( $this->lock ); + $this->assertArrayEquals( array( + 'wikimedia/cdb' => array( + 'version' => '1.0.1', + 'type' => 'library', + ), + 'cssjanus/cssjanus' => array( + 'version' => '1.1.1', + 'type' => 'library', + ), + 'leafo/lessphp' => array( + 'version' => '0.5.0', + 'type' => 'library', + ), + 'psr/log' => array( + 'version' => '1.0.0', + 'type' => 'library', + ), + 'oojs/oojs-ui' => array( + 'version' => '0.6.0', + 'type' => 'library', + ), + 'composer/installers' => array( + 'version' => '1.0.19', + 'type' => 'composer-installer', + ), + 'mediawiki/translate' => array( + 'version' => '2014.12', + 'type' => 'mediawiki-extension', + ), + 'mediawiki/universal-language-selector' => array( + 'version' => '2014.12', + 'type' => 'mediawiki-extension', + ), + ), $lock->getInstalledDependencies(), false, true ); + } + +} diff --git a/tests/phpunit/includes/logging/LogFormatterTest.php b/tests/phpunit/includes/logging/LogFormatterTest.php index 6210d098..515990e6 100644 --- a/tests/phpunit/includes/logging/LogFormatterTest.php +++ b/tests/phpunit/includes/logging/LogFormatterTest.php @@ -239,4 +239,57 @@ class LogFormatterTest extends MediaWikiLangTestCase { $this->assertEquals( $comment, $formatter->getComment() ); } + + /** + * @dataProvider provideApiParamFormatting + * @covers LogFormatter::formatParametersForApi + * @covers LogFormatter::formatParameterValueForApi + */ + public function testApiParamFormatting( $key, $value, $expected ) { + $entry = $this->newLogEntry( 'param', array( $key => $value ) ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + ApiResult::setIndexedTagName( $expected, 'param' ); + ApiResult::setArrayType( $expected, 'assoc' ); + + $this->assertEquals( $expected, $formatter->formatParametersForApi() ); + } + + public static function provideApiParamFormatting() { + return array( + array( 0, 'value', array( 'value' ) ), + array( 'named', 'value', array( 'named' => 'value' ) ), + array( '::key', 'value', array( 'key' => 'value' ) ), + array( '4::key', 'value', array( 'key' => 'value' ) ), + array( '4:raw:key', 'value', array( 'key' => 'value' ) ), + array( '4:plain:key', 'value', array( 'key' => 'value' ) ), + array( '4:bool:key', '1', array( 'key' => true ) ), + array( '4:bool:key', '0', array( 'key' => false ) ), + array( '4:number:key', '123', array( 'key' => 123 ) ), + array( '4:number:key', '123.5', array( 'key' => 123.5 ) ), + array( '4:array:key', array(), array( 'key' => array( ApiResult::META_TYPE => 'array' ) ) ), + array( '4:assoc:key', array(), array( 'key' => array( ApiResult::META_TYPE => 'assoc' ) ) ), + array( '4:kvp:key', array(), array( 'key' => array( ApiResult::META_TYPE => 'kvp' ) ) ), + array( '4:timestamp:key', '20150102030405', array( 'key' => '2015-01-02T03:04:05Z' ) ), + array( '4:msg:key', 'parentheses', array( + 'key_key' => 'parentheses', + 'key_text' => wfMessage( 'parentheses' )->text(), + ) ), + array( '4:msg-content:key', 'parentheses', array( + 'key_key' => 'parentheses', + 'key_text' => wfMessage( 'parentheses' )->inContentLanguage()->text(), + ) ), + array( '4:title:key', 'project:foo', array( + 'key_ns' => NS_PROJECT, + 'key_title' => Title::newFromText( 'project:foo' )->getFullText(), + ) ), + array( '4:title-link:key', 'project:foo', array( + 'key_ns' => NS_PROJECT, + 'key_title' => Title::newFromText( 'project:foo' )->getFullText(), + ) ), + array( '4:user:key', 'foo', array( 'key' => 'Foo' ) ), + array( '4:user-link:key', 'foo', array( 'key' => 'Foo' ) ), + ); + } } diff --git a/tests/phpunit/includes/mail/MailAddressTest.php b/tests/phpunit/includes/mail/MailAddressTest.php index 2d078120..18d2acdf 100644 --- a/tests/phpunit/includes/mail/MailAddressTest.php +++ b/tests/phpunit/includes/mail/MailAddressTest.php @@ -14,6 +14,9 @@ class MailAddressTest extends MediaWikiTestCase { * @covers MailAddress::newFromUser */ public function testNewFromUser() { + if ( wfIsWindows() ) { + $this->markTestSkipped( 'This test only works on non-Windows platforms' ); + } $user = $this->getMock( 'User' ); $user->expects( $this->any() )->method( 'getName' )->will( $this->returnValue( 'UserName' ) ); $user->expects( $this->any() )->method( 'getEmail' )->will( $this->returnValue( 'foo@bar.baz' ) ); @@ -49,6 +52,7 @@ class MailAddressTest extends MediaWikiTestCase { array( false, 'foo@bar.baz', 'AUserName', 'Some real name', 'AUserName <foo@bar.baz>' ), array( false, 'foo@bar.baz', '', '', 'foo@bar.baz' ), array( true, 'foo@bar.baz', '', '', 'foo@bar.baz' ), + array( true, '', '', '', '' ), ); } @@ -59,5 +63,4 @@ class MailAddressTest extends MediaWikiTestCase { $ma = new MailAddress( 'some@email.com', 'UserName', 'A real name' ); $this->assertEquals( $ma->toString(), (string)$ma ); } - -}
\ No newline at end of file +} diff --git a/tests/phpunit/includes/media/BitmapScalingTest.php b/tests/phpunit/includes/media/BitmapScalingTest.php index 1972c969..e4415ece 100644 --- a/tests/phpunit/includes/media/BitmapScalingTest.php +++ b/tests/phpunit/includes/media/BitmapScalingTest.php @@ -113,7 +113,7 @@ class BitmapScalingTest extends MediaWikiTestCase { $file = new FakeDimensionFile( array( 4000, 4000 ) ); $handler = new BitmapHandler; $params = array( 'width' => '3700' ); // Still bigger than max size. - $this->assertEquals( 'TransformParameterError', + $this->assertEquals( 'TransformTooBigImageAreaError', get_class( $handler->doTransform( $file, 'dummy path', '', $params ) ) ); } @@ -125,7 +125,7 @@ class BitmapScalingTest extends MediaWikiTestCase { $file->mustRender = true; $handler = new BitmapHandler; $params = array( 'width' => '5000' ); // Still bigger than max size. - $this->assertEquals( 'TransformParameterError', + $this->assertEquals( 'TransformTooBigImageAreaError', get_class( $handler->doTransform( $file, 'dummy path', '', $params ) ) ); } diff --git a/tests/phpunit/includes/media/FormatMetadataTest.php b/tests/phpunit/includes/media/FormatMetadataTest.php index 002e2cb9..54758f94 100644 --- a/tests/phpunit/includes/media/FormatMetadataTest.php +++ b/tests/phpunit/includes/media/FormatMetadataTest.php @@ -68,4 +68,36 @@ class FormatMetadataTest extends MediaWikiMediaTestCase { // TODO: more test cases ); } + + /** + * @param mixed $input + * @param mixed $output + * @dataProvider provideResolveMultivalueValue + * @covers FormatMetadata::resolveMultivalueValue + */ + public function testResolveMultivalueValue( $input, $output ) { + $formatMetadata = new FormatMetadata(); + $class = new ReflectionClass( 'FormatMetadata' ); + $method = $class->getMethod( 'resolveMultivalueValue' ); + $method->setAccessible( true ); + $actualInput = $method->invoke( $formatMetadata, $input ); + $this->assertEquals( $output, $actualInput ); + } + + public function provideResolveMultivalueValue() { + return array( + 'nonArray' => array( 'foo', 'foo' ), + 'multiValue' => array( array( 'first', 'second', 'third', '_type' => 'ol' ), 'first' ), + 'noType' => array( array( 'first', 'second', 'third' ), 'first' ), + 'typeFirst' => array( array( '_type' => 'ol', 'first', 'second', 'third' ), 'first' ), + 'multilang' => array( + array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ), + array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ), + ), + 'multilang-multivalue' => array( + array( 'en' => array( 'first', 'second' ), 'de' => array( 'Erste', 'Zweite' ), '_type' => 'lang' ), + array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ), + ), + ); + } } diff --git a/tests/phpunit/includes/media/MediaHandlerTest.php b/tests/phpunit/includes/media/MediaHandlerTest.php index d8cfcc45..78ea9530 100644 --- a/tests/phpunit/includes/media/MediaHandlerTest.php +++ b/tests/phpunit/includes/media/MediaHandlerTest.php @@ -7,50 +7,62 @@ class MediaHandlerTest extends MediaWikiTestCase { /** * @covers MediaHandler::fitBoxWidth - * @todo split into a dataprovider and test method + * + * @dataProvider provideTestFitBoxWidth */ - public function testFitBoxWidth() { - $vals = array( - array( - 'width' => 50, - 'height' => 50, - 'tests' => array( + public function testFitBoxWidth( $width, $height, $max, $expected ) { + $y = round( $expected * $height / $width ); + $result = MediaHandler::fitBoxWidth( $width, $height, $max ); + $y2 = round( $result * $height / $width ); + $this->assertEquals( $expected, + $result, + "($width, $height, $max) wanted: {$expected}x$y, got: {z$result}x$y2" ); + } + + public static function provideTestFitBoxWidth() { + return array_merge( + static::generateTestFitBoxWidthData( 50, 50, array( 50 => 50, 17 => 17, - 18 => 18 ) ), - array( - 'width' => 366, - 'height' => 300, - 'tests' => array( + 18 => 18 ) + ), + static::generateTestFitBoxWidthData( 366, 300, array( 50 => 61, 17 => 21, - 18 => 22 ) ), - array( - 'width' => 300, - 'height' => 366, - 'tests' => array( + 18 => 22 ) + ), + static::generateTestFitBoxWidthData( 300, 366, array( 50 => 41, 17 => 14, - 18 => 15 ) ), - array( - 'width' => 100, - 'height' => 400, - 'tests' => array( + 18 => 15 ) + ), + static::generateTestFitBoxWidthData( 100, 400, array( 50 => 12, 17 => 4, - 18 => 4 ) ) ); - foreach ( $vals as $row ) { - $tests = $row['tests']; - $height = $row['height']; - $width = $row['width']; - foreach ( $tests as $max => $expected ) { - $y = round( $expected * $height / $width ); - $result = MediaHandler::fitBoxWidth( $width, $height, $max ); - $y2 = round( $result * $height / $width ); - $this->assertEquals( $expected, - $result, - "($width, $height, $max) wanted: {$expected}x$y, got: {$result}x$y2" ); - } + 18 => 4 ) + ) + ); + } + + /** + * Generate single test cases by combining the dimensions and tests contents + * + * It creates: + * [$width, $height, $max, $expected], + * [$width, $height, $max2, $expected2], ... + * out of parameters: + * $width, $height, { $max => $expected, $max2 => $expected2, ... } + * + * @param $width int + * @param $height int + * @param $tests array associative array of $max => $expected values + * @return array + */ + private static function generateTestFitBoxWidthData( $width, $height, $tests ) { + $result = array(); + foreach ( $tests as $max => $expected ) { + $result[] = array( $width, $height, $max, $expected ); } + return $result; } } diff --git a/tests/phpunit/includes/media/SVGMetadataExtractorTest.php b/tests/phpunit/includes/media/SVGMetadataExtractorTest.php index ab33d1c2..0241aec4 100644 --- a/tests/phpunit/includes/media/SVGMetadataExtractorTest.php +++ b/tests/phpunit/includes/media/SVGMetadataExtractorTest.php @@ -6,11 +6,6 @@ */ class SVGMetadataExtractorTest extends MediaWikiTestCase { - protected function setUp() { - parent::setUp(); - AutoLoader::loadClass( 'SVGMetadataExtractorTest' ); - } - /** * @dataProvider provideSvgFiles */ diff --git a/tests/phpunit/includes/normal/CleanUpTest.php b/tests/phpunit/includes/normal/CleanUpTest.php deleted file mode 100644 index f4b469b8..00000000 --- a/tests/phpunit/includes/normal/CleanUpTest.php +++ /dev/null @@ -1,409 +0,0 @@ -<?php -/** - * Tests for UtfNormal::cleanUp() function. - * - * Copyright © 2004 Brion Vibber <brion@pobox.com> - * https://www.mediawiki.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - */ - -/** - * Additional tests for UtfNormal::cleanUp() function, inclusion - * regression checks for known problems. - * Requires PHPUnit. - * - * @ingroup UtfNormal - * @group Large - * - * @todo covers tags, will be UtfNormal::cleanUp once the below is resolved - * @todo split me into test methods and providers per the below comment - * - * We ignore code coverage for this test suite until they are rewritten - * to use data providers (bug 46561). - * @codeCoverageIgnore - */ -class CleanUpTest extends MediaWikiTestCase { - /** @todo document */ - public function testAscii() { - $text = 'This is plain ASCII text.'; - $this->assertEquals( $text, UtfNormal::cleanUp( $text ) ); - } - - /** @todo document */ - public function testNull() { - $text = "a \x00 null"; - $expect = "a \xef\xbf\xbd null"; - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testLatin() { - $text = "L'\xc3\xa9cole"; - $this->assertEquals( $text, UtfNormal::cleanUp( $text ) ); - } - - /** @todo document */ - public function testLatinNormal() { - $text = "L'e\xcc\x81cole"; - $expect = "L'\xc3\xa9cole"; - $this->assertEquals( $expect, UtfNormal::cleanUp( $text ) ); - } - - /** - * This test is *very* expensive! - * @todo document - */ - function XtestAllChars() { - $rep = UTF8_REPLACEMENT; - for ( $i = 0x0; $i < UNICODE_MAX; $i++ ) { - $char = codepointToUtf8( $i ); - $clean = UtfNormal::cleanUp( $char ); - $x = sprintf( "%04X", $i ); - - if ( $i % 0x1000 == 0 ) { - echo "U+$x\n"; - } - - if ( $i == 0x0009 || - $i == 0x000a || - $i == 0x000d || - ( $i > 0x001f && $i < UNICODE_SURROGATE_FIRST ) || - ( $i > UNICODE_SURROGATE_LAST && $i < 0xfffe ) || - ( $i > 0xffff && $i <= UNICODE_MAX ) - ) { - if ( isset( UtfNormal::$utfCanonicalComp[$char] ) - || isset( UtfNormal::$utfCanonicalDecomp[$char] ) - ) { - $comp = UtfNormal::NFC( $char ); - $this->assertEquals( - bin2hex( $comp ), - bin2hex( $clean ), - "U+$x should be decomposed" ); - } else { - $this->assertEquals( - bin2hex( $char ), - bin2hex( $clean ), - "U+$x should be intact" ); - } - } else { - $this->assertEquals( bin2hex( $rep ), bin2hex( $clean ), $x ); - } - } - } - - /** @todo document */ - public static function provideAllBytes() { - return array( - array( '', '' ), - array( 'x', '' ), - array( '', 'x' ), - array( 'x', 'x' ), - ); - } - - /** - * @dataProvider provideAllBytes - * @todo document - */ - function testBytes( $head, $tail ) { - for ( $i = 0x0; $i < 256; $i++ ) { - $char = $head . chr( $i ) . $tail; - $clean = UtfNormal::cleanUp( $char ); - $x = sprintf( "%02X", $i ); - - if ( $i == 0x0009 || - $i == 0x000a || - $i == 0x000d || - ( $i > 0x001f && $i < 0x80 ) - ) { - $this->assertEquals( - bin2hex( $char ), - bin2hex( $clean ), - "ASCII byte $x should be intact" ); - if ( $char != $clean ) { - return; - } - } else { - $norm = $head . UTF8_REPLACEMENT . $tail; - $this->assertEquals( - bin2hex( $norm ), - bin2hex( $clean ), - "Forbidden byte $x should be rejected" ); - if ( $norm != $clean ) { - return; - } - } - } - } - - /** - * @dataProvider provideAllBytes - * @todo document - */ - function testDoubleBytes( $head, $tail ) { - for ( $first = 0xc0; $first < 0x100; $first += 2 ) { - for ( $second = 0x80; $second < 0x100; $second += 2 ) { - $char = $head . chr( $first ) . chr( $second ) . $tail; - $clean = UtfNormal::cleanUp( $char ); - $x = sprintf( "%02X,%02X", $first, $second ); - if ( $first > 0xc1 && - $first < 0xe0 && - $second < 0xc0 - ) { - $norm = UtfNormal::NFC( $char ); - $this->assertEquals( - bin2hex( $norm ), - bin2hex( $clean ), - "Pair $x should be intact" ); - if ( $norm != $clean ) { - return; - } - } elseif ( $first > 0xfd || $second > 0xbf ) { - # fe and ff are not legal head bytes -- expect two replacement chars - $norm = $head . UTF8_REPLACEMENT . UTF8_REPLACEMENT . $tail; - $this->assertEquals( - bin2hex( $norm ), - bin2hex( $clean ), - "Forbidden pair $x should be rejected" ); - if ( $norm != $clean ) { - return; - } - } else { - $norm = $head . UTF8_REPLACEMENT . $tail; - $this->assertEquals( - bin2hex( $norm ), - bin2hex( $clean ), - "Forbidden pair $x should be rejected" ); - if ( $norm != $clean ) { - return; - } - } - } - } - } - - /** - * @dataProvider provideAllBytes - * @todo document - */ - function testTripleBytes( $head, $tail ) { - for ( $first = 0xc0; $first < 0x100; $first += 2 ) { - for ( $second = 0x80; $second < 0x100; $second += 2 ) { - #for( $third = 0x80; $third < 0x100; $third++ ) { - for ( $third = 0x80; $third < 0x81; $third++ ) { - $char = $head . chr( $first ) . chr( $second ) . chr( $third ) . $tail; - $clean = UtfNormal::cleanUp( $char ); - $x = sprintf( "%02X,%02X,%02X", $first, $second, $third ); - - if ( $first >= 0xe0 && - $first < 0xf0 && - $second < 0xc0 && - $third < 0xc0 - ) { - if ( $first == 0xe0 && $second < 0xa0 ) { - $this->assertEquals( - bin2hex( $head . UTF8_REPLACEMENT . $tail ), - bin2hex( $clean ), - "Overlong triplet $x should be rejected" ); - } elseif ( $first == 0xed && - ( chr( $first ) . chr( $second ) . chr( $third ) ) >= UTF8_SURROGATE_FIRST - ) { - $this->assertEquals( - bin2hex( $head . UTF8_REPLACEMENT . $tail ), - bin2hex( $clean ), - "Surrogate triplet $x should be rejected" ); - } else { - $this->assertEquals( - bin2hex( UtfNormal::NFC( $char ) ), - bin2hex( $clean ), - "Triplet $x should be intact" ); - } - } elseif ( $first > 0xc1 && $first < 0xe0 && $second < 0xc0 ) { - $this->assertEquals( - bin2hex( UtfNormal::NFC( $head . chr( $first ) . - chr( $second ) ) . UTF8_REPLACEMENT . $tail ), - bin2hex( $clean ), - "Valid 2-byte $x + broken tail" ); - } elseif ( $second > 0xc1 && $second < 0xe0 && $third < 0xc0 ) { - $this->assertEquals( - bin2hex( $head . UTF8_REPLACEMENT . - UtfNormal::NFC( chr( $second ) . chr( $third ) . $tail ) ), - bin2hex( $clean ), - "Broken head + valid 2-byte $x" ); - } elseif ( ( $first > 0xfd || $second > 0xfd ) && - ( ( $second > 0xbf && $third > 0xbf ) || - ( $second < 0xc0 && $third < 0xc0 ) || - ( $second > 0xfd ) || - ( $third > 0xfd ) ) - ) { - # fe and ff are not legal head bytes -- expect three replacement chars - $this->assertEquals( - bin2hex( $head . UTF8_REPLACEMENT . UTF8_REPLACEMENT . UTF8_REPLACEMENT . $tail ), - bin2hex( $clean ), - "Forbidden triplet $x should be rejected" ); - } elseif ( $first > 0xc2 && $second < 0xc0 && $third < 0xc0 ) { - $this->assertEquals( - bin2hex( $head . UTF8_REPLACEMENT . $tail ), - bin2hex( $clean ), - "Forbidden triplet $x should be rejected" ); - } else { - $this->assertEquals( - bin2hex( $head . UTF8_REPLACEMENT . UTF8_REPLACEMENT . $tail ), - bin2hex( $clean ), - "Forbidden triplet $x should be rejected" ); - } - } - } - } - } - - /** @todo document */ - public function testChunkRegression() { - # Check for regression against a chunking bug - $text = "\x46\x55\xb8" . - "\xdc\x96" . - "\xee" . - "\xe7" . - "\x44" . - "\xaa" . - "\x2f\x25"; - $expect = "\x46\x55\xef\xbf\xbd" . - "\xdc\x96" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\x44" . - "\xef\xbf\xbd" . - "\x2f\x25"; - - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testInterposeRegression() { - $text = "\x4e\x30" . - "\xb1" . # bad tail - "\x3a" . - "\x92" . # bad tail - "\x62\x3a" . - "\x84" . # bad tail - "\x43" . - "\xc6" . # bad head - "\x3f" . - "\x92" . # bad tail - "\xad" . # bad tail - "\x7d" . - "\xd9\x95"; - - $expect = "\x4e\x30" . - "\xef\xbf\xbd" . - "\x3a" . - "\xef\xbf\xbd" . - "\x62\x3a" . - "\xef\xbf\xbd" . - "\x43" . - "\xef\xbf\xbd" . - "\x3f" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\x7d" . - "\xd9\x95"; - - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testOverlongRegression() { - $text = "\x67" . - "\x1a" . # forbidden ascii - "\xea" . # bad head - "\xc1\xa6" . # overlong sequence - "\xad" . # bad tail - "\x1c" . # forbidden ascii - "\xb0" . # bad tail - "\x3c" . - "\x9e"; # bad tail - $expect = "\x67" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\x3c" . - "\xef\xbf\xbd"; - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testSurrogateRegression() { - $text = "\xed\xb4\x96" . # surrogate 0xDD16 - "\x83" . # bad tail - "\xb4" . # bad tail - "\xac"; # bad head - $expect = "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd"; - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testBomRegression() { - $text = "\xef\xbf\xbe" . # U+FFFE, illegal char - "\xb2" . # bad tail - "\xef" . # bad head - "\x59"; - $expect = "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\xef\xbf\xbd" . - "\x59"; - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testForbiddenRegression() { - $text = "\xef\xbf\xbf"; # U+FFFF, illegal char - $expect = "\xef\xbf\xbd"; - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } - - /** @todo document */ - public function testHangulRegression() { - $text = "\xed\x9c\xaf" . # Hangul char - "\xe1\x87\x81"; # followed by another final jamo - $expect = $text; # Should *not* change. - $this->assertEquals( - bin2hex( $expect ), - bin2hex( UtfNormal::cleanUp( $text ) ) ); - } -} diff --git a/tests/phpunit/includes/objectcache/BagOStuffTest.php b/tests/phpunit/includes/objectcache/BagOStuffTest.php index 987b6e64..4516bb4e 100644 --- a/tests/phpunit/includes/objectcache/BagOStuffTest.php +++ b/tests/phpunit/includes/objectcache/BagOStuffTest.php @@ -1,8 +1,6 @@ <?php /** - * This class will test BagOStuff. - * - * @author Matthias Mullie <mmullie@wikimedia.org> + * @author Matthias Mullie <mmullie@wikimedia.org> */ class BagOStuffTest extends MediaWikiTestCase { private $cache; @@ -23,6 +21,10 @@ class BagOStuffTest extends MediaWikiTestCase { $this->cache->delete( wfMemcKey( 'test' ) ); } + /** + * @covers BagOStuff::merge + * @covers BagOStuff::mergeViaLock + */ public function testMerge() { $key = wfMemcKey( 'test' ); @@ -100,6 +102,9 @@ class BagOStuffTest extends MediaWikiTestCase { } } + /** + * @covers BagOStuff::add + */ public function testAdd() { $key = wfMemcKey( 'test' ); $this->assertTrue( $this->cache->add( $key, 'test' ) ); @@ -125,6 +130,9 @@ class BagOStuffTest extends MediaWikiTestCase { $this->assertEquals( $expectedValue, $actualValue, 'Value should be 1 after incrementing' ); } + /** + * @covers BagOStuff::getMulti + */ public function testGetMulti() { $value1 = array( 'this' => 'is', 'a' => 'test' ); $value2 = array( 'this' => 'is', 'another' => 'test' ); diff --git a/tests/phpunit/includes/ArticleTablesTest.php b/tests/phpunit/includes/page/ArticleTablesTest.php index 9f2b7a05..9f2b7a05 100644 --- a/tests/phpunit/includes/ArticleTablesTest.php +++ b/tests/phpunit/includes/page/ArticleTablesTest.php diff --git a/tests/phpunit/includes/ArticleTest.php b/tests/phpunit/includes/page/ArticleTest.php index ae069eaf..ae069eaf 100644 --- a/tests/phpunit/includes/ArticleTest.php +++ b/tests/phpunit/includes/page/ArticleTest.php diff --git a/tests/phpunit/includes/ImagePage404Test.php b/tests/phpunit/includes/page/ImagePage404Test.php index 197a2b32..197a2b32 100644 --- a/tests/phpunit/includes/ImagePage404Test.php +++ b/tests/phpunit/includes/page/ImagePage404Test.php diff --git a/tests/phpunit/includes/ImagePageTest.php b/tests/phpunit/includes/page/ImagePageTest.php index 3c255b5f..3c255b5f 100644 --- a/tests/phpunit/includes/ImagePageTest.php +++ b/tests/phpunit/includes/page/ImagePageTest.php diff --git a/tests/phpunit/includes/WikiPageTest.php b/tests/phpunit/includes/page/WikiPageTest.php index 7f7945b8..c011e9a9 100644 --- a/tests/phpunit/includes/WikiPageTest.php +++ b/tests/phpunit/includes/page/WikiPageTest.php @@ -55,8 +55,8 @@ class WikiPageTest extends MediaWikiLangTestCase { } /** - * @param Title $title - * @param string $model + * @param Title|string $title + * @param string|null $model * @return WikiPage */ protected function newPage( $title, $model = null ) { diff --git a/tests/phpunit/includes/WikiPageTestContentHandlerUseDB.php b/tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php index 3db76280..3db76280 100644 --- a/tests/phpunit/includes/WikiPageTestContentHandlerUseDB.php +++ b/tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php diff --git a/tests/phpunit/includes/parser/NewParserTest.php b/tests/phpunit/includes/parser/NewParserTest.php index 0df52f5e..91aad10c 100644 --- a/tests/phpunit/includes/parser/NewParserTest.php +++ b/tests/phpunit/includes/parser/NewParserTest.php @@ -124,7 +124,7 @@ class NewParserTest extends MediaWikiTestCase { $tmpGlobals['wgFileExtensions'][] = 'svg'; $tmpGlobals['wgSVGConverter'] = 'rsvg'; $tmpGlobals['wgSVGConverters']['rsvg'] = - '$path/rsvg-convert -w $width -h $height $input -o $output'; + '$path/rsvg-convert -w $width -h $height -o $output $input'; if ( $GLOBALS['wgStyleDirectory'] === false ) { $tmpGlobals['wgStyleDirectory'] = "$IP/skins"; @@ -160,9 +160,6 @@ class NewParserTest extends MediaWikiTestCase { $this->djVuSupport = new DjVuSupport(); // Tidy support $this->tidySupport = new TidySupport(); - // We always set 'wgUseTidy' to false when parsing, but certain - // test-running modes still use tidy if available, so ensure - // that the tidy-related options are all set to their defaults. $tmpGlobals['wgUseTidy'] = false; $tmpGlobals['wgAlwaysUseTidy'] = false; $tmpGlobals['wgDebugTidy'] = false; @@ -419,6 +416,7 @@ class NewParserTest extends MediaWikiTestCase { 'wgMathDirectory' => $uploadDir . '/math', 'wgDefaultLanguageVariant' => $variant, 'wgLinkHolderBatchSize' => $linkHolderBatchSize, + 'wgUseTidy' => isset( $opts['tidy'] ), ); if ( $config ) { @@ -434,7 +432,7 @@ class NewParserTest extends MediaWikiTestCase { $this->savedGlobals = array(); /** @since 1.20 */ - wfRunHooks( 'ParserTestGlobals', array( &$settings ) ); + Hooks::run( 'ParserTestGlobals', array( &$settings ) ); $langObj = Language::factory( $lang ); $settings['wgContLang'] = $langObj; @@ -480,16 +478,16 @@ class NewParserTest extends MediaWikiTestCase { */ protected function getUploadDir() { if ( $this->keepUploads ) { + // Don't use getNewTempDirectory() as this is meant to persist $dir = wfTempDir() . '/mwParser-images'; if ( is_dir( $dir ) ) { return $dir; } } else { - $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images"; + $dir = $this->getNewTempDirectory(); } - // wfDebug( "Creating upload directory $dir\n" ); if ( file_exists( $dir ) ) { wfDebug( "Already exists!\n" ); @@ -727,12 +725,21 @@ class NewParserTest extends MediaWikiTestCase { . "Current configuration is:\n\$wgTexvc = '$wgTexvc'" ); } } + if ( isset( $opts['djvu'] ) ) { if ( !$this->djVuSupport->isEnabled() ) { $this->markTestSkipped( "SKIPPED: djvu binaries do not exist or are not executable.\n" ); } } + if ( isset( $opts['tidy'] ) ) { + if ( !$this->tidySupport->isEnabled() ) { + $this->markTestSkipped( "SKIPPED: tidy extension is not installed.\n" ); + } else { + $options->setTidy( true ); + } + } + if ( isset( $opts['pst'] ) ) { $out = $parser->preSaveTransform( $input, $title, $user, $options ); } elseif ( isset( $opts['msg'] ) ) { @@ -753,12 +760,7 @@ class NewParserTest extends MediaWikiTestCase { $output->setTOCEnabled( !isset( $opts['notoc'] ) ); $out = $output->getText(); if ( isset( $opts['tidy'] ) ) { - if ( !$this->tidySupport->isEnabled() ) { - $this->markTestSkipped( "SKIPPED: tidy extension is not installed.\n" ); - } else { - $out = MWTidy::tidy( $out ); - $out = preg_replace( '/\s+$/', '', $out ); - } + $out = preg_replace( '/\s+$/', '', $out ); } if ( isset( $opts['showtitle'] ) ) { @@ -769,6 +771,14 @@ class NewParserTest extends MediaWikiTestCase { $out = "$title\n$out"; } + if ( isset( $opts['showindicators'] ) ) { + $indicators = ''; + foreach ( $output->getIndicators() as $id => $content ) { + $indicators .= "$id=$content\n"; + } + $out = $indicators . $out; + } + if ( isset( $opts['ill'] ) ) { $out = implode( ' ', $output->getLanguageLinks() ); } elseif ( isset( $opts['cat'] ) ) { @@ -939,7 +949,7 @@ class NewParserTest extends MediaWikiTestCase { $class = $wgParserConf['class']; $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf ); - wfRunHooks( 'ParserTestParser', array( &$parser ) ); + Hooks::run( 'ParserTestParser', array( &$parser ) ); return $parser; } diff --git a/tests/phpunit/includes/parser/ParserOutputTest.php b/tests/phpunit/includes/parser/ParserOutputTest.php index c024cee5..e660e096 100644 --- a/tests/phpunit/includes/parser/ParserOutputTest.php +++ b/tests/phpunit/includes/parser/ParserOutputTest.php @@ -1,5 +1,9 @@ <?php +/** + * @group Database + * ^--- trigger DB shadowing because we are using Title magic + */ class ParserOutputTest extends MediaWikiTestCase { public static function provideIsLinkInternal() { @@ -84,4 +88,5 @@ class ParserOutputTest extends MediaWikiTestCase { $this->assertEquals( $po->getProperty( 'foo' ), false ); $this->assertArrayNotHasKey( 'foo', $properties ); } + } diff --git a/tests/phpunit/includes/parser/TagHooksTest.php b/tests/phpunit/includes/parser/TagHooksTest.php index e3c4cc84..251da471 100644 --- a/tests/phpunit/includes/parser/TagHooksTest.php +++ b/tests/phpunit/includes/parser/TagHooksTest.php @@ -89,7 +89,7 @@ class TagHookTest extends MediaWikiTestCase { global $wgParserConf, $wgContLang; $parser = new Parser( $wgParserConf ); - $parser->setFunctionTagHook( $tag, array( $this, 'functionTagCallback' ), SFH_OBJECT_ARGS ); + $parser->setFunctionTagHook( $tag, array( $this, 'functionTagCallback' ), Parser::SFH_OBJECT_ARGS ); $parser->parse( "Foo<$tag>Bar</$tag>Baz", Title::newFromText( 'Test' ), diff --git a/tests/phpunit/includes/PasswordTest.php b/tests/phpunit/includes/password/PasswordTest.php index ceb794b5..5ad8aca6 100644 --- a/tests/phpunit/includes/PasswordTest.php +++ b/tests/phpunit/includes/password/PasswordTest.php @@ -30,4 +30,10 @@ class PasswordTest extends MediaWikiTestCase { $this->assertFalse( $invalid1->equals( $invalid2 ) ); } + + public function testInvalidPlaintext() { + $invalid = User::getPasswordFactory()->newFromPlaintext( null ); + + $this->assertInstanceOf( 'InvalidPassword', $invalid ); + } } diff --git a/tests/phpunit/includes/password/PasswordTestCase.php b/tests/phpunit/includes/password/PasswordTestCase.php index ef16f1c4..9a142cbc 100644 --- a/tests/phpunit/includes/password/PasswordTestCase.php +++ b/tests/phpunit/includes/password/PasswordTestCase.php @@ -49,10 +49,13 @@ abstract class PasswordTestCase extends MediaWikiTestCase { * An array of tests in the form of (bool, string, string), where the first * element is whether the second parameter (a password hash) and the third * parameter (a password) should match. - * * @return array + * @throws MWException + * @abstract */ - abstract public static function providePasswordTests(); + public static function providePasswordTests() { + throw new MWException( "Not implemented" ); + } /** * @dataProvider providePasswordTests diff --git a/tests/phpunit/includes/registration/ExtensionProcessorTest.php b/tests/phpunit/includes/registration/ExtensionProcessorTest.php new file mode 100644 index 00000000..8715711f --- /dev/null +++ b/tests/phpunit/includes/registration/ExtensionProcessorTest.php @@ -0,0 +1,374 @@ +<?php + +class ExtensionProcessorTest extends MediaWikiTestCase { + + private $dir; + + public function setUp() { + parent::setUp(); + $this->dir = __DIR__ . '/FooBar/extension.json'; + } + + /** + * 'name' is absolutely required + * + * @var array + */ + static $default = array( + 'name' => 'FooBar', + ); + + /** + * @covers ExtensionProcessor::extractInfo + */ + public function testExtractInfo() { + // Test that attributes that begin with @ are ignored + $processor = new ExtensionProcessor(); + $processor->extractInfo( $this->dir, self::$default + array( + '@metadata' => array( 'foobarbaz' ), + 'AnAttribute' => array( 'omg' ), + 'AutoloadClasses' => array( 'FooBar' => 'includes/FooBar.php' ), + ) ); + + $extracted = $processor->getExtractedInfo(); + $attributes = $extracted['attributes']; + $this->assertArrayHasKey( 'AnAttribute', $attributes ); + $this->assertArrayNotHasKey( '@metadata', $attributes ); + $this->assertArrayNotHasKey( 'AutoloadClasses', $attributes ); + } + + public static function provideRegisterHooks() { + return array( + // No hooks + array( + array(), + self::$default, + array(), + ), + // No current hooks, adding one for "FooBaz" + array( + array(), + array( 'Hooks' => array( 'FooBaz' => 'FooBazCallback' ) ) + self::$default, + array( 'FooBaz' => array( 'FooBazCallback' ) ), + ), + // Hook for "FooBaz", adding another one + array( + array( 'FooBaz' => array( 'PriorCallback' ) ), + array( 'Hooks' => array( 'FooBaz' => 'FooBazCallback' ) ) + self::$default, + array( 'FooBaz' => array( 'PriorCallback', 'FooBazCallback' ) ), + ), + // Hook for "BarBaz", adding one for "FooBaz" + array( + array( 'BarBaz' => array( 'BarBazCallback' ) ), + array( 'Hooks' => array( 'FooBaz' => 'FooBazCallback' ) ) + self::$default, + array( + 'BarBaz' => array( 'BarBazCallback' ), + 'FooBaz' => array( 'FooBazCallback' ), + ), + ), + ); + } + + /** + * @covers ExtensionProcessor::extractHooks + * @dataProvider provideRegisterHooks + */ + public function testRegisterHooks( $pre, $info, $expected ) { + $processor = new MockExtensionProcessor( array( 'wgHooks' => $pre ) ); + $processor->extractInfo( $this->dir, $info ); + $extracted = $processor->getExtractedInfo(); + $this->assertEquals( $expected, $extracted['globals']['wgHooks'] ); + } + + /** + * @covers ExtensionProcessor::extractConfig + */ + public function testExtractConfig() { + $processor = new ExtensionProcessor; + $info = array( + 'config' => array( + 'Bar' => 'somevalue', + 'Foo' => 10, + '@IGNORED' => 'yes', + ), + ) + self::$default; + $processor->extractInfo( $this->dir, $info ); + $extracted = $processor->getExtractedInfo(); + $this->assertEquals( 'somevalue', $extracted['globals']['wgBar'] ); + $this->assertEquals( 10, $extracted['globals']['wgFoo'] ); + $this->assertArrayNotHasKey( 'wg@IGNORED', $extracted['globals'] ); + } + + public static function provideExtracttExtensionMessagesFiles() { + $dir = __DIR__ . '/FooBar/'; + return array( + array( + array( 'ExtensionMessagesFiles' => array( 'FooBarAlias' => 'FooBar.alias.php' ) ), + array( 'wgExtensionMessagesFiles' => array( 'FooBarAlias' => $dir . 'FooBar.alias.php' ) ) + ), + array( + array( + 'ExtensionMessagesFiles' => array( + 'FooBarAlias' => 'FooBar.alias.php', + 'FooBarMagic' => 'FooBar.magic.i18n.php', + ), + ), + array( + 'wgExtensionMessagesFiles' => array( + 'FooBarAlias' => $dir . 'FooBar.alias.php', + 'FooBarMagic' => $dir . 'FooBar.magic.i18n.php', + ), + ), + ), + ); + } + + /** + * @covers ExtensionProcessor::extracttExtensionMessagesFiles + * @dataProvider provideExtracttExtensionMessagesFiles + */ + public function testExtracttExtensionMessagesFiles( $input, $expected ) { + $processor = new ExtensionProcessor(); + $processor->extractInfo( $this->dir, $input + self::$default ); + $out = $processor->getExtractedInfo(); + foreach ( $expected as $key => $value ) { + $this->assertEquals( $value, $out['globals'][$key] ); + } + } + + + public static function provideExtractMessagesDirs() { + $dir = __DIR__ . '/FooBar/'; + return array( + array( + array( 'MessagesDirs' => array( 'VisualEditor' => 'i18n' ) ), + array( 'wgMessagesDirs' => array( 'VisualEditor' => array( $dir . 'i18n' ) ) ) + ), + array( + array( 'MessagesDirs' => array( 'VisualEditor' => array( 'i18n', 'foobar' ) ) ), + array( 'wgMessagesDirs' => array( 'VisualEditor' => array( $dir . 'i18n', $dir . 'foobar' ) ) ) + ), + ); + } + + /** + * @covers ExtensionProcessor::extractMessagesDirs + * @dataProvider provideExtractMessagesDirs + */ + public function testExtractMessagesDirs( $input, $expected ) { + $processor = new ExtensionProcessor(); + $processor->extractInfo( $this->dir, $input + self::$default ); + $out = $processor->getExtractedInfo(); + foreach ( $expected as $key => $value ) { + $this->assertEquals( $value, $out['globals'][$key] ); + } + } + + /** + * @covers ExtensionProcessor::extractResourceLoaderModules + * @dataProvider provideExtractResourceLoaderModules + */ + public function testExtractResourceLoaderModules( $input, $expected ) { + $processor = new ExtensionProcessor(); + $processor->extractInfo( $this->dir, $input + self::$default ); + $out = $processor->getExtractedInfo(); + foreach ( $expected as $key => $value ) { + $this->assertEquals( $value, $out['globals'][$key] ); + } + } + + public static function provideExtractResourceLoaderModules() { + $dir = __DIR__ . '/FooBar/'; + return array( + // Generic module with localBasePath/remoteExtPath specified + array( + // Input + array( + 'ResourceModules' => array( + 'test.foo' => array( + 'styles' => 'foobar.js', + 'localBasePath' => '', + 'remoteExtPath' => 'FooBar', + ), + ), + ), + // Expected + array( + 'wgResourceModules' => array( + 'test.foo' => array( + 'styles' => 'foobar.js', + 'localBasePath' => $dir, + 'remoteExtPath' => 'FooBar', + ), + ), + ), + ), + // ResourceFileModulePaths specified: + array( + // Input + array( + 'ResourceFileModulePaths' => array( + 'localBasePath' => '', + 'remoteExtPath' => 'FooBar', + ), + 'ResourceModules' => array( + // No paths + 'test.foo' => array( + 'styles' => 'foo.js', + ), + // Different paths set + 'test.bar' => array( + 'styles' => 'bar.js', + 'localBasePath' => 'subdir', + 'remoteExtPath' => 'FooBar/subdir', + ), + // Custom class with no paths set + 'test.class' => array( + 'class' => 'FooBarModule', + 'extra' => 'argument', + ), + // Custom class with a localBasePath + 'test.class.with.path' => array( + 'class' => 'FooBarPathModule', + 'extra' => 'argument', + 'localBasePath' => '', + ) + ), + ), + // Expected + array( + 'wgResourceModules' => array( + 'test.foo' => array( + 'styles' => 'foo.js', + 'localBasePath' => $dir, + 'remoteExtPath' => 'FooBar', + ), + 'test.bar' => array( + 'styles' => 'bar.js', + 'localBasePath' => $dir . 'subdir', + 'remoteExtPath' => 'FooBar/subdir', + ), + 'test.class' => array( + 'class' => 'FooBarModule', + 'extra' => 'argument', + 'localBasePath' => $dir, + 'remoteExtPath' => 'FooBar', + ), + 'test.class.with.path' => array( + 'class' => 'FooBarPathModule', + 'extra' => 'argument', + 'localBasePath' => $dir, + 'remoteExtPath' => 'FooBar', + ) + ), + ), + ), + // ResourceModuleSkinStyles with file module paths + array( + // Input + array( + 'ResourceFileModulePaths' => array( + 'localBasePath' => '', + 'remoteSkinPath' => 'FooBar', + ), + 'ResourceModuleSkinStyles' => array( + 'foobar' => array( + 'test.foo' => 'foo.css', + ) + ), + ), + // Expected + array( + 'wgResourceModuleSkinStyles' => array( + 'foobar' => array( + 'test.foo' => 'foo.css', + 'localBasePath' => $dir, + 'remoteSkinPath' => 'FooBar', + ), + ), + ), + ), + // ResourceModuleSkinStyles with file module paths and an override + array( + // Input + array( + 'ResourceFileModulePaths' => array( + 'localBasePath' => '', + 'remoteSkinPath' => 'FooBar', + ), + 'ResourceModuleSkinStyles' => array( + 'foobar' => array( + 'test.foo' => 'foo.css', + 'remoteSkinPath' => 'BarFoo' + ), + ), + ), + // Expected + array( + 'wgResourceModuleSkinStyles' => array( + 'foobar' => array( + 'test.foo' => 'foo.css', + 'localBasePath' => $dir, + 'remoteSkinPath' => 'BarFoo', + ), + ), + ), + ), + ); + } + + public static function provideSetToGlobal() { + return array( + array( + array( 'wgAPIModules', 'wgAvailableRights' ), + array(), + array( + 'APIModules' => array( 'foobar' => 'ApiFooBar' ), + 'AvailableRights' => array( 'foobar', 'unfoobar' ), + ), + array( + 'wgAPIModules' => array( 'foobar' => 'ApiFooBar' ), + 'wgAvailableRights' => array( 'foobar', 'unfoobar' ), + ), + ), + array( + array( 'wgAPIModules', 'wgAvailableRights' ), + array( + 'wgAPIModules' => array( 'barbaz' => 'ApiBarBaz' ), + 'wgAvailableRights' => array( 'barbaz' ) + ), + array( + 'APIModules' => array( 'foobar' => 'ApiFooBar' ), + 'AvailableRights' => array( 'foobar', 'unfoobar' ), + ), + array( + 'wgAPIModules' => array( 'barbaz' => 'ApiBarBaz', 'foobar' => 'ApiFooBar' ), + 'wgAvailableRights' => array( 'barbaz', 'foobar', 'unfoobar' ), + ), + ), + array( + array( 'wgGroupPermissions' ), + array( + 'wgGroupPermissions' => array( 'sysop' => array( 'delete' ) ), + ), + array( + 'GroupPermissions' => array( 'sysop' => array( 'undelete' ), 'user' => array( 'edit' ) ), + ), + array( + 'wgGroupPermissions' => array( 'sysop' => array( 'delete', 'undelete' ), 'user' => array( 'edit' ) ), + ) + ) + ); + } +} + + +/** + * Allow overriding the default value of $this->globals + * so we can test merging + */ +class MockExtensionProcessor extends ExtensionProcessor { + public function __construct( $globals = array() ) { + $this->globals = $globals + $this->globals; + } +} diff --git a/tests/phpunit/includes/registration/ExtensionRegistryTest.php b/tests/phpunit/includes/registration/ExtensionRegistryTest.php new file mode 100644 index 00000000..1b24628c --- /dev/null +++ b/tests/phpunit/includes/registration/ExtensionRegistryTest.php @@ -0,0 +1,195 @@ +<?php + +class ExtensionRegistryTest extends MediaWikiTestCase { + + /** + * @covers ExtensionRegistry::exportExtractedData + * @dataProvider provideExportExtractedDataGlobals + */ + public function testExportExtractedDataGlobals( $desc, $before, $globals, $expected ) { + // Set globals for test + if ( $before ) { + foreach ( $before as $key => $value ) { + // mw prefixed globals does not exist normally + if ( substr( $key, 0, 2 ) == 'mw' ) { + $GLOBALS[$key] = $value; + } else { + $this->setMwGlobals( $key, $value ); + } + } + } + + $info = array( + 'globals' => $globals, + 'callbacks' => array(), + 'defines' => array(), + 'credits' => array(), + 'attributes' => array(), + ); + $registry = new ExtensionRegistry(); + $class = new ReflectionClass( 'ExtensionRegistry' ); + $method = $class->getMethod( 'exportExtractedData' ); + $method->setAccessible( true ); + $method->invokeArgs( $registry, array( $info ) ); + foreach ( $expected as $name => $value ) { + $this->assertArrayHasKey( $name, $GLOBALS, $desc ); + $this->assertEquals( $value, $GLOBALS[$name], $desc ); + } + + // Remove mw prefixed globals + if ( $before ) { + foreach ( $before as $key => $value ) { + if ( substr( $key, 0, 2 ) == 'mw' ) { + unset( $GLOBALS[$key] ); + } + } + } + } + + public static function provideExportExtractedDataGlobals() { + // "mwtest" prefix used instead of "$wg" to avoid potential conflicts + return array( + array( + 'Simple non-array values', + array( + 'mwtestFooBarConfig' => true, + 'mwtestFooBarConfig2' => 'string', + ), + array( + 'mwtestFooBarDefault' => 1234, + 'mwtestFooBarConfig' => false, + ), + array( + 'mwtestFooBarConfig' => true, + 'mwtestFooBarConfig2' => 'string', + 'mwtestFooBarDefault' => 1234, + ), + ), + array( + 'No global already set, simple array', + null, + array( + 'mwtestDefaultOptions' => array( + 'foobar' => true, + ) + ), + array( + 'mwtestDefaultOptions' => array( + 'foobar' => true, + ) + ), + ), + array( + 'Global already set, simple array', + array( + 'mwtestDefaultOptions' => array( + 'foobar' => true, + 'foo' => 'string' + ), + ), + array( + 'mwtestDefaultOptions' => array( + 'barbaz' => 12345, + 'foobar' => false, + ), + ), + array( + 'mwtestDefaultOptions' => array( + 'barbaz' => 12345, + 'foo' => 'string', + 'foobar' => true, + ), + ) + ), + array( + 'No global already set, $wgHooks', + array( + 'wgHooks' => array(), + ), + array( + 'wgHooks' => array( + 'FooBarEvent' => array( + 'FooBarClass::onFooBarEvent' + ), + ), + ), + array( + 'wgHooks' => array( + 'FooBarEvent' => array( + 'FooBarClass::onFooBarEvent' + ), + ), + ), + ), + array( + 'Global already set, $wgHooks', + array( + 'wgHooks' => array( + 'FooBarEvent' => array( + 'FooBarClass::onFooBarEvent' + ), + 'BazBarEvent' => array( + 'FooBarClass::onBazBarEvent', + ), + ), + ), + array( + 'wgHooks' => array( + 'FooBarEvent' => array( + 'BazBarClass::onFooBarEvent', + ), + ), + ), + array( + 'wgHooks' => array( + 'FooBarEvent' => array( + 'FooBarClass::onFooBarEvent', + 'BazBarClass::onFooBarEvent', + ), + 'BazBarEvent' => array( + 'FooBarClass::onBazBarEvent', + ), + ), + ), + ), + array( + 'Global already set, $wgGroupPermissions', + array( + 'wgGroupPermissions' => array( + 'sysop' => array( + 'something' => true, + ), + 'user' => array( + 'somethingtwo' => true, + ) + ), + ), + array( + 'wgGroupPermissions' => array( + 'customgroup' => array( + 'right' => true, + ), + 'user' => array( + 'right' => true, + 'somethingtwo' => false, + ) + ), + ), + array( + 'wgGroupPermissions' => array( + 'customgroup' => array( + 'right' => true, + ), + 'sysop' => array( + 'something' => true, + ), + 'user' => array( + 'somethingtwo' => true, + 'right' => true, + ) + ), + ), + ) + ); + } +} diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php new file mode 100644 index 00000000..122995a5 --- /dev/null +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php @@ -0,0 +1,247 @@ +<?php + +/** + * @group ResourceLoader + */ +class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase { + + protected function setUp() { + parent::setUp(); + + // The return value of the closure shouldn't matter since this test should + // never call it + SkinFactory::getDefaultInstance()->register( + 'fakeskin', + 'FakeSkin', + function () { + } + ); + } + + private static function getModules() { + $base = array( + 'localBasePath' => realpath( dirname( __FILE__ ) ), + ); + + return array( + 'noTemplateModule' => array(), + + 'htmlTemplateModule' => $base + array( + 'templates' => array( + 'templates/template.html', + 'templates/template2.html', + ) + ), + + 'aliasedHtmlTemplateModule' => $base + array( + 'templates' => array( + 'foo.html' => 'templates/template.html', + 'bar.html' => 'templates/template2.html', + ) + ), + + 'templateModuleHandlebars' => $base + array( + 'templates' => array( + 'templates/template_awesome.handlebars', + ), + ), + + 'aliasFooFromBar' => $base + array( + 'templates' => array( + 'foo.foo' => 'templates/template.bar', + ), + ), + ); + } + + public static function providerTemplateDependencies() { + $modules = self::getModules(); + + return array( + array( + $modules['noTemplateModule'], + array(), + ), + array( + $modules['htmlTemplateModule'], + array( + 'mediawiki.template', + ), + ), + array( + $modules['templateModuleHandlebars'], + array( + 'mediawiki.template', + 'mediawiki.template.handlebars', + ), + ), + array( + $modules['aliasFooFromBar'], + array( + 'mediawiki.template', + 'mediawiki.template.foo', + ), + ), + ); + } + + /** + * @dataProvider providerTemplateDependencies + * @covers ResourceLoaderFileModule::__construct + * @covers ResourceLoaderFileModule::getDependencies + */ + public function testTemplateDependencies( $module, $expected ) { + $rl = new ResourceLoaderFileModule( $module ); + $this->assertEquals( $rl->getDependencies(), $expected ); + } + + /** + * @covers ResourceLoaderFileModule::getAllStyleFiles + * @covers ResourceLoaderFileModule::getAllSkinStyleFiles + * @covers ResourceLoaderFileModule::getSkinStyleFiles + */ + public function testGetAllSkinStyleFiles() { + $baseParams = array( + 'scripts' => array( + 'foo.js', + 'bar.js', + ), + 'styles' => array( + 'foo.css', + 'bar.css' => array( 'media' => 'print' ), + 'screen.less' => array( 'media' => 'screen' ), + 'screen-query.css' => array( 'media' => 'screen and (min-width: 400px)' ), + ), + 'skinStyles' => array( + 'default' => 'quux-fallback.less', + 'fakeskin' => array( + 'baz-vector.css', + 'quux-vector.less', + ), + ), + 'messages' => array( + 'hello', + 'world', + ), + ); + + $module = new ResourceLoaderFileModule( $baseParams ); + + $this->assertEquals( + array( + 'foo.css', + 'baz-vector.css', + 'quux-vector.less', + 'quux-fallback.less', + 'bar.css', + 'screen.less', + 'screen-query.css', + ), + array_map( 'basename', $module->getAllStyleFiles() ) + ); + } + + /** + * Strip @noflip annotations from CSS code. + * @param string $css + * @return string + */ + private static function stripNoflip( $css ) { + return str_replace( '/*@noflip*/ ', '', $css ); + } + + /** + * What happens when you mix @embed and @noflip? + * This really is an integration test, but oh well. + * + * @covers ResourceLoaderFileModule::getStyles + * @covers ResourceLoaderFileModule::getStyleFiles + */ + public function testMixedCssAnnotations( ) { + $basePath = __DIR__ . '/../../data/css'; + $testModule = new ResourceLoaderFileModule( array( + 'localBasePath' => $basePath, + 'styles' => array( 'test.css' ), + ) ); + $expectedModule = new ResourceLoaderFileModule( array( + 'localBasePath' => $basePath, + 'styles' => array( 'expected.css' ), + ) ); + + $contextLtr = $this->getResourceLoaderContext( 'en', 'ltr' ); + $contextRtl = $this->getResourceLoaderContext( 'he', 'rtl' ); + + // Since we want to compare the effect of @noflip+@embed against the effect of just @embed, and + // the @noflip annotations are always preserved, we need to strip them first. + $this->assertEquals( + $expectedModule->getStyles( $contextLtr ), + self::stripNoflip( $testModule->getStyles( $contextLtr ) ), + "/*@noflip*/ with /*@embed*/ gives correct results in LTR mode" + ); + $this->assertEquals( + $expectedModule->getStyles( $contextLtr ), + self::stripNoflip( $testModule->getStyles( $contextRtl ) ), + "/*@noflip*/ with /*@embed*/ gives correct results in RTL mode" + ); + } + + public static function providerGetTemplates() { + $modules = self::getModules(); + + return array( + array( + $modules['noTemplateModule'], + array(), + ), + array( + $modules['templateModuleHandlebars'], + array( + 'templates/template_awesome.handlebars' => "wow\n", + ), + ), + array( + $modules['htmlTemplateModule'], + array( + 'templates/template.html' => "<strong>hello</strong>\n", + 'templates/template2.html' => "<div>goodbye</div>\n", + ), + ), + array( + $modules['aliasedHtmlTemplateModule'], + array( + 'foo.html' => "<strong>hello</strong>\n", + 'bar.html' => "<div>goodbye</div>\n", + ), + ), + ); + } + + /** + * @dataProvider providerGetTemplates + * @covers ResourceLoaderFileModule::getTemplates + */ + public function testGetTemplates( $module, $expected ) { + $rl = new ResourceLoaderFileModule( $module ); + + $this->assertEquals( $rl->getTemplates(), $expected ); + } + + public static function providerGetModifiedTime() { + $modules = self::getModules(); + + return array( + // Check the default value when no templates present in module is 1 + array( $modules['noTemplateModule'], 1 ), + ); + } + + /** + * @dataProvider providerGetModifiedTime + * @covers ResourceLoaderFileModule::getModifiedTime + */ + public function testGetModifiedTime( $module, $expected ) { + $rl = new ResourceLoaderFileModule( $module ); + $ts = $rl->getModifiedTime( $this->getResourceLoaderContext() ); + $this->assertEquals( $ts, $expected ); + } +} diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php new file mode 100644 index 00000000..d0bc210b --- /dev/null +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php @@ -0,0 +1,162 @@ +<?php + +/** + * @group ResourceLoader + */ +class ResourceLoaderImageModuleTest extends ResourceLoaderTestCase { + + public static $commonImageData = array( + 'add' => 'add.gif', + 'remove' => array( + 'file' => 'remove.svg', + 'variants' => array( 'destructive' ), + ), + 'next' => array( + 'file' => array( + 'ltr' => 'next.svg', + 'rtl' => 'prev.svg' + ), + ), + 'help' => array( + 'file' => array( + 'ltr' => 'help-ltr.svg', + 'rtl' => 'help-rtl.svg', + 'lang' => array( + 'he' => 'help-ltr.svg', + ) + ), + ), + 'bold' => array( + 'file' => array( + 'default' => 'bold-a.svg', + 'lang' => array( + 'en' => 'bold-b.svg', + 'ar,de' => 'bold-f.svg', + ) + ), + ) + ); + + public static $commonImageVariants = array( + 'invert' => array( + 'color' => '#FFFFFF', + 'global' => true, + ), + 'primary' => array( + 'color' => '#598AD1', + ), + 'constructive' => array( + 'color' => '#00C697', + ), + 'destructive' => array( + 'color' => '#E81915', + ), + ); + + public static function providerGetModules() { + return array( + array( + array( + 'class' => 'ResourceLoaderImageModule', + 'prefix' => 'oo-ui-icon', + 'variants' => self::$commonImageVariants, + 'images' => self::$commonImageData, + ), + '.oo-ui-icon-add { + ... +} +.oo-ui-icon-add-invert { + ... +} +.oo-ui-icon-remove { + ... +} +.oo-ui-icon-remove-invert { + ... +} +.oo-ui-icon-remove-destructive { + ... +} +.oo-ui-icon-next { + ... +} +.oo-ui-icon-next-invert { + ... +} +.oo-ui-icon-help { + ... +} +.oo-ui-icon-help-invert { + ... +} +.oo-ui-icon-bold { + ... +} +.oo-ui-icon-bold-invert { + ... +}', + ), + array( + array( + 'class' => 'ResourceLoaderImageModule', + 'selectorWithoutVariant' => '.mw-ui-icon-{name}:after, .mw-ui-icon-{name}:before', + 'selectorWithVariant' => '.mw-ui-icon-{name}-{variant}:after, .mw-ui-icon-{name}-{variant}:before', + 'variants' => self::$commonImageVariants, + 'images' => self::$commonImageData, + ), + '.mw-ui-icon-add:after, .mw-ui-icon-add:before { + ... +} +.mw-ui-icon-add-invert:after, .mw-ui-icon-add-invert:before { + ... +} +.mw-ui-icon-remove:after, .mw-ui-icon-remove:before { + ... +} +.mw-ui-icon-remove-invert:after, .mw-ui-icon-remove-invert:before { + ... +} +.mw-ui-icon-remove-destructive:after, .mw-ui-icon-remove-destructive:before { + ... +} +.mw-ui-icon-next:after, .mw-ui-icon-next:before { + ... +} +.mw-ui-icon-next-invert:after, .mw-ui-icon-next-invert:before { + ... +} +.mw-ui-icon-help:after, .mw-ui-icon-help:before { + ... +} +.mw-ui-icon-help-invert:after, .mw-ui-icon-help-invert:before { + ... +} +.mw-ui-icon-bold:after, .mw-ui-icon-bold:before { + ... +} +.mw-ui-icon-bold-invert:after, .mw-ui-icon-bold-invert:before { + ... +}', + ), + ); + } + + /** + * @dataProvider providerGetModules + * @covers ResourceLoaderImageModule::getStyles + */ + public function testGetStyles( $module, $expected ) { + $module = new ResourceLoaderImageModuleTestable( $module, __DIR__ . '/../../data/resourceloader' ); + $styles = $module->getStyles( $this->getResourceLoaderContext() ); + $this->assertEquals( $expected, $styles['all'] ); + } +} + +class ResourceLoaderImageModuleTestable extends ResourceLoaderImageModule { + /** + * Replace with a stub to make test cases easier to write. + */ + protected function getCssDeclarations( $primary, $fallback ) { + return array( '...' ); + } +} diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderImageTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderImageTest.php new file mode 100644 index 00000000..758cfe19 --- /dev/null +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderImageTest.php @@ -0,0 +1,122 @@ +<?php + +/** + * @group ResourceLoader + */ +class ResourceLoaderImageTest extends ResourceLoaderTestCase { + + protected $imagesPath; + + protected function setUp() { + parent::setUp(); + $this->imagesPath = __DIR__ . '/../../data/resourceloader'; + } + + protected function getTestImage( $name ) { + $options = ResourceLoaderImageModuleTest::$commonImageData[$name]; + $fileDescriptor = is_string( $options ) ? $options : $options['file']; + $allowedVariants = is_array( $options ) && isset( $options['variants'] ) ? $options['variants'] : array(); + $variants = array_fill_keys( $allowedVariants, array( 'color' => 'red' ) ); + return new ResourceLoaderImageTestable( $name, 'test', $fileDescriptor, $this->imagesPath, $variants ); + } + + public static function provideGetPath() { + return array( + array( 'add', 'en', 'add.gif' ), + array( 'add', 'he', 'add.gif' ), + array( 'remove', 'en', 'remove.svg' ), + array( 'remove', 'he', 'remove.svg' ), + array( 'next', 'en', 'next.svg' ), + array( 'next', 'he', 'prev.svg' ), + array( 'help', 'en', 'help-ltr.svg' ), + array( 'help', 'ar', 'help-rtl.svg' ), + array( 'help', 'he', 'help-ltr.svg' ), + array( 'bold', 'en', 'bold-b.svg' ), + array( 'bold', 'de', 'bold-f.svg' ), + array( 'bold', 'ar', 'bold-f.svg' ), + array( 'bold', 'fr', 'bold-a.svg' ), + array( 'bold', 'he', 'bold-a.svg' ), + ); + } + + /** + * @covers ResourceLoaderImage::getPath + * @dataProvider provideGetPath + */ + public function testGetPath( $imageName, $languageCode, $path ) { + static $dirMap = array( + 'en' => 'ltr', + 'de' => 'ltr', + 'fr' => 'ltr', + 'he' => 'rtl', + 'ar' => 'rtl', + ); + static $contexts = array(); + + $image = $this->getTestImage( $imageName ); + $context = $this->getResourceLoaderContext( $languageCode, $dirMap[$languageCode] ); + + $this->assertEquals( $image->getPath( $context ), $this->imagesPath . '/' . $path ); + } + + /** + * @covers ResourceLoaderImage::getExtension + * @covers ResourceLoaderImage::getMimeType + */ + public function testGetExtension() { + $image = $this->getTestImage( 'remove' ); + $this->assertEquals( $image->getExtension(), 'svg' ); + $this->assertEquals( $image->getExtension( 'original' ), 'svg' ); + $this->assertEquals( $image->getExtension( 'rasterized' ), 'png' ); + $image = $this->getTestImage( 'add' ); + $this->assertEquals( $image->getExtension(), 'gif' ); + $this->assertEquals( $image->getExtension( 'original' ), 'gif' ); + $this->assertEquals( $image->getExtension( 'rasterized' ), 'gif' ); + } + + /** + * @covers ResourceLoaderImage::getImageData + * @covers ResourceLoaderImage::variantize + * @covers ResourceLoaderImage::massageSvgPathdata + */ + public function testGetImageData() { + $context = $this->getResourceLoaderContext( 'en', 'ltr' ); + + $image = $this->getTestImage( 'remove' ); + $data = file_get_contents( $this->imagesPath . '/remove.svg' ); + $dataConstructive = file_get_contents( $this->imagesPath . '/remove_variantize.svg' ); + $this->assertEquals( $image->getImageData( $context, null, 'original' ), $data ); + $this->assertEquals( $image->getImageData( $context, 'destructive', 'original' ), $dataConstructive ); + // Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output + $this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), 'RASTERIZESTUB' ); + + $image = $this->getTestImage( 'add' ); + $data = file_get_contents( $this->imagesPath . '/add.gif' ); + $this->assertEquals( $image->getImageData( $context, null, 'original' ), $data ); + $this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), $data ); + } + + /** + * @covers ResourceLoaderImage::massageSvgPathdata + */ + public function testMassageSvgPathdata() { + $image = $this->getTestImage( 'next' ); + $data = file_get_contents( $this->imagesPath . '/next.svg' ); + $dataMassaged = file_get_contents( $this->imagesPath . '/next_massage.svg' ); + $this->assertEquals( $image->massageSvgPathdata( $data ), $dataMassaged ); + } +} + +class ResourceLoaderImageTestable extends ResourceLoaderImage { + // Make some protected methods public + public function getPath( ResourceLoaderContext $context ) { + return parent::getPath( $context ); + } + public function massageSvgPathdata( $svg ) { + return parent::massageSvgPathdata( $svg ); + } + // Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output + public function rasterize( $svg ) { + return 'RASTERIZESTUB'; + } +} diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderModuleTest.php index b0edaaf7..6d1ed4e0 100644 --- a/tests/phpunit/includes/resourceloader/ResourceLoaderModuleTest.php +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderModuleTest.php @@ -2,71 +2,12 @@ class ResourceLoaderModuleTest extends ResourceLoaderTestCase { - protected function setUp() { - parent::setUp(); - - // The return value of the closure shouldn't matter since this test should - // never call it - SkinFactory::getDefaultInstance()->register( - 'fakeskin', - 'FakeSkin', - function () { - } - ); - } - - /** - * @covers ResourceLoaderFileModule::getAllSkinStyleFiles - */ - public function testGetAllSkinStyleFiles() { - $context = self::getResourceLoaderContext(); - - $baseParams = array( - 'scripts' => array( - 'foo.js', - 'bar.js', - ), - 'styles' => array( - 'foo.css', - 'bar.css' => array( 'media' => 'print' ), - 'screen.less' => array( 'media' => 'screen' ), - 'screen-query.css' => array( 'media' => 'screen and (min-width: 400px)' ), - ), - 'skinStyles' => array( - 'default' => 'quux-fallback.less', - 'fakeskin' => array( - 'baz-vector.css', - 'quux-vector.less', - ), - ), - 'messages' => array( - 'hello', - 'world', - ), - ); - - $module = new ResourceLoaderFileModule( $baseParams ); - - $this->assertEquals( - array( - 'foo.css', - 'baz-vector.css', - 'quux-vector.less', - 'quux-fallback.less', - 'bar.css', - 'screen.less', - 'screen-query.css', - ), - array_map( 'basename', $module->getAllStyleFiles() ) - ); - } - /** * @covers ResourceLoaderModule::getDefinitionSummary * @covers ResourceLoaderFileModule::getDefinitionSummary */ public function testDefinitionSummary() { - $context = self::getResourceLoaderContext(); + $context = $this->getResourceLoaderContext(); $baseParams = array( 'scripts' => array( 'foo.js', 'bar.js' ), diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php index a1893873..7f3506cc 100644 --- a/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php @@ -1,6 +1,6 @@ <?php -class ResourceLoaderStartupModuleTest extends ResourceLoaderTestCase { +class ResourceLoaderStartUpModuleTest extends ResourceLoaderTestCase { public static function provideGetModuleRegistrations() { return array( @@ -23,7 +23,7 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.blank", - "1388534400" + 1388534400 ] ] );', ) ), @@ -40,17 +40,17 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.blank", - "1388534400" + 1388534400 ], [ "test.group.foo", - "1388534400", + 1388534400, [], "x-foo" ], [ "test.group.bar", - "1388534400", + 1388534400, [], "x-bar" ] @@ -68,7 +68,7 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.blank", - "1388534400" + 1388534400 ] ] );' ) ), @@ -90,7 +90,7 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.blank", - "1388534400", + 1388534400, [], null, "example" @@ -115,8 +115,8 @@ mw.loader.addSource( { 'test.z.foo' => new ResourceLoaderTestModule( array( 'dependencies' => array( 'test.x.core', - 'test.x.polyfil', - 'test.y.polyfil', + 'test.x.polyfill', + 'test.y.polyfill', ), ) ), ), @@ -126,31 +126,31 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.x.core", - "1388534400" + 1388534400 ], [ "test.x.polyfill", - "1388534400", + 1388534400, [], null, - "local", + null, "return true;" ], [ "test.y.polyfill", - "1388534400", + 1388534400, [], null, - "local", + null, "return !!( window.JSON \u0026\u0026 JSON.parse \u0026\u0026 JSON.stringify);" ], [ "test.z.foo", - "1388534400", + 1388534400, [ - "test.x.core", - "test.x.polyfil", - "test.y.polyfil" + 0, + 1, + 2 ] ] ] );', @@ -222,63 +222,63 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.blank", - "1388534400" + 1388534400 ], [ "test.x.core", - "1388534400" + 1388534400 ], [ "test.x.util", - "1388534400", + 1388534400, [ - "test.x.core" + 1 ] ], [ "test.x.foo", - "1388534400", + 1388534400, [ - "test.x.core" + 1 ] ], [ "test.x.bar", - "1388534400", + 1388534400, [ - "test.x.util" + 2 ] ], [ "test.x.quux", - "1388534400", + 1388534400, [ - "test.x.foo", - "test.x.bar", + 3, + 4, "test.x.unknown" ] ], [ "test.group.foo.1", - "1388534400", + 1388534400, [], "x-foo" ], [ "test.group.foo.2", - "1388534400", + 1388534400, [], "x-foo" ], [ "test.group.bar.1", - "1388534400", + 1388534400, [], "x-bar" ], [ "test.group.bar.2", - "1388534400", + 1388534400, [], "x-bar", "example" @@ -290,7 +290,7 @@ mw.loader.addSource( { /** * @dataProvider provideGetModuleRegistrations - * @covers ResourceLoaderStartupModule::optimizeDependencies + * @covers ResourceLoaderStartUpModule::compileUnresolvedDependencies * @covers ResourceLoaderStartUpModule::getModuleRegistrations * @covers ResourceLoader::makeLoaderSourcesScript * @covers ResourceLoader::makeLoaderRegisterScript @@ -300,7 +300,7 @@ mw.loader.addSource( { $this->setMwGlobals( 'wgResourceLoaderSources', $case['sources'] ); } - $context = self::getResourceLoaderContext(); + $context = $this->getResourceLoaderContext(); $rl = $context->getResourceLoader(); $rl->register( $case['modules'] ); @@ -337,15 +337,15 @@ mw.loader.addSource( { public function testRegistrationsMinified( $modules ) { $this->setMwGlobals( 'wgResourceLoaderDebug', false ); - $context = self::getResourceLoaderContext(); + $context = $this->getResourceLoaderContext(); $rl = $context->getResourceLoader(); $rl->register( $modules ); $module = new ResourceLoaderStartUpModule(); $this->assertEquals( 'mw.loader.addSource({"local":"/w/load.php"});' . 'mw.loader.register([' -. '["test.blank","1388534400"],' -. '["test.min","1388534400",["test.blank"],null,"local",' +. '["test.blank",1388534400],' +. '["test.min",1388534400,[0],null,null,' . '"return!!(window.JSON\u0026\u0026JSON.parse\u0026\u0026JSON.stringify);"' . ']]);', $module->getModuleRegistrations( $context ), @@ -357,7 +357,7 @@ mw.loader.addSource( { * @dataProvider provideRegistrations */ public function testRegistrationsUnminified( $modules ) { - $context = self::getResourceLoaderContext(); + $context = $this->getResourceLoaderContext(); $rl = $context->getResourceLoader(); $rl->register( $modules ); $module = new ResourceLoaderStartUpModule(); @@ -367,16 +367,16 @@ mw.loader.addSource( { } );mw.loader.register( [ [ "test.blank", - "1388534400" + 1388534400 ], [ "test.min", - "1388534400", + 1388534400, [ - "test.blank" + 0 ], null, - "local", + null, "return !!( window.JSON \u0026\u0026 JSON.parse \u0026\u0026 JSON.stringify);" ] ] );', diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php index f19f6886..ca7307ec 100644 --- a/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php @@ -2,13 +2,9 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { - protected static $resourceLoaderRegisterModulesHook; - protected function setUp() { parent::setUp(); - // $wgResourceLoaderLESSFunctions, $wgResourceLoaderLESSImportPaths; $wgResourceLoaderLESSVars; - $this->setMwGlobals( array( 'wgResourceLoaderLESSFunctions' => array( 'test-sum' => function ( $frame, $less ) { @@ -30,35 +26,33 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { ) ); } - /* Hook Methods */ - - /** - * ResourceLoaderRegisterModules hook - */ - public static function resourceLoaderRegisterModules( &$resourceLoader ) { - self::$resourceLoaderRegisterModulesHook = true; - - return true; - } - - /* Provider Methods */ public static function provideValidModules() { return array( array( 'TEST.validModule1', new ResourceLoaderTestModule() ), ); } - /* Test Methods */ - /** * Ensures that the ResourceLoaderRegisterModules hook is called when a new * ResourceLoader object is constructed. * @covers ResourceLoader::__construct */ public function testCreatingNewResourceLoaderCallsRegistrationHook() { - self::$resourceLoaderRegisterModulesHook = false; + $resourceLoaderRegisterModulesHook = false; + + $this->setMwGlobals( 'wgHooks', array( + 'ResourceLoaderRegisterModules' => array( + function ( &$resourceLoader ) use ( &$resourceLoaderRegisterModulesHook ) { + $resourceLoaderRegisterModulesHook = true; + } + ) + ) ); + $resourceLoader = new ResourceLoader(); - $this->assertTrue( self::$resourceLoaderRegisterModulesHook ); + $this->assertTrue( + $resourceLoaderRegisterModulesHook, + 'Hook ResourceLoaderRegisterModules called' + ); return $resourceLoader; } @@ -80,7 +74,7 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { * @covers ResourceLoaderFileModule::compileLessFile */ public function testLessFileCompilation() { - $context = self::getResourceLoaderContext(); + $context = $this->getResourceLoaderContext(); $basePath = __DIR__ . '/../../data/less/module'; $module = new ResourceLoaderFileModule( array( 'localBasePath' => $basePath, @@ -96,43 +90,11 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { * @param string $css * @return string */ - private function stripNoflip( $css ) { + private static function stripNoflip( $css ) { return str_replace( '/*@noflip*/ ', '', $css ); } /** - * What happens when you mix @embed and @noflip? - * This really is an integration test, but oh well. - */ - public function testMixedCssAnnotations( ) { - $basePath = __DIR__ . '/../../data/css'; - $testModule = new ResourceLoaderFileModule( array( - 'localBasePath' => $basePath, - 'styles' => array( 'test.css' ), - ) ); - $expectedModule = new ResourceLoaderFileModule( array( - 'localBasePath' => $basePath, - 'styles' => array( 'expected.css' ), - ) ); - - $contextLtr = self::getResourceLoaderContext( 'en' ); - $contextRtl = self::getResourceLoaderContext( 'he' ); - - // Since we want to compare the effect of @noflip+@embed against the effect of just @embed, and - // the @noflip annotations are always preserved, we need to strip them first. - $this->assertEquals( - $expectedModule->getStyles( $contextLtr ), - $this->stripNoflip( $testModule->getStyles( $contextLtr ) ), - "/*@noflip*/ with /*@embed*/ gives correct results in LTR mode" - ); - $this->assertEquals( - $expectedModule->getStyles( $contextLtr ), - $this->stripNoflip( $testModule->getStyles( $contextRtl ) ), - "/*@noflip*/ with /*@embed*/ gives correct results in RTL mode" - ); - } - - /** * @dataProvider providePackedModules * @covers ResourceLoader::makePackedModulesString */ @@ -193,6 +155,7 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { /** * @dataProvider provideAddSource * @covers ResourceLoader::addSource + * @covers ResourceLoader::getSources */ public function testAddSource( $name, $info, $expected ) { $rl = new ResourceLoader; @@ -223,6 +186,106 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { ); } + public static function provideLoaderImplement() { + return array( + array( array( + 'title' => 'Implement scripts, styles and messages', + + 'name' => 'test.example', + 'scripts' => 'mw.example();', + 'styles' => array( 'css' => array( '.mw-example {}' ) ), + 'messages' => array( 'example' => '' ), + 'templates' => array(), + + 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) { +mw.example(); +}, { + "css": [ + ".mw-example {}" + ] +}, { + "example": "" +} );', + ) ), + array( array( + 'title' => 'Implement scripts', + + 'name' => 'test.example', + 'scripts' => 'mw.example();', + 'styles' => array(), + 'messages' => new XmlJsCode( '{}' ), + 'templates' => array(), + 'title' => 'scripts, styles and messags', + + 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) { +mw.example(); +} );', + ) ), + array( array( + 'title' => 'Implement styles', + + 'name' => 'test.example', + 'scripts' => array(), + 'styles' => array( 'css' => array( '.mw-example {}' ) ), + 'messages' => new XmlJsCode( '{}' ), + 'templates' => array(), + + 'expected' => 'mw.loader.implement( "test.example", [], { + "css": [ + ".mw-example {}" + ] +} );', + ) ), + array( array( + 'title' => 'Implement scripts and messages', + + 'name' => 'test.example', + 'scripts' => 'mw.example();', + 'styles' => array(), + 'messages' => array( 'example' => '' ), + 'templates' => array(), + + 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) { +mw.example(); +}, {}, { + "example": "" +} );', + ) ), + array( array( + 'title' => 'Implement scripts and templates', + + 'name' => 'test.example', + 'scripts' => 'mw.example();', + 'styles' => array(), + 'messages' => new XmlJsCode( '{}' ), + 'templates' => array( 'example.html' => '' ), + + 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) { +mw.example(); +}, {}, {}, { + "example.html": "" +} );', + ) ), + ); + } + + /** + * @dataProvider provideLoaderImplement + * @covers ResourceLoader::makeLoaderImplementScript + */ + public function testMakeLoaderImplementScript( $case ) { + $this->assertEquals( + $case['expected'], + ResourceLoader::makeLoaderImplementScript( + $case['name'], + $case['scripts'], + $case['styles'], + $case['messages'], + $case['templates'] + ) + ); + } + /** * @covers ResourceLoader::getLoadScript */ @@ -242,8 +305,14 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { $this->assertTrue( true ); } } -} -/* Hooks */ -global $wgHooks; -$wgHooks['ResourceLoaderRegisterModules'][] = 'ResourceLoaderTest::resourceLoaderRegisterModules'; + /** + * @covers ResourceLoader::isModuleRegistered + */ + public function testIsModuleRegistered() { + $rl = new ResourceLoader(); + $rl->register( 'test.module', new ResourceLoaderTestModule() ); + $this->assertTrue( $rl->isModuleRegistered( 'test.module' ) ); + $this->assertFalse( $rl->isModuleRegistered( 'test.modulenotregistered' ) ); + } +} diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderWikiModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderWikiModuleTest.php index 9dc18050..93a3ebba 100644 --- a/tests/phpunit/includes/resourceloader/ResourceLoaderWikiModuleTest.php +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderWikiModuleTest.php @@ -3,11 +3,93 @@ class ResourceLoaderWikiModuleTest extends ResourceLoaderTestCase { /** + * @covers ResourceLoaderWikiModule::__construct + * @dataProvider provideConstructor + */ + public function testConstructor( $params ) { + $module = new ResourceLoaderWikiModule( $params ); + $this->assertInstanceOf( 'ResourceLoaderWikiModule', $module ); + } + + public static function provideConstructor() { + return array( + // Nothing + array( null ), + array( array() ), + // Unrecognized settings + array( array( 'foo' => 'baz' ) ), + // Real settings + array( array( 'scripts' => array( 'MediaWiki:Common.js' ) ) ), + ); + } + + /** + * @dataProvider provideGetPages + * @covers ResourceLoaderWikiModule::getPages + */ + public function testGetPages( $params, Config $config, $expected ) { + $module = new ResourceLoaderWikiModule( $params ); + $module->setConfig( $config ); + + // Use getDefinitionSummary because getPages is protected + $summary = $module->getDefinitionSummary( ResourceLoaderContext::newDummyContext() ); + $this->assertEquals( + $expected, + $summary['pages'] + ); + } + + public static function provideGetPages() { + $settings = array( + 'UseSiteJs' => true, + 'UseSiteCss' => true, + ); + + $params = array( + 'styles' => array( 'MediaWiki:Common.css' ), + 'scripts' => array( 'MediaWiki:Common.js' ), + ); + + return array( + array( array(), new HashConfig( $settings ), array() ), + array( $params, new HashConfig( $settings ), array( + 'MediaWiki:Common.js' => array( 'type' => 'script' ), + 'MediaWiki:Common.css' => array( 'type' => 'style' ) + ) ), + array( $params, new HashConfig( array( 'UseSiteCss' => false ) + $settings ), array( + 'MediaWiki:Common.js' => array( 'type' => 'script' ), + ) ), + array( $params, new HashConfig( array( 'UseSiteJs' => false ) + $settings ), array( + 'MediaWiki:Common.css' => array( 'type' => 'style' ), + ) ), + array( $params, new HashConfig( array( 'UseSiteJs' => false, 'UseSiteCss' => false ) ), array() ), + ); + } + + /** + * @covers ResourceLoaderWikiModule::getGroup + * @dataProvider provideGetGroup + */ + public function testGetGroup( $params, $expected ) { + $module = new ResourceLoaderWikiModule( $params ); + $this->assertEquals( $expected, $module->getGroup() ); + } + + public static function provideGetGroup() { + return array( + // No group specified + array( array(), null ), + // A random group + array( array( 'group' => 'foobar' ), 'foobar' ), + ); + } + + /** * @covers ResourceLoaderWikiModule::isKnownEmpty * @dataProvider provideIsKnownEmpty */ public function testIsKnownEmpty( $titleInfo, $group, $expected ) { - $module = $this->getMockBuilder( 'ResourceLoaderWikiModuleTestModule' ) + $module = $this->getMockBuilder( 'ResourceLoaderWikiModule' ) ->setMethods( array( 'getTitleInfo', 'getGroup' ) ) ->getMock(); $module->expects( $this->any() ) diff --git a/tests/phpunit/includes/resourceloader/templates/template.html b/tests/phpunit/includes/resourceloader/templates/template.html new file mode 100644 index 00000000..1f6a7d22 --- /dev/null +++ b/tests/phpunit/includes/resourceloader/templates/template.html @@ -0,0 +1 @@ +<strong>hello</strong> diff --git a/tests/phpunit/includes/resourceloader/templates/template2.html b/tests/phpunit/includes/resourceloader/templates/template2.html new file mode 100644 index 00000000..a322f67d --- /dev/null +++ b/tests/phpunit/includes/resourceloader/templates/template2.html @@ -0,0 +1 @@ +<div>goodbye</div> diff --git a/tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars b/tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars new file mode 100644 index 00000000..5f5c07d5 --- /dev/null +++ b/tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars @@ -0,0 +1 @@ +wow diff --git a/tests/phpunit/includes/search/SearchEngineTest.php b/tests/phpunit/includes/search/SearchEngineTest.php index 3da13615..d0cbfa0a 100644 --- a/tests/phpunit/includes/search/SearchEngineTest.php +++ b/tests/phpunit/includes/search/SearchEngineTest.php @@ -14,8 +14,6 @@ class SearchEngineTest extends MediaWikiLangTestCase { */ protected $search; - protected $pageList; - /** * Checks for database type & version. * Will skip current test if DB does not support search. @@ -37,10 +35,6 @@ class SearchEngineTest extends MediaWikiLangTestCase { 'wgSearchType' => $searchType ) ); - if ( !isset( self::$pageList ) ) { - $this->addPages(); - } - $this->search = new $searchType( $this->db ); } @@ -50,33 +44,32 @@ class SearchEngineTest extends MediaWikiLangTestCase { parent::tearDown(); } - protected function addPages() { + public function addDBData() { if ( !$this->isWikitextNS( NS_MAIN ) ) { // @todo cover the case of non-wikitext content in the main namespace return; } - $this->insertPage( "Not_Main_Page", "This is not a main page", 0 ); + $this->insertPage( 'Not_Main_Page', 'This is not a main page' ); $this->insertPage( 'Talk:Not_Main_Page', - 'This is not a talk page to the main page, see [[smithee]]', - 1 + 'This is not a talk page to the main page, see [[smithee]]' ); - $this->insertPage( 'Smithee', 'A smithee is one who smiths. See also [[Alan Smithee]]', 0 ); - $this->insertPage( 'Talk:Smithee', 'This article sucks.', 1 ); - $this->insertPage( 'Unrelated_page', 'Nothing in this page is about the S word.', 0 ); - $this->insertPage( 'Another_page', 'This page also is unrelated.', 0 ); - $this->insertPage( 'Help:Help', 'Help me!', 4 ); - $this->insertPage( 'Thppt', 'Blah blah', 0 ); - $this->insertPage( 'Alan_Smithee', 'yum', 0 ); - $this->insertPage( 'Pages', 'are\'food', 0 ); - $this->insertPage( 'HalfOneUp', 'AZ', 0 ); - $this->insertPage( 'FullOneUp', 'AZ', 0 ); - $this->insertPage( 'HalfTwoLow', 'az', 0 ); - $this->insertPage( 'FullTwoLow', 'az', 0 ); - $this->insertPage( 'HalfNumbers', '1234567890', 0 ); - $this->insertPage( 'FullNumbers', '1234567890', 0 ); - $this->insertPage( 'DomainName', 'example.com', 0 ); + $this->insertPage( 'Smithee', 'A smithee is one who smiths. See also [[Alan Smithee]]' ); + $this->insertPage( 'Talk:Smithee', 'This article sucks.' ); + $this->insertPage( 'Unrelated_page', 'Nothing in this page is about the S word.' ); + $this->insertPage( 'Another_page', 'This page also is unrelated.' ); + $this->insertPage( 'Help:Help', 'Help me!' ); + $this->insertPage( 'Thppt', 'Blah blah' ); + $this->insertPage( 'Alan_Smithee', 'yum' ); + $this->insertPage( 'Pages', 'are\'food' ); + $this->insertPage( 'HalfOneUp', 'AZ' ); + $this->insertPage( 'FullOneUp', 'AZ' ); + $this->insertPage( 'HalfTwoLow', 'az' ); + $this->insertPage( 'FullTwoLow', 'az' ); + $this->insertPage( 'HalfNumbers', '1234567890' ); + $this->insertPage( 'FullNumbers', '1234567890' ); + $this->insertPage( 'DomainName', 'example.com' ); } protected function fetchIds( $results ) { @@ -101,30 +94,6 @@ class SearchEngineTest extends MediaWikiLangTestCase { return $matches; } - /** - * Insert a new page - * - * @param string $pageName Page name - * @param string $text Page's content - * @param int $ns Unused - */ - protected function insertPage( $pageName, $text, $ns ) { - $title = Title::newFromText( $pageName, $ns ); - - $user = User::newFromName( 'WikiSysop' ); - $comment = 'Search Test'; - - // avoid memory leak...? - LinkCache::singleton()->clear(); - - $page = WikiPage::factory( $title ); - $page->doEditContent( ContentHandler::makeContent( $text, $title ), $comment, 0, false, $user ); - - $this->pageList[] = array( $title, $page->getId() ); - - return true; - } - public function testFullWidth() { $this->assertEquals( array( 'FullOneUp', 'FullTwoLow', 'HalfOneUp', 'HalfTwoLow' ), diff --git a/tests/phpunit/includes/site/CachingSiteStoreTest.php b/tests/phpunit/includes/site/CachingSiteStoreTest.php new file mode 100644 index 00000000..d0a79803 --- /dev/null +++ b/tests/phpunit/includes/site/CachingSiteStoreTest.php @@ -0,0 +1,161 @@ +<?php + +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @since 1.25 + * + * @ingroup Site + * @ingroup Test + * + * @group Site + * @group Database + * + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + */ +class CachingSiteStoreTest extends MediaWikiTestCase { + + /** + * @covers CachingSiteStore::getSites + */ + public function testGetSites() { + $testSites = TestSites::getSites(); + + $store = new CachingSiteStore( + $this->getHashSiteStore( $testSites ), + wfGetMainCache() + ); + + $sites = $store->getSites(); + + $this->assertInstanceOf( 'SiteList', $sites ); + + /** + * @var Site $site + */ + foreach ( $sites as $site ) { + $this->assertInstanceOf( 'Site', $site ); + } + + foreach ( $testSites as $site ) { + if ( $site->getGlobalId() !== null ) { + $this->assertTrue( $sites->hasSite( $site->getGlobalId() ) ); + } + } + } + + /** + * @covers CachingSiteStore::saveSites + */ + public function testSaveSites() { + $store = new CachingSiteStore( new HashSiteStore(), wfGetMainCache() ); + + $sites = array(); + + $site = new Site(); + $site->setGlobalId( 'ertrywuutr' ); + $site->setLanguageCode( 'en' ); + $sites[] = $site; + + $site = new MediaWikiSite(); + $site->setGlobalId( 'sdfhxujgkfpth' ); + $site->setLanguageCode( 'nl' ); + $sites[] = $site; + + $this->assertTrue( $store->saveSites( $sites ) ); + + $site = $store->getSite( 'ertrywuutr' ); + $this->assertInstanceOf( 'Site', $site ); + $this->assertEquals( 'en', $site->getLanguageCode() ); + + $site = $store->getSite( 'sdfhxujgkfpth' ); + $this->assertInstanceOf( 'Site', $site ); + $this->assertEquals( 'nl', $site->getLanguageCode() ); + } + + /** + * @covers CachingSiteStore::reset + */ + public function testReset() { + $dbSiteStore = $this->getMockBuilder( 'SiteStore' ) + ->disableOriginalConstructor() + ->getMock(); + + // php 5.3 compatibility! + $self = $this; + + $dbSiteStore->expects( $this->any() ) + ->method( 'getSite' ) + ->will( $this->returnValue( $self->getTestSite() ) ); + + $dbSiteStore->expects( $this->any() ) + ->method( 'getSites' ) + ->will( $this->returnCallback( function() use( $self ) { + $siteList = new SiteList(); + $siteList->setSite( $self->getTestSite() ); + + return $siteList; + } ) ); + + $store = new CachingSiteStore( $dbSiteStore, wfGetMainCache() ); + + // initialize internal cache + $this->assertGreaterThan( 0, $store->getSites()->count(), 'count sites' ); + + $store->getSite( 'enwiki' )->setLanguageCode( 'en-ca' ); + + // sanity check: $store should have the new language code for 'enwiki' + $this->assertEquals( 'en-ca', $store->getSite( 'enwiki' )->getLanguageCode(), 'sanity check' ); + + // purge cache + $store->reset(); + + // the internal cache of $store should be updated, and now pulling + // the site from the 'fallback' DBSiteStore with the original language code. + $this->assertEquals( 'en', $store->getSite( 'enwiki' )->getLanguageCode(), 'reset' ); + } + + public function getTestSite() { + $enwiki = new MediaWikiSite(); + $enwiki->setGlobalId( 'enwiki' ); + $enwiki->setLanguageCode( 'en' ); + + return $enwiki; + } + + /** + * @covers CachingSiteStore::clear + */ + public function testClear() { + $store = new CachingSiteStore( new HashSiteStore(), wfGetMainCache() ); + $this->assertTrue( $store->clear() ); + + $site = $store->getSite( 'enwiki' ); + $this->assertNull( $site ); + + $sites = $store->getSites(); + $this->assertEquals( 0, $sites->count() ); + } + + private function getHashSiteStore( array $sites ) { + $siteStore = new HashSiteStore(); + $siteStore->saveSites( $sites ); + + return $siteStore; + } + +} diff --git a/tests/phpunit/includes/site/DBSiteStoreTest.php b/tests/phpunit/includes/site/DBSiteStoreTest.php new file mode 100644 index 00000000..673ba54d --- /dev/null +++ b/tests/phpunit/includes/site/DBSiteStoreTest.php @@ -0,0 +1,133 @@ +<?php + +/** + * Tests for the DBSiteStore class. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @since 1.21 + * + * @ingroup Site + * @ingroup Test + * + * @group Site + * @group Database + * + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + */ +class DBSiteStoreTest extends MediaWikiTestCase { + + /** + * @covers DBSiteStore::getSites + */ + public function testGetSites() { + $expectedSites = TestSites::getSites(); + TestSites::insertIntoDb(); + + $store = new DBSiteStore(); + + $sites = $store->getSites(); + + $this->assertInstanceOf( 'SiteList', $sites ); + + /** + * @var Site $site + */ + foreach ( $sites as $site ) { + $this->assertInstanceOf( 'Site', $site ); + } + + foreach ( $expectedSites as $site ) { + if ( $site->getGlobalId() !== null ) { + $this->assertTrue( $sites->hasSite( $site->getGlobalId() ) ); + } + } + } + + /** + * @covers DBSiteStore::saveSites + */ + public function testSaveSites() { + $store = new DBSiteStore(); + + $sites = array(); + + $site = new Site(); + $site->setGlobalId( 'ertrywuutr' ); + $site->setLanguageCode( 'en' ); + $sites[] = $site; + + $site = new MediaWikiSite(); + $site->setGlobalId( 'sdfhxujgkfpth' ); + $site->setLanguageCode( 'nl' ); + $sites[] = $site; + + $this->assertTrue( $store->saveSites( $sites ) ); + + $site = $store->getSite( 'ertrywuutr' ); + $this->assertInstanceOf( 'Site', $site ); + $this->assertEquals( 'en', $site->getLanguageCode() ); + $this->assertTrue( is_integer( $site->getInternalId() ) ); + $this->assertTrue( $site->getInternalId() >= 0 ); + + $site = $store->getSite( 'sdfhxujgkfpth' ); + $this->assertInstanceOf( 'Site', $site ); + $this->assertEquals( 'nl', $site->getLanguageCode() ); + $this->assertTrue( is_integer( $site->getInternalId() ) ); + $this->assertTrue( $site->getInternalId() >= 0 ); + } + + /** + * @covers DBSiteStore::reset + */ + public function testReset() { + $store1 = new DBSiteStore(); + $store2 = new DBSiteStore(); + + // initialize internal cache + $this->assertGreaterThan( 0, $store1->getSites()->count() ); + $this->assertGreaterThan( 0, $store2->getSites()->count() ); + + // Clear actual data. Will purge the external cache and reset the internal + // cache in $store1, but not the internal cache in store2. + $this->assertTrue( $store1->clear() ); + + // sanity check: $store2 should have a stale cache now + $this->assertNotNull( $store2->getSite( 'enwiki' ) ); + + // purge cache + $store2->reset(); + + // ...now the internal cache of $store2 should be updated and thus empty. + $site = $store2->getSite( 'enwiki' ); + $this->assertNull( $site ); + } + + /** + * @covers DBSiteStore::clear + */ + public function testClear() { + $store = new DBSiteStore(); + $this->assertTrue( $store->clear() ); + + $site = $store->getSite( 'enwiki' ); + $this->assertNull( $site ); + + $sites = $store->getSites(); + $this->assertEquals( 0, $sites->count() ); + } +} diff --git a/tests/phpunit/includes/site/FileBasedSiteLookupTest.php b/tests/phpunit/includes/site/FileBasedSiteLookupTest.php new file mode 100644 index 00000000..90ebe5c4 --- /dev/null +++ b/tests/phpunit/includes/site/FileBasedSiteLookupTest.php @@ -0,0 +1,101 @@ +<?php + +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @since 1.25 + * + * @ingroup Site + * @ingroup Test + * + * @covers FileBasedSiteLookup + * @group Site + * + * @author Katie Filbert < aude.wiki@gmail.com > + */ +class FileBasedSiteLookupTest extends PHPUnit_Framework_TestCase { + + protected function setUp() { + $this->cacheFile = $this->getCacheFile(); + } + + protected function tearDown() { + unlink( $this->cacheFile ); + } + + public function testGetSites() { + $sites = $this->getSites(); + $cacheBuilder = $this->newSitesCacheFileBuilder( $sites ); + $cacheBuilder->build(); + + $cache = new FileBasedSiteLookup( $this->cacheFile ); + $this->assertEquals( $sites, $cache->getSites() ); + } + + public function testGetSite() { + $sites = $this->getSites(); + $cacheBuilder = $this->newSitesCacheFileBuilder( $sites ); + $cacheBuilder->build(); + + $cache = new FileBasedSiteLookup( $this->cacheFile ); + + $this->assertEquals( $sites->getSite( 'enwiktionary' ), $cache->getSite( 'enwiktionary' ) ); + } + + private function newSitesCacheFileBuilder( SiteList $sites ) { + return new SitesCacheFileBuilder( + $this->getSiteLookup( $sites ), + $this->cacheFile + ); + } + + private function getSiteLookup( SiteList $sites ) { + $siteLookup = $this->getMockBuilder( 'SiteLookup' ) + ->disableOriginalConstructor() + ->getMock(); + + $siteLookup->expects( $this->any() ) + ->method( 'getSites' ) + ->will( $this->returnValue( $sites ) ); + + return $siteLookup; + } + + private function getSites() { + $sites = array(); + + $site = new Site(); + $site->setGlobalId( 'foobar' ); + $sites[] = $site; + + $site = new MediaWikiSite(); + $site->setGlobalId( 'enwiktionary' ); + $site->setGroup( 'wiktionary' ); + $site->setLanguageCode( 'en' ); + $site->addNavigationId( 'enwiktionary' ); + $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" ); + $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" ); + $sites[] = $site; + + return new SiteList( $sites ); + } + + private function getCacheFile() { + return tempnam( sys_get_temp_dir(), 'mw-test-sitelist' ); + } + +} diff --git a/tests/phpunit/includes/site/HashSiteStoreTest.php b/tests/phpunit/includes/site/HashSiteStoreTest.php new file mode 100644 index 00000000..49a96338 --- /dev/null +++ b/tests/phpunit/includes/site/HashSiteStoreTest.php @@ -0,0 +1,105 @@ +<?php + +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @since 1.25 + * + * @ingroup Site + * @group Site + * + * @author Katie Filbert < aude.wiki@gmail.com > + */ +class HashSiteStoreTest extends MediaWikiTestCase { + + /** + * @covers HashSiteStore::getSites + */ + public function testGetSites() { + $expectedSites = array(); + + foreach( TestSites::getSites() as $testSite ) { + $siteId = $testSite->getGlobalId(); + $expectedSites[$siteId] = $testSite; + } + + $siteStore = new HashSiteStore( $expectedSites ); + + $this->assertEquals( new SiteList( $expectedSites ), $siteStore->getSites() ); + } + + /** + * @covers HashSiteStore::saveSite + * @covers HashSiteStore::getSite + */ + public function testSaveSite() { + $store = new HashSiteStore(); + + $site = new Site(); + $site->setGlobalId( 'dewiki' ); + + $this->assertCount( 0, $store->getSites(), '0 sites in store' ); + + $store->saveSite( $site ); + + $this->assertCount( 1, $store->getSites(), 'Store has 1 sites' ); + $this->assertEquals( $site, $store->getSite( 'dewiki' ), 'Store has dewiki' ); + } + + /** + * @covers HashSiteStore::saveSites + */ + public function testSaveSites() { + $store = new HashSiteStore(); + + $sites = array(); + + $site = new Site(); + $site->setGlobalId( 'enwiki' ); + $site->setLanguageCode( 'en' ); + $sites[] = $site; + + $site = new MediaWikiSite(); + $site->setGlobalId( 'eswiki' ); + $site->setLanguageCode( 'es' ); + $sites[] = $site; + + $this->assertCount( 0, $store->getSites(), '0 sites in store' ); + + $store->saveSites( $sites ); + + $this->assertCount( 2, $store->getSites(), 'Store has 2 sites' ); + $this->assertTrue( $store->getSites()->hasSite( 'enwiki' ), 'Store has enwiki' ); + $this->assertTrue( $store->getSites()->hasSite( 'eswiki' ), 'Store has eswiki' ); + } + + /** + * @covers HashSiteStore::clear + */ + public function testClear() { + $store = new HashSiteStore(); + + $site = new Site(); + $site->setGlobalId( 'arwiki' ); + $store->saveSite( $site ); + + $this->assertCount( 1, $store->getSites(), '1 site in store' ); + + $store->clear(); + $this->assertCount( 0, $store->getSites(), '0 sites in store' ); + } +} diff --git a/tests/phpunit/includes/site/MediaWikiSiteTest.php b/tests/phpunit/includes/site/MediaWikiSiteTest.php index c3fd1557..ef2ccca2 100644 --- a/tests/phpunit/includes/site/MediaWikiSiteTest.php +++ b/tests/phpunit/includes/site/MediaWikiSiteTest.php @@ -26,7 +26,6 @@ * * @group Site * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class MediaWikiSiteTest extends SiteTest { diff --git a/tests/phpunit/includes/site/SiteExporterTest.php b/tests/phpunit/includes/site/SiteExporterTest.php new file mode 100644 index 00000000..19dd0aa1 --- /dev/null +++ b/tests/phpunit/includes/site/SiteExporterTest.php @@ -0,0 +1,147 @@ +<?php + +/** + * Tests for the SiteExporter class. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * + * @ingroup Site + * @ingroup Test + * + * @group Site + * + * @covers SiteExporter + * + * @author Daniel Kinzler + */ +class SiteExporterTest extends PHPUnit_Framework_TestCase { + + public function testConstructor_InvalidArgument() { + $this->setExpectedException( 'InvalidArgumentException' ); + + new SiteExporter( 'Foo' ); + } + + public function testExportSites() { + $foo = Site::newForType( Site::TYPE_UNKNOWN ); + $foo->setGlobalId( 'Foo' ); + + $acme = Site::newForType( Site::TYPE_UNKNOWN ); + $acme->setGlobalId( 'acme.com' ); + $acme->setGroup( 'Test' ); + $acme->addLocalId( Site::ID_INTERWIKI, 'acme' ); + $acme->setPath( Site::PATH_LINK, 'http://acme.com/' ); + + $tmp = tmpfile(); + $exporter = new SiteExporter( $tmp ); + + $exporter->exportSites( array( $foo, $acme ) ); + + fseek( $tmp, 0 ); + $xml = fread( $tmp, 16*1024 ); + + $this->assertContains( '<sites ', $xml ); + $this->assertContains( '<site>', $xml ); + $this->assertContains( '<globalid>Foo</globalid>', $xml ); + $this->assertContains( '</site>', $xml ); + $this->assertContains( '<globalid>acme.com</globalid>', $xml ); + $this->assertContains( '<group>Test</group>', $xml ); + $this->assertContains( '<localid type="interwiki">acme</localid>', $xml ); + $this->assertContains( '<path type="link">http://acme.com/</path>', $xml ); + $this->assertContains( '</sites>', $xml ); + + // NOTE: HHVM (at least on wmf Jenkins) doesn't like file URLs. + $xsdFile = __DIR__ . '/../../../../docs/sitelist-1.0.xsd'; + $xsdData = file_get_contents( $xsdFile ); + + $document = new DOMDocument(); + $document->loadXML( $xml, LIBXML_NONET ); + $document->schemaValidateSource( $xsdData ); + } + + private function newSiteStore( SiteList $sites ) { + $store = $this->getMock( 'SiteStore' ); + + $store->expects( $this->once() ) + ->method( 'saveSites' ) + ->will( $this->returnCallback( function ( $moreSites ) use ( $sites ) { + foreach ( $moreSites as $site ) { + $sites->setSite( $site ); + } + } ) ); + + $store->expects( $this->any() ) + ->method( 'getSites' ) + ->will( $this->returnValue( new SiteList() ) ); + + return $store; + } + + public function provideRoundTrip() { + $foo = Site::newForType( Site::TYPE_UNKNOWN ); + $foo->setGlobalId( 'Foo' ); + + $acme = Site::newForType( Site::TYPE_UNKNOWN ); + $acme->setGlobalId( 'acme.com' ); + $acme->setGroup( 'Test' ); + $acme->addLocalId( Site::ID_INTERWIKI, 'acme' ); + $acme->setPath( Site::PATH_LINK, 'http://acme.com/' ); + + $dewiki = Site::newForType( Site::TYPE_MEDIAWIKI ); + $dewiki->setGlobalId( 'dewiki' ); + $dewiki->setGroup( 'wikipedia' ); + $dewiki->setForward( true ); + $dewiki->addLocalId( Site::ID_INTERWIKI, 'wikipedia' ); + $dewiki->addLocalId( Site::ID_EQUIVALENT, 'de' ); + $dewiki->setPath( Site::PATH_LINK, 'http://de.wikipedia.org/w/' ); + $dewiki->setPath( MediaWikiSite::PATH_PAGE, 'http://de.wikipedia.org/wiki/' ); + $dewiki->setSource( 'meta.wikimedia.org' ); + + return array( + 'empty' => array( + new SiteList() + ), + + 'some' => array( + new SiteList( array( $foo, $acme, $dewiki ) ), + ), + ); + } + + /** + * @dataProvider provideRoundTrip() + */ + public function testRoundTrip( SiteList $sites ) { + $tmp = tmpfile(); + $exporter = new SiteExporter( $tmp ); + + $exporter->exportSites( $sites ); + + fseek( $tmp, 0 ); + $xml = fread( $tmp, 16*1024 ); + + $actualSites = new SiteList(); + $store = $this->newSiteStore( $actualSites ); + + $importer = new SiteImporter( $store ); + $importer->importFromXML( $xml ); + + $this->assertEquals( $sites, $actualSites ); + } + +} diff --git a/tests/phpunit/includes/site/SiteImporterTest.php b/tests/phpunit/includes/site/SiteImporterTest.php new file mode 100644 index 00000000..cb0316ab --- /dev/null +++ b/tests/phpunit/includes/site/SiteImporterTest.php @@ -0,0 +1,200 @@ +<?php + +/** + * Tests for the SiteImporter class. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * + * @ingroup Site + * @ingroup Test + * + * @group Site + * + * @covers SiteImporter + * + * @author Daniel Kinzler + */ +class SiteImporterTest extends PHPUnit_Framework_TestCase { + + private function newSiteImporter( array $expectedSites, $errorCount ) { + $store = $this->getMock( 'SiteStore' ); + + $self = $this; + $store->expects( $this->once() ) + ->method( 'saveSites' ) + ->will( $this->returnCallback( function ( $sites ) use ( $expectedSites, $self ) { + $self->assertSitesEqual( $expectedSites, $sites ); + } ) ); + + $store->expects( $this->any() ) + ->method( 'getSites' ) + ->will( $this->returnValue( new SiteList() ) ); + + $errorHandler = $this->getMock( 'Psr\Log\LoggerInterface' ); + $errorHandler->expects( $this->exactly( $errorCount ) ) + ->method( 'error' ); + + $importer = new SiteImporter( $store ); + $importer->setExceptionCallback( array( $errorHandler, 'error' ) ); + + return $importer; + } + + public function assertSitesEqual( $expected, $actual, $message = '' ) { + $this->assertEquals( + $this->getSerializedSiteList( $expected ), + $this->getSerializedSiteList( $actual ), + $message + ); + } + + public function provideImportFromXML() { + $foo = Site::newForType( Site::TYPE_UNKNOWN ); + $foo->setGlobalId( 'Foo' ); + + $acme = Site::newForType( Site::TYPE_UNKNOWN ); + $acme->setGlobalId( 'acme.com' ); + $acme->setGroup( 'Test' ); + $acme->addLocalId( Site::ID_INTERWIKI, 'acme' ); + $acme->setPath( Site::PATH_LINK, 'http://acme.com/' ); + + $dewiki = Site::newForType( Site::TYPE_MEDIAWIKI ); + $dewiki->setGlobalId( 'dewiki' ); + $dewiki->setGroup( 'wikipedia' ); + $dewiki->setForward( true ); + $dewiki->addLocalId( Site::ID_INTERWIKI, 'wikipedia' ); + $dewiki->addLocalId( Site::ID_EQUIVALENT, 'de' ); + $dewiki->setPath( Site::PATH_LINK, 'http://de.wikipedia.org/w/' ); + $dewiki->setPath( MediaWikiSite::PATH_PAGE, 'http://de.wikipedia.org/wiki/' ); + $dewiki->setSource( 'meta.wikimedia.org' ); + + return array( + 'empty' => array( + '<sites></sites>', + array(), + ), + 'no sites' => array( + '<sites><Foo><globalid>Foo</globalid></Foo><Bar><quux>Bla</quux></Bar></sites>', + array(), + ), + 'minimal' => array( + '<sites>' . + '<site><globalid>Foo</globalid></site>' . + '</sites>', + array( $foo ), + ), + 'full' => array( + '<sites>' . + '<site><globalid>Foo</globalid></site>' . + '<site>' . + '<globalid>acme.com</globalid>' . + '<localid type="interwiki">acme</localid>' . + '<group>Test</group>' . + '<path type="link">http://acme.com/</path>' . + '</site>' . + '<site type="mediawiki">' . + '<source>meta.wikimedia.org</source>' . + '<globalid>dewiki</globalid>' . + '<localid type="interwiki">wikipedia</localid>' . + '<localid type="equivalent">de</localid>' . + '<group>wikipedia</group>' . + '<forward/>' . + '<path type="link">http://de.wikipedia.org/w/</path>' . + '<path type="page_path">http://de.wikipedia.org/wiki/</path>' . + '</site>' . + '</sites>', + array( $foo, $acme, $dewiki ), + ), + 'skip' => array( + '<sites>' . + '<site><globalid>Foo</globalid></site>' . + '<site><barf>Foo</barf></site>' . + '<site>' . + '<globalid>acme.com</globalid>' . + '<localid type="interwiki">acme</localid>' . + '<silly>boop!</silly>' . + '<group>Test</group>' . + '<path type="link">http://acme.com/</path>' . + '</site>' . + '</sites>', + array( $foo, $acme ), + 1 + ), + ); + } + + /** + * @dataProvider provideImportFromXML + */ + public function testImportFromXML( $xml, array $expectedSites, $errorCount = 0 ) { + $importer = $this->newSiteImporter( $expectedSites, $errorCount ); + $importer->importFromXML( $xml ); + } + + public function testImportFromXML_malformed() { + $this->setExpectedException( 'Exception' ); + + $store = $this->getMock( 'SiteStore' ); + $importer = new SiteImporter( $store ); + $importer->importFromXML( 'THIS IS NOT XML' ); + } + + public function testImportFromFile() { + $foo = Site::newForType( Site::TYPE_UNKNOWN ); + $foo->setGlobalId( 'Foo' ); + + $acme = Site::newForType( Site::TYPE_UNKNOWN ); + $acme->setGlobalId( 'acme.com' ); + $acme->setGroup( 'Test' ); + $acme->addLocalId( Site::ID_INTERWIKI, 'acme' ); + $acme->setPath( Site::PATH_LINK, 'http://acme.com/' ); + + $dewiki = Site::newForType( Site::TYPE_MEDIAWIKI ); + $dewiki->setGlobalId( 'dewiki' ); + $dewiki->setGroup( 'wikipedia' ); + $dewiki->setForward( true ); + $dewiki->addLocalId( Site::ID_INTERWIKI, 'wikipedia' ); + $dewiki->addLocalId( Site::ID_EQUIVALENT, 'de' ); + $dewiki->setPath( Site::PATH_LINK, 'http://de.wikipedia.org/w/' ); + $dewiki->setPath( MediaWikiSite::PATH_PAGE, 'http://de.wikipedia.org/wiki/' ); + $dewiki->setSource( 'meta.wikimedia.org' ); + + $importer = $this->newSiteImporter( array( $foo, $acme, $dewiki ), 0 ); + + $file = __DIR__ . '/SiteImporterTest.xml'; + $importer->importFromFile( $file ); + } + + /** + * @param Site[] $sites + * + * @return array[] + */ + private function getSerializedSiteList( $sites ) { + $serialized = array(); + + foreach ( $sites as $site ) { + $key = $site->getGlobalId(); + $data = unserialize( $site->serialize() ); + + $serialized[$key] = $data; + } + + return $serialized; + } +} diff --git a/tests/phpunit/includes/site/SiteImporterTest.xml b/tests/phpunit/includes/site/SiteImporterTest.xml new file mode 100644 index 00000000..720b1faf --- /dev/null +++ b/tests/phpunit/includes/site/SiteImporterTest.xml @@ -0,0 +1,19 @@ +<sites version="1.0" xmlns="http://www.mediawiki.org/xml/sitelist-1.0/"> + <site><globalid>Foo</globalid></site> + <site> + <globalid>acme.com</globalid> + <localid type="interwiki">acme</localid> + <group>Test</group> + <path type="link">http://acme.com/</path> + </site> + <site type="mediawiki"> + <source>meta.wikimedia.org</source> + <globalid>dewiki</globalid> + <localid type="interwiki">wikipedia</localid> + <localid type="equivalent">de</localid> + <group>wikipedia</group> + <forward/> + <path type="link">http://de.wikipedia.org/w/</path> + <path type="page_path">http://de.wikipedia.org/wiki/</path> + </site> +</sites> diff --git a/tests/phpunit/includes/site/SiteListTest.php b/tests/phpunit/includes/site/SiteListTest.php index 534ed9c9..d6c58cf6 100644 --- a/tests/phpunit/includes/site/SiteListTest.php +++ b/tests/phpunit/includes/site/SiteListTest.php @@ -26,7 +26,6 @@ * * @group Site * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class SiteListTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/site/SiteSQLStoreTest.php b/tests/phpunit/includes/site/SiteSQLStoreTest.php index 6002c1a1..69088006 100644 --- a/tests/phpunit/includes/site/SiteSQLStoreTest.php +++ b/tests/phpunit/includes/site/SiteSQLStoreTest.php @@ -1,8 +1,6 @@ <?php /** - * Tests for the SiteSQLStore class. - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -19,7 +17,7 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @since 1.21 + * @since 1.25 * * @ingroup Site * @ingroup Test @@ -27,108 +25,16 @@ * @group Site * @group Database * - * @licence GNU GPL v2+ - * @author Jeroen De Dauw < jeroendedauw@gmail.com > + * @author Katie Filbert < aude.wiki@gmail.com > */ class SiteSQLStoreTest extends MediaWikiTestCase { /** - * @covers SiteSQLStore::getSites - */ - public function testGetSites() { - $expectedSites = TestSites::getSites(); - TestSites::insertIntoDb(); - - $store = SiteSQLStore::newInstance(); - - $sites = $store->getSites(); - - $this->assertInstanceOf( 'SiteList', $sites ); - - /** - * @var Site $site - */ - foreach ( $sites as $site ) { - $this->assertInstanceOf( 'Site', $site ); - } - - foreach ( $expectedSites as $site ) { - if ( $site->getGlobalId() !== null ) { - $this->assertTrue( $sites->hasSite( $site->getGlobalId() ) ); - } - } - } - - /** - * @covers SiteSQLStore::saveSites - */ - public function testSaveSites() { - $store = SiteSQLStore::newInstance(); - - $sites = array(); - - $site = new Site(); - $site->setGlobalId( 'ertrywuutr' ); - $site->setLanguageCode( 'en' ); - $sites[] = $site; - - $site = new MediaWikiSite(); - $site->setGlobalId( 'sdfhxujgkfpth' ); - $site->setLanguageCode( 'nl' ); - $sites[] = $site; - - $this->assertTrue( $store->saveSites( $sites ) ); - - $site = $store->getSite( 'ertrywuutr' ); - $this->assertInstanceOf( 'Site', $site ); - $this->assertEquals( 'en', $site->getLanguageCode() ); - $this->assertTrue( is_integer( $site->getInternalId() ) ); - $this->assertTrue( $site->getInternalId() >= 0 ); - - $site = $store->getSite( 'sdfhxujgkfpth' ); - $this->assertInstanceOf( 'Site', $site ); - $this->assertEquals( 'nl', $site->getLanguageCode() ); - $this->assertTrue( is_integer( $site->getInternalId() ) ); - $this->assertTrue( $site->getInternalId() >= 0 ); - } - - /** - * @covers SiteSQLStore::reset + * @covers SiteSQLStore::newInstance */ - public function testReset() { - $store1 = SiteSQLStore::newInstance(); - $store2 = SiteSQLStore::newInstance(); - - // initialize internal cache - $this->assertGreaterThan( 0, $store1->getSites()->count() ); - $this->assertGreaterThan( 0, $store2->getSites()->count() ); - - // Clear actual data. Will purge the external cache and reset the internal - // cache in $store1, but not the internal cache in store2. - $this->assertTrue( $store1->clear() ); - - // sanity check: $store2 should have a stale cache now - $this->assertNotNull( $store2->getSite( 'enwiki' ) ); - - // purge cache - $store2->reset(); - - // ...now the internal cache of $store2 should be updated and thus empty. - $site = $store2->getSite( 'enwiki' ); - $this->assertNull( $site ); + public function testNewInstance() { + $siteStore = SiteSQLStore::newInstance(); + $this->assertInstanceOf( 'SiteSQLStore', $siteStore ); } - /** - * @covers SiteSQLStore::clear - */ - public function testClear() { - $store = SiteSQLStore::newInstance(); - $this->assertTrue( $store->clear() ); - - $site = $store->getSite( 'enwiki' ); - $this->assertNull( $site ); - - $sites = $store->getSites(); - $this->assertEquals( 0, $sites->count() ); - } } diff --git a/tests/phpunit/includes/site/SiteTest.php b/tests/phpunit/includes/site/SiteTest.php index 29c1ff33..63d90d2e 100644 --- a/tests/phpunit/includes/site/SiteTest.php +++ b/tests/phpunit/includes/site/SiteTest.php @@ -26,7 +26,6 @@ * * @group Site * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class SiteTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/site/SitesCacheFileBuilderTest.php b/tests/phpunit/includes/site/SitesCacheFileBuilderTest.php new file mode 100644 index 00000000..087341a0 --- /dev/null +++ b/tests/phpunit/includes/site/SitesCacheFileBuilderTest.php @@ -0,0 +1,135 @@ +<?php + +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @since 1.25 + * + * @ingroup Site + * @ingroup Test + * + * @covers SitesCacheFileBuilder + * @group Site + * + * @author Katie Filbert < aude.wiki@gmail.com > + */ +class SitesCacheFileBuilderTest extends PHPUnit_Framework_TestCase { + + protected function setUp() { + $this->cacheFile = $this->getCacheFile(); + } + + protected function tearDown() { + unlink( $this->cacheFile ); + } + + public function testBuild() { + $cacheBuilder = $this->newSitesCacheFileBuilder( $this->getSites() ); + $cacheBuilder->build(); + + $contents = file_get_contents( $this->cacheFile ); + $this->assertEquals( json_encode( $this->getExpectedData() ), $contents ); + } + + private function getExpectedData() { + return array( + 'sites' => array( + 'foobar' => array( + 'globalid' => 'foobar', + 'type' => 'unknown', + 'group' => 'none', + 'source' => 'local', + 'language' => null, + 'localids' => array(), + 'config' => array(), + 'data' => array(), + 'forward' => false, + 'internalid' => null, + 'identifiers' => array() + ), + 'enwiktionary' => array( + 'globalid' => 'enwiktionary', + 'type' => 'mediawiki', + 'group' => 'wiktionary', + 'source' => 'local', + 'language' => 'en', + 'localids' => array( + 'equivalent' => array( 'enwiktionary' ) + ), + 'config' => array(), + 'data' => array( + 'paths' => array( + 'page_path' => 'https://en.wiktionary.org/wiki/$1', + 'file_path' => 'https://en.wiktionary.org/w/$1' + ) + ), + 'forward' => false, + 'internalid' => null, + 'identifiers' => array( + array( + 'type' => 'equivalent', + 'key' => 'enwiktionary' + ) + ) + ) + ) + ); + } + + private function newSitesCacheFileBuilder( SiteList $sites ) { + return new SitesCacheFileBuilder( + $this->getSiteLookup( $sites ), + $this->cacheFile + ); + } + + private function getSiteLookup( SiteList $sites ) { + $siteLookup = $this->getMockBuilder( 'SiteLookup' ) + ->disableOriginalConstructor() + ->getMock(); + + $siteLookup->expects( $this->any() ) + ->method( 'getSites' ) + ->will( $this->returnValue( $sites ) ); + + return $siteLookup; + } + + private function getSites() { + $sites = array(); + + $site = new Site(); + $site->setGlobalId( 'foobar' ); + $sites[] = $site; + + $site = new MediaWikiSite(); + $site->setGlobalId( 'enwiktionary' ); + $site->setGroup( 'wiktionary' ); + $site->setLanguageCode( 'en' ); + $site->addNavigationId( 'enwiktionary' ); + $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" ); + $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" ); + $sites[] = $site; + + return new SiteList( $sites ); + } + + private function getCacheFile() { + return tempnam( sys_get_temp_dir(), 'mw-test-sitelist' ); + } + +} diff --git a/tests/phpunit/includes/site/TestSites.php b/tests/phpunit/includes/site/TestSites.php index af314ba2..4c402484 100644 --- a/tests/phpunit/includes/site/TestSites.php +++ b/tests/phpunit/includes/site/TestSites.php @@ -26,7 +26,6 @@ * * @group Site * - * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class TestSites { @@ -108,7 +107,7 @@ class TestSites { * @since 0.1 */ public static function insertIntoDb() { - $sitesTable = SiteSQLStore::newInstance(); + $sitesTable = new DBSiteStore(); $sitesTable->clear(); $sitesTable->saveSites( TestSites::getSites() ); } diff --git a/tests/phpunit/includes/skins/SkinTemplateTest.php b/tests/phpunit/includes/skins/SkinTemplateTest.php index baa995d4..8084a66f 100644 --- a/tests/phpunit/includes/skins/SkinTemplateTest.php +++ b/tests/phpunit/includes/skins/SkinTemplateTest.php @@ -5,7 +5,6 @@ * * @group Output * - * @licence GNU GPL v2+ * @author Bene* < benestar.wikimedia@gmail.com > */ diff --git a/tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php b/tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php index 779fa558..fd6911f6 100644 --- a/tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php +++ b/tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php @@ -28,21 +28,52 @@ class SpecialPageFactoryTest extends MediaWikiTestCase { SpecialPageFactory::resetList(); } + public function testResetList() { + SpecialPageFactory::resetList(); + $this->assertContains( 'Specialpages', SpecialPageFactory::getNames() ); + } + + public function testHookNotCalledTwice() { + $count = 0; + $this->mergeMwGlobalArrayValue( 'wgHooks', array( + 'SpecialPage_initList' => array( + function () use ( &$count ) { + $count++; + } + ) ) ); + SpecialPageFactory::resetList(); + SpecialPageFactory::getNames(); + SpecialPageFactory::getNames(); + $this->assertEquals( 1, $count ); + } + public function newSpecialAllPages() { return new SpecialAllPages(); } public function specialPageProvider() { + $specialPageTestHelper = new SpecialPageTestHelper(); + return array( 'class name' => array( 'SpecialAllPages', false ), - 'closure' => array( function() { + 'closure' => array( function () { return new SpecialAllPages(); }, false ), - 'function' => array( array( $this, 'newSpecialAllPages' ), false ), + 'function' => array( array( $this, 'newSpecialAllPages' ), false ), + 'callback string' => array( 'SpecialPageTestHelper::newSpecialAllPages', false ), + 'callback with object' => array( + array( $specialPageTestHelper, 'newSpecialAllPages' ), + false + ), + 'callback array' => array( + array( 'SpecialPageTestHelper', 'newSpecialAllPages' ), + false + ) ); } /** + * @covers SpecialPageFactory::getPage * @dataProvider specialPageProvider */ public function testGetPage( $spec, $shouldReuseInstance ) { @@ -56,6 +87,9 @@ class SpecialPageFactoryTest extends MediaWikiTestCase { $this->assertEquals( $shouldReuseInstance, $page2 === $page, "Should re-use instance:" ); } + /** + * @covers SpecialPageFactory::getNames + */ public function testGetNames() { $this->mergeMwGlobalArrayValue( 'wgSpecialPages', array( 'testdummy' => 'SpecialAllPages' ) ); SpecialPageFactory::resetList(); @@ -65,6 +99,9 @@ class SpecialPageFactoryTest extends MediaWikiTestCase { $this->assertContains( 'testdummy', $names ); } + /** + * @covers SpecialPageFactory::resolveAlias + */ public function testResolveAlias() { $this->setMwGlobals( 'wgContLang', Language::factory( 'de' ) ); SpecialPageFactory::resetList(); @@ -74,6 +111,9 @@ class SpecialPageFactoryTest extends MediaWikiTestCase { $this->assertEquals( 'Foo', $param ); } + /** + * @covers SpecialPageFactory::getLocalNameFor + */ public function testGetLocalNameFor() { $this->setMwGlobals( 'wgContLang', Language::factory( 'de' ) ); SpecialPageFactory::resetList(); @@ -82,6 +122,9 @@ class SpecialPageFactoryTest extends MediaWikiTestCase { $this->assertEquals( 'Spezialseiten/Foo', $name ); } + /** + * @covers SpecialPageFactory::getTitleForAlias + */ public function testGetTitleForAlias() { $this->setMwGlobals( 'wgContLang', Language::factory( 'de' ) ); SpecialPageFactory::resetList(); @@ -222,4 +265,19 @@ class SpecialPageFactoryTest extends MediaWikiTestCase { ); } + public function testGetAliasListRecursion() { + $called = false; + $this->mergeMwGlobalArrayValue( 'wgHooks', array( + 'SpecialPage_initList' => array( + function () use ( &$called ) { + SpecialPageFactory::getLocalNameFor( 'Specialpages' ); + $called = true; + } + ), + ) ); + SpecialPageFactory::resetList(); + SpecialPageFactory::getLocalNameFor( 'Specialpages' ); + $this->assertTrue( $called, 'Recursive call succeeded' ); + } + } diff --git a/tests/phpunit/includes/SpecialPageTest.php b/tests/phpunit/includes/specialpage/SpecialPageTest.php index 245cdffd..5a0aef97 100644 --- a/tests/phpunit/includes/SpecialPageTest.php +++ b/tests/phpunit/includes/specialpage/SpecialPageTest.php @@ -5,7 +5,6 @@ * * @group Database * - * @licence GNU GPL v2+ * @author Katie Filbert < aude.wiki@gmail.com > */ class SpecialPageTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/specialpage/SpecialPageTestHelper.php b/tests/phpunit/includes/specialpage/SpecialPageTestHelper.php new file mode 100644 index 00000000..37e29dcb --- /dev/null +++ b/tests/phpunit/includes/specialpage/SpecialPageTestHelper.php @@ -0,0 +1,24 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + */ +class SpecialPageTestHelper { + + public static function newSpecialAllPages() { + return new SpecialAllPages(); + } + +} diff --git a/tests/phpunit/includes/specials/SpecialBooksourcesTest.php b/tests/phpunit/includes/specials/SpecialBooksourcesTest.php new file mode 100644 index 00000000..69485a03 --- /dev/null +++ b/tests/phpunit/includes/specials/SpecialBooksourcesTest.php @@ -0,0 +1,36 @@ +<?php +class SpecialBooksourcesTest extends MediaWikiTestCase { + public static function provideISBNs() { + return array( + array( '978-0-300-14424-6', true ), + array( '0-14-020652-3', true ), + array( '020652-3', false ), + array( '9781234567897', true ), + array( '1-4133-0454-0', true ), + array( '978-1413304541', true ), + array( '0136091814', true ), + array( '0136091812', false ), + array( '9780136091813', true ), + array( '9780136091817', false ), + array( '123456789X', true ), + + // Bug 67021 + array( '1413304541', false ), + array( '141330454X', false ), + array( '1413304540', true ), + array( '14133X4540', false ), + array( '97814133X4541', false ), + array( '978035642615X', false ), + array( '9781413304541', true ), + array( '9780356426150', true ), + ); + } + + /** + * @covers SpecialBookSources::isValidISBN + * @dataProvider provideISBNs + */ + public function testIsValidISBN( $isbn, $isValid ) { + $this->assertSame( $isValid, SpecialBookSources::isValidISBN( $isbn ) ); + } +} diff --git a/tests/phpunit/includes/specials/SpecialMIMESearchTest.php b/tests/phpunit/includes/specials/SpecialMIMESearchTest.php index 14d19685..fe1c9e83 100644 --- a/tests/phpunit/includes/specials/SpecialMIMESearchTest.php +++ b/tests/phpunit/includes/specials/SpecialMIMESearchTest.php @@ -5,11 +5,11 @@ class SpecialMIMESearchTest extends MediaWikiTestCase { - /** @var MIMESearchPage */ + /** @var MIMEsearchPage */ private $page; function setUp() { - $this->page = new MIMESearchPage; + $this->page = new MIMEsearchPage; $context = new RequestContext(); $context->setTitle( Title::makeTitle( NS_SPECIAL, 'MIMESearch' ) ); $context->setRequest( new FauxRequest() ); diff --git a/tests/phpunit/includes/title/ForeignTitleTest.php b/tests/phpunit/includes/title/ForeignTitleTest.php new file mode 100644 index 00000000..599d2a33 --- /dev/null +++ b/tests/phpunit/includes/title/ForeignTitleTest.php @@ -0,0 +1,103 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author This, that and the other + */ + +/** + * @covers ForeignTitle + * + * @group Title + */ +class ForeignTitleTest extends MediaWikiTestCase { + + public function basicProvider() { + return array( + array( + new ForeignTitle( 20, 'Contributor', 'JohnDoe' ), + 20, 'Contributor', 'JohnDoe' + ), + array( + new ForeignTitle( '1', 'Discussion', 'Capital' ), + 1, 'Discussion', 'Capital' + ), + array( + new ForeignTitle( 0, '', 'MainNamespace' ), + 0, '', 'MainNamespace' + ), + array( + new ForeignTitle( 4, 'Some ns', 'Article title with spaces' ), + 4, 'Some_ns', 'Article_title_with_spaces' + ), + ); + } + + /** + * @dataProvider basicProvider + */ + public function testBasic( ForeignTitle $title, $expectedId, $expectedName, + $expectedText ) { + + $this->assertEquals( true, $title->isNamespaceIdKnown() ); + $this->assertEquals( $expectedId, $title->getNamespaceId() ); + $this->assertEquals( $expectedName, $title->getNamespaceName() ); + $this->assertEquals( $expectedText, $title->getText() ); + } + + public function testUnknownNamespaceCheck( ) { + $title = new ForeignTitle( null, 'this', 'that' ); + + $this->assertEquals( false, $title->isNamespaceIdKnown() ); + $this->assertEquals( 'this', $title->getNamespaceName() ); + $this->assertEquals( 'that', $title->getText() ); + } + + public function testUnknownNamespaceError( ) { + $this->setExpectedException( 'MWException' ); + $title = new ForeignTitle( null, 'this', 'that' ); + $title->getNamespaceId(); + } + + public function fullTextProvider() { + return array( + array( + new ForeignTitle( 20, 'Contributor', 'JohnDoe' ), + 'Contributor:JohnDoe' + ), + array( + new ForeignTitle( '1', 'Discussion', 'Capital' ), + 'Discussion:Capital' + ), + array( + new ForeignTitle( 0, '', 'MainNamespace' ), + 'MainNamespace' + ), + array( + new ForeignTitle( 4, 'Some ns', 'Article title with spaces' ), + 'Some_ns:Article_title_with_spaces' + ), + ); + } + + /** + * @dataProvider fullTextProvider + */ + public function testFullText( ForeignTitle $title, $fullText ) { + $this->assertEquals( $fullText, $title->getFullText() ); + } +} diff --git a/tests/phpunit/includes/title/MediaWikiPageLinkRendererTest.php b/tests/phpunit/includes/title/MediaWikiPageLinkRendererTest.php index 4171c10e..cd0d0b1c 100644 --- a/tests/phpunit/includes/title/MediaWikiPageLinkRendererTest.php +++ b/tests/phpunit/includes/title/MediaWikiPageLinkRendererTest.php @@ -16,7 +16,6 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @license GPL 2+ * @author Daniel Kinzler */ diff --git a/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php b/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php index f95b3050..78d304c1 100644 --- a/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php +++ b/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php @@ -16,7 +16,6 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @license GPL 2+ * @author Daniel Kinzler */ @@ -39,6 +38,7 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { 'wgLang' => Language::factory( 'en' ), 'wgAllowUserJs' => false, 'wgDefaultLanguageVariant' => false, + 'wgMetaNamespace' => 'Project', 'wgLocalInterwikis' => array( 'localtestiw' ), 'wgCapitalLinks' => true, @@ -82,6 +82,8 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { protected function makeCodec( $lang ) { $gender = $this->getGenderCache(); $lang = Language::factory( $lang ); + // language object can came from cache, which does not respect test settings + $lang->resetNamespaces(); return new MediaWikiTitleCodec( $lang, $gender ); } @@ -367,13 +369,6 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { /** * @dataProvider provideGetNamespaceName - * - * @param int $namespace - * @param string $text - * @param string $lang - * @param string $expected - * - * @internal param \TitleValue $title */ public function testGetNamespaceName( $namespace, $text, $lang, $expected ) { $codec = $this->makeCodec( $lang ); diff --git a/tests/phpunit/includes/title/NaiveForeignTitleFactoryTest.php b/tests/phpunit/includes/title/NaiveForeignTitleFactoryTest.php new file mode 100644 index 00000000..504e8712 --- /dev/null +++ b/tests/phpunit/includes/title/NaiveForeignTitleFactoryTest.php @@ -0,0 +1,91 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author This, that and the other + */ + +/** + * @covers NaiveForeignTitleFactory + * + * @group Title + */ +class NaiveForeignTitleFactoryTest extends MediaWikiTestCase { + + public function basicProvider() { + return array( + array( + 'MainNamespaceArticle', 0, + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + ), + array( + 'MainNamespaceArticle', null, + new ForeignTitle( null, '', 'MainNamespaceArticle' ), + ), + array( + 'Talk:Nice_talk', 1, + new ForeignTitle( 1, 'Talk', 'Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', 0, + new ForeignTitle( 0, '', 'Bogus:Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', 9000, // non-existent local namespace ID + new ForeignTitle( 9000, 'Bogus', 'Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', 4, // existing local namespace ID + new ForeignTitle( 4, 'Bogus', 'Nice_talk' ), + ), + array( + 'Talk:Extra:Nice_talk', 1, + new ForeignTitle( 1, 'Talk', 'Extra:Nice_talk' ), + ), + array( + 'Talk:Extra:Nice_talk', null, + new ForeignTitle( null, 'Talk', 'Extra:Nice_talk' ), + ), + ); + } + + /** + * @dataProvider basicProvider + */ + public function testBasic( $title, $ns, ForeignTitle $foreignTitle ) { + $factory = new NaiveForeignTitleFactory(); + $testTitle = $factory->createForeignTitle( $title, $ns ); + + $this->assertEquals( $testTitle->isNamespaceIdKnown(), + $foreignTitle->isNamespaceIdKnown() ); + + if ( + $testTitle->isNamespaceIdKnown() && + $foreignTitle->isNamespaceIdKnown() + ) { + $this->assertEquals( $testTitle->getNamespaceId(), + $foreignTitle->getNamespaceId() ); + } + + $this->assertEquals( $testTitle->getNamespaceName(), + $foreignTitle->getNamespaceName() ); + $this->assertEquals( $testTitle->getText(), $foreignTitle->getText() ); + + $this->assertEquals( str_replace( ' ', '_', $title ), + $foreignTitle->getFullText() ); + } +} diff --git a/tests/phpunit/includes/title/NaiveImportTitleFactoryTest.php b/tests/phpunit/includes/title/NaiveImportTitleFactoryTest.php new file mode 100644 index 00000000..98b414e0 --- /dev/null +++ b/tests/phpunit/includes/title/NaiveImportTitleFactoryTest.php @@ -0,0 +1,89 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author This, that and the other + */ + +/** + * @covers NaiveImportTitleFactory + * + * @group Title + */ +class NaiveImportTitleFactoryTest extends MediaWikiTestCase { + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgLanguageCode' => 'en', + 'wgContLang' => Language::factory( 'en' ), + 'wgExtraNamespaces' => array( 100 => 'Portal' ), + ) ); + } + + public function basicProvider() { + return array( + array( + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + Title::newFromText( 'MainNamespaceArticle' ) + ), + array( + new ForeignTitle( null, '', 'MainNamespaceArticle' ), + Title::newFromText( 'MainNamespaceArticle' ) + ), + array( + new ForeignTitle( 1, 'Discussion', 'Nice_talk' ), + Title::newFromText( 'Talk:Nice_talk' ) + ), + array( + new ForeignTitle( 0, '', 'Bogus:Nice_talk' ), + Title::newFromText( 'Bogus:Nice_talk' ) + ), + array( + new ForeignTitle( 100, 'Bogus', 'Nice_talk' ), + Title::newFromText( 'Bogus:Nice_talk' ) // not Portal:Nice_talk + ), + array( + new ForeignTitle( 1, 'Bogus', 'Nice_talk' ), + Title::newFromText( 'Talk:Nice_talk' ) // not Bogus:Nice_talk + ), + array( + new ForeignTitle( 100, 'Portal', 'Nice_talk' ), + Title::newFromText( 'Portal:Nice_talk' ) + ), + array( + new ForeignTitle( 724, 'Portal', 'Nice_talk' ), + Title::newFromText( 'Portal:Nice_talk' ) + ), + array( + new ForeignTitle( 2, 'Portal', 'Nice_talk' ), + Title::newFromText( 'User:Nice_talk' ) + ), + ); + } + + /** + * @dataProvider basicProvider + */ + public function testBasic( ForeignTitle $foreignTitle, Title $title ) { + $factory = new NaiveImportTitleFactory(); + $testTitle = $factory->createTitleFromForeignTitle( $foreignTitle ); + + $this->assertTrue( $title->equals( $testTitle ) ); + } +} diff --git a/tests/phpunit/includes/title/NamespaceAwareForeignTitleFactoryTest.php b/tests/phpunit/includes/title/NamespaceAwareForeignTitleFactoryTest.php new file mode 100644 index 00000000..9cb195cc --- /dev/null +++ b/tests/phpunit/includes/title/NamespaceAwareForeignTitleFactoryTest.php @@ -0,0 +1,89 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author This, that and the other + */ + +/** + * @covers NamespaceAwareForeignTitleFactory + * + * @group Title + */ +class NamespaceAwareForeignTitleFactoryTest extends MediaWikiTestCase { + + public function basicProvider() { + return array( + array( + 'MainNamespaceArticle', 0, + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + ), + array( + 'MainNamespaceArticle', null, + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + ), + array( + 'Talk:Nice_talk', 1, + new ForeignTitle( 1, 'Talk', 'Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', 0, + new ForeignTitle( 0, '', 'Bogus:Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', null, + new ForeignTitle( 9000, 'Bogus', 'Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', 4, + new ForeignTitle( 4, 'Bogus', 'Nice_talk' ), + ), + array( + 'Bogus:Nice_talk', 1, + new ForeignTitle( 1, 'Talk', 'Nice_talk' ), + ), + ); + } + + /** + * @dataProvider basicProvider + */ + public function testBasic( $title, $ns, ForeignTitle $foreignTitle ) { + + $foreignNamespaces = array( + 0 => '', 1 => 'Talk', 100 => 'Portal', 9000 => 'Bogus' + ); + + $factory = new NamespaceAwareForeignTitleFactory( $foreignNamespaces ); + $testTitle = $factory->createForeignTitle( $title, $ns ); + + $this->assertEquals( $testTitle->isNamespaceIdKnown(), + $foreignTitle->isNamespaceIdKnown() ); + + if ( + $testTitle->isNamespaceIdKnown() && + $foreignTitle->isNamespaceIdKnown() + ) { + $this->assertEquals( $testTitle->getNamespaceId(), + $foreignTitle->getNamespaceId() ); + } + + $this->assertEquals( $testTitle->getNamespaceName(), + $foreignTitle->getNamespaceName() ); + $this->assertEquals( $testTitle->getText(), $foreignTitle->getText() ); + } +} diff --git a/tests/phpunit/includes/title/NamespaceImportTitleFactoryTest.php b/tests/phpunit/includes/title/NamespaceImportTitleFactoryTest.php new file mode 100644 index 00000000..d6fe6848 --- /dev/null +++ b/tests/phpunit/includes/title/NamespaceImportTitleFactoryTest.php @@ -0,0 +1,77 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author This, that and the other + */ + +/** + * @covers NamespaceImportTitleFactory + * + * @group Title + */ +class NamespaceImportTitleFactoryTest extends MediaWikiTestCase { + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgLanguageCode' => 'en', + 'wgContLang' => Language::factory( 'en' ), + ) ); + } + + public function basicProvider() { + return array( + array( + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + 0, + Title::newFromText( 'MainNamespaceArticle' ) + ), + array( + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + 2, + Title::newFromText( 'User:MainNamespaceArticle' ) + ), + array( + new ForeignTitle( 1, 'Discussion', 'Nice_talk' ), + 0, + Title::newFromText( 'Nice_talk' ) + ), + array( + new ForeignTitle( 0, '', 'Bogus:Nice_talk' ), + 0, + Title::newFromText( 'Bogus:Nice_talk' ) + ), + array( + new ForeignTitle( 0, '', 'Bogus:Nice_talk' ), + 2, + Title::newFromText( 'User:Bogus:Nice_talk' ) + ), + ); + } + + /** + * @dataProvider basicProvider + */ + public function testBasic( ForeignTitle $foreignTitle, $ns, Title $title ) { + $factory = new NamespaceImportTitleFactory( $ns ); + $testTitle = $factory->createTitleFromForeignTitle( $foreignTitle ); + + $this->assertTrue( $title->equals( $testTitle ) ); + } +} diff --git a/tests/phpunit/includes/title/SubpageImportTitleFactoryTest.php b/tests/phpunit/includes/title/SubpageImportTitleFactoryTest.php new file mode 100644 index 00000000..d5c17f3e --- /dev/null +++ b/tests/phpunit/includes/title/SubpageImportTitleFactoryTest.php @@ -0,0 +1,86 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author This, that and the other + */ + +/** + * @covers SubpageImportTitleFactory + * + * @group Title + */ +class SubpageImportTitleFactoryTest extends MediaWikiTestCase { + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgLanguageCode' => 'en', + 'wgContLang' => Language::factory( 'en' ), + 'wgNamespacesWithSubpages' => array( 0 => false, 2 => true ), + ) ); + } + + public function basicProvider() { + return array( + array( + new ForeignTitle( 0, '', 'MainNamespaceArticle' ), + Title::newFromText( 'User:Graham' ), + Title::newFromText( 'User:Graham/MainNamespaceArticle' ) + ), + array( + new ForeignTitle( 1, 'Discussion', 'Nice_talk' ), + Title::newFromText( 'User:Graham' ), + Title::newFromText( 'User:Graham/Discussion:Nice_talk' ) + ), + array( + new ForeignTitle( 0, '', 'Bogus:Nice_talk' ), + Title::newFromText( 'User:Graham' ), + Title::newFromText( 'User:Graham/Bogus:Nice_talk' ) + ), + ); + } + + /** + * @dataProvider basicProvider + */ + public function testBasic( ForeignTitle $foreignTitle, Title $rootPage, + Title $title ) { + + $factory = new SubpageImportTitleFactory( $rootPage ); + $testTitle = $factory->createTitleFromForeignTitle( $foreignTitle ); + + $this->assertTrue( $testTitle->equals( $title ) ); + } + + public function failureProvider() { + return array( + array( + Title::newFromText( 'Graham' ), + ), + ); + } + + /** + * @dataProvider failureProvider + */ + public function testFailures( Title $rootPage ) { + $this->setExpectedException( 'MWException' ); + new SubpageImportTitleFactory( $rootPage ); + } +} diff --git a/tests/phpunit/includes/title/TitleValueTest.php b/tests/phpunit/includes/title/TitleValueTest.php index 3ba008d6..184198d2 100644 --- a/tests/phpunit/includes/title/TitleValueTest.php +++ b/tests/phpunit/includes/title/TitleValueTest.php @@ -16,7 +16,6 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @license GPL 2+ * @author Daniel Kinzler */ diff --git a/tests/phpunit/includes/upload/UploadBaseTest.php b/tests/phpunit/includes/upload/UploadBaseTest.php index 3d3b0068..9441b77f 100644 --- a/tests/phpunit/includes/upload/UploadBaseTest.php +++ b/tests/phpunit/includes/upload/UploadBaseTest.php @@ -9,21 +9,17 @@ class UploadBaseTest extends MediaWikiTestCase { protected $upload; protected function setUp() { - global $wgHooks; parent::setUp(); $this->upload = new UploadTestHandler; - $this->hooks = $wgHooks; - $wgHooks['InterwikiLoadPrefix'][] = function ( $prefix, &$data ) { - return false; - }; - } - - protected function tearDown() { - global $wgHooks; - $wgHooks = $this->hooks; - parent::tearDown(); + $this->setMwGlobals( 'wgHooks', array( + 'InterwikiLoadPrefix' => array( + function ( $prefix, &$data ) { + return false; + } + ), + ) ); } /** @@ -97,7 +93,7 @@ class UploadBaseTest extends MediaWikiTestCase { // Helper used to create an empty file of size $size. private function createFileOfSize( $size ) { - $filename = tempnam( wfTempDir(), "mwuploadtest" ); + $filename = $this->getNewTempFile(); $fh = fopen( $filename, 'w' ); ftruncate( $fh, $size ); @@ -112,22 +108,21 @@ class UploadBaseTest extends MediaWikiTestCase { * This method should be abstracted so we can test different settings. */ public function testMaxUploadSize() { - global $wgMaxUploadSize; - $savedGlobal = $wgMaxUploadSize; // save global - global $wgFileExtensions; - $wgFileExtensions[] = 'txt'; - - $wgMaxUploadSize = 100; + $this->setMwGlobals( array( + 'wgMaxUploadSize' => 100, + 'wgFileExtensions' => array( + 'txt', + ), + ) ); - $filename = $this->createFileOfSize( $wgMaxUploadSize ); + $filename = $this->createFileOfSize( 100 ); $this->upload->initializePathInfo( basename( $filename ) . '.txt', $filename, 100 ); $result = $this->upload->verifyUpload(); - unlink( $filename ); $this->assertEquals( - array( 'status' => UploadBase::OK ), $result ); - - $wgMaxUploadSize = $savedGlobal; // restore global + array( 'status' => UploadBase::OK ), + $result + ); } diff --git a/tests/phpunit/includes/upload/UploadFromUrlTest.php b/tests/phpunit/includes/upload/UploadFromUrlTest.php index ec56b63e..b7496629 100644 --- a/tests/phpunit/includes/upload/UploadFromUrlTest.php +++ b/tests/phpunit/includes/upload/UploadFromUrlTest.php @@ -35,7 +35,10 @@ class UploadFromUrlTest extends ApiTestCase { wfSetupSession( $sessionId ); - return array( $module->getResultData(), $req ); + return array( + $module->getResult()->getResultData( null, array( 'Strip' => 'all' ) ), + $req + ); } /** diff --git a/tests/phpunit/includes/utils/CdbTest.php b/tests/phpunit/includes/utils/CdbTest.php deleted file mode 100644 index 487ee1fc..00000000 --- a/tests/phpunit/includes/utils/CdbTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * Test the CDB reader/writer - * @covers CdbWriterPHP - * @covers CdbWriterDBA - */ -class CdbTest extends MediaWikiTestCase { - - protected function setUp() { - parent::setUp(); - if ( !CdbReader::haveExtension() ) { - $this->markTestSkipped( 'Native CDB support is not available' ); - } - } - - /** - * @group medium - */ - public function testCdb() { - $dir = wfTempDir(); - if ( !is_writable( $dir ) ) { - $this->markTestSkipped( "Temp dir isn't writable" ); - } - - $phpcdbfile = $this->getNewTempFile(); - $dbacdbfile = $this->getNewTempFile(); - - $w1 = new CdbWriterPHP( $phpcdbfile ); - $w2 = new CdbWriterDBA( $dbacdbfile ); - - $data = array(); - for ( $i = 0; $i < 1000; $i++ ) { - $key = $this->randomString(); - $value = $this->randomString(); - $w1->set( $key, $value ); - $w2->set( $key, $value ); - - if ( !isset( $data[$key] ) ) { - $data[$key] = $value; - } - } - - $w1->close(); - $w2->close(); - - $this->assertEquals( - md5_file( $phpcdbfile ), - md5_file( $dbacdbfile ), - 'same hash' - ); - - $r1 = new CdbReaderPHP( $phpcdbfile ); - $r2 = new CdbReaderDBA( $dbacdbfile ); - - foreach ( $data as $key => $value ) { - if ( $key === '' ) { - // Known bug - continue; - } - $v1 = $r1->get( $key ); - $v2 = $r2->get( $key ); - - $v1 = $v1 === false ? '(not found)' : $v1; - $v2 = $v2 === false ? '(not found)' : $v2; - - # cdbAssert( 'Mismatch', $key, $v1, $v2 ); - $this->cdbAssert( "PHP error", $key, $v1, $value ); - $this->cdbAssert( "DBA error", $key, $v2, $value ); - } - } - - private function randomString() { - $len = mt_rand( 0, 10 ); - $s = ''; - for ( $j = 0; $j < $len; $j++ ) { - $s .= chr( mt_rand( 0, 255 ) ); - } - - return $s; - } - - private function cdbAssert( $msg, $key, $v1, $v2 ) { - $this->assertEquals( - $v2, - $v1, - $msg . ', k=' . bin2hex( $key ) - ); - } -} diff --git a/tests/phpunit/includes/utils/IPTest.php b/tests/phpunit/includes/utils/IPTest.php index ebe347fd..09c1587d 100644 --- a/tests/phpunit/includes/utils/IPTest.php +++ b/tests/phpunit/includes/utils/IPTest.php @@ -9,7 +9,7 @@ * dataprovider. */ -class IPTest extends MediaWikiTestCase { +class IPTest extends PHPUnit_Framework_TestCase { /** * not sure it should be tested with boolean false. hashar 20100924 * @covers IP::isIPAddress diff --git a/tests/phpunit/includes/utils/MWCryptHKDFTest.php b/tests/phpunit/includes/utils/MWCryptHKDFTest.php index 7e37534a..73e4c1a9 100644 --- a/tests/phpunit/includes/utils/MWCryptHKDFTest.php +++ b/tests/phpunit/includes/utils/MWCryptHKDFTest.php @@ -6,6 +6,12 @@ class MWCryptHKDFTest extends MediaWikiTestCase { + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( 'wgSecretKey', '5bf1945342e67799cb50704a7fa19ac6' ); + } + /** * Test basic usage works */ diff --git a/tests/phpunit/includes/MWFunctionTest.php b/tests/phpunit/includes/utils/MWFunctionTest.php index f2a720e8..f4d17999 100644 --- a/tests/phpunit/includes/MWFunctionTest.php +++ b/tests/phpunit/includes/utils/MWFunctionTest.php @@ -13,6 +13,7 @@ class MWFunctionTest extends MediaWikiTestCase { $args = array( $arg1, $arg2, $arg3, $arg4 ); $newObject = new MWBlankClass( $arg1, $arg2, $arg3, $arg4 ); + $this->hideDeprecated( 'MWFunction::newObj' ); $this->assertEquals( MWFunction::newObj( 'MWBlankClass', $args )->args, $newObject->args diff --git a/tests/phpunit/includes/utils/UIDGeneratorTest.php b/tests/phpunit/includes/utils/UIDGeneratorTest.php index 50fa3849..0e11ccad 100644 --- a/tests/phpunit/includes/utils/UIDGeneratorTest.php +++ b/tests/phpunit/includes/utils/UIDGeneratorTest.php @@ -35,14 +35,14 @@ class UIDGeneratorTest extends MediaWikiTestCase { $lastId_bin = wfBaseConvert( $lastId, 10, 2 ); $this->assertGreaterThanOrEqual( - substr( $id_bin, 0, $tbits ), substr( $lastId_bin, 0, $tbits ), + substr( $id_bin, 0, $tbits ), "New ID timestamp ($id_bin) >= prior one ($lastId_bin)." ); if ( $hostbits ) { $this->assertEquals( - substr( $id_bin, 0, -$hostbits ), - substr( $lastId_bin, 0, -$hostbits ), + substr( $id_bin, -$hostbits ), + substr( $lastId_bin, -$hostbits ), "Host ID of ($id_bin) is same as prior one ($lastId_bin)." ); } diff --git a/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php b/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php index 34ffb535..05d07d45 100644 --- a/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php +++ b/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php @@ -4,7 +4,7 @@ * @covers ZipDirectoryReader * NOTE: this test is more like an integration test than a unit test */ -class ZipDirectoryReaderTest extends MediaWikiTestCase { +class ZipDirectoryReaderTest extends PHPUnit_Framework_TestCase { protected $zipDir; protected $entries; |