From c2a308075f6a1740f06e05fbb2576a59d6ab0c87 Mon Sep 17 00:00:00 2001 From: Erik Bernhardson Date: Thu, 10 May 2018 15:03:55 -0700 Subject: [PATCH] Convert SearchResultSet to typical iteration The funky iteration here was at best annoying. Switch it over to an iterator based approach with appropriate BC code to simulate the old iteration style. Depends-On: I19a8d6621a130811871dec9335038797627d9448 Change-Id: I9fccda15dd58a0dc35771d3b5cd7a6e8b02514a0 --- RELEASE-NOTES-1.32 | 7 ++ includes/api/ApiQuerySearch.php | 11 +- includes/search/SearchNearMatchResultSet.php | 22 +--- includes/search/SearchResult.php | 31 +++-- includes/search/SearchResultSet.php | 64 +++++++--- includes/search/SqlSearchResultSet.php | 27 +++-- .../search/BasicSearchResultSetWidget.php | 6 +- .../search/SimpleSearchResultSetWidget.php | 4 +- tests/common/TestsAutoLoader.php | 3 + .../includes/api/ApiQuerySearchTest.php | 109 ++++++++++++++++++ .../includes/search/SearchEngineTest.php | 11 +- .../search/SearchNearMatchResultSetTest.php | 15 +++ .../includes/search/SearchResultSetTest.php | 45 ++++++++ .../includes/search/SearchResultTest.php | 38 ++++++ .../includes/specials/SpecialSearchTest.php | 4 +- .../phpunit/mocks/search/MockSearchEngine.php | 45 ++++++++ .../phpunit/mocks/search/MockSearchResult.php | 32 +++++ .../mocks/search/MockSearchResultSet.php | 36 ++++++ 18 files changed, 434 insertions(+), 76 deletions(-) create mode 100644 tests/phpunit/includes/api/ApiQuerySearchTest.php create mode 100644 tests/phpunit/includes/search/SearchNearMatchResultSetTest.php create mode 100644 tests/phpunit/includes/search/SearchResultSetTest.php create mode 100644 tests/phpunit/includes/search/SearchResultTest.php create mode 100644 tests/phpunit/mocks/search/MockSearchEngine.php create mode 100644 tests/phpunit/mocks/search/MockSearchResult.php create mode 100644 tests/phpunit/mocks/search/MockSearchResultSet.php diff --git a/RELEASE-NOTES-1.32 b/RELEASE-NOTES-1.32 index 8649e5e101..57952f5b63 100644 --- a/RELEASE-NOTES-1.32 +++ b/RELEASE-NOTES-1.32 @@ -188,6 +188,13 @@ because of Phabricator reports. * The ApiQueryContributions class has been renamed to ApiQueryUserContribs. * The XMPInfo, XMPReader, and XMPValidate classes have been deprecated in favor of the namespaced classes provided by the wikimedia/xmp-reader library. +* SearchResultSet::{next,rewind} are deprecated. Calling code should + use foreach on the SearchResultSet, or the extractResults method. Extending + code should override extractResults. +* Instantiating SearchResultSet directly is deprecated. SearchEngine + implementations must subclass SearchResultSet for their purposes. +* SearchResult::setExtensionData argument has been changed from accepting an + array to accepting a Closure that returns the array when called. * Class CryptRand, everything in MWCryptRand except generateHex() and function MediaWikiServices::getCryptRand() are deprecated, use random_bytes() to generate cryptographically secure random byte sequences. diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php index 7d46a5fb08..87913e682f 100644 --- a/includes/api/ApiQuerySearch.php +++ b/includes/api/ApiQuerySearch.php @@ -142,10 +142,9 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); $titles = []; $count = 0; - $result = $matches->next(); $limit = $params['limit']; - while ( $result ) { + foreach ( $matches as $result ) { if ( ++$count > $limit ) { // We've reached the one extra which shows that there are // additional items to be had. Stop here... @@ -155,7 +154,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { // Silently skip broken and missing titles if ( $result->isBrokenTitle() || $result->isMissingRevision() ) { - $result = $matches->next(); continue; } @@ -172,8 +170,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { } else { $titles[] = $result->getTitle(); } - - $result = $matches->next(); } // Here we assume interwiki results do not count with @@ -301,8 +297,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { // Include number of results if requested $totalhits += $interwikiMatches->getTotalHits(); - $result = $interwikiMatches->next(); - while ( $result ) { + foreach ( $interwikiMatches as $result ) { $title = $result->getTitle(); $vals = $this->getSearchResultData( $result, $prop, $terms ); @@ -322,8 +317,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { // pagination info so just bail out break; } - - $result = $interwikiMatches->next(); } } if ( $totalhits !== null ) { diff --git a/includes/search/SearchNearMatchResultSet.php b/includes/search/SearchNearMatchResultSet.php index 31417974d9..42bc62d670 100644 --- a/includes/search/SearchNearMatchResultSet.php +++ b/includes/search/SearchNearMatchResultSet.php @@ -3,28 +3,18 @@ * A SearchResultSet wrapper for SearchNearMatcher */ class SearchNearMatchResultSet extends SearchResultSet { - private $fetched = false; - /** * @param Title|null $match Title if matched, else null */ public function __construct( $match ) { - $this->result = $match; - } - - public function numRows() { - return $this->result ? 1 : 0; - } - - public function next() { - if ( $this->fetched || !$this->result ) { - return false; + if ( $match === null ) { + $this->results = []; + } else { + $this->results = [ SearchResult::newFromTitle( $match, $this ) ]; } - $this->fetched = true; - return SearchResult::newFromTitle( $this->result, $this ); } - public function rewind() { - $this->fetched = false; + public function numRows() { + return $this->results ? 1 : 0; } } diff --git a/includes/search/SearchResult.php b/includes/search/SearchResult.php index dc294c324e..2f20d9d52d 100644 --- a/includes/search/SearchResult.php +++ b/includes/search/SearchResult.php @@ -57,8 +57,8 @@ class SearchResult { protected $searchEngine; /** - * A set of extension data. - * @var array[] + * A function returning a set of extension data. + * @var Closure|null */ protected $extensionData; @@ -267,17 +267,34 @@ class SearchResult { * @return array[] */ public function getExtensionData() { - return $this->extensionData; + if ( $this->extensionData ) { + return call_user_func( $this->extensionData ); + } else { + return []; + } } /** * Set extension data for this result. * The data is: * augmentor name => data - * @param array[] $extensionData + * @param Closure|array $extensionData Takes no arguments, returns + * either array of extension data or null. */ - public function setExtensionData( array $extensionData ) { - $this->extensionData = $extensionData; + public function setExtensionData( $extensionData ) { + if ( $extensionData instanceof Closure ) { + $this->extensionData = $extensionData; + } elseif ( is_array( $extensionData ) ) { + wfDeprecated( __METHOD__ . ' with array argument', 1.32 ); + $this->extensionData = function () use ( $extensionData ) { + return $extensionData; + }; + } else { + $type = is_object( $extensionData ) + ? get_class( $extensionData ) + : gettype( $extensionData ); + throw new \InvalidArgumentException( + __METHOD__ . " must be called with Closure|array, but received $type" ); + } } - } diff --git a/includes/search/SearchResultSet.php b/includes/search/SearchResultSet.php index e3eb4c250e..eb57559958 100644 --- a/includes/search/SearchResultSet.php +++ b/includes/search/SearchResultSet.php @@ -24,7 +24,7 @@ /** * @ingroup Search */ -class SearchResultSet { +class SearchResultSet implements IteratorAggregate { /** * Types of interwiki results @@ -54,7 +54,7 @@ class SearchResultSet { * as an array. * @var SearchResult[] */ - private $results; + protected $results; /** * Set of result's extra data, indexed per result id @@ -65,7 +65,16 @@ class SearchResultSet { */ protected $extraData = []; + /** @var ArrayIterator|null Iterator supporting BC iteration methods */ + private $bcIterator; + public function __construct( $containedSyntax = false ) { + if ( static::class === __CLASS__ ) { + // This class will eventually be abstract. SearchEngine implementations + // already have to extend this class anyways to provide the actual + // search results. + wfDeprecated( __METHOD__, 1.32 ); + } $this->containedSyntax = $containedSyntax; } @@ -171,20 +180,39 @@ class SearchResultSet { /** * Fetches next search result, or false. - * STUB - * FIXME: refactor as iterator, so we could use nicer interfaces. - * @deprecated since 1.32; Use self::extractResults() + * @deprecated since 1.32; Use self::extractResults() or foreach * @return SearchResult|false */ - function next() { - return false; + public function next() { + wfDeprecated( __METHOD__, '1.32' ); + $it = $this->bcIterator(); + $searchResult = $it->current(); + $it->next(); + return $searchResult === null ? false : $searchResult; } /** * Rewind result set back to beginning - * @deprecated since 1.32; Use self::extractResults() + * @deprecated since 1.32; Use self::extractResults() or foreach */ - function rewind() { + public function rewind() { + wfDeprecated( __METHOD__, '1.32' ); + $this->bcIterator()->rewind(); + } + + private function bcIterator() { + if ( $this->bcIterator === null ) { + $this->bcIterator = 'RECURSION'; + $this->bcIterator = $this->getIterator(); + } elseif ( $this->bcIterator === 'RECURSION' ) { + // Either next/rewind or extractResults must be implemented. This + // class was potentially instantiated directly. It should be + // abstract with abstract methods to enforce this but that's a + // breaking change... + wfDeprecated( static::class . ' without implementing extractResults', '1.32' ); + $this->bcIterator = new ArrayIterator( [] ); + } + return $this->bcIterator; } /** @@ -258,15 +286,19 @@ class SearchResultSet { /** * Returns extra data for specific result and store it in SearchResult object. * @param SearchResult $result - * @return array|null List of data as name => value or null if none present. */ public function augmentResult( SearchResult $result ) { $id = $result->getTitle()->getArticleID(); - if ( !$id || !isset( $this->extraData[$id] ) ) { - return null; + if ( $id === -1 ) { + return; } - $result->setExtensionData( $this->extraData[$id] ); - return $this->extraData[$id]; + $result->setExtensionData( function () use ( $id ) { + if ( isset( $this->extraData[$id] ) ) { + return $this->extraData[$id]; + } else { + return []; + } + } ); } /** @@ -278,4 +310,8 @@ class SearchResultSet { public function getOffset() { return null; } + + final public function getIterator() { + return new ArrayIterator( $this->extractResults() ); + } } diff --git a/includes/search/SqlSearchResultSet.php b/includes/search/SqlSearchResultSet.php index 53d09e82b1..022dc0a643 100644 --- a/includes/search/SqlSearchResultSet.php +++ b/includes/search/SqlSearchResultSet.php @@ -7,8 +7,11 @@ use Wikimedia\Rdbms\ResultWrapper; * @ingroup Search */ class SqlSearchResultSet extends SearchResultSet { + /** @var ResultWrapper Result object from database */ protected $resultSet; + /** @var string Requested search query */ protected $terms; + /** @var int|null Total number of hits for $terms */ protected $totalHits; function __construct( ResultWrapper $resultSet, $terms, $total = null ) { @@ -29,25 +32,21 @@ class SqlSearchResultSet extends SearchResultSet { return $this->resultSet->numRows(); } - function next() { + public function extractResults() { if ( $this->resultSet === false ) { - return false; - } - - $row = $this->resultSet->fetchObject(); - if ( $row === false ) { - return false; + return []; } - return SearchResult::newFromTitle( - Title::makeTitle( $row->page_namespace, $row->page_title ), $this - ); - } - - function rewind() { - if ( $this->resultSet ) { + if ( $this->results === null ) { + $this->results = []; $this->resultSet->rewind(); + while ( ( $row = $this->resultSet->fetchObject() ) !== false ) { + $this->results[] = SearchResult::newFromTitle( + Title::makeTitle( $row->page_namespace, $row->page_title ), $this + ); + } } + return $this->results; } function free() { diff --git a/includes/widget/search/BasicSearchResultSetWidget.php b/includes/widget/search/BasicSearchResultSetWidget.php index e23664050f..8521e68f00 100644 --- a/includes/widget/search/BasicSearchResultSetWidget.php +++ b/includes/widget/search/BasicSearchResultSetWidget.php @@ -123,10 +123,8 @@ class BasicSearchResultSetWidget { $terms = $wgContLang->convertForSearchResult( $resultSet->termMatches() ); $hits = []; - $result = $resultSet->next(); - while ( $result ) { - $hits[] .= $this->resultWidget->render( $result, $terms, $offset++ ); - $result = $resultSet->next(); + foreach ( $resultSet as $result ) { + $hits[] = $this->resultWidget->render( $result, $terms, $offset++ ); } return ""; diff --git a/includes/widget/search/SimpleSearchResultSetWidget.php b/includes/widget/search/SimpleSearchResultSetWidget.php index d0c259fea2..248099ada0 100644 --- a/includes/widget/search/SimpleSearchResultSetWidget.php +++ b/includes/widget/search/SimpleSearchResultSetWidget.php @@ -56,12 +56,10 @@ class SimpleSearchResultSetWidget implements SearchResultSetWidget { $iwResults = []; foreach ( $resultSets as $resultSet ) { - $result = $resultSet->next(); - while ( $result ) { + foreach ( $resultSet as $result ) { if ( !$result->isBrokenTitle() ) { $iwResults[$result->getTitle()->getInterwiki()][] = $result; } - $result = $resultSet->next(); } } diff --git a/tests/common/TestsAutoLoader.php b/tests/common/TestsAutoLoader.php index a79867913e..4ecd383c76 100644 --- a/tests/common/TestsAutoLoader.php +++ b/tests/common/TestsAutoLoader.php @@ -184,6 +184,9 @@ $wgAutoloadClasses += [ => "$testDir/phpunit/mocks/session/DummySessionBackend.php", 'DummySessionProvider' => "$testDir/phpunit/mocks/session/DummySessionProvider.php", 'MockMessageLocalizer' => "$testDir/phpunit/mocks/MockMessageLocalizer.php", + 'MockSearchEngine' => "$testDir/phpunit/mocks/search/MockSearchEngine.php", + 'MockSearchResultSet' => "$testDir/phpunit/mocks/search/MockSearchResultSet.php", + 'MockSearchResult' => "$testDir/phpunit/mocks/search/MockSearchResult.php", # tests/suites 'ParserTestFileSuite' => "$testDir/phpunit/suites/ParserTestFileSuite.php", diff --git a/tests/phpunit/includes/api/ApiQuerySearchTest.php b/tests/phpunit/includes/api/ApiQuerySearchTest.php new file mode 100644 index 0000000000..0700cf77de --- /dev/null +++ b/tests/phpunit/includes/api/ApiQuerySearchTest.php @@ -0,0 +1,109 @@ + [ [], [] ], + 'has search results' => [ + [ 'Zomg' ], + [ $this->mockResult( 'Zomg' ) ], + ], + 'filters broken search results' => [ + [ 'A', 'B' ], + [ + $this->mockResult( 'a' ), + $this->mockResult( 'Zomg' )->setBrokenTitle( true ), + $this->mockResult( 'b' ), + ], + ], + 'filters results with missing revision' => [ + [ 'B', 'A' ], + [ + $this->mockResult( 'Zomg' )->setMissingRevision( true ), + $this->mockResult( 'b' ), + $this->mockResult( 'a' ), + ], + ], + ]; + } + + /** + * @dataProvider provideSearchResults + */ + public function testSearchResults( $expect, $hits, array $params = [] ) { + MockSearchEngine::addMockResults( 'my query', $hits ); + list( $response, $request ) = $this->doApiRequest( $params + [ + 'action' => 'query', + 'list' => 'search', + 'srsearch' => 'my query', + ] ); + $titles = []; + foreach ( $response['query']['search'] as $result ) { + $titles[] = $result['title']; + } + $this->assertEquals( $expect, $titles ); + } + + public function provideInterwikiResults() { + return [ + 'empty' => [ [], [] ], + 'one wiki response' => [ + [ 'utwiki' => [ 'Qwerty' ] ], + [ + SearchResultSet::SECONDARY_RESULTS => [ + 'utwiki' => new MockSearchResultSet( [ + $this->mockResult( 'Qwerty' )->setInterwikiPrefix( 'utwiki' ), + ] ), + ], + ] + ], + ]; + } + + /** + * @dataProvider provideInterwikiResults + */ + public function testInterwikiResults( $expect, $hits, array $params = [] ) { + MockSearchEngine::setMockInterwikiResults( $hits ); + list( $response, $request ) = $this->doApiRequest( $params + [ + 'action' => 'query', + 'list' => 'search', + 'srsearch' => 'my query', + 'srinterwiki' => true, + ] ); + if ( !$expect ) { + $this->assertArrayNotHasKey( 'interwikisearch', $response['query'] ); + return; + } + $results = []; + $this->assertArrayHasKey( 'interwikisearchinfo', $response['query'] ); + foreach ( $response['query']['interwikisearch'] as $wiki => $wikiResults ) { + $results[$wiki] = []; + foreach ( $wikiResults as $wikiResult ) { + $results[$wiki][] = $wikiResult['title']; + } + } + $this->assertEquals( $expect, $results ); + } + + public function setUp() { + parent::setUp(); + MockSearchEngine::clearMockResults(); + $this->registerMockSearchEngine(); + } + + private function registerMockSearchEngine() { + $this->setMwGlobals( [ + 'wgSearchType' => MockSearchEngine::class, + ] ); + } + + private function mockResult( $title ) { + return MockSearchResult::newFromtitle( Title::newFromText( $title ) ); + } + +} diff --git a/tests/phpunit/includes/search/SearchEngineTest.php b/tests/phpunit/includes/search/SearchEngineTest.php index e8077769e1..2561fd060f 100644 --- a/tests/phpunit/includes/search/SearchEngineTest.php +++ b/tests/phpunit/includes/search/SearchEngineTest.php @@ -84,10 +84,8 @@ class SearchEngineTest extends MediaWikiLangTestCase { $this->assertTrue( is_object( $results ) ); $matches = []; - $row = $results->next(); - while ( $row ) { + foreach ( $results as $row ) { $matches[] = $row->getTitle()->getPrefixedText(); - $row = $results->next(); } $results->free(); # Search is not guaranteed to return results in a certain order; @@ -173,7 +171,7 @@ class SearchEngineTest extends MediaWikiLangTestCase { public function testPhraseSearchHighlight() { $phrase = "smithee is one who smiths"; $res = $this->search->searchText( "\"$phrase\"" ); - $match = $res->next(); + $match = $res->getIterator()->current(); $snippet = "A " . $phrase . ""; $this->assertStringStartsWith( $snippet, $match->getTextSnippet( $res->termMatches() ), @@ -277,7 +275,7 @@ class SearchEngineTest extends MediaWikiLangTestCase { $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SearchResultsAugment' => [ [ $this, 'addAugmentors' ] ] ] ); $this->search->augmentSearchResults( $resultSet ); - for ( $result = $resultSet->next(); $result; $result = $resultSet->next() ) { + foreach ( $resultSet as $result ) { $id = $result->getTitle()->getArticleID(); $augmentData = "Result:$id:" . $result->getTitle()->getText(); $augmentData2 = "Result2:$id:" . $result->getTitle()->getText(); @@ -292,11 +290,10 @@ class SearchEngineTest extends MediaWikiLangTestCase { ->method( 'augmentAll' ) ->willReturnCallback( function ( SearchResultSet $resultSet ) { $data = []; - for ( $result = $resultSet->next(); $result; $result = $resultSet->next() ) { + foreach ( $resultSet as $result ) { $id = $result->getTitle()->getArticleID(); $data[$id] = "Result:$id:" . $result->getTitle()->getText(); } - $resultSet->rewind(); return $data; } ); $setAugmentors['testSet'] = $setAugmentor; diff --git a/tests/phpunit/includes/search/SearchNearMatchResultSetTest.php b/tests/phpunit/includes/search/SearchNearMatchResultSetTest.php new file mode 100644 index 0000000000..67493c4243 --- /dev/null +++ b/tests/phpunit/includes/search/SearchNearMatchResultSetTest.php @@ -0,0 +1,15 @@ +assertEquals( 0, $resultSet->numRows() ); + + $resultSet = new SearchNearMatchResultSet( Title::newMainPage() ); + $this->assertEquals( 1, $resultSet->numRows() ); + } +} diff --git a/tests/phpunit/includes/search/SearchResultSetTest.php b/tests/phpunit/includes/search/SearchResultSetTest.php new file mode 100644 index 0000000000..9eeee63d21 --- /dev/null +++ b/tests/phpunit/includes/search/SearchResultSetTest.php @@ -0,0 +1,45 @@ +assertEquals( 1, $resultSet->numRows() ); + $count = 0; + foreach ( $resultSet as $iterResult ) { + $this->assertEquals( $result, $iterResult ); + $count++; + } + $this->assertEquals( 1, $count ); + + $this->hideDeprecated( 'SearchResultSet::rewind' ); + $this->hideDeprecated( 'SearchResultSet::next' ); + $resultSet->rewind(); + $count = 0; + while ( false !== ( $iterResult = $resultSet->next() ) ) { + $this->assertEquals( $result, $iterResult ); + $count++; + } + $this->assertEquals( 1, $count ); + } + + /** + * @covers SearchResultSet::augmentResult + * @covers SearchResultSet::setAugmentedData + */ + public function testDelayedResultAugment() { + $result = SearchResult::newFromTitle( Title::newMainPage() ); + $resultSet = new MockSearchResultSet( [ $result ] ); + $resultSet->augmentResult( $result ); + $this->assertEquals( [], $result->getExtensionData() ); + $resultSet->setAugmentedData( 'foo', [ + $result->getTitle()->getArticleID() => 'bar' + ] ); + $this->assertEquals( [ 'foo' => 'bar' ], $result->getExtensionData() ); + } +} diff --git a/tests/phpunit/includes/search/SearchResultTest.php b/tests/phpunit/includes/search/SearchResultTest.php new file mode 100644 index 0000000000..0e1e24c089 --- /dev/null +++ b/tests/phpunit/includes/search/SearchResultTest.php @@ -0,0 +1,38 @@ +assertEquals( [], $result->getExtensionData(), 'starts empty' ); + + $data = [ 'hello' => 'world' ]; + $result->setExtensionData( function () use ( &$data ) { + return $data; + } ); + $this->assertEquals( $data, $result->getExtensionData(), 'can set extension data' ); + $data['this'] = 'that'; + $this->assertEquals( $data, $result->getExtensionData(), 'refetches from callback' ); + } + + /** + * @covers SearchResult::getExtensionData + * @covers SearchResult::setExtensionData + */ + public function testExtensionDataArrayBC() { + $result = SearchResult::newFromTitle( Title::newMainPage() ); + $data = [ 'hello' => 'world' ]; + $this->hideDeprecated( 'SearchResult::setExtensionData with array argument' ); + $this->assertEquals( [], $result->getExtensionData(), 'starts empty' ); + $result->setExtensionData( $data ); + $this->assertEquals( $data, $result->getExtensionData(), 'can set extension data' ); + $data['this'] = 'that'; + $this->assertNotEquals( $data, $result->getExtensionData(), 'shouldnt hold any reference' ); + + $result->setExtensionData( $data ); + $this->assertEquals( $data, $result->getExtensionData(), 'can replace extension data' ); + } +} diff --git a/tests/phpunit/includes/specials/SpecialSearchTest.php b/tests/phpunit/includes/specials/SpecialSearchTest.php index f0a5726699..196321caed 100644 --- a/tests/phpunit/includes/specials/SpecialSearchTest.php +++ b/tests/phpunit/includes/specials/SpecialSearchTest.php @@ -262,8 +262,8 @@ class SpecialSearchTestMockResultSet extends SearchResultSet { $this->containedSyntax = $containedSyntax; } - public function numRows() { - return count( $this->results ); + public function expandResults() { + return $this->results; } public function getTotalHits() { diff --git a/tests/phpunit/mocks/search/MockSearchEngine.php b/tests/phpunit/mocks/search/MockSearchEngine.php new file mode 100644 index 0000000000..4d7c78a017 --- /dev/null +++ b/tests/phpunit/mocks/search/MockSearchEngine.php @@ -0,0 +1,45 @@ +getLinkCache(); + foreach ( $results as $result ) { + // TODO: better page ids? + $lc->addGoodLinkObj( mt_rand(), $result->getTitle() ); + } + } + + /** + * @param SearchResultSet[][] $interwikiResults + */ + public static function setMockInterwikiResults( array $interwikiResults ) { + self::$interwikiResults = $interwikiResults; + } + + protected function doSearchText( $term ) { + if ( isset( self::$results[ $term ] ) ) { + $results = array_slice( self::$results[ $term ], $this->offset, $this->limit ); + } else { + $results = []; + } + return new MockSearchResultSet( $results, self::$interwikiResults ); + } +} diff --git a/tests/phpunit/mocks/search/MockSearchResult.php b/tests/phpunit/mocks/search/MockSearchResult.php new file mode 100644 index 0000000000..d92d39a634 --- /dev/null +++ b/tests/phpunit/mocks/search/MockSearchResult.php @@ -0,0 +1,32 @@ +isMissingRevision; + } + public function setMissingRevision( $isMissingRevision ) { + $this->isMissingRevision = $isMissingRevision; + return $this; + } + + public function isBrokenTitle() { + return $this->isBrokenTitle; + } + + public function setBrokenTitle( $isBrokenTitle ) { + $this->isBrokenTitle = $isBrokenTitle; + return $this; + } + + public function getInterwikiPrefix() { + return $this->interwikiPrefix; + } + + public function setInterwikiPrefix( $interwikiPrefix ) { + $this->interwikiPrefix = $interwikiPrefix; + return $this; + } +} diff --git a/tests/phpunit/mocks/search/MockSearchResultSet.php b/tests/phpunit/mocks/search/MockSearchResultSet.php new file mode 100644 index 0000000000..99f093fced --- /dev/null +++ b/tests/phpunit/mocks/search/MockSearchResultSet.php @@ -0,0 +1,36 @@ +results = $results; + $this->interwikiResults = $interwikiResults; + } + + public function numRows() { + return count( $this->results ); + } + + public function hasInterwikiResults( $type = self::SECONDARY_RESULTS ) { + return isset( $this->interwikiResults[$type] ) && + count( $this->interwikiResults[$type] ) > 0; + } + + public function getInterwikiResults( $type = self::SECONDARY_RESULTS ) { + if ( $this->hasInterwikiResults( $type ) ) { + return $this->interwikiResults[$type]; + } else { + return null; + } + } +} -- 2.20.1