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( 'Sample' );
32 $this->insertPage( 'Sample Ban' );
33 $this->insertPage( 'Sample Eat' );
34 $this->insertPage( 'Sample Who' );
35 $this->insertPage( 'Sample Zoo' );
36 $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
37 $this->insertPage( 'Redirect Test' );
38 $this->insertPage( 'Redirect Test Worse Result' );
39 $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
40 $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
41 $this->insertPage( 'Redirect Test2' );
42 $this->insertPage( 'Redirect Test2 Worse Result' );
44 $this->insertPage( 'Talk:Sandbox' );
45 $this->insertPage( 'Talk:Example' );
47 $this->insertPage( 'User:Example' );
50 protected function setUp() {
53 if ( !$this->isWikitextNS( NS_MAIN
) ) {
54 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
57 // Avoid special pages from extensions interferring with the tests
58 $this->setMwGlobals( [
59 'wgSpecialPages' => [],
63 $this->search
= MediaWikiServices
::getInstance()->newSearchEngine();
64 $this->search
->setNamespaces( [] );
66 $this->originalHandlers
= TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
;
67 TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
= [];
69 SpecialPageFactory
::resetList();
72 public function tearDown() {
75 TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
= $this->originalHandlers
;
77 SpecialPageFactory
::resetList();
80 protected function searchProvision( array $results = null ) {
81 if ( $results === null ) {
82 $this->setMwGlobals( 'wgHooks', [] );
84 $this->setMwGlobals( 'wgHooks', [
85 'PrefixSearchBackend' => [
86 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
95 public static function provideSearch() {
103 'Main namespace with title prefix',
110 // Third result when testing offset
116 'Talk namespace prefix',
124 'User namespace prefix',
131 'Special namespace prefix',
132 'query' => 'Special:',
134 'Special:ActiveUsers',
135 'Special:AllMessages',
136 'Special:AllMyUploads',
138 // Third result when testing offset
144 'Special namespace with prefix',
145 'query' => 'Special:Un',
148 'Special:UncategorizedCategories',
149 'Special:UncategorizedFiles',
151 // Third result when testing offset
153 'Special:UncategorizedPages',
158 'query' => 'Special:EditWatchlist',
160 'Special:EditWatchlist',
164 'Special page subpages',
165 'query' => 'Special:EditWatchlist/',
167 'Special:EditWatchlist/clear',
168 'Special:EditWatchlist/raw',
172 'Special page subpages with prefix',
173 'query' => 'Special:EditWatchlist/cl',
175 'Special:EditWatchlist/clear',
182 * @dataProvider provideSearch
183 * @covers SearchEngine::defaultPrefixSearch
185 public function testSearch( array $case ) {
186 $this->search
->setLimitOffset( 3 );
187 $results = $this->search
->defaultPrefixSearch( $case['query'] );
188 $results = array_map( function ( Title
$t ) {
189 return $t->getPrefixedText();
200 * @dataProvider provideSearch
201 * @covers SearchEngine::defaultPrefixSearch
203 public function testSearchWithOffset( array $case ) {
204 $this->search
->setLimitOffset( 3, 1 );
205 $results = $this->search
->defaultPrefixSearch( $case['query'] );
206 $results = array_map( function ( Title
$t ) {
207 return $t->getPrefixedText();
210 // We don't expect the first result when offsetting
211 array_shift( $case['results'] );
212 // And sometimes we expect a different last result
213 $expected = isset( $case['offsetresult'] ) ?
214 array_merge( $case['results'], $case['offsetresult'] ) :
224 public static function provideSearchBackend() {
241 'Exact match not on top (T72958)',
255 'Exact match missing (T72958)',
269 'Exact match missing and not existing',
283 "Exact match shouldn't override already found match if " .
284 "exact is redirect and found isn't",
286 // Target of the exact match is low in the list
287 'Redirect Test Worse Result',
290 'query' => 'redirect test',
292 // Redirect target is pulled up and exact match isn't added
294 'Redirect Test Worse Result',
298 "Exact match shouldn't override already found match if " .
299 "both exact match and found match are redirect",
301 // Another redirect to the same target as the exact match
302 // is low in the list
303 'Redirect Test2 Worse Result',
306 'query' => 'redirect TEST2',
308 // Found redirect is pulled to the top and exact match isn't
311 'Redirect Test2 Worse Result',
315 "Exact match should override any already found matches that " .
316 "are redirects to it",
318 // Another redirect to the same target as the exact match
319 // is low in the list
320 'Redirect Test Worse Result',
323 'query' => 'Redirect Test',
325 // Found redirect is pulled to the top and exact match isn't
328 'Redirect Test Worse Result',
336 * @dataProvider provideSearchBackend
337 * @covers PrefixSearch::searchBackend
339 public function testSearchBackend( array $case ) {
340 $search = $stub = $this->getMockBuilder( 'SearchEngine' )
341 ->setMethods( [ 'completionSearchBackend' ] )->getMock();
343 $return = SearchSuggestionSet
::fromStrings( $case['provision'] );
345 $search->expects( $this->any() )
346 ->method( 'completionSearchBackend' )
347 ->will( $this->returnValue( $return ) );
349 $search->setLimitOffset( 3 );
350 $results = $search->completionSearch( $case['query'] );
352 $results = $results->map( function ( SearchSuggestion
$s ) {
353 return $s->getText();