From ff764440da623c34c37f290a1ecd9f167f68433f Mon Sep 17 00:00:00 2001 From: daniel Date: Sat, 23 Apr 2016 21:00:18 +0200 Subject: [PATCH] Introduce InterwikiTest This test class verifies that Interwiki currectly handles information from the database, CDB files, and plain arrays. This is intended to allow confident refactoring when implementing T113034. Bug: T113034 Change-Id: I2b68d11e3e946d81fa5f5f65a28fc399e7936c0f --- includes/interwiki/Interwiki.php | 26 +- .../includes/interwiki/InterwikiTest.php | 254 ++++++++++++++++++ 2 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 tests/phpunit/includes/interwiki/InterwikiTest.php diff --git a/includes/interwiki/Interwiki.php b/includes/interwiki/Interwiki.php index 9a5a3ffba0..f68651bc1f 100644 --- a/includes/interwiki/Interwiki.php +++ b/includes/interwiki/Interwiki.php @@ -60,8 +60,8 @@ class Interwiki { $this->mURL = $url; $this->mAPI = $api; $this->mWikiID = $wikiId; - $this->mLocal = $local; - $this->mTrans = $trans; + $this->mLocal = (bool)$local; + $this->mTrans = (bool)$trans; } /** @@ -115,7 +115,16 @@ class Interwiki { } /** - * Purge the cache for an interwiki prefix + * Resets locally cached Interwiki objects. This is intended for use during testing only. + * This does not invalidate entries in the persistent cache, as invalidateCache() does. + * @since 1.27 + */ + public static function resetLocalCache() { + static::$smCache = []; + } + + /** + * Purge the cache (local and persistent) for an interwiki prefix. * @param string $prefix * @since 1.26 */ @@ -123,6 +132,7 @@ class Interwiki { $cache = ObjectCache::getMainWANInstance(); $key = wfMemcKey( 'interwiki', $prefix ); $cache->delete( $key ); + unset( static::$smCache[$prefix] ); } /** @@ -141,7 +151,7 @@ class Interwiki { // Split values list( $local, $url ) = explode( ' ', $value, 2 ); $s->mURL = $url; - $s->mLocal = (int)$local; + $s->mLocal = (bool)$local; } else { $s = false; } @@ -262,8 +272,8 @@ class Interwiki { if ( isset( $mc['iw_url'] ) ) { $iw = new Interwiki(); $iw->mURL = $mc['iw_url']; - $iw->mLocal = isset( $mc['iw_local'] ) ? $mc['iw_local'] : 0; - $iw->mTrans = isset( $mc['iw_trans'] ) ? $mc['iw_trans'] : 0; + $iw->mLocal = isset( $mc['iw_local'] ) ? (bool)$mc['iw_local'] : false; + $iw->mTrans = isset( $mc['iw_trans'] ) ? (bool)$mc['iw_trans'] : false; $iw->mAPI = isset( $mc['iw_api'] ) ? $mc['iw_api'] : ''; $iw->mWikiID = isset( $mc['iw_wikiid'] ) ? $mc['iw_wikiid'] : ''; @@ -453,7 +463,7 @@ class Interwiki { public function getName() { $msg = wfMessage( 'interwiki-name-' . $this->mPrefix )->inContentLanguage(); - return !$msg->exists() ? '' : $msg; + return !$msg->exists() ? '' : $msg->text(); } /** @@ -464,7 +474,7 @@ class Interwiki { public function getDescription() { $msg = wfMessage( 'interwiki-desc-' . $this->mPrefix )->inContentLanguage(); - return !$msg->exists() ? '' : $msg; + return !$msg->exists() ? '' : $msg->text(); } /** diff --git a/tests/phpunit/includes/interwiki/InterwikiTest.php b/tests/phpunit/includes/interwiki/InterwikiTest.php new file mode 100644 index 0000000000..411d6a3ff6 --- /dev/null +++ b/tests/phpunit/includes/interwiki/InterwikiTest.php @@ -0,0 +1,254 @@ +setContentLang( 'qqx' ); + + $this->assertSame( '(interwiki-name-xyz)', $interwiki->getName() ); + $this->assertSame( '(interwiki-desc-xyz)', $interwiki->getDescription() ); + $this->assertSame( 'http://xyz.acme.test/w/api.php', $interwiki->getAPI() ); + $this->assertSame( 'http://xyz.acme.test/wiki/$1', $interwiki->getURL() ); + $this->assertSame( 'xyzwiki', $interwiki->getWikiID() ); + $this->assertTrue( $interwiki->isLocal() ); + $this->assertFalse( $interwiki->isTranscludable() ); + } + + public function testGetUrl() { + $interwiki = new Interwiki( + 'xyz', + 'http://xyz.acme.test/wiki/$1' + ); + + $this->assertSame( 'http://xyz.acme.test/wiki/$1', $interwiki->getURL() ); + $this->assertSame( 'http://xyz.acme.test/wiki/Foo%26Bar', $interwiki->getURL( 'Foo&Bar' ) ); + } + + //// tests for static data access methods below /////////////////////////////////////////////// + + private function populateDB( $iwrows ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->delete( 'interwiki', '*', __METHOD__ ); + $dbw->insert( 'interwiki', array_values( $iwrows ), __METHOD__ ); + $this->tablesUsed[] = 'interwiki'; + } + + public function testDatabaseStorage() { + // NOTE: database setup is expensive, so we only do + // it once and run all the tests in one go. + $dewiki = [ + 'iw_prefix' => 'de', + 'iw_url' => 'http://de.wikipedia.org/wiki/', + 'iw_api' => 'http://de.wikipedia.org/w/api.php', + 'iw_wikiid' => 'dewiki', + 'iw_local' => 1, + 'iw_trans' => 0 + ]; + + $zzwiki = [ + 'iw_prefix' => 'zz', + 'iw_url' => 'http://zzwiki.org/wiki/', + 'iw_api' => 'http://zzwiki.org/w/api.php', + 'iw_wikiid' => 'zzwiki', + 'iw_local' => 0, + 'iw_trans' => 0 + ]; + + $this->populateDB( [ $dewiki, $zzwiki ] ); + + Interwiki::resetLocalCache(); + $this->setMwGlobals( 'wgInterwikiCache', false ); + + $this->assertEquals( + [ $dewiki, $zzwiki ], + Interwiki::getAllPrefixes(), + 'getAllPrefixes()' + ); + $this->assertEquals( + [ $dewiki ], + Interwiki::getAllPrefixes( true ), + 'getAllPrefixes()' + ); + $this->assertEquals( + [ $zzwiki ], + Interwiki::getAllPrefixes( false ), + 'getAllPrefixes()' + ); + + $this->assertTrue( Interwiki::isValidInterwiki( 'de' ), 'known prefix is valid' ); + $this->assertFalse( Interwiki::isValidInterwiki( 'xyz' ), 'unknown prefix is valid' ); + + $this->assertNull( Interwiki::fetch( null ), 'no prefix' ); + $this->assertFalse( Interwiki::fetch( 'xyz' ), 'unknown prefix' ); + + $interwiki = Interwiki::fetch( 'de' ); + $this->assertInstanceOf( 'Interwiki', $interwiki ); + $this->assertSame( $interwiki, Interwiki::fetch( 'de' ), 'in-process caching' ); + + $this->assertSame( 'http://de.wikipedia.org/wiki/', $interwiki->getURL(), 'getURL' ); + $this->assertSame( 'http://de.wikipedia.org/w/api.php', $interwiki->getAPI(), 'getAPI' ); + $this->assertSame( 'dewiki', $interwiki->getWikiID(), 'getWikiID' ); + $this->assertSame( true, $interwiki->isLocal(), 'isLocal' ); + $this->assertSame( false, $interwiki->isTranscludable(), 'isTranscludable' ); + + Interwiki::invalidateCache( 'de' ); + $this->assertNotSame( $interwiki, Interwiki::fetch( 'de' ), 'invalidate cache' ); + } + + /** + * @param string $thisSite + * @param string[] $local + * @param string[] $global + * + * @return string[] + */ + private function populateHash( $thisSite, $local, $global ) { + $hash = []; + $hash[ '__sites:' . wfWikiID() ] = $thisSite; + + $globals = []; + $locals = []; + + foreach ( $local as $row ) { + $prefix = $row['iw_prefix']; + $data = $row['iw_local'] . ' ' . $row['iw_url']; + $locals[] = $prefix; + $hash[ "_{$thisSite}:{$prefix}" ] = $data; + } + + foreach ( $global as $row ) { + $prefix = $row['iw_prefix']; + $data = $row['iw_local'] . ' ' . $row['iw_url']; + $globals[] = $prefix; + $hash[ "__global:{$prefix}" ] = $data; + } + + $hash[ '__list:__global' ] = implode( ' ', $globals ); + $hash[ '__list:_' . $thisSite ] = implode( ' ', $locals ); + + return $hash; + } + + private function populateCDB( $thisSite, $local, $global ) { + $cdbFile = tempnam( wfTempDir(), 'MW-ClassicInterwikiLookupTest-' ) . '.cdb'; + $cdb = CdbWriter::open( $cdbFile ); + + $hash = $this->populateHash( $thisSite, $local, $global ); + + foreach ( $hash as $key => $value ) { + $cdb->set( $key, $value ); + } + + $cdb->close(); + return $cdbFile; + } + + public function testCDBStorage() { + // NOTE: CDB setup is expensive, so we only do + // it once and run all the tests in one go. + + $dewiki = [ + 'iw_prefix' => 'de', + 'iw_url' => 'http://de.wikipedia.org/wiki/', + 'iw_local' => 1 + ]; + + $zzwiki = [ + 'iw_prefix' => 'zz', + 'iw_url' => 'http://zzwiki.org/wiki/', + 'iw_local' => 0 + ]; + + $cdbFile = $this->populateCDB( + 'en', + [ $dewiki ], + [ $zzwiki ] + ); + + Interwiki::resetLocalCache(); + $this->setMwGlobals( 'wgInterwikiCache', $cdbFile ); + + $this->assertEquals( + [ $dewiki, $zzwiki ], + Interwiki::getAllPrefixes(), + 'getAllPrefixes()' + ); + + $this->assertTrue( Interwiki::isValidInterwiki( 'de' ), 'known prefix is valid' ); + $this->assertTrue( Interwiki::isValidInterwiki( 'zz' ), 'known prefix is valid' ); + + $interwiki = Interwiki::fetch( 'de' ); + $this->assertInstanceOf( 'Interwiki', $interwiki ); + + $this->assertSame( 'http://de.wikipedia.org/wiki/', $interwiki->getURL(), 'getURL' ); + $this->assertSame( true, $interwiki->isLocal(), 'isLocal' ); + + $interwiki = Interwiki::fetch( 'zz' ); + $this->assertInstanceOf( 'Interwiki', $interwiki ); + + $this->assertSame( 'http://zzwiki.org/wiki/', $interwiki->getURL(), 'getURL' ); + $this->assertSame( false, $interwiki->isLocal(), 'isLocal' ); + + // cleanup temp file + unlink( $cdbFile ); + } + + public function testArrayStorage() { + $dewiki = [ + 'iw_prefix' => 'de', + 'iw_url' => 'http://de.wikipedia.org/wiki/', + 'iw_local' => 1 + ]; + + $zzwiki = [ + 'iw_prefix' => 'zz', + 'iw_url' => 'http://zzwiki.org/wiki/', + 'iw_local' => 0 + ]; + + $cdbData = $this->populateHash( + 'en', + [ $dewiki ], + [ $zzwiki ] + ); + + Interwiki::resetLocalCache(); + $this->setMwGlobals( 'wgInterwikiCache', $cdbData ); + + $this->assertEquals( + [ $dewiki, $zzwiki ], + Interwiki::getAllPrefixes(), + 'getAllPrefixes()' + ); + + $this->assertTrue( Interwiki::isValidInterwiki( 'de' ), 'known prefix is valid' ); + $this->assertTrue( Interwiki::isValidInterwiki( 'zz' ), 'known prefix is valid' ); + + $interwiki = Interwiki::fetch( 'de' ); + $this->assertInstanceOf( 'Interwiki', $interwiki ); + + $this->assertSame( 'http://de.wikipedia.org/wiki/', $interwiki->getURL(), 'getURL' ); + $this->assertSame( true, $interwiki->isLocal(), 'isLocal' ); + + $interwiki = Interwiki::fetch( 'zz' ); + $this->assertInstanceOf( 'Interwiki', $interwiki ); + + $this->assertSame( 'http://zzwiki.org/wiki/', $interwiki->getURL(), 'getURL' ); + $this->assertSame( false, $interwiki->isLocal(), 'isLocal' ); + } + +} -- 2.20.1