]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - tests/phpunit/includes/PrefixSearchTest.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.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                 if ( wfGetDB( DB_REPLICA )->getType() === 'postgres' ) {
214                         // Postgres will sort lexicographically on utf8 code units (" " before "/")
215                         sort( $case['results'], SORT_STRING );
216                 }
217
218                 $searcher = new StringPrefixSearch;
219                 $results = $searcher->search( $case['query'], 3, $namespaces );
220                 $this->assertEquals(
221                         $case['results'],
222                         $results,
223                         $case[0]
224                 );
225         }
226
227         /**
228          * @dataProvider provideSearch
229          * @covers PrefixSearch::search
230          * @covers PrefixSearch::searchBackend
231          */
232         public function testSearchWithOffset( array $case ) {
233                 $this->searchProvision( null );
234
235                 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
236
237                 $searcher = new StringPrefixSearch;
238                 $results = $searcher->search( $case['query'], 3, $namespaces, 1 );
239
240                 if ( wfGetDB( DB_REPLICA )->getType() === 'postgres' ) {
241                         // Postgres will sort lexicographically on utf8 code units (" " before "/")
242                         sort( $case['results'], SORT_STRING );
243                 }
244
245                 // We don't expect the first result when offsetting
246                 array_shift( $case['results'] );
247                 // And sometimes we expect a different last result
248                 $expected = isset( $case['offsetresult'] ) ?
249                         array_merge( $case['results'], $case['offsetresult'] ) :
250                         $case['results'];
251
252                 $this->assertEquals(
253                         $expected,
254                         $results,
255                         $case[0]
256                 );
257         }
258
259         public static function provideSearchBackend() {
260                 return [
261                         [ [
262                                 'Simple case',
263                                 'provision' => [
264                                         'Bar',
265                                         'Barcelona',
266                                         'Barbara',
267                                 ],
268                                 'query' => 'Bar',
269                                 'results' => [
270                                         'Bar',
271                                         'Barcelona',
272                                         'Barbara',
273                                 ],
274                         ] ],
275                         [ [
276                                 'Exact match not on top (T72958)',
277                                 'provision' => [
278                                         'Barcelona',
279                                         'Bar',
280                                         'Barbara',
281                                 ],
282                                 'query' => 'Bar',
283                                 'results' => [
284                                         'Bar',
285                                         'Barcelona',
286                                         'Barbara',
287                                 ],
288                         ] ],
289                         [ [
290                                 'Exact match missing (T72958)',
291                                 'provision' => [
292                                         'Barcelona',
293                                         'Barbara',
294                                         'Bart',
295                                 ],
296                                 'query' => 'Bar',
297                                 'results' => [
298                                         'Bar',
299                                         'Barcelona',
300                                         'Barbara',
301                                 ],
302                         ] ],
303                         [ [
304                                 'Exact match missing and not existing',
305                                 'provision' => [
306                                         'Exile',
307                                         'Exist',
308                                         'External',
309                                 ],
310                                 'query' => 'Ex',
311                                 'results' => [
312                                         'Exile',
313                                         'Exist',
314                                         'External',
315                                 ],
316                         ] ],
317                         [ [
318                                 "Exact match shouldn't override already found match if " .
319                                         "exact is redirect and found isn't",
320                                 'provision' => [
321                                         // Target of the exact match is low in the list
322                                         'Redirect Test Worse Result',
323                                         'Redirect Test',
324                                 ],
325                                 'query' => 'redirect test',
326                                 'results' => [
327                                         // Redirect target is pulled up and exact match isn't added
328                                         'Redirect Test',
329                                         'Redirect Test Worse Result',
330                                 ],
331                         ] ],
332                         [ [
333                                 "Exact match shouldn't override already found match if " .
334                                         "both exact match and found match are redirect",
335                                 'provision' => [
336                                         // Another redirect to the same target as the exact match
337                                         // is low in the list
338                                         'Redirect Test2 Worse Result',
339                                         'Redirect test2',
340                                 ],
341                                 'query' => 'redirect TEST2',
342                                 'results' => [
343                                         // Found redirect is pulled to the top and exact match isn't
344                                         // added
345                                         'Redirect test2',
346                                         'Redirect Test2 Worse Result',
347                                 ],
348                         ] ],
349                         [ [
350                                 "Exact match should override any already found matches that " .
351                                         "are redirects to it",
352                                 'provision' => [
353                                         // Another redirect to the same target as the exact match
354                                         // is low in the list
355                                         'Redirect Test Worse Result',
356                                         'Redirect test',
357                                 ],
358                                 'query' => 'Redirect Test',
359                                 'results' => [
360                                         // Found redirect is pulled to the top and exact match isn't
361                                         // added
362                                         'Redirect Test',
363                                         'Redirect Test Worse Result',
364                                 ],
365                         ] ],
366                 ];
367         }
368
369         /**
370          * @dataProvider provideSearchBackend
371          * @covers PrefixSearch::searchBackend
372          */
373         public function testSearchBackend( array $case ) {
374                 $this->searchProvision( $case['provision'] );
375                 $searcher = new StringPrefixSearch;
376                 $results = $searcher->search( $case['query'], 3 );
377                 $this->assertEquals(
378                         $case['results'],
379                         $results,
380                         $case[0]
381                 );
382         }
383 }