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