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