3 use MediaWiki\MediaWikiServices
;
4 use Wikimedia\TestingAccessWrapper
;
10 class SearchEnginePrefixTest
extends MediaWikiLangTestCase
{
11 private $originalHandlers;
18 public function addDBDataOnce() {
19 if ( !$this->isWikitextNS( NS_MAIN
) ) {
20 // tests are skipped if NS_MAIN is not wikitext
24 $this->insertPage( 'Sandbox' );
25 $this->insertPage( 'Bar' );
26 $this->insertPage( 'Example' );
27 $this->insertPage( 'Example Bar' );
28 $this->insertPage( 'Example Foo' );
29 $this->insertPage( 'Example Foo/Bar' );
30 $this->insertPage( 'Example/Baz' );
31 $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
32 $this->insertPage( 'Redirect Test' );
33 $this->insertPage( 'Redirect Test Worse Result' );
34 $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
35 $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
36 $this->insertPage( 'Redirect Test2' );
37 $this->insertPage( 'Redirect Test2 Worse Result' );
39 $this->insertPage( 'Talk:Sandbox' );
40 $this->insertPage( 'Talk:Example' );
42 $this->insertPage( 'User:Example' );
45 protected function setUp() {
48 if ( !$this->isWikitextNS( NS_MAIN
) ) {
49 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
52 // Avoid special pages from extensions interferring with the tests
53 $this->setMwGlobals( [
54 'wgSpecialPages' => [],
58 $this->search
= MediaWikiServices
::getInstance()->newSearchEngine();
59 $this->search
->setNamespaces( [] );
61 $this->originalHandlers
= TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
;
62 TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
= [];
64 SpecialPageFactory
::resetList();
67 public function tearDown() {
70 TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
= $this->originalHandlers
;
72 SpecialPageFactory
::resetList();
75 protected function searchProvision( array $results = null ) {
76 if ( $results === null ) {
77 $this->setMwGlobals( 'wgHooks', [] );
79 $this->setMwGlobals( 'wgHooks', [
80 'PrefixSearchBackend' => [
81 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
90 public static function provideSearch() {
98 'Main namespace with title prefix',
105 // Third result when testing offset
111 'Talk namespace prefix',
119 'User namespace prefix',
126 'Special namespace prefix',
127 'query' => 'Special:',
129 'Special:ActiveUsers',
130 'Special:AllMessages',
131 'Special:AllMyUploads',
133 // Third result when testing offset
139 'Special namespace with prefix',
140 'query' => 'Special:Un',
143 'Special:UncategorizedCategories',
144 'Special:UncategorizedFiles',
146 // Third result when testing offset
148 'Special:UncategorizedPages',
153 'query' => 'Special:EditWatchlist',
155 'Special:EditWatchlist',
159 'Special page subpages',
160 'query' => 'Special:EditWatchlist/',
162 'Special:EditWatchlist/clear',
163 'Special:EditWatchlist/raw',
167 'Special page subpages with prefix',
168 'query' => 'Special:EditWatchlist/cl',
170 'Special:EditWatchlist/clear',
177 * @dataProvider provideSearch
178 * @covers SearchEngine::defaultPrefixSearch
180 public function testSearch( array $case ) {
181 $this->search
->setLimitOffset( 3 );
182 $results = $this->search
->defaultPrefixSearch( $case['query'] );
183 $results = array_map( function( Title
$t ) {
184 return $t->getPrefixedText();
194 * @dataProvider provideSearch
195 * @covers SearchEngine::defaultPrefixSearch
197 public function testSearchWithOffset( array $case ) {
198 $this->search
->setLimitOffset( 3, 1 );
199 $results = $this->search
->defaultPrefixSearch( $case['query'] );
200 $results = array_map( function( Title
$t ) {
201 return $t->getPrefixedText();
204 // We don't expect the first result when offsetting
205 array_shift( $case['results'] );
206 // And sometimes we expect a different last result
207 $expected = isset( $case['offsetresult'] ) ?
208 array_merge( $case['results'], $case['offsetresult'] ) :
218 public static function provideSearchBackend() {
235 'Exact match not on top (T72958)',
249 'Exact match missing (T72958)',
263 'Exact match missing and not existing',
277 "Exact match shouldn't override already found match if " .
278 "exact is redirect and found isn't",
280 // Target of the exact match is low in the list
281 'Redirect Test Worse Result',
284 'query' => 'redirect test',
286 // Redirect target is pulled up and exact match isn't added
288 'Redirect Test Worse Result',
292 "Exact match shouldn't override already found match if " .
293 "both exact match and found match are redirect",
295 // Another redirect to the same target as the exact match
296 // is low in the list
297 'Redirect Test2 Worse Result',
300 'query' => 'redirect TEST2',
302 // Found redirect is pulled to the top and exact match isn't
305 'Redirect Test2 Worse Result',
309 "Exact match should override any already found matches that " .
310 "are redirects to it",
312 // Another redirect to the same target as the exact match
313 // is low in the list
314 'Redirect Test Worse Result',
317 'query' => 'Redirect Test',
319 // Found redirect is pulled to the top and exact match isn't
322 'Redirect Test Worse Result',
330 * @dataProvider provideSearchBackend
331 * @covers PrefixSearch::searchBackend
333 public function testSearchBackend( array $case ) {
334 $search = $stub = $this->getMockBuilder( 'SearchEngine' )
335 ->setMethods( [ 'completionSearchBackend' ] )->getMock();
337 $return = SearchSuggestionSet
::fromStrings( $case['provision'] );
339 $search->expects( $this->any() )
340 ->method( 'completionSearchBackend' )
341 ->will( $this->returnValue( $return ) );
343 $search->setLimitOffset( 3 );
344 $results = $search->completionSearch( $case['query'] );
346 $results = $results->map( function( SearchSuggestion
$s ) {
347 return $s->getText();