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