From c74fe71cd8ead6f9daa96e5e5da9da1fc2f8c02a Mon Sep 17 00:00:00 2001 From: "Mark A. Hershberger" Date: Wed, 10 Feb 2010 10:36:11 +0000 Subject: [PATCH] * new FauxResponse class to help with unit testing * Add append() method to FileRepo classes to enable chunked uploading * Change chunksessionkey to chunksession * Remove echo json stuff * Fix a multitude of bugs in my own code * still to test: mwEmbed use of chunked upload --- includes/AutoLoader.php | 1 + includes/WebRequest.php | 13 +- includes/WebResponse.php | 30 ++++- includes/api/ApiUpload.php | 55 ++++----- includes/filerepo/FSRepo.php | 29 ++++- includes/filerepo/FileRepo.php | 44 +++---- includes/filerepo/ForeignAPIRepo.php | 35 +++--- includes/filerepo/NullRepo.php | 3 + includes/upload/UploadFromChunks.php | 50 ++++---- maintenance/tests/UploadFromChunksTest.php | 135 ++++++++++++++------- 10 files changed, 258 insertions(+), 137 deletions(-) diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 7cd9b5856f..8cf9a75890 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -84,6 +84,7 @@ $wgAutoloadLocalClasses = array( 'FakeTitle' => 'includes/FakeTitle.php', 'FakeMemCachedClient' => 'includes/ObjectCache.php', 'FauxRequest' => 'includes/WebRequest.php', + 'FauxResponse' => 'includes/WebResponse.php', 'FeedItem' => 'includes/Feed.php', 'FeedUtils' => 'includes/FeedUtils.php', 'FileDeleteForm' => 'includes/FileDeleteForm.php', diff --git a/includes/WebRequest.php b/includes/WebRequest.php index 8b68434fd6..6ae71ceb2d 100644 --- a/includes/WebRequest.php +++ b/includes/WebRequest.php @@ -712,6 +712,7 @@ class WebRequest { class FauxRequest extends WebRequest { private $wasPosted = false; private $session = array(); + private $response; /** * @param $data Array of *non*-urlencoded key => value pairs, the @@ -767,9 +768,8 @@ class FauxRequest extends WebRequest { } public function getSessionData( $key ) { - if( !isset( $this->session[$key] ) ) - return null; - return $this->session[$key]; + if( isset( $this->session[$key] ) ) + return $this->session[$key]; } public function setSessionData( $key, $data ) { @@ -780,4 +780,11 @@ class FauxRequest extends WebRequest { return false; } + public function response() { + /* Lazy initialization of response object for this request */ + if ( !is_object( $this->response ) ) { + $this->response = new FauxResponse; + } + return $this->response; + } } diff --git a/includes/WebResponse.php b/includes/WebResponse.php index 09d373850f..f7d57e41c1 100644 --- a/includes/WebResponse.php +++ b/includes/WebResponse.php @@ -6,7 +6,7 @@ */ class WebResponse { - /** + /** * Output a HTTP header, wrapper for PHP's * header() * @param $string String: header to output @@ -58,3 +58,31 @@ class WebResponse { } } } + + +class FauxResponse extends WebResponse { + private $headers; + private $cookies; + + public function header($string, $replace=true) { + list($key, $val) = explode(":", $string, 2); + + if($replace || !isset($this->headers[$key])) { + $this->headers[$key] = $val; + } + } + + public function getheader($key) { + return $this->headers[$key]; + } + + public function setcookie( $name, $value, $expire = 0 ) { + $this->cookies[$name] = $value; + } + + public function getcookie( $name ) { + if ( isset($this->cookies[$name]) ) { + return $this->cookies[$name]; + } + } +} \ No newline at end of file diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index 03d8b4d7f5..de50147b4e 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -40,6 +40,10 @@ class ApiUpload extends ApiBase { public function execute() { global $wgUser, $wgAllowCopyUploads; + // Check whether upload is enabled + if ( !UploadBase::isEnabled() ) + $this->dieUsageMsg( array( 'uploaddisabled' ) ); + $this->getMain()->isWriteMode(); $this->mParams = $this->extractRequestParams(); $request = $this->getMain()->getRequest(); @@ -53,15 +57,27 @@ class ApiUpload extends ApiBase { // Add the uploaded file to the params array $this->mParams['file'] = $request->getFileName( 'file' ); - // Check whether upload is enabled - if ( !UploadBase::isEnabled() ) - $this->dieUsageMsg( array( 'uploaddisabled' ) ); - // One and only one of the following parameters is needed $this->requireOnlyOneParameter( $this->mParams, 'sessionkey', 'file', 'url', 'enablechunks' ); - if ( $this->mParams['sessionkey'] ) { + // Initialize $this->mUpload + if ( $this->mParams['enablechunks'] ) { + $this->mUpload = new UploadFromChunks(); + + $this->mUpload->initialize( + $request->getVal( 'done', null ), + $request->getVal( 'filename', null ), + $request->getVal( 'chunksession', null ), + $request->getFileTempName( 'chunk' ), + $request->getFileSize( 'chunk' ), + $request->getSessionData( 'wsUploadData' ) + ); + + if ( !$this->mUpload->status->isOK() ) { + return $this->dieUsageMsg( $this->mUpload->status->getErrorsArray() ); + } + } elseif ( $this->mParams['sessionkey'] ) { /** * Upload stashed in a previous request */ @@ -72,30 +88,13 @@ class ApiUpload extends ApiBase { $this->mUpload = new UploadFromStash(); $this->mUpload->initialize( $this->mParams['filename'], $_SESSION['wsUploadData'][$this->mParams['sessionkey']] ); - } else { + } elseif ( isset( $this->mParams['filename'] ) ) { /** * Upload from url, etc * Parameter filename is required */ - if ( !isset( $this->mParams['filename'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); - // Initialize $this->mUpload - if ( $this->mParams['enablechunks'] ) { - $this->mUpload = new UploadFromChunks(); - $this->mUpload->initialize( - $request->getVal( 'done', null ), - $request->getVal( 'filename', null ), - $request->getVal( 'chunksessionkey', null ), - $request->getFileTempName( 'chunk' ), - $request->getFileSize( 'chunk' ), - $request->getSessionData( 'wsUploadData' ) - ); - - if ( !$this->mUpload->status->isOK() ) { - return $this->dieUsageMsg( $this->mUpload->status->getErrorsArray() ); - } - } elseif ( isset( $this->mParams['file'] ) ) { + if ( isset( $this->mParams['file'] ) ) { $this->mUpload = new UploadFromFile(); $this->mUpload->initialize( $this->mParams['filename'], @@ -120,7 +119,7 @@ class ApiUpload extends ApiBase { return $this->dieUsage( $status->getWikiText(), 'fetchfileerror' ); } } - } + } else $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); if ( !isset( $this->mUpload ) ) $this->dieUsage( 'No upload module set', 'nomodule' ); @@ -242,6 +241,8 @@ class ApiUpload extends ApiBase { $this->getResult()->setIndexedTagName( $result['details'], 'error' ); $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error ); + } elseif( isset($status->value->uploadUrl) ) { + return $status->value; } $file = $this->mUpload->getLocalFile(); @@ -272,7 +273,7 @@ class ApiUpload extends ApiBase { 'ignorewarnings' => false, 'file' => null, 'enablechunks' => false, - 'chunksessionkey' => null, + 'chunksession' => null, 'chunk' => null, 'done' => false, 'url' => null, @@ -295,7 +296,7 @@ class ApiUpload extends ApiBase { 'ignorewarnings' => 'Ignore any warnings', 'file' => 'File contents', 'enablechunks' => 'Set to use chunk mode; see http://firefogg.org/dev/chunk_post.html for protocol', - 'chunksessionkey' => 'The session key, established on the first contact during the chunked upload', + 'chunksession' => 'The session key, established on the first contact during the chunked upload', 'chunk' => 'The data in this chunk of a chunked upload', 'done' => 'Set to 1 on the last chunk of a chunked upload', 'url' => 'Url to fetch the file from', diff --git a/includes/filerepo/FSRepo.php b/includes/filerepo/FSRepo.php index 9f70e8581f..3696e7e909 100644 --- a/includes/filerepo/FSRepo.php +++ b/includes/filerepo/FSRepo.php @@ -227,6 +227,33 @@ class FSRepo extends FileRepo { return $status; } + function append( $srcPath, $toAppendPath ) { + $status = $this->newGood(); + + // Resolve the virtual URL + if ( self::isVirtualUrl( $srcPath ) ) { + $srcPath = $this->resolveVirtualUrl( $srcPath ); + } + // Make sure the files are there + if ( !is_file( $srcPath ) ) + $status->fatal( 'append-src-filenotfound', $srcPath ); + + if ( !is_file( $toAppendPath ) ) + $status->fatal( 'append-toappend-filenotfound', $toAppendPath ); + + // Do the append + if( file_put_contents( $srcPath, file_get_contents( $toAppendPath ), FILE_APPEND ) ) { + $status->value = $srcPath; + } else { + $status->fatal( 'fileappenderror', $toAppendPath, $srcPath); + } + + // Remove the source file + unlink( $toAppendPath ); + + return $status; + } + /** * Checks existence of specified array of files. * @@ -575,7 +602,7 @@ class FSRepo extends FileRepo { } return strtr( $param, $this->simpleCleanPairs ); } - + /** * Chmod a file, supressing the warnings. * @param String $path The path to change diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php index fbcc77ac89..f30bb3ab46 100644 --- a/includes/filerepo/FileRepo.php +++ b/includes/filerepo/FileRepo.php @@ -30,7 +30,7 @@ abstract class FileRepo { // Optional settings $this->initialCapital = MWNamespace::isCapitalized( NS_FILE ); foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription', - 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection', + 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection', 'descriptionCacheExpiry', 'hashLevels', 'url', 'thumbUrl' ) as $var ) { if ( isset( $info[$var] ) ) { @@ -87,7 +87,7 @@ abstract class FileRepo { * * ignoreRedirect: If true, do not follow file redirects * - * private: If true, return restricted (deleted) files if the current + * private: If true, return restricted (deleted) files if the current * user is allowed to view them. Otherwise, such files will not * be found. */ @@ -123,12 +123,12 @@ abstract class FileRepo { } } } - + # Now try redirects if ( !empty( $options['ignoreRedirect'] ) ) { return false; } - $redir = $this->checkRedirect( $title ); + $redir = $this->checkRedirect( $title ); if( $redir && $redir->getNamespace() == NS_FILE) { $img = $this->newFile( $redir ); if( !$img ) { @@ -141,9 +141,9 @@ abstract class FileRepo { } return false; } - + /* - * Find many files at once. + * Find many files at once. * @param array $items, an array of titles, or an array of findFile() options with * the "title" option giving the title. Example: * @@ -168,7 +168,7 @@ abstract class FileRepo { } return $result; } - + /** * Create a new File object from the local repository * @param mixed $sha1 SHA-1 key @@ -189,14 +189,14 @@ abstract class FileRepo { return call_user_func( $this->fileFactoryKey, $sha1, $this ); } } - + /** * Find an instance of the file with this key, created at the specified time * Returns false if the file does not exist. Repositories not supporting * version control should return false if the time is specified. * * @param string $sha1 string - * @param array $options Option array, same as findFile(). + * @param array $options Option array, same as findFile(). */ function findFileFromKey( $sha1, $options = array() ) { if ( !is_array( $options ) ) { @@ -234,7 +234,7 @@ abstract class FileRepo { function getThumbScriptUrl() { return $this->thumbScriptUrl; } - + /** * Get the URL corresponding to one of the four basic zones * @param String $zone One of: public, deleted, temp, thumb @@ -280,7 +280,7 @@ abstract class FileRepo { return $path; } } - + /** * Get a relative path including trailing slash, e.g. f/fa/ * If the repo is not hashed, returns an empty string @@ -397,6 +397,8 @@ abstract class FileRepo { */ abstract function storeTemp( $originalName, $srcPath ); + abstract function append( $srcPath, $toAppendPath ); + /** * Remove a temporary file or mark it for garbage collection * @param string $virtualUrl The virtual URL returned by storeTemp @@ -587,14 +589,14 @@ abstract class FileRepo { /** * Invalidates image redirect cache related to that image * Doesn't do anything for repositories that don't support image redirects. - * + * * STUB * @param Title $title Title of image - */ + */ function invalidateImageRedirect( $title ) {} - + /** - * Get an array or iterator of file objects for files that have a given + * Get an array or iterator of file objects for files that have a given * SHA-1 content hash. * * STUB @@ -602,9 +604,9 @@ abstract class FileRepo { function findBySha1( $hash ) { return array(); } - + /** - * Get the human-readable name of the repo. + * Get the human-readable name of the repo. * @return string */ public function getDisplayName() { @@ -616,12 +618,12 @@ abstract class FileRepo { if ( !wfEmptyMsg( 'shared-repo-name-' . $this->name, $repoName ) ) { return $repoName; } - return wfMsg( 'shared-repo' ); + return wfMsg( 'shared-repo' ); } - + /** * Get a key on the primary cache for this repository. - * Returns false if the repository's cache is not accessible at this site. + * Returns false if the repository's cache is not accessible at this site. * The parameters are the parts of the key, as for wfMemcKey(). * * STUB @@ -631,7 +633,7 @@ abstract class FileRepo { } /** - * Get a key for this repo in the local cache domain. These cache keys are + * Get a key for this repo in the local cache domain. These cache keys are * not shared with remote instances of the repo. * The parameters are the parts of the key, as for wfMemcKey(). */ diff --git a/includes/filerepo/ForeignAPIRepo.php b/includes/filerepo/ForeignAPIRepo.php index 6f43719c3c..da920e0836 100644 --- a/includes/filerepo/ForeignAPIRepo.php +++ b/includes/filerepo/ForeignAPIRepo.php @@ -22,7 +22,7 @@ class ForeignAPIRepo extends FileRepo { var $apiThumbCacheExpiry = 86400; protected $mQueryCache = array(); protected $mFileExists = array(); - + function __construct( $info ) { parent::__construct( $info ); $this->mApiBase = $info['apibase']; // http://commons.wikimedia.org/w/api.php @@ -42,7 +42,7 @@ class ForeignAPIRepo extends FileRepo { $this->thumbUrl = $this->url . '/thumb'; } } - + /** * Per docs in FileRepo, this needs to return false if we don't support versioned * files. Well, we don't. @@ -63,13 +63,16 @@ class ForeignAPIRepo extends FileRepo { function storeTemp( $originalName, $srcPath ) { return false; } + function append( $srcPath, $toAppendPath ){ + return false; + } function publishBatch( $triplets, $flags = 0 ) { return false; } function deleteBatch( $sourceDestPairs ) { return false; } - + function fileExistsBatch( $files, $flags = 0 ) { $results = array(); @@ -99,10 +102,10 @@ class ForeignAPIRepo extends FileRepo { function getFileProps( $virtualUrl ) { return false; } - + protected function queryImage( $query ) { $data = $this->fetchImageQuery( $query ); - + if( isset( $data['query']['pages'] ) ) { foreach( $data['query']['pages'] as $pageid => $info ) { if( isset( $info['imageinfo'][0] ) ) { @@ -112,10 +115,10 @@ class ForeignAPIRepo extends FileRepo { } return false; } - + protected function fetchImageQuery( $query ) { global $wgMemc; - + $url = $this->mApiBase . '?' . wfArrayToCgi( @@ -123,7 +126,7 @@ class ForeignAPIRepo extends FileRepo { array( 'format' => 'json', 'action' => 'query' ) ) ); - + if( !isset( $this->mQueryCache[$url] ) ) { $key = $this->getLocalCacheKey( 'ForeignAPIRepo', 'Metadata', md5( $url ) ); $data = $wgMemc->get( $key ); @@ -143,14 +146,14 @@ class ForeignAPIRepo extends FileRepo { } return FormatJson::decode( $this->mQueryCache[$url], true ); } - + function getImageInfo( $title, $time = false ) { return $this->queryImage( array( 'titles' => 'Image:' . $title->getText(), 'iiprop' => 'timestamp|user|comment|url|size|sha1|metadata|mime', 'prop' => 'imageinfo' ) ); } - + function findBySha1( $hash ) { $results = $this->fetchImageQuery( array( 'aisha1base36' => $hash, @@ -164,7 +167,7 @@ class ForeignAPIRepo extends FileRepo { } return $ret; } - + function getThumbUrl( $name, $width=-1, $height=-1 ) { $info = $this->queryImage( array( 'titles' => 'Image:' . $name, @@ -179,14 +182,14 @@ class ForeignAPIRepo extends FileRepo { return false; } } - + function getThumbUrlFromCache( $name, $width, $height ) { global $wgMemc, $wgUploadPath, $wgServer, $wgUploadDirectory; - + if ( !$this->canCacheThumbs() ) { return $this->getThumbUrl( $name, $width, $height ); } - + $key = $this->getLocalCacheKey( 'ForeignAPIRepo', 'ThumbUrl', $name ); if ( $thumbUrl = $wgMemc->get($key) ) { wfDebug("Got thumb from local cache. $thumbUrl \n"); @@ -194,7 +197,7 @@ class ForeignAPIRepo extends FileRepo { } else { $foreignUrl = $this->getThumbUrl( $name, $width, $height ); - + // We need the same filename as the remote one :) $fileName = rawurldecode( pathinfo( $foreignUrl, PATHINFO_BASENAME ) ); $path = 'thumb/' . $this->getHashPath( $name ) . $name . "/"; @@ -213,7 +216,7 @@ class ForeignAPIRepo extends FileRepo { return $localUrl; } } - + /** * @see FileRepo::getZoneUrl() */ diff --git a/includes/filerepo/NullRepo.php b/includes/filerepo/NullRepo.php index 030c3363cb..f5784986e6 100644 --- a/includes/filerepo/NullRepo.php +++ b/includes/filerepo/NullRepo.php @@ -14,6 +14,9 @@ class NullRepo extends FileRepo { function storeTemp( $originalName, $srcPath ) { return false; } + function append( $srcPath, $toAppendPath ){ + return false; + } function publishBatch( $triplets, $flags = 0 ) { return false; } diff --git a/includes/upload/UploadFromChunks.php b/includes/upload/UploadFromChunks.php index d23571050e..8432d1283a 100644 --- a/includes/upload/UploadFromChunks.php +++ b/includes/upload/UploadFromChunks.php @@ -22,7 +22,6 @@ class UploadFromChunks extends UploadBase { protected $chunkMode; // INIT, CHUNK, DONE protected $sessionKey; protected $comment; - protected $fileSize = 0; protected $repoPath; protected $pageText; protected $watch; @@ -37,9 +36,8 @@ class UploadFromChunks extends UploadBase { throw new MWException( 'not implemented' ); } - public function initialize( $done, $filename, $sessionKey, $path, - $fileSize, $sessionData ) - { + public function initialize( $done, $filename, $sessionKey, $path, $fileSize, $sessionData ) { + global $wgTmpDirectory; $this->status = new Status; $this->initFromSessionKey( $sessionKey, $sessionData ); @@ -47,7 +45,7 @@ class UploadFromChunks extends UploadBase { if ( !$this->sessionKey && !$done ) { // session key not set, init the chunk upload system: $this->chunkMode = self::INIT; - $this->mDesiredDestName = $filename; + $this->initializePathInfo( $filename, $path, 0, true); } else if ( $this->sessionKey && !$done ) { $this->chunkMode = self::CHUNK; } else if ( $this->sessionKey && $done ) { @@ -55,7 +53,7 @@ class UploadFromChunks extends UploadBase { } if ( $this->chunkMode == self::CHUNK || $this->chunkMode == self::DONE ) { $this->mTempPath = $path; - $this->fileSize += $fileSize; + $this->mFileSize += $fileSize; } } @@ -128,24 +126,25 @@ class UploadFromChunks extends UploadBase { // a) the user must have requested the token to get here and // b) should only happen over POST // c) we need the token to validate chunks are coming from a non-xss request - $token = urlencode( $wgUser->editToken() ); - echo FormatJson::encode( array( - 'uploadUrl' => wfExpandUrl( wfScript( 'api' ) ) . "?action=upload&" . - "token={$token}&format=json&enablechunks=true&chunksessionkey=" . - $this->setupChunkSession( $comment, $pageText, $watch ) ) ); - $wgOut->disable(); + return Status::newGood( + array('uploadUrl' => wfExpandUrl( wfScript( 'api' ) ) . "?" . + wfArrayToCGI(array('action' => 'upload', + 'token' => $wgUser->editToken(), + 'format' => 'json', + 'filename' => $pageText, + 'enablechunks' => 'true', + 'chunksession' => $this->setupChunkSession( $comment, $pageText, $watch ) ) ) ) ); } else if ( $this->chunkMode == self::CHUNK ) { - $status = $this->appendChunk(); - if ( !$status->isOK() ) { - return $status; + $this->appendChunk(); + if ( !$this->status->isOK() ) { + return $this->status; } // return success: // firefogg expects a specific result // http://www.firefogg.org/dev/chunk_post.html - echo FormatJson::encode( - array( 'result' => 1, 'filesize' => $this->fileSize ) + return Status::newGood( + array( 'result' => 1, 'filesize' => $this->mFileSize ) ); - $wgOut->disable(); } else if ( $this->chunkMode == self::DONE ) { if ( !$comment ) $comment = $this->comment; @@ -164,12 +163,9 @@ class UploadFromChunks extends UploadBase { // firefogg expects a specific result // http://www.firefogg.org/dev/chunk_post.html - echo FormatJson::encode( array( - 'result' => 1, - 'done' => 1, - 'resultUrl' => $file->getDescriptionUrl() ) + return Status::newGood( + array('result' => 1, 'done' => 1, 'resultUrl' => $file->getDescriptionUrl() ) ); - $wgOut->disable(); } return Status::newGood(); @@ -199,18 +195,18 @@ class UploadFromChunks extends UploadBase { if ( !$this->repoPath ) { $this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath ); - if ( $status->isOK() ) { - $this->repoPath = $status->value; + if ( $this->status->isOK() ) { + $this->repoPath = $this->status->value; $_SESSION['wsUploadData'][$this->sessionKey]['repoPath'] = $this->repoPath; } - return $status; + return; } if ( $this->getRealPath( $this->repoPath ) ) { $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath ); } else { $this->status = Status::newFatal( 'filenotfound', $this->repoPath ); } - if ( $this->fileSize > $wgMaxUploadSize ) + if ( $this->mFileSize > $wgMaxUploadSize ) $this->status = Status::newFatal( 'largefileserver' ); } diff --git a/maintenance/tests/UploadFromChunksTest.php b/maintenance/tests/UploadFromChunksTest.php index 2b8a293b02..ebad77404b 100644 --- a/maintenance/tests/UploadFromChunksTest.php +++ b/maintenance/tests/UploadFromChunksTest.php @@ -9,6 +9,38 @@ class UploadFromChunksTest extends ApiSetup { $wgEnableUploads=true; ini_set('file_loads', true); + parent::setup(); + + } + + function makeChunk() { + $file = tempnam( wfTempDir(), "" ); + $fh = fopen($file, "w"); + if($fh == false) { + $this->markTestIncomplete("Couldn't open $file!\n"); + return; + } + fwrite($fh, "123"); + fclose($fh); + + $_FILES['chunk']['tmp_name'] = $file; + $_FILES['chunk']['size'] = 3; + $_FILES['chunk']['error'] = null; + $_FILES['chunk']['name'] = "test.txt"; + } + + function cleanChunk() { + if(file_exists($_FILES['chunk']['tmp_name'])) + unlink($_FILES['chunk']['tmp_name']); + } + + function doApiRequest($params) { + $session = isset( $_SESSION ) ? $_SESSION : array(); + $req = new FauxRequest($params, true, $session); + $module = new ApiMain($req, true); + $module->execute(); + + return $module->getResultData(); } function testGetTitle() { @@ -22,89 +54,110 @@ class UploadFromChunksTest extends ApiSetup { $this->assertEquals(Title::makeTitleSafe(NS_FILE, "Temp.png"), $c->getTitle()); } - function testGetEditToken() { + function testLogin() { + $data = $this->doApiRequest(array('action' => 'login', + "lgname" => self::$userName, + "lgpassword" => self::$passWord ) ); + $this->assertArrayHasKey("login", $data); + $this->assertArrayHasKey("result", $data['login']); + $this->assertEquals("Success", $data['login']['result']); + + return $data; } - function testInitFromSessionKey() { - } + /** + * @depends testLogin + */ + function testGetEditToken($data) { + global $wgUser; + $wgUser = User::newFromName(self::$userName); + $wgUser->load(); - function testInitialize() { + $data = $this->doApiRequest(array('action' => 'query', + 'prop' => 'info', + 'intoken' => 'edit')); } function testSetupChunkSession() { } - function makeChunk() { - $file = tempnam( wfTempDir(), "" ); - $fh = fopen($file, "w"); - if($fh == false) { - $this->markTestIncomplete("Couldn't open $file!\n"); - return; - } - fwrite($fh, "123"); - fclose($fh); - - $_FILES['chunk']['tmp_name'] = $file; - $_FILES['chunk']['size'] = 3; - $_FILES['chunk']['error'] = null; - $_FILES['chunk']['name'] = "test.txt"; - } - - function cleanChunk() { - unlink($_FILES['chunk']['tmp_name']); - } - /** * @expectedException UsageException */ function testPerformUploadInitError() { global $wgUser; - $wgUser = User::newFromId(1); - $token = $wgUser->editToken(); - $this->makeChunk(); $req = new FauxRequest( array('action' => 'upload', 'enablechunks' => '1', 'filename' => 'test.png', - 'token' => $token, )); $module = new ApiMain($req, true); $module->execute(); } - function testPerformUploadInitSuccess() { + /** + * @depends testLogin + */ + function testPerformUploadInitSuccess($login) { global $wgUser; - $wgUser = User::newFromId(1); + $wgUser = User::newFromName(self::$userName); $token = $wgUser->editToken(); - $this->makeChunk(); - $req = new FauxRequest( + $data = $this->doApiRequest( array('action' => 'upload', 'enablechunks' => '1', 'filename' => 'test.png', 'token' => $token, )); - $module = new ApiMain($req, true); - $module->execute(); - } - function testAppendToUploadFile() { - } + $this->assertArrayHasKey("upload", $data); + $this->assertArrayHasKey("uploadUrl", $data['upload']); - function testAppendChunk() { + return array('data' => $data, 'session' => $_SESSION, 'token' => $token); } - function testPeformUploadChunk() { - } + /** + * @depends testPerformUploadInitSuccess + */ + function testAppendChunk($combo) { + global $wgUser; + $data = $combo['data']; + $_SESSION = $combo['session']; + $wgUser = User::newFromName(self::$userName); + $token = $wgUser->editToken(); + + $url = $data['upload']['uploadUrl']; + $params = wfCgiToArray(substr($url, strpos($url, "?"))); + + for($i=0;$i<10;$i++) { + $this->makeChunk(); + $data = $this->doApiRequest($params); + $this->cleanChunk(); + } - function testPeformUploadDone() { + return array('data' => $data, 'session' => $_SESSION, 'token' => $token, 'params' => $params); } + /** + * @depends testAppendChunk + */ + function testUploadChunkDone($combo) { + global $wgUser; + $data = $combo['data']; + $params = $combo['params']; + $_SESSION = $combo['session']; + $wgUser = User::newFromName(self::$userName); + $token = $wgUser->editToken(); + $params['done'] = 1; + $this->makeChunk(); + $data = $this->doApiRequest($params); + $this->cleanChunk(); + } } -- 2.20.1