Merge "Block: Remove old IP addresses from default autoblock_whitelist message"
[lhc/web/wiklou.git] / tests / phpunit / includes / PrefixSearchTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * @group Search
7 * @group Database
8 * @covers PrefixSearch
9 */
10 class PrefixSearchTest extends MediaWikiLangTestCase {
11 const NS_NONCAP = 12346;
12
13 private $originalHandlers;
14
15 public function addDBDataOnce() {
16 if ( !$this->isWikitextNS( NS_MAIN ) ) {
17 // tests are skipped if NS_MAIN is not wikitext
18 return;
19 }
20
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' );
35
36 $this->insertPage( 'Talk:Sandbox' );
37 $this->insertPage( 'Talk:Example' );
38
39 $this->insertPage( 'User:Example' );
40
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' ) );
44 }
45
46 protected function setUp() {
47 parent::setUp();
48
49 if ( !$this->isWikitextNS( NS_MAIN ) ) {
50 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
51 }
52
53 // Avoid special pages from extensions interferring with the tests
54 $this->setMwGlobals( [
55 'wgSpecialPages' => [],
56 'wgHooks' => [],
57 'wgExtraNamespaces' => [ self::NS_NONCAP => 'NonCap' ],
58 'wgCapitalLinkOverrides' => [ self::NS_NONCAP => false ],
59 ] );
60
61 $this->originalHandlers = TestingAccessWrapper::newFromClass( 'Hooks' )->handlers;
62 TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = [];
63
64 // Clear caches so that our new namespace appears
65 MWNamespace::getCanonicalNamespaces( true );
66 Language::factory( 'en' )->resetNamespaces();
67
68 SpecialPageFactory::resetList();
69 }
70
71 public function tearDown() {
72 parent::tearDown();
73
74 TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = $this->originalHandlers;
75
76 SpecialPageFactory::resetList();
77 }
78
79 protected function searchProvision( array $results = null ) {
80 if ( $results === null ) {
81 $this->setMwGlobals( 'wgHooks', [] );
82 } else {
83 $this->setMwGlobals( 'wgHooks', [
84 'PrefixSearchBackend' => [
85 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
86 $srchres = $results;
87 return false;
88 }
89 ],
90 ] );
91 }
92 }
93
94 public static function provideSearch() {
95 return [
96 [ [
97 'Empty string',
98 'query' => '',
99 'results' => [],
100 ] ],
101 [ [
102 'Main namespace with title prefix',
103 'query' => 'Ex',
104 'results' => [
105 'Example',
106 'Example/Baz',
107 'Example Bar',
108 ],
109 // Third result when testing offset
110 'offsetresult' => [
111 'Example Foo',
112 ],
113 ] ],
114 [ [
115 'Talk namespace prefix',
116 'query' => 'Talk:',
117 'results' => [
118 'Talk:Example',
119 'Talk:Sandbox',
120 ],
121 ] ],
122 [ [
123 'User namespace prefix',
124 'query' => 'User:',
125 'results' => [
126 'User:Example',
127 ],
128 ] ],
129 [ [
130 'Special namespace prefix',
131 'query' => 'Special:',
132 'results' => [
133 'Special:ActiveUsers',
134 'Special:AllMessages',
135 'Special:AllMyUploads',
136 ],
137 // Third result when testing offset
138 'offsetresult' => [
139 'Special:AllPages',
140 ],
141 ] ],
142 [ [
143 'Special namespace with prefix',
144 'query' => 'Special:Un',
145 'results' => [
146 'Special:Unblock',
147 'Special:UncategorizedCategories',
148 'Special:UncategorizedFiles',
149 ],
150 // Third result when testing offset
151 'offsetresult' => [
152 'Special:UncategorizedPages',
153 ],
154 ] ],
155 [ [
156 'Special page name',
157 'query' => 'Special:EditWatchlist',
158 'results' => [
159 'Special:EditWatchlist',
160 ],
161 ] ],
162 [ [
163 'Special page subpages',
164 'query' => 'Special:EditWatchlist/',
165 'results' => [
166 'Special:EditWatchlist/clear',
167 'Special:EditWatchlist/raw',
168 ],
169 ] ],
170 [ [
171 'Special page subpages with prefix',
172 'query' => 'Special:EditWatchlist/cl',
173 'results' => [
174 'Special:EditWatchlist/clear',
175 ],
176 ] ],
177 [ [
178 'Namespace with case sensitive first letter',
179 'query' => 'NonCap:upper',
180 'results' => []
181 ] ],
182 [ [
183 'Multinamespace search',
184 'query' => 'B',
185 'results' => [
186 'Bar',
187 'NonCap:Bar',
188 ],
189 'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
190 ] ],
191 [ [
192 'Multinamespace search with lowercase first letter',
193 'query' => 'sand',
194 'results' => [
195 'Sandbox',
196 'NonCap:sandbox',
197 ],
198 'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
199 ] ],
200 ];
201 }
202
203 /**
204 * @dataProvider provideSearch
205 * @covers PrefixSearch::search
206 * @covers PrefixSearch::searchBackend
207 */
208 public function testSearch( array $case ) {
209 $this->searchProvision( null );
210
211 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
212
213 $searcher = new StringPrefixSearch;
214 $results = $searcher->search( $case['query'], 3, $namespaces );
215 $this->assertEquals(
216 $case['results'],
217 $results,
218 $case[0]
219 );
220 }
221
222 /**
223 * @dataProvider provideSearch
224 * @covers PrefixSearch::search
225 * @covers PrefixSearch::searchBackend
226 */
227 public function testSearchWithOffset( array $case ) {
228 $this->searchProvision( null );
229
230 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
231
232 $searcher = new StringPrefixSearch;
233 $results = $searcher->search( $case['query'], 3, $namespaces, 1 );
234
235 // We don't expect the first result when offsetting
236 array_shift( $case['results'] );
237 // And sometimes we expect a different last result
238 $expected = isset( $case['offsetresult'] ) ?
239 array_merge( $case['results'], $case['offsetresult'] ) :
240 $case['results'];
241
242 $this->assertEquals(
243 $expected,
244 $results,
245 $case[0]
246 );
247 }
248
249 public static function provideSearchBackend() {
250 return [
251 [ [
252 'Simple case',
253 'provision' => [
254 'Bar',
255 'Barcelona',
256 'Barbara',
257 ],
258 'query' => 'Bar',
259 'results' => [
260 'Bar',
261 'Barcelona',
262 'Barbara',
263 ],
264 ] ],
265 [ [
266 'Exact match not on top (T72958)',
267 'provision' => [
268 'Barcelona',
269 'Bar',
270 'Barbara',
271 ],
272 'query' => 'Bar',
273 'results' => [
274 'Bar',
275 'Barcelona',
276 'Barbara',
277 ],
278 ] ],
279 [ [
280 'Exact match missing (T72958)',
281 'provision' => [
282 'Barcelona',
283 'Barbara',
284 'Bart',
285 ],
286 'query' => 'Bar',
287 'results' => [
288 'Bar',
289 'Barcelona',
290 'Barbara',
291 ],
292 ] ],
293 [ [
294 'Exact match missing and not existing',
295 'provision' => [
296 'Exile',
297 'Exist',
298 'External',
299 ],
300 'query' => 'Ex',
301 'results' => [
302 'Exile',
303 'Exist',
304 'External',
305 ],
306 ] ],
307 [ [
308 "Exact match shouldn't override already found match if " .
309 "exact is redirect and found isn't",
310 'provision' => [
311 // Target of the exact match is low in the list
312 'Redirect Test Worse Result',
313 'Redirect Test',
314 ],
315 'query' => 'redirect test',
316 'results' => [
317 // Redirect target is pulled up and exact match isn't added
318 'Redirect Test',
319 'Redirect Test Worse Result',
320 ],
321 ] ],
322 [ [
323 "Exact match shouldn't override already found match if " .
324 "both exact match and found match are redirect",
325 'provision' => [
326 // Another redirect to the same target as the exact match
327 // is low in the list
328 'Redirect Test2 Worse Result',
329 'Redirect test2',
330 ],
331 'query' => 'redirect TEST2',
332 'results' => [
333 // Found redirect is pulled to the top and exact match isn't
334 // added
335 'Redirect test2',
336 'Redirect Test2 Worse Result',
337 ],
338 ] ],
339 [ [
340 "Exact match should override any already found matches that " .
341 "are redirects to it",
342 'provision' => [
343 // Another redirect to the same target as the exact match
344 // is low in the list
345 'Redirect Test Worse Result',
346 'Redirect test',
347 ],
348 'query' => 'Redirect Test',
349 'results' => [
350 // Found redirect is pulled to the top and exact match isn't
351 // added
352 'Redirect Test',
353 'Redirect Test Worse Result',
354 ],
355 ] ],
356 ];
357 }
358
359 /**
360 * @dataProvider provideSearchBackend
361 * @covers PrefixSearch::searchBackend
362 */
363 public function testSearchBackend( array $case ) {
364 $this->searchProvision( $case['provision'] );
365 $searcher = new StringPrefixSearch;
366 $results = $searcher->search( $case['query'], 3 );
367 $this->assertEquals(
368 $case['results'],
369 $results,
370 $case[0]
371 );
372 }
373 }