* new FauxResponse class to help with unit testing
authorMark A. Hershberger <mah@users.mediawiki.org>
Wed, 10 Feb 2010 10:36:11 +0000 (10:36 +0000)
committerMark A. Hershberger <mah@users.mediawiki.org>
Wed, 10 Feb 2010 10:36:11 +0000 (10:36 +0000)
* 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
includes/WebRequest.php
includes/WebResponse.php
includes/api/ApiUpload.php
includes/filerepo/FSRepo.php
includes/filerepo/FileRepo.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/NullRepo.php
includes/upload/UploadFromChunks.php
maintenance/tests/UploadFromChunksTest.php

index 7cd9b58..8cf9a75 100644 (file)
@@ -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',
index 8b68434..6ae71ce 100644 (file)
@@ -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;
+       }
 }
index 09d3738..f7d57e4 100644 (file)
@@ -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
index 03d8b4d..de50147 100644 (file)
@@ -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',
index 9f70e85..3696e7e 100644 (file)
@@ -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
index fbcc77a..f30bb3a 100644 (file)
@@ -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().
         */
index 6f43719..da920e0 100644 (file)
@@ -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()
         */
index 030c336..f578498 100644 (file)
@@ -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;
        }
index d235710..8432d12 100644 (file)
@@ -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' );
        }
 
index 2b8a293..ebad774 100644 (file)
@@ -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();
+       }
 }