3 use Wikimedia\TestingAccessWrapper
;
10 class PrefixSearchTest
extends MediaWikiLangTestCase
{
11 const NS_NONCAP
= 12346;
13 private $originalHandlers;
15 public function addDBDataOnce() {
16 if ( !$this->isWikitextNS( NS_MAIN
) ) {
17 // tests are skipped if NS_MAIN is not wikitext
21 $this->insertPage( 'Sandbox' );
22 $this->insertPage( 'Bar' );
23 $this->insertPage( 'Example' );
24 $this->insertPage( 'Example Bar' );
25 $this->insertPage( 'Example Foo' );
26 $this->insertPage( 'Example Foo/Bar' );
27 $this->insertPage( 'Example/Baz' );
28 $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
29 $this->insertPage( 'Redirect Test' );
30 $this->insertPage( 'Redirect Test Worse Result' );
31 $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
32 $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
33 $this->insertPage( 'Redirect Test2' );
34 $this->insertPage( 'Redirect Test2 Worse Result' );
36 $this->insertPage( 'Talk:Sandbox' );
37 $this->insertPage( 'Talk:Example' );
39 $this->insertPage( 'User:Example' );
41 $this->insertPage( Title
::makeTitle( self
::NS_NONCAP
, 'Bar' ) );
42 $this->insertPage( Title
::makeTitle( self
::NS_NONCAP
, 'Upper' ) );
43 $this->insertPage( Title
::makeTitle( self
::NS_NONCAP
, 'sandbox' ) );
46 protected function setUp() {
49 if ( !$this->isWikitextNS( NS_MAIN
) ) {
50 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
53 // Avoid special pages from extensions interferring with the tests
54 $this->setMwGlobals( [
55 'wgSpecialPages' => [],
57 'wgExtraNamespaces' => [ self
::NS_NONCAP
=> 'NonCap' ],
58 'wgCapitalLinkOverrides' => [ self
::NS_NONCAP
=> false ],
61 $this->originalHandlers
= TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
;
62 TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
= [];
64 // Clear caches so that our new namespace appears
65 MWNamespace
::clearCaches();
66 Language
::factory( 'en' )->resetNamespaces();
68 SpecialPageFactory
::resetList();
71 public function tearDown() {
72 MWNamespace
::clearCaches();
73 Language
::factory( 'en' )->resetNamespaces();
77 TestingAccessWrapper
::newFromClass( 'Hooks' )->handlers
= $this->originalHandlers
;
79 SpecialPageFactory
::resetList();
82 protected function searchProvision( array $results = null ) {
83 if ( $results === null ) {
84 $this->setMwGlobals( 'wgHooks', [] );
86 $this->setMwGlobals( 'wgHooks', [
87 'PrefixSearchBackend' => [
88 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
97 public static function provideSearch() {
105 'Main namespace with title prefix',
112 // Third result when testing offset
118 'Talk namespace prefix',
126 'User namespace prefix',
133 'Special namespace prefix',
134 'query' => 'Special:',
136 'Special:ActiveUsers',
137 'Special:AllMessages',
138 'Special:AllMyUploads',
140 // Third result when testing offset
146 'Special namespace with prefix',
147 'query' => 'Special:Un',
150 'Special:UncategorizedCategories',
151 'Special:UncategorizedFiles',
153 // Third result when testing offset
155 'Special:UncategorizedPages',
160 'query' => 'Special:EditWatchlist',
162 'Special:EditWatchlist',
166 'Special page subpages',
167 'query' => 'Special:EditWatchlist/',
169 'Special:EditWatchlist/clear',
170 'Special:EditWatchlist/raw',
174 'Special page subpages with prefix',
175 'query' => 'Special:EditWatchlist/cl',
177 'Special:EditWatchlist/clear',
181 'Namespace with case sensitive first letter',
182 'query' => 'NonCap:upper',
186 'Multinamespace search',
192 'namespaces' => [ NS_MAIN
, self
::NS_NONCAP
],
195 'Multinamespace search with lowercase first letter',
201 'namespaces' => [ NS_MAIN
, self
::NS_NONCAP
],
207 * @dataProvider provideSearch
208 * @covers PrefixSearch::search
209 * @covers PrefixSearch::searchBackend
211 public function testSearch( array $case ) {
212 $this->searchProvision( null );
214 $namespaces = isset( $case['namespaces'] ) ?
$case['namespaces'] : [];
216 if ( wfGetDB( DB_REPLICA
)->getType() === 'postgres' ) {
217 // Postgres will sort lexicographically on utf8 code units (" " before "/")
218 sort( $case['results'], SORT_STRING
);
221 $searcher = new StringPrefixSearch
;
222 $results = $searcher->search( $case['query'], 3, $namespaces );
231 * @dataProvider provideSearch
232 * @covers PrefixSearch::search
233 * @covers PrefixSearch::searchBackend
235 public function testSearchWithOffset( array $case ) {
236 $this->searchProvision( null );
238 $namespaces = isset( $case['namespaces'] ) ?
$case['namespaces'] : [];
240 $searcher = new StringPrefixSearch
;
241 $results = $searcher->search( $case['query'], 3, $namespaces, 1 );
243 if ( wfGetDB( DB_REPLICA
)->getType() === 'postgres' ) {
244 // Postgres will sort lexicographically on utf8 code units (" " before "/")
245 sort( $case['results'], SORT_STRING
);
248 // We don't expect the first result when offsetting
249 array_shift( $case['results'] );
250 // And sometimes we expect a different last result
251 $expected = isset( $case['offsetresult'] ) ?
252 array_merge( $case['results'], $case['offsetresult'] ) :
262 public static function provideSearchBackend() {
279 'Exact match not on top (T72958)',
293 'Exact match missing (T72958)',
307 'Exact match missing and not existing',
321 "Exact match shouldn't override already found match if " .
322 "exact is redirect and found isn't",
324 // Target of the exact match is low in the list
325 'Redirect Test Worse Result',
328 'query' => 'redirect test',
330 // Redirect target is pulled up and exact match isn't added
332 'Redirect Test Worse Result',
336 "Exact match shouldn't override already found match if " .
337 "both exact match and found match are redirect",
339 // Another redirect to the same target as the exact match
340 // is low in the list
341 'Redirect Test2 Worse Result',
344 'query' => 'redirect TEST2',
346 // Found redirect is pulled to the top and exact match isn't
349 'Redirect Test2 Worse Result',
353 "Exact match should override any already found matches that " .
354 "are redirects to it",
356 // Another redirect to the same target as the exact match
357 // is low in the list
358 'Redirect Test Worse Result',
361 'query' => 'Redirect Test',
363 // Found redirect is pulled to the top and exact match isn't
366 'Redirect Test Worse Result',
373 * @dataProvider provideSearchBackend
374 * @covers PrefixSearch::searchBackend
376 public function testSearchBackend( array $case ) {
377 $this->searchProvision( $case['provision'] );
378 $searcher = new StringPrefixSearch
;
379 $results = $searcher->search( $case['query'], 3 );