Update docs/hooks.txt for ShowSearchHitTitle
[mediawiki.git] / tests / phpunit / includes / PrefixSearchTest.php
blobc5a7e04e30db0083620b920393b30763c56d8399
1 <?php
2 /**
3 * @group Search
4 * @group Database
5 * @covers PrefixSearch
6 */
7 class PrefixSearchTest extends MediaWikiLangTestCase {
8 const NS_NONCAP = 12346;
10 private $originalHandlers;
12 public function addDBDataOnce() {
13 if ( !$this->isWikitextNS( NS_MAIN ) ) {
14 // tests are skipped if NS_MAIN is not wikitext
15 return;
18 $this->insertPage( 'Sandbox' );
19 $this->insertPage( 'Bar' );
20 $this->insertPage( 'Example' );
21 $this->insertPage( 'Example Bar' );
22 $this->insertPage( 'Example Foo' );
23 $this->insertPage( 'Example Foo/Bar' );
24 $this->insertPage( 'Example/Baz' );
25 $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
26 $this->insertPage( 'Redirect Test' );
27 $this->insertPage( 'Redirect Test Worse Result' );
28 $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
29 $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
30 $this->insertPage( 'Redirect Test2' );
31 $this->insertPage( 'Redirect Test2 Worse Result' );
33 $this->insertPage( 'Talk:Sandbox' );
34 $this->insertPage( 'Talk:Example' );
36 $this->insertPage( 'User:Example' );
38 $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'Bar' ) );
39 $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'Upper' ) );
40 $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'sandbox' ) );
43 protected function setUp() {
44 parent::setUp();
46 if ( !$this->isWikitextNS( NS_MAIN ) ) {
47 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
50 // Avoid special pages from extensions interferring with the tests
51 $this->setMwGlobals( [
52 'wgSpecialPages' => [],
53 'wgHooks' => [],
54 'wgExtraNamespaces' => [ self::NS_NONCAP => 'NonCap' ],
55 'wgCapitalLinkOverrides' => [ self::NS_NONCAP => false ],
56 ] );
58 $this->originalHandlers = TestingAccessWrapper::newFromClass( 'Hooks' )->handlers;
59 TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = [];
61 // Clear caches so that our new namespace appears
62 MWNamespace::getCanonicalNamespaces( true );
63 Language::factory( 'en' )->resetNamespaces();
65 SpecialPageFactory::resetList();
68 public function tearDown() {
69 parent::tearDown();
71 TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = $this->originalHandlers;
73 SpecialPageFactory::resetList();
76 protected function searchProvision( array $results = null ) {
77 if ( $results === null ) {
78 $this->setMwGlobals( 'wgHooks', [] );
79 } else {
80 $this->setMwGlobals( 'wgHooks', [
81 'PrefixSearchBackend' => [
82 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
83 $srchres = $results;
84 return false;
87 ] );
91 public static function provideSearch() {
92 return [
93 [ [
94 'Empty string',
95 'query' => '',
96 'results' => [],
97 ] ],
98 [ [
99 'Main namespace with title prefix',
100 'query' => 'Ex',
101 'results' => [
102 'Example',
103 'Example/Baz',
104 'Example Bar',
106 // Third result when testing offset
107 'offsetresult' => [
108 'Example Foo',
110 ] ],
112 'Talk namespace prefix',
113 'query' => 'Talk:',
114 'results' => [
115 'Talk:Example',
116 'Talk:Sandbox',
118 ] ],
120 'User namespace prefix',
121 'query' => 'User:',
122 'results' => [
123 'User:Example',
125 ] ],
127 'Special namespace prefix',
128 'query' => 'Special:',
129 'results' => [
130 'Special:ActiveUsers',
131 'Special:AllMessages',
132 'Special:AllMyUploads',
134 // Third result when testing offset
135 'offsetresult' => [
136 'Special:AllPages',
138 ] ],
140 'Special namespace with prefix',
141 'query' => 'Special:Un',
142 'results' => [
143 'Special:Unblock',
144 'Special:UncategorizedCategories',
145 'Special:UncategorizedFiles',
147 // Third result when testing offset
148 'offsetresult' => [
149 'Special:UncategorizedPages',
151 ] ],
153 'Special page name',
154 'query' => 'Special:EditWatchlist',
155 'results' => [
156 'Special:EditWatchlist',
158 ] ],
160 'Special page subpages',
161 'query' => 'Special:EditWatchlist/',
162 'results' => [
163 'Special:EditWatchlist/clear',
164 'Special:EditWatchlist/raw',
166 ] ],
168 'Special page subpages with prefix',
169 'query' => 'Special:EditWatchlist/cl',
170 'results' => [
171 'Special:EditWatchlist/clear',
173 ] ],
175 'Namespace with case sensitive first letter',
176 'query' => 'NonCap:upper',
177 'results' => []
178 ] ],
180 'Multinamespace search',
181 'query' => 'B',
182 'results' => [
183 'Bar',
184 'NonCap:Bar',
186 'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
187 ] ],
189 'Multinamespace search with lowercase first letter',
190 'query' => 'sand',
191 'results' => [
192 'Sandbox',
193 'NonCap:sandbox',
195 'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
196 ] ],
201 * @dataProvider provideSearch
202 * @covers PrefixSearch::search
203 * @covers PrefixSearch::searchBackend
205 public function testSearch( array $case ) {
206 $this->searchProvision( null );
208 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
210 $searcher = new StringPrefixSearch;
211 $results = $searcher->search( $case['query'], 3, $namespaces );
212 $this->assertEquals(
213 $case['results'],
214 $results,
215 $case[0]
220 * @dataProvider provideSearch
221 * @covers PrefixSearch::search
222 * @covers PrefixSearch::searchBackend
224 public function testSearchWithOffset( array $case ) {
225 $this->searchProvision( null );
227 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
229 $searcher = new StringPrefixSearch;
230 $results = $searcher->search( $case['query'], 3, $namespaces, 1 );
232 // We don't expect the first result when offsetting
233 array_shift( $case['results'] );
234 // And sometimes we expect a different last result
235 $expected = isset( $case['offsetresult'] ) ?
236 array_merge( $case['results'], $case['offsetresult'] ) :
237 $case['results'];
239 $this->assertEquals(
240 $expected,
241 $results,
242 $case[0]
246 public static function provideSearchBackend() {
247 return [
249 'Simple case',
250 'provision' => [
251 'Bar',
252 'Barcelona',
253 'Barbara',
255 'query' => 'Bar',
256 'results' => [
257 'Bar',
258 'Barcelona',
259 'Barbara',
261 ] ],
263 'Exact match not on top (bug 70958)',
264 'provision' => [
265 'Barcelona',
266 'Bar',
267 'Barbara',
269 'query' => 'Bar',
270 'results' => [
271 'Bar',
272 'Barcelona',
273 'Barbara',
275 ] ],
277 'Exact match missing (bug 70958)',
278 'provision' => [
279 'Barcelona',
280 'Barbara',
281 'Bart',
283 'query' => 'Bar',
284 'results' => [
285 'Bar',
286 'Barcelona',
287 'Barbara',
289 ] ],
291 'Exact match missing and not existing',
292 'provision' => [
293 'Exile',
294 'Exist',
295 'External',
297 'query' => 'Ex',
298 'results' => [
299 'Exile',
300 'Exist',
301 'External',
303 ] ],
305 "Exact match shouldn't override already found match if " .
306 "exact is redirect and found isn't",
307 'provision' => [
308 // Target of the exact match is low in the list
309 'Redirect Test Worse Result',
310 'Redirect Test',
312 'query' => 'redirect test',
313 'results' => [
314 // Redirect target is pulled up and exact match isn't added
315 'Redirect Test',
316 'Redirect Test Worse Result',
318 ] ],
320 "Exact match shouldn't override already found match if " .
321 "both exact match and found match are redirect",
322 'provision' => [
323 // Another redirect to the same target as the exact match
324 // is low in the list
325 'Redirect Test2 Worse Result',
326 'Redirect test2',
328 'query' => 'redirect TEST2',
329 'results' => [
330 // Found redirect is pulled to the top and exact match isn't
331 // added
332 'Redirect test2',
333 'Redirect Test2 Worse Result',
335 ] ],
337 "Exact match should override any already found matches that " .
338 "are redirects to it",
339 'provision' => [
340 // Another redirect to the same target as the exact match
341 // is low in the list
342 'Redirect Test Worse Result',
343 'Redirect test',
345 'query' => 'Redirect Test',
346 'results' => [
347 // Found redirect is pulled to the top and exact match isn't
348 // added
349 'Redirect Test',
350 'Redirect Test Worse Result',
352 ] ],
357 * @dataProvider provideSearchBackend
358 * @covers PrefixSearch::searchBackend
360 public function testSearchBackend( array $case ) {
361 $this->searchProvision( $case['provision'] );
362 $searcher = new StringPrefixSearch;
363 $results = $searcher->search( $case['query'], 3 );
364 $this->assertEquals(
365 $case['results'],
366 $results,
367 $case[0]