From 9441dde8bfb95277df073717ed7817dced40f948 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Fri, 28 Mar 2014 05:41:12 +0100 Subject: Update to MediaWiki 1.22.5 --- tests/phpunit/includes/cache/GenderCacheTest.php | 104 +++++++++ tests/phpunit/includes/cache/MessageCacheTest.php | 128 +++++++++++ .../phpunit/includes/cache/ProcessCacheLRUTest.php | 237 +++++++++++++++++++++ 3 files changed, 469 insertions(+) create mode 100644 tests/phpunit/includes/cache/GenderCacheTest.php create mode 100644 tests/phpunit/includes/cache/MessageCacheTest.php create mode 100644 tests/phpunit/includes/cache/ProcessCacheLRUTest.php (limited to 'tests/phpunit/includes/cache') diff --git a/tests/phpunit/includes/cache/GenderCacheTest.php b/tests/phpunit/includes/cache/GenderCacheTest.php new file mode 100644 index 00000000..ce2db5d7 --- /dev/null +++ b/tests/phpunit/includes/cache/GenderCacheTest.php @@ -0,0 +1,104 @@ +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 provideUserGenders + * @covers GenderCache::getGenderOf + */ + public 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 provideUserGenders + * @covers GenderCache::getGenderOf + */ + public function testUserObjects( $username, $expectedGender ) { + $genderCache = GenderCache::singleton(); + $user = User::newFromName( $username ); + $gender = $genderCache->getGenderOf( $user ); + $this->assertEquals( $gender, $expectedGender, "GenderCache normal" ); + } + + public static function provideUserGenders() { + 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 provideStripSubpages + * @covers GenderCache::getGenderOf + */ + public function testStripSubpages( $pageWithSubpage, $expectedGender ) { + $genderCache = GenderCache::singleton(); + $gender = $genderCache->getGenderOf( $pageWithSubpage ); + $this->assertEquals( $gender, $expectedGender, "GenderCache must strip of subpages" ); + } + + public static function provideStripSubpages() { + 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/MessageCacheTest.php b/tests/phpunit/includes/cache/MessageCacheTest.php new file mode 100644 index 00000000..803acf73 --- /dev/null +++ b/tests/phpunit/includes/cache/MessageCacheTest.php @@ -0,0 +1,128 @@ +configureLanguages(); + MessageCache::singleton()->enable(); + } + + /** + * Helper function -- setup site language for testing + */ + protected function configureLanguages() { + // for the test, we need the content language to be anything but English, + // let's choose e.g. German (de) + $langCode = 'de'; + $langObj = Language::factory( $langCode ); + + $this->setMwGlobals( array( + 'wgLanguageCode' => $langCode, + 'wgLang' => $langObj, + 'wgContLang' => $langObj, + ) ); + } + + function addDBData() { + $this->configureLanguages(); + + // Set up messages and fallbacks ab -> ru -> de + $this->makePage( 'FallbackLanguageTest-Full', 'ab' ); + $this->makePage( 'FallbackLanguageTest-Full', 'ru' ); + $this->makePage( 'FallbackLanguageTest-Full', 'de' ); + + // Fallbacks where ab does not exist + $this->makePage( 'FallbackLanguageTest-Partial', 'ru' ); + $this->makePage( 'FallbackLanguageTest-Partial', 'de' ); + + // Fallback to the content language + $this->makePage( 'FallbackLanguageTest-ContLang', 'de' ); + + // Add customizations for an existing message. + $this->makePage( 'sunday', 'ru' ); + + // Full key tests -- always want russian + $this->makePage( 'MessageCacheTest-FullKeyTest', 'ab' ); + $this->makePage( 'MessageCacheTest-FullKeyTest', 'ru' ); + + // In content language -- get base if no derivative + $this->makePage( 'FallbackLanguageTest-NoDervContLang', 'de', 'de/none', false ); + } + + /** + * Helper function for addDBData -- adds a simple page to the database + * + * @param string $title Title of page to be created + * @param string $lang Language and content of the created page + * @param string|null $content Content of the created page, or null for a generic string + * @param bool $createSubPage Set to false if a root page should be created + */ + protected function makePage( $title, $lang, $content = null, $createSubPage = true ) { + global $wgContLang; + + if ( $content === null ) { + $content = $lang; + } + if ( $lang !== $wgContLang->getCode() || $createSubPage ) { + $title = "$title/$lang"; + } + + $title = Title::newFromText( $title, NS_MEDIAWIKI ); + $wikiPage = new WikiPage( $title ); + $contentHandler = ContentHandler::makeContent( $content, $title ); + $wikiPage->doEditContent( $contentHandler, "$lang translation test case" ); + } + + /** + * Test message fallbacks, bug #1495 + * + * @dataProvider provideMessagesForFallback + */ + public function testMessageFallbacks( $message, $lang, $expectedContent ) { + $result = MessageCache::singleton()->get( $message, true, $lang ); + $this->assertEquals( $expectedContent, $result, "Message fallback failed." ); + } + + function provideMessagesForFallback() { + return array( + array( 'FallbackLanguageTest-Full', 'ab', 'ab' ), + array( 'FallbackLanguageTest-Partial', 'ab', 'ru' ), + array( 'FallbackLanguageTest-ContLang', 'ab', 'de' ), + array( 'FallbackLanguageTest-None', 'ab', false ), + + // Existing message with customizations on the fallbacks + array( 'sunday', 'ab', 'амҽыш' ), + + // bug 46579 + array( 'FallbackLanguageTest-NoDervContLang', 'de', 'de/none' ), + // UI language different from content language should only use de/none as last option + array( 'FallbackLanguageTest-NoDervContLang', 'fit', 'de/none' ), + ); + } + + /** + * There's a fallback case where the message key is given as fully qualified -- this + * should ignore the passed $lang and use the language from the key + * + * @dataProvider provideMessagesForFullKeys + */ + public function testFullKeyBehaviour( $message, $lang, $expectedContent ) { + $result = MessageCache::singleton()->get( $message, true, $lang, true ); + $this->assertEquals( $expectedContent, $result, "Full key message fallback failed." ); + } + + function provideMessagesForFullKeys() { + return array( + array( 'MessageCacheTest-FullKeyTest/ru', 'ru', 'ru' ), + array( 'MessageCacheTest-FullKeyTest/ru', 'ab', 'ru' ), + array( 'MessageCacheTest-FullKeyTest/ru/foo', 'ru', false ), + ); + } + +} diff --git a/tests/phpunit/includes/cache/ProcessCacheLRUTest.php b/tests/phpunit/includes/cache/ProcessCacheLRUTest.php new file mode 100644 index 00000000..d3793d83 --- /dev/null +++ b/tests/phpunit/includes/cache/ProcessCacheLRUTest.php @@ -0,0 +1,237 @@ +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 + */ + public 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 + */ + public function testConstructorGivenInvalidValue( $maxSize ) { + new ProcessCacheLRUTestable( $maxSize ); + } + + /** + * Value which are forbidden by the constructor + */ + public static function provideInvalidConstructorArg() { + return array( + array( null ), + array( array() ), + array( new stdClass() ), + array( 0 ), + array( '5' ), + array( -1 ), + ); + } + + public 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' ) ); + } + + public 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. + */ + public 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 + */ + public static 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. + */ + public 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() + ); + } + + public 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). + */ + public 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' ) + ); + } + + public 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 ); + } +} -- cgit v1.2.3-54-g00ecf