summaryrefslogtreecommitdiff
path: root/tests/phpunit/includes
diff options
context:
space:
mode:
Diffstat (limited to 'tests/phpunit/includes')
-rw-r--r--tests/phpunit/includes/ArticleTablesTest.php4
-rw-r--r--tests/phpunit/includes/BlockTest.php107
-rw-r--r--tests/phpunit/includes/CdbTest.php2
-rw-r--r--tests/phpunit/includes/DiffHistoryBlobTest.php40
-rw-r--r--tests/phpunit/includes/EditPageTest.php9
-rw-r--r--tests/phpunit/includes/ExtraParserTest.php4
-rw-r--r--tests/phpunit/includes/GlobalFunctions/GlobalTest.php46
-rw-r--r--tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php35
-rw-r--r--tests/phpunit/includes/HtmlTest.php361
-rw-r--r--tests/phpunit/includes/IPTest.php40
-rw-r--r--tests/phpunit/includes/LinksUpdateTest.php154
-rw-r--r--tests/phpunit/includes/LocalisationCacheTest.php31
-rw-r--r--tests/phpunit/includes/MWNamespaceTest.php43
-rw-r--r--tests/phpunit/includes/MessageTest.php2
-rw-r--r--tests/phpunit/includes/PreferencesTest.php75
-rw-r--r--tests/phpunit/includes/RecentChangeTest.php273
-rw-r--r--tests/phpunit/includes/RevisionStorageTest.php408
-rw-r--r--tests/phpunit/includes/SampleTest.php2
-rw-r--r--tests/phpunit/includes/SanitizerTest.php36
-rw-r--r--tests/phpunit/includes/TemplateCategoriesTest.php16
-rw-r--r--tests/phpunit/includes/TestUser.php (renamed from tests/phpunit/includes/api/ApiTestUser.php)3
-rw-r--r--tests/phpunit/includes/TimestampTest.php72
-rw-r--r--tests/phpunit/includes/TitleMethodsTest.php131
-rw-r--r--tests/phpunit/includes/TitleTest.php75
-rw-r--r--tests/phpunit/includes/UserTest.php28
-rw-r--r--tests/phpunit/includes/WebRequestTest.php39
-rw-r--r--tests/phpunit/includes/WikiPageTest.php784
-rw-r--r--tests/phpunit/includes/XmlTest.php55
-rw-r--r--tests/phpunit/includes/ZipDirectoryReaderTest.php2
-rw-r--r--tests/phpunit/includes/api/ApiBlockTest.php51
-rw-r--r--tests/phpunit/includes/api/ApiEditPageTest.php84
-rw-r--r--tests/phpunit/includes/api/ApiOptionsTest.php276
-rw-r--r--tests/phpunit/includes/api/ApiPurgeTest.php1
-rw-r--r--tests/phpunit/includes/api/ApiQueryTest.php1
-rw-r--r--tests/phpunit/includes/api/ApiTest.php1
-rw-r--r--tests/phpunit/includes/api/ApiTestCase.php59
-rw-r--r--tests/phpunit/includes/api/ApiUploadTest.php1
-rw-r--r--tests/phpunit/includes/api/ApiWatchTest.php88
-rw-r--r--tests/phpunit/includes/api/PrefixUniquenessTest.php24
-rw-r--r--tests/phpunit/includes/api/RandomImageGenerator.php2
-rw-r--r--tests/phpunit/includes/api/generateRandomImages.php8
-rw-r--r--tests/phpunit/includes/cache/GenderCacheTest.php101
-rw-r--r--tests/phpunit/includes/cache/ProcessCacheLRUTest.php239
-rw-r--r--tests/phpunit/includes/db/DatabaseSQLTest.php147
-rw-r--r--tests/phpunit/includes/db/DatabaseSqliteTest.php10
-rw-r--r--tests/phpunit/includes/db/ORMRowTest.php234
-rw-r--r--tests/phpunit/includes/db/TestORMRowTest.php174
-rw-r--r--tests/phpunit/includes/debug/MWDebugTest.php7
-rw-r--r--tests/phpunit/includes/filerepo/FileBackendTest.php754
-rw-r--r--tests/phpunit/includes/filerepo/FileRepoTest.php8
-rw-r--r--tests/phpunit/includes/filerepo/StoreBatchTest.php1
-rw-r--r--tests/phpunit/includes/libs/CSSJanusTest.php560
-rw-r--r--tests/phpunit/includes/libs/CSSMinTest.php142
-rw-r--r--tests/phpunit/includes/libs/GenericArrayObjectTest.php245
-rw-r--r--tests/phpunit/includes/libs/JavaScriptMinifierTest.php66
-rw-r--r--tests/phpunit/includes/media/BitmapMetadataHandlerTest.php5
-rw-r--r--tests/phpunit/includes/media/ExifRotationTest.php20
-rw-r--r--tests/phpunit/includes/media/ExifTest.php22
-rw-r--r--tests/phpunit/includes/media/FormatMetadataTest.php2
-rw-r--r--tests/phpunit/includes/media/GIFMetadataExtractorTest.php2
-rw-r--r--tests/phpunit/includes/media/GIFTest.php2
-rw-r--r--tests/phpunit/includes/media/JpegMetadataExtractorTest.php2
-rw-r--r--tests/phpunit/includes/media/JpegTest.php2
-rw-r--r--tests/phpunit/includes/media/PNGMetadataExtractorTest.php2
-rw-r--r--tests/phpunit/includes/media/PNGTest.php2
-rw-r--r--tests/phpunit/includes/media/SVGMetadataExtractorTest.php24
-rw-r--r--tests/phpunit/includes/media/TiffTest.php2
-rw-r--r--tests/phpunit/includes/media/XMPTest.php11
-rw-r--r--tests/phpunit/includes/mobile/DeviceDetectionTest.php40
-rw-r--r--tests/phpunit/includes/parser/MediaWikiParserTest.php2
-rw-r--r--tests/phpunit/includes/parser/NewParserTest.php18
-rw-r--r--tests/phpunit/includes/parser/ParserMethodsTest.php33
-rw-r--r--tests/phpunit/includes/parser/PreprocessorTest.php4
-rw-r--r--tests/phpunit/includes/specials/SpecialSearchTest.php8
-rw-r--r--tests/phpunit/includes/upload/UploadFromUrlTest.php14
-rw-r--r--tests/phpunit/includes/upload/UploadStashTest.php6
-rw-r--r--tests/phpunit/includes/upload/UploadTest.php4
77 files changed, 5899 insertions, 489 deletions
diff --git a/tests/phpunit/includes/ArticleTablesTest.php b/tests/phpunit/includes/ArticleTablesTest.php
index 02571b55..17cee6e8 100644
--- a/tests/phpunit/includes/ArticleTablesTest.php
+++ b/tests/phpunit/includes/ArticleTablesTest.php
@@ -17,14 +17,14 @@ class ArticleTablesTest extends MediaWikiLangTestCase {
$wgLang = Language::factory( 'fr' );
$status = $page->doEdit( '{{:{{int:history}}}}', 'Test code for bug 14404', 0, false, $user );
- $templates1 = $page->getUsedTemplates();
+ $templates1 = $title->getTemplateLinksFrom();
$wgLang = Language::factory( 'de' );
$page->mPreparedEdit = false; // In order to force the rerendering of the same wikitext
// We need an edit, a purge is not enough to regenerate the tables
$status = $page->doEdit( '{{:{{int:history}}}}', 'Test code for bug 14404', EDIT_UPDATE, false, $user );
- $templates2 = $page->getUsedTemplates();
+ $templates2 = $title->getTemplateLinksFrom();
$this->assertEquals( $templates1, $templates2 );
$this->assertEquals( $templates1[0]->getFullText(), 'Historial' );
diff --git a/tests/phpunit/includes/BlockTest.php b/tests/phpunit/includes/BlockTest.php
index 749f40b4..0c95b8d1 100644
--- a/tests/phpunit/includes/BlockTest.php
+++ b/tests/phpunit/includes/BlockTest.php
@@ -67,7 +67,7 @@ class BlockTest extends MediaWikiLangTestCase {
// $this->dumpBlocks();
$this->assertTrue( $this->block->equals( Block::newFromTarget('UTBlockee') ), "newFromTarget() returns the same block as the one that was made");
-
+
$this->assertTrue( $this->block->equals( Block::newFromID( $this->blockId ) ), "newFromID() returns the same block as the one that was made");
}
@@ -122,4 +122,109 @@ class BlockTest extends MediaWikiLangTestCase {
array( false )
);
}
+
+ function testBlockedUserCanNotCreateAccount() {
+ $username = 'BlockedUserToCreateAccountWith';
+ $u = User::newFromName( $username );
+ $u->setPassword( 'NotRandomPass' );
+ $u->addToDatabase();
+ unset( $u );
+
+
+ // Sanity check
+ $this->assertNull(
+ Block::newFromTarget( $username ),
+ "$username should not be blocked"
+ );
+
+ // Reload user
+ $u = User::newFromName( $username );
+ $this->assertFalse(
+ $u->isBlockedFromCreateAccount(),
+ "Our sandbox user should be able to create account before being blocked"
+ );
+
+ // Foreign perspective (blockee not on current wiki)...
+ $block = new Block(
+ /* $address */ $username,
+ /* $user */ 14146,
+ /* $by */ 0,
+ /* $reason */ 'crosswiki block...',
+ /* $timestamp */ wfTimestampNow(),
+ /* $auto */ false,
+ /* $expiry */ $this->db->getInfinity(),
+ /* anonOnly */ false,
+ /* $createAccount */ true,
+ /* $enableAutoblock */ true,
+ /* $hideName (ipb_deleted) */ true,
+ /* $blockEmail */ true,
+ /* $allowUsertalk */ false,
+ /* $byName */ 'MetaWikiUser'
+ );
+ $block->insert();
+
+ // Reload block from DB
+ $userBlock = Block::newFromTarget( $username );
+ $this->assertTrue(
+ (bool) $block->prevents( 'createaccount' ),
+ "Block object in DB should prevents 'createaccount'"
+ );
+
+ $this->assertInstanceOf(
+ 'Block',
+ $userBlock,
+ "'$username' block block object should be existent"
+ );
+
+ // Reload user
+ $u = User::newFromName( $username );
+ $this->assertTrue(
+ (bool) $u->isBlockedFromCreateAccount(),
+ "Our sandbox user '$username' should NOT be able to create account"
+ );
+ }
+
+ function testCrappyCrossWikiBlocks() {
+ // Delete the last round's block if it's still there
+ $oldBlock = Block::newFromTarget( 'UserOnForeignWiki' );
+ if ( $oldBlock ) {
+ // An old block will prevent our new one from saving.
+ $oldBlock->delete();
+ }
+
+ // Foreign perspective (blockee not on current wiki)...
+ $block = new Block(
+ /* $address */ 'UserOnForeignWiki',
+ /* $user */ 14146,
+ /* $by */ 0,
+ /* $reason */ 'crosswiki block...',
+ /* $timestamp */ wfTimestampNow(),
+ /* $auto */ false,
+ /* $expiry */ $this->db->getInfinity(),
+ /* anonOnly */ false,
+ /* $createAccount */ true,
+ /* $enableAutoblock */ true,
+ /* $hideName (ipb_deleted) */ true,
+ /* $blockEmail */ true,
+ /* $allowUsertalk */ false,
+ /* $byName */ 'MetaWikiUser'
+ );
+
+ $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'] );
+ $this->assertEquals( 'UserOnForeignWiki', $block->getTarget()->getName(), 'Correct blockee name' );
+ $this->assertEquals( '14146', $block->getTarget()->getId(), 'Correct blockee id' );
+ $this->assertEquals( 'MetaWikiUser', $block->getBlocker(), 'Correct blocker name' );
+ $this->assertEquals( 'MetaWikiUser', $block->getByName(), 'Correct blocker name' );
+ $this->assertEquals( 0, $block->getBy(), 'Correct blocker id' );
+ }
}
diff --git a/tests/phpunit/includes/CdbTest.php b/tests/phpunit/includes/CdbTest.php
index 6c3e6664..b5418dd7 100644
--- a/tests/phpunit/includes/CdbTest.php
+++ b/tests/phpunit/includes/CdbTest.php
@@ -8,7 +8,7 @@ class CdbTest extends MediaWikiTestCase {
public function setUp() {
if ( !CdbReader::haveExtension() ) {
- $this->markTestIncomplete( 'This test requires native CDB support to be present.' );
+ $this->markTestSkipped( 'Native CDB support is not available' );
}
}
diff --git a/tests/phpunit/includes/DiffHistoryBlobTest.php b/tests/phpunit/includes/DiffHistoryBlobTest.php
new file mode 100644
index 00000000..cdb6ed2f
--- /dev/null
+++ b/tests/phpunit/includes/DiffHistoryBlobTest.php
@@ -0,0 +1,40 @@
+<?php
+
+class DiffHistoryBlobTest extends MediaWikiTestCase {
+ function setUp() {
+ if ( !extension_loaded( 'xdiff' ) ) {
+ $this->markTestSkipped( 'The xdiff extension is not available' );
+ return;
+ }
+ if ( !function_exists( 'xdiff_string_rabdiff' ) ) {
+ $this->markTestSkipped( 'The version of xdiff extension is lower than 1.5.0' );
+ return;
+ }
+ if ( !extension_loaded( 'hash' ) && !extension_loaded( 'mhash' ) ) {
+ $this->markTestSkipped( 'Neither the hash nor mhash extension is available' );
+ return;
+ }
+ }
+
+ /**
+ * Test for DiffHistoryBlob::xdiffAdler32()
+ * @dataProvider provideXdiffAdler32
+ */
+ function testXdiffAdler32( $input ) {
+ $xdiffHash = substr( xdiff_string_rabdiff( $input, '' ), 0, 4 );
+ $dhb = new DiffHistoryBlob;
+ $myHash = $dhb->xdiffAdler32( $input );
+ $this->assertSame( bin2hex( $xdiffHash ), bin2hex( $myHash ),
+ "Hash of " . addcslashes( $input, "\0..\37!@\@\177..\377" ) );
+ }
+
+ function provideXdiffAdler32() {
+ return array(
+ array( '', 'Empty string' ),
+ array( "\0", 'Null' ),
+ array( "\0\0\0", "Several nulls" ),
+ array( "Hello", "An ASCII string" ),
+ array( str_repeat( "x", 6000 ), "A string larger than xdiff's NMAX (5552)" )
+ );
+ }
+}
diff --git a/tests/phpunit/includes/EditPageTest.php b/tests/phpunit/includes/EditPageTest.php
index e98e9707..8ecfd7e5 100644
--- a/tests/phpunit/includes/EditPageTest.php
+++ b/tests/phpunit/includes/EditPageTest.php
@@ -1,5 +1,8 @@
<?php
+/**
+ * @group Editing
+ */
class EditPageTest extends MediaWikiTestCase {
/**
@@ -27,7 +30,11 @@ class EditPageTest extends MediaWikiTestCase {
array(
"== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
"Section"
- )
+ ),
+ array(
+ "== Section== \t\r\n followed by whitespace (bug 35051)",
+ 'Section',
+ ),
);
}
}
diff --git a/tests/phpunit/includes/ExtraParserTest.php b/tests/phpunit/includes/ExtraParserTest.php
index a9088cb2..903a6d25 100644
--- a/tests/phpunit/includes/ExtraParserTest.php
+++ b/tests/phpunit/includes/ExtraParserTest.php
@@ -21,6 +21,8 @@ class ExtraParserTest extends MediaWikiTestCase {
$this->options = new ParserOptions;
$this->options->setTemplateCallback( array( __CLASS__, 'statelessFetchTemplate' ) );
$this->parser = new Parser;
+
+ MagicWord::clearCache();
}
// Bug 8689 - Long numeric lines kill the parser
@@ -146,7 +148,7 @@ class ExtraParserTest extends MediaWikiTestCase {
*/
function testTrackingCategory() {
$title = Title::newFromText( __FUNCTION__ );
- $catName = wfMsgForContent( 'broken-file-category' );
+ $catName = wfMessage( 'broken-file-category' )->inContentLanguage()->text();
$cat = Title::makeTitleSafe( NS_CATEGORY, $catName );
$expected = array( $cat->getDBkey() );
$parserOutput = $this->parser->parse( "[[file:nonexistent]]" , $title, $this->options );
diff --git a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php
index 3cb42f12..9097d301 100644
--- a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php
+++ b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php
@@ -55,6 +55,12 @@ class GlobalTest extends MediaWikiTestCase {
wfUrlencode( "\xE7\x89\xB9\xE5\x88\xA5:Contributions/Foobar" ) );
}
+ function testExpandIRI() {
+ $this->assertEquals(
+ "https://te.wikibooks.org/wiki/ఉబుంటు_వాడుకరి_మార్గదర్శని",
+ wfExpandIRI( "https://te.wikibooks.org/wiki/%E0%B0%89%E0%B0%AC%E0%B1%81%E0%B0%82%E0%B0%9F%E0%B1%81_%E0%B0%B5%E0%B0%BE%E0%B0%A1%E0%B1%81%E0%B0%95%E0%B0%B0%E0%B0%BF_%E0%B0%AE%E0%B0%BE%E0%B0%B0%E0%B1%8D%E0%B0%97%E0%B0%A6%E0%B0%B0%E0%B1%8D%E0%B0%B6%E0%B0%A8%E0%B0%BF" ) );
+ }
+
function testReadOnlyEmpty() {
global $wgReadOnly;
$wgReadOnly = null;
@@ -305,7 +311,7 @@ class GlobalTest extends MediaWikiTestCase {
function testDebugFunctionTest() {
- global $wgDebugLogFile, $wgOut, $wgShowDebug, $wgDebugTimestamps;
+ global $wgDebugLogFile, $wgDebugTimestamps;
$old_log_file = $wgDebugLogFile;
$wgDebugLogFile = tempnam( wfTempDir(), 'mw-' );
@@ -327,33 +333,7 @@ class GlobalTest extends MediaWikiTestCase {
wfDebug( "\00305This has böth UTF and control chars\003" );
$this->assertEquals( " 05This has böth UTF and control chars ", file_get_contents( $wgDebugLogFile ) );
unlink( $wgDebugLogFile );
-
-
-
- $old_wgOut = $wgOut;
- $old_wgShowDebug = $wgShowDebug;
-
- $wgOut = new MockOutputPage;
-
- $wgShowDebug = true;
-
- $message = "\00305This has böth UTF and control chars\003";
-
- wfDebug( $message );
-
- if( $wgOut->message == "JAJA is a stupid error message. Anyway, here's your message: $message" ) {
- $this->assertTrue( true, 'MockOutputPage called, set the proper message.' );
- }
- else {
- $this->assertTrue( false, 'MockOutputPage was not called.' );
- }
-
- $wgOut = $old_wgOut;
- $wgShowDebug = $old_wgShowDebug;
- unlink( $wgDebugLogFile );
-
-
-
+
wfDebugMem();
$this->assertGreaterThan( 5000, preg_replace( '/\D/', '', file_get_contents( $wgDebugLogFile ) ) );
unlink( $wgDebugLogFile );
@@ -616,13 +596,3 @@ class GlobalTest extends MediaWikiTestCase {
/* TODO: many more! */
}
-
-class MockOutputPage {
-
- public $message;
-
- function debug( $message ) {
- $this->message = "JAJA is a stupid error message. Anyway, here's your message: $message";
- }
-}
-
diff --git a/tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php b/tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php
new file mode 100644
index 00000000..4c4c4c04
--- /dev/null
+++ b/tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php
@@ -0,0 +1,35 @@
+<?php
+
+class wfGetCaller extends MediaWikiTestCase {
+
+ function testZero() {
+ $this->assertEquals( __METHOD__, wfGetCaller( 1 ) );
+ }
+
+ function callerOne() {
+ return wfGetCaller();
+ }
+
+ function testOne() {
+ $this->assertEquals( "wfGetCaller::testOne", self::callerOne() );
+ }
+
+ function intermediateFunction( $level = 2, $n = 0 ) {
+ if ( $n > 0 )
+ return self::intermediateFunction( $level, $n - 1 );
+ return wfGetCaller( $level );
+ }
+
+ function testTwo() {
+ $this->assertEquals( "wfGetCaller::testTwo", self::intermediateFunction() );
+ }
+
+ function testN() {
+ $this->assertEquals( "wfGetCaller::testN", self::intermediateFunction( 2, 0 ) );
+ $this->assertEquals( "wfGetCaller::intermediateFunction", self::intermediateFunction( 1, 0 ) );
+
+ for ($i=0; $i < 10; $i++)
+ $this->assertEquals( "wfGetCaller::intermediateFunction", self::intermediateFunction( $i + 1, $i ) );
+ }
+}
+
diff --git a/tests/phpunit/includes/HtmlTest.php b/tests/phpunit/includes/HtmlTest.php
index 67b60d32..a18f7922 100644
--- a/tests/phpunit/includes/HtmlTest.php
+++ b/tests/phpunit/includes/HtmlTest.php
@@ -6,15 +6,18 @@ class HtmlTest extends MediaWikiTestCase {
private static $oldContLang;
private static $oldLanguageCode;
private static $oldNamespaces;
+ private static $oldHTML5;
public function setUp() {
- global $wgLang, $wgContLang, $wgLanguageCode;
-
+ global $wgLang, $wgContLang, $wgLanguageCode, $wgHTML5;
+
+ // Save globals
self::$oldLang = $wgLang;
self::$oldContLang = $wgContLang;
self::$oldNamespaces = $wgContLang->getNamespaces();
self::$oldLanguageCode = $wgLanguageCode;
-
+ self::$oldHTML5 = $wgHTML5;
+
$wgLanguageCode = 'en';
$wgContLang = $wgLang = Language::factory( $wgLanguageCode );
@@ -36,18 +39,41 @@ class HtmlTest extends MediaWikiTestCase {
9 => 'MediaWiki_talk',
10 => 'Template',
11 => 'Template_talk',
+ 14 => 'Category',
+ 15 => 'Category_talk',
100 => 'Custom',
101 => 'Custom_talk',
) );
}
-
+
public function tearDown() {
- global $wgLang, $wgContLang, $wgLanguageCode;
+ global $wgLang, $wgContLang, $wgLanguageCode, $wgHTML5;
+ // Restore globals
$wgContLang->setNamespaces( self::$oldNamespaces );
$wgLang = self::$oldLang;
$wgContLang = self::$oldContLang;
$wgLanguageCode = self::$oldLanguageCode;
+ $wgHTML5 = self::$oldHTML5;
+ }
+
+ /**
+ * Wrapper to easily set $wgHTML5 = true.
+ * Original value will be restored after test completion.
+ * @todo Move to MediaWikiTestCase
+ */
+ public function enableHTML5() {
+ global $wgHTML5;
+ $wgHTML5 = true;
+ }
+ /**
+ * Wrapper to easily set $wgHTML5 = false
+ * Original value will be restored after test completion.
+ * @todo Move to MediaWikiTestCase
+ */
+ public function disableHTML5() {
+ global $wgHTML5;
+ $wgHTML5 = false;
}
public function testExpandAttributesSkipsNullAndFalse() {
@@ -213,7 +239,7 @@ class HtmlTest extends MediaWikiTestCase {
function testNamespaceSelector() {
$this->assertEquals(
- '<select id="namespace" name="namespace">' . "\n" .
+ '<select>' . "\n" .
'<option value="0">(Main)</option>' . "\n" .
'<option value="1">Talk</option>' . "\n" .
'<option value="2">User</option>' . "\n" .
@@ -226,12 +252,15 @@ class HtmlTest extends MediaWikiTestCase {
'<option value="9">MediaWiki talk</option>' . "\n" .
'<option value="10">Template</option>' . "\n" .
'<option value="11">Template talk</option>' . "\n" .
+'<option value="14">Category</option>' . "\n" .
+'<option value="15">Category talk</option>' . "\n" .
'<option value="100">Custom</option>' . "\n" .
'<option value="101">Custom talk</option>' . "\n" .
'</select>',
Html::namespaceSelector(),
'Basic namespace selector without custom options'
);
+
$this->assertEquals(
'<label for="mw-test-namespace">Select a namespace:</label>&#160;' .
'<select id="mw-test-namespace" name="wpNamespace">' . "\n" .
@@ -248,6 +277,8 @@ class HtmlTest extends MediaWikiTestCase {
'<option value="9">MediaWiki talk</option>' . "\n" .
'<option value="10">Template</option>' . "\n" .
'<option value="11">Template talk</option>' . "\n" .
+'<option value="14">Category</option>' . "\n" .
+'<option value="15">Category talk</option>' . "\n" .
'<option value="100">Custom</option>' . "\n" .
'<option value="101">Custom talk</option>' . "\n" .
'</select>',
@@ -257,77 +288,281 @@ class HtmlTest extends MediaWikiTestCase {
),
'Basic namespace selector with custom values'
);
- }
- function testNamespaceSelectorIdAndNameDefaultsAttributes() {
-
- $this->assertNsSelectorIdAndName(
- 'namespace', 'namespace',
- Html::namespaceSelector( array(), array(
- # neither 'id' nor 'name' key given
- )),
- "Neither 'id' nor 'name' key given"
+ $this->assertEquals(
+ '<label>Select a namespace:</label>&#160;' .
+'<select>' . "\n" .
+'<option value="0">(Main)</option>' . "\n" .
+'<option value="1">Talk</option>' . "\n" .
+'<option value="2">User</option>' . "\n" .
+'<option value="3">User talk</option>' . "\n" .
+'<option value="4">MyWiki</option>' . "\n" .
+'<option value="5">MyWiki Talk</option>' . "\n" .
+'<option value="6">File</option>' . "\n" .
+'<option value="7">File talk</option>' . "\n" .
+'<option value="8">MediaWiki</option>' . "\n" .
+'<option value="9">MediaWiki talk</option>' . "\n" .
+'<option value="10">Template</option>' . "\n" .
+'<option value="11">Template talk</option>' . "\n" .
+'<option value="14">Category</option>' . "\n" .
+'<option value="15">Category talk</option>' . "\n" .
+'<option value="100">Custom</option>' . "\n" .
+'<option value="101">Custom talk</option>' . "\n" .
+'</select>',
+ Html::namespaceSelector(
+ array( 'label' => 'Select a namespace:' )
+ ),
+ 'Basic namespace selector with a custom label but no id attribtue for the <select>'
);
+ }
- $this->assertNsSelectorIdAndName(
- 'namespace', 'select_name',
- Html::namespaceSelector( array(), array(
- 'name' => 'select_name',
- # no 'id' key given
- )),
- "No 'id' key given, 'name' given"
+ function testCanFilterOutNamespaces() {
+ $this->assertEquals(
+'<select>' . "\n" .
+'<option value="2">User</option>' . "\n" .
+'<option value="4">MyWiki</option>' . "\n" .
+'<option value="5">MyWiki Talk</option>' . "\n" .
+'<option value="6">File</option>' . "\n" .
+'<option value="7">File talk</option>' . "\n" .
+'<option value="8">MediaWiki</option>' . "\n" .
+'<option value="9">MediaWiki talk</option>' . "\n" .
+'<option value="10">Template</option>' . "\n" .
+'<option value="11">Template talk</option>' . "\n" .
+'<option value="14">Category</option>' . "\n" .
+'<option value="15">Category talk</option>' . "\n" .
+'</select>',
+ Html::namespaceSelector(
+ array( 'exclude' => array( 0, 1, 3, 100, 101 ) )
+ ),
+ 'Namespace selector namespace filtering.'
);
+ }
- $this->assertNsSelectorIdAndName(
- 'select_id', 'namespace',
- Html::namespaceSelector( array(), array(
- 'id' => 'select_id',
- # no 'name' key given
- )),
- "'id' given, no 'name' key given"
+ function testCanDisableANamespaces() {
+ $this->assertEquals(
+'<select>' . "\n" .
+'<option disabled="" value="0">(Main)</option>' . "\n" .
+'<option disabled="" value="1">Talk</option>' . "\n" .
+'<option disabled="" value="2">User</option>' . "\n" .
+'<option disabled="" value="3">User talk</option>' . "\n" .
+'<option disabled="" value="4">MyWiki</option>' . "\n" .
+'<option value="5">MyWiki Talk</option>' . "\n" .
+'<option value="6">File</option>' . "\n" .
+'<option value="7">File talk</option>' . "\n" .
+'<option value="8">MediaWiki</option>' . "\n" .
+'<option value="9">MediaWiki talk</option>' . "\n" .
+'<option value="10">Template</option>' . "\n" .
+'<option value="11">Template talk</option>' . "\n" .
+'<option value="14">Category</option>' . "\n" .
+'<option value="15">Category talk</option>' . "\n" .
+'<option value="100">Custom</option>' . "\n" .
+'<option value="101">Custom talk</option>' . "\n" .
+'</select>',
+ Html::namespaceSelector( array(
+ 'disable' => array( 0, 1, 2, 3, 4 )
+ ) ),
+ 'Namespace selector namespace disabling'
);
+ }
- $this->assertNsSelectorIdAndName(
- 'select_id', 'select_name',
- Html::namespaceSelector( array(), array(
- 'id' => 'select_id',
- 'name' => 'select_name',
- )),
- "Both 'id' and 'name' given"
+ /**
+ * @dataProvider providesHtml5InputTypes
+ */
+ function testHtmlElementAcceptsNewHtml5TypesInHtml5Mode( $HTML5InputType ) {
+ $this->enableHTML5();
+ $this->assertEquals(
+ '<input type="' . $HTML5InputType . '" />',
+ HTML::element( 'input', array( 'type' => $HTML5InputType ) ),
+ 'In HTML5, HTML::element() should accept type="' . $HTML5InputType . '"'
);
}
/**
- * Helper to verify <select> attributes generated by Html::namespaceSelector()
- * This helper expect the Html method to use 'namespace' as a default value for
- * both 'id' and 'name' attributes.
- *
- * @param String $expectedId <select> id attribute value
- * @param String $expectedName <select> name attribute value
- * @param String $html Output of a call to Html::namespaceSelector()
- * @param String $msg Optional message (default: '')
- */
- function assertNsSelectorIdAndName( $expectedId, $expectedName, $html, $msg = '' ) {
- $actualId = 'namespace';
- if( 1 === preg_match( '/id="(.+?)"/', $html, $m ) ) {
- $actualId = $m[1];
+ * List of input element types values introduced by HTML5
+ * Full list at http://www.w3.org/TR/html-markup/input.html
+ */
+ function providesHtml5InputTypes() {
+ $types = array(
+ 'datetime',
+ 'datetime-local',
+ 'date',
+ 'month',
+ 'time',
+ 'week',
+ 'number',
+ 'range',
+ 'email',
+ 'url',
+ 'search',
+ 'tel',
+ 'color',
+ );
+ $cases = array();
+ foreach( $types as $type ) {
+ $cases[] = array( $type );
}
+ return $cases;
+ }
- $actualName = 'namespace';
- if( 1 === preg_match( '/name="(.+?)"/', $html, $m ) ) {
- $actualName = $m[1];
- }
- $this->assertEquals(
- array( #expected
- 'id' => $expectedId,
- 'name' => $expectedName,
- ),
- array( #actual
- 'id' => $actualId,
- 'name' => $actualName,
- ),
- 'Html::namespaceSelector() got wrong id and/or name attribute(s). ' . $msg
+ /**
+ * Test out Html::element drops default value
+ * @cover Html::dropDefaults
+ * @dataProvider provideElementsWithAttributesHavingDefaultValues
+ */
+ function testDropDefaults( $expected, $element, $message = '' ) {
+ $this->enableHTML5();
+ $this->assertEquals( $expected, $element, $message );
+ }
+
+ function provideElementsWithAttributesHavingDefaultValues() {
+ # Use cases in a concise format:
+ # <expected>, <element name>, <array of attributes> [, <message>]
+ # Will be mapped to Html::element()
+ $cases = array();
+
+ ### Generic cases, match $attribDefault static array
+ $cases[] = array( '<area />',
+ 'area', array( 'shape' => 'rect' )
+ );
+
+ $cases[] = array( '<button></button>',
+ 'button', array( 'formaction' => 'GET' )
+ );
+ $cases[] = array( '<button></button>',
+ 'button', array( 'formenctype' => 'application/x-www-form-urlencoded' )
+ );
+ $cases[] = array( '<button></button>',
+ 'button', array( 'type' => 'submit' )
+ );
+
+ $cases[] = array( '<canvas></canvas>',
+ 'canvas', array( 'height' => '150' )
+ );
+ $cases[] = array( '<canvas></canvas>',
+ 'canvas', array( 'width' => '300' )
+ );
+ # Also check with numeric values
+ $cases[] = array( '<canvas></canvas>',
+ 'canvas', array( 'height' => 150 )
+ );
+ $cases[] = array( '<canvas></canvas>',
+ 'canvas', array( 'width' => 300 )
+ );
+
+ $cases[] = array( '<command />',
+ 'command', array( 'type' => 'command' )
+ );
+
+ $cases[] = array( '<form></form>',
+ 'form', array( 'action' => 'GET' )
+ );
+ $cases[] = array( '<form></form>',
+ 'form', array( 'autocomplete' => 'on' )
+ );
+ $cases[] = array( '<form></form>',
+ 'form', array( 'enctype' => 'application/x-www-form-urlencoded' )
+ );
+
+ $cases[] = array( '<input />',
+ 'input', array( 'formaction' => 'GET' )
+ );
+ $cases[] = array( '<input />',
+ 'input', array( 'type' => 'text' )
+ );
+
+ $cases[] = array( '<keygen />',
+ 'keygen', array( 'keytype' => 'rsa' )
);
+
+ $cases[] = array( '<link />',
+ 'link', array( 'media' => 'all' )
+ );
+
+ $cases[] = array( '<menu></menu>',
+ 'menu', array( 'type' => 'list' )
+ );
+
+ $cases[] = array( '<script></script>',
+ 'script', array( 'type' => 'text/javascript' )
+ );
+
+ $cases[] = array( '<style></style>',
+ 'style', array( 'media' => 'all' )
+ );
+ $cases[] = array( '<style></style>',
+ 'style', array( 'type' => 'text/css' )
+ );
+
+ $cases[] = array( '<textarea></textarea>',
+ 'textarea', array( 'wrap' => 'soft' )
+ );
+
+ ### SPECIFIC CASES
+
+ # <link type="text/css" />
+ $cases[] = array( '<link />',
+ 'link', array( 'type' => 'text/css' )
+ );
+
+ # <input /> specific handling
+ $cases[] = array( '<input type="checkbox" />',
+ 'input', array( 'type' => 'checkbox', 'value' => 'on' ),
+ 'Default value "on" is stripped of checkboxes',
+ );
+ $cases[] = array( '<input type="radio" />',
+ 'input', array( 'type' => 'radio', 'value' => 'on' ),
+ 'Default value "on" is stripped of radio buttons',
+ );
+ $cases[] = array( '<input type="submit" value="Submit" />',
+ 'input', array( 'type' => 'submit', 'value' => 'Submit' ),
+ 'Default value "Submit" is kept on submit buttons (for possible l10n issues)',
+ );
+ $cases[] = array( '<input type="color" />',
+ 'input', array( 'type' => 'color', 'value' => '' ),
+ );
+ $cases[] = array( '<input type="range" />',
+ 'input', array( 'type' => 'range', 'value' => '' ),
+ );
+
+ # <select /> specifc handling
+ $cases[] = array( '<select multiple=""></select>',
+ 'select', array( 'size' => '4', 'multiple' => true ),
+ );
+ # .. with numeric value
+ $cases[] = array( '<select multiple=""></select>',
+ 'select', array( 'size' => 4, 'multiple' => true ),
+ );
+ $cases[] = array( '<select></select>',
+ 'select', array( 'size' => '1', 'multiple' => false ),
+ );
+ # .. with numeric value
+ $cases[] = array( '<select></select>',
+ 'select', array( 'size' => 1, 'multiple' => false ),
+ );
+
+ # Passing an array as value
+ $cases[] = array( '<a class="css-class-one css-class-two"></a>',
+ 'a', array( 'class' => array( 'css-class-one', 'css-class-two' ) ),
+ "dropDefaults accepts values given as an array"
+ );
+
+ # FIXME: doDropDefault should remove defaults given in an array
+ # Expected should be '<a></a>'
+ $cases[] = array( '<a class=""></a>',
+ 'a', array( 'class' => array( '', '' ) ),
+ "dropDefaults accepts values given as an array"
+ );
+
+
+ # Craft the Html elements
+ $ret = array();
+ foreach( $cases as $case ) {
+ $ret[] = array(
+ $case[0],
+ Html::element( $case[1], $case[2] )
+ );
+ }
+ return $ret;
}
}
diff --git a/tests/phpunit/includes/IPTest.php b/tests/phpunit/includes/IPTest.php
index 4397b879..f50b2fe9 100644
--- a/tests/phpunit/includes/IPTest.php
+++ b/tests/phpunit/includes/IPTest.php
@@ -1,6 +1,7 @@
<?php
/**
* Tests for IP validity functions. Ported from /t/inc/IP.t by avar.
+ * @group IP
*/
class IPTest extends MediaWikiTestCase {
@@ -14,9 +15,9 @@ class IPTest extends MediaWikiTestCase {
$this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
$this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
$this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
- $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1'), 'IPv6 with a double :: occurence' );
- $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::'), 'IPv6 with a double :: occurence, last at end' );
- $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1'), 'IPv6 with a double :: occurence, firt at beginning' );
+ $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1'), 'IPv6 with a double :: occurrence' );
+ $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::'), 'IPv6 with a double :: occurrence, last at end' );
+ $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1'), 'IPv6 with a double :: occurrence, firt at beginning' );
$this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
$this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
$this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
@@ -505,4 +506,37 @@ class IPTest extends MediaWikiTestCase {
array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
);
}
+
+ /**
+ * Test for IP::prettifyIP()
+ * @dataProvider provideIPsToPrettify
+ */
+ function testPrettifyIP( $ip, $prettified ) {
+ $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
+ }
+
+ /**
+ * Provider for IP::testPrettifyIP()
+ */
+ function provideIPsToPrettify() {
+ return array(
+ array( '0:0:0:0:0:0:0:0', '::' ),
+ array( '0:0:0::0:0:0', '::' ),
+ array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
+ array( '0:0::f', '::f' ),
+ array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
+ array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
+ array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
+ array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
+ array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
+ array( '0:0:0:0:0:0:0:0/16', '::/16' ),
+ array( '0:0:0::0:0:0/64', '::/64' ),
+ array( '0:0::f/52', '::f/52' ),
+ array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
+ array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
+ array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
+ array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
+ array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
+ );
+ }
}
diff --git a/tests/phpunit/includes/LinksUpdateTest.php b/tests/phpunit/includes/LinksUpdateTest.php
new file mode 100644
index 00000000..49462001
--- /dev/null
+++ b/tests/phpunit/includes/LinksUpdateTest.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ *
+ * @group Database
+ * ^--- make sure temporary tables are used.
+ */
+class LinksUpdateTest extends MediaWikiTestCase {
+
+ function __construct( $name = null, array $data = array(), $dataName = '' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->tablesUsed = array_merge ( $this->tablesUsed,
+ array( 'interwiki',
+
+ 'page_props',
+ 'pagelinks',
+ 'categorylinks',
+ 'langlinks',
+ 'externallinks',
+ 'imagelinks',
+ 'templatelinks',
+ 'iwlinks' ) );
+ }
+
+ function setUp() {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->replace( 'interwiki',
+ array('iw_prefix'),
+ array( 'iw_prefix' => 'linksupdatetest',
+ 'iw_url' => 'http://testing.com/wiki/$1',
+ 'iw_api' => 'http://testing.com/w/api.php',
+ 'iw_local' => 0,
+ 'iw_trans' => 0,
+ 'iw_wikiid' => 'linksupdatetest',
+ ) );
+ }
+
+ protected function makeTitleAndParserOutput( $name, $id ) {
+ $t = Title::newFromText( $name );
+ $t->mArticleID = $id; # XXX: this is fugly
+
+ $po = new ParserOutput();
+ $po->setTitleText( $t->getPrefixedText() );
+
+ return array( $t, $po );
+ }
+
+ public function testUpdate_pagelinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->addLink( Title::newFromText( "Foo" ) );
+ $po->addLink( Title::newFromText( "Special:Foo" ) ); // special namespace should be ignored
+ $po->addLink( Title::newFromText( "linksupdatetest:Foo" ) ); // interwiki link should be ignored
+ $po->addLink( Title::newFromText( "#Foo" ) ); // hash link should be ignored
+
+ $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
+ array( NS_MAIN, 'Foo' ),
+ ) );
+
+ $po = new ParserOutput();
+ $po->setTitleText( $t->getPrefixedText() );
+
+ $po->addLink( Title::newFromText( "Bar" ) );
+
+ $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
+ array( NS_MAIN, 'Bar' ),
+ ) );
+ }
+
+ public function testUpdate_externallinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->addExternalLink( "http://testing.com/wiki/Foo" );
+
+ $this->assertLinksUpdate( $t, $po, 'externallinks', 'el_to, el_index', 'el_from = 111', array(
+ array( 'http://testing.com/wiki/Foo', 'http://com.testing./wiki/Foo' ),
+ ) );
+ }
+
+ public function testUpdate_categorylinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->addCategory( "Foo", "FOO" );
+
+ $this->assertLinksUpdate( $t, $po, 'categorylinks', 'cl_to, cl_sortkey', 'cl_from = 111', array(
+ array( 'Foo', "FOO\nTESTING" ),
+ ) );
+ }
+
+ public function testUpdate_iwlinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $target = Title::makeTitleSafe( NS_MAIN, "Foo", '', 'linksupdatetest' );
+ $po->addInterwikiLink( $target );
+
+ $this->assertLinksUpdate( $t, $po, 'iwlinks', 'iwl_prefix, iwl_title', 'iwl_from = 111', array(
+ array( 'linksupdatetest', 'Foo' ),
+ ) );
+ }
+
+ public function testUpdate_templatelinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->addTemplate( Title::newFromText( "Template:Foo" ), 23, 42 );
+
+ $this->assertLinksUpdate( $t, $po, 'templatelinks', 'tl_namespace, tl_title', 'tl_from = 111', array(
+ array( NS_TEMPLATE, 'Foo' ),
+ ) );
+ }
+
+ public function testUpdate_imagelinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->addImage( "Foo.png" );
+
+
+ $this->assertLinksUpdate( $t, $po, 'imagelinks', 'il_to', 'il_from = 111', array(
+ array( 'Foo.png' ),
+ ) );
+ }
+
+ public function testUpdate_langlinks() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->addLanguageLink( Title::newFromText( "en:Foo" ) );
+
+
+ $this->assertLinksUpdate( $t, $po, 'langlinks', 'll_lang, ll_title', 'll_from = 111', array(
+ array( 'En', 'Foo' ),
+ ) );
+ }
+
+ public function testUpdate_page_props() {
+ list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
+
+ $po->setProperty( "foo", "bar" );
+
+ $this->assertLinksUpdate( $t, $po, 'page_props', 'pp_propname, pp_value', 'pp_page = 111', array(
+ array( 'foo', 'bar' ),
+ ) );
+ }
+
+ #@todo: test recursive, too!
+
+ protected function assertLinksUpdate( Title $title, ParserOutput $parserOutput, $table, $fields, $condition, Array $expectedRows ) {
+ $update = new LinksUpdate( $title, $parserOutput );
+
+ $update->doUpdate();
+
+ $this->assertSelect( $table, $fields, $condition, $expectedRows );
+ }
+}
+
diff --git a/tests/phpunit/includes/LocalisationCacheTest.php b/tests/phpunit/includes/LocalisationCacheTest.php
new file mode 100644
index 00000000..356db87c
--- /dev/null
+++ b/tests/phpunit/includes/LocalisationCacheTest.php
@@ -0,0 +1,31 @@
+<?php
+
+class LocalisationCacheTest extends MediaWikiTestCase {
+ public function testPuralRulesFallback() {
+ $cache = Language::getLocalisationCache();
+
+ $this->assertEquals(
+ $cache->getItem( 'ru', 'pluralRules' ),
+ $cache->getItem( 'os', 'pluralRules' ),
+ 'os plural rules (undefined) fallback to ru (defined)'
+ );
+
+ $this->assertEquals(
+ $cache->getItem( 'ru', 'compiledPluralRules' ),
+ $cache->getItem( 'os', 'compiledPluralRules' ),
+ 'os compiled plural rules (undefined) fallback to ru (defined)'
+ );
+
+ $this->assertNotEquals(
+ $cache->getItem( 'ksh', 'pluralRules' ),
+ $cache->getItem( 'de', 'pluralRules' ),
+ 'ksh plural rules (defined) dont fallback to de (defined)'
+ );
+
+ $this->assertNotEquals(
+ $cache->getItem( 'ksh', 'compiledPluralRules' ),
+ $cache->getItem( 'de', 'compiledPluralRules' ),
+ 'ksh compiled plural rules (defined) dont fallback to de (defined)'
+ );
+ }
+}
diff --git a/tests/phpunit/includes/MWNamespaceTest.php b/tests/phpunit/includes/MWNamespaceTest.php
index 6b231fc5..3b05d675 100644
--- a/tests/phpunit/includes/MWNamespaceTest.php
+++ b/tests/phpunit/includes/MWNamespaceTest.php
@@ -53,10 +53,6 @@ class MWNamespaceTest extends MediaWikiTestCase {
$this->assertIsNotSubject( NS_TALK );
$this->assertIsNotSubject( NS_USER_TALK );
$this->assertIsNotSubject( 101 ); # user defined
-
- // Back compat
- $this->assertTrue( MWNamespace::isMain( NS_MAIN ) == MWNamespace::isSubject( NS_MAIN ) );
- $this->assertTrue( MWNamespace::isMain( NS_USER_TALK ) == MWNamespace::isSubject( NS_USER_TALK ) );
}
/**
@@ -442,6 +438,36 @@ class MWNamespaceTest extends MediaWikiTestCase {
}
/**
+ */
+ public function testGetSubjectNamespaces() {
+ $subjectsNS = MWNamespace::getSubjectNamespaces();
+ $this->assertContains( NS_MAIN, $subjectsNS,
+ "Talk namespaces should have NS_MAIN" );
+ $this->assertNotContains( NS_TALK, $subjectsNS,
+ "Talk namespaces should have NS_TALK" );
+
+ $this->assertNotContains( NS_MEDIA, $subjectsNS,
+ "Talk namespaces should not have NS_MEDIA" );
+ $this->assertNotContains( NS_SPECIAL, $subjectsNS,
+ "Talk namespaces should not have NS_SPECIAL" );
+ }
+
+ /**
+ */
+ public function testGetTalkNamespaces() {
+ $talkNS = MWNamespace::getTalkNamespaces();
+ $this->assertContains( NS_TALK, $talkNS,
+ "Subject namespaces should have NS_TALK" );
+ $this->assertNotContains( NS_MAIN, $talkNS,
+ "Subject namespaces should not have NS_MAIN" );
+
+ $this->assertNotContains( NS_MEDIA, $talkNS,
+ "Subject namespaces should not have NS_MEDIA" );
+ $this->assertNotContains( NS_SPECIAL, $talkNS,
+ "Subject namespaces should not have NS_SPECIAL" );
+ }
+
+ /**
* Some namespaces are always capitalized per code definition
* in MWNamespace::$alwaysCapitalizedNamespaces
*/
@@ -550,6 +576,15 @@ class MWNamespaceTest extends MediaWikiTestCase {
}
+ public function testIsNonincludable() {
+ global $wgNonincludableNamespaces;
+ $wgNonincludableNamespaces = array( NS_USER );
+
+ $this->assertTrue( MWNamespace::isNonincludable( NS_USER ) );
+
+ $this->assertFalse( MWNamespace::isNonincludable( NS_TEMPLATE ) );
+ }
+
####### HELPERS ###########################################################
function __call( $method, $args ) {
// Call the real method if it exists
diff --git a/tests/phpunit/includes/MessageTest.php b/tests/phpunit/includes/MessageTest.php
index 295b6d74..20181fd4 100644
--- a/tests/phpunit/includes/MessageTest.php
+++ b/tests/phpunit/includes/MessageTest.php
@@ -16,6 +16,8 @@ class MessageTest extends MediaWikiLangTestCase {
$this->assertInstanceOf( 'Message', wfMessage( 'i-dont-exist-evar' ) );
$this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->text() );
$this->assertEquals( '&lt;i-dont-exist-evar&gt;', wfMessage( 'i-dont-exist-evar' )->text() );
+ $this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->plain() );
+ $this->assertEquals( '&lt;i-dont-exist-evar&gt;', wfMessage( 'i-dont-exist-evar' )->escaped() );
}
function testInLanguage() {
diff --git a/tests/phpunit/includes/PreferencesTest.php b/tests/phpunit/includes/PreferencesTest.php
new file mode 100644
index 00000000..0e123177
--- /dev/null
+++ b/tests/phpunit/includes/PreferencesTest.php
@@ -0,0 +1,75 @@
+<?php
+
+class PreferencesTest extends MediaWikiTestCase {
+ /** Array of User objects */
+ private $prefUsers;
+ private $context;
+
+ function __construct() {
+ parent::__construct();
+ global $wgEnableEmail;
+
+ $this->prefUsers['noemail'] = new User;
+
+ $this->prefUsers['notauth'] = new User;
+ $this->prefUsers['notauth']
+ ->setEmail( 'noauth@example.org' );
+
+ $this->prefUsers['auth'] = new User;
+ $this->prefUsers['auth']
+ ->setEmail( 'noauth@example.org' );
+ $this->prefUsers['auth']
+ ->setEmailAuthenticationTimestamp( 1330946623 );
+
+ $this->context = new RequestContext;
+ $this->context->setTitle( Title::newFromText('PreferencesTest') );
+
+ //some tests depends on email setting
+ $wgEnableEmail = true;
+ }
+
+ /**
+ * Placeholder to verify bug 34302
+ * @covers Preferences::profilePreferences
+ */
+ function testEmailFieldsWhenUserHasNoEmail() {
+ $prefs = $this->prefsFor( 'noemail' );
+ $this->assertArrayHasKey( 'cssclass',
+ $prefs['emailaddress']
+ );
+ $this->assertEquals( 'mw-email-none', $prefs['emailaddress']['cssclass'] );
+ }
+ /**
+ * Placeholder to verify bug 34302
+ * @covers Preferences::profilePreferences
+ */
+ function testEmailFieldsWhenUserEmailNotAuthenticated() {
+ $prefs = $this->prefsFor( 'notauth' );
+ $this->assertArrayHasKey( 'cssclass',
+ $prefs['emailaddress']
+ );
+ $this->assertEquals( 'mw-email-not-authenticated', $prefs['emailaddress']['cssclass'] );
+ }
+ /**
+ * Placeholder to verify bug 34302
+ * @covers Preferences::profilePreferences
+ */
+ function testEmailFieldsWhenUserEmailIsAuthenticated() {
+ $prefs = $this->prefsFor( 'auth' );
+ $this->assertArrayHasKey( 'cssclass',
+ $prefs['emailaddress']
+ );
+ $this->assertEquals( 'mw-email-authenticated', $prefs['emailaddress']['cssclass'] );
+ }
+
+ /** Helper */
+ function prefsFor( $user_key ) {
+ $preferences = array();
+ Preferences::profilePreferences(
+ $this->prefUsers[$user_key]
+ , $this->context
+ , $preferences
+ );
+ return $preferences;
+ }
+}
diff --git a/tests/phpunit/includes/RecentChangeTest.php b/tests/phpunit/includes/RecentChangeTest.php
new file mode 100644
index 00000000..fbf271cc
--- /dev/null
+++ b/tests/phpunit/includes/RecentChangeTest.php
@@ -0,0 +1,273 @@
+<?php
+/**
+ * @group Database
+ */
+class RecentChangeTest extends MediaWikiTestCase {
+ protected $title;
+ protected $target;
+ protected $user;
+ protected $user_comment;
+ protected $context;
+
+ function __construct() {
+ parent::__construct();
+
+ $this->title = Title::newFromText( 'SomeTitle' );
+ $this->target = Title::newFromText( 'TestTarget' );
+ $this->user = User::newFromName( 'UserName' );
+
+ $this->user_comment = '<User comment about action>';
+ $this->context = RequestContext::newExtraneousContext( $this->title );
+ }
+
+ /**
+ * The testIrcMsgForAction* tests are supposed to cover the hacky
+ * LogFormatter::getIRCActionText / bug 34508
+ *
+ * Third parties bots listen to those messages. They are clever enough
+ * to fetch the i18n messages from the wiki and then analyze the IRC feed
+ * to reverse engineer the $1, $2 messages.
+ * One thing bots can not detect is when MediaWiki change the meaning of
+ * a message like what happened when we deployed 1.19. $1 became the user
+ * performing the action which broke basically all bots around.
+ *
+ * Should cover the following log actions (which are most commonly used by bots):
+ * - block/block
+ * - block/unblock
+ * - delete/delete
+ * - delete/restore
+ * - newusers/create
+ * - newusers/create2
+ * - newusers/autocreate
+ * - move/move
+ * - move/move_redir
+ * - protect/protect
+ * - protect/modifyprotect
+ * - protect/unprotect
+ * - upload/upload
+ *
+ * As well as the following Auto Edit Summaries:
+ * - blank
+ * - replace
+ * - rollback
+ * - undo
+ */
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypeBlock() {
+ # block/block
+ $this->assertIRCComment(
+ $this->context->msg( 'blocklogentry', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'block', 'block',
+ array(),
+ $this->user_comment
+ );
+ # block/unblock
+ $this->assertIRCComment(
+ $this->context->msg( 'unblocklogentry', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'block', 'unblock',
+ array(),
+ $this->user_comment
+ );
+ }
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypeDelete() {
+ # delete/delete
+ $this->assertIRCComment(
+ $this->context->msg( 'deletedarticle', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'delete', 'delete',
+ array(),
+ $this->user_comment
+ );
+
+ # delete/restore
+ $this->assertIRCComment(
+ $this->context->msg( 'undeletedarticle', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'delete', 'restore',
+ array(),
+ $this->user_comment
+ );
+ }
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypeNewusers() {
+ $this->assertIRCComment(
+ 'New user account',
+ 'newusers', 'newusers',
+ array()
+ );
+ $this->assertIRCComment(
+ 'New user account',
+ 'newusers', 'create',
+ array()
+ );
+ $this->assertIRCComment(
+ 'created new account SomeTitle',
+ 'newusers', 'create2',
+ array()
+ );
+ $this->assertIRCComment(
+ 'Account created automatically',
+ 'newusers', 'autocreate',
+ array()
+ );
+ }
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypeMove() {
+ $move_params = array(
+ '4::target' => $this->target->getPrefixedText(),
+ '5::noredir' => 0,
+ );
+
+ # move/move
+ $this->assertIRCComment(
+ $this->context->msg( '1movedto2', 'SomeTitle', 'TestTarget' )->plain() . ': ' . $this->user_comment,
+ 'move', 'move',
+ $move_params,
+ $this->user_comment
+ );
+
+ # move/move_redir
+ $this->assertIRCComment(
+ $this->context->msg( '1movedto2_redir', 'SomeTitle', 'TestTarget' )->plain() . ': ' . $this->user_comment,
+ 'move', 'move_redir',
+ $move_params,
+ $this->user_comment
+ );
+ }
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypePatrol() {
+ # patrol/patrol
+ $this->assertIRCComment(
+ $this->context->msg( 'patrol-log-line', 'revision 777', '[[SomeTitle]]', '' )->plain(),
+ 'patrol', 'patrol',
+ array(
+ '4::curid' => '777',
+ '5::previd' => '666',
+ '6::auto' => 0,
+ )
+ );
+ }
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypeProtect() {
+ $protectParams = array(
+ '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)'
+ );
+
+ # protect/protect
+ $this->assertIRCComment(
+ $this->context->msg( 'protectedarticle', 'SomeTitle ' . $protectParams[0] )->plain() . ': ' . $this->user_comment,
+ 'protect', 'protect',
+ $protectParams,
+ $this->user_comment
+ );
+
+ # protect/unprotect
+ $this->assertIRCComment(
+ $this->context->msg( 'unprotectedarticle', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'protect', 'unprotect',
+ array(),
+ $this->user_comment
+ );
+
+ # protect/modify
+ $this->assertIRCComment(
+ $this->context->msg( 'modifiedarticleprotection', 'SomeTitle ' . $protectParams[0] )->plain() . ': ' . $this->user_comment,
+ 'protect', 'modify',
+ $protectParams,
+ $this->user_comment
+ );
+ }
+
+ /**
+ * @covers LogFormatter::getIRCActionText
+ */
+ function testIrcMsgForLogTypeUpload() {
+ # upload/upload
+ $this->assertIRCComment(
+ $this->context->msg( 'uploadedimage', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'upload', 'upload',
+ array(),
+ $this->user_comment
+ );
+
+ # upload/overwrite
+ $this->assertIRCComment(
+ $this->context->msg( 'overwroteimage', 'SomeTitle' )->plain() . ': ' . $this->user_comment,
+ 'upload', 'overwrite',
+ array(),
+ $this->user_comment
+ );
+ }
+
+ /**
+ * @todo: Emulate these edits somehow and extract
+ * raw edit summary from RecentChange object
+ * --
+
+ function testIrcMsgForBlankingAES() {
+ // $this->context->msg( 'autosumm-blank', .. );
+ }
+
+ function testIrcMsgForReplaceAES() {
+ // $this->context->msg( 'autosumm-replace', .. );
+ }
+
+ function testIrcMsgForRollbackAES() {
+ // $this->context->msg( 'revertpage', .. );
+ }
+
+ function testIrcMsgForUndoAES() {
+ // $this->context->msg( 'undo-summary', .. );
+ }
+
+ * --
+ */
+
+ /**
+ * @param $expected String Expected IRC text without colors codes
+ * @param $type String Log type (move, delete, suppress, patrol ...)
+ * @param $action String A log type action
+ * @param $comment String (optional) A comment for the log action
+ * @param $msg String (optional) A message for PHPUnit :-)
+ */
+ function assertIRCComment( $expected, $type, $action, $params, $comment = null, $msg = '' ) {
+
+ $logEntry = new ManualLogEntry( $type, $action );
+ $logEntry->setPerformer( $this->user );
+ $logEntry->setTarget( $this->title );
+ if ( $comment !== null ) {
+ $logEntry->setComment( $comment );
+ }
+ $logEntry->setParameters( $params );
+
+ $formatter = LogFormatter::newFromEntry( $logEntry );
+ $formatter->setContext( $this->context );
+
+ // Apply the same transformation as done in RecentChange::getIRCLine for rc_comment
+ $ircRcComment = RecentChange::cleanupForIRC( $formatter->getIRCActionComment() );
+
+ $this->assertEquals(
+ $expected,
+ $ircRcComment,
+ $msg
+ );
+ }
+
+}
diff --git a/tests/phpunit/includes/RevisionStorageTest.php b/tests/phpunit/includes/RevisionStorageTest.php
new file mode 100644
index 00000000..8a7facec
--- /dev/null
+++ b/tests/phpunit/includes/RevisionStorageTest.php
@@ -0,0 +1,408 @@
+<?php
+
+/**
+ * Test class for Revision storage.
+ *
+ * @group Database
+ * ^--- important, causes temporary tables to be used instead of the real database
+ *
+ * @group medium
+ * ^--- important, causes tests not to fail with timeout
+ */
+class RevisionStorageTest extends MediaWikiTestCase {
+
+ var $the_page;
+
+ function __construct( $name = null, array $data = array(), $dataName = '' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->tablesUsed = array_merge( $this->tablesUsed,
+ array( 'page',
+ 'revision',
+ 'text',
+
+ 'recentchanges',
+ 'logging',
+
+ 'page_props',
+ 'pagelinks',
+ 'categorylinks',
+ 'langlinks',
+ 'externallinks',
+ 'imagelinks',
+ 'templatelinks',
+ 'iwlinks' ) );
+ }
+
+ public function setUp() {
+ if ( !$this->the_page ) {
+ $this->the_page = $this->createPage( 'RevisionStorageTest_the_page', "just a dummy page" );
+ }
+ }
+
+ protected function makeRevision( $props = null ) {
+ if ( $props === null ) $props = array();
+
+ if ( !isset( $props['content'] ) && !isset( $props['text'] ) ) $props['text'] = 'Lorem Ipsum';
+ if ( !isset( $props['comment'] ) ) $props['comment'] = 'just a test';
+ if ( !isset( $props['page'] ) ) $props['page'] = $this->the_page->getId();
+
+ $rev = new Revision( $props );
+
+ $dbw = wfgetDB( DB_MASTER );
+ $rev->insertOn( $dbw );
+
+ return $rev;
+ }
+
+ protected function createPage( $page, $text, $model = null ) {
+ if ( is_string( $page ) ) $page = Title::newFromText( $page );
+ if ( $page instanceof Title ) $page = new WikiPage( $page );
+
+ if ( $page->exists() ) {
+ $page->doDeleteArticle( "done" );
+ }
+
+ $page->doEdit( $text, "testing", EDIT_NEW );
+
+ return $page;
+ }
+
+ protected function assertRevEquals( Revision $orig, Revision $rev = null ) {
+ $this->assertNotNull( $rev, 'missing revision' );
+
+ $this->assertEquals( $orig->getId(), $rev->getId() );
+ $this->assertEquals( $orig->getPage(), $rev->getPage() );
+ $this->assertEquals( $orig->getTimestamp(), $rev->getTimestamp() );
+ $this->assertEquals( $orig->getUser(), $rev->getUser() );
+ $this->assertEquals( $orig->getSha1(), $rev->getSha1() );
+ }
+
+ /**
+ * @covers Revision::__construct
+ */
+ public function testConstructFromRow()
+ {
+ $orig = $this->makeRevision();
+
+ $dbr = wfgetDB( DB_SLAVE );
+ $res = $dbr->select( 'revision', '*', array( 'rev_id' => $orig->getId() ) );
+ $this->assertTrue( is_object( $res ), 'query failed' );
+
+ $row = $res->fetchObject();
+ $res->free();
+
+ $rev = new Revision( $row );
+
+ $this->assertRevEquals( $orig, $rev );
+ }
+
+ /**
+ * @covers Revision::newFromRow
+ */
+ public function testNewFromRow()
+ {
+ $orig = $this->makeRevision();
+
+ $dbr = wfgetDB( DB_SLAVE );
+ $res = $dbr->select( 'revision', '*', array( 'rev_id' => $orig->getId() ) );
+ $this->assertTrue( is_object( $res ), 'query failed' );
+
+ $row = $res->fetchObject();
+ $res->free();
+
+ $rev = Revision::newFromRow( $row );
+
+ $this->assertRevEquals( $orig, $rev );
+ }
+
+
+ /**
+ * @covers Revision::newFromArchiveRow
+ */
+ public function testNewFromArchiveRow()
+ {
+ $page = $this->createPage( 'RevisionStorageTest_testNewFromArchiveRow', 'Lorem Ipsum' );
+ $orig = $page->getRevision();
+ $page->doDeleteArticle( 'test Revision::newFromArchiveRow' );
+
+ $dbr = wfgetDB( DB_SLAVE );
+ $res = $dbr->select( 'archive', '*', array( 'ar_rev_id' => $orig->getId() ) );
+ $this->assertTrue( is_object( $res ), 'query failed' );
+
+ $row = $res->fetchObject();
+ $res->free();
+
+ $rev = Revision::newFromArchiveRow( $row );
+
+ $this->assertRevEquals( $orig, $rev );
+ }
+
+ /**
+ * @covers Revision::newFromId
+ */
+ public function testNewFromId()
+ {
+ $orig = $this->makeRevision();
+
+ $rev = Revision::newFromId( $orig->getId() );
+
+ $this->assertRevEquals( $orig, $rev );
+ }
+
+ /**
+ * @covers Revision::fetchRevision
+ */
+ public function testFetchRevision()
+ {
+ $page = $this->createPage( 'RevisionStorageTest_testFetchRevision', 'one' );
+ $id1 = $page->getRevision()->getId();
+
+ $page->doEdit( 'two', 'second rev' );
+ $id2 = $page->getRevision()->getId();
+
+ $res = Revision::fetchRevision( $page->getTitle() );
+
+ #note: order is unspecified
+ $rows = array();
+ while ( ( $row = $res->fetchObject() ) ) {
+ $rows[ $row->rev_id ]= $row;
+ }
+
+ $row = $res->fetchObject();
+ $this->assertEquals( 1, count($rows), 'expected exactly one revision' );
+ $this->assertArrayHasKey( $id2, $rows, 'missing revision with id ' . $id2 );
+ }
+
+ /**
+ * @covers Revision::selectFields
+ */
+ public function testSelectFields()
+ {
+ $fields = Revision::selectFields();
+
+ $this->assertTrue( in_array( 'rev_id', $fields ), 'missing rev_id in list of fields');
+ $this->assertTrue( in_array( 'rev_page', $fields ), 'missing rev_page in list of fields');
+ $this->assertTrue( in_array( 'rev_timestamp', $fields ), 'missing rev_timestamp in list of fields');
+ $this->assertTrue( in_array( 'rev_user', $fields ), 'missing rev_user in list of fields');
+ }
+
+ /**
+ * @covers Revision::getPage
+ */
+ public function testGetPage()
+ {
+ $page = $this->the_page;
+
+ $orig = $this->makeRevision( array( 'page' => $page->getId() ) );
+ $rev = Revision::newFromId( $orig->getId() );
+
+ $this->assertEquals( $page->getId(), $rev->getPage() );
+ }
+
+ /**
+ * @covers Revision::getText
+ */
+ public function testGetText()
+ {
+ $orig = $this->makeRevision( array( 'text' => 'hello hello.' ) );
+ $rev = Revision::newFromId( $orig->getId() );
+
+ $this->assertEquals( 'hello hello.', $rev->getText() );
+ }
+
+ /**
+ * @covers Revision::revText
+ */
+ public function testRevText()
+ {
+ $this->hideDeprecated( 'Revision::revText' );
+ $orig = $this->makeRevision( array( 'text' => 'hello hello rev.' ) );
+ $rev = Revision::newFromId( $orig->getId() );
+
+ $this->assertEquals( 'hello hello rev.', $rev->revText() );
+ }
+
+ /**
+ * @covers Revision::getRawText
+ */
+ public function testGetRawText()
+ {
+ $orig = $this->makeRevision( array( 'text' => 'hello hello raw.' ) );
+ $rev = Revision::newFromId( $orig->getId() );
+
+ $this->assertEquals( 'hello hello raw.', $rev->getRawText() );
+ }
+ /**
+ * @covers Revision::isCurrent
+ */
+ public function testIsCurrent()
+ {
+ $page = $this->createPage( 'RevisionStorageTest_testIsCurrent', 'Lorem Ipsum' );
+ $rev1 = $page->getRevision();
+
+ # @todo: find out if this should be true
+ # $this->assertTrue( $rev1->isCurrent() );
+
+ $rev1x = Revision::newFromId( $rev1->getId() );
+ $this->assertTrue( $rev1x->isCurrent() );
+
+ $page->doEdit( 'Bla bla', 'second rev' );
+ $rev2 = $page->getRevision();
+
+ # @todo: find out if this should be true
+ # $this->assertTrue( $rev2->isCurrent() );
+
+ $rev1x = Revision::newFromId( $rev1->getId() );
+ $this->assertFalse( $rev1x->isCurrent() );
+
+ $rev2x = Revision::newFromId( $rev2->getId() );
+ $this->assertTrue( $rev2x->isCurrent() );
+ }
+
+ /**
+ * @covers Revision::getPrevious
+ */
+ public function testGetPrevious()
+ {
+ $page = $this->createPage( 'RevisionStorageTest_testGetPrevious', 'Lorem Ipsum testGetPrevious' );
+ $rev1 = $page->getRevision();
+
+ $this->assertNull( $rev1->getPrevious() );
+
+ $page->doEdit( 'Bla bla', 'second rev testGetPrevious' );
+ $rev2 = $page->getRevision();
+
+ $this->assertNotNull( $rev2->getPrevious() );
+ $this->assertEquals( $rev1->getId(), $rev2->getPrevious()->getId() );
+ }
+
+ /**
+ * @covers Revision::getNext
+ */
+ public function testGetNext()
+ {
+ $page = $this->createPage( 'RevisionStorageTest_testGetNext', 'Lorem Ipsum testGetNext' );
+ $rev1 = $page->getRevision();
+
+ $this->assertNull( $rev1->getNext() );
+
+ $page->doEdit( 'Bla bla', 'second rev testGetNext' );
+ $rev2 = $page->getRevision();
+
+ $this->assertNotNull( $rev1->getNext() );
+ $this->assertEquals( $rev2->getId(), $rev1->getNext()->getId() );
+ }
+
+ /**
+ * @covers Revision::newNullRevision
+ */
+ public function testNewNullRevision()
+ {
+ $page = $this->createPage( 'RevisionStorageTest_testNewNullRevision', 'some testing text' );
+ $orig = $page->getRevision();
+
+ $dbw = wfGetDB( DB_MASTER );
+ $rev = Revision::newNullRevision( $dbw, $page->getId(), 'a null revision', false );
+
+ $this->assertNotEquals( $orig->getId(), $rev->getId(), 'new null revision shold have a different id from the original revision' );
+ $this->assertEquals( $orig->getTextId(), $rev->getTextId(), 'new null revision shold have the same text id as the original revision' );
+ $this->assertEquals( 'some testing text', $rev->getText() );
+ }
+
+ public function dataUserWasLastToEdit() {
+ return array(
+ array( #0
+ 3, true, # actually the last edit
+ ),
+ array( #1
+ 2, true, # not the current edit, but still by this user
+ ),
+ array( #2
+ 1, false, # edit by another user
+ ),
+ array( #3
+ 0, false, # first edit, by this user, but another user edited in the mean time
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataUserWasLastToEdit
+ */
+ public function testUserWasLastToEdit( $sinceIdx, $expectedLast ) {
+ $userA = \User::newFromName( "RevisionStorageTest_userA" );
+ $userB = \User::newFromName( "RevisionStorageTest_userB" );
+
+ if ( $userA->getId() === 0 ) {
+ $userA = \User::createNew( $userA->getName() );
+ }
+
+ if ( $userB->getId() === 0 ) {
+ $userB = \User::createNew( $userB->getName() );
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ $revisions = array();
+
+ // create revisions -----------------------------
+ $page = WikiPage::factory( Title::newFromText( 'RevisionStorageTest_testUserWasLastToEdit' ) );
+
+ # zero
+ $revisions[0] = new Revision( array(
+ 'page' => $page->getId(),
+ 'timestamp' => '20120101000000',
+ 'user' => $userA->getId(),
+ 'text' => 'zero',
+ 'summary' => 'edit zero'
+ ) );
+ $revisions[0]->insertOn( $dbw );
+
+ # one
+ $revisions[1] = new Revision( array(
+ 'page' => $page->getId(),
+ 'timestamp' => '20120101000100',
+ 'user' => $userA->getId(),
+ 'text' => 'one',
+ 'summary' => 'edit one'
+ ) );
+ $revisions[1]->insertOn( $dbw );
+
+ # two
+ $revisions[2] = new Revision( array(
+ 'page' => $page->getId(),
+ 'timestamp' => '20120101000200',
+ 'user' => $userB->getId(),
+ 'text' => 'two',
+ 'summary' => 'edit two'
+ ) );
+ $revisions[2]->insertOn( $dbw );
+
+ # three
+ $revisions[3] = new Revision( array(
+ 'page' => $page->getId(),
+ 'timestamp' => '20120101000300',
+ 'user' => $userA->getId(),
+ 'text' => 'three',
+ 'summary' => 'edit three'
+ ) );
+ $revisions[3]->insertOn( $dbw );
+
+ # four
+ $revisions[4] = new Revision( array(
+ 'page' => $page->getId(),
+ 'timestamp' => '20120101000200',
+ 'user' => $userA->getId(),
+ 'text' => 'zero',
+ 'summary' => 'edit four'
+ ) );
+ $revisions[4]->insertOn( $dbw );
+
+ // test it ---------------------------------
+ $since = $revisions[ $sinceIdx ]->getTimestamp();
+
+ $wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since );
+
+ $this->assertEquals( $expectedLast, $wasLast );
+ }
+}
diff --git a/tests/phpunit/includes/SampleTest.php b/tests/phpunit/includes/SampleTest.php
index 77a371d5..59ba0a04 100644
--- a/tests/phpunit/includes/SampleTest.php
+++ b/tests/phpunit/includes/SampleTest.php
@@ -47,7 +47,7 @@ class TestSample extends MediaWikiLangTestCase {
array( 'Text', null, 'Text' ),
array( 'text', null, 'Text' ),
array( 'Text', NS_USER, 'User:Text' ),
- array( 'Photo.jpg', NS_IMAGE, 'File:Photo.jpg' )
+ array( 'Photo.jpg', NS_FILE, 'File:Photo.jpg' )
);
}
diff --git a/tests/phpunit/includes/SanitizerTest.php b/tests/phpunit/includes/SanitizerTest.php
index b76aa5c7..66af2581 100644
--- a/tests/phpunit/includes/SanitizerTest.php
+++ b/tests/phpunit/includes/SanitizerTest.php
@@ -110,21 +110,27 @@ class SanitizerTest extends MediaWikiTestCase {
$this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=&foobar;' ), array( 'foo' => '&foobar;' ), 'Entity-like items are accepted' );
}
- function testDeprecatedAttributes() {
- $GLOBALS['wgCleanupPresentationalAttributes'] = true;
- $this->assertEquals( Sanitizer::fixTagAttributes( 'clear="left"', 'br' ), ' style="clear: left;"', 'Deprecated attributes are converted to styles when enabled.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'clear="all"', 'br' ), ' style="clear: both;"', 'clear=all is converted to clear: both; not clear: all;' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'CLEAR="ALL"', 'br' ), ' style="clear: both;"', 'clear=ALL is not treated differently from clear=all' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'width="100"', 'td' ), ' style="width: 100px;"', 'Numeric sizes use pixels instead of numbers.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'width="100%"', 'td' ), ' style="width: 100%;"', 'Units are allowed in sizes.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'WIDTH="100%"', 'td' ), ' style="width: 100%;"', 'Uppercase WIDTH is treated as lowercase width.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'WiDTh="100%"', 'td' ), ' style="width: 100%;"', 'Mixed case does not break WiDTh.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'nowrap="true"', 'td' ), ' style="white-space: nowrap;"', 'nowrap attribute is output as white-space: nowrap; not something else.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'nowrap=""', 'td' ), ' style="white-space: nowrap;"', 'nowrap="" is considered true, not false' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'NOWRAP="true"', 'td' ), ' style="white-space: nowrap;"', 'nowrap attribute works when uppercase.' );
- $this->assertEquals( Sanitizer::fixTagAttributes( 'NoWrAp="true"', 'td' ), ' style="white-space: nowrap;"', 'nowrap attribute works when mixed-case.' );
- $GLOBALS['wgCleanupPresentationalAttributes'] = false;
- $this->assertEquals( Sanitizer::fixTagAttributes( 'clear="left"', 'br' ), ' clear="left"', 'Deprecated attributes are not converted to styles when enabled.' );
+ /**
+ * @dataProvider provideDeprecatedAttributes
+ */
+ function testDeprecatedAttributesUnaltered( $inputAttr, $inputEl ) {
+ $this->assertEquals( " $inputAttr", Sanitizer::fixTagAttributes( $inputAttr, $inputEl ) );
+ }
+
+ public static function provideDeprecatedAttributes() {
+ return array(
+ array( 'clear="left"', 'br' ),
+ array( 'clear="all"', 'br' ),
+ array( 'width="100"', 'td' ),
+ array( 'nowrap="true"', 'td' ),
+ array( 'nowrap=""', 'td' ),
+ array( 'align="right"', 'td' ),
+ array( 'align="center"', 'table' ),
+ array( 'align="left"', 'tr' ),
+ array( 'align="center"', 'div' ),
+ array( 'align="left"', 'h1' ),
+ array( 'align="left"', 'span' ),
+ );
}
/**
diff --git a/tests/phpunit/includes/TemplateCategoriesTest.php b/tests/phpunit/includes/TemplateCategoriesTest.php
index de9d6dc6..39ce6e31 100644
--- a/tests/phpunit/includes/TemplateCategoriesTest.php
+++ b/tests/phpunit/includes/TemplateCategoriesTest.php
@@ -3,26 +3,24 @@
/**
* @group Database
*/
-require dirname( __FILE__ ) . "/../../../maintenance/runJobs.php";
+require __DIR__ . "/../../../maintenance/runJobs.php";
class TemplateCategoriesTest extends MediaWikiLangTestCase {
function testTemplateCategories() {
- global $wgUser;
-
$title = Title::newFromText( "Categorized from template" );
- $article = new Article( $title );
- $wgUser = new User();
- $wgUser->mRights['*'] = array( 'createpage', 'edit', 'purge' );
+ $page = WikiPage::factory( $title );
+ $user = new User();
+ $user->mRights = array( 'createpage', 'edit', 'purge' );
- $status = $article->doEdit( '{{Categorising template}}', 'Create a page with a template', 0 );
+ $status = $page->doEdit( '{{Categorising template}}', 'Create a page with a template', 0, false, $user );
$this->assertEquals(
array()
, $title->getParentCategories()
);
- $template = new Article( Title::newFromText( 'Template:Categorising template' ) );
- $status = $template->doEdit( '[[Category:Solved bugs]]', 'Add a category through a template', 0 );
+ $template = WikiPage::factory( Title::newFromText( 'Template:Categorising template' ) );
+ $status = $template->doEdit( '[[Category:Solved bugs]]', 'Add a category through a template', 0, false, $user );
// Run the job queue
$jobs = new RunJobs;
diff --git a/tests/phpunit/includes/api/ApiTestUser.php b/tests/phpunit/includes/TestUser.php
index 8d5f61a7..c4d89455 100644
--- a/tests/phpunit/includes/api/ApiTestUser.php
+++ b/tests/phpunit/includes/TestUser.php
@@ -1,7 +1,7 @@
<?php
/* Wraps the user object, so we can also retain full access to properties like password if we log in via the API */
-class ApiTestUser {
+class TestUser {
public $username;
public $password;
public $email;
@@ -55,5 +55,4 @@ class ApiTestUser {
$this->user->saveSettings();
}
-
}
diff --git a/tests/phpunit/includes/TimestampTest.php b/tests/phpunit/includes/TimestampTest.php
new file mode 100644
index 00000000..231228f5
--- /dev/null
+++ b/tests/phpunit/includes/TimestampTest.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Tests timestamp parsing and output.
+ */
+class TimestampTest extends MediaWikiTestCase {
+ /**
+ * Test parsing of valid timestamps and outputing to MW format.
+ * @dataProvider provideValidTimestamps
+ */
+ function testValidParse( $format, $original, $expected ) {
+ $timestamp = new MWTimestamp( $original );
+ $this->assertEquals( $expected, $timestamp->getTimestamp( TS_MW ) );
+ }
+
+ /**
+ * Test outputting valid timestamps to different formats.
+ * @dataProvider provideValidTimestamps
+ */
+ function testValidOutput( $format, $expected, $original ) {
+ $timestamp = new MWTimestamp( $original );
+ $this->assertEquals( $expected, (string) $timestamp->getTimestamp( $format ) );
+ }
+
+ /**
+ * Test an invalid timestamp.
+ * @expectedException TimestampException
+ */
+ function testInvalidParse() {
+ $timestamp = new MWTimestamp( "This is not a timestamp." );
+ }
+
+ /**
+ * Test requesting an invalid output format.
+ * @expectedException TimestampException
+ */
+ function testInvalidOutput() {
+ $timestamp = new MWTimestamp( '1343761268' );
+ $timestamp->getTimestamp( 98 );
+ }
+
+ /**
+ * Test human readable timestamp format.
+ */
+ function testHumanOutput() {
+ $timestamp = new MWTimestamp( time() - 3600 );
+ $this->assertEquals( "1 hour ago", $timestamp->getHumanTimestamp()->toString() );
+ }
+
+ /**
+ * Returns a list of valid timestamps in the format:
+ * array( type, timestamp_of_type, timestamp_in_MW )
+ */
+ function provideValidTimestamps() {
+ return array(
+ // Various formats
+ array( TS_UNIX, '1343761268', '20120731190108' ),
+ array( TS_MW, '20120731190108', '20120731190108' ),
+ array( TS_DB, '2012-07-31 19:01:08', '20120731190108' ),
+ array( TS_ISO_8601, '2012-07-31T19:01:08Z', '20120731190108' ),
+ array( TS_ISO_8601_BASIC, '20120731T190108Z', '20120731190108' ),
+ array( TS_EXIF, '2012:07:31 19:01:08', '20120731190108' ),
+ array( TS_RFC2822, 'Tue, 31 Jul 2012 19:01:08 GMT', '20120731190108' ),
+ array( TS_ORACLE, '31-07-2012 19:01:08.000000', '20120731190108' ),
+ array( TS_POSTGRES, '2012-07-31 19:01:08 GMT', '20120731190108' ),
+ array( TS_DB2, '2012-07-31 19:01:08', '20120731190108' ),
+ // Some extremes and weird values
+ array( TS_ISO_8601, '9999-12-31T23:59:59Z', '99991231235959' ),
+ array( TS_UNIX, '-62135596801', '00001231235959' )
+ );
+ }
+}
diff --git a/tests/phpunit/includes/TitleMethodsTest.php b/tests/phpunit/includes/TitleMethodsTest.php
index 2f1103e8..aed658ba 100644
--- a/tests/phpunit/includes/TitleMethodsTest.php
+++ b/tests/phpunit/includes/TitleMethodsTest.php
@@ -21,8 +21,8 @@ class TitleMethodsTest extends MediaWikiTestCase {
$titleA = Title::newFromText( $titleA );
$titleB = Title::newFromText( $titleB );
- $this->assertEquals( $titleA->equals( $titleB ), $expectedBool );
- $this->assertEquals( $titleB->equals( $titleA ), $expectedBool );
+ $this->assertEquals( $expectedBool, $titleA->equals( $titleB ) );
+ $this->assertEquals( $expectedBool, $titleB->equals( $titleA ) );
}
public function dataInNamespace() {
@@ -43,7 +43,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
*/
public function testInNamespace( $title, $ns, $expectedBool ) {
$title = Title::newFromText( $title );
- $this->assertEquals( $title->inNamespace( $ns ), $expectedBool );
+ $this->assertEquals( $expectedBool, $title->inNamespace( $ns ) );
}
public function testInNamespaces() {
@@ -72,7 +72,130 @@ class TitleMethodsTest extends MediaWikiTestCase {
*/
public function testHasSubjectNamespace( $title, $ns, $expectedBool ) {
$title = Title::newFromText( $title );
- $this->assertEquals( $title->hasSubjectNamespace( $ns ), $expectedBool );
+ $this->assertEquals( $expectedBool, $title->hasSubjectNamespace( $ns ) );
+ }
+
+ public function dataIsCssOrJsPage() {
+ return array(
+ array( 'Foo', false ),
+ array( 'Foo.js', false ),
+ array( 'Foo/bar.js', false ),
+ array( 'User:Foo', false ),
+ array( 'User:Foo.js', false ),
+ array( 'User:Foo/bar.js', false ),
+ array( 'User:Foo/bar.css', false ),
+ array( 'User talk:Foo/bar.css', false ),
+ array( 'User:Foo/bar.js.xxx', false ),
+ array( 'User:Foo/bar.xxx', false ),
+ array( 'MediaWiki:Foo.js', true ),
+ array( 'MediaWiki:Foo.css', true ),
+ array( 'MediaWiki:Foo.JS', false ),
+ array( 'MediaWiki:Foo.CSS', false ),
+ array( 'MediaWiki:Foo.css.xxx', false ),
+ );
+ }
+
+ /**
+ * @dataProvider dataIsCssOrJsPage
+ */
+ public function testIsCssOrJsPage( $title, $expectedBool ) {
+ $title = Title::newFromText( $title );
+ $this->assertEquals( $expectedBool, $title->isCssOrJsPage() );
+ }
+
+
+ public function dataIsCssJsSubpage() {
+ return array(
+ array( 'Foo', false ),
+ array( 'Foo.js', false ),
+ array( 'Foo/bar.js', false ),
+ array( 'User:Foo', false ),
+ array( 'User:Foo.js', false ),
+ array( 'User:Foo/bar.js', true ),
+ array( 'User:Foo/bar.css', true ),
+ array( 'User talk:Foo/bar.css', false ),
+ array( 'User:Foo/bar.js.xxx', false ),
+ array( 'User:Foo/bar.xxx', false ),
+ array( 'MediaWiki:Foo.js', false ),
+ array( 'User:Foo/bar.JS', false ),
+ array( 'User:Foo/bar.CSS', false ),
+ );
+ }
+
+ /**
+ * @dataProvider dataIsCssJsSubpage
+ */
+ public function testIsCssJsSubpage( $title, $expectedBool ) {
+ $title = Title::newFromText( $title );
+ $this->assertEquals( $expectedBool, $title->isCssJsSubpage() );
+ }
+
+ public function dataIsCssSubpage() {
+ return array(
+ array( 'Foo', false ),
+ array( 'Foo.css', false ),
+ array( 'User:Foo', false ),
+ array( 'User:Foo.js', false ),
+ array( 'User:Foo.css', false ),
+ array( 'User:Foo/bar.js', false ),
+ array( 'User:Foo/bar.css', true ),
+ );
+ }
+
+ /**
+ * @dataProvider dataIsCssSubpage
+ */
+ public function testIsCssSubpage( $title, $expectedBool ) {
+ $title = Title::newFromText( $title );
+ $this->assertEquals( $expectedBool, $title->isCssSubpage() );
+ }
+
+ public function dataIsJsSubpage() {
+ return array(
+ array( 'Foo', false ),
+ array( 'Foo.css', false ),
+ array( 'User:Foo', false ),
+ array( 'User:Foo.js', false ),
+ array( 'User:Foo.css', false ),
+ array( 'User:Foo/bar.js', true ),
+ array( 'User:Foo/bar.css', false ),
+ );
+ }
+
+ /**
+ * @dataProvider dataIsJsSubpage
+ */
+ public function testIsJsSubpage( $title, $expectedBool ) {
+ $title = Title::newFromText( $title );
+ $this->assertEquals( $expectedBool, $title->isJsSubpage() );
+ }
+
+ public function dataIsWikitextPage() {
+ return array(
+ array( 'Foo', true ),
+ array( 'Foo.js', true ),
+ array( 'Foo/bar.js', true ),
+ array( 'User:Foo', true ),
+ array( 'User:Foo.js', true ),
+ array( 'User:Foo/bar.js', false ),
+ array( 'User:Foo/bar.css', false ),
+ array( 'User talk:Foo/bar.css', true ),
+ array( 'User:Foo/bar.js.xxx', true ),
+ array( 'User:Foo/bar.xxx', true ),
+ array( 'MediaWiki:Foo.js', false ),
+ array( 'MediaWiki:Foo.css', false ),
+ array( 'MediaWiki:Foo/bar.css', false ),
+ array( 'User:Foo/bar.JS', true ),
+ array( 'User:Foo/bar.CSS', true ),
+ );
+ }
+
+ /**
+ * @dataProvider dataIsWikitextPage
+ */
+ public function testIsWikitextPage( $title, $expectedBool ) {
+ $title = Title::newFromText( $title );
+ $this->assertEquals( $expectedBool, $title->isWikitextPage() );
}
}
diff --git a/tests/phpunit/includes/TitleTest.php b/tests/phpunit/includes/TitleTest.php
index 1c8be5f9..f61652df 100644
--- a/tests/phpunit/includes/TitleTest.php
+++ b/tests/phpunit/includes/TitleTest.php
@@ -77,4 +77,79 @@ class TitleTest extends MediaWikiTestCase {
}
+ /**
+ * @dataProvider provideCasesForGetpageviewlanguage
+ */
+ function testGetpageviewlanguage( $expected, $titleText, $contLang, $lang, $variant, $msg='' ) {
+ // Save globals
+ global $wgContLang, $wgLang, $wgAllowUserJs, $wgLanguageCode, $wgDefaultLanguageVariant;
+ $save['wgContLang'] = $wgContLang;
+ $save['wgLang'] = $wgLang;
+ $save['wgAllowUserJs'] = $wgAllowUserJs;
+ $save['wgLanguageCode'] = $wgLanguageCode;
+ $save['wgDefaultLanguageVariant'] = $wgDefaultLanguageVariant;
+
+ // Setup test environnement:
+ $wgContLang = Language::factory( $contLang );
+ $wgLang = Language::factory( $lang );
+ # To test out .js titles:
+ $wgAllowUserJs = true;
+ $wgLanguageCode = $contLang;
+ $wgDefaultLanguageVariant = $variant;
+
+ $title = Title::newFromText( $titleText );
+ $this->assertInstanceOf( 'Title', $title,
+ "Test must be passed a valid title text, you gave '$titleText'"
+ );
+ $this->assertEquals( $expected,
+ $title->getPageViewLanguage()->getCode(),
+ $msg
+ );
+
+ // Restore globals
+ $wgContLang = $save['wgContLang'];
+ $wgLang = $save['wgLang'];
+ $wgAllowUserJs = $save['wgAllowUserJs'];
+ $wgLanguageCode = $save['wgLanguageCode'];
+ $wgDefaultLanguageVariant = $save['wgDefaultLanguageVariant'];
+ }
+
+ function provideCasesForGetpageviewlanguage() {
+ # Format:
+ # - expected
+ # - Title name
+ # - wgContLang (expected in most case)
+ # - wgLang (on some specific pages)
+ # - wgDefaultLanguageVariant
+ # - Optional message
+ return array(
+ array( 'fr', 'Main_page', 'fr', 'fr', false ),
+ array( 'es', 'Main_page', 'es', 'zh-tw', false ),
+ array( 'zh', 'Main_page', 'zh', 'zh-tw', false ),
+
+ array( 'es', 'Main_page', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'es', 'MediaWiki:About', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'es', 'MediaWiki:About/', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'de', 'MediaWiki:About/de', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'MediaWiki:Common.js', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'MediaWiki:Common.css', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'User:JohnDoe/Common.js', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'User:JohnDoe/Monobook.css', 'es', 'zh-tw', 'zh-cn' ),
+
+ array( 'zh-cn', 'Main_page', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'zh', 'MediaWiki:About', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'zh', 'MediaWiki:About/', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'de', 'MediaWiki:About/de', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'zh-cn', 'MediaWiki:About/zh-cn', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'zh-tw', 'MediaWiki:About/zh-tw', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'MediaWiki:Common.js', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'MediaWiki:Common.css', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'User:JohnDoe/Common.js', 'zh', 'zh-tw', 'zh-cn' ),
+ array( 'en', 'User:JohnDoe/Monobook.css', 'zh', 'zh-tw', 'zh-cn' ),
+
+ array( 'zh-tw', 'Special:NewPages', 'es', 'zh-tw', 'zh-cn' ),
+ array( 'zh-tw', 'Special:NewPages', 'zh', 'zh-tw', 'zh-cn' ),
+
+ );
+ }
}
diff --git a/tests/phpunit/includes/UserTest.php b/tests/phpunit/includes/UserTest.php
index ef03e835..7a424aef 100644
--- a/tests/phpunit/includes/UserTest.php
+++ b/tests/phpunit/includes/UserTest.php
@@ -140,4 +140,32 @@ class UserTest extends MediaWikiTestCase {
array( 'Ab cd', false, ' Ideographic space' ),
);
}
+
+ /**
+ * Test, if for all rights a right- message exist,
+ * which is used on Special:ListGroupRights as help text
+ * Extensions and core
+ */
+ public function testAllRightsWithMessage() {
+ //Getting all user rights, for core: User::$mCoreRights, for extensions: $wgAvailableRights
+ $allRights = User::getAllRights();
+ $allMessageKeys = Language::getMessageKeysFor( 'en' );
+
+ $rightsWithMessage = array();
+ foreach ( $allMessageKeys as $message ) {
+ // === 0: must be at beginning of string (position 0)
+ if ( strpos( $message, 'right-' ) === 0 ) {
+ $rightsWithMessage[] = substr( $message, strlen( 'right-' ) );
+ }
+ }
+
+ sort( $allRights );
+ sort( $rightsWithMessage );
+
+ $this->assertEquals(
+ $allRights,
+ $rightsWithMessage,
+ 'Each user rights (core/extensions) has a corresponding right- message.'
+ );
+ }
}
diff --git a/tests/phpunit/includes/WebRequestTest.php b/tests/phpunit/includes/WebRequestTest.php
index e72408f6..1fc0b4b3 100644
--- a/tests/phpunit/includes/WebRequestTest.php
+++ b/tests/phpunit/includes/WebRequestTest.php
@@ -1,14 +1,22 @@
<?php
class WebRequestTest extends MediaWikiTestCase {
+ static $oldServer;
+
+ function setUp() {
+ self::$oldServer = $_SERVER;
+ }
+
+ function tearDown() {
+ $_SERVER = self::$oldServer;
+ }
+
/**
* @dataProvider provideDetectServer
*/
function testDetectServer( $expected, $input, $description ) {
- $oldServer = $_SERVER;
$_SERVER = $input;
$result = WebRequest::detectServer();
- $_SERVER = $oldServer;
$this->assertEquals( $expected, $result, $description );
}
@@ -91,13 +99,11 @@ class WebRequestTest extends MediaWikiTestCase {
*/
function testGetIP( $expected, $input, $squid, $private, $description ) {
global $wgSquidServersNoPurge, $wgUsePrivateIPs;
- $oldServer = $_SERVER;
$_SERVER = $input;
$wgSquidServersNoPurge = $squid;
$wgUsePrivateIPs = $private;
$request = new WebRequest();
$result = $request->getIP();
- $_SERVER = $oldServer;
$this->assertEquals( $expected, $result, $description );
}
@@ -182,4 +188,29 @@ class WebRequestTest extends MediaWikiTestCase {
# Next call throw an exception about lacking an IP
$request->getIP();
}
+
+ function languageProvider() {
+ return array(
+ array( '', array(), 'Empty Accept-Language header' ),
+ array( 'en', array( 'en' => 1 ), 'One language' ),
+ array( 'en, ar', array( 'en' => 1, 'ar' => 1 ), 'Two languages listed in appearance order.' ),
+ array( 'zh-cn,zh-tw', array( 'zh-cn' => 1, 'zh-tw' => 1 ), 'Two equally prefered languages, listed in appearance order per rfc3282. Checks c9119' ),
+ array( 'es, en; q=0.5', array( 'es' => 1, 'en' => '0.5' ), 'Spanish as first language and English and second' ),
+ array( 'en; q=0.5, es', array( 'es' => 1, 'en' => '0.5' ), 'Less prefered language first' ),
+ array( 'fr, en; q=0.5, es', array( 'fr' => 1, 'es' => 1, 'en' => '0.5' ), 'Three languages' ),
+ array( 'en; q=0.5, es', array( 'es' => 1, 'en' => '0.5' ), 'Two languages' ),
+ array( 'en, zh;q=0', array( 'en' => 1 ), "It's Chinese to me" ),
+ array( 'es; q=1, pt;q=0.7, it; q=0.6, de; q=0.1, ru;q=0', array( 'es' => '1', 'pt' => '0.7', 'it' => '0.6', 'de' => '0.1' ), 'Preference for romance languages' ),
+ array( 'en-gb, en-us; q=1', array( 'en-gb' => 1, 'en-us' => '1' ), 'Two equally prefered English variants' ),
+ );
+ }
+
+ /**
+ * @dataProvider languageProvider
+ */
+ function testAcceptLang($acceptLanguageHeader, $expectedLanguages, $description) {
+ $_SERVER = array( 'HTTP_ACCEPT_LANGUAGE' => $acceptLanguageHeader );
+ $request = new WebRequest();
+ $this->assertSame( $request->getAcceptLang(), $expectedLanguages, $description);
+ }
}
diff --git a/tests/phpunit/includes/WikiPageTest.php b/tests/phpunit/includes/WikiPageTest.php
new file mode 100644
index 00000000..0e1e1ce8
--- /dev/null
+++ b/tests/phpunit/includes/WikiPageTest.php
@@ -0,0 +1,784 @@
+<?php
+/**
+* @group Database
+* ^--- important, causes temporary tables to be used instead of the real database
+* @group medium
+**/
+
+class WikiPageTest extends MediaWikiLangTestCase {
+
+ var $pages_to_delete;
+
+ function __construct( $name = null, array $data = array(), $dataName = '' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->tablesUsed = array_merge ( $this->tablesUsed,
+ array( 'page',
+ 'revision',
+ 'text',
+
+ 'recentchanges',
+ 'logging',
+
+ 'page_props',
+ 'pagelinks',
+ 'categorylinks',
+ 'langlinks',
+ 'externallinks',
+ 'imagelinks',
+ 'templatelinks',
+ 'iwlinks' ) );
+ }
+
+ public function setUp() {
+ parent::setUp();
+ $this->pages_to_delete = array();
+ }
+
+ public function tearDown() {
+ foreach ( $this->pages_to_delete as $p ) {
+ /* @var $p WikiPage */
+
+ try {
+ if ( $p->exists() ) {
+ $p->doDeleteArticle( "testing done." );
+ }
+ } catch ( MWException $ex ) {
+ // fail silently
+ }
+ }
+ parent::tearDown();
+ }
+
+ protected function newPage( $title ) {
+ if ( is_string( $title ) ) $title = Title::newFromText( $title );
+
+ $p = new WikiPage( $title );
+
+ $this->pages_to_delete[] = $p;
+
+ return $p;
+ }
+
+ protected function createPage( $page, $text, $model = null ) {
+ if ( is_string( $page ) ) $page = Title::newFromText( $page );
+ if ( $page instanceof Title ) $page = $this->newPage( $page );
+
+ $page->doEdit( $text, "testing", EDIT_NEW );
+
+ return $page;
+ }
+
+ public function testDoEdit() {
+ $title = Title::newFromText( "WikiPageTest_testDoEdit" );
+
+ $page = $this->newPage( $title );
+
+ $text = "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
+ . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.";
+
+ $page->doEdit( $text, "testing 1" );
+
+ $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
+ $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
+
+ $id = $page->getId();
+
+ # ------------------------
+ $page = new WikiPage( $title );
+
+ $retrieved = $page->getText();
+ $this->assertEquals( $text, $retrieved, 'retrieved text doesn\'t equal original' );
+
+ # ------------------------
+ $text = "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
+ . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.";
+
+ $page->doEdit( $text, "testing 2" );
+
+ # ------------------------
+ $page = new WikiPage( $title );
+
+ $retrieved = $page->getText();
+ $this->assertEquals( $text, $retrieved, 'retrieved text doesn\'t equal original' );
+
+ # ------------------------
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+ $n = $res->numRows();
+ $res->free();
+
+ $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
+ }
+
+ public function testDoQuickEdit() {
+ global $wgUser;
+
+ $page = $this->createPage( "WikiPageTest_testDoQuickEdit", "original text" );
+
+ $text = "quick text";
+ $page->doQuickEdit( $text, $wgUser, "testing q" );
+
+ # ---------------------
+ $page = new WikiPage( $page->getTitle() );
+ $this->assertEquals( $text, $page->getText() );
+ }
+
+ public function testDoDeleteArticle() {
+ $page = $this->createPage( "WikiPageTest_testDoDeleteArticle", "[[original text]] foo" );
+ $id = $page->getId();
+
+ $page->doDeleteArticle( "testing deletion" );
+
+ $this->assertFalse( $page->exists(), "WikiPage::exists should return false after page was deleted" );
+ $this->assertFalse( $page->getText(), "WikiPage::getText should return false after page was deleted" );
+
+ $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
+ $this->assertFalse( $t->exists(), "Title::exists should return false after page was deleted" );
+
+ # ------------------------
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+ $n = $res->numRows();
+ $res->free();
+
+ $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
+ }
+
+ public function testDoDeleteUpdates() {
+ $page = $this->createPage( "WikiPageTest_testDoDeleteArticle", "[[original text]] foo" );
+ $id = $page->getId();
+
+ $page->doDeleteUpdates( $id );
+
+ # ------------------------
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );
+ $n = $res->numRows();
+ $res->free();
+
+ $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
+ }
+
+ public function testGetRevision() {
+ $page = $this->newPage( "WikiPageTest_testGetRevision" );
+
+ $rev = $page->getRevision();
+ $this->assertNull( $rev );
+
+ # -----------------
+ $this->createPage( $page, "some text" );
+
+ $rev = $page->getRevision();
+
+ $this->assertEquals( $page->getLatest(), $rev->getId() );
+ $this->assertEquals( "some text", $rev->getText() );
+ }
+
+ public function testGetText() {
+ $page = $this->newPage( "WikiPageTest_testGetText" );
+
+ $text = $page->getText();
+ $this->assertFalse( $text );
+
+ # -----------------
+ $this->createPage( $page, "some text" );
+
+ $text = $page->getText();
+ $this->assertEquals( "some text", $text );
+ }
+
+ public function testGetRawText() {
+ $page = $this->newPage( "WikiPageTest_testGetRawText" );
+
+ $text = $page->getRawText();
+ $this->assertFalse( $text );
+
+ # -----------------
+ $this->createPage( $page, "some text" );
+
+ $text = $page->getRawText();
+ $this->assertEquals( "some text", $text );
+ }
+
+
+ public function testExists() {
+ $page = $this->newPage( "WikiPageTest_testExists" );
+ $this->assertFalse( $page->exists() );
+
+ # -----------------
+ $this->createPage( $page, "some text" );
+ $this->assertTrue( $page->exists() );
+
+ $page = new WikiPage( $page->getTitle() );
+ $this->assertTrue( $page->exists() );
+
+ # -----------------
+ $page->doDeleteArticle( "done testing" );
+ $this->assertFalse( $page->exists() );
+
+ $page = new WikiPage( $page->getTitle() );
+ $this->assertFalse( $page->exists() );
+ }
+
+ public function dataHasViewableContent() {
+ return array(
+ array( 'WikiPageTest_testHasViewableContent', false, true ),
+ array( 'Special:WikiPageTest_testHasViewableContent', false ),
+ array( 'MediaWiki:WikiPageTest_testHasViewableContent', false ),
+ array( 'Special:Userlogin', true ),
+ array( 'MediaWiki:help', true ),
+ );
+ }
+
+ /**
+ * @dataProvider dataHasViewableContent
+ */
+ public function testHasViewableContent( $title, $viewable, $create = false ) {
+ $page = $this->newPage( $title );
+ $this->assertEquals( $viewable, $page->hasViewableContent() );
+
+ if ( $create ) {
+ $this->createPage( $page, "some text" );
+ $this->assertTrue( $page->hasViewableContent() );
+
+ $page = new WikiPage( $page->getTitle() );
+ $this->assertTrue( $page->hasViewableContent() );
+ }
+ }
+
+ public function dataGetRedirectTarget() {
+ return array(
+ array( 'WikiPageTest_testGetRedirectTarget_1', "hello world", null ),
+ array( 'WikiPageTest_testGetRedirectTarget_2', "#REDIRECT [[hello world]]", "Hello world" ),
+ );
+ }
+
+ /**
+ * @dataProvider dataGetRedirectTarget
+ */
+ public function testGetRedirectTarget( $title, $text, $target ) {
+ $page = $this->createPage( $title, $text );
+
+ # now, test the actual redirect
+ $t = $page->getRedirectTarget();
+ $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
+ }
+
+ /**
+ * @dataProvider dataGetRedirectTarget
+ */
+ public function testIsRedirect( $title, $text, $target ) {
+ $page = $this->createPage( $title, $text );
+ $this->assertEquals( !is_null( $target ), $page->isRedirect() );
+ }
+
+ public function dataIsCountable() {
+ return array(
+
+ // any
+ array( 'WikiPageTest_testIsCountable',
+ '',
+ 'any',
+ true
+ ),
+ array( 'WikiPageTest_testIsCountable',
+ 'Foo',
+ 'any',
+ true
+ ),
+
+ // comma
+ array( 'WikiPageTest_testIsCountable',
+ 'Foo',
+ 'comma',
+ false
+ ),
+ array( 'WikiPageTest_testIsCountable',
+ 'Foo, bar',
+ 'comma',
+ true
+ ),
+
+ // link
+ array( 'WikiPageTest_testIsCountable',
+ 'Foo',
+ 'link',
+ false
+ ),
+ array( 'WikiPageTest_testIsCountable',
+ 'Foo [[bar]]',
+ 'link',
+ true
+ ),
+
+ // redirects
+ array( 'WikiPageTest_testIsCountable',
+ '#REDIRECT [[bar]]',
+ 'any',
+ false
+ ),
+ array( 'WikiPageTest_testIsCountable',
+ '#REDIRECT [[bar]]',
+ 'comma',
+ false
+ ),
+ array( 'WikiPageTest_testIsCountable',
+ '#REDIRECT [[bar]]',
+ 'link',
+ false
+ ),
+
+ // not a content namespace
+ array( 'Talk:WikiPageTest_testIsCountable',
+ 'Foo',
+ 'any',
+ false
+ ),
+ array( 'Talk:WikiPageTest_testIsCountable',
+ 'Foo, bar',
+ 'comma',
+ false
+ ),
+ array( 'Talk:WikiPageTest_testIsCountable',
+ 'Foo [[bar]]',
+ 'link',
+ false
+ ),
+
+ // not a content namespace, different model
+ array( 'MediaWiki:WikiPageTest_testIsCountable.js',
+ 'Foo',
+ 'any',
+ false
+ ),
+ array( 'MediaWiki:WikiPageTest_testIsCountable.js',
+ 'Foo, bar',
+ 'comma',
+ false
+ ),
+ array( 'MediaWiki:WikiPageTest_testIsCountable.js',
+ 'Foo [[bar]]',
+ 'link',
+ false
+ ),
+ );
+ }
+
+
+ /**
+ * @dataProvider dataIsCountable
+ */
+ public function testIsCountable( $title, $text, $mode, $expected ) {
+ global $wgArticleCountMethod;
+
+ $old = $wgArticleCountMethod;
+ $wgArticleCountMethod = $mode;
+
+ $page = $this->createPage( $title, $text );
+ $editInfo = $page->prepareTextForEdit( $page->getText() );
+
+ $v = $page->isCountable();
+ $w = $page->isCountable( $editInfo );
+ $wgArticleCountMethod = $old;
+
+ $this->assertEquals( $expected, $v, "isCountable( null ) returned unexpected value " . var_export( $v, true )
+ . " instead of " . var_export( $expected, true ) . " in mode `$mode` for text \"$text\"" );
+
+ $this->assertEquals( $expected, $w, "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
+ . " instead of " . var_export( $expected, true ) . " in mode `$mode` for text \"$text\"" );
+ }
+
+ public function dataGetParserOutput() {
+ return array(
+ array("hello ''world''\n", "<p>hello <i>world</i></p>"),
+ // @todo: more...?
+ );
+ }
+
+ /**
+ * @dataProvider dataGetParserOutput
+ */
+ public function testGetParserOutput( $text, $expectedHtml ) {
+ $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text );
+
+ $opt = new ParserOptions();
+ $po = $page->getParserOutput( $opt );
+ $text = $po->getText();
+
+ $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
+ $text = preg_replace( '!\s*(</p>)!sm', '\1', $text ); # don't let tidy confuse us
+
+ $this->assertEquals( $expectedHtml, $text );
+ return $po;
+ }
+
+ static $sections =
+
+ "Intro
+
+== stuff ==
+hello world
+
+== test ==
+just a test
+
+== foo ==
+more stuff
+";
+
+
+ public function dataReplaceSection() {
+ return array(
+ array( 'WikiPageTest_testReplaceSection',
+ WikiPageTest::$sections,
+ "0",
+ "No more",
+ null,
+ trim( preg_replace( '/^Intro/sm', 'No more', WikiPageTest::$sections ) )
+ ),
+ array( 'WikiPageTest_testReplaceSection',
+ WikiPageTest::$sections,
+ "",
+ "No more",
+ null,
+ "No more"
+ ),
+ array( 'WikiPageTest_testReplaceSection',
+ WikiPageTest::$sections,
+ "2",
+ "== TEST ==\nmore fun",
+ null,
+ trim( preg_replace( '/^== test ==.*== foo ==/sm', "== TEST ==\nmore fun\n\n== foo ==", WikiPageTest::$sections ) )
+ ),
+ array( 'WikiPageTest_testReplaceSection',
+ WikiPageTest::$sections,
+ "8",
+ "No more",
+ null,
+ trim( WikiPageTest::$sections )
+ ),
+ array( 'WikiPageTest_testReplaceSection',
+ WikiPageTest::$sections,
+ "new",
+ "No more",
+ "New",
+ trim( WikiPageTest::$sections ) . "\n\n== New ==\n\nNo more"
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataReplaceSection
+ */
+ public function testReplaceSection( $title, $text, $section, $with, $sectionTitle, $expected ) {
+ $page = $this->createPage( $title, $text );
+ $text = $page->replaceSection( $section, $with, $sectionTitle );
+ $text = trim( $text );
+
+ $this->assertEquals( $expected, $text );
+ }
+
+ /* @todo FIXME: fix this!
+ public function testGetUndoText() {
+ global $wgDiff3;
+
+ wfSuppressWarnings();
+ $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
+ wfRestoreWarnings();
+
+ if( !$haveDiff3 ) {
+ $this->markTestSkipped( "diff3 not installed or not found" );
+ return;
+ }
+
+ $text = "one";
+ $page = $this->createPage( "WikiPageTest_testGetUndoText", $text );
+ $rev1 = $page->getRevision();
+
+ $text .= "\n\ntwo";
+ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section two");
+ $rev2 = $page->getRevision();
+
+ $text .= "\n\nthree";
+ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section three");
+ $rev3 = $page->getRevision();
+
+ $text .= "\n\nfour";
+ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section four");
+ $rev4 = $page->getRevision();
+
+ $text .= "\n\nfive";
+ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section five");
+ $rev5 = $page->getRevision();
+
+ $text .= "\n\nsix";
+ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section six");
+ $rev6 = $page->getRevision();
+
+ $undo6 = $page->getUndoText( $rev6 );
+ if ( $undo6 === false ) $this->fail( "getUndoText failed for rev6" );
+ $this->assertEquals( "one\n\ntwo\n\nthree\n\nfour\n\nfive", $undo6 );
+
+ $undo3 = $page->getUndoText( $rev4, $rev2 );
+ if ( $undo3 === false ) $this->fail( "getUndoText failed for rev4..rev2" );
+ $this->assertEquals( "one\n\ntwo\n\nfive", $undo3 );
+
+ $undo2 = $page->getUndoText( $rev2 );
+ if ( $undo2 === false ) $this->fail( "getUndoText failed for rev2" );
+ $this->assertEquals( "one\n\nfive", $undo2 );
+ }
+ */
+
+ /**
+ * @todo FIXME: this is a better rollback test than the one below, but it keeps failing in jenkins for some reason.
+ */
+ public function broken_testDoRollback() {
+ $admin = new User();
+ $admin->setName("Admin");
+
+ $text = "one";
+ $page = $this->newPage( "WikiPageTest_testDoRollback" );
+ $page->doEdit( $text, "section one", EDIT_NEW, false, $admin );
+
+ $user1 = new User();
+ $user1->setName( "127.0.1.11" );
+ $text .= "\n\ntwo";
+ $page = new WikiPage( $page->getTitle() );
+ $page->doEdit( $text, "adding section two", 0, false, $user1 );
+
+ $user2 = new User();
+ $user2->setName( "127.0.2.13" );
+ $text .= "\n\nthree";
+ $page = new WikiPage( $page->getTitle() );
+ $page->doEdit( $text, "adding section three", 0, false, $user2 );
+
+ # we are having issues with doRollback spuriously failing. apparently the last revision somehow goes missing
+ # or not committed under some circumstances. so, make sure the last revision has the right user name.
+ $dbr = wfGetDB( DB_SLAVE );
+ $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
+
+ $page = new WikiPage( $page->getTitle() );
+ $rev3 = $page->getRevision();
+ $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
+
+ $rev2 = $rev3->getPrevious();
+ $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
+
+ $rev1 = $rev2->getPrevious();
+ $this->assertEquals( 'Admin', $rev1->getUserText() );
+
+ # now, try the actual rollback
+ $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
+ $token = $admin->getEditToken( array( $page->getTitle()->getPrefixedText(), $user2->getName() ), null );
+ $errors = $page->doRollback( $user2->getName(), "testing revert", $token, false, $details, $admin );
+
+ if ( $errors ) {
+ $this->fail( "Rollback failed:\n" . print_r( $errors, true ) . ";\n" . print_r( $details, true ) );
+ }
+
+ $page = new WikiPage( $page->getTitle() );
+ $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(), "rollback did not revert to the correct revision" );
+ $this->assertEquals( "one\n\ntwo", $page->getText() );
+ }
+
+ /**
+ * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason.
+ */
+ public function testDoRollback() {
+ $admin = new User();
+ $admin->setName("Admin");
+
+ $text = "one";
+ $page = $this->newPage( "WikiPageTest_testDoRollback" );
+ $page->doEdit( $text, "section one", EDIT_NEW, false, $admin );
+ $rev1 = $page->getRevision();
+
+ $user1 = new User();
+ $user1->setName( "127.0.1.11" );
+ $text .= "\n\ntwo";
+ $page = new WikiPage( $page->getTitle() );
+ $page->doEdit( $text, "adding section two", 0, false, $user1 );
+
+ # now, try the rollback
+ $admin->addGroup( "sysop" ); #XXX: make the test user a sysop...
+ $token = $admin->getEditToken( array( $page->getTitle()->getPrefixedText(), $user1->getName() ), null );
+ $errors = $page->doRollback( $user1->getName(), "testing revert", $token, false, $details, $admin );
+
+ if ( $errors ) {
+ $this->fail( "Rollback failed:\n" . print_r( $errors, true ) . ";\n" . print_r( $details, true ) );
+ }
+
+ $page = new WikiPage( $page->getTitle() );
+ $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(), "rollback did not revert to the correct revision" );
+ $this->assertEquals( "one", $page->getText() );
+ }
+
+ public function dataGetAutosummary( ) {
+ return array(
+ array(
+ 'Hello there, world!',
+ '#REDIRECT [[Foo]]',
+ 0,
+ '/^Redirected page .*Foo/'
+ ),
+
+ array(
+ null,
+ 'Hello world!',
+ EDIT_NEW,
+ '/^Created page .*Hello/'
+ ),
+
+ array(
+ 'Hello there, world!',
+ '',
+ 0,
+ '/^Blanked/'
+ ),
+
+ array(
+ 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
+ labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et
+ ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.',
+ 'Hello world!',
+ 0,
+ '/^Replaced .*Hello/'
+ ),
+
+ array(
+ 'foo',
+ 'bar',
+ 0,
+ '/^$/'
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataGetAutoSummary
+ */
+ public function testGetAutosummary( $old, $new, $flags, $expected ) {
+ $page = $this->newPage( "WikiPageTest_testGetAutosummary" );
+
+ $summary = $page->getAutosummary( $old, $new, $flags );
+
+ $this->assertTrue( (bool)preg_match( $expected, $summary ), "Autosummary didn't match expected pattern $expected: $summary" );
+ }
+
+ public function dataGetAutoDeleteReason( ) {
+ return array(
+ array(
+ array(),
+ false,
+ false
+ ),
+
+ array(
+ array(
+ array( "first edit", null ),
+ ),
+ "/first edit.*only contributor/",
+ false
+ ),
+
+ array(
+ array(
+ array( "first edit", null ),
+ array( "second edit", null ),
+ ),
+ "/second edit.*only contributor/",
+ true
+ ),
+
+ array(
+ array(
+ array( "first edit", "127.0.2.22" ),
+ array( "second edit", "127.0.3.33" ),
+ ),
+ "/second edit/",
+ true
+ ),
+
+ array(
+ array(
+ array( "first edit: "
+ . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
+ . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. "
+ . "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea "
+ . "takimata sanctus est Lorem ipsum dolor sit amet.'", null ),
+ ),
+ '/first edit:.*\.\.\."/',
+ false
+ ),
+
+ array(
+ array(
+ array( "first edit", "127.0.2.22" ),
+ array( "", "127.0.3.33" ),
+ ),
+ "/before blanking.*first edit/",
+ true
+ ),
+
+ );
+ }
+
+ /**
+ * @dataProvider dataGetAutoDeleteReason
+ */
+ public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
+ global $wgUser;
+
+ $page = $this->newPage( "WikiPageTest_testGetAutoDeleteReason" );
+
+ $c = 1;
+
+ foreach ( $edits as $edit ) {
+ $user = new User();
+
+ if ( !empty( $edit[1] ) ) $user->setName( $edit[1] );
+ else $user = $wgUser;
+
+ $page->doEdit( $edit[0], "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
+
+ $c += 1;
+ }
+
+ $reason = $page->getAutoDeleteReason( $hasHistory );
+
+ if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) $this->assertEquals( $expectedResult, $reason );
+ else $this->assertTrue( (bool)preg_match( $expectedResult, $reason ), "Autosummary didn't match expected pattern $expectedResult: $reason" );
+
+ $this->assertEquals( $expectedHistory, $hasHistory, "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
+
+ $page->doDeleteArticle( "done" );
+ }
+
+ public function dataPreSaveTransform() {
+ return array(
+ array( 'hello this is ~~~',
+ "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
+ ),
+ array( 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
+ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataPreSaveTransform
+ */
+ public function testPreSaveTransform( $text, $expected ) {
+ $this->hideDeprecated( 'WikiPage::preSaveTransform' );
+ $user = new User();
+ $user->setName("127.0.0.1");
+
+ $page = $this->newPage( "WikiPageTest_testPreloadTransform" );
+ $text = $page->preSaveTransform( $text, $user );
+
+ $this->assertEquals( $expected, $text );
+ }
+
+}
+
diff --git a/tests/phpunit/includes/XmlTest.php b/tests/phpunit/includes/XmlTest.php
index 1d9361f2..93ed3dc7 100644
--- a/tests/phpunit/includes/XmlTest.php
+++ b/tests/phpunit/includes/XmlTest.php
@@ -193,52 +193,6 @@ class XmlTest extends MediaWikiTestCase {
);
}
- function testNamespaceSelector() {
- $this->assertEquals(
- '<select class="namespaceselector" id="namespace" name="namespace">' . "\n" .
-'<option value="0">(Main)</option>' . "\n" .
-'<option value="1">Talk</option>' . "\n" .
-'<option value="2">User</option>' . "\n" .
-'<option value="3">User talk</option>' . "\n" .
-'<option value="4">MyWiki</option>' . "\n" .
-'<option value="5">MyWiki Talk</option>' . "\n" .
-'<option value="6">File</option>' . "\n" .
-'<option value="7">File talk</option>' . "\n" .
-'<option value="8">MediaWiki</option>' . "\n" .
-'<option value="9">MediaWiki talk</option>' . "\n" .
-'<option value="10">Template</option>' . "\n" .
-'<option value="11">Template talk</option>' . "\n" .
-'<option value="100">Custom</option>' . "\n" .
-'<option value="101">Custom talk</option>' . "\n" .
-'</select>',
- Xml::namespaceSelector(),
- 'Basic namespace selector without custom options'
- );
- $this->assertEquals(
- '<label for="namespace">Select a namespace:</label>' .
-'&#160;<select class="namespaceselector" id="namespace" name="myname">' . "\n" .
-'<option value="all">all</option>' . "\n" .
-'<option value="0">(Main)</option>' . "\n" .
-'<option value="1">Talk</option>' . "\n" .
-'<option value="2" selected="">User</option>' . "\n" .
-'<option value="3">User talk</option>' . "\n" .
-'<option value="4">MyWiki</option>' . "\n" .
-'<option value="5">MyWiki Talk</option>' . "\n" .
-'<option value="6">File</option>' . "\n" .
-'<option value="7">File talk</option>' . "\n" .
-'<option value="8">MediaWiki</option>' . "\n" .
-'<option value="9">MediaWiki talk</option>' . "\n" .
-'<option value="10">Template</option>' . "\n" .
-'<option value="11">Template talk</option>' . "\n" .
-'<option value="100">Custom</option>' . "\n" .
-'<option value="101">Custom talk</option>' . "\n" .
-'</select>',
- Xml::namespaceSelector( $selected = '2', $all = 'all', $element_name = 'myname', $label = 'Select a namespace:' ),
- 'Basic namespace selector with custom values'
- );
- }
-
-
#
# textarea
#
@@ -297,6 +251,15 @@ class XmlTest extends MediaWikiTestCase {
);
}
+ function testLanguageSelector() {
+ $select = Xml::languageSelector( 'en', true, null,
+ array( 'id' => 'testlang' ), wfMessage( 'yourlanguage' ) );
+ $this->assertEquals(
+ '<label for="testlang">Language:</label>',
+ $select[0]
+ );
+ }
+
#
# JS
#
diff --git a/tests/phpunit/includes/ZipDirectoryReaderTest.php b/tests/phpunit/includes/ZipDirectoryReaderTest.php
index f7ca59e2..d90a6950 100644
--- a/tests/phpunit/includes/ZipDirectoryReaderTest.php
+++ b/tests/phpunit/includes/ZipDirectoryReaderTest.php
@@ -4,7 +4,7 @@ class ZipDirectoryReaderTest extends MediaWikiTestCase {
var $zipDir, $entries;
function setUp() {
- $this->zipDir = dirname( __FILE__ ) . '/../data/zip';
+ $this->zipDir = __DIR__ . '/../data/zip';
}
function zipCallback( $entry ) {
diff --git a/tests/phpunit/includes/api/ApiBlockTest.php b/tests/phpunit/includes/api/ApiBlockTest.php
index b95d8214..5dfceee8 100644
--- a/tests/phpunit/includes/api/ApiBlockTest.php
+++ b/tests/phpunit/includes/api/ApiBlockTest.php
@@ -1,6 +1,7 @@
<?php
/**
+ * @group API
* @group Database
*/
class ApiBlockTest extends ApiTestCase {
@@ -32,8 +33,6 @@ class ApiBlockTest extends ApiTestCase {
* Root cause is https://gerrit.wikimedia.org/r/3434
* Which made the Block/Unblock API to actually verify the token
* previously always considered valid (bug 34212).
- *
- * @group Broken
*/
function testMakeNormalBlock() {
@@ -57,7 +56,7 @@ class ApiBlockTest extends ApiTestCase {
'action' => 'block',
'user' => 'UTApiBlockee',
'reason' => 'Some reason',
- 'token' => $pageinfo['blocktoken'] ), $data, false, self::$users['sysop']->user );
+ 'token' => $pageinfo['blocktoken'] ), null, false, self::$users['sysop']->user );
$block = Block::newFromTarget('UTApiBlockee');
@@ -69,4 +68,50 @@ class ApiBlockTest extends ApiTestCase {
}
+ /**
+ * @dataProvider provideBlockUnblockAction
+ */
+ function testGetTokenUsingABlockingAction( $action ) {
+ $data = $this->doApiRequest(
+ array(
+ 'action' => $action,
+ 'user' => 'UTApiBlockee',
+ 'gettoken' => '' ),
+ null,
+ false,
+ self::$users['sysop']->user
+ );
+ $this->assertEquals( 34, strlen( $data[0][$action]["{$action}token"] ) );
+ }
+
+ /**
+ * Attempting to block without a token should give a UsageException with
+ * error message:
+ * "The token parameter must be set"
+ *
+ * @dataProvider provideBlockUnblockAction
+ * @expectedException UsageException
+ */
+ function testBlockingActionWithNoToken( $action ) {
+ $this->doApiRequest(
+ array(
+ 'action' => $action,
+ 'user' => 'UTApiBlockee',
+ 'reason' => 'Some reason',
+ ),
+ null,
+ false,
+ self::$users['sysop']->user
+ );
+ }
+
+ /**
+ * Just provide the 'block' and 'unblock' action to test both API calls
+ */
+ function provideBlockUnblockAction() {
+ return array(
+ array( 'block' ),
+ array( 'unblock' ),
+ );
+ }
}
diff --git a/tests/phpunit/includes/api/ApiEditPageTest.php b/tests/phpunit/includes/api/ApiEditPageTest.php
new file mode 100644
index 00000000..5297d6da
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiEditPageTest.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Tests for MediaWiki api.php?action=edit.
+ *
+ * @author Daniel Kinzler
+ *
+ * @group API
+ * @group Database
+ */
+class ApiEditPageTest extends ApiTestCase {
+
+ function setUp() {
+ parent::setUp();
+ $this->doLogin();
+ }
+
+ function testEdit( ) {
+ $name = 'ApiEditPageTest_testEdit';
+
+ // -- test new page --------------------------------------------
+ $apiResult = $this->doApiRequestWithToken( array(
+ 'action' => 'edit',
+ 'title' => $name,
+ 'text' => 'some text', ) );
+ $apiResult = $apiResult[0];
+
+ # Validate API result data
+ $this->assertArrayHasKey( 'edit', $apiResult );
+ $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+ $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+
+ $this->assertArrayHasKey( 'new', $apiResult['edit'] );
+ $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
+
+ $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
+
+ // -- test existing page, no change ----------------------------
+ $data = $this->doApiRequestWithToken( array(
+ 'action' => 'edit',
+ 'title' => $name,
+ 'text' => 'some text', ) );
+
+ $this->assertEquals( 'Success', $data[0]['edit']['result'] );
+
+ $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
+ $this->assertArrayHasKey( 'nochange', $data[0]['edit'] );
+
+ // -- test existing page, with change --------------------------
+ $data = $this->doApiRequestWithToken( array(
+ 'action' => 'edit',
+ 'title' => $name,
+ 'text' => 'different text' ) );
+
+ $this->assertEquals( 'Success', $data[0]['edit']['result'] );
+
+ $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
+ $this->assertArrayNotHasKey( 'nochange', $data[0]['edit'] );
+
+ $this->assertArrayHasKey( 'oldrevid', $data[0]['edit'] );
+ $this->assertArrayHasKey( 'newrevid', $data[0]['edit'] );
+ $this->assertNotEquals(
+ $data[0]['edit']['newrevid'],
+ $data[0]['edit']['oldrevid'],
+ "revision id should change after edit"
+ );
+ }
+
+ function testEditAppend() {
+ $this->markTestIncomplete( "not yet implemented" );
+ }
+
+ function testEditSection() {
+ $this->markTestIncomplete( "not yet implemented" );
+ }
+
+ function testUndo() {
+ $this->markTestIncomplete( "not yet implemented" );
+ }
+
+ function testEditNonText() {
+ $this->markTestIncomplete( "not yet implemented" );
+ }
+}
diff --git a/tests/phpunit/includes/api/ApiOptionsTest.php b/tests/phpunit/includes/api/ApiOptionsTest.php
new file mode 100644
index 00000000..5243fca1
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiOptionsTest.php
@@ -0,0 +1,276 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ */
+class ApiOptionsTest extends MediaWikiLangTestCase {
+
+ private $mTested, $mApiMainMock, $mUserMock, $mContext, $mSession;
+
+ private $mOldGetPreferencesHooks = false;
+
+ private static $Success = array( 'options' => 'success' );
+
+ function setUp() {
+ parent::setUp();
+
+ $this->mUserMock = $this->getMockBuilder( 'User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->mApiMainMock = $this->getMockBuilder( 'ApiBase' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ // Set up groups
+ $this->mUserMock->expects( $this->any() )
+ ->method( 'getEffectiveGroups' )->will( $this->returnValue( array( '*', 'user')) );
+
+ // Create a new context
+ $this->mContext = new DerivativeContext( new RequestContext() );
+ $this->mContext->getContext()->setTitle( Title::newFromText( 'Test' ) );
+ $this->mContext->setUser( $this->mUserMock );
+
+ $this->mApiMainMock->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->mContext ) );
+
+ $this->mApiMainMock->expects( $this->any() )
+ ->method( 'getResult' )
+ ->will( $this->returnValue( new ApiResult( $this->mApiMainMock ) ) );
+
+
+ // Empty session
+ $this->mSession = array();
+
+ $this->mTested = new ApiOptions( $this->mApiMainMock, 'options' );
+
+ global $wgHooks;
+ if ( !isset( $wgHooks['GetPreferences'] ) ) {
+ $wgHooks['GetPreferences'] = array();
+ }
+ $this->mOldGetPreferencesHooks = $wgHooks['GetPreferences'];
+ $wgHooks['GetPreferences'][] = array( $this, 'hookGetPreferences' );
+ }
+
+ public function tearDown() {
+ global $wgHooks;
+
+ if ( $this->mOldGetPreferencesHooks !== false ) {
+ $wgHooks['GetPreferences'] = $this->mOldGetPreferencesHooks;
+ $this->mOldGetPreferencesHooks = false;
+ }
+
+ parent::tearDown();
+ }
+
+ public function hookGetPreferences( $user, &$preferences ) {
+ foreach ( array( 'name', 'willBeNull', 'willBeEmpty', 'willBeHappy' ) as $k ) {
+ $preferences[$k] = array(
+ 'type' => 'text',
+ 'section' => 'test',
+ 'label' => '&#160;',
+ );
+ }
+
+ return true;
+ }
+
+ private function getSampleRequest( $custom = array() ) {
+ $request = array(
+ 'token' => '123ABC',
+ 'change' => null,
+ 'optionname' => null,
+ 'optionvalue' => null,
+ );
+ return array_merge( $request, $custom );
+ }
+
+ private function executeQuery( $request ) {
+ $this->mContext->setRequest( new FauxRequest( $request, true, $this->mSession ) );
+ $this->mTested->execute();
+ return $this->mTested->getResult()->getData();
+ }
+
+ /**
+ * @expectedException UsageException
+ */
+ public function testNoToken() {
+ $request = $this->getSampleRequest( array( 'token' => null ) );
+
+ $this->executeQuery( $request );
+ }
+
+ public function testAnon() {
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'isAnon' )
+ ->will( $this->returnValue( true ) );
+
+ try {
+ $request = $this->getSampleRequest();
+
+ $this->executeQuery( $request );
+ } catch ( UsageException $e ) {
+ $this->assertEquals( 'notloggedin', $e->getCodeString() );
+ $this->assertEquals( 'Anonymous users cannot change preferences', $e->getMessage() );
+ return;
+ }
+ $this->fail( "UsageException was not thrown" );
+ }
+
+ public function testNoOptionname() {
+ try {
+ $request = $this->getSampleRequest( array( 'optionvalue' => '1' ) );
+
+ $this->executeQuery( $request );
+ } catch ( UsageException $e ) {
+ $this->assertEquals( 'nooptionname', $e->getCodeString() );
+ $this->assertEquals( 'The optionname parameter must be set', $e->getMessage() );
+ return;
+ }
+ $this->fail( "UsageException was not thrown" );
+ }
+
+ public function testNoChanges() {
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'setOption' );
+
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'saveSettings' );
+
+ try {
+ $request = $this->getSampleRequest();
+
+ $this->executeQuery( $request );
+ } catch ( UsageException $e ) {
+ $this->assertEquals( 'nochanges', $e->getCodeString() );
+ $this->assertEquals( 'No changes were requested', $e->getMessage() );
+ return;
+ }
+ $this->fail( "UsageException was not thrown" );
+ }
+
+ public function testReset() {
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'setOption' );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'saveSettings' );
+
+ $request = $this->getSampleRequest( array( 'reset' => '' ) );
+
+ $response = $this->executeQuery( $request );
+
+ $this->assertEquals( self::$Success, $response );
+ }
+
+ public function testOptionWithValue() {
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'name' ), $this->equalTo( 'value' ) );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'saveSettings' );
+
+ $request = $this->getSampleRequest( array( 'optionname' => 'name', 'optionvalue' => 'value' ) );
+
+ $response = $this->executeQuery( $request );
+
+ $this->assertEquals( self::$Success, $response );
+ }
+
+ public function testOptionResetValue() {
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'name' ), $this->equalTo( null ) );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'saveSettings' );
+
+ $request = $this->getSampleRequest( array( 'optionname' => 'name' ) );
+ $response = $this->executeQuery( $request );
+
+ $this->assertEquals( self::$Success, $response );
+ }
+
+ public function testChange() {
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->at( 1 ) )
+ ->method( 'getOptions' );
+
+ $this->mUserMock->expects( $this->at( 2 ) )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'willBeNull' ), $this->equalTo( null ) );
+
+ $this->mUserMock->expects( $this->at( 3 ) )
+ ->method( 'getOptions' );
+
+ $this->mUserMock->expects( $this->at( 4 ) )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'willBeEmpty' ), $this->equalTo( '' ) );
+
+ $this->mUserMock->expects( $this->at( 5 ) )
+ ->method( 'getOptions' );
+
+ $this->mUserMock->expects( $this->at( 6 ) )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'willBeHappy' ), $this->equalTo( 'Happy' ) );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'saveSettings' );
+
+ $request = $this->getSampleRequest( array( 'change' => 'willBeNull|willBeEmpty=|willBeHappy=Happy' ) );
+
+ $response = $this->executeQuery( $request );
+
+ $this->assertEquals( self::$Success, $response );
+ }
+
+ public function testResetChangeOption() {
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->at( 2 ) )
+ ->method( 'getOptions' );
+
+ $this->mUserMock->expects( $this->at( 3 ) )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'willBeHappy' ), $this->equalTo( 'Happy' ) );
+
+ $this->mUserMock->expects( $this->at( 4 ) )
+ ->method( 'getOptions' );
+
+ $this->mUserMock->expects( $this->at( 5 ) )
+ ->method( 'setOption' )
+ ->with( $this->equalTo( 'name' ), $this->equalTo( 'value' ) );
+
+ $this->mUserMock->expects( $this->once() )
+ ->method( 'saveSettings' );
+
+ $args = array(
+ 'reset' => '',
+ 'change' => 'willBeHappy=Happy',
+ 'optionname' => 'name',
+ 'optionvalue' => 'value'
+ );
+
+ $response = $this->executeQuery( $this->getSampleRequest( $args ) );
+
+ $this->assertEquals( self::$Success, $response );
+ }
+}
diff --git a/tests/phpunit/includes/api/ApiPurgeTest.php b/tests/phpunit/includes/api/ApiPurgeTest.php
index 70c20746..2566c6cd 100644
--- a/tests/phpunit/includes/api/ApiPurgeTest.php
+++ b/tests/phpunit/includes/api/ApiPurgeTest.php
@@ -1,6 +1,7 @@
<?php
/**
+ * @group API
* @group Database
*/
class ApiPurgeTest extends ApiTestCase {
diff --git a/tests/phpunit/includes/api/ApiQueryTest.php b/tests/phpunit/includes/api/ApiQueryTest.php
index ae05a30a..a4b9dc70 100644
--- a/tests/phpunit/includes/api/ApiQueryTest.php
+++ b/tests/phpunit/includes/api/ApiQueryTest.php
@@ -1,6 +1,7 @@
<?php
/**
+ * @group API
* @group Database
*/
class ApiQueryTest extends ApiTestCase {
diff --git a/tests/phpunit/includes/api/ApiTest.php b/tests/phpunit/includes/api/ApiTest.php
index 1d9c3238..c3eacd5b 100644
--- a/tests/phpunit/includes/api/ApiTest.php
+++ b/tests/phpunit/includes/api/ApiTest.php
@@ -1,6 +1,7 @@
<?php
/**
+ * @group API
* @group Database
*/
class ApiTest extends ApiTestCase {
diff --git a/tests/phpunit/includes/api/ApiTestCase.php b/tests/phpunit/includes/api/ApiTestCase.php
index 8801391f..b84292e3 100644
--- a/tests/phpunit/includes/api/ApiTestCase.php
+++ b/tests/phpunit/includes/api/ApiTestCase.php
@@ -1,10 +1,6 @@
<?php
abstract class ApiTestCase extends MediaWikiLangTestCase {
- /**
- * @var Array of ApiTestUser
- */
- public static $users;
protected static $apiUrl;
/**
@@ -23,13 +19,13 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
$wgRequest = new FauxRequest( array() );
self::$users = array(
- 'sysop' => new ApiTestUser(
+ 'sysop' => new TestUser(
'Apitestsysop',
'Api Test Sysop',
'api_test_sysop@example.com',
array( 'sysop' )
),
- 'uploader' => new ApiTestUser(
+ 'uploader' => new TestUser(
'Apitestuser',
'Api Test User',
'api_test_user@example.com',
@@ -43,15 +39,31 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
}
- protected function doApiRequest( $params, $session = null, $appendModule = false, $user = null ) {
+ protected function doApiRequest( Array $params, Array $session = null, $appendModule = false, User $user = null ) {
+ global $wgRequest, $wgUser;
+
if ( is_null( $session ) ) {
- $session = array();
+ # re-use existing global session by default
+ $session = $wgRequest->getSessionArray();
}
- $context = $this->apiContext->newTestContext( $params, $session, $user );
+ # set up global environment
+ if ( $user ) {
+ $wgUser = $user;
+ }
+
+ $wgRequest = new FauxRequest( $params, true, $session );
+ RequestContext::getMain()->setRequest( $wgRequest );
+
+ # set up local environment
+ $context = $this->apiContext->newTestContext( $wgRequest, $wgUser );
+
$module = new ApiMain( $context, true );
+
+ # run it!
$module->execute();
+ # construct result
$results = array(
$module->getResultData(),
$context->getRequest(),
@@ -68,11 +80,17 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
* Add an edit token to the API request
* This is cheating a bit -- we grab a token in the correct format and then add it to the pseudo-session and to the
* request, without actually requesting a "real" edit token
- * @param $params: key-value API params
- * @param $session: session array
- * @param $user String|null A User object for the context
+ * @param $params Array: key-value API params
+ * @param $session Array|null: session array
+ * @param $user User|null A User object for the context
*/
- protected function doApiRequestWithToken( $params, $session, $user = null ) {
+ protected function doApiRequestWithToken( Array $params, Array $session = null, User $user = null ) {
+ global $wgRequest;
+
+ if ( $session === null ) {
+ $session = $wgRequest->getSessionArray();
+ }
+
if ( $session['wsToken'] ) {
// add edit token to fake session
$session['wsEditToken'] = $session['wsToken'];
@@ -97,17 +115,17 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
'lgtoken' => $token,
'lgname' => self::$users['sysop']->username,
'lgpassword' => self::$users['sysop']->password
- ), $data );
+ ), $data[2] );
return $data;
}
- protected function getTokenList( $user ) {
+ protected function getTokenList( $user, $session = null ) {
$data = $this->doApiRequest( array(
'action' => 'query',
'titles' => 'Main Page',
- 'intoken' => 'edit|delete|protect|move|block|unblock',
- 'prop' => 'info' ), false, $user->user );
+ 'intoken' => 'edit|delete|protect|move|block|unblock|watch',
+ 'prop' => 'info' ), $session, false, $user->user );
return $data;
}
}
@@ -154,14 +172,13 @@ class ApiTestContext extends RequestContext {
/**
* Returns a DerivativeContext with the request variables in place
*
- * @param $params Array key-value API params
- * @param $session Array session data
+ * @param $request WebRequest request object including parameters and session
* @param $user User or null
* @return DerivativeContext
*/
- public function newTestContext( $params, $session, $user = null ) {
+ public function newTestContext( WebRequest $request, User $user = null ) {
$context = new DerivativeContext( $this );
- $context->setRequest( new FauxRequest( $params, true, $session ) );
+ $context->setRequest( $request );
if ( $user !== null ) {
$context->setUser( $user );
}
diff --git a/tests/phpunit/includes/api/ApiUploadTest.php b/tests/phpunit/includes/api/ApiUploadTest.php
index 7a700326..642fed05 100644
--- a/tests/phpunit/includes/api/ApiUploadTest.php
+++ b/tests/phpunit/includes/api/ApiUploadTest.php
@@ -1,6 +1,7 @@
<?php
/**
+ * @group API
* @group Database
*/
diff --git a/tests/phpunit/includes/api/ApiWatchTest.php b/tests/phpunit/includes/api/ApiWatchTest.php
index b7803746..d2e98152 100644
--- a/tests/phpunit/includes/api/ApiWatchTest.php
+++ b/tests/phpunit/includes/api/ApiWatchTest.php
@@ -1,8 +1,9 @@
<?php
/**
+ * @group API
* @group Database
- * @todo This test suite is severly broken and need a full review
+ * @todo This test suite is severly broken and need a full review
*/
class ApiWatchTest extends ApiTestCase {
@@ -10,28 +11,28 @@ class ApiWatchTest extends ApiTestCase {
parent::setUp();
$this->doLogin();
}
-
+
function getTokens() {
- return $this->getTokenList( self::$users['sysop'] );
+ $data = $this->getTokenList( self::$users['sysop'] );
+
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key];
+
+ return $pageinfo;
}
/**
- * @group Broken
*/
function testWatchEdit() {
-
- $data = $this->getTokens();
-
- $keys = array_keys( $data[0]['query']['pages'] );
- $key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key];
+ $pageinfo = $this->getTokens();
$data = $this->doApiRequest( array(
'action' => 'edit',
'title' => 'UTPage',
'text' => 'new text',
'token' => $pageinfo['edittoken'],
- 'watchlist' => 'watch' ), $data );
+ 'watchlist' => 'watch' ) );
$this->assertArrayHasKey( 'edit', $data[0] );
$this->assertArrayHasKey( 'result', $data[0]['edit'] );
$this->assertEquals( 'Success', $data[0]['edit']['result'] );
@@ -41,13 +42,14 @@ class ApiWatchTest extends ApiTestCase {
/**
* @depends testWatchEdit
- * @group Broken
*/
function testWatchClear() {
-
+
+ $pageinfo = $this->getTokens();
+
$data = $this->doApiRequest( array(
'action' => 'query',
- 'list' => 'watchlist' ), $data );
+ 'list' => 'watchlist' ) );
if ( isset( $data[0]['query']['watchlist'] ) ) {
$wl = $data[0]['query']['watchlist'];
@@ -56,7 +58,8 @@ class ApiWatchTest extends ApiTestCase {
$data = $this->doApiRequest( array(
'action' => 'watch',
'title' => $page['title'],
- 'unwatch' => true ), $data );
+ 'unwatch' => true,
+ 'token' => $pageinfo['watchtoken'] ) );
}
}
$data = $this->doApiRequest( array(
@@ -70,22 +73,17 @@ class ApiWatchTest extends ApiTestCase {
}
/**
- * @group Broken
- */
+ */
function testWatchProtect() {
-
- $data = $this->getTokens();
-
- $keys = array_keys( $data[0]['query']['pages'] );
- $key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key];
+
+ $pageinfo = $this->getTokens();
$data = $this->doApiRequest( array(
'action' => 'protect',
'token' => $pageinfo['protecttoken'],
'title' => 'UTPage',
'protections' => 'edit=sysop',
- 'watchlist' => 'unwatch' ), $data );
+ 'watchlist' => 'unwatch' ) );
$this->assertArrayHasKey( 'protect', $data[0] );
$this->assertArrayHasKey( 'protections', $data[0]['protect'] );
@@ -94,21 +92,20 @@ class ApiWatchTest extends ApiTestCase {
}
/**
- * @group Broken
*/
function testGetRollbackToken() {
-
- $data = $this->getTokens();
-
+
+ $pageinfo = $this->getTokens();
+
if ( !Title::newFromText( 'UTPage' )->exists() ) {
- $this->markTestIncomplete( "The article [[UTPage]] does not exist" );
+ $this->markTestSkipped( "The article [[UTPage]] does not exist" ); //TODO: just create it?
}
$data = $this->doApiRequest( array(
'action' => 'query',
'prop' => 'revisions',
'titles' => 'UTPage',
- 'rvtoken' => 'rollback' ), $data );
+ 'rvtoken' => 'rollback' ) );
$this->assertArrayHasKey( 'query', $data[0] );
$this->assertArrayHasKey( 'pages', $data[0]['query'] );
@@ -116,7 +113,7 @@ class ApiWatchTest extends ApiTestCase {
$key = array_pop( $keys );
if ( isset( $data[0]['query']['pages'][$key]['missing'] ) ) {
- $this->markTestIncomplete( "Target page (UTPage) doesn't exist" );
+ $this->markTestSkipped( "Target page (UTPage) doesn't exist" );
}
$this->assertArrayHasKey( 'pageid', $data[0]['query']['pages'][$key] );
@@ -128,21 +125,27 @@ class ApiWatchTest extends ApiTestCase {
}
/**
- * @depends testGetRollbackToken
* @group Broken
+ * Broken because there is currently no revision info in the $pageinfo
+ *
+ * @depends testGetRollbackToken
*/
function testWatchRollback( $data ) {
$keys = array_keys( $data[0]['query']['pages'] );
$key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key]['revisions'][0];
+ $pageinfo = $data[0]['query']['pages'][$key];
+ $revinfo = $pageinfo['revisions'][0];
try {
$data = $this->doApiRequest( array(
'action' => 'rollback',
'title' => 'UTPage',
- 'user' => $pageinfo['user'],
+ 'user' => $revinfo['user'],
'token' => $pageinfo['rollbacktoken'],
- 'watchlist' => 'watch' ), $data );
+ 'watchlist' => 'watch' ) );
+
+ $this->assertArrayHasKey( 'rollback', $data[0] );
+ $this->assertArrayHasKey( 'title', $data[0]['rollback'] );
} catch( UsageException $ue ) {
if( $ue->getCodeString() == 'onlyauthor' ) {
$this->markTestIncomplete( "Only one author to 'UTPage', cannot test rollback" );
@@ -150,32 +153,23 @@ class ApiWatchTest extends ApiTestCase {
$this->fail( "Received error '" . $ue->getCodeString() . "'" );
}
}
-
- $this->assertArrayHasKey( 'rollback', $data[0] );
- $this->assertArrayHasKey( 'title', $data[0]['rollback'] );
}
/**
- * @group Broken
*/
function testWatchDelete() {
-
- $data = $this->getTokens();
-
- $keys = array_keys( $data[0]['query']['pages'] );
- $key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key];
+ $pageinfo = $this->getTokens();
$data = $this->doApiRequest( array(
'action' => 'delete',
'token' => $pageinfo['deletetoken'],
- 'title' => 'UTPage' ), $data );
+ 'title' => 'UTPage' ) );
$this->assertArrayHasKey( 'delete', $data[0] );
$this->assertArrayHasKey( 'title', $data[0]['delete'] );
$data = $this->doApiRequest( array(
'action' => 'query',
- 'list' => 'watchlist' ), $data );
+ 'list' => 'watchlist' ) );
$this->markTestIncomplete( 'This test needs to verify the deleted article was added to the users watchlist' );
}
diff --git a/tests/phpunit/includes/api/PrefixUniquenessTest.php b/tests/phpunit/includes/api/PrefixUniquenessTest.php
new file mode 100644
index 00000000..69b01ea7
--- /dev/null
+++ b/tests/phpunit/includes/api/PrefixUniquenessTest.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Checks that all API query modules, core and extensions, have unique prefixes
+ * @group API
+ */
+class PrefixUniquenessTest extends MediaWikiTestCase {
+ public function testPrefixes() {
+ $main = new ApiMain( new FauxRequest() );
+ $query = new ApiQuery( $main, 'foo', 'bar' );
+ $modules = $query->getModules();
+ $prefixes = array();
+
+ foreach ( $modules as $name => $class ) {
+ $module = new $class( $main, $name );
+ $prefix = $module->getModulePrefix();
+ if ( isset( $prefixes[$prefix] ) ) {
+ $this->fail( "Module prefix '{$prefix}' is shared between {$class} and {$prefixes[$prefix]}" );
+ }
+ $prefixes[$module->getModulePrefix()] = $class;
+ }
+ $this->assertTrue( true ); // dummy call to make this test non-incomplete
+ }
+}
diff --git a/tests/phpunit/includes/api/RandomImageGenerator.php b/tests/phpunit/includes/api/RandomImageGenerator.php
index 86c0a828..8b6a3849 100644
--- a/tests/phpunit/includes/api/RandomImageGenerator.php
+++ b/tests/phpunit/includes/api/RandomImageGenerator.php
@@ -79,7 +79,7 @@ class RandomImageGenerator {
foreach ( array(
'/usr/share/dict/words',
'/usr/dict/words',
- dirname( __FILE__ ) . '/words.txt' )
+ __DIR__ . '/words.txt' )
as $dictionaryFile ) {
if ( is_file( $dictionaryFile ) and is_readable( $dictionaryFile ) ) {
$this->dictionaryFile = $dictionaryFile;
diff --git a/tests/phpunit/includes/api/generateRandomImages.php b/tests/phpunit/includes/api/generateRandomImages.php
index f3a14e5b..ee345623 100644
--- a/tests/phpunit/includes/api/generateRandomImages.php
+++ b/tests/phpunit/includes/api/generateRandomImages.php
@@ -6,14 +6,18 @@
*/
// Evaluate the include path relative to this file
-$IP = dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) );
+$IP = dirname( dirname( dirname( dirname( __DIR__ ) ) ) );
// Start up MediaWiki in command-line mode
require_once( "$IP/maintenance/Maintenance.php" );
-require("RandomImageGenerator.php");
+require( __DIR__ . "/RandomImageGenerator.php" );
class GenerateRandomImages extends Maintenance {
+ public function getDbType() {
+ return Maintenance::DB_NONE;
+ }
+
public function execute() {
$getOptSpec = array(
diff --git a/tests/phpunit/includes/cache/GenderCacheTest.php b/tests/phpunit/includes/cache/GenderCacheTest.php
new file mode 100644
index 00000000..a8b987e2
--- /dev/null
+++ b/tests/phpunit/includes/cache/GenderCacheTest.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @group Database
+ * @group Cache
+ */
+class GenderCacheTest extends MediaWikiLangTestCase {
+
+ function setUp() {
+ global $wgDefaultUserOptions;
+ parent::setUp();
+ //ensure the correct default gender
+ $wgDefaultUserOptions['gender'] = 'unknown';
+ }
+
+ function addDBData() {
+ $user = User::newFromName( 'UTMale' );
+ if( $user->getID() == 0 ) {
+ $user->addToDatabase();
+ $user->setPassword( 'UTMalePassword' );
+ }
+ //ensure the right gender
+ $user->setOption( 'gender', 'male' );
+ $user->saveSettings();
+
+ $user = User::newFromName( 'UTFemale' );
+ if( $user->getID() == 0 ) {
+ $user->addToDatabase();
+ $user->setPassword( 'UTFemalePassword' );
+ }
+ //ensure the right gender
+ $user->setOption( 'gender', 'female' );
+ $user->saveSettings();
+
+ $user = User::newFromName( 'UTDefaultGender' );
+ if( $user->getID() == 0 ) {
+ $user->addToDatabase();
+ $user->setPassword( 'UTDefaultGenderPassword' );
+ }
+ //ensure the default gender
+ $user->setOption( 'gender', null );
+ $user->saveSettings();
+ }
+
+ /**
+ * test usernames
+ *
+ * @dataProvider dataUserName
+ */
+ function testUserName( $username, $expectedGender ) {
+ $genderCache = GenderCache::singleton();
+ $gender = $genderCache->getGenderOf( $username );
+ $this->assertEquals( $gender, $expectedGender, "GenderCache normal" );
+ }
+
+ /**
+ * genderCache should work with user objects, too
+ *
+ * @dataProvider dataUserName
+ */
+ function testUserObjects( $username, $expectedGender ) {
+ $genderCache = GenderCache::singleton();
+ $user = User::newFromName( $username );
+ $gender = $genderCache->getGenderOf( $user );
+ $this->assertEquals( $gender, $expectedGender, "GenderCache normal" );
+ }
+
+ function dataUserName() {
+ return array(
+ array( 'UTMale', 'male' ),
+ array( 'UTFemale', 'female' ),
+ array( 'UTDefaultGender', 'unknown' ),
+ array( 'UTNotExist', 'unknown' ),
+ //some not valid user
+ array( '127.0.0.1', 'unknown' ),
+ array( 'user@test', 'unknown' ),
+ );
+ }
+
+ /**
+ * test strip of subpages to avoid unnecessary queries
+ * against the never existing username
+ *
+ * @dataProvider dataStripSubpages
+ */
+ function testStripSubpages( $pageWithSubpage, $expectedGender ) {
+ $genderCache = GenderCache::singleton();
+ $gender = $genderCache->getGenderOf( $pageWithSubpage );
+ $this->assertEquals( $gender, $expectedGender, "GenderCache must strip of subpages" );
+ }
+
+ function dataStripSubpages() {
+ return array(
+ array( 'UTMale/subpage', 'male' ),
+ array( 'UTFemale/subpage', 'female' ),
+ array( 'UTDefaultGender/subpage', 'unknown' ),
+ array( 'UTNotExist/subpage', 'unknown' ),
+ array( '127.0.0.1/subpage', 'unknown' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/cache/ProcessCacheLRUTest.php b/tests/phpunit/includes/cache/ProcessCacheLRUTest.php
new file mode 100644
index 00000000..30bfb124
--- /dev/null
+++ b/tests/phpunit/includes/cache/ProcessCacheLRUTest.php
@@ -0,0 +1,239 @@
+<?php
+
+/**
+ * Test for ProcessCacheLRU class.
+ *
+ * Note that it uses the ProcessCacheLRUTestable class which extends some
+ * properties and methods visibility. That class is defined at the end of the
+ * file containing this class.
+ *
+ * @group Cache
+ */
+class ProcessCacheLRUTest extends MediaWikiTestCase {
+
+ /**
+ * 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' ) {
+ $this->assertAttributeEquals( array(), 'cache', $cache, $msg );
+ }
+
+ /**
+ * Helper to fill a cache object passed by reference
+ */
+ function fillCache( &$cache, $numEntries ) {
+ // Fill cache with three values
+ for( $i=1; $i<=$numEntries; $i++) {
+ $cache->set( "cache-key-$i", "prop-$i", "value-$i" );
+ }
+ }
+
+ /**
+ * 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 ) {
+ $expected = array();
+
+ if( $entryToFill === 0 ) {
+ # The cache is empty!
+ return array();
+ } elseif( $entryToFill <= $cacheMaxEntries ) {
+ # Cache is not fully filled
+ $firstKey = 1;
+ } else {
+ # Cache overflowed
+ $firstKey = 1 + $entryToFill - $cacheMaxEntries;
+ }
+
+ $lastKey = $entryToFill;
+
+ for( $i=$firstKey; $i<=$lastKey; $i++ ) {
+ $expected["cache-key-$i"] = array( "prop-$i" => "value-$i" );
+ }
+ return $expected;
+ }
+
+ /**
+ * Highlight diff between assertEquals and assertNotSame
+ */
+ function testPhpUnitArrayEquality() {
+ $one = array( 'A' => 1, 'B' => 2 );
+ $two = array( 'B' => 2, 'A' => 1 );
+ $this->assertEquals( $one, $two ); // ==
+ $this->assertNotSame( $one, $two ); // ===
+ }
+
+ /**
+ * @dataProvider provideInvalidConstructorArg
+ * @expectedException MWException
+ */
+ function testConstructorGivenInvalidValue( $maxSize ) {
+ $c = new ProcessCacheLRUTestable( $maxSize );
+ }
+
+ /**
+ * Value which are forbidden by the constructor
+ */
+ function provideInvalidConstructorArg() {
+ return array(
+ array( null ),
+ array( array() ),
+ array( new stdClass() ),
+ array( 0 ),
+ array( '5' ),
+ array( -1 ),
+ );
+ }
+
+ function testAddAndGetAKey() {
+ $oneCache = new ProcessCacheLRUTestable( 1 );
+ $this->assertCacheEmpty( $oneCache );
+
+ // First set just one value
+ $oneCache->set( 'cache-key', 'prop1', 'value1' );
+ $this->assertEquals( 1, $oneCache->getEntriesCount() );
+ $this->assertTrue( $oneCache->has( 'cache-key', 'prop1' ) );
+ $this->assertEquals( 'value1', $oneCache->get( 'cache-key', 'prop1' ) );
+ }
+
+ function testDeleteOldKey() {
+ $oneCache = new ProcessCacheLRUTestable( 1 );
+ $this->assertCacheEmpty( $oneCache );
+
+ $oneCache->set( 'cache-key', 'prop1', 'value1' );
+ $oneCache->set( 'cache-key', 'prop1', 'value2' );
+ $this->assertEquals( 'value2', $oneCache->get( 'cache-key', 'prop1' ) );
+ }
+
+ /**
+ * This test that we properly overflow when filling a cache with
+ * a sequence of always different cache-keys. Meant to verify we correclty
+ * delete the older key.
+ *
+ * @dataProvider provideCacheFilling
+ * @param $cacheMaxEntries Maximum entry the created cache will hold
+ * @param $entryToFill Number of entries to insert in the created cache.
+ */
+ function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
+ $cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
+ $this->fillCache( $cache, $entryToFill);
+
+ $this->assertSame(
+ $this->getExpectedCache( $cacheMaxEntries, $entryToFill ),
+ $cache->getCache(),
+ "Filling a $cacheMaxEntries entries cache with $entryToFill entries"
+ );
+
+ }
+
+ /**
+ * Provider for testFillingCache
+ */
+ function provideCacheFilling() {
+ // ($cacheMaxEntries, $entryToFill, $msg='')
+ return array(
+ array( 1, 0 ),
+ array( 1, 1 ),
+ array( 1, 2 ), # overflow
+ array( 5, 33 ), # overflow
+ );
+
+ }
+
+ /**
+ * Create a cache with only one remaining entry then update
+ * the first inserted entry. Should bump it to the top.
+ */
+ function testReplaceExistingKeyShouldBumpEntryToTop() {
+ $maxEntries = 3;
+
+ $cache = new ProcessCacheLRUTestable( $maxEntries );
+ // Fill cache leaving just one remaining slot
+ $this->fillCache( $cache, $maxEntries - 1 );
+
+ // Set an existing cache key
+ $cache->set( "cache-key-1", "prop-1", "new-value-for-1" );
+
+ $this->assertSame(
+ array(
+ 'cache-key-2' => array( 'prop-2' => 'value-2' ),
+ 'cache-key-1' => array( 'prop-1' => 'new-value-for-1' ),
+ ),
+ $cache->getCache()
+ );
+ }
+
+ function testRecentlyAccessedKeyStickIn() {
+ $cache = new ProcessCacheLRUTestable( 2 );
+ $cache->set( 'first' , 'prop1', 'value1' );
+ $cache->set( 'second', 'prop2', 'value2' );
+
+ // Get first
+ $cache->get( 'first', 'prop1' );
+ // Cache a third value, should invalidate the least used one
+ $cache->set( 'third', 'prop3', 'value3' );
+
+ $this->assertFalse( $cache->has( 'second', 'prop2' ) );
+ }
+
+ /**
+ * This first create a full cache then update the value for the 2nd
+ * 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).
+ */
+ function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
+ $maxEntries = 3;
+
+ $cache = new ProcessCacheLRUTestable( $maxEntries );
+ $this->fillCache( $cache, $maxEntries );
+
+ // Set an existing cache key
+ $cache->set( "cache-key-2", "prop-2", "new-value-for-2" );
+ $this->assertSame(
+ array(
+ 'cache-key-1' => array( 'prop-1' => 'value-1' ),
+ 'cache-key-3' => array( 'prop-3' => 'value-3' ),
+ 'cache-key-2' => array( 'prop-2' => 'new-value-for-2' ),
+ ),
+ $cache->getCache()
+ );
+ $this->assertEquals( 'new-value-for-2',
+ $cache->get( 'cache-key-2', 'prop-2' )
+ );
+ }
+
+ function testBumpExistingKeyToTop() {
+ $cache = new ProcessCacheLRUTestable( 3 );
+ $this->fillCache( $cache, 3 );
+
+ // Set the very first cache key to a new value
+ $cache->set( "cache-key-1", "prop-1", "new value for 1" );
+ $this->assertEquals(
+ array(
+ 'cache-key-2' => array( 'prop-2' => 'value-2' ),
+ 'cache-key-3' => array( 'prop-3' => 'value-3' ),
+ 'cache-key-1' => array( 'prop-1' => 'new value for 1' ),
+ ),
+ $cache->getCache()
+ );
+
+ }
+
+}
+
+/**
+ * Overrides some ProcessCacheLRU methods and properties accessibility.
+ */
+class ProcessCacheLRUTestable extends ProcessCacheLRU {
+ public $cache = array();
+
+ public function getCache() {
+ return $this->cache;
+ }
+ public function getEntriesCount() {
+ return count( $this->cache );
+ }
+}
diff --git a/tests/phpunit/includes/db/DatabaseSQLTest.php b/tests/phpunit/includes/db/DatabaseSQLTest.php
new file mode 100644
index 00000000..e37cd445
--- /dev/null
+++ b/tests/phpunit/includes/db/DatabaseSQLTest.php
@@ -0,0 +1,147 @@
+<?php
+
+/**
+ * Test the abstract database layer
+ * Using Mysql for the sql at the moment TODO
+ *
+ * @group Database
+ */
+class DatabaseSQLTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ // TODO support other DBMS or find another way to do it
+ if( $this->db->getType() !== 'mysql' ) {
+ $this->markTestSkipped( 'No mysql database' );
+ }
+ }
+
+ /**
+ * @dataProvider dataSelectSQLText
+ */
+ function testSelectSQLText( $sql, $sqlText ) {
+ $this->assertEquals( trim( $this->db->selectSQLText(
+ isset( $sql['tables'] ) ? $sql['tables'] : array(),
+ isset( $sql['fields'] ) ? $sql['fields'] : array(),
+ isset( $sql['conds'] ) ? $sql['conds'] : array(),
+ __METHOD__,
+ isset( $sql['options'] ) ? $sql['options'] : array(),
+ isset( $sql['join_conds'] ) ? $sql['join_conds'] : array()
+ ) ), $sqlText );
+ }
+
+ function dataSelectSQLText() {
+ return array(
+ array(
+ array(
+ 'tables' => 'table',
+ 'fields' => array( 'field', 'alias' => 'field2' ),
+ 'conds' => array( 'alias' => 'text' ),
+ ),
+ "SELECT field,field2 AS alias " .
+ "FROM `unittest_table` " .
+ "WHERE alias = 'text'"
+ ),
+ array(
+ array(
+ 'tables' => 'table',
+ 'fields' => array( 'field', 'alias' => 'field2' ),
+ 'conds' => array( 'alias' => 'text' ),
+ 'options' => array( 'LIMIT' => 1, 'ORDER BY' => 'field' ),
+ ),
+ "SELECT field,field2 AS alias " .
+ "FROM `unittest_table` " .
+ "WHERE alias = 'text' " .
+ "ORDER BY field " .
+ "LIMIT 1"
+ ),
+ array(
+ array(
+ 'tables' => array( 'table', 't2' => 'table2' ),
+ 'fields' => array( 'tid', 'field', 'alias' => 'field2', 't2.id' ),
+ 'conds' => array( 'alias' => 'text' ),
+ 'options' => array( 'LIMIT' => 1, 'ORDER BY' => 'field' ),
+ 'join_conds' => array( 't2' => array(
+ 'LEFT JOIN', 'tid = t2.id'
+ )),
+ ),
+ "SELECT tid,field,field2 AS alias,t2.id " .
+ "FROM `unittest_table` LEFT JOIN `unittest_table2` `t2` ON ((tid = t2.id)) " .
+ "WHERE alias = 'text' " .
+ "ORDER BY field " .
+ "LIMIT 1"
+ ),
+ array(
+ array(
+ 'tables' => array( 'table', 't2' => 'table2' ),
+ 'fields' => array( 'tid', 'field', 'alias' => 'field2', 't2.id' ),
+ 'conds' => array( 'alias' => 'text' ),
+ 'options' => array( 'LIMIT' => 1, 'GROUP BY' => 'field', 'HAVING' => 'COUNT(*) > 1' ),
+ 'join_conds' => array( 't2' => array(
+ 'LEFT JOIN', 'tid = t2.id'
+ )),
+ ),
+ "SELECT tid,field,field2 AS alias,t2.id " .
+ "FROM `unittest_table` LEFT JOIN `unittest_table2` `t2` ON ((tid = t2.id)) " .
+ "WHERE alias = 'text' " .
+ "GROUP BY field HAVING COUNT(*) > 1 " .
+ "LIMIT 1"
+ ),
+ array(
+ array(
+ 'tables' => array( 'table', 't2' => 'table2' ),
+ 'fields' => array( 'tid', 'field', 'alias' => 'field2', 't2.id' ),
+ 'conds' => array( 'alias' => 'text' ),
+ 'options' => array( 'LIMIT' => 1, 'GROUP BY' => array( 'field', 'field2' ), 'HAVING' => array( 'COUNT(*) > 1', 'field' => 1 ) ),
+ 'join_conds' => array( 't2' => array(
+ 'LEFT JOIN', 'tid = t2.id'
+ )),
+ ),
+ "SELECT tid,field,field2 AS alias,t2.id " .
+ "FROM `unittest_table` LEFT JOIN `unittest_table2` `t2` ON ((tid = t2.id)) " .
+ "WHERE alias = 'text' " .
+ "GROUP BY field,field2 HAVING (COUNT(*) > 1) AND field = '1' " .
+ "LIMIT 1"
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataConditional
+ */
+ function testConditional( $sql, $sqlText ) {
+ $this->assertEquals( trim( $this->db->conditional(
+ $sql['conds'],
+ $sql['true'],
+ $sql['false']
+ ) ), $sqlText );
+ }
+
+ function dataConditional() {
+ return array(
+ array(
+ array(
+ 'conds' => array( 'field' => 'text' ),
+ 'true' => 1,
+ 'false' => 'NULL',
+ ),
+ "(CASE WHEN field = 'text' THEN 1 ELSE NULL END)"
+ ),
+ array(
+ array(
+ 'conds' => array( 'field' => 'text', 'field2' => 'anothertext' ),
+ 'true' => 1,
+ 'false' => 'NULL',
+ ),
+ "(CASE WHEN field = 'text' AND field2 = 'anothertext' THEN 1 ELSE NULL END)"
+ ),
+ array(
+ array(
+ 'conds' => 'field=1',
+ 'true' => 1,
+ 'false' => 'NULL',
+ ),
+ "(CASE WHEN field=1 THEN 1 ELSE NULL END)"
+ ),
+ );
+ }
+} \ No newline at end of file
diff --git a/tests/phpunit/includes/db/DatabaseSqliteTest.php b/tests/phpunit/includes/db/DatabaseSqliteTest.php
index 067c731a..d226598b 100644
--- a/tests/phpunit/includes/db/DatabaseSqliteTest.php
+++ b/tests/phpunit/includes/db/DatabaseSqliteTest.php
@@ -250,6 +250,16 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
}
}
+ public function testInsertIdType() {
+ $db = new DatabaseSqliteStandalone( ':memory:' );
+ $this->assertInstanceOf( 'ResultWrapper',
+ $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ ), "Database creationg" );
+ $this->assertTrue( $db->insert( 'a', array( 'a_1' => 10 ), __METHOD__ ),
+ "Insertion worked" );
+ $this->assertEquals( "integer", gettype( $db->insertId() ), "Actual typecheck" );
+ $this->assertTrue( $db->close(), "closing database" );
+ }
+
private function prepareDB( $version ) {
static $maint = null;
if ( $maint === null ) {
diff --git a/tests/phpunit/includes/db/ORMRowTest.php b/tests/phpunit/includes/db/ORMRowTest.php
new file mode 100644
index 00000000..9dcaf2b3
--- /dev/null
+++ b/tests/phpunit/includes/db/ORMRowTest.php
@@ -0,0 +1,234 @@
+<?php
+
+/**
+ * Abstract class to construct tests for ORMRow deriving classes.
+ *
+ * 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.20
+ *
+ * @ingroup Test
+ *
+ * @group ORM
+ *
+ * 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 >
+ */
+abstract class ORMRowTest extends \MediaWikiTestCase {
+
+ /**
+ * @since 1.20
+ * @return string
+ */
+ protected abstract function getRowClass();
+
+ /**
+ * @since 1.20
+ * @return IORMTable
+ */
+ protected abstract function getTableInstance();
+
+ /**
+ * @since 1.20
+ * @return array
+ */
+ public abstract function constructorTestProvider();
+
+ /**
+ * @since 1.20
+ * @param IORMRow $row
+ * @param array $data
+ */
+ protected function verifyFields( IORMRow $row, array $data ) {
+ foreach ( array_keys( $data ) as $fieldName ) {
+ $this->assertEquals( $data[$fieldName], $row->getField( $fieldName ) );
+ }
+ }
+
+ /**
+ * @since 1.20
+ * @param array $data
+ * @param boolean $loadDefaults
+ * @return IORMRow
+ */
+ protected function getRowInstance( array $data, $loadDefaults ) {
+ $class = $this->getRowClass();
+ return new $class( $this->getTableInstance(), $data, $loadDefaults );
+ }
+
+ /**
+ * @since 1.20
+ * @return array
+ */
+ protected function getMockValues() {
+ return array(
+ 'id' => 1,
+ 'str' => 'foobar4645645',
+ 'int' => 42,
+ 'float' => 4.2,
+ 'bool' => true,
+ 'array' => array( 42, 'foobar' ),
+ 'blob' => new stdClass()
+ );
+ }
+
+ /**
+ * @since 1.20
+ * @return array
+ */
+ protected function getMockFields() {
+ $mockValues = $this->getMockValues();
+ $mockFields = array();
+
+ foreach ( $this->getTableInstance()->getFields() as $name => $type ) {
+ if ( $name !== 'id' ) {
+ $mockFields[$name] = $mockValues[$type];
+ }
+ }
+
+ return $mockFields;
+ }
+
+ /**
+ * @since 1.20
+ * @return array of IORMRow
+ */
+ public function instanceProvider() {
+ $instances = array();
+
+ foreach ( $this->constructorTestProvider() as $arguments ) {
+ $instances[] = array( call_user_func_array( array( $this, 'getRowInstance' ), $arguments ) );
+ }
+
+ return $instances;
+ }
+
+ /**
+ * @dataProvider constructorTestProvider
+ */
+ public function testConstructor( array $data, $loadDefaults ) {
+ $this->verifyFields( $this->getRowInstance( $data, $loadDefaults ), $data );
+ }
+
+ /**
+ * @dataProvider constructorTestProvider
+ */
+ public function testSave( array $data, $loadDefaults ) {
+ $item = $this->getRowInstance( $data, $loadDefaults );
+
+ $this->assertTrue( $item->save() );
+
+ $this->assertTrue( $item->hasIdField() );
+ $this->assertTrue( is_integer( $item->getId() ) );
+
+ $id = $item->getId();
+
+ $this->assertTrue( $item->save() );
+
+ $this->assertEquals( $id, $item->getId() );
+
+ $this->verifyFields( $item, $data );
+ }
+
+ /**
+ * @dataProvider constructorTestProvider
+ */
+ public function testRemove( array $data, $loadDefaults ) {
+ $item = $this->getRowInstance( $data, $loadDefaults );
+
+ $this->assertTrue( $item->save() );
+
+ $this->assertTrue( $item->remove() );
+
+ $this->assertFalse( $item->hasIdField() );
+
+ $this->assertTrue( $item->save() );
+
+ $this->verifyFields( $item, $data );
+
+ $this->assertTrue( $item->remove() );
+
+ $this->assertFalse( $item->hasIdField() );
+
+ $this->verifyFields( $item, $data );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSetField( IORMRow $item ) {
+ foreach ( $this->getMockFields() as $name => $value ) {
+ $item->setField( $name, $value );
+ $this->assertEquals( $value, $item->getField( $name ) );
+ }
+ }
+
+ /**
+ * @since 1.20
+ * @param array $expected
+ * @param IORMRow $item
+ */
+ protected function assertFieldValues( array $expected, IORMRow $item ) {
+ foreach ( $expected as $name => $type ) {
+ if ( $name !== 'id' ) {
+ $this->assertEquals( $expected[$name], $item->getField( $name ) );
+ }
+ }
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSetFields( IORMRow $item ) {
+ $originalValues = $item->getFields();
+
+ $item->setFields( array(), false );
+
+ foreach ( $item->getTable()->getFields() as $name => $type ) {
+ $originalHas = array_key_exists( $name, $originalValues );
+ $newHas = $item->hasField( $name );
+
+ $this->assertEquals( $originalHas, $newHas );
+
+ if ( $originalHas && $newHas ) {
+ $this->assertEquals( $originalValues[$name], $item->getField( $name ) );
+ }
+ }
+
+ $mockFields = $this->getMockFields();
+
+ $item->setFields( $mockFields, false );
+
+ $this->assertFieldValues( $originalValues, $item );
+
+ $item->setFields( $mockFields, true );
+
+ $this->assertFieldValues( $mockFields, $item );
+ }
+
+ // TODO: test all of the methods!
+
+} \ No newline at end of file
diff --git a/tests/phpunit/includes/db/TestORMRowTest.php b/tests/phpunit/includes/db/TestORMRowTest.php
new file mode 100644
index 00000000..afd1cb80
--- /dev/null
+++ b/tests/phpunit/includes/db/TestORMRowTest.php
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * Tests for the TestORMRow class.
+ * TestORMRow is a dummy class to be able to test the abstract ORMRow 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.20
+ *
+ * @ingroup Test
+ *
+ * @group ORM
+ *
+ * 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";
+
+class TestORMRowTest extends ORMRowTest {
+
+ /**
+ * @since 1.20
+ * @return string
+ */
+ protected function getRowClass() {
+ return 'TestORMRow';
+ }
+
+ /**
+ * @since 1.20
+ * @return IORMTable
+ */
+ protected function getTableInstance() {
+ return TestORMTable::singleton();
+ }
+
+ public function setUp() {
+ parent::setUp();
+
+ $dbw = wfGetDB( DB_MASTER );
+
+ $isSqlite = $GLOBALS['wgDBtype'] === 'sqlite';
+
+ $idField = $isSqlite ? 'INTEGER' : 'INT unsigned';
+ $primaryKey = $isSqlite ? 'PRIMARY KEY AUTOINCREMENT' : 'auto_increment PRIMARY KEY';
+
+ $dbw->query(
+ 'CREATE TABLE IF NOT EXISTS ' . $dbw->tableName( 'orm_test' ) . '(
+ test_id ' . $idField . ' NOT NULL ' . $primaryKey . ',
+ test_name VARCHAR(255) NOT NULL,
+ test_age TINYINT unsigned NOT NULL,
+ test_height FLOAT NOT NULL,
+ test_awesome TINYINT unsigned NOT NULL,
+ test_stuff BLOB NOT NULL,
+ test_moarstuff BLOB NOT NULL,
+ test_time varbinary(14) NOT NULL
+ );'
+ );
+ }
+
+ public function constructorTestProvider() {
+ return array(
+ array(
+ array(
+ 'name' => 'Foobar',
+ 'age' => 42,
+ 'height' => 9000.1,
+ 'awesome' => true,
+ 'stuff' => array( 13, 11, 7, 5, 3, 2 ),
+ 'moarstuff' => (object)array( 'foo' => 'bar', 'bar' => array( 4, 2 ), 'baz' => true )
+ ),
+ true
+ ),
+ );
+ }
+
+}
+
+class TestORMRow extends ORMRow {}
+
+class TestORMTable extends ORMTable {
+
+ /**
+ * Returns the name of the database table objects of this type are stored in.
+ *
+ * @since 1.20
+ *
+ * @return string
+ */
+ public function getName() {
+ return 'orm_test';
+ }
+
+ /**
+ * Returns the name of a IORMRow implementing class that
+ * represents single rows in this table.
+ *
+ * @since 1.20
+ *
+ * @return string
+ */
+ public function getRowClass() {
+ return 'TestORMRow';
+ }
+
+ /**
+ * Returns an array with the fields and their types this object contains.
+ * This corresponds directly to the fields in the database, without prefix.
+ *
+ * field name => type
+ *
+ * Allowed types:
+ * * id
+ * * str
+ * * int
+ * * float
+ * * bool
+ * * array
+ * * blob
+ *
+ * @since 1.20
+ *
+ * @return array
+ */
+ public function getFields() {
+ return array(
+ 'id' => 'id',
+ 'name' => 'str',
+ 'age' => 'int',
+ 'height' => 'float',
+ 'awesome' => 'bool',
+ 'stuff' => 'array',
+ 'moarstuff' => 'blob',
+ 'time' => 'int', // 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 5a4e66d4..246b2918 100644
--- a/tests/phpunit/includes/debug/MWDebugTest.php
+++ b/tests/phpunit/includes/debug/MWDebugTest.php
@@ -12,6 +12,11 @@ class MWDebugTest extends MediaWikiTestCase {
}
/** Clear log before each test */
MWDebug::clearLog();
+ wfSuppressWarnings();
+ }
+
+ function tearDown() {
+ wfRestoreWarnings();
}
function testAddLog() {
@@ -30,7 +35,7 @@ class MWDebugTest extends MediaWikiTestCase {
$this->assertEquals( array( array(
'msg' => 'Warning message',
'type' => 'warn',
- 'caller' => 'MWDebug::warning',
+ 'caller' => 'MWDebugTest::testAddWarning',
) ),
MWDebug::getLog()
);
diff --git a/tests/phpunit/includes/filerepo/FileBackendTest.php b/tests/phpunit/includes/filerepo/FileBackendTest.php
index da44797a..a2dc5c6c 100644
--- a/tests/phpunit/includes/filerepo/FileBackendTest.php
+++ b/tests/phpunit/includes/filerepo/FileBackendTest.php
@@ -3,11 +3,11 @@
/**
* @group FileRepo
* @group FileBackend
+ * @group medium
*/
class FileBackendTest extends MediaWikiTestCase {
private $backend, $multiBackend;
private $filesToPrune = array();
- private $dirsToPrune = array();
private static $backendToUse;
function setUp() {
@@ -23,10 +23,14 @@ class FileBackendTest extends MediaWikiTestCase {
foreach ( $wgFileBackends as $conf ) {
if ( $conf['name'] == $name ) {
$useConfig = $conf;
+ break;
}
}
$useConfig['name'] = 'localtesting'; // swap name
- $class = $conf['class'];
+ $useConfig['shardViaHashLevels'] = array( // test sharding
+ 'unittest-cont1' => array( 'levels' => 1, 'base' => 16, 'repeat' => 1 )
+ );
+ $class = $useConfig['class'];
self::$backendToUse = new $class( $useConfig );
$this->singleBackend = self::$backendToUse;
}
@@ -34,6 +38,7 @@ class FileBackendTest extends MediaWikiTestCase {
$this->singleBackend = new FSFileBackend( array(
'name' => 'localtesting',
'lockManager' => 'fsLockManager',
+ #'parallelize' => 'implicit',
'containerPaths' => array(
'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1",
'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" )
@@ -42,6 +47,7 @@ class FileBackendTest extends MediaWikiTestCase {
$this->multiBackend = new FileBackendMultiWrite( array(
'name' => 'localtesting',
'lockManager' => 'fsLockManager',
+ 'parallelize' => 'implicit',
'backends' => array(
array(
'name' => 'localmutlitesting1',
@@ -204,7 +210,7 @@ class FileBackendTest extends MediaWikiTestCase {
$this->tearDownFiles();
}
- function doTestStore( $op ) {
+ private function doTestStore( $op ) {
$backendName = $this->backendClass();
$source = $op['src'];
@@ -219,9 +225,9 @@ class FileBackendTest extends MediaWikiTestCase {
$status = $this->backend->doOperation( $op );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Store from $source to $dest succeeded without warnings ($backendName)." );
- $this->assertEquals( array(), $status->errors,
+ $this->assertEquals( true, $status->isOK(),
"Store from $source to $dest succeeded ($backendName)." );
$this->assertEquals( array( 0 => true ), $status->success,
"Store from $source to $dest has proper 'success' field in Status ($backendName)." );
@@ -238,13 +244,15 @@ class FileBackendTest extends MediaWikiTestCase {
$props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
$this->assertEquals( $props1, $props2,
"Source and destination have the same props ($backendName)." );
+
+ $this->assertBackendPathsConsistent( array( $dest ) );
}
public function provider_testStore() {
$cases = array();
$tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath();
- $toPath = $this->baseStorePath() . '/unittest-cont1/fun/obj1.txt';
+ $toPath = $this->baseStorePath() . '/unittest-cont1/e/fun/obj1.txt';
$op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath );
$cases[] = array(
$op, // operation
@@ -286,7 +294,7 @@ class FileBackendTest extends MediaWikiTestCase {
$this->tearDownFiles();
}
- function doTestCopy( $op ) {
+ private function doTestCopy( $op ) {
$backendName = $this->backendClass();
$source = $op['src'];
@@ -296,7 +304,7 @@ class FileBackendTest extends MediaWikiTestCase {
$status = $this->backend->doOperation(
array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $source succeeded ($backendName)." );
if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
@@ -305,7 +313,7 @@ class FileBackendTest extends MediaWikiTestCase {
$status = $this->backend->doOperation( $op );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Copy from $source to $dest succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Copy from $source to $dest succeeded ($backendName)." );
@@ -325,13 +333,15 @@ class FileBackendTest extends MediaWikiTestCase {
$props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
$this->assertEquals( $props1, $props2,
"Source and destination have the same props ($backendName)." );
+
+ $this->assertBackendPathsConsistent( array( $source, $dest ) );
}
public function provider_testCopy() {
$cases = array();
- $source = $this->baseStorePath() . '/unittest-cont1/file.txt';
- $dest = $this->baseStorePath() . '/unittest-cont2/fileMoved.txt';
+ $source = $this->baseStorePath() . '/unittest-cont1/e/file.txt';
+ $dest = $this->baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
$op = array( 'op' => 'copy', 'src' => $source, 'dst' => $dest );
$cases[] = array(
@@ -384,7 +394,7 @@ class FileBackendTest extends MediaWikiTestCase {
$status = $this->backend->doOperation(
array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $source succeeded ($backendName)." );
if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
@@ -392,7 +402,7 @@ class FileBackendTest extends MediaWikiTestCase {
}
$status = $this->backend->doOperation( $op );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Move from $source to $dest succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Move from $source to $dest succeeded ($backendName)." );
@@ -414,13 +424,15 @@ class FileBackendTest extends MediaWikiTestCase {
"Source file does not exist accourding to props ($backendName)." );
$this->assertEquals( true, $props2['fileExists'],
"Destination file exists accourding to props ($backendName)." );
+
+ $this->assertBackendPathsConsistent( array( $source, $dest ) );
}
public function provider_testMove() {
$cases = array();
- $source = $this->baseStorePath() . '/unittest-cont1/file.txt';
- $dest = $this->baseStorePath() . '/unittest-cont2/fileMoved.txt';
+ $source = $this->baseStorePath() . '/unittest-cont1/e/file.txt';
+ $dest = $this->baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
$op = array( 'op' => 'move', 'src' => $source, 'dst' => $dest );
$cases[] = array(
@@ -472,13 +484,13 @@ class FileBackendTest extends MediaWikiTestCase {
if ( $withSource ) {
$status = $this->backend->doOperation(
array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $source succeeded ($backendName)." );
}
$status = $this->backend->doOperation( $op );
if ( $okStatus ) {
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Deletion of file at $source succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Deletion of file at $source succeeded ($backendName)." );
@@ -499,12 +511,14 @@ class FileBackendTest extends MediaWikiTestCase {
$props1 = $this->backend->getFileProps( array( 'src' => $source ) );
$this->assertFalse( $props1['fileExists'],
"Source file $source does not exist according to props ($backendName)." );
+
+ $this->assertBackendPathsConsistent( array( $source ) );
}
public function provider_testDelete() {
$cases = array();
- $source = $this->baseStorePath() . '/unittest-cont1/myfacefile.txt';
+ $source = $this->baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
$op = array( 'op' => 'delete', 'src' => $source );
$cases[] = array(
@@ -554,13 +568,13 @@ class FileBackendTest extends MediaWikiTestCase {
if ( $alreadyExists ) {
$status = $this->backend->doOperation(
array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $dest succeeded ($backendName)." );
}
$status = $this->backend->doOperation( $op );
if ( $okStatus ) {
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $dest succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Creation of file at $dest succeeded ($backendName)." );
@@ -590,6 +604,8 @@ class FileBackendTest extends MediaWikiTestCase {
$this->backend->getFileSize( array( 'src' => $dest ) ),
"Destination file $dest has original size according to props ($backendName)." );
}
+
+ $this->assertBackendPathsConsistent( array( $dest ) );
}
/**
@@ -598,7 +614,7 @@ class FileBackendTest extends MediaWikiTestCase {
public function provider_testCreate() {
$cases = array();
- $dest = $this->baseStorePath() . '/unittest-cont2/myspacefile.txt';
+ $dest = $this->baseStorePath() . '/unittest-cont2/a/myspacefile.txt';
$op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
$cases[] = array(
@@ -649,6 +665,54 @@ class FileBackendTest extends MediaWikiTestCase {
return $cases;
}
+ public function testDoQuickOperations() {
+ $this->backend = $this->singleBackend;
+ $this->doTestDoQuickOperations();
+ $this->tearDownFiles();
+
+ $this->backend = $this->multiBackend;
+ $this->doTestDoQuickOperations();
+ $this->tearDownFiles();
+ }
+
+ private function doTestDoQuickOperations() {
+ $backendName = $this->backendClass();
+
+ $base = $this->baseStorePath();
+ $files = array(
+ "$base/unittest-cont1/e/fileA.a",
+ "$base/unittest-cont1/e/fileB.a",
+ "$base/unittest-cont1/e/fileC.a"
+ );
+ $ops = array();
+ $purgeOps = array();
+ foreach ( $files as $path ) {
+ $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
+ $this->assertGoodStatus( $status,
+ "Preparing $path succeeded without warnings ($backendName)." );
+ $ops[] = array( 'op' => 'create', 'dst' => $path, 'content' => mt_rand(0,50000) );
+ $purgeOps[] = array( 'op' => 'delete', 'src' => $path );
+ }
+ $purgeOps[] = array( 'op' => 'null' );
+ $status = $this->backend->doQuickOperations( $ops );
+ $this->assertGoodStatus( $status,
+ "Creation of source files succeeded ($backendName)." );
+
+ foreach ( $files as $file ) {
+ $this->assertTrue( $this->backend->fileExists( array( 'src' => $file ) ),
+ "File $file exists." );
+ }
+
+ $status = $this->backend->doQuickOperations( $purgeOps );
+ $this->assertGoodStatus( $status,
+ "Quick deletion of source files succeeded ($backendName)." );
+
+ foreach ( $files as $file ) {
+ $this->assertFalse( $this->backend->fileExists( array( 'src' => $file ) ),
+ "File $file purged." );
+ }
+ }
+
/**
* @dataProvider provider_testConcatenate
*/
@@ -667,7 +731,7 @@ class FileBackendTest extends MediaWikiTestCase {
$this->tearDownFiles();
}
- public function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
+ private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
$backendName = $this->backendClass();
$expContent = '';
@@ -684,7 +748,7 @@ class FileBackendTest extends MediaWikiTestCase {
}
$status = $this->backend->doOperations( $ops );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of source files succeeded ($backendName)." );
$dest = $params['dst'];
@@ -701,7 +765,7 @@ class FileBackendTest extends MediaWikiTestCase {
// Combine the files into one
$status = $this->backend->concatenate( $params );
if ( $okStatus ) {
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of concat file at $dest succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Creation of concat file at $dest succeeded ($backendName)." );
@@ -736,16 +800,16 @@ class FileBackendTest extends MediaWikiTestCase {
$rand = mt_rand( 0, 2000000000 ) . time();
$dest = wfTempDir() . "/randomfile!$rand.txt";
$srcs = array(
- $this->baseStorePath() . '/unittest-cont1/file1.txt',
- $this->baseStorePath() . '/unittest-cont1/file2.txt',
- $this->baseStorePath() . '/unittest-cont1/file3.txt',
- $this->baseStorePath() . '/unittest-cont1/file4.txt',
- $this->baseStorePath() . '/unittest-cont1/file5.txt',
- $this->baseStorePath() . '/unittest-cont1/file6.txt',
- $this->baseStorePath() . '/unittest-cont1/file7.txt',
- $this->baseStorePath() . '/unittest-cont1/file8.txt',
- $this->baseStorePath() . '/unittest-cont1/file9.txt',
- $this->baseStorePath() . '/unittest-cont1/file10.txt'
+ $this->baseStorePath() . '/unittest-cont1/e/file1.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file2.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file3.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file4.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file5.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file6.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file7.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file8.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file9.txt',
+ $this->baseStorePath() . '/unittest-cont1/e/file10.txt'
);
$content = array(
'egfage',
@@ -800,8 +864,8 @@ class FileBackendTest extends MediaWikiTestCase {
if ( $alreadyExists ) {
$this->prepare( array( 'dir' => dirname( $path ) ) );
- $status = $this->backend->create( array( 'dst' => $path, 'content' => $content ) );
- $this->assertEquals( array(), $status->errors,
+ $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
+ $this->assertGoodStatus( $status,
"Creation of file at $path succeeded ($backendName)." );
$size = $this->backend->getFileSize( array( 'src' => $path ) );
@@ -810,20 +874,34 @@ class FileBackendTest extends MediaWikiTestCase {
$this->assertEquals( strlen( $content ), $size,
"Correct file size of '$path'" );
- $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 5,
+ $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
"Correct file timestamp of '$path'" );
$size = $stat['size'];
$time = $stat['mtime'];
$this->assertEquals( strlen( $content ), $size,
"Correct file size of '$path'" );
- $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 5,
+ $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
"Correct file timestamp of '$path'" );
+
+ $this->backend->clearCache( array( $path ) );
+
+ $size = $this->backend->getFileSize( array( 'src' => $path ) );
+
+ $this->assertEquals( strlen( $content ), $size,
+ "Correct file size of '$path'" );
+
+ $this->backend->preloadCache( array( $path ) );
+
+ $size = $this->backend->getFileSize( array( 'src' => $path ) );
+
+ $this->assertEquals( strlen( $content ), $size,
+ "Correct file size of '$path'" );
} else {
$size = $this->backend->getFileSize( array( 'src' => $path ) );
$time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
$stat = $this->backend->getFileStat( array( 'src' => $path ) );
-
+
$this->assertFalse( $size, "Correct file size of '$path'" );
$this->assertFalse( $time, "Correct file timestamp of '$path'" );
$this->assertFalse( $stat, "Correct file stat of '$path'" );
@@ -834,9 +912,9 @@ class FileBackendTest extends MediaWikiTestCase {
$cases = array();
$base = $this->baseStorePath();
- $cases[] = array( "$base/unittest-cont1/b/z/some_file.txt", "some file contents", true );
- $cases[] = array( "$base/unittest-cont1/b/some-other_file.txt", "", true );
- $cases[] = array( "$base/unittest-cont1/b/some-diff_file.txt", null, false );
+ $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents", true );
+ $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "", true );
+ $cases[] = array( "$base/unittest-cont1/e/b/some-diff_file.txt", null, false );
return $cases;
}
@@ -856,14 +934,14 @@ class FileBackendTest extends MediaWikiTestCase {
$this->tearDownFiles();
}
- public function doTestGetFileContents( $source, $content ) {
+ private function doTestGetFileContents( $source, $content ) {
$backendName = $this->backendClass();
$this->prepare( array( 'dir' => dirname( $source ) ) );
$status = $this->backend->doOperation(
array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $source succeeded ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Creation of file at $source succeeded with OK status ($backendName)." );
@@ -880,8 +958,8 @@ class FileBackendTest extends MediaWikiTestCase {
$cases = array();
$base = $this->baseStorePath();
- $cases[] = array( "$base/unittest-cont1/b/z/some_file.txt", "some file contents" );
- $cases[] = array( "$base/unittest-cont1/b/some-other_file.txt", "more file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "more file contents" );
return $cases;
}
@@ -901,14 +979,14 @@ class FileBackendTest extends MediaWikiTestCase {
$this->tearDownFiles();
}
- public function doTestGetLocalCopy( $source, $content ) {
+ private function doTestGetLocalCopy( $source, $content ) {
$backendName = $this->backendClass();
$this->prepare( array( 'dir' => dirname( $source ) ) );
$status = $this->backend->doOperation(
array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of file at $source succeeded ($backendName)." );
$tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
@@ -923,8 +1001,8 @@ class FileBackendTest extends MediaWikiTestCase {
$cases = array();
$base = $this->baseStorePath();
- $cases[] = array( "$base/unittest-cont1/a/z/some_file.txt", "some file contents" );
- $cases[] = array( "$base/unittest-cont1/a/some-other_file.txt", "more file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
return $cases;
}
@@ -949,9 +1027,8 @@ class FileBackendTest extends MediaWikiTestCase {
$this->prepare( array( 'dir' => dirname( $source ) ) );
- $status = $this->backend->doOperation(
- array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
- $this->assertEquals( array(), $status->errors,
+ $status = $this->create( array( 'content' => $content, 'dst' => $source ) );
+ $this->assertGoodStatus( $status,
"Creation of file at $source succeeded ($backendName)." );
$tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
@@ -966,8 +1043,8 @@ class FileBackendTest extends MediaWikiTestCase {
$cases = array();
$base = $this->baseStorePath();
- $cases[] = array( "$base/unittest-cont1/a/z/some_file.txt", "some file contents" );
- $cases[] = array( "$base/unittest-cont1/a/some-other_file.txt", "more file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
+ $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
return $cases;
}
@@ -988,19 +1065,19 @@ class FileBackendTest extends MediaWikiTestCase {
function provider_testPrepareAndClean() {
$base = $this->baseStorePath();
return array(
- array( "$base/unittest-cont1/a/z/some_file1.txt", true ),
+ array( "$base/unittest-cont1/e/a/z/some_file1.txt", true ),
array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
# Specific to FS backend with no basePath field set
#array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
);
}
- function doTestPrepareAndClean( $path, $isOK ) {
+ private function doTestPrepareAndClean( $path, $isOK ) {
$backendName = $this->backendClass();
$status = $this->prepare( array( 'dir' => dirname( $path ) ) );
if ( $isOK ) {
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Preparing dir $path succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Preparing dir $path succeeded ($backendName)." );
@@ -1011,7 +1088,7 @@ class FileBackendTest extends MediaWikiTestCase {
$status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
if ( $isOK ) {
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Cleaning dir $path succeeded without warnings ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Cleaning dir $path succeeded ($backendName)." );
@@ -1021,6 +1098,58 @@ class FileBackendTest extends MediaWikiTestCase {
}
}
+ public function testRecursiveClean() {
+ $this->backend = $this->singleBackend;
+ $this->doTestRecursiveClean();
+ $this->tearDownFiles();
+
+ $this->backend = $this->multiBackend;
+ $this->doTestRecursiveClean();
+ $this->tearDownFiles();
+ }
+
+ private function doTestRecursiveClean() {
+ $backendName = $this->backendClass();
+
+ $base = $this->baseStorePath();
+ $dirs = array(
+ "$base/unittest-cont1/e/a",
+ "$base/unittest-cont1/e/a/b",
+ "$base/unittest-cont1/e/a/b/c",
+ "$base/unittest-cont1/e/a/b/c/d0",
+ "$base/unittest-cont1/e/a/b/c/d1",
+ "$base/unittest-cont1/e/a/b/c/d2",
+ "$base/unittest-cont1/e/a/b/c/d0/1",
+ "$base/unittest-cont1/e/a/b/c/d0/2",
+ "$base/unittest-cont1/e/a/b/c/d1/3",
+ "$base/unittest-cont1/e/a/b/c/d1/4",
+ "$base/unittest-cont1/e/a/b/c/d2/5",
+ "$base/unittest-cont1/e/a/b/c/d2/6"
+ );
+ foreach ( $dirs as $dir ) {
+ $status = $this->prepare( array( 'dir' => $dir ) );
+ $this->assertGoodStatus( $status,
+ "Preparing dir $dir succeeded without warnings ($backendName)." );
+ }
+
+ if ( $this->backend instanceof FSFileBackend ) {
+ foreach ( $dirs as $dir ) {
+ $this->assertEquals( true, $this->backend->directoryExists( array( 'dir' => $dir ) ),
+ "Dir $dir exists ($backendName)." );
+ }
+ }
+
+ $status = $this->backend->clean(
+ array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
+ $this->assertGoodStatus( $status,
+ "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
+
+ foreach ( $dirs as $dir ) {
+ $this->assertEquals( false, $this->backend->directoryExists( array( 'dir' => $dir ) ),
+ "Dir $dir no longer exists ($backendName)." );
+ }
+ }
+
// @TODO: testSecure
public function testDoOperations() {
@@ -1033,39 +1162,127 @@ class FileBackendTest extends MediaWikiTestCase {
$this->tearDownFiles();
$this->doTestDoOperations();
$this->tearDownFiles();
+ }
+
+ private function doTestDoOperations() {
+ $base = $this->baseStorePath();
+
+ $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
+ $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
+ $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
+ $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
+ $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
+ $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
+ $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
+
+ $this->prepare( array( 'dir' => dirname( $fileA ) ) );
+ $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
+ $this->prepare( array( 'dir' => dirname( $fileB ) ) );
+ $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
+ $this->prepare( array( 'dir' => dirname( $fileC ) ) );
+ $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
+ $this->prepare( array( 'dir' => dirname( $fileD ) ) );
+
+ $status = $this->backend->doOperations( array(
+ array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
+ // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
+ array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
+ // Now: A:<A>, B:<B>, C:<A>, D:<empty>
+ array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
+ // Now: A:<A>, B:<B>, C:<empty>, D:<A>
+ array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
+ // Now: A:<A>, B:<empty>, C:<B>, D:<A>
+ array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
+ // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
+ array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
+ // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
+ array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
+ // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
+ array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
+ // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
+ array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
+ // Does nothing
+ array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
+ // Does nothing
+ array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
+ // Does nothing
+ array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
+ // Does nothing
+ array( 'op' => 'null' ),
+ // Does nothing
+ ) );
+ $this->assertGoodStatus( $status, "Operation batch succeeded" );
+ $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
+ $this->assertEquals( 13, count( $status->success ),
+ "Operation batch has correct success array" );
+
+ $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
+ "File does not exist at $fileA" );
+ $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
+ "File does not exist at $fileB" );
+ $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
+ "File does not exist at $fileD" );
+
+ $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
+ "File exists at $fileC" );
+ $this->assertEquals( $fileBContents,
+ $this->backend->getFileContents( array( 'src' => $fileC ) ),
+ "Correct file contents of $fileC" );
+ $this->assertEquals( strlen( $fileBContents ),
+ $this->backend->getFileSize( array( 'src' => $fileC ) ),
+ "Correct file size of $fileC" );
+ $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
+ $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
+ "Correct file SHA-1 of $fileC" );
+ }
+
+ public function testDoOperationsPipeline() {
$this->backend = $this->singleBackend;
$this->tearDownFiles();
- $this->doTestDoOperationsFailing();
+ $this->doTestDoOperationsPipeline();
$this->tearDownFiles();
$this->backend = $this->multiBackend;
$this->tearDownFiles();
- $this->doTestDoOperationsFailing();
+ $this->doTestDoOperationsPipeline();
$this->tearDownFiles();
-
- // @TODO: test some cases where the ops should fail
}
- function doTestDoOperations() {
+ // concurrency orientated
+ private function doTestDoOperationsPipeline() {
$base = $this->baseStorePath();
- $fileA = "$base/unittest-cont1/a/b/fileA.txt";
$fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
- $fileB = "$base/unittest-cont1/a/b/fileB.txt";
$fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
- $fileC = "$base/unittest-cont1/a/b/fileC.txt";
$fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
- $fileD = "$base/unittest-cont1/a/b/fileD.txt";
+
+ $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();
+ 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";
+ $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
$this->prepare( array( 'dir' => dirname( $fileA ) ) );
- $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
+ $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
$this->prepare( array( 'dir' => dirname( $fileB ) ) );
- $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
$this->prepare( array( 'dir' => dirname( $fileC ) ) );
- $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
+ $this->prepare( array( 'dir' => dirname( $fileD ) ) );
$status = $this->backend->doOperations( array(
+ array( 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ),
+ array( 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ),
+ array( 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ),
array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
// Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
@@ -1094,9 +1311,9 @@ class FileBackendTest extends MediaWikiTestCase {
// Does nothing
) );
- $this->assertEquals( array(), $status->errors, "Operation batch succeeded" );
+ $this->assertGoodStatus( $status, "Operation batch succeeded" );
$this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
- $this->assertEquals( 13, count( $status->success ),
+ $this->assertEquals( 16, count( $status->success ),
"Operation batch has correct success array" );
$this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
@@ -1119,7 +1336,19 @@ class FileBackendTest extends MediaWikiTestCase {
"Correct file SHA-1 of $fileC" );
}
- function doTestDoOperationsFailing() {
+ public function testDoOperationsFailing() {
+ $this->backend = $this->singleBackend;
+ $this->tearDownFiles();
+ $this->doTestDoOperationsFailing();
+ $this->tearDownFiles();
+
+ $this->backend = $this->multiBackend;
+ $this->tearDownFiles();
+ $this->doTestDoOperationsFailing();
+ $this->tearDownFiles();
+ }
+
+ private function doTestDoOperationsFailing() {
$base = $this->baseStorePath();
$fileA = "$base/unittest-cont2/a/b/fileA.txt";
@@ -1131,11 +1360,11 @@ class FileBackendTest extends MediaWikiTestCase {
$fileD = "$base/unittest-cont2/a/b/fileD.txt";
$this->prepare( array( 'dir' => dirname( $fileA ) ) );
- $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
+ $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
$this->prepare( array( 'dir' => dirname( $fileB ) ) );
- $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
+ $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
$this->prepare( array( 'dir' => dirname( $fileC ) ) );
- $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
+ $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
$status = $this->backend->doOperations( array(
array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
@@ -1195,23 +1424,26 @@ class FileBackendTest extends MediaWikiTestCase {
private function doTestGetFileList() {
$backendName = $this->backendClass();
-
$base = $this->baseStorePath();
+
+ // Should have no errors
+ $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
+
$files = array(
- "$base/unittest-cont1/test1.txt",
- "$base/unittest-cont1/test2.txt",
- "$base/unittest-cont1/test3.txt",
- "$base/unittest-cont1/subdir1/test1.txt",
- "$base/unittest-cont1/subdir1/test2.txt",
- "$base/unittest-cont1/subdir2/test3.txt",
- "$base/unittest-cont1/subdir2/test4.txt",
- "$base/unittest-cont1/subdir2/subdir/test1.txt",
- "$base/unittest-cont1/subdir2/subdir/test2.txt",
- "$base/unittest-cont1/subdir2/subdir/test3.txt",
- "$base/unittest-cont1/subdir2/subdir/test4.txt",
- "$base/unittest-cont1/subdir2/subdir/test5.txt",
- "$base/unittest-cont1/subdir2/subdir/sub/test0.txt",
- "$base/unittest-cont1/subdir2/subdir/sub/120-px-file.txt",
+ "$base/unittest-cont1/e/test1.txt",
+ "$base/unittest-cont1/e/test2.txt",
+ "$base/unittest-cont1/e/test3.txt",
+ "$base/unittest-cont1/e/subdir1/test1.txt",
+ "$base/unittest-cont1/e/subdir1/test2.txt",
+ "$base/unittest-cont1/e/subdir2/test3.txt",
+ "$base/unittest-cont1/e/subdir2/test4.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/test2.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/test3.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/test4.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/test5.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/sub/test0.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/sub/120-px-file.txt",
);
// Add the files
@@ -1220,28 +1452,28 @@ class FileBackendTest extends MediaWikiTestCase {
$this->prepare( array( 'dir' => dirname( $file ) ) );
$ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
}
- $status = $this->backend->doOperations( $ops );
- $this->assertEquals( array(), $status->errors,
+ $status = $this->backend->doQuickOperations( $ops );
+ $this->assertGoodStatus( $status,
"Creation of files succeeded ($backendName)." );
$this->assertEquals( true, $status->isOK(),
"Creation of files succeeded with OK status ($backendName)." );
// Expected listing
$expected = array(
- "test1.txt",
- "test2.txt",
- "test3.txt",
- "subdir1/test1.txt",
- "subdir1/test2.txt",
- "subdir2/test3.txt",
- "subdir2/test4.txt",
- "subdir2/subdir/test1.txt",
- "subdir2/subdir/test2.txt",
- "subdir2/subdir/test3.txt",
- "subdir2/subdir/test4.txt",
- "subdir2/subdir/test5.txt",
- "subdir2/subdir/sub/test0.txt",
- "subdir2/subdir/sub/120-px-file.txt",
+ "e/test1.txt",
+ "e/test2.txt",
+ "e/test3.txt",
+ "e/subdir1/test1.txt",
+ "e/subdir1/test2.txt",
+ "e/subdir2/test3.txt",
+ "e/subdir2/test4.txt",
+ "e/subdir2/subdir/test1.txt",
+ "e/subdir2/subdir/test2.txt",
+ "e/subdir2/subdir/test3.txt",
+ "e/subdir2/subdir/test4.txt",
+ "e/subdir2/subdir/test5.txt",
+ "e/subdir2/subdir/sub/test0.txt",
+ "e/subdir2/subdir/sub/120-px-file.txt",
);
sort( $expected );
@@ -1279,7 +1511,7 @@ class FileBackendTest extends MediaWikiTestCase {
// Actual listing (no trailing slash)
$list = array();
- $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/subdir2/subdir" ) );
+ $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
foreach ( $iter as $file ) {
$list[] = $file;
}
@@ -1289,7 +1521,7 @@ class FileBackendTest extends MediaWikiTestCase {
// Actual listing (with trailing slash)
$list = array();
- $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/subdir2/subdir/" ) );
+ $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir/" ) );
foreach ( $iter as $file ) {
$list[] = $file;
}
@@ -1306,6 +1538,26 @@ class FileBackendTest extends MediaWikiTestCase {
$this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
+ // Expected listing (top files only)
+ $expected = array(
+ "test1.txt",
+ "test2.txt",
+ "test3.txt",
+ "test4.txt",
+ "test5.txt"
+ );
+ sort( $expected );
+
+ // Actual listing (top files only)
+ $list = array();
+ $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
+
foreach ( $files as $file ) { // clean up
$this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
}
@@ -1314,12 +1566,268 @@ class FileBackendTest extends MediaWikiTestCase {
foreach ( $iter as $iter ) {} // no errors
}
+ public function testGetDirectoryList() {
+ $this->backend = $this->singleBackend;
+ $this->tearDownFiles();
+ $this->doTestGetDirectoryList();
+ $this->tearDownFiles();
+
+ $this->backend = $this->multiBackend;
+ $this->tearDownFiles();
+ $this->doTestGetDirectoryList();
+ $this->tearDownFiles();
+ }
+
+ private function doTestGetDirectoryList() {
+ $backendName = $this->backendClass();
+
+ $base = $this->baseStorePath();
+ $files = array(
+ "$base/unittest-cont1/e/test1.txt",
+ "$base/unittest-cont1/e/test2.txt",
+ "$base/unittest-cont1/e/test3.txt",
+ "$base/unittest-cont1/e/subdir1/test1.txt",
+ "$base/unittest-cont1/e/subdir1/test2.txt",
+ "$base/unittest-cont1/e/subdir2/test3.txt",
+ "$base/unittest-cont1/e/subdir2/test4.txt",
+ "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
+ "$base/unittest-cont1/e/subdir3/subdir/test2.txt",
+ "$base/unittest-cont1/e/subdir4/subdir/test3.txt",
+ "$base/unittest-cont1/e/subdir4/subdir/test4.txt",
+ "$base/unittest-cont1/e/subdir4/subdir/test5.txt",
+ "$base/unittest-cont1/e/subdir4/subdir/sub/test0.txt",
+ "$base/unittest-cont1/e/subdir4/subdir/sub/120-px-file.txt",
+ );
+
+ // Add the files
+ $ops = array();
+ foreach ( $files as $file ) {
+ $this->prepare( array( 'dir' => dirname( $file ) ) );
+ $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
+ }
+ $status = $this->backend->doQuickOperations( $ops );
+ $this->assertGoodStatus( $status,
+ "Creation of files succeeded ($backendName)." );
+ $this->assertEquals( true, $status->isOK(),
+ "Creation of files succeeded with OK status ($backendName)." );
+
+ $this->assertEquals( true,
+ $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) ),
+ "Directory exists in ($backendName)." );
+ $this->assertEquals( true,
+ $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) ),
+ "Directory exists in ($backendName)." );
+ $this->assertEquals( false,
+ $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/test1.txt" ) ),
+ "Directory does not exists in ($backendName)." );
+
+ // Expected listing
+ $expected = array(
+ "e",
+ );
+ sort( $expected );
+
+ // Actual listing (no trailing slash)
+ $list = array();
+ $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
+
+ // Expected listing
+ $expected = array(
+ "subdir1",
+ "subdir2",
+ "subdir3",
+ "subdir4",
+ );
+ sort( $expected );
+
+ // Actual listing (no trailing slash)
+ $list = array();
+ $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
+
+ // Actual listing (with trailing slash)
+ $list = array();
+ $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
+
+ // Expected listing
+ $expected = array(
+ "subdir",
+ );
+ sort( $expected );
+
+ // Actual listing (no trailing slash)
+ $list = array();
+ $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
+
+ // Actual listing (with trailing slash)
+ $list = array();
+ $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2/" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
+
+ // Actual listing (using iterator second time)
+ $list = array();
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName), second iteration." );
+
+ // Expected listing (recursive)
+ $expected = array(
+ "e",
+ "e/subdir1",
+ "e/subdir2",
+ "e/subdir3",
+ "e/subdir4",
+ "e/subdir2/subdir",
+ "e/subdir3/subdir",
+ "e/subdir4/subdir",
+ "e/subdir4/subdir/sub",
+ );
+ sort( $expected );
+
+ // Actual listing (recursive)
+ $list = array();
+ $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
+
+ // Expected listing (recursive)
+ $expected = array(
+ "subdir",
+ "subdir/sub",
+ );
+ sort( $expected );
+
+ // Actual listing (recursive)
+ $list = array();
+ $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir4" ) );
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
+
+ // Actual listing (recursive, second time)
+ $list = array();
+ foreach ( $iter as $file ) {
+ $list[] = $file;
+ }
+ sort( $list );
+
+ $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
+
+ foreach ( $files as $file ) { // clean up
+ $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
+ }
+
+ $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
+ foreach ( $iter as $iter ) {} // no errors
+ }
+
+ public function testLockCalls() {
+ $this->backend = $this->singleBackend;
+ $this->doTestLockCalls();
+ }
+
+ private function doTestLockCalls() {
+ $backendName = $this->backendClass();
+
+ for ( $i=0; $i<50; $i++ ) {
+ $paths = array(
+ "test1.txt",
+ "test2.txt",
+ "test3.txt",
+ "subdir1",
+ "subdir1", // duplicate
+ "subdir1/test1.txt",
+ "subdir1/test2.txt",
+ "subdir2",
+ "subdir2", // duplicate
+ "subdir2/test3.txt",
+ "subdir2/test4.txt",
+ "subdir2/subdir",
+ "subdir2/subdir/test1.txt",
+ "subdir2/subdir/test2.txt",
+ "subdir2/subdir/test3.txt",
+ "subdir2/subdir/test4.txt",
+ "subdir2/subdir/test5.txt",
+ "subdir2/subdir/sub",
+ "subdir2/subdir/sub/test0.txt",
+ "subdir2/subdir/sub/120-px-file.txt",
+ );
+
+ $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
+ $this->assertEquals( array(), $status->errors,
+ "Locking of files succeeded ($backendName)." );
+ $this->assertEquals( true, $status->isOK(),
+ "Locking of files succeeded with OK status ($backendName)." );
+
+ $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
+ $this->assertEquals( array(), $status->errors,
+ "Locking of files succeeded ($backendName)." );
+ $this->assertEquals( true, $status->isOK(),
+ "Locking of files succeeded with OK status ($backendName)." );
+
+ $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
+ $this->assertEquals( array(), $status->errors,
+ "Locking of files succeeded ($backendName)." );
+ $this->assertEquals( true, $status->isOK(),
+ "Locking of files succeeded with OK status ($backendName)." );
+
+ $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
+ $this->assertEquals( array(), $status->errors,
+ "Locking of files succeeded ($backendName)." );
+ $this->assertEquals( true, $status->isOK(),
+ "Locking of files succeeded with OK status ($backendName)." );
+ }
+ }
+
// test helper wrapper for backend prepare() function
private function prepare( array $params ) {
- $this->dirsToPrune[] = $params['dir'];
return $this->backend->prepare( $params );
}
+ // test helper wrapper for backend prepare() function
+ private function create( array $params ) {
+ $params['op'] = 'create';
+ return $this->backend->doQuickOperations( array( $params ) );
+ }
+
function tearDownFiles() {
foreach ( $this->filesToPrune as $file ) {
@unlink( $file );
@@ -1328,10 +1836,7 @@ class FileBackendTest extends MediaWikiTestCase {
foreach ( $containers as $container ) {
$this->deleteFiles( $container );
}
- foreach ( $this->dirsToPrune as $dir ) {
- $this->recursiveClean( $dir );
- }
- $this->filesToPrune = $this->dirsToPrune = array();
+ $this->filesToPrune = array();
}
private function deleteFiles( $container ) {
@@ -1339,17 +1844,22 @@ class FileBackendTest extends MediaWikiTestCase {
$iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
if ( $iter ) {
foreach ( $iter as $file ) {
- $this->backend->delete( array( 'src' => "$base/$container/$file" ), array( 'force' => 1 ) );
+ $this->backend->delete( array( 'src' => "$base/$container/$file" ),
+ array( 'force' => 1, 'nonLocking' => 1 ) );
}
}
+ $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
}
- private function recursiveClean( $dir ) {
- do {
- if ( !$this->backend->clean( array( 'dir' => $dir ) )->isOK() ) {
- break;
- }
- } while ( $dir = FileBackend::parentStoragePath( $dir ) );
+ function assertBackendPathsConsistent( array $paths ) {
+ if ( $this->backend instanceof FileBackendMultiWrite ) {
+ $status = $this->backend->consistencyCheck( $paths );
+ $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
+ }
+ }
+
+ function assertGoodStatus( $status, $msg ) {
+ $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );
}
function tearDown() {
diff --git a/tests/phpunit/includes/filerepo/FileRepoTest.php b/tests/phpunit/includes/filerepo/FileRepoTest.php
index 0f023138..8f92c123 100644
--- a/tests/phpunit/includes/filerepo/FileRepoTest.php
+++ b/tests/phpunit/includes/filerepo/FileRepoTest.php
@@ -34,8 +34,12 @@ class FileRepoTest extends MediaWikiTestCase {
function testFileRepoConstructionWithRequiredOptions() {
$f = new FileRepo( array(
'name' => 'FileRepoTestRepository',
- 'backend' => 'local-backend',
- ));
+ 'backend' => new FSFileBackend( array(
+ 'name' => 'local-testing',
+ 'lockManager' => 'nullLockManager',
+ 'containerPaths' => array()
+ ) )
+ ) );
$this->assertInstanceOf( 'FileRepo', $f );
}
}
diff --git a/tests/phpunit/includes/filerepo/StoreBatchTest.php b/tests/phpunit/includes/filerepo/StoreBatchTest.php
index 6abceeb3..3ab56af8 100644
--- a/tests/phpunit/includes/filerepo/StoreBatchTest.php
+++ b/tests/phpunit/includes/filerepo/StoreBatchTest.php
@@ -1,6 +1,7 @@
<?php
/**
* @group FileRepo
+ * @group medium
*/
class StoreBatchTest extends MediaWikiTestCase {
diff --git a/tests/phpunit/includes/libs/CSSJanusTest.php b/tests/phpunit/includes/libs/CSSJanusTest.php
new file mode 100644
index 00000000..54f66077
--- /dev/null
+++ b/tests/phpunit/includes/libs/CSSJanusTest.php
@@ -0,0 +1,560 @@
+<?php
+/**
+ * Based on the test suite of the original Python
+ * CSSJanus libary:
+ * http://code.google.com/p/cssjanus/source/browse/trunk/cssjanus_test.py
+ * Ported to PHP for ResourceLoader and has been extended since.
+ */
+class CSSJanusTest extends MediaWikiTestCase {
+ /**
+ * @dataProvider provideTransformCases
+ */
+ function testTransform( $cssA, $cssB = null ) {
+
+ if ( $cssB ) {
+ $transformedA = CSSJanus::transform( $cssA );
+ $this->assertEquals( $transformedA, $cssB, 'Test A-B transformation' );
+
+ $transformedB = CSSJanus::transform( $cssB );
+ $this->assertEquals( $transformedB, $cssA, 'Test B-A transformation' );
+
+ // If no B version is provided, it means
+ // the output should equal the input.
+ } else {
+ $transformedA = CSSJanus::transform( $cssA );
+ $this->assertEquals( $transformedA, $cssA, 'Nothing was flipped' );
+ }
+ }
+
+ /**
+ * @dataProvider provideTransformAdvancedCases
+ */
+ function testTransformAdvanced( $code, $expectedOutput, $options = array() ) {
+ $swapLtrRtlInURL = isset( $options['swapLtrRtlInURL'] ) ? $options['swapLtrRtlInURL'] : false;
+ $swapLeftRightInURL = isset( $options['swapLeftRightInURL'] ) ? $options['swapLeftRightInURL'] : false;
+
+ $flipped = CSSJanus::transform( $code, $swapLtrRtlInURL, $swapLeftRightInURL );
+
+ $this->assertEquals( $expectedOutput, $flipped,
+ 'Test flipping, options: url-ltr-rtl=' . ($swapLtrRtlInURL ? 'true' : 'false')
+ . ' url-left-right=' . ($swapLeftRightInURL ? 'true' : 'false')
+ );
+ }
+ /**
+ * @dataProvider provideTransformBrokenCases
+ * @group Broken
+ */
+ function testTransformBroken( $code, $expectedOutput ) {
+ $flipped = CSSJanus::transform( $code );
+
+ $this->assertEquals( $expectedOutput, $flipped, 'Test flipping' );
+ }
+
+ /**
+ * These transform cases are tested *in both directions*
+ * No need to declare a principle twice in both directions here.
+ */
+ function provideTransformCases() {
+ return array(
+ // Property keys
+ array(
+ '.foo { left: 0; }',
+ '.foo { right: 0; }'
+ ),
+ // Guard against partial keys
+ // (CSS currently doesn't have flippable properties
+ // that contain the direction as part of the key without
+ // dash separation)
+ array(
+ '.foo { alright: 0; }'
+ ),
+ array(
+ '.foo { balleft: 0; }'
+ ),
+
+ // Dashed property keys
+ array(
+ '.foo { padding-left: 0; }',
+ '.foo { padding-right: 0; }'
+ ),
+ array(
+ '.foo { margin-left: 0; }',
+ '.foo { margin-right: 0; }'
+ ),
+ array(
+ '.foo { border-left: 0; }',
+ '.foo { border-right: 0; }'
+ ),
+
+ // Double-dashed property keys
+ array(
+ '.foo { border-left-color: red; }',
+ '.foo { border-right-color: red; }'
+ ),
+ array(
+ // Includes unknown properties?
+ '.foo { x-left-y: 0; }',
+ '.foo { x-right-y: 0; }'
+ ),
+
+ // Multi-value properties
+ array(
+ '.foo { padding: 0; }'
+ ),
+ array(
+ '.foo { padding: 0 1px; }'
+ ),
+ array(
+ '.foo { padding: 0 1px 2px; }'
+ ),
+ array(
+ '.foo { padding: 0 1px 2px 3px; }',
+ '.foo { padding: 0 3px 2px 1px; }'
+ ),
+
+ // Shorthand / Four notation
+ array(
+ '.foo { padding: .25em 15px 0pt 0ex; }',
+ '.foo { padding: .25em 0ex 0pt 15px; }'
+ ),
+ array(
+ '.foo { margin: 1px -4px 3px 2px; }',
+ '.foo { margin: 1px 2px 3px -4px; }'
+ ),
+ array(
+ '.foo { padding: 0 15px .25em 0; }',
+ '.foo { padding: 0 0 .25em 15px; }'
+ ),
+ array(
+ '.foo { padding: 1px 4.1grad 3px 2%; }',
+ '.foo { padding: 1px 2% 3px 4.1grad; }'
+ ),
+ array(
+ '.foo { padding: 1px 2px 3px auto; }',
+ '.foo { padding: 1px auto 3px 2px; }'
+ ),
+ array(
+ '.foo { padding: 1px inherit 3px auto; }',
+ '.foo { padding: 1px auto 3px inherit; }'
+ ),
+ array(
+ '.foo { border-radius: .25em 15px 0pt 0ex; }',
+ '.foo { border-radius: .25em 0ex 0pt 15px; }'
+ ),
+ array(
+ '.foo { x-unknown: a b c d; }'
+ ),
+ array(
+ '.foo barpx 0 2% { opacity: 0; }'
+ ),
+ array(
+ '#settings td p strong'
+ ),
+ array(
+ # Not sure how 4+ values should behave,
+ # testing to make sure changes are detected
+ '.foo { x-unknown: 1 2 3 4 5; }',
+ '.foo { x-unknown: 1 4 3 2 5; }',
+ ),
+ array(
+ '.foo { x-unknown: 1 2 3 4 5 6; }',
+ '.foo { x-unknown: 1 4 3 2 5 6; }',
+ ),
+
+ // Shorthand / Three notation
+ array(
+ '.foo { margin: 1em 0 .25em; }'
+ ),
+ array(
+ '.foo { margin:-1.5em 0 -.75em; }'
+ ),
+
+ // Shorthand / Two notation
+ array(
+ '.foo { padding: 1px 2px; }'
+ ),
+
+ // Shorthand / One notation
+ array(
+ '.foo { padding: 1px; }'
+ ),
+
+ // Direction
+ // Note: This differs from the Python implementation,
+ // see also CSSJanus::fixDirection for more info.
+ array(
+ '.foo { direction: ltr; }',
+ '.foo { direction: rtl; }'
+ ),
+ array(
+ '.foo { direction: rtl; }',
+ '.foo { direction: ltr; }'
+ ),
+ array(
+ 'input { direction: ltr; }',
+ 'input { direction: rtl; }'
+ ),
+ array(
+ 'input { direction: rtl; }',
+ 'input { direction: ltr; }'
+ ),
+ array(
+ 'body { direction: ltr; }',
+ 'body { direction: rtl; }'
+ ),
+ array(
+ '.foo, body, input { direction: ltr; }',
+ '.foo, body, input { direction: rtl; }'
+ ),
+ array(
+ 'body { padding: 10px; direction: ltr; }',
+ 'body { padding: 10px; direction: rtl; }'
+ ),
+ array(
+ 'body { direction: ltr } .myClass { direction: ltr }',
+ 'body { direction: rtl } .myClass { direction: rtl }'
+ ),
+
+ // Left/right values
+ array(
+ '.foo { float: left; }',
+ '.foo { float: right; }'
+ ),
+ array(
+ '.foo { text-align: left; }',
+ '.foo { text-align: right; }'
+ ),
+ array(
+ '.foo { -x-unknown: left; }',
+ '.foo { -x-unknown: right; }'
+ ),
+ // Guard against selectors that look flippable
+ array(
+ '.column-left { width: 0; }'
+ ),
+ array(
+ 'a.left { width: 0; }'
+ ),
+ array(
+ 'a.leftification { width: 0; }'
+ ),
+ array(
+ 'a.ltr { width: 0; }'
+ ),
+ array(
+ # <div class="a-ltr png">
+ '.a-ltr.png { width: 0; }'
+ ),
+ array(
+ # <foo-ltr attr="x">
+ 'foo-ltr[attr="x"] { width: 0; }'
+ ),
+ array(
+ 'div.left > span.right+span.left { width: 0; }'
+ ),
+ array(
+ '.thisclass .left .myclass { width: 0; }'
+ ),
+ array(
+ '.thisclass .left .myclass #myid { width: 0; }'
+ ),
+
+ // Cursor values (east/west)
+ array(
+ '.foo { cursor: e-resize; }',
+ '.foo { cursor: w-resize; }'
+ ),
+ array(
+ '.foo { cursor: se-resize; }',
+ '.foo { cursor: sw-resize; }'
+ ),
+ array(
+ '.foo { cursor: ne-resize; }',
+ '.foo { cursor: nw-resize; }'
+ ),
+
+ // Background
+ array(
+ '.foo { background-position: top left; }',
+ '.foo { background-position: top right; }'
+ ),
+ array(
+ '.foo { background: url(/foo/bar.png) top left; }',
+ '.foo { background: url(/foo/bar.png) top right; }'
+ ),
+ array(
+ '.foo { background: url(/foo/bar.png) top left no-repeat; }',
+ '.foo { background: url(/foo/bar.png) top right no-repeat; }'
+ ),
+ array(
+ '.foo { background: url(/foo/bar.png) no-repeat top left; }',
+ '.foo { background: url(/foo/bar.png) no-repeat top right; }'
+ ),
+ array(
+ '.foo { background: #fff url(/foo/bar.png) no-repeat top left; }',
+ '.foo { background: #fff url(/foo/bar.png) no-repeat top right; }'
+ ),
+ array(
+ '.foo { background-position: 100% 40%; }',
+ '.foo { background-position: 0% 40%; }'
+ ),
+ array(
+ '.foo { background-position: 23% 0; }',
+ '.foo { background-position: 77% 0; }'
+ ),
+ array(
+ '.foo { background-position: 23% auto; }',
+ '.foo { background-position: 77% auto; }'
+ ),
+ array(
+ '.foo { background-position-x: 23%; }',
+ '.foo { background-position-x: 77%; }'
+ ),
+ array(
+ '.foo { background-position-y: 23%; }',
+ '.foo { background-position-y: 23%; }'
+ ),
+ array(
+ '.foo { background:url(../foo.png) no-repeat 75% 50%; }',
+ '.foo { background:url(../foo.png) no-repeat 25% 50%; }'
+ ),
+ array(
+ '.foo { background: 10% 20% } .bar { background: 40% 30% }',
+ '.foo { background: 90% 20% } .bar { background: 60% 30% }'
+ ),
+
+ // Multiple rules
+ array(
+ 'body { direction: rtl; float: right; } .foo { direction: ltr; float: right; }',
+ 'body { direction: ltr; float: left; } .foo { direction: rtl; float: left; }',
+ ),
+
+ // Duplicate properties
+ array(
+ '.foo { float: left; float: right; float: left; }',
+ '.foo { float: right; float: left; float: right; }',
+ ),
+
+ // Preserve comments
+ array(
+ '/* left /* right */left: 10px',
+ '/* left /* right */right: 10px'
+ ),
+ array(
+ '/*left*//*left*/left: 10px',
+ '/*left*//*left*/right: 10px'
+ ),
+ array(
+ '/* Going right is cool */ .foo { width: 0 }',
+ ),
+ array(
+ "/* padding-right 1 2 3 4 */\n#test { width: 0}\n/*right*/"
+ ),
+ array(
+ "/** Two line comment\n * left\n \*/\n#test {width: 0}"
+ ),
+
+ // @noflip annotation
+ array(
+ // before selector (single)
+ '/* @noflip */ div { float: left; }'
+ ),
+ array(
+ // before selector (multiple)
+ '/* @noflip */ div, .notme { float: left; }'
+ ),
+ array(
+ // inside selector
+ 'div, /* @noflip */ .foo { float: left; }'
+ ),
+ array(
+ // after selector
+ 'div, .notme /* @noflip */ { float: left; }'
+ ),
+ array(
+ // before multiple rules
+ '/* @noflip */ div { float: left; } .foo { float: left; }',
+ '/* @noflip */ div { float: left; } .foo { float: right; }'
+ ),
+ array(
+ // after multiple rules
+ '.foo { float: left; } /* @noflip */ div { float: left; }',
+ '.foo { float: right; } /* @noflip */ div { float: left; }'
+ ),
+ array(
+ // before multiple properties
+ 'div { /* @noflip */ float: left; text-align: left; }',
+ 'div { /* @noflip */ float: left; text-align: right; }'
+ ),
+ array(
+ // after multiple properties
+ 'div { float: left; /* @noflip */ text-align: left; }',
+ 'div { float: right; /* @noflip */ text-align: left; }'
+ ),
+
+ // Guard against css3 stuff
+ array(
+ 'background-image: -moz-linear-gradient(#326cc1, #234e8c);'
+ ),
+ array(
+ 'background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#666666), to(#ffffff));'
+ ),
+
+ // CSS syntax / white-space variations
+ // spaces, no spaces, tabs, new lines, omitting semi-colons
+ array(
+ ".foo { left: 0; }",
+ ".foo { right: 0; }"
+ ),
+ array(
+ ".foo{ left: 0; }",
+ ".foo{ right: 0; }"
+ ),
+ array(
+ ".foo{ left: 0 }",
+ ".foo{ right: 0 }"
+ ),
+ array(
+ ".foo{left:0 }",
+ ".foo{right:0 }"
+ ),
+ array(
+ ".foo{left:0}",
+ ".foo{right:0}"
+ ),
+ array(
+ ".foo { left : 0 ; }",
+ ".foo { right : 0 ; }"
+ ),
+ array(
+ ".foo\n { left : 0 ; }",
+ ".foo\n { right : 0 ; }"
+ ),
+ array(
+ ".foo\n { \nleft : 0 ; }",
+ ".foo\n { \nright : 0 ; }"
+ ),
+ array(
+ ".foo\n { \n left : 0 ; }",
+ ".foo\n { \n right : 0 ; }"
+ ),
+ array(
+ ".foo\n { \n left\n : 0; }",
+ ".foo\n { \n right\n : 0; }"
+ ),
+ array(
+ ".foo \n { \n left\n : 0; }",
+ ".foo \n { \n right\n : 0; }"
+ ),
+ array(
+ ".foo\n{\nleft\n:\n0;}",
+ ".foo\n{\nright\n:\n0;}"
+ ),
+ array(
+ ".foo\n.bar {\n\tleft: 0;\n}",
+ ".foo\n.bar {\n\tright: 0;\n}"
+ ),
+ array(
+ ".foo\t{\tleft\t:\t0;}",
+ ".foo\t{\tright\t:\t0;}"
+ ),
+ );
+ }
+
+ /**
+ * These cases are tested in one way only (format: actual, expected, msg).
+ * If both ways can be tested, either put both versions in here or move
+ * it to provideTransformCases().
+ */
+ function provideTransformAdvancedCases() {
+ $bgPairs = array(
+ # [ - _ . ] <-> [ left right ltr rtl ]
+ 'foo.jpg' => 'foo.jpg',
+ 'left.jpg' => 'right.jpg',
+ 'ltr.jpg' => 'rtl.jpg',
+
+ 'foo-left.png' => 'foo-right.png',
+ 'foo_left.png' => 'foo_right.png',
+ 'foo.left.png' => 'foo.right.png',
+
+ 'foo-ltr.png' => 'foo-rtl.png',
+ 'foo_ltr.png' => 'foo_rtl.png',
+ 'foo.ltr.png' => 'foo.rtl.png',
+
+ 'left-foo.png' => 'right-foo.png',
+ 'left_foo.png' => 'right_foo.png',
+ 'left.foo.png' => 'right.foo.png',
+
+ 'ltr-foo.png' => 'rtl-foo.png',
+ 'ltr_foo.png' => 'rtl_foo.png',
+ 'ltr.foo.png' => 'rtl.foo.png',
+
+ 'foo-ltr-left.gif' => 'foo-rtl-right.gif',
+ 'foo_ltr_left.gif' => 'foo_rtl_right.gif',
+ 'foo.ltr.left.gif' => 'foo.rtl.right.gif',
+ 'foo-ltr_left.gif' => 'foo-rtl_right.gif',
+ 'foo_ltr.left.gif' => 'foo_rtl.right.gif',
+ );
+ $provider = array();
+ foreach ( $bgPairs as $left => $right ) {
+ # By default '-rtl' and '-left' etc. are not touched,
+ # Only when the appropiate parameter is set.
+ $provider[] = array(
+ ".foo { background: url(images/$left); }",
+ ".foo { background: url(images/$left); }"
+ );
+ $provider[] = array(
+ ".foo { background: url(images/$right); }",
+ ".foo { background: url(images/$right); }"
+ );
+ $provider[] = array(
+ ".foo { background: url(images/$left); }",
+ ".foo { background: url(images/$right); }",
+ array(
+ 'swapLtrRtlInURL' => true,
+ 'swapLeftRightInURL' => true,
+ )
+ );
+ $provider[] = array(
+ ".foo { background: url(images/$right); }",
+ ".foo { background: url(images/$left); }",
+ array(
+ 'swapLtrRtlInURL' => true,
+ 'swapLeftRightInURL' => true,
+ )
+ );
+ }
+
+ return $provider;
+ }
+
+ /**
+ * Cases that are currently failing, but
+ * should be looked at in the future as enhancements and/or bug fix
+ */
+ function provideTransformBrokenCases() {
+ return array(
+ // Guard against partial keys
+ array(
+ '.foo { leftxx: 0; }',
+ '.foo { leftxx: 0; }'
+ ),
+ array(
+ '.foo { rightxx: 0; }',
+ '.foo { rightxx: 0; }'
+ ),
+
+ // Guard against selectors that look flippable
+ array(
+ # <foo-left-x attr="x">
+ 'foo-left-x[attr="x"] { width: 0; }',
+ 'foo-left-x[attr="x"] { width: 0; }'
+ ),
+ array(
+ # <div class="foo" data-left="x">
+ '.foo[data-left="x"] { width: 0; }',
+ '.foo[data-left="x"] { width: 0; }'
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/libs/CSSMinTest.php b/tests/phpunit/includes/libs/CSSMinTest.php
new file mode 100644
index 00000000..a3827756
--- /dev/null
+++ b/tests/phpunit/includes/libs/CSSMinTest.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * This file test the CSSMin library shipped with Mediawiki.
+ *
+ * @author Timo Tijhof
+ */
+
+class CSSMinTest extends MediaWikiTestCase {
+ protected $oldServer = null, $oldCanServer = null;
+
+ function setUp() {
+ parent::setUp();
+
+ // Fake $wgServer and $wgCanonicalServer
+ global $wgServer, $wgCanonicalServer;
+ $this->oldServer = $wgServer;
+ $this->oldCanServer = $wgCanonicalServer;
+ $wgServer = $wgCanonicalServer = 'http://wiki.example.org';
+ }
+
+ function tearDown() {
+ // Restore $wgServer and $wgCanonicalServer
+ global $wgServer, $wgCanonicalServer;
+ $wgServer = $this->oldServer;
+ $wgCanonicalServer = $this->oldCanServer;
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider provideMinifyCases
+ */
+ function testMinify( $code, $expectedOutput ) {
+ $minified = CSSMin::minify( $code );
+
+ $this->assertEquals( $expectedOutput, $minified, 'Minified output should be in the form expected.' );
+ }
+
+ function provideMinifyCases() {
+ return array(
+ // Whitespace
+ array( "\r\t\f \v\n\r", "" ),
+ array( "foo, bar {\n\tprop: value;\n}", "foo,bar{prop:value}" ),
+
+ // Loose comments
+ array( "/* foo */", "" ),
+ array( "/*******\n foo\n *******/", "" ),
+ array( "/*!\n foo\n */", "" ),
+
+ // Inline comments in various different places
+ array( "/* comment */foo, bar {\n\tprop: value;\n}", "foo,bar{prop:value}" ),
+ array( "foo/* comment */, bar {\n\tprop: value;\n}", "foo,bar{prop:value}" ),
+ array( "foo,/* comment */ bar {\n\tprop: value;\n}", "foo,bar{prop:value}" ),
+ array( "foo, bar/* comment */ {\n\tprop: value;\n}", "foo,bar{prop:value}" ),
+ array( "foo, bar {\n\t/* comment */prop: value;\n}", "foo,bar{prop:value}" ),
+ array( "foo, bar {\n\tprop: /* comment */value;\n}", "foo,bar{prop:value}" ),
+ array( "foo, bar {\n\tprop: value /* comment */;\n}", "foo,bar{prop:value }" ),
+ array( "foo, bar {\n\tprop: value; /* comment */\n}", "foo,bar{prop:value; }" ),
+
+ // Keep track of things that aren't as minified as much as they
+ // could be (bug 35493)
+ array( 'foo { prop: value ;}', 'foo{prop:value }' ),
+ array( 'foo { prop : value; }', 'foo{prop :value}' ),
+ array( 'foo { prop: value ; }', 'foo{prop:value }' ),
+ array( 'foo { font-family: "foo" , "bar"; }', 'foo{font-family:"foo" ,"bar"}' ),
+ array( "foo { src:\n\turl('foo') ,\n\turl('bar') ; }", "foo{src:url('foo') ,url('bar') }" ),
+
+ // Interesting cases with string values
+ // - Double quotes, single quotes
+ array( 'foo { content: ""; }', 'foo{content:""}' ),
+ array( "foo { content: ''; }", "foo{content:''}" ),
+ array( 'foo { content: "\'"; }', 'foo{content:"\'"}' ),
+ array( "foo { content: '\"'; }", "foo{content:'\"'}" ),
+ // - Whitespace in string values
+ array( 'foo { content: " "; }', 'foo{content:" "}' ),
+ );
+ }
+
+ /**
+ * @dataProvider provideRemapCases
+ */
+ function testRemap( $message, $params, $expectedOutput ) {
+ $remapped = call_user_func_array( 'CSSMin::remap', $params );
+
+ $messageAdd = " Case: $message";
+ $this->assertEquals( $expectedOutput, $remapped, 'CSSMin::remap should return the expected url form.' . $messageAdd );
+ }
+
+ function provideRemapCases() {
+ // Parameter signature:
+ // CSSMin::remap( $code, $local, $remote, $embedData = true )
+ return array(
+ array(
+ 'Simple case',
+ array( 'foo { prop: url(bar.png); }', false, 'http://example.org', false ),
+ 'foo { prop: url(http://example.org/bar.png); }',
+ ),
+ array(
+ 'Without trailing slash',
+ array( 'foo { prop: url(../bar.png); }', false, 'http://example.org/quux', false ),
+ 'foo { prop: url(http://example.org/quux/../bar.png); }',
+ ),
+ array(
+ 'With trailing slash on remote (bug 27052)',
+ array( 'foo { prop: url(../bar.png); }', false, 'http://example.org/quux/', false ),
+ 'foo { prop: url(http://example.org/quux/../bar.png); }',
+ ),
+ array(
+ 'Guard against stripping double slashes from query',
+ array( 'foo { prop: url(bar.png?corge=//grault); }', false, 'http://example.org/quux/', false ),
+ 'foo { prop: url(http://example.org/quux/bar.png?corge=//grault); }',
+ ),
+ array(
+ 'Expand absolute paths',
+ array( 'foo { prop: url(/w/skin/images/bar.png); }', false, 'http://example.org/quux', false ),
+ 'foo { prop: url(http://wiki.example.org/w/skin/images/bar.png); }',
+ ),
+ );
+ }
+
+ /**
+ * Seperated because they are currently broken (bug 35492)
+ *
+ * @group Broken
+ * @dataProvider provideStringCases
+ */
+ function testMinifyWithCSSStringValues( $code, $expectedOutput ) {
+ $this->testMinifyOutput( $code, $expectedOutput );
+ }
+
+ function provideStringCases() {
+ return array(
+ // String values should be respected
+ // - More than one space in a string value
+ array( 'foo { content: " "; }', 'foo{content:" "}' ),
+ // - Using a tab in a string value (turns into a space)
+ array( "foo { content: '\t'; }", "foo{content:'\t'}" ),
+ // - Using css-like syntax in string values
+ array( 'foo::after { content: "{;}"; position: absolute; }', 'foo::after{content:"{;}";position:absolute}' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/libs/GenericArrayObjectTest.php b/tests/phpunit/includes/libs/GenericArrayObjectTest.php
new file mode 100644
index 00000000..70fce111
--- /dev/null
+++ b/tests/phpunit/includes/libs/GenericArrayObjectTest.php
@@ -0,0 +1,245 @@
+<?php
+
+/**
+ * Tests for the GenericArrayObject and deriving classes.
+ *
+ * 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.20
+ *
+ * @ingroup Test
+ * @group GenericArrayObject
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class GenericArrayObjectTest extends MediaWikiTestCase {
+
+ /**
+ * Returns objects that can serve as elements in the concrete GenericArrayObject deriving class being tested.
+ *
+ * @since 1.20
+ *
+ * @return array
+ */
+ public abstract function elementInstancesProvider();
+
+ /**
+ * Returns the name of the concrete class being tested.
+ *
+ * @since 1.20
+ *
+ * @return string
+ */
+ public abstract function getInstanceClass();
+
+ /**
+ * Provides instances of the concrete class being tested.
+ *
+ * @since 1.20
+ *
+ * @return array
+ */
+ public function instanceProvider() {
+ $instances = array();
+
+ foreach ( $this->elementInstancesProvider() as $elementInstances ) {
+ $instances[] = $this->getNew( $elementInstances );
+ }
+
+ return $this->arrayWrap( $instances );
+ }
+
+ /**
+ * @since 1.20
+ *
+ * @param array $elements
+ *
+ * @return GenericArrayObject
+ */
+ protected function getNew( array $elements = array() ) {
+ $class = $this->getInstanceClass();
+ return new $class( $elements );
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testConstructor( array $elements ) {
+ $arrayObject = $this->getNew( $elements );
+
+ $this->assertEquals( count( $elements ), $arrayObject->count() );
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testIsEmpty( array $elements ) {
+ $arrayObject = $this->getNew( $elements );
+
+ $this->assertEquals( $elements === array(), $arrayObject->isEmpty() );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @since 1.20
+ *
+ * @param GenericArrayObject $list
+ */
+ public function testUnset( GenericArrayObject $list ) {
+ if ( !$list->isEmpty() ) {
+ $offset = $list->getIterator()->key();
+ $count = $list->count();
+ $list->offsetUnset( $offset );
+ $this->assertEquals( $count - 1, $list->count() );
+ }
+
+ if ( !$list->isEmpty() ) {
+ $offset = $list->getIterator()->key();
+ $count = $list->count();
+ unset( $list[$offset] );
+ $this->assertEquals( $count - 1, $list->count() );
+ }
+
+ $exception = null;
+ try { $list->offsetUnset( 'sdfsedtgsrdysftu' ); } catch ( \Exception $exception ){}
+ $this->assertInstanceOf( '\Exception', $exception );
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testAppend( array $elements ) {
+ $list = $this->getNew();
+
+ $listSize = count( $elements );
+
+ foreach ( $elements as $element ) {
+ $list->append( $element );
+ }
+
+ $this->assertEquals( $listSize, $list->count() );
+
+ $list = $this->getNew();
+
+ foreach ( $elements as $element ) {
+ $list[] = $element;
+ }
+
+ $this->assertEquals( $listSize, $list->count() );
+
+ $this->checkTypeChecks( function( GenericArrayObject $list, $element ) {
+ $list->append( $element );
+ } );
+ }
+
+ /**
+ * @since 1.20
+ *
+ * @param callback $function
+ */
+ protected function checkTypeChecks( $function ) {
+ $excption = null;
+ $list = $this->getNew();
+
+ $elementClass = $list->getObjectType();
+
+ foreach ( array( 42, 'foo', array(), new \stdClass(), 4.2 ) as $element ) {
+ $validValid = $element instanceof $elementClass;
+
+ try{
+ call_user_func( $function, $list, $element );
+ $valid = true;
+ }
+ catch ( InvalidArgumentException $exception ) {
+ $valid = false;
+ }
+
+ $this->assertEquals(
+ $validValid,
+ $valid,
+ 'Object of invalid type got successfully added to a GenericArrayObject'
+ );
+ }
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testOffsetSet( array $elements ) {
+ if ( $elements === array() ) {
+ $this->assertTrue( true );
+ return;
+ }
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list->offsetSet( 42, $element );
+ $this->assertEquals( $element, $list->offsetGet( 42 ) );
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list['oHai'] = $element;
+ $this->assertEquals( $element, $list['oHai'] );
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list->offsetSet( 9001, $element );
+ $this->assertEquals( $element, $list[9001] );
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list->offsetSet( null, $element );
+ $this->assertEquals( $element, $list[0] );
+
+ $list = $this->getNew();
+ $offset = 0;
+
+ foreach ( $elements as $element ) {
+ $list->offsetSet( null, $element );
+ $this->assertEquals( $element, $list[$offset++] );
+ }
+
+ $this->assertEquals( count( $elements ), $list->count() );
+
+ $this->checkTypeChecks( function( GenericArrayObject $list, $element ) {
+ $list->offsetSet( mt_rand(), $element );
+ } );
+ }
+
+}
diff --git a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php
index d2bfeedf..f121b018 100644
--- a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php
+++ b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php
@@ -4,9 +4,18 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
function provideCases() {
return array(
- // Basic tokens
+
+ // Basic whitespace and comments that should be stripped entirely
array( "\r\t\f \v\n\r", "" ),
array( "/* Foo *\n*bar\n*/", "" ),
+
+ /**
+ * Slashes used inside block comments (bug 26931).
+ * At some point there was a bug that caused this comment to be ended at '* /',
+ * causing /M... to be left as the beginning of a regex.
+ */
+ array( "/**\n * Foo\n * {\n * 'bar' : {\n * //Multiple rules with configurable operators\n * 'baz' : false\n * }\n */", ""),
+
/**
* ' Foo \' bar \
* baz \' quox ' .
@@ -15,11 +24,13 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
array( "\" Foo \\\" bar \\\n baz \\\" quox \" .length", "\" Foo \\\" bar \\\n baz \\\" quox \".length" ),
array( "// Foo b/ar baz", "" ),
array( "/ Foo \\/ bar [ / \\] / ] baz / .length", "/ Foo \\/ bar [ / \\] / ] baz /.length" ),
+
// HTML comments
array( "<!-- Foo bar", "" ),
array( "<!-- Foo --> bar", "" ),
array( "--> Foo", "" ),
array( "x --> y", "x-->y" ),
+
// Semicolon insertion
array( "(function(){return\nx;})", "(function(){return\nx;})" ),
array( "throw\nx;", "throw\nx;" ),
@@ -35,12 +46,19 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
array( "5.\nx;", "5.\nx;" ),
array( "0xFF.\nx;", "0xFF.x;" ),
array( "5.3.\nx;", "5.3.x;" ),
+
+ // Semicolon insertion between an expression having an inline
+ // comment after it, and a statement on the next line (bug 27046).
+ array( "var a = this //foo bar \n for ( b = 0; c < d; b++ ) {}", "var a=this\nfor(b=0;c<d;b++){}" ),
+
// Token separation
array( "x in y", "x in y" ),
array( "/x/g in y", "/x/g in y" ),
array( "x in 30", "x in 30" ),
array( "x + ++ y", "x+ ++y" ),
+ array( "x ++ + y", "x++ +y" ),
array( "x / /y/.exec(z)", "x/ /y/.exec(z)" ),
+
// State machine
array( "/ x/g", "/ x/g" ),
array( "(function(){return/ x/g})", "(function(){return/ x/g})" ),
@@ -63,15 +81,18 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
array( "function x(){}/ x/g", "function x(){}/ x/g" ),
array( "+function x(){}/ x/g", "+function x(){}/x/g" ),
- // Tests for things that broke in the past
// Multiline quoted string
array( "var foo=\"\\\nblah\\\n\";", "var foo=\"\\\nblah\\\n\";" ),
+
// Multiline quoted string followed by string with spaces
array( "var foo=\"\\\nblah\\\n\";\nvar baz = \" foo \";\n", "var foo=\"\\\nblah\\\n\";var baz=\" foo \";" ),
+
// URL in quoted string ( // is not a comment)
array( "aNode.setAttribute('href','http://foo.bar.org/baz');", "aNode.setAttribute('href','http://foo.bar.org/baz');" ),
+
// URL in quoted string after multiline quoted string
array( "var foo=\"\\\nblah\\\n\";\naNode.setAttribute('href','http://foo.bar.org/baz');", "var foo=\"\\\nblah\\\n\";aNode.setAttribute('href','http://foo.bar.org/baz');" ),
+
// Division vs. regex nastiness
array( "alert( (10+10) / '/'.charCodeAt( 0 ) + '//' );", "alert((10+10)/'/'.charCodeAt(0)+'//');" ),
array( "if(1)/a /g.exec('Pa ss');", "if(1)/a /g.exec('Pa ss');" ),
@@ -81,11 +102,12 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
// Unicode letter characters should pass through ok in identifiers (bug 31187)
array( "var KaŝSkatolVal = {}", 'var KaŝSkatolVal={}'),
- // And also per spec unicode char escape values should work in identifiers,
+
+ // Per spec unicode char escape values should work in identifiers,
// as long as it's a valid char. In future it might get normalized.
array( "var Ka\\u015dSkatolVal = {}", 'var Ka\\u015dSkatolVal={}'),
- /* Some structures that might look invalid at first sight */
+ // Some structures that might look invalid at first sight
array( "var a = 5.;", "var a=5.;" ),
array( "5.0.toString();", "5.0.toString();" ),
array( "5..toString();", "5..toString();" ),
@@ -110,24 +132,6 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
$this->assertEquals( $expectedOutput, $minified, "Minified output should be in the form expected." );
}
- /**
- * @dataProvider provideBug32548
- */
- function testBug32548Exponent($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.
- $prefix = 'var longVarName' . str_repeat('_', 973) . '=';
- $suffix = ',shortVarName=0;';
-
- $input = $prefix . $num . $suffix;
- $expected = $prefix . "\n" . $num . $suffix;
-
- $minified = JavaScriptMinifier::minify( $input );
-
- $this->assertEquals( $expected, $minified, "Line breaks must not occur in middle of exponent");
- }
-
function provideBug32548() {
return array(
array(
@@ -145,4 +149,22 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
),
);
}
+
+ /**
+ * @dataProvider provideBug32548
+ */
+ function testBug32548Exponent( $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.
+ $prefix = 'var longVarName' . str_repeat( '_', 973 ) . '=';
+ $suffix = ',shortVarName=0;';
+
+ $input = $prefix . $num . $suffix;
+ $expected = $prefix . "\n" . $num . $suffix;
+
+ $minified = JavaScriptMinifier::minify( $input );
+
+ $this->assertEquals( $expected, $minified, "Line breaks must not occur in middle of exponent");
+ }
}
diff --git a/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php b/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
index f4f52dd8..88f87ef9 100644
--- a/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
+++ b/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
@@ -2,7 +2,7 @@
class BitmapMetadataHandlerTest extends MediaWikiTestCase {
public function setUp() {
- $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->filePath = __DIR__ . '/../../data/media/';
}
/**
@@ -73,7 +73,8 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
$this->assertEquals( '2020:07:14 01:36:05', $meta['DateTimeDigitized'] );
$this->assertEquals( '1997:03:02 00:01:02', $meta['DateTimeOriginal'] );
}
- /* File has an invalid time (+ one valid but really weird time)
+ /**
+ * File has an invalid time (+ one valid but really weird time)
* that shouldn't be included
*/
public function testIPTCDatesInvalid() {
diff --git a/tests/phpunit/includes/media/ExifRotationTest.php b/tests/phpunit/includes/media/ExifRotationTest.php
index 25149a05..6af52dd1 100644
--- a/tests/phpunit/includes/media/ExifRotationTest.php
+++ b/tests/phpunit/includes/media/ExifRotationTest.php
@@ -5,16 +5,12 @@
*/
class ExifRotationTest extends MediaWikiTestCase {
- /** track directories creations. Content will be deleted. */
- private $createdDirs = array();
-
function setUp() {
parent::setUp();
$this->handler = new BitmapHandler();
- $filePath = dirname( __FILE__ ) . '/../../data/media';
+ $filePath = __DIR__ . '/../../data/media';
- $tmpDir = wfTempDir() . '/exif-test-' . time() . '-' . mt_rand();
- $this->createdDirs[] = $tmpDir;
+ $tmpDir = $this->getNewTempDirectory();
$this->repo = new FSRepo( array(
'name' => 'temp',
@@ -42,17 +38,7 @@ class ExifRotationTest extends MediaWikiTestCase {
$wgShowEXIF = $this->show;
$wgEnableAutoRotation = $this->oldAuto;
- $this->tearDownFiles();
- }
-
- private function tearDownFiles() {
- foreach( $this->createdDirs as $dir ) {
- wfRecursiveRemoveDir( $dir );
- }
- }
-
- function __destruct() {
- $this->tearDownFiles();
+ parent::tearDown();
}
/**
diff --git a/tests/phpunit/includes/media/ExifTest.php b/tests/phpunit/includes/media/ExifTest.php
index b39c15e4..045777d7 100644
--- a/tests/phpunit/includes/media/ExifTest.php
+++ b/tests/phpunit/includes/media/ExifTest.php
@@ -2,22 +2,22 @@
class ExifTest extends MediaWikiTestCase {
public function setUp() {
- $this->mediaPath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->mediaPath = __DIR__ . '/../../data/media/';
if ( !wfDl( 'exif' ) ) {
$this->markTestSkipped( "This test needs the exif extension." );
}
- global $wgShowEXIF;
- $this->showExif = $wgShowEXIF;
- $wgShowEXIF = true;
+ global $wgShowEXIF;
+ $this->showExif = $wgShowEXIF;
+ $wgShowEXIF = true;
}
- public function tearDown() {
- global $wgShowEXIF;
- $wgShowEXIF = $this->showExif;
- }
- public function testGPSExtraction() {
+ public function tearDown() {
+ global $wgShowEXIF;
+ $wgShowEXIF = $this->showExif;
+ }
+ public function testGPSExtraction() {
$filename = $this->mediaPath . 'exif-gps.jpg';
$seg = JpegMetadataExtractor::segmentSplitter( $filename );
$exif = new Exif( $filename, $seg['byteOrder'] );
@@ -25,14 +25,14 @@ class ExifTest extends MediaWikiTestCase {
$expected = array(
'GPSLatitude' => 88.5180555556,
'GPSLongitude' => -21.12357,
- 'GPSAltitude' => -200,
+ 'GPSAltitude' => -3.141592653,
'GPSDOP' => '5/1',
'GPSVersionID' => '2.2.0.0',
);
$this->assertEquals( $expected, $data, '', 0.0000000001 );
}
- public function testUnicodeUserComment() {
+ public function testUnicodeUserComment() {
$filename = $this->mediaPath . 'exif-user-comment.jpg';
$seg = JpegMetadataExtractor::segmentSplitter( $filename );
$exif = new Exif( $filename, $seg['byteOrder'] );
diff --git a/tests/phpunit/includes/media/FormatMetadataTest.php b/tests/phpunit/includes/media/FormatMetadataTest.php
index 8a632f52..6ade6702 100644
--- a/tests/phpunit/includes/media/FormatMetadataTest.php
+++ b/tests/phpunit/includes/media/FormatMetadataTest.php
@@ -4,7 +4,7 @@ class FormatMetadataTest extends MediaWikiTestCase {
if ( !wfDl( 'exif' ) ) {
$this->markTestSkipped( "This test needs the exif extension." );
}
- $filePath = dirname( __FILE__ ) . '/../../data/media';
+ $filePath = __DIR__ . '/../../data/media';
$this->backend = new FSFileBackend( array(
'name' => 'localtesting',
'lockManager' => 'nullLockManager',
diff --git a/tests/phpunit/includes/media/GIFMetadataExtractorTest.php b/tests/phpunit/includes/media/GIFMetadataExtractorTest.php
index 47fc368b..650fdd5c 100644
--- a/tests/phpunit/includes/media/GIFMetadataExtractorTest.php
+++ b/tests/phpunit/includes/media/GIFMetadataExtractorTest.php
@@ -2,7 +2,7 @@
class GIFMetadataExtractorTest extends MediaWikiTestCase {
public function setUp() {
- $this->mediaPath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->mediaPath = __DIR__ . '/../../data/media/';
}
/**
* Put in a file, and see if the metadata coming out is as expected.
diff --git a/tests/phpunit/includes/media/GIFTest.php b/tests/phpunit/includes/media/GIFTest.php
index 36658358..5dcbeee0 100644
--- a/tests/phpunit/includes/media/GIFTest.php
+++ b/tests/phpunit/includes/media/GIFTest.php
@@ -2,7 +2,7 @@
class GIFHandlerTest extends MediaWikiTestCase {
public function setUp() {
- $this->filePath = dirname( __FILE__ ) . '/../../data/media';
+ $this->filePath = __DIR__ . '/../../data/media';
$this->backend = new FSFileBackend( array(
'name' => 'localtesting',
'lockManager' => 'nullLockManager',
diff --git a/tests/phpunit/includes/media/JpegMetadataExtractorTest.php b/tests/phpunit/includes/media/JpegMetadataExtractorTest.php
index f48382a4..41d81190 100644
--- a/tests/phpunit/includes/media/JpegMetadataExtractorTest.php
+++ b/tests/phpunit/includes/media/JpegMetadataExtractorTest.php
@@ -9,7 +9,7 @@
class JpegMetadataExtractorTest extends MediaWikiTestCase {
public function setUp() {
- $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->filePath = __DIR__ . '/../../data/media/';
}
/**
diff --git a/tests/phpunit/includes/media/JpegTest.php b/tests/phpunit/includes/media/JpegTest.php
index ddabf5b8..ea007f90 100644
--- a/tests/phpunit/includes/media/JpegTest.php
+++ b/tests/phpunit/includes/media/JpegTest.php
@@ -2,7 +2,7 @@
class JpegTest extends MediaWikiTestCase {
public function setUp() {
- $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->filePath = __DIR__ . '/../../data/media/';
if ( !wfDl( 'exif' ) ) {
$this->markTestSkipped( "This test needs the exif extension." );
}
diff --git a/tests/phpunit/includes/media/PNGMetadataExtractorTest.php b/tests/phpunit/includes/media/PNGMetadataExtractorTest.php
index 9f702c50..1b1b2ec3 100644
--- a/tests/phpunit/includes/media/PNGMetadataExtractorTest.php
+++ b/tests/phpunit/includes/media/PNGMetadataExtractorTest.php
@@ -2,7 +2,7 @@
class PNGMetadataExtractorTest extends MediaWikiTestCase {
function setUp() {
- $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->filePath = __DIR__ . '/../../data/media/';
}
/**
* Tests zTXt tag (compressed textual metadata)
diff --git a/tests/phpunit/includes/media/PNGTest.php b/tests/phpunit/includes/media/PNGTest.php
index b6f911fd..fe73c9c7 100644
--- a/tests/phpunit/includes/media/PNGTest.php
+++ b/tests/phpunit/includes/media/PNGTest.php
@@ -2,7 +2,7 @@
class PNGHandlerTest extends MediaWikiTestCase {
public function setUp() {
- $this->filePath = dirname( __FILE__ ) . '/../../data/media';
+ $this->filePath = __DIR__ . '/../../data/media';
$this->backend = new FSFileBackend( array(
'name' => 'localtesting',
'lockManager' => 'nullLockManager',
diff --git a/tests/phpunit/includes/media/SVGMetadataExtractorTest.php b/tests/phpunit/includes/media/SVGMetadataExtractorTest.php
index 526beae8..2116554e 100644
--- a/tests/phpunit/includes/media/SVGMetadataExtractorTest.php
+++ b/tests/phpunit/includes/media/SVGMetadataExtractorTest.php
@@ -39,27 +39,33 @@ class SVGMetadataExtractorTest extends MediaWikiTestCase {
}
function providerSvgFiles() {
- $base = dirname( __FILE__ ) . '/../../data/media';
+ $base = __DIR__ . '/../../data/media';
return array(
array(
"$base/Wikimedia-logo.svg",
array(
'width' => 1024,
- 'height' => 1024
+ 'height' => 1024,
+ 'originalWidth' => '1024',
+ 'originalHeight' => '1024',
)
),
array(
"$base/QA_icon.svg",
array(
'width' => 60,
- 'height' => 60
+ 'height' => 60,
+ 'originalWidth' => '60',
+ 'originalHeight' => '60',
)
),
array(
"$base/Gtk-media-play-ltr.svg",
array(
'width' => 60,
- 'height' => 60
+ 'height' => 60,
+ 'originalWidth' => '60.0000000',
+ 'originalHeight' => '60.0000000',
)
),
array(
@@ -67,14 +73,16 @@ class SVGMetadataExtractorTest extends MediaWikiTestCase {
// This file triggered bug 31719, needs entity expansion in the xmlns checks
array(
'width' => 385,
- 'height' => 385
+ 'height' => 385,
+ 'originalWidth' => '385',
+ 'originalHeight' => '385.0004883',
)
)
);
}
function providerSvgFilesWithXMLMetadata() {
- $base = dirname( __FILE__ ) . '/../../data/media';
+ $base = __DIR__ . '/../../data/media';
$metadata =
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<ns4:Work xmlns:ns4="http://creativecommons.org/ns#" rdf:about="">
@@ -89,7 +97,9 @@ class SVGMetadataExtractorTest extends MediaWikiTestCase {
array(
'height' => 593,
'metadata' => $metadata,
- 'width' => 959
+ 'width' => 959,
+ 'originalWidth' => '958.69',
+ 'originalHeight' => '592.78998',
)
),
);
diff --git a/tests/phpunit/includes/media/TiffTest.php b/tests/phpunit/includes/media/TiffTest.php
index d4cf503b..4c79f66c 100644
--- a/tests/phpunit/includes/media/TiffTest.php
+++ b/tests/phpunit/includes/media/TiffTest.php
@@ -5,7 +5,7 @@ class TiffTest extends MediaWikiTestCase {
global $wgShowEXIF;
$this->showExif = $wgShowEXIF;
$wgShowEXIF = true;
- $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->filePath = __DIR__ . '/../../data/media/';
$this->handler = new TiffHandler;
}
diff --git a/tests/phpunit/includes/media/XMPTest.php b/tests/phpunit/includes/media/XMPTest.php
index c952b23c..8198d3b0 100644
--- a/tests/phpunit/includes/media/XMPTest.php
+++ b/tests/phpunit/includes/media/XMPTest.php
@@ -22,11 +22,11 @@ class XMPTest extends MediaWikiTestCase {
}
$reader = new XMPReader;
$reader->parse( $xmp );
- $this->assertEquals( $expected, $reader->getResults(), $info );
+ $this->assertEquals( $expected, $reader->getResults(), $info, 0.0000000001 );
}
public function dataXMPParse() {
- $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/' ;
+ $xmpPath = __DIR__ . '/../../data/xmp/' ;
$data = array();
// $xmpFiles format: array of arrays with first arg file base name,
@@ -52,6 +52,7 @@ class XMPTest extends MediaWikiTestCase {
array( 'utf32BE', 'UTF-32BE encoding' ),
array( 'utf32LE', 'UTF-32LE encoding' ),
array( 'xmpExt', 'Extended XMP missing second part' ),
+ array( 'gps', 'Handling of exif GPS parameters in XMP' ),
);
foreach( $xmpFiles as $file ) {
$xmp = file_get_contents( $xmpPath . $file[0] . '.xmp' );
@@ -72,7 +73,7 @@ class XMPTest extends MediaWikiTestCase {
* world example file to double check the support for this is right.
*/
function testExtendedXMP() {
- $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/';
+ $xmpPath = __DIR__ . '/../../data/xmp/';
$standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
$extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
@@ -102,7 +103,7 @@ class XMPTest extends MediaWikiTestCase {
* and thus should only return the StandardXMP, not the ExtendedXMP.
*/
function testExtendedXMPWithWrongGUID() {
- $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/';
+ $xmpPath = __DIR__ . '/../../data/xmp/';
$standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
$extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
@@ -130,7 +131,7 @@ class XMPTest extends MediaWikiTestCase {
* which should cause it to ignore the ExtendedXMP packet.
*/
function testExtendedXMPMissingPacket() {
- $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/';
+ $xmpPath = __DIR__ . '/../../data/xmp/';
$standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
$extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
diff --git a/tests/phpunit/includes/mobile/DeviceDetectionTest.php b/tests/phpunit/includes/mobile/DeviceDetectionTest.php
new file mode 100644
index 00000000..0e156532
--- /dev/null
+++ b/tests/phpunit/includes/mobile/DeviceDetectionTest.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @group Mobile
+ */
+ class DeviceDetectionTest extends MediaWikiTestCase {
+
+ /**
+ * @dataProvider provideTestFormatName
+ */
+ public function testFormatName( $format, $userAgent ) {
+ $detector = new DeviceDetection();
+ $this->assertEquals( $format, $detector->detectFormatName( $userAgent ) );
+ }
+
+ public function provideTestFormatName() {
+ return array(
+ array( 'android', 'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17' ),
+ array( 'iphone2', 'Mozilla/5.0 (ipod: U;CPU iPhone OS 2_2 like Mac OS X: es_es) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.0 Mobile/3B48b Safari/419.3' ),
+ array( 'iphone', 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3B48b Safari/419.3' ),
+ array( 'nokia', 'Mozilla/5.0 (SymbianOS/9.1; U; [en]; SymbianOS/91 Series60/3.0) AppleWebKit/413 (KHTML, like Gecko) Safari/413' ),
+ array( 'palm_pre', 'Mozilla/5.0 (webOS/1.0; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Pre/1.0' ),
+ array( 'wii', 'Opera/9.00 (Nintendo Wii; U; ; 1309-9; en)' ),
+ array( 'operamini', 'Opera/9.50 (J2ME/MIDP; Opera Mini/4.0.10031/298; U; en)' ),
+ array( 'operamobile', 'Opera/9.51 Beta (Microsoft Windows; PPC; Opera Mobi/1718; U; en)' ),
+ array( 'kindle', 'Mozilla/4.0 (compatible; Linux 2.6.10) NetFront/3.3 Kindle/1.0 (screen 600x800)' ),
+ array( 'kindle2', 'Mozilla/4.0 (compatible; Linux 2.6.22) NetFront/3.4 Kindle/2.0 (screen 824x1200; rotate)' ),
+ array( 'capable', 'Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1' ),
+ array( 'netfront', 'Mozilla/4.08 (Windows; Mobile Content Viewer/1.0) NetFront/3.2' ),
+ array( 'wap2', 'SonyEricssonK608i/R2L/SN356841000828910 Browser/SEMC-Browser/4.2 Profile/MIDP-2.0 Configuration/CLDC-1.1' ),
+ array( 'wap2', 'NokiaN73-2/3.0-630.0.2 Series60/3.0 Profile/MIDP-2.0 Configuration/CLDC-1.1' ),
+ array( 'psp', 'Mozilla/4.0 (PSP (PlayStation Portable); 2.00)' ),
+ array( 'ps3', 'Mozilla/5.0 (PLAYSTATION 3; 1.00)' ),
+ array( 'ie', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' ),
+ array( 'ie', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)' ),
+ array( 'blackberry', 'BlackBerry9300/5.0.0.716 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/133' ),
+ array( 'blackberry-lt5', 'BlackBerry7250/4.0.0 Profile/MIDP-2.0 Configuration/CLDC-1.1' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/parser/MediaWikiParserTest.php b/tests/phpunit/includes/parser/MediaWikiParserTest.php
index 816c017a..6a6fded1 100644
--- a/tests/phpunit/includes/parser/MediaWikiParserTest.php
+++ b/tests/phpunit/includes/parser/MediaWikiParserTest.php
@@ -1,5 +1,5 @@
<?php
-require_once( dirname( __FILE__ ) . '/NewParserTest.php' );
+require_once( __DIR__ . '/NewParserTest.php' );
/**
* The UnitTest must be either a class that inherits from MediaWikiTestCase
diff --git a/tests/phpunit/includes/parser/NewParserTest.php b/tests/phpunit/includes/parser/NewParserTest.php
index d9b16710..69a96e66 100644
--- a/tests/phpunit/includes/parser/NewParserTest.php
+++ b/tests/phpunit/includes/parser/NewParserTest.php
@@ -186,7 +186,7 @@ class NewParserTest extends MediaWikiTestCase {
if ( !$this->db->selectField( 'image', '1', array( 'img_name' => $image->getName() ) ) ) {
$image->recordUpload2(
'', // archive name
- 'Upload of some lame file',
+ 'Upload of some lame file',
'Some lame file',
array(
'size' => 12345,
@@ -197,7 +197,7 @@ class NewParserTest extends MediaWikiTestCase {
'mime' => 'image/jpeg',
'metadata' => serialize( array() ),
'sha1' => wfBaseConvert( '', 16, 36, 31 ),
- 'fileExists' => true ),
+ 'fileExists' => true ),
$this->db->timestamp( '20010115123500' ), $user
);
}
@@ -207,8 +207,8 @@ class NewParserTest extends MediaWikiTestCase {
if ( !$this->db->selectField( 'image', '1', array( 'img_name' => $image->getName() ) ) ) {
$image->recordUpload2(
'', // archive name
- 'zomgnotcensored',
- 'Borderline image',
+ 'zomgnotcensored',
+ 'Borderline image',
array(
'size' => 12345,
'width' => 320,
@@ -218,7 +218,7 @@ class NewParserTest extends MediaWikiTestCase {
'mime' => 'image/jpeg',
'metadata' => serialize( array() ),
'sha1' => wfBaseConvert( '', 16, 36, 31 ),
- 'fileExists' => true ),
+ 'fileExists' => true ),
$this->db->timestamp( '20010115123500' ), $user
);
}
@@ -326,7 +326,6 @@ class NewParserTest extends MediaWikiTestCase {
'wgExternalLinkTarget' => false,
'wgAlwaysUseTidy' => false,
'wgHtml5' => true,
- 'wgCleanupPresentationalAttributes' => true,
'wgWellFormedXml' => true,
'wgAllowMicrodataAttributes' => true,
'wgAdaptiveMessageCache' => true,
@@ -345,6 +344,9 @@ class NewParserTest extends MediaWikiTestCase {
$this->savedGlobals = array();
+ /** @since 1.20 */
+ wfRunHooks( 'ParserTestGlobals', array( &$settings ) );
+
foreach ( $settings as $var => $val ) {
if ( array_key_exists( $var, $GLOBALS ) ) {
$this->savedGlobals[$var] = $GLOBALS[$var];
@@ -380,7 +382,7 @@ class NewParserTest extends MediaWikiTestCase {
# The entries saved into RepoGroup cache with previous globals will be wrong.
RepoGroup::destroySingleton();
FileBackendGroup::destroySingleton();
- MessageCache::singleton()->destroyInstance();
+ MessageCache::destroyInstance();
return $context;
}
@@ -596,7 +598,7 @@ class NewParserTest extends MediaWikiTestCase {
* Run a fuzz test series
* Draw input from a set of test files
*
- * @todo @fixme Needs some work to not eat memory until the world explodes
+ * @todo fixme Needs some work to not eat memory until the world explodes
*
* @group ParserFuzz
*/
diff --git a/tests/phpunit/includes/parser/ParserMethodsTest.php b/tests/phpunit/includes/parser/ParserMethodsTest.php
new file mode 100644
index 00000000..dea406c3
--- /dev/null
+++ b/tests/phpunit/includes/parser/ParserMethodsTest.php
@@ -0,0 +1,33 @@
+<?php
+
+class ParserMethodsTest extends MediaWikiLangTestCase {
+
+ public function dataPreSaveTransform() {
+ return array(
+ array( 'hello this is ~~~',
+ "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
+ ),
+ array( 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
+ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataPreSaveTransform
+ */
+ public function testPreSaveTransform( $text, $expected ) {
+ global $wgParser;
+
+ $title = Title::newFromText( str_replace( '::', '__', __METHOD__ ) );
+ $user = new User();
+ $user->setName( "127.0.0.1" );
+ $popts = ParserOptions::newFromUser( $user );
+ $text = $wgParser->preSaveTransform( $text, $title, $user, $popts );
+
+ $this->assertEquals( $expected, $text );
+ }
+
+ // TODO: Add tests for cleanSig() / cleanSigInSig(), getSection(), replaceSection(), getPreloadText()
+}
+
diff --git a/tests/phpunit/includes/parser/PreprocessorTest.php b/tests/phpunit/includes/parser/PreprocessorTest.php
index 9d3499a0..fee56748 100644
--- a/tests/phpunit/includes/parser/PreprocessorTest.php
+++ b/tests/phpunit/includes/parser/PreprocessorTest.php
@@ -103,7 +103,7 @@ class PreprocessorTest extends MediaWikiTestCase {
array( "{{foo|bar=|}", "<root>{{foo|bar=|}</root>"),
array( "{{Foo|} Bar=", "<root>{{Foo|} Bar=</root>"),
array( "{{Foo|} Bar=}}", "<root><template><title>Foo</title><part><name>} Bar</name>=<value></value></part></template></root>"),
- /* array( file_get_contents( dirname( __FILE__ ) . '/QuoteQuran.txt' ), file_get_contents( dirname( __FILE__ ) . '/QuoteQuranExpanded.txt' ) ), */
+ /* array( file_get_contents( __DIR__ . '/QuoteQuran.txt' ), file_get_contents( __DIR__ . '/QuoteQuranExpanded.txt' ) ), */
);
}
@@ -165,7 +165,7 @@ class PreprocessorTest extends MediaWikiTestCase {
* @dataProvider provideFiles
*/
function testPreprocessorOutputFiles( $filename ) {
- $folder = dirname( __FILE__ ) . "/../../../parser/preprocess";
+ $folder = __DIR__ . "/../../../parser/preprocess";
$wikiText = file_get_contents( "$folder/$filename.txt" );
$output = $this->preprocessToXml( $wikiText );
diff --git a/tests/phpunit/includes/specials/SpecialSearchTest.php b/tests/phpunit/includes/specials/SpecialSearchTest.php
index ea9d5533..20e42a68 100644
--- a/tests/phpunit/includes/specials/SpecialSearchTest.php
+++ b/tests/phpunit/includes/specials/SpecialSearchTest.php
@@ -87,6 +87,14 @@ class SpecialSearchTest extends MediaWikiTestCase {
'advanced', array( 2, 14 ),
'Bug 33583: search with no option should honor User search preferences'
),
+ array(
+ $EMPTY_REQUEST, array_fill_keys( array_map( function( $ns ) {
+ return "searchNs$ns";
+ }, $defaultNS ), 0 ) + array( 'searchNs2' => 1, 'searchNs14' => 1 ),
+ 'advanced', array( 2, 14 ),
+ 'Bug 33583: search with no option should honor User search preferences'
+ . 'and have all other namespace disabled'
+ ),
);
}
diff --git a/tests/phpunit/includes/upload/UploadFromUrlTest.php b/tests/phpunit/includes/upload/UploadFromUrlTest.php
index d56cce31..f66c387b 100644
--- a/tests/phpunit/includes/upload/UploadFromUrlTest.php
+++ b/tests/phpunit/includes/upload/UploadFromUrlTest.php
@@ -20,7 +20,7 @@ class UploadFromUrlTest extends ApiTestCase {
}
}
- protected function doApiRequest( $params, $unused = null, $appendModule = false, $user = null ) {
+ protected function doApiRequest( Array $params, Array $unused = null, $appendModule = false, User $user = null ) {
$sessionId = session_id();
session_write_close();
@@ -228,11 +228,11 @@ class UploadFromUrlTest extends ApiTestCase {
$talk = $this->user->user->getTalkPage();
if ( $talk->exists() ) {
- $a = new Article( $talk );
- $a->doDeleteArticle( '' );
+ $page = WikiPage::factory( $talk );
+ $page->doDeleteArticle( '' );
}
- $this->assertFalse( (bool)$talk->getArticleId( Title::GAID_FOR_UPDATE ), 'User talk does not exist' );
+ $this->assertFalse( (bool)$talk->getArticleID( Title::GAID_FOR_UPDATE ), 'User talk does not exist' );
$data = $this->doApiRequest( array(
'action' => 'upload',
@@ -249,7 +249,7 @@ class UploadFromUrlTest extends ApiTestCase {
$job->run();
$this->assertTrue( wfLocalFile( 'UploadFromUrlTest.png' )->exists() );
- $this->assertTrue( (bool)$talk->getArticleId( Title::GAID_FOR_UPDATE ), 'User talk exists' );
+ $this->assertTrue( (bool)$talk->getArticleID( Title::GAID_FOR_UPDATE ), 'User talk exists' );
$this->deleteFile( 'UploadFromUrlTest.png' );
@@ -341,8 +341,8 @@ class UploadFromUrlTest extends ApiTestCase {
$file = wfFindFile( $name, array( 'ignoreRedirect' => true ) );
$empty = "";
FileDeleteForm::doDelete( $t, $file, $empty, "none", true );
- $a = new Article ( $t );
- $a->doDeleteArticle( "testing" );
+ $page = WikiPage::factory( $t );
+ $page->doDeleteArticle( "testing" );
}
$t = Title::newFromText( $name, NS_FILE );
diff --git a/tests/phpunit/includes/upload/UploadStashTest.php b/tests/phpunit/includes/upload/UploadStashTest.php
index c9dbb138..66fafaaf 100644
--- a/tests/phpunit/includes/upload/UploadStashTest.php
+++ b/tests/phpunit/includes/upload/UploadStashTest.php
@@ -12,17 +12,17 @@ class UploadStashTest extends MediaWikiTestCase {
parent::setUp();
// Setup a file for bug 29408
- $this->bug29408File = dirname( __FILE__ ) . '/bug29408';
+ $this->bug29408File = __DIR__ . '/bug29408';
file_put_contents( $this->bug29408File, "\x00" );
self::$users = array(
- 'sysop' => new ApiTestUser(
+ 'sysop' => new TestUser(
'Uploadstashtestsysop',
'Upload Stash Test Sysop',
'upload_stash_test_sysop@example.com',
array( 'sysop' )
),
- 'uploader' => new ApiTestUser(
+ 'uploader' => new TestUser(
'Uploadstashtestuser',
'Upload Stash Test User',
'upload_stash_test_user@example.com',
diff --git a/tests/phpunit/includes/upload/UploadTest.php b/tests/phpunit/includes/upload/UploadTest.php
index 4293d23b..6948f5b1 100644
--- a/tests/phpunit/includes/upload/UploadTest.php
+++ b/tests/phpunit/includes/upload/UploadTest.php
@@ -12,7 +12,9 @@ class UploadTest extends MediaWikiTestCase {
$this->upload = new UploadTestHandler;
$this->hooks = $wgHooks;
- $wgHooks['InterwikiLoadPrefix'][] = 'MediaWikiTestCase::disableInterwikis';
+ $wgHooks['InterwikiLoadPrefix'][] = function( $prefix, &$data ) {
+ return false;
+ };
}
function tearDown() {