Index: trunk/phase3/tests/phpunit/includes/api/ApiTestCaseUpload.php |
— | — | @@ -104,6 +104,27 @@ |
105 | 105 | return true; |
106 | 106 | |
107 | 107 | } |
| 108 | + function fakeUploadChunk( $fieldName, $fileName, $type, & $chunkData ){ |
| 109 | + $tmpName = tempnam( wfTempDir(), "" ); |
| 110 | + // copy the chunk data to temp location: |
| 111 | + if ( !file_put_contents( $tmpName, $chunkData ) ) { |
| 112 | + throw new Exception( "couldn't copy chunk data to $tmpName" ); |
| 113 | + } |
| 114 | + |
| 115 | + clearstatcache(); |
| 116 | + $size = filesize( $tmpName ); |
| 117 | + if ( $size === false ) { |
| 118 | + throw new Exception( "couldn't stat $tmpName" ); |
| 119 | + } |
| 120 | + |
| 121 | + $_FILES[ $fieldName ] = array( |
| 122 | + 'name' => $fileName, |
| 123 | + 'type' => $type, |
| 124 | + 'tmp_name' => $tmpName, |
| 125 | + 'size' => $size, |
| 126 | + 'error' => null |
| 127 | + ); |
| 128 | + } |
108 | 129 | |
109 | 130 | function clearTempUpload() { |
110 | 131 | if( isset( $_FILES['file']['tmp_name'] ) ) { |
Index: trunk/phase3/tests/phpunit/includes/api/ApiUploadTest.php |
— | — | @@ -423,5 +423,143 @@ |
424 | 424 | $this->deleteFileByFilename( $fileName ); |
425 | 425 | unlink( $filePath ); |
426 | 426 | } |
| 427 | + |
| 428 | + |
| 429 | + /** |
| 430 | + * @depends testLogin |
| 431 | + */ |
| 432 | + public function testUploadChunks( $session ) { |
| 433 | + global $wgUser; |
| 434 | + $wgUser = self::$users['uploader']->user; // @todo FIXME: still used somewhere |
| 435 | + |
| 436 | + $chunkSize = 1048576; |
| 437 | + // Download a large image file |
| 438 | + // ( using RandomImageGenerator for large files is not stable ) |
| 439 | + $mimeType = 'image/jpeg'; |
| 440 | + $url = 'http://upload.wikimedia.org/wikipedia/commons/e/ed/Oberaargletscher_from_Oberaar%2C_2010_07.JPG'; |
| 441 | + $filePath = wfTempDir() . '/Oberaargletscher_from_Oberaar.jpg'; |
| 442 | + try { |
| 443 | + // Only download if the file is not avaliable in the temp location: |
| 444 | + if( !is_file( $filePath ) ){ |
| 445 | + copy($url, $filePath); |
| 446 | + } |
| 447 | + } |
| 448 | + catch ( Exception $e ) { |
| 449 | + $this->markTestIncomplete( $e->getMessage() ); |
| 450 | + } |
| 451 | + |
| 452 | + $fileSize = filesize( $filePath ); |
| 453 | + $fileName = basename( $filePath ); |
| 454 | + |
| 455 | + $this->deleteFileByFileName( $fileName ); |
| 456 | + $this->deleteFileByContent( $filePath ); |
| 457 | + |
| 458 | + // Base upload params: |
| 459 | + $params = array( |
| 460 | + 'action' => 'upload', |
| 461 | + 'stash' => 1, |
| 462 | + 'filename' => $fileName, |
| 463 | + 'filesize' => $fileSize, |
| 464 | + 'offset' => 0, |
| 465 | + ); |
| 466 | + |
| 467 | + // Upload chunks |
| 468 | + $chunkSessionKey = false; |
| 469 | + $resultOffset = 0; |
| 470 | + // Open the file: |
| 471 | + $handle = @fopen ($filePath, "r"); |
| 472 | + if( $handle === false ){ |
| 473 | + $this->markTestIncomplete( "could not open file: $filePath" ); |
| 474 | + } |
| 475 | + while (!feof ($handle)) { |
| 476 | + // Get the current chunk |
| 477 | + $chunkData = @fread( $handle, $chunkSize ); |
| 478 | + |
| 479 | + // Upload the current chunk into the $_FILE object: |
| 480 | + $this->fakeUploadChunk( 'chunk', 'blob', $mimeType, $chunkData ); |
| 481 | + |
| 482 | + // Check for chunkSessionKey |
| 483 | + if( !$chunkSessionKey ){ |
| 484 | + // Upload fist chunk ( and get the session key ) |
| 485 | + try { |
| 486 | + list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session, |
| 487 | + self::$users['uploader']->user ); |
| 488 | + } catch ( UsageException $e ) { |
| 489 | + $this->markTestIncomplete( $e->getMessage() ); |
| 490 | + } |
| 491 | + // Make sure we got a valid chunk continue: |
| 492 | + $this->assertTrue( isset( $result['upload'] ) ); |
| 493 | + $this->assertTrue( isset( $result['upload']['filekey'] ) ); |
| 494 | + // If we don't get a session key mark test incomplete. |
| 495 | + if( ! isset( $result['upload']['filekey'] ) ){ |
| 496 | + $this->markTestIncomplete( "no filekey provided" ); |
| 497 | + } |
| 498 | + $chunkSessionKey = $result['upload']['filekey']; |
| 499 | + $this->assertEquals( 'Continue', $result['upload']['result'] ); |
| 500 | + // First chunk should have chunkSize == offset |
| 501 | + $this->assertEquals( $chunkSize, $result['upload']['offset'] ); |
| 502 | + $resultOffset = $result['upload']['offset']; |
| 503 | + continue; |
| 504 | + } |
| 505 | + // Filekey set to chunk session |
| 506 | + $params['filekey'] = $chunkSessionKey; |
| 507 | + // Update the offset ( always add chunkSize for subquent chunks should be in-sync with $result['upload']['offset'] ) |
| 508 | + $params['offset'] += $chunkSize; |
| 509 | + // Make sure param offset is insync with resultOffset: |
| 510 | + $this->assertEquals( $resultOffset, $params['offset'] ); |
| 511 | + // Upload current chunk |
| 512 | + try { |
| 513 | + list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session, |
| 514 | + self::$users['uploader']->user ); |
| 515 | + } catch ( UsageException $e ) { |
| 516 | + $this->markTestIncomplete( $e->getMessage() ); |
| 517 | + } |
| 518 | + // Make sure we got a valid chunk continue: |
| 519 | + $this->assertTrue( isset( $result['upload'] ) ); |
| 520 | + $this->assertTrue( isset( $result['upload']['filekey'] ) ); |
| 521 | + |
| 522 | + // Check if we were on the last chunk: |
| 523 | + if( $params['offset'] + $chunkSize >= $fileSize ){ |
| 524 | + $this->assertEquals( 'Success', $result['upload']['result'] ); |
| 525 | + break; |
| 526 | + } else { |
| 527 | + $this->assertEquals( 'Continue', $result['upload']['result'] ); |
| 528 | + // update $resultOffset |
| 529 | + $resultOffset = $result['upload']['offset']; |
| 530 | + } |
| 531 | + } |
| 532 | + fclose ($handle); |
| 533 | + |
| 534 | + // Check that we got a valid file result: |
| 535 | + wfDebug( __METHOD__ . " hohoh filesize {$fileSize} info {$result['upload']['imageinfo']['size']}\n\n"); |
| 536 | + $this->assertEquals( $fileSize, $result['upload']['imageinfo']['size'] ); |
| 537 | + $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] ); |
| 538 | + $this->assertTrue( isset( $result['upload']['filekey'] ) ); |
| 539 | + $filekey = $result['upload']['filekey']; |
| 540 | + |
| 541 | + // Now we should try to release the file from stash |
| 542 | + $params = array( |
| 543 | + 'action' => 'upload', |
| 544 | + 'filekey' => $filekey, |
| 545 | + 'filename' => $fileName, |
| 546 | + 'comment' => 'dummy comment', |
| 547 | + 'text' => "This is the page text for $fileName, altered", |
| 548 | + ); |
| 549 | + $this->clearFakeUploads(); |
| 550 | + $exception = false; |
| 551 | + try { |
| 552 | + list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session, |
| 553 | + self::$users['uploader']->user ); |
| 554 | + } catch ( UsageException $e ) { |
| 555 | + $exception = true; |
| 556 | + } |
| 557 | + $this->assertTrue( isset( $result['upload'] ) ); |
| 558 | + $this->assertEquals( 'Success', $result['upload']['result'] ); |
| 559 | + $this->assertFalse( $exception ); |
| 560 | + |
| 561 | + // clean up |
| 562 | + $this->deleteFileByFilename( $fileName ); |
| 563 | + // don't remove downloaded temporary file for fast subquent tests. |
| 564 | + //unlink( $filePath ); |
| 565 | + } |
427 | 566 | } |
428 | | - |