Merge "jquery.makeCollapsible: collapse to caption support"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiUploadTest.php
1 <?php
2
3 /**
4 * @group API
5 * @group Database
6 * @group medium
7 */
8
9 /**
10 * n.b. Ensure that you can write to the images/ directory as the
11 * user that will run tests.
12 */
13
14 // Note for reviewers: this intentionally duplicates functionality already in "ApiSetup" and so on.
15 // This framework works better IMO and has less strangeness (such as test cases inheriting from "ApiSetup"...)
16 // (and in the case of the other Upload tests, this flat out just actually works... )
17
18 // TODO: port the other Upload tests, and other API tests to this framework
19
20 require_once 'ApiTestCaseUpload.php';
21
22 /**
23 * @group Database
24 * @group Broken
25 * Broken test, reports false errors from time to time.
26 * See https://bugzilla.wikimedia.org/26169
27 *
28 * This is pretty sucky... needs to be prettified.
29 */
30 class ApiUploadTest extends ApiTestCaseUpload {
31 /**
32 * Testing login
33 * XXX this is a funny way of getting session context
34 */
35 public function testLogin() {
36 $user = self::$users['uploader'];
37
38 $params = array(
39 'action' => 'login',
40 'lgname' => $user->username,
41 'lgpassword' => $user->password
42 );
43 list( $result, , $session ) = $this->doApiRequest( $params );
44 $this->assertArrayHasKey( "login", $result );
45 $this->assertArrayHasKey( "result", $result['login'] );
46 $this->assertEquals( "NeedToken", $result['login']['result'] );
47 $token = $result['login']['token'];
48
49 $params = array(
50 'action' => 'login',
51 'lgtoken' => $token,
52 'lgname' => $user->username,
53 'lgpassword' => $user->password
54 );
55 list( $result, , $session ) = $this->doApiRequest( $params, $session );
56 $this->assertArrayHasKey( "login", $result );
57 $this->assertArrayHasKey( "result", $result['login'] );
58 $this->assertEquals( "Success", $result['login']['result'] );
59 $this->assertArrayHasKey( 'lgtoken', $result['login'] );
60
61 $this->assertNotEmpty( $session, 'API Login must return a session' );
62
63 return $session;
64 }
65
66 /**
67 * @depends testLogin
68 */
69 public function testUploadRequiresToken( $session ) {
70 $exception = false;
71 try {
72 $this->doApiRequest( array(
73 'action' => 'upload'
74 ) );
75 } catch ( UsageException $e ) {
76 $exception = true;
77 $this->assertEquals( "The token parameter must be set", $e->getMessage() );
78 }
79 $this->assertTrue( $exception, "Got exception" );
80 }
81
82 /**
83 * @depends testLogin
84 */
85 public function testUploadMissingParams( $session ) {
86 $exception = false;
87 try {
88 $this->doApiRequestWithToken( array(
89 'action' => 'upload',
90 ), $session, self::$users['uploader']->user );
91 } catch ( UsageException $e ) {
92 $exception = true;
93 $this->assertEquals( "One of the parameters filekey, file, url, statuskey is required",
94 $e->getMessage() );
95 }
96 $this->assertTrue( $exception, "Got exception" );
97 }
98
99 /**
100 * @depends testLogin
101 */
102 public function testUpload( $session ) {
103 $extension = 'png';
104 $mimeType = 'image/png';
105
106 try {
107 $randomImageGenerator = new RandomImageGenerator();
108 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
109 } catch ( Exception $e ) {
110 $this->markTestIncomplete( $e->getMessage() );
111 }
112
113 $filePath = $filePaths[0];
114 $fileSize = filesize( $filePath );
115 $fileName = basename( $filePath );
116
117 $this->deleteFileByFileName( $fileName );
118 $this->deleteFileByContent( $filePath );
119
120 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
121 $this->markTestIncomplete( "Couldn't upload file!\n" );
122 }
123
124 $params = array(
125 'action' => 'upload',
126 'filename' => $fileName,
127 'file' => 'dummy content',
128 'comment' => 'dummy comment',
129 'text' => "This is the page text for $fileName",
130 );
131
132 $exception = false;
133 try {
134 list( $result, , ) = $this->doApiRequestWithToken( $params, $session,
135 self::$users['uploader']->user );
136 } catch ( UsageException $e ) {
137 $exception = true;
138 }
139 $this->assertTrue( isset( $result['upload'] ) );
140 $this->assertEquals( 'Success', $result['upload']['result'] );
141 $this->assertEquals( $fileSize, (int)$result['upload']['imageinfo']['size'] );
142 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
143 $this->assertFalse( $exception );
144
145 // clean up
146 $this->deleteFileByFilename( $fileName );
147 unlink( $filePath );
148 }
149
150 /**
151 * @depends testLogin
152 */
153 public function testUploadZeroLength( $session ) {
154 $mimeType = 'image/png';
155
156 $filePath = tempnam( wfTempDir(), "" );
157 $fileName = "apiTestUploadZeroLength.png";
158
159 $this->deleteFileByFileName( $fileName );
160
161 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
162 $this->markTestIncomplete( "Couldn't upload file!\n" );
163 }
164
165 $params = array(
166 'action' => 'upload',
167 'filename' => $fileName,
168 'file' => 'dummy content',
169 'comment' => 'dummy comment',
170 'text' => "This is the page text for $fileName",
171 );
172
173 $exception = false;
174 try {
175 $this->doApiRequestWithToken( $params, $session, self::$users['uploader']->user );
176 } catch ( UsageException $e ) {
177 $this->assertContains( 'The file you submitted was empty', $e->getMessage() );
178 $exception = true;
179 }
180 $this->assertTrue( $exception );
181
182 // clean up
183 $this->deleteFileByFilename( $fileName );
184 unlink( $filePath );
185 }
186
187 /**
188 * @depends testLogin
189 */
190 public function testUploadSameFileName( $session ) {
191 $extension = 'png';
192 $mimeType = 'image/png';
193
194 try {
195 $randomImageGenerator = new RandomImageGenerator();
196 $filePaths = $randomImageGenerator->writeImages( 2, $extension, wfTempDir() );
197 } catch ( Exception $e ) {
198 $this->markTestIncomplete( $e->getMessage() );
199 }
200
201 // we'll reuse this filename
202 $fileName = basename( $filePaths[0] );
203
204 // clear any other files with the same name
205 $this->deleteFileByFileName( $fileName );
206
207 // we reuse these params
208 $params = array(
209 'action' => 'upload',
210 'filename' => $fileName,
211 'file' => 'dummy content',
212 'comment' => 'dummy comment',
213 'text' => "This is the page text for $fileName",
214 );
215
216 // first upload .... should succeed
217
218 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] ) ) {
219 $this->markTestIncomplete( "Couldn't upload file!\n" );
220 }
221
222 $exception = false;
223 try {
224 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
225 self::$users['uploader']->user );
226 } catch ( UsageException $e ) {
227 $exception = true;
228 }
229 $this->assertTrue( isset( $result['upload'] ) );
230 $this->assertEquals( 'Success', $result['upload']['result'] );
231 $this->assertFalse( $exception );
232
233 // second upload with the same name (but different content)
234
235 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] ) ) {
236 $this->markTestIncomplete( "Couldn't upload file!\n" );
237 }
238
239 $exception = false;
240 try {
241 list( $result, , ) = $this->doApiRequestWithToken( $params, $session,
242 self::$users['uploader']->user ); // FIXME: leaks a temporary file
243 } catch ( UsageException $e ) {
244 $exception = true;
245 }
246 $this->assertTrue( isset( $result['upload'] ) );
247 $this->assertEquals( 'Warning', $result['upload']['result'] );
248 $this->assertTrue( isset( $result['upload']['warnings'] ) );
249 $this->assertTrue( isset( $result['upload']['warnings']['exists'] ) );
250 $this->assertFalse( $exception );
251
252 // clean up
253 $this->deleteFileByFilename( $fileName );
254 unlink( $filePaths[0] );
255 unlink( $filePaths[1] );
256 }
257
258 /**
259 * @depends testLogin
260 */
261 public function testUploadSameContent( $session ) {
262 $extension = 'png';
263 $mimeType = 'image/png';
264
265 try {
266 $randomImageGenerator = new RandomImageGenerator();
267 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
268 } catch ( Exception $e ) {
269 $this->markTestIncomplete( $e->getMessage() );
270 }
271
272 $fileNames[0] = basename( $filePaths[0] );
273 $fileNames[1] = "SameContentAs" . $fileNames[0];
274
275 // clear any other files with the same name or content
276 $this->deleteFileByContent( $filePaths[0] );
277 $this->deleteFileByFileName( $fileNames[0] );
278 $this->deleteFileByFileName( $fileNames[1] );
279
280 // first upload .... should succeed
281
282 $params = array(
283 'action' => 'upload',
284 'filename' => $fileNames[0],
285 'file' => 'dummy content',
286 'comment' => 'dummy comment',
287 'text' => "This is the page text for " . $fileNames[0],
288 );
289
290 if ( !$this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePaths[0] ) ) {
291 $this->markTestIncomplete( "Couldn't upload file!\n" );
292 }
293
294 $exception = false;
295 try {
296 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
297 self::$users['uploader']->user );
298 } catch ( UsageException $e ) {
299 $exception = true;
300 }
301 $this->assertTrue( isset( $result['upload'] ) );
302 $this->assertEquals( 'Success', $result['upload']['result'] );
303 $this->assertFalse( $exception );
304
305 // second upload with the same content (but different name)
306
307 if ( !$this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePaths[0] ) ) {
308 $this->markTestIncomplete( "Couldn't upload file!\n" );
309 }
310
311 $params = array(
312 'action' => 'upload',
313 'filename' => $fileNames[1],
314 'file' => 'dummy content',
315 'comment' => 'dummy comment',
316 'text' => "This is the page text for " . $fileNames[1],
317 );
318
319 $exception = false;
320 try {
321 list( $result ) = $this->doApiRequestWithToken( $params, $session,
322 self::$users['uploader']->user ); // FIXME: leaks a temporary file
323 } catch ( UsageException $e ) {
324 $exception = true;
325 }
326 $this->assertTrue( isset( $result['upload'] ) );
327 $this->assertEquals( 'Warning', $result['upload']['result'] );
328 $this->assertTrue( isset( $result['upload']['warnings'] ) );
329 $this->assertTrue( isset( $result['upload']['warnings']['duplicate'] ) );
330 $this->assertFalse( $exception );
331
332 // clean up
333 $this->deleteFileByFilename( $fileNames[0] );
334 $this->deleteFileByFilename( $fileNames[1] );
335 unlink( $filePaths[0] );
336 }
337
338 /**
339 * @depends testLogin
340 */
341 public function testUploadStash( $session ) {
342 $this->setMwGlobals( array(
343 'wgUser' => self::$users['uploader']->user, // @todo FIXME: still used somewhere
344 ) );
345
346 $extension = 'png';
347 $mimeType = 'image/png';
348
349 try {
350 $randomImageGenerator = new RandomImageGenerator();
351 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
352 } catch ( Exception $e ) {
353 $this->markTestIncomplete( $e->getMessage() );
354 }
355
356 $filePath = $filePaths[0];
357 $fileSize = filesize( $filePath );
358 $fileName = basename( $filePath );
359
360 $this->deleteFileByFileName( $fileName );
361 $this->deleteFileByContent( $filePath );
362
363 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
364 $this->markTestIncomplete( "Couldn't upload file!\n" );
365 }
366
367 $params = array(
368 'action' => 'upload',
369 'stash' => 1,
370 'filename' => $fileName,
371 'file' => 'dummy content',
372 'comment' => 'dummy comment',
373 'text' => "This is the page text for $fileName",
374 );
375
376 $exception = false;
377 try {
378 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
379 self::$users['uploader']->user ); // FIXME: leaks a temporary file
380 } catch ( UsageException $e ) {
381 $exception = true;
382 }
383 $this->assertFalse( $exception );
384 $this->assertTrue( isset( $result['upload'] ) );
385 $this->assertEquals( 'Success', $result['upload']['result'] );
386 $this->assertEquals( $fileSize, (int)$result['upload']['imageinfo']['size'] );
387 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
388 $this->assertTrue( isset( $result['upload']['filekey'] ) );
389 $this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] );
390 $filekey = $result['upload']['filekey'];
391
392 // it should be visible from Special:UploadStash
393 // XXX ...but how to test this, with a fake WebRequest with the session?
394
395 // now we should try to release the file from stash
396 $params = array(
397 'action' => 'upload',
398 'filekey' => $filekey,
399 'filename' => $fileName,
400 'comment' => 'dummy comment',
401 'text' => "This is the page text for $fileName, altered",
402 );
403
404 $this->clearFakeUploads();
405 $exception = false;
406 try {
407 list( $result ) = $this->doApiRequestWithToken( $params, $session,
408 self::$users['uploader']->user );
409 } catch ( UsageException $e ) {
410 $exception = true;
411 }
412 $this->assertTrue( isset( $result['upload'] ) );
413 $this->assertEquals( 'Success', $result['upload']['result'] );
414 $this->assertFalse( $exception, "No UsageException exception." );
415
416 // clean up
417 $this->deleteFileByFilename( $fileName );
418 unlink( $filePath );
419 }
420
421 /**
422 * @depends testLogin
423 */
424 public function testUploadChunks( $session ) {
425 $this->setMwGlobals( array(
426 'wgUser' => self::$users['uploader']->user, // @todo FIXME: still used somewhere
427 ) );
428
429 $chunkSize = 1048576;
430 // Download a large image file
431 // ( using RandomImageGenerator for large files is not stable )
432 $mimeType = 'image/jpeg';
433 $url = 'http://upload.wikimedia.org/wikipedia/commons/e/ed/Oberaargletscher_from_Oberaar%2C_2010_07.JPG';
434 $filePath = wfTempDir() . '/Oberaargletscher_from_Oberaar.jpg';
435 try {
436 // Only download if the file is not avaliable in the temp location:
437 if ( !is_file( $filePath ) ) {
438 copy( $url, $filePath );
439 }
440 } catch ( Exception $e ) {
441 $this->markTestIncomplete( $e->getMessage() );
442 }
443
444 $fileSize = filesize( $filePath );
445 $fileName = basename( $filePath );
446
447 $this->deleteFileByFileName( $fileName );
448 $this->deleteFileByContent( $filePath );
449
450 // Base upload params:
451 $params = array(
452 'action' => 'upload',
453 'stash' => 1,
454 'filename' => $fileName,
455 'filesize' => $fileSize,
456 'offset' => 0,
457 );
458
459 // Upload chunks
460 $chunkSessionKey = false;
461 $resultOffset = 0;
462 // Open the file:
463 $handle = @fopen( $filePath, "r" );
464 if ( $handle === false ) {
465 $this->markTestIncomplete( "could not open file: $filePath" );
466 }
467 while ( !feof( $handle ) ) {
468 // Get the current chunk
469 $chunkData = @fread( $handle, $chunkSize );
470
471 // Upload the current chunk into the $_FILE object:
472 $this->fakeUploadChunk( 'chunk', 'blob', $mimeType, $chunkData );
473
474 // Check for chunkSessionKey
475 if ( !$chunkSessionKey ) {
476 // Upload fist chunk ( and get the session key )
477 try {
478 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
479 self::$users['uploader']->user );
480 } catch ( UsageException $e ) {
481 $this->markTestIncomplete( $e->getMessage() );
482 }
483 // Make sure we got a valid chunk continue:
484 $this->assertTrue( isset( $result['upload'] ) );
485 $this->assertTrue( isset( $result['upload']['filekey'] ) );
486 // If we don't get a session key mark test incomplete.
487 if ( !isset( $result['upload']['filekey'] ) ) {
488 $this->markTestIncomplete( "no filekey provided" );
489 }
490 $chunkSessionKey = $result['upload']['filekey'];
491 $this->assertEquals( 'Continue', $result['upload']['result'] );
492 // First chunk should have chunkSize == offset
493 $this->assertEquals( $chunkSize, $result['upload']['offset'] );
494 $resultOffset = $result['upload']['offset'];
495 continue;
496 }
497 // Filekey set to chunk session
498 $params['filekey'] = $chunkSessionKey;
499 // Update the offset ( always add chunkSize for subquent chunks should be in-sync with $result['upload']['offset'] )
500 $params['offset'] += $chunkSize;
501 // Make sure param offset is insync with resultOffset:
502 $this->assertEquals( $resultOffset, $params['offset'] );
503 // Upload current chunk
504 try {
505 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
506 self::$users['uploader']->user );
507 } catch ( UsageException $e ) {
508 $this->markTestIncomplete( $e->getMessage() );
509 }
510 // Make sure we got a valid chunk continue:
511 $this->assertTrue( isset( $result['upload'] ) );
512 $this->assertTrue( isset( $result['upload']['filekey'] ) );
513
514 // Check if we were on the last chunk:
515 if ( $params['offset'] + $chunkSize >= $fileSize ) {
516 $this->assertEquals( 'Success', $result['upload']['result'] );
517 break;
518 } else {
519 $this->assertEquals( 'Continue', $result['upload']['result'] );
520 // update $resultOffset
521 $resultOffset = $result['upload']['offset'];
522 }
523 }
524 fclose( $handle );
525
526 // Check that we got a valid file result:
527 wfDebug( __METHOD__ . " hohoh filesize {$fileSize} info {$result['upload']['imageinfo']['size']}\n\n" );
528 $this->assertEquals( $fileSize, $result['upload']['imageinfo']['size'] );
529 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
530 $this->assertTrue( isset( $result['upload']['filekey'] ) );
531 $filekey = $result['upload']['filekey'];
532
533 // Now we should try to release the file from stash
534 $params = array(
535 'action' => 'upload',
536 'filekey' => $filekey,
537 'filename' => $fileName,
538 'comment' => 'dummy comment',
539 'text' => "This is the page text for $fileName, altered",
540 );
541 $this->clearFakeUploads();
542 $exception = false;
543 try {
544 list( $result ) = $this->doApiRequestWithToken( $params, $session,
545 self::$users['uploader']->user );
546 } catch ( UsageException $e ) {
547 $exception = true;
548 }
549 $this->assertTrue( isset( $result['upload'] ) );
550 $this->assertEquals( 'Success', $result['upload']['result'] );
551 $this->assertFalse( $exception );
552
553 // clean up
554 $this->deleteFileByFilename( $fileName );
555 // don't remove downloaded temporary file for fast subquent tests.
556 //unlink( $filePath );
557 }
558 }