Update formatting
[lhc/web/wiklou.git] / tests / phpunit / includes / api / query / ApiQueryBasicTest.php
1 <?php
2 /**
3 *
4 *
5 * Created on Feb 6, 2013
6 *
7 * Copyright © 2013 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * http://www.gnu.org/copyleft/gpl.html
23 *
24 * @file
25 *
26 * These tests validate basic functionality of the api query module
27 *
28 * @group API
29 * @group Database
30 * @group medium
31 */
32 class ApiQueryBasicTest extends ApiTestCase {
33 /**
34 * Create a set of pages. These must not change, otherwise the tests might give wrong results.
35 * @see MediaWikiTestCase::addDBData()
36 */
37 function addDBData() {
38 try {
39 if ( Title::newFromText( 'AQBT-All' )->exists() ) {
40 return;
41 }
42
43 // Ordering is important, as it will be returned in the same order as stored in the index
44 $this->editPage( 'AQBT-All', '[[Category:AQBT-Cat]] [[AQBT-Links]] {{AQBT-T}}' );
45 $this->editPage( 'AQBT-Categories', '[[Category:AQBT-Cat]]' );
46 $this->editPage( 'AQBT-Links', '[[AQBT-All]] [[AQBT-Categories]] [[AQBT-Templates]]' );
47 $this->editPage( 'AQBT-Templates', '{{AQBT-T}}' );
48 $this->editPage( 'AQBT-T', 'Content', '', NS_TEMPLATE );
49
50 // Refresh due to the bug with listing transclusions as links if they don't exist
51 $this->editPage( 'AQBT-All', '[[Category:AQBT-Cat]] [[AQBT-Links]] {{AQBT-T}}' );
52 $this->editPage( 'AQBT-Templates', '{{AQBT-T}}' );
53 } catch ( Exception $e ) {
54 $this->exceptionFromAddDBData = $e;
55 }
56 }
57
58 private static $links = array(
59 array( 'prop' => 'links', 'titles' => 'AQBT-All' ),
60 array( 'pages' => array(
61 '1' => array(
62 'pageid' => 1,
63 'ns' => 0,
64 'title' => 'AQBT-All',
65 'links' => array(
66 array( 'ns' => 0, 'title' => 'AQBT-Links' ),
67 ) ) ) ) );
68
69 private static $templates = array(
70 array( 'prop' => 'templates', 'titles' => 'AQBT-All' ),
71 array( 'pages' => array(
72 '1' => array(
73 'pageid' => 1,
74 'ns' => 0,
75 'title' => 'AQBT-All',
76 'templates' => array(
77 array( 'ns' => 10, 'title' => 'Template:AQBT-T' ),
78 ) ) ) ) );
79
80 private static $categories = array(
81 array( 'prop' => 'categories', 'titles' => 'AQBT-All' ),
82 array( 'pages' => array(
83 '1' => array(
84 'pageid' => 1,
85 'ns' => 0,
86 'title' => 'AQBT-All',
87 'categories' => array(
88 array( 'ns' => 14, 'title' => 'Category:AQBT-Cat' ),
89 ) ) ) ) );
90
91 private static $allpages = array(
92 array( 'list' => 'allpages', 'apprefix' => 'AQBT-' ),
93 array( 'allpages' => array(
94 array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ),
95 array( 'pageid' => 2, 'ns' => 0, 'title' => 'AQBT-Categories' ),
96 array( 'pageid' => 3, 'ns' => 0, 'title' => 'AQBT-Links' ),
97 array( 'pageid' => 4, 'ns' => 0, 'title' => 'AQBT-Templates' ),
98 ) ) );
99
100 private static $alllinks = array(
101 array( 'list' => 'alllinks', 'alprefix' => 'AQBT-' ),
102 array( 'alllinks' => array(
103 array( 'ns' => 0, 'title' => 'AQBT-All' ),
104 array( 'ns' => 0, 'title' => 'AQBT-Categories' ),
105 array( 'ns' => 0, 'title' => 'AQBT-Links' ),
106 array( 'ns' => 0, 'title' => 'AQBT-Templates' ),
107 ) ) );
108
109 private static $alltransclusions = array(
110 array( 'list' => 'alltransclusions', 'atprefix' => 'AQBT-' ),
111 array( 'alltransclusions' => array(
112 array( 'ns' => 10, 'title' => 'Template:AQBT-T' ),
113 array( 'ns' => 10, 'title' => 'Template:AQBT-T' ),
114 ) ) );
115
116 private static $allcategories = array(
117 array( 'list' => 'allcategories', 'acprefix' => 'AQBT-' ),
118 array( 'allcategories' => array(
119 array( '*' => 'AQBT-Cat' ),
120 ) ) );
121
122 private static $backlinks = array(
123 array( 'list' => 'backlinks', 'bltitle' => 'AQBT-Links' ),
124 array( 'backlinks' => array(
125 array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ),
126 ) ) );
127
128 private static $embeddedin = array(
129 array( 'list' => 'embeddedin', 'eititle' => 'Template:AQBT-T' ),
130 array( 'embeddedin' => array(
131 array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ),
132 array( 'pageid' => 4, 'ns' => 0, 'title' => 'AQBT-Templates' ),
133 ) ) );
134
135 private static $categorymembers = array(
136 array( 'list' => 'categorymembers', 'cmtitle' => 'Category:AQBT-Cat' ),
137 array( 'categorymembers' => array(
138 array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ),
139 array( 'pageid' => 2, 'ns' => 0, 'title' => 'AQBT-Categories' ),
140 ) ) );
141
142 private static $generatorAllpages = array(
143 array( 'generator' => 'allpages', 'gapprefix' => 'AQBT-' ),
144 array( 'pages' => array(
145 '1' => array(
146 'pageid' => 1,
147 'ns' => 0,
148 'title' => 'AQBT-All' ),
149 '2' => array(
150 'pageid' => 2,
151 'ns' => 0,
152 'title' => 'AQBT-Categories' ),
153 '3' => array(
154 'pageid' => 3,
155 'ns' => 0,
156 'title' => 'AQBT-Links' ),
157 '4' => array(
158 'pageid' => 4,
159 'ns' => 0,
160 'title' => 'AQBT-Templates' ),
161 ) ) );
162
163 private static $generatorLinks = array(
164 array( 'generator' => 'links', 'titles' => 'AQBT-Links' ),
165 array( 'pages' => array(
166 '1' => array(
167 'pageid' => 1,
168 'ns' => 0,
169 'title' => 'AQBT-All' ),
170 '2' => array(
171 'pageid' => 2,
172 'ns' => 0,
173 'title' => 'AQBT-Categories' ),
174 '4' => array(
175 'pageid' => 4,
176 'ns' => 0,
177 'title' => 'AQBT-Templates' ),
178 ) ) );
179
180 private static $generatorLinksPropLinks = array(
181 array( 'prop' => 'links' ),
182 array( 'pages' => array(
183 '1' => array( 'links' => array(
184 array( 'ns' => 0, 'title' => 'AQBT-Links' ),
185 ) ) ) ) );
186
187 private static $generatorLinksPropTemplates = array(
188 array( 'prop' => 'templates' ),
189 array( 'pages' => array(
190 '1' => array( 'templates' => array(
191 array( 'ns' => 10, 'title' => 'Template:AQBT-T' ) ) ),
192 '4' => array( 'templates' => array(
193 array( 'ns' => 10, 'title' => 'Template:AQBT-T' ) ) ),
194 ) ) );
195
196 /**
197 * Test basic props
198 */
199 public function testProps() {
200 $this->check( self::$links );
201 $this->check( self::$templates );
202 $this->check( self::$categories );
203 }
204
205 /**
206 * Test basic lists
207 */
208 public function testLists() {
209 $this->check( self::$allpages );
210 $this->check( self::$alllinks );
211 $this->check( self::$alltransclusions );
212 // This test is temporarily disabled until a sqlite bug is fixed
213 // $this->check( self::$allcategories );
214 $this->check( self::$backlinks );
215 $this->check( self::$embeddedin );
216 $this->check( self::$categorymembers );
217 }
218
219 /**
220 * Test basic lists
221 */
222 public function testAllTogether() {
223
224 // All props together
225 $this->check( $this->merge(
226 self::$links,
227 self::$templates,
228 self::$categories
229 ) );
230
231 // All lists together
232 $this->check( $this->merge(
233 self::$allpages,
234 self::$alllinks,
235 self::$alltransclusions,
236 // This test is temporarily disabled until a sqlite bug is fixed
237 // self::$allcategories,
238 self::$backlinks,
239 self::$embeddedin,
240 self::$categorymembers
241 ) );
242
243 // All props+lists together
244 $this->check( $this->merge(
245 self::$links,
246 self::$templates,
247 self::$categories,
248 self::$allpages,
249 self::$alllinks,
250 self::$alltransclusions,
251 // This test is temporarily disabled until a sqlite bug is fixed
252 // self::$allcategories,
253 self::$backlinks,
254 self::$embeddedin,
255 self::$categorymembers
256 ) );
257 }
258
259 /**
260 * Test basic lists
261 */
262 public function testGenerator() {
263 // generator=allpages
264 $this->check( self::$generatorAllpages );
265 // generator=allpages & list=allpages
266 $this->check( $this->merge(
267 self::$generatorAllpages,
268 self::$allpages ) );
269 // generator=links
270 $this->check( self::$generatorLinks );
271 // generator=links & prop=links
272 $this->check( $this->merge(
273 self::$generatorLinks,
274 self::$generatorLinksPropLinks ) );
275 // generator=links & prop=templates
276 $this->check( $this->merge(
277 self::$generatorLinks,
278 self::$generatorLinksPropTemplates ) );
279 // generator=links & prop=links|templates
280 $this->check( $this->merge(
281 self::$generatorLinks,
282 self::$generatorLinksPropLinks,
283 self::$generatorLinksPropTemplates ) );
284 // generator=links & prop=links|templates & list=allpages|...
285 $this->check( $this->merge(
286 self::$generatorLinks,
287 self::$generatorLinksPropLinks,
288 self::$generatorLinksPropTemplates,
289 self::$allpages,
290 self::$alllinks,
291 self::$alltransclusions,
292 // This test is temporarily disabled until a sqlite bug is fixed
293 // self::$allcategories,
294 self::$backlinks,
295 self::$embeddedin,
296 self::$categorymembers ) );
297 }
298
299 /**
300 * Merges all requests (parameter arrays) into one
301 * @return array
302 */
303 private function merge( /*...*/ ) {
304 $request = array();
305 $expected = array();
306 foreach ( func_get_args() as $v ) {
307 $request = array_merge_recursive( $request, $v[0] );
308 $this->mergeExpected( $expected, $v[1] );
309 }
310 return array( $request, $expected );
311 }
312
313 /**
314 * Recursively merges the expected values in the $item into the $all
315 */
316 private function mergeExpected( &$all, $item ) {
317 foreach ( $item as $k => $v ) {
318 if ( array_key_exists( $k, $all ) ) {
319 if ( is_array( $all[$k] ) ) {
320 $this->mergeExpected( $all[$k], $v );
321 } else {
322 $this->assertEquals( $all[$k], $v );
323 }
324 } else {
325 $all[$k] = $v;
326 }
327 }
328 }
329
330 /**
331 * Checks that the request's result matches the expected results.
332 * @param $values array is a two element array( request, expected_results )
333 * @throws Exception
334 */
335 private function check( $values ) {
336 $request = $values[0];
337 $expected = $values[1];
338 if ( !array_key_exists( 'action', $request ) ) {
339 $request['action'] = 'query';
340 }
341 foreach ( $request as &$val ) {
342 if ( is_array( $val ) ) {
343 $val = implode( '|', array_unique( $val ) );
344 }
345 }
346 $result = $this->doApiRequest( $request );
347 $result = $result[0];
348 $expected = array( 'query' => $expected );
349 try {
350 $this->assertQueryResults( $expected, $result );
351 } catch ( Exception $e ) {
352 print( "\nRequest:\n" );
353 print_r( $request );
354 print( "\nExpected:\n" );
355 print_r( $expected );
356 print( "\nResult:\n" );
357 print_r( $result );
358 throw $e; // rethrow it
359 }
360 }
361
362 /**
363 * Recursively compare arrays, ignoring mismatches in numeric key and pageids.
364 * @param $expected array expected values
365 * @param $result array returned values
366 */
367 private function assertQueryResults( $expected, $result ) {
368 reset( $expected );
369 reset( $result );
370 while ( true ) {
371 $e = each( $expected );
372 $r = each( $result );
373 // If either of the arrays is shorter, abort. If both are done, success.
374 $this->assertEquals( (bool)$e, (bool)$r );
375 if ( !$e ) {
376 break; // done
377 }
378 // continue only if keys are identical or both keys are numeric
379 $this->assertTrue( $e['key'] === $r['key'] || ( is_numeric( $e['key'] ) && is_numeric( $r['key'] ) ) );
380 // don't compare pageids
381 if ( $e['key'] !== 'pageid' ) {
382 // If values are arrays, compare recursively, otherwise compare with ===
383 if ( is_array( $e['value'] ) && is_array( $r['value'] ) ) {
384 $this->assertQueryResults( $e['value'], $r['value'] );
385 } else {
386 $this->assertEquals( $e['value'], $r['value'] );
387 }
388 }
389 }
390 }
391 }