MediaWiki  REL1_19
FileBackendTest.php
Go to the documentation of this file.
00001 <?php
00002 
00007 class FileBackendTest extends MediaWikiTestCase {
00008         private $backend, $multiBackend;
00009         private $filesToPrune = array();
00010         private $dirsToPrune = array();
00011         private static $backendToUse;
00012 
00013         function setUp() {
00014                 global $wgFileBackends;
00015                 parent::setUp();
00016                 $tmpPrefix = wfTempDir() . '/filebackend-unittest-' . time() . '-' . mt_rand();
00017                 if ( $this->getCliArg( 'use-filebackend=' ) ) {
00018                         if ( self::$backendToUse ) {
00019                                 $this->singleBackend = self::$backendToUse;
00020                         } else {
00021                                 $name = $this->getCliArg( 'use-filebackend=' );
00022                                 $useConfig = array();
00023                                 foreach ( $wgFileBackends as $conf ) {
00024                                         if ( $conf['name'] == $name ) {
00025                                                 $useConfig = $conf;
00026                                         }
00027                                 }
00028                                 $useConfig['name'] = 'localtesting'; // swap name
00029                                 $class = $conf['class'];
00030                                 self::$backendToUse = new $class( $useConfig );
00031                                 $this->singleBackend = self::$backendToUse;
00032                         }
00033                 } else {
00034                         $this->singleBackend = new FSFileBackend( array(
00035                                 'name'        => 'localtesting',
00036                                 'lockManager' => 'fsLockManager',
00037                                 'containerPaths' => array(
00038                                         'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1",
00039                                         'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" )
00040                         ) );
00041                 }
00042                 $this->multiBackend = new FileBackendMultiWrite( array(
00043                         'name'        => 'localtesting',
00044                         'lockManager' => 'fsLockManager',
00045                         'backends'    => array(
00046                                 array(
00047                                         'name'          => 'localmutlitesting1',
00048                                         'class'         => 'FSFileBackend',
00049                                         'lockManager'   => 'nullLockManager',
00050                                         'containerPaths' => array(
00051                                                 'unittest-cont1' => "{$tmpPrefix}-localtestingmulti1-cont1",
00052                                                 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti1-cont2" ),
00053                                         'isMultiMaster' => false
00054                                 ),
00055                                 array(
00056                                         'name'          => 'localmutlitesting2',
00057                                         'class'         => 'FSFileBackend',
00058                                         'lockManager'   => 'nullLockManager',
00059                                         'containerPaths' => array(
00060                                                 'unittest-cont1' => "{$tmpPrefix}-localtestingmulti2-cont1",
00061                                                 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti2-cont2" ),
00062                                         'isMultiMaster' => true
00063                                 )
00064                         )
00065                 ) );
00066                 $this->filesToPrune = array();
00067         }
00068 
00069         private function baseStorePath() {
00070                 return 'mwstore://localtesting';
00071         }
00072 
00073         private function backendClass() {
00074                 return get_class( $this->backend );
00075         }
00076 
00080         public function testIsStoragePath( $path, $isStorePath ) {
00081                 $this->assertEquals( $isStorePath, FileBackend::isStoragePath( $path ),
00082                         "FileBackend::isStoragePath on path '$path'" );
00083         }
00084 
00085         function provider_testIsStoragePath() {
00086                 return array(
00087                         array( 'mwstore://', true ),
00088                         array( 'mwstore://backend', true ),
00089                         array( 'mwstore://backend/container', true ),
00090                         array( 'mwstore://backend/container/', true ),
00091                         array( 'mwstore://backend/container/path', true ),
00092                         array( 'mwstore://backend//container/', true ),
00093                         array( 'mwstore://backend//container//', true ),
00094                         array( 'mwstore://backend//container//path', true ),
00095                         array( 'mwstore:///', true ),
00096                         array( 'mwstore:/', false ),
00097                         array( 'mwstore:', false ),
00098                 );
00099         }
00100 
00104         public function testSplitStoragePath( $path, $res ) {
00105                 $this->assertEquals( $res, FileBackend::splitStoragePath( $path ),
00106                         "FileBackend::splitStoragePath on path '$path'" );
00107         }
00108 
00109         function provider_testSplitStoragePath() {
00110                 return array(
00111                         array( 'mwstore://backend/container', array( 'backend', 'container', '' ) ),
00112                         array( 'mwstore://backend/container/', array( 'backend', 'container', '' ) ),
00113                         array( 'mwstore://backend/container/path', array( 'backend', 'container', 'path' ) ),
00114                         array( 'mwstore://backend/container//path', array( 'backend', 'container', '/path' ) ),
00115                         array( 'mwstore://backend//container/path', array( null, null, null ) ),
00116                         array( 'mwstore://backend//container//path', array( null, null, null ) ),
00117                         array( 'mwstore://', array( null, null, null ) ),
00118                         array( 'mwstore://backend', array( null, null, null ) ),
00119                         array( 'mwstore:///', array( null, null, null ) ),
00120                         array( 'mwstore:/', array( null, null, null ) ),
00121                         array( 'mwstore:', array( null, null, null ) )
00122                 );
00123         }
00124 
00128         public function testNormalizeStoragePath( $path, $res ) {
00129                 $this->assertEquals( $res, FileBackend::normalizeStoragePath( $path ),
00130                         "FileBackend::normalizeStoragePath on path '$path'" );
00131         }
00132 
00133         function provider_normalizeStoragePath() {
00134                 return array(
00135                         array( 'mwstore://backend/container', 'mwstore://backend/container' ),
00136                         array( 'mwstore://backend/container/', 'mwstore://backend/container' ),
00137                         array( 'mwstore://backend/container/path', 'mwstore://backend/container/path' ),
00138                         array( 'mwstore://backend/container//path', 'mwstore://backend/container/path' ),
00139                         array( 'mwstore://backend/container///path', 'mwstore://backend/container/path' ),
00140                         array( 'mwstore://backend/container///path//to///obj', 'mwstore://backend/container/path/to/obj',
00141                         array( 'mwstore://', null ),
00142                         array( 'mwstore://backend', null ),
00143                         array( 'mwstore://backend//container/path', null ),
00144                         array( 'mwstore://backend//container//path', null ),
00145                         array( 'mwstore:///', null ),
00146                         array( 'mwstore:/', null ),
00147                         array( 'mwstore:', null ), )
00148                 );
00149         }
00150 
00154         public function testParentStoragePath( $path, $res ) {
00155                 $this->assertEquals( $res, FileBackend::parentStoragePath( $path ),
00156                         "FileBackend::parentStoragePath on path '$path'" );
00157         }
00158 
00159         function provider_testParentStoragePath() {
00160                 return array(
00161                         array( 'mwstore://backend/container/path/to/obj', 'mwstore://backend/container/path/to' ),
00162                         array( 'mwstore://backend/container/path/to', 'mwstore://backend/container/path' ),
00163                         array( 'mwstore://backend/container/path', 'mwstore://backend/container' ),
00164                         array( 'mwstore://backend/container', null ),
00165                         array( 'mwstore://backend/container/path/to/obj/', 'mwstore://backend/container/path/to' ),
00166                         array( 'mwstore://backend/container/path/to/', 'mwstore://backend/container/path' ),
00167                         array( 'mwstore://backend/container/path/', 'mwstore://backend/container' ),
00168                         array( 'mwstore://backend/container/', null ),
00169                 );
00170         }
00171 
00175         public function testExtensionFromPath( $path, $res ) {
00176                 $this->assertEquals( $res, FileBackend::extensionFromPath( $path ),
00177                         "FileBackend::extensionFromPath on path '$path'" );
00178         }
00179 
00180         function provider_testExtensionFromPath() {
00181                 return array(
00182                         array( 'mwstore://backend/container/path.txt', 'txt' ),
00183                         array( 'mwstore://backend/container/path.svg.png', 'png' ),
00184                         array( 'mwstore://backend/container/path', '' ),
00185                         array( 'mwstore://backend/container/path.', '' ),
00186                 );
00187         }
00188 
00192         public function testStore( $op ) {
00193                 $this->filesToPrune[] = $op['src'];
00194 
00195                 $this->backend = $this->singleBackend;
00196                 $this->tearDownFiles();
00197                 $this->doTestStore( $op );
00198                 $this->tearDownFiles();
00199 
00200                 $this->backend = $this->multiBackend;
00201                 $this->tearDownFiles();
00202                 $this->doTestStore( $op );
00203                 $this->filesToPrune[] = $op['src']; # avoid file leaking
00204                 $this->tearDownFiles();
00205         }
00206 
00207         function doTestStore( $op ) {
00208                 $backendName = $this->backendClass();
00209 
00210                 $source = $op['src'];
00211                 $dest = $op['dst'];
00212                 $this->prepare( array( 'dir' => dirname( $dest ) ) );
00213 
00214                 file_put_contents( $source, "Unit test file" );
00215 
00216                 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
00217                         $this->backend->store( $op );
00218                 }
00219 
00220                 $status = $this->backend->doOperation( $op );
00221 
00222                 $this->assertEquals( array(), $status->errors,
00223                         "Store from $source to $dest succeeded without warnings ($backendName)." );
00224                 $this->assertEquals( array(), $status->errors,
00225                         "Store from $source to $dest succeeded ($backendName)." );
00226                 $this->assertEquals( array( 0 => true ), $status->success,
00227                         "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
00228                 $this->assertEquals( true, file_exists( $source ),
00229                         "Source file $source still exists ($backendName)." );
00230                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00231                         "Destination file $dest exists ($backendName)." );
00232 
00233                 $this->assertEquals( filesize( $source ),
00234                         $this->backend->getFileSize( array( 'src' => $dest ) ),
00235                         "Destination file $dest has correct size ($backendName)." );
00236 
00237                 $props1 = FSFile::getPropsFromPath( $source );
00238                 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
00239                 $this->assertEquals( $props1, $props2,
00240                         "Source and destination have the same props ($backendName)." );
00241         }
00242 
00243         public function provider_testStore() {
00244                 $cases = array();
00245 
00246                 $tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath();
00247                 $toPath = $this->baseStorePath() . '/unittest-cont1/fun/obj1.txt';
00248                 $op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath );
00249                 $cases[] = array(
00250                         $op, // operation
00251                         $tmpName, // source
00252                         $toPath, // dest
00253                 );
00254 
00255                 $op2 = $op;
00256                 $op2['overwrite'] = true;
00257                 $cases[] = array(
00258                         $op2, // operation
00259                         $tmpName, // source
00260                         $toPath, // dest
00261                 );
00262 
00263                 $op2 = $op;
00264                 $op2['overwriteSame'] = true;
00265                 $cases[] = array(
00266                         $op2, // operation
00267                         $tmpName, // source
00268                         $toPath, // dest
00269                 );
00270 
00271                 return $cases;
00272         }
00273 
00277         public function testCopy( $op ) {
00278                 $this->backend = $this->singleBackend;
00279                 $this->tearDownFiles();
00280                 $this->doTestCopy( $op );
00281                 $this->tearDownFiles();
00282 
00283                 $this->backend = $this->multiBackend;
00284                 $this->tearDownFiles();
00285                 $this->doTestCopy( $op );
00286                 $this->tearDownFiles();
00287         }
00288 
00289         function doTestCopy( $op ) {
00290                 $backendName = $this->backendClass();
00291 
00292                 $source = $op['src'];
00293                 $dest = $op['dst'];
00294                 $this->prepare( array( 'dir' => dirname( $source ) ) );
00295                 $this->prepare( array( 'dir' => dirname( $dest ) ) );
00296 
00297                 $status = $this->backend->doOperation(
00298                         array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
00299                 $this->assertEquals( array(), $status->errors,
00300                         "Creation of file at $source succeeded ($backendName)." );
00301 
00302                 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
00303                         $this->backend->copy( $op );
00304                 }
00305 
00306                 $status = $this->backend->doOperation( $op );
00307 
00308                 $this->assertEquals( array(), $status->errors,
00309                         "Copy from $source to $dest succeeded without warnings ($backendName)." );
00310                 $this->assertEquals( true, $status->isOK(),
00311                         "Copy from $source to $dest succeeded ($backendName)." );
00312                 $this->assertEquals( array( 0 => true ), $status->success,
00313                         "Copy from $source to $dest has proper 'success' field in Status ($backendName)." );
00314                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $source ) ),
00315                         "Source file $source still exists ($backendName)." );
00316                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00317                         "Destination file $dest exists after copy ($backendName)." );
00318 
00319                 $this->assertEquals(
00320                         $this->backend->getFileSize( array( 'src' => $source ) ),
00321                         $this->backend->getFileSize( array( 'src' => $dest ) ),
00322                         "Destination file $dest has correct size ($backendName)." );
00323 
00324                 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
00325                 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
00326                 $this->assertEquals( $props1, $props2,
00327                         "Source and destination have the same props ($backendName)." );
00328         }
00329 
00330         public function provider_testCopy() {
00331                 $cases = array();
00332 
00333                 $source = $this->baseStorePath() . '/unittest-cont1/file.txt';
00334                 $dest = $this->baseStorePath() . '/unittest-cont2/fileMoved.txt';
00335 
00336                 $op = array( 'op' => 'copy', 'src' => $source, 'dst' => $dest );
00337                 $cases[] = array(
00338                         $op, // operation
00339                         $source, // source
00340                         $dest, // dest
00341                 );
00342 
00343                 $op2 = $op;
00344                 $op2['overwrite'] = true;
00345                 $cases[] = array(
00346                         $op2, // operation
00347                         $source, // source
00348                         $dest, // dest
00349                 );
00350 
00351                 $op2 = $op;
00352                 $op2['overwriteSame'] = true;
00353                 $cases[] = array(
00354                         $op2, // operation
00355                         $source, // source
00356                         $dest, // dest
00357                 );
00358 
00359                 return $cases;
00360         }
00361 
00365         public function testMove( $op ) {
00366                 $this->backend = $this->singleBackend;
00367                 $this->tearDownFiles();
00368                 $this->doTestMove( $op );
00369                 $this->tearDownFiles();
00370 
00371                 $this->backend = $this->multiBackend;
00372                 $this->tearDownFiles();
00373                 $this->doTestMove( $op );
00374                 $this->tearDownFiles();
00375         }
00376 
00377         private function doTestMove( $op ) {
00378                 $backendName = $this->backendClass();
00379 
00380                 $source = $op['src'];
00381                 $dest = $op['dst'];
00382                 $this->prepare( array( 'dir' => dirname( $source ) ) );
00383                 $this->prepare( array( 'dir' => dirname( $dest ) ) );
00384 
00385                 $status = $this->backend->doOperation(
00386                         array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
00387                 $this->assertEquals( array(), $status->errors,
00388                         "Creation of file at $source succeeded ($backendName)." );
00389 
00390                 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
00391                         $this->backend->copy( $op );
00392                 }
00393 
00394                 $status = $this->backend->doOperation( $op );
00395                 $this->assertEquals( array(), $status->errors,
00396                         "Move from $source to $dest succeeded without warnings ($backendName)." );
00397                 $this->assertEquals( true, $status->isOK(),
00398                         "Move from $source to $dest succeeded ($backendName)." );
00399                 $this->assertEquals( array( 0 => true ), $status->success,
00400                         "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
00401                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
00402                         "Source file $source does not still exists ($backendName)." );
00403                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00404                         "Destination file $dest exists after move ($backendName)." );
00405 
00406                 $this->assertNotEquals(
00407                         $this->backend->getFileSize( array( 'src' => $source ) ),
00408                         $this->backend->getFileSize( array( 'src' => $dest ) ),
00409                         "Destination file $dest has correct size ($backendName)." );
00410 
00411                 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
00412                 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
00413                 $this->assertEquals( false, $props1['fileExists'],
00414                         "Source file does not exist accourding to props ($backendName)." );
00415                 $this->assertEquals( true, $props2['fileExists'],
00416                         "Destination file exists accourding to props ($backendName)." );
00417         }
00418 
00419         public function provider_testMove() {
00420                 $cases = array();
00421 
00422                 $source = $this->baseStorePath() . '/unittest-cont1/file.txt';
00423                 $dest = $this->baseStorePath() . '/unittest-cont2/fileMoved.txt';
00424 
00425                 $op = array( 'op' => 'move', 'src' => $source, 'dst' => $dest );
00426                 $cases[] = array(
00427                         $op, // operation
00428                         $source, // source
00429                         $dest, // dest
00430                 );
00431 
00432                 $op2 = $op;
00433                 $op2['overwrite'] = true;
00434                 $cases[] = array(
00435                         $op2, // operation
00436                         $source, // source
00437                         $dest, // dest
00438                 );
00439 
00440                 $op2 = $op;
00441                 $op2['overwriteSame'] = true;
00442                 $cases[] = array(
00443                         $op2, // operation
00444                         $source, // source
00445                         $dest, // dest
00446                 );
00447 
00448                 return $cases;
00449         }
00450 
00454         public function testDelete( $op, $withSource, $okStatus ) {
00455                 $this->backend = $this->singleBackend;
00456                 $this->tearDownFiles();
00457                 $this->doTestDelete( $op, $withSource, $okStatus );
00458                 $this->tearDownFiles();
00459 
00460                 $this->backend = $this->multiBackend;
00461                 $this->tearDownFiles();
00462                 $this->doTestDelete( $op, $withSource, $okStatus );
00463                 $this->tearDownFiles();
00464         }
00465 
00466         private function doTestDelete( $op, $withSource, $okStatus ) {
00467                 $backendName = $this->backendClass();
00468 
00469                 $source = $op['src'];
00470                 $this->prepare( array( 'dir' => dirname( $source ) ) );
00471 
00472                 if ( $withSource ) {
00473                         $status = $this->backend->doOperation(
00474                                 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
00475                         $this->assertEquals( array(), $status->errors,
00476                                 "Creation of file at $source succeeded ($backendName)." );
00477                 }
00478 
00479                 $status = $this->backend->doOperation( $op );
00480                 if ( $okStatus ) {
00481                         $this->assertEquals( array(), $status->errors,
00482                                 "Deletion of file at $source succeeded without warnings ($backendName)." );
00483                         $this->assertEquals( true, $status->isOK(),
00484                                 "Deletion of file at $source succeeded ($backendName)." );
00485                         $this->assertEquals( array( 0 => true ), $status->success,
00486                                 "Deletion of file at $source has proper 'success' field in Status ($backendName)." );
00487                 } else {
00488                         $this->assertEquals( false, $status->isOK(),
00489                                 "Deletion of file at $source failed ($backendName)." );
00490                 }
00491 
00492                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
00493                         "Source file $source does not exist after move ($backendName)." );
00494 
00495                 $this->assertFalse(
00496                         $this->backend->getFileSize( array( 'src' => $source ) ),
00497                         "Source file $source has correct size (false) ($backendName)." );
00498 
00499                 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
00500                 $this->assertFalse( $props1['fileExists'],
00501                         "Source file $source does not exist according to props ($backendName)." );
00502         }
00503 
00504         public function provider_testDelete() {
00505                 $cases = array();
00506 
00507                 $source = $this->baseStorePath() . '/unittest-cont1/myfacefile.txt';
00508 
00509                 $op = array( 'op' => 'delete', 'src' => $source );
00510                 $cases[] = array(
00511                         $op, // operation
00512                         true, // with source
00513                         true // succeeds
00514                 );
00515 
00516                 $cases[] = array(
00517                         $op, // operation
00518                         false, // without source
00519                         false // fails
00520                 );
00521 
00522                 $op['ignoreMissingSource'] = true;
00523                 $cases[] = array(
00524                         $op, // operation
00525                         false, // without source
00526                         true // succeeds
00527                 );
00528 
00529                 return $cases;
00530         }
00531 
00535         public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
00536                 $this->backend = $this->singleBackend;
00537                 $this->tearDownFiles();
00538                 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
00539                 $this->tearDownFiles();
00540 
00541                 $this->backend = $this->multiBackend;
00542                 $this->tearDownFiles();
00543                 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
00544                 $this->tearDownFiles();
00545         }
00546 
00547         private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
00548                 $backendName = $this->backendClass();
00549 
00550                 $dest = $op['dst'];
00551                 $this->prepare( array( 'dir' => dirname( $dest ) ) );
00552 
00553                 $oldText = 'blah...blah...waahwaah';
00554                 if ( $alreadyExists ) {
00555                         $status = $this->backend->doOperation(
00556                                 array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
00557                         $this->assertEquals( array(), $status->errors,
00558                                 "Creation of file at $dest succeeded ($backendName)." );
00559                 }
00560 
00561                 $status = $this->backend->doOperation( $op );
00562                 if ( $okStatus ) {
00563                         $this->assertEquals( array(), $status->errors,
00564                                 "Creation of file at $dest succeeded without warnings ($backendName)." );
00565                         $this->assertEquals( true, $status->isOK(),
00566                                 "Creation of file at $dest succeeded ($backendName)." );
00567                         $this->assertEquals( array( 0 => true ), $status->success,
00568                                 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
00569                 } else {
00570                         $this->assertEquals( false, $status->isOK(),
00571                                 "Creation of file at $dest failed ($backendName)." );
00572                 }
00573 
00574                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00575                         "Destination file $dest exists after creation ($backendName)." );
00576 
00577                 $props1 = $this->backend->getFileProps( array( 'src' => $dest ) );
00578                 $this->assertEquals( true, $props1['fileExists'],
00579                         "Destination file $dest exists according to props ($backendName)." );
00580                 if ( $okStatus ) { // file content is what we saved
00581                         $this->assertEquals( $newSize, $props1['size'],
00582                                 "Destination file $dest has expected size according to props ($backendName)." );
00583                         $this->assertEquals( $newSize,
00584                                 $this->backend->getFileSize( array( 'src' => $dest ) ),
00585                                 "Destination file $dest has correct size ($backendName)." );
00586                 } else { // file content is some other previous text
00587                         $this->assertEquals( strlen( $oldText ), $props1['size'],
00588                                 "Destination file $dest has original size according to props ($backendName)." );
00589                         $this->assertEquals( strlen( $oldText ),
00590                                 $this->backend->getFileSize( array( 'src' => $dest ) ),
00591                                 "Destination file $dest has original size according to props ($backendName)." );
00592                 }
00593         }
00594 
00598         public function provider_testCreate() {
00599                 $cases = array();
00600 
00601                 $dest = $this->baseStorePath() . '/unittest-cont2/myspacefile.txt';
00602 
00603                 $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
00604                 $cases[] = array(
00605                         $op, // operation
00606                         false, // no dest already exists
00607                         true, // succeeds
00608                         strlen( $op['content'] )
00609                 );
00610 
00611                 $op2 = $op;
00612                 $op2['content'] = "\n";
00613                 $cases[] = array(
00614                         $op2, // operation
00615                         false, // no dest already exists
00616                         true, // succeeds
00617                         strlen( $op2['content'] )
00618                 );
00619 
00620                 $op2 = $op;
00621                 $op2['content'] = "fsf\n waf 3kt";
00622                 $cases[] = array(
00623                         $op2, // operation
00624                         true, // dest already exists
00625                         false, // fails
00626                         strlen( $op2['content'] )
00627                 );
00628 
00629                 $op2 = $op;
00630                 $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
00631                 $op2['overwrite'] = true;
00632                 $cases[] = array(
00633                         $op2, // operation
00634                         true, // dest already exists
00635                         true, // succeeds
00636                         strlen( $op2['content'] )
00637                 );
00638 
00639                 $op2 = $op;
00640                 $op2['content'] = "39qjmg3-qg";
00641                 $op2['overwriteSame'] = true;
00642                 $cases[] = array(
00643                         $op2, // operation
00644                         true, // dest already exists
00645                         false, // succeeds
00646                         strlen( $op2['content'] )
00647                 );
00648 
00649                 return $cases;
00650         }
00651 
00655         public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
00656                 $this->filesToPrune[] = $op['dst'];
00657 
00658                 $this->backend = $this->singleBackend;
00659                 $this->tearDownFiles();
00660                 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
00661                 $this->tearDownFiles();
00662 
00663                 $this->backend = $this->multiBackend;
00664                 $this->tearDownFiles();
00665                 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
00666                 $this->filesToPrune[] = $op['dst']; # avoid file leaking
00667                 $this->tearDownFiles();
00668         }
00669 
00670         public function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
00671                 $backendName = $this->backendClass();
00672 
00673                 $expContent = '';
00674                 // Create sources
00675                 $ops = array();
00676                 foreach ( $srcs as $i => $source ) {
00677                         $this->prepare( array( 'dir' => dirname( $source ) ) );
00678                         $ops[] = array(
00679                                 'op'      => 'create', // operation
00680                                 'dst'     => $source, // source
00681                                 'content' => $srcsContent[$i]
00682                         );
00683                         $expContent .= $srcsContent[$i];
00684                 }
00685                 $status = $this->backend->doOperations( $ops );
00686 
00687                 $this->assertEquals( array(), $status->errors,
00688                         "Creation of source files succeeded ($backendName)." );
00689 
00690                 $dest = $params['dst'];
00691                 if ( $alreadyExists ) {
00692                         $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
00693                         $this->assertEquals( true, $ok,
00694                                 "Creation of file at $dest succeeded ($backendName)." );
00695                 } else {
00696                         $ok = file_put_contents( $dest, '' ) !== false;
00697                         $this->assertEquals( true, $ok,
00698                                 "Creation of 0-byte file at $dest succeeded ($backendName)." );
00699                 }
00700 
00701                 // Combine the files into one
00702                 $status = $this->backend->concatenate( $params );
00703                 if ( $okStatus ) {
00704                         $this->assertEquals( array(), $status->errors,
00705                                 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
00706                         $this->assertEquals( true, $status->isOK(),
00707                                 "Creation of concat file at $dest succeeded ($backendName)." );
00708                 } else {
00709                         $this->assertEquals( false, $status->isOK(),
00710                                 "Creation of concat file at $dest failed ($backendName)." );
00711                 }
00712 
00713                 if ( $okStatus ) {
00714                         $this->assertEquals( true, is_file( $dest ),
00715                                 "Dest concat file $dest exists after creation ($backendName)." );
00716                 } else {
00717                         $this->assertEquals( true, is_file( $dest ),
00718                                 "Dest concat file $dest exists after failed creation ($backendName)." );
00719                 }
00720 
00721                 $contents = file_get_contents( $dest );
00722                 $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
00723 
00724                 if ( $okStatus ) {
00725                         $this->assertEquals( $expContent, $contents,
00726                                 "Concat file at $dest has correct contents ($backendName)." );
00727                 } else {
00728                         $this->assertNotEquals( $expContent, $contents,
00729                                 "Concat file at $dest has correct contents ($backendName)." );
00730                 }
00731         }
00732 
00733         function provider_testConcatenate() {
00734                 $cases = array();
00735 
00736                 $rand = mt_rand( 0, 2000000000 ) . time();
00737                 $dest = wfTempDir() . "/randomfile!$rand.txt";
00738                 $srcs = array(
00739                         $this->baseStorePath() . '/unittest-cont1/file1.txt',
00740                         $this->baseStorePath() . '/unittest-cont1/file2.txt',
00741                         $this->baseStorePath() . '/unittest-cont1/file3.txt',
00742                         $this->baseStorePath() . '/unittest-cont1/file4.txt',
00743                         $this->baseStorePath() . '/unittest-cont1/file5.txt',
00744                         $this->baseStorePath() . '/unittest-cont1/file6.txt',
00745                         $this->baseStorePath() . '/unittest-cont1/file7.txt',
00746                         $this->baseStorePath() . '/unittest-cont1/file8.txt',
00747                         $this->baseStorePath() . '/unittest-cont1/file9.txt',
00748                         $this->baseStorePath() . '/unittest-cont1/file10.txt'
00749                 );
00750                 $content = array(
00751                         'egfage',
00752                         'ageageag',
00753                         'rhokohlr',
00754                         'shgmslkg',
00755                         'kenga',
00756                         'owagmal',
00757                         'kgmae',
00758                         'g eak;g',
00759                         'lkaem;a',
00760                         'legma'
00761                 );
00762                 $params = array( 'srcs' => $srcs, 'dst' => $dest );
00763 
00764                 $cases[] = array(
00765                         $params, // operation
00766                         $srcs, // sources
00767                         $content, // content for each source
00768                         false, // no dest already exists
00769                         true, // succeeds
00770                 );
00771 
00772                 $cases[] = array(
00773                         $params, // operation
00774                         $srcs, // sources
00775                         $content, // content for each source
00776                         true, // dest already exists
00777                         false, // succeeds
00778                 );
00779 
00780                 return $cases;
00781         }
00782 
00786         public function testGetFileStat( $path, $content, $alreadyExists ) {
00787                 $this->backend = $this->singleBackend;
00788                 $this->tearDownFiles();
00789                 $this->doTestGetFileStat( $path, $content, $alreadyExists );
00790                 $this->tearDownFiles();
00791 
00792                 $this->backend = $this->multiBackend;
00793                 $this->tearDownFiles();
00794                 $this->doTestGetFileStat( $path, $content, $alreadyExists );
00795                 $this->tearDownFiles();
00796         }
00797 
00798         private function doTestGetFileStat( $path, $content, $alreadyExists ) {
00799                 $backendName = $this->backendClass();
00800 
00801                 if ( $alreadyExists ) {
00802                         $this->prepare( array( 'dir' => dirname( $path ) ) );
00803                         $status = $this->backend->create( array( 'dst' => $path, 'content' => $content ) );
00804                         $this->assertEquals( array(), $status->errors,
00805                                 "Creation of file at $path succeeded ($backendName)." );
00806 
00807                         $size = $this->backend->getFileSize( array( 'src' => $path ) );
00808                         $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
00809                         $stat = $this->backend->getFileStat( array( 'src' => $path ) );
00810 
00811                         $this->assertEquals( strlen( $content ), $size,
00812                                 "Correct file size of '$path'" );
00813                         $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 5,
00814                                 "Correct file timestamp of '$path'" );
00815 
00816                         $size = $stat['size'];
00817                         $time = $stat['mtime'];
00818                         $this->assertEquals( strlen( $content ), $size,
00819                                 "Correct file size of '$path'" );
00820                         $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 5,
00821                                 "Correct file timestamp of '$path'" );
00822                 } else {
00823                         $size = $this->backend->getFileSize( array( 'src' => $path ) );
00824                         $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
00825                         $stat = $this->backend->getFileStat( array( 'src' => $path ) );
00826                         
00827                         $this->assertFalse( $size, "Correct file size of '$path'" );
00828                         $this->assertFalse( $time, "Correct file timestamp of '$path'" );
00829                         $this->assertFalse( $stat, "Correct file stat of '$path'" );
00830                 }
00831         }
00832 
00833         function provider_testGetFileStat() {
00834                 $cases = array();
00835 
00836                 $base = $this->baseStorePath();
00837                 $cases[] = array( "$base/unittest-cont1/b/z/some_file.txt", "some file contents", true );
00838                 $cases[] = array( "$base/unittest-cont1/b/some-other_file.txt", "", true );
00839                 $cases[] = array( "$base/unittest-cont1/b/some-diff_file.txt", null, false );
00840 
00841                 return $cases;
00842         }
00843 
00847         public function testGetFileContents( $source, $content ) {
00848                 $this->backend = $this->singleBackend;
00849                 $this->tearDownFiles();
00850                 $this->doTestGetFileContents( $source, $content );
00851                 $this->tearDownFiles();
00852 
00853                 $this->backend = $this->multiBackend;
00854                 $this->tearDownFiles();
00855                 $this->doTestGetFileContents( $source, $content );
00856                 $this->tearDownFiles();
00857         }
00858 
00859         public function doTestGetFileContents( $source, $content ) {
00860                 $backendName = $this->backendClass();
00861 
00862                 $this->prepare( array( 'dir' => dirname( $source ) ) );
00863 
00864                 $status = $this->backend->doOperation(
00865                         array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
00866                 $this->assertEquals( array(), $status->errors,
00867                         "Creation of file at $source succeeded ($backendName)." );
00868                 $this->assertEquals( true, $status->isOK(),
00869                         "Creation of file at $source succeeded with OK status ($backendName)." );
00870 
00871                 $newContents = $this->backend->getFileContents( array( 'src' => $source, 'latest' => 1 ) );
00872                 $this->assertNotEquals( false, $newContents,
00873                         "Read of file at $source succeeded ($backendName)." );
00874 
00875                 $this->assertEquals( $content, $newContents,
00876                         "Contents read match data at $source ($backendName)." );
00877         }
00878 
00879         function provider_testGetFileContents() {
00880                 $cases = array();
00881 
00882                 $base = $this->baseStorePath();
00883                 $cases[] = array( "$base/unittest-cont1/b/z/some_file.txt", "some file contents" );
00884                 $cases[] = array( "$base/unittest-cont1/b/some-other_file.txt", "more file contents" );
00885 
00886                 return $cases;
00887         }
00888 
00892         public function testGetLocalCopy( $source, $content ) {
00893                 $this->backend = $this->singleBackend;
00894                 $this->tearDownFiles();
00895                 $this->doTestGetLocalCopy( $source, $content );
00896                 $this->tearDownFiles();
00897 
00898                 $this->backend = $this->multiBackend;
00899                 $this->tearDownFiles();
00900                 $this->doTestGetLocalCopy( $source, $content );
00901                 $this->tearDownFiles();
00902         }
00903 
00904         public function doTestGetLocalCopy( $source, $content ) {
00905                 $backendName = $this->backendClass();
00906 
00907                 $this->prepare( array( 'dir' => dirname( $source ) ) );
00908 
00909                 $status = $this->backend->doOperation(
00910                         array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
00911                 $this->assertEquals( array(), $status->errors,
00912                         "Creation of file at $source succeeded ($backendName)." );
00913 
00914                 $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
00915                 $this->assertNotNull( $tmpFile,
00916                         "Creation of local copy of $source succeeded ($backendName)." );
00917 
00918                 $contents = file_get_contents( $tmpFile->getPath() );
00919                 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
00920         }
00921 
00922         function provider_testGetLocalCopy() {
00923                 $cases = array();
00924 
00925                 $base = $this->baseStorePath();
00926                 $cases[] = array( "$base/unittest-cont1/a/z/some_file.txt", "some file contents" );
00927                 $cases[] = array( "$base/unittest-cont1/a/some-other_file.txt", "more file contents" );
00928 
00929                 return $cases;
00930         }
00931 
00935         public function testGetLocalReference( $source, $content ) {
00936                 $this->backend = $this->singleBackend;
00937                 $this->tearDownFiles();
00938                 $this->doTestGetLocalReference( $source, $content );
00939                 $this->tearDownFiles();
00940 
00941                 $this->backend = $this->multiBackend;
00942                 $this->tearDownFiles();
00943                 $this->doTestGetLocalReference( $source, $content );
00944                 $this->tearDownFiles();
00945         }
00946 
00947         private function doTestGetLocalReference( $source, $content ) {
00948                 $backendName = $this->backendClass();
00949 
00950                 $this->prepare( array( 'dir' => dirname( $source ) ) );
00951 
00952                 $status = $this->backend->doOperation(
00953                         array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
00954                 $this->assertEquals( array(), $status->errors,
00955                         "Creation of file at $source succeeded ($backendName)." );
00956 
00957                 $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
00958                 $this->assertNotNull( $tmpFile,
00959                         "Creation of local copy of $source succeeded ($backendName)." );
00960 
00961                 $contents = file_get_contents( $tmpFile->getPath() );
00962                 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
00963         }
00964 
00965         function provider_testGetLocalReference() {
00966                 $cases = array();
00967 
00968                 $base = $this->baseStorePath();
00969                 $cases[] = array( "$base/unittest-cont1/a/z/some_file.txt", "some file contents" );
00970                 $cases[] = array( "$base/unittest-cont1/a/some-other_file.txt", "more file contents" );
00971 
00972                 return $cases;
00973         }
00974 
00978         public function testPrepareAndClean( $path, $isOK ) {
00979                 $this->backend = $this->singleBackend;
00980                 $this->doTestPrepareAndClean( $path, $isOK );
00981                 $this->tearDownFiles();
00982 
00983                 $this->backend = $this->multiBackend;
00984                 $this->doTestPrepareAndClean( $path, $isOK );
00985                 $this->tearDownFiles();
00986         }
00987 
00988         function provider_testPrepareAndClean() {
00989                 $base = $this->baseStorePath();
00990                 return array(
00991                         array( "$base/unittest-cont1/a/z/some_file1.txt", true ),
00992                         array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
00993                         # Specific to FS backend with no basePath field set
00994                         #array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
00995                 );
00996         }
00997 
00998         function doTestPrepareAndClean( $path, $isOK ) {
00999                 $backendName = $this->backendClass();
01000 
01001                 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
01002                 if ( $isOK ) {
01003                         $this->assertEquals( array(), $status->errors,
01004                                 "Preparing dir $path succeeded without warnings ($backendName)." );
01005                         $this->assertEquals( true, $status->isOK(),
01006                                 "Preparing dir $path succeeded ($backendName)." );
01007                 } else {
01008                         $this->assertEquals( false, $status->isOK(),
01009                                 "Preparing dir $path failed ($backendName)." );
01010                 }
01011 
01012                 $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
01013                 if ( $isOK ) {
01014                         $this->assertEquals( array(), $status->errors,
01015                                 "Cleaning dir $path succeeded without warnings ($backendName)." );
01016                         $this->assertEquals( true, $status->isOK(),
01017                                 "Cleaning dir $path succeeded ($backendName)." );
01018                 } else {
01019                         $this->assertEquals( false, $status->isOK(),
01020                                 "Cleaning dir $path failed ($backendName)." );
01021                 }
01022         }
01023 
01024         // @TODO: testSecure
01025 
01026         public function testDoOperations() {
01027                 $this->backend = $this->singleBackend;
01028                 $this->tearDownFiles();
01029                 $this->doTestDoOperations();
01030                 $this->tearDownFiles();
01031 
01032                 $this->backend = $this->multiBackend;
01033                 $this->tearDownFiles();
01034                 $this->doTestDoOperations();
01035                 $this->tearDownFiles();
01036 
01037                 $this->backend = $this->singleBackend;
01038                 $this->tearDownFiles();
01039                 $this->doTestDoOperationsFailing();
01040                 $this->tearDownFiles();
01041 
01042                 $this->backend = $this->multiBackend;
01043                 $this->tearDownFiles();
01044                 $this->doTestDoOperationsFailing();
01045                 $this->tearDownFiles();
01046 
01047                 // @TODO: test some cases where the ops should fail
01048         }
01049 
01050         function doTestDoOperations() {
01051                 $base = $this->baseStorePath();
01052 
01053                 $fileA = "$base/unittest-cont1/a/b/fileA.txt";
01054                 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
01055                 $fileB = "$base/unittest-cont1/a/b/fileB.txt";
01056                 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
01057                 $fileC = "$base/unittest-cont1/a/b/fileC.txt";
01058                 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
01059                 $fileD = "$base/unittest-cont1/a/b/fileD.txt";
01060 
01061                 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
01062                 $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
01063                 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
01064                 $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
01065                 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
01066                 $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
01067 
01068                 $status = $this->backend->doOperations( array(
01069                         array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
01070                         // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
01071                         array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
01072                         // Now: A:<A>, B:<B>, C:<A>, D:<empty>
01073                         array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
01074                         // Now: A:<A>, B:<B>, C:<empty>, D:<A>
01075                         array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
01076                         // Now: A:<A>, B:<empty>, C:<B>, D:<A>
01077                         array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
01078                         // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
01079                         array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
01080                         // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
01081                         array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
01082                         // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
01083                         array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
01084                         // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
01085                         array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
01086                         // Does nothing
01087                         array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
01088                         // Does nothing
01089                         array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
01090                         // Does nothing
01091                         array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
01092                         // Does nothing
01093                         array( 'op' => 'null' ),
01094                         // Does nothing
01095                 ) );
01096 
01097                 $this->assertEquals( array(), $status->errors, "Operation batch succeeded" );
01098                 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
01099                 $this->assertEquals( 13, count( $status->success ),
01100                         "Operation batch has correct success array" );
01101 
01102                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
01103                         "File does not exist at $fileA" );
01104                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
01105                         "File does not exist at $fileB" );
01106                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
01107                         "File does not exist at $fileD" );
01108 
01109                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
01110                         "File exists at $fileC" );
01111                 $this->assertEquals( $fileBContents,
01112                         $this->backend->getFileContents( array( 'src' => $fileC ) ),
01113                         "Correct file contents of $fileC" );
01114                 $this->assertEquals( strlen( $fileBContents ),
01115                         $this->backend->getFileSize( array( 'src' => $fileC ) ),
01116                         "Correct file size of $fileC" );
01117                 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
01118                         $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
01119                         "Correct file SHA-1 of $fileC" );
01120         }
01121 
01122         function doTestDoOperationsFailing() {
01123                 $base = $this->baseStorePath();
01124 
01125                 $fileA = "$base/unittest-cont2/a/b/fileA.txt";
01126                 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
01127                 $fileB = "$base/unittest-cont2/a/b/fileB.txt";
01128                 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
01129                 $fileC = "$base/unittest-cont2/a/b/fileC.txt";
01130                 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
01131                 $fileD = "$base/unittest-cont2/a/b/fileD.txt";
01132 
01133                 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
01134                 $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
01135                 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
01136                 $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
01137                 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
01138                 $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
01139 
01140                 $status = $this->backend->doOperations( array(
01141                         array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
01142                         // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
01143                         array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
01144                         // Now: A:<A>, B:<B>, C:<A>, D:<empty>
01145                         array( 'op' => 'copy', 'src' => $fileB, 'dst' => $fileD, 'overwrite' => 1 ),
01146                         // Now: A:<A>, B:<B>, C:<A>, D:<B>
01147                         array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD ),
01148                         // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
01149                         array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC, 'overwriteSame' => 1 ),
01150                         // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
01151                         array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileA, 'overwrite' => 1 ),
01152                         // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
01153                         array( 'op' => 'delete', 'src' => $fileD ),
01154                         // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
01155                         array( 'op' => 'null' ),
01156                         // Does nothing
01157                 ), array( 'force' => 1 ) );
01158 
01159                 $this->assertNotEquals( array(), $status->errors, "Operation had warnings" );
01160                 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
01161                 $this->assertEquals( 8, count( $status->success ),
01162                         "Operation batch has correct success array" );
01163 
01164                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
01165                         "File does not exist at $fileB" );
01166                 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
01167                         "File does not exist at $fileD" );
01168 
01169                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileA ) ),
01170                         "File does not exist at $fileA" );
01171                 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
01172                         "File exists at $fileC" );
01173                 $this->assertEquals( $fileBContents,
01174                         $this->backend->getFileContents( array( 'src' => $fileA ) ),
01175                         "Correct file contents of $fileA" );
01176                 $this->assertEquals( strlen( $fileBContents ),
01177                         $this->backend->getFileSize( array( 'src' => $fileA ) ),
01178                         "Correct file size of $fileA" );
01179                 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
01180                         $this->backend->getFileSha1Base36( array( 'src' => $fileA ) ),
01181                         "Correct file SHA-1 of $fileA" );
01182         }
01183 
01184         public function testGetFileList() {
01185                 $this->backend = $this->singleBackend;
01186                 $this->tearDownFiles();
01187                 $this->doTestGetFileList();
01188                 $this->tearDownFiles();
01189 
01190                 $this->backend = $this->multiBackend;
01191                 $this->tearDownFiles();
01192                 $this->doTestGetFileList();
01193                 $this->tearDownFiles();
01194         }
01195 
01196         private function doTestGetFileList() {
01197                 $backendName = $this->backendClass();
01198 
01199                 $base = $this->baseStorePath();
01200                 $files = array(
01201                         "$base/unittest-cont1/test1.txt",
01202                         "$base/unittest-cont1/test2.txt",
01203                         "$base/unittest-cont1/test3.txt",
01204                         "$base/unittest-cont1/subdir1/test1.txt",
01205                         "$base/unittest-cont1/subdir1/test2.txt",
01206                         "$base/unittest-cont1/subdir2/test3.txt",
01207                         "$base/unittest-cont1/subdir2/test4.txt",
01208                         "$base/unittest-cont1/subdir2/subdir/test1.txt",
01209                         "$base/unittest-cont1/subdir2/subdir/test2.txt",
01210                         "$base/unittest-cont1/subdir2/subdir/test3.txt",
01211                         "$base/unittest-cont1/subdir2/subdir/test4.txt",
01212                         "$base/unittest-cont1/subdir2/subdir/test5.txt",
01213                         "$base/unittest-cont1/subdir2/subdir/sub/test0.txt",
01214                         "$base/unittest-cont1/subdir2/subdir/sub/120-px-file.txt",
01215                 );
01216 
01217                 // Add the files
01218                 $ops = array();
01219                 foreach ( $files as $file ) {
01220                         $this->prepare( array( 'dir' => dirname( $file ) ) );
01221                         $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
01222                 }
01223                 $status = $this->backend->doOperations( $ops );
01224                 $this->assertEquals( array(), $status->errors,
01225                         "Creation of files succeeded ($backendName)." );
01226                 $this->assertEquals( true, $status->isOK(),
01227                         "Creation of files succeeded with OK status ($backendName)." );
01228 
01229                 // Expected listing
01230                 $expected = array(
01231                         "test1.txt",
01232                         "test2.txt",
01233                         "test3.txt",
01234                         "subdir1/test1.txt",
01235                         "subdir1/test2.txt",
01236                         "subdir2/test3.txt",
01237                         "subdir2/test4.txt",
01238                         "subdir2/subdir/test1.txt",
01239                         "subdir2/subdir/test2.txt",
01240                         "subdir2/subdir/test3.txt",
01241                         "subdir2/subdir/test4.txt",
01242                         "subdir2/subdir/test5.txt",
01243                         "subdir2/subdir/sub/test0.txt",
01244                         "subdir2/subdir/sub/120-px-file.txt",
01245                 );
01246                 sort( $expected );
01247 
01248                 // Actual listing (no trailing slash)
01249                 $list = array();
01250                 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1" ) );
01251                 foreach ( $iter as $file ) {
01252                         $list[] = $file;
01253                 }
01254                 sort( $list );
01255 
01256                 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01257 
01258                 // Actual listing (with trailing slash)
01259                 $list = array();
01260                 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/" ) );
01261                 foreach ( $iter as $file ) {
01262                         $list[] = $file;
01263                 }
01264                 sort( $list );
01265 
01266                 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01267 
01268                 // Expected listing
01269                 $expected = array(
01270                         "test1.txt",
01271                         "test2.txt",
01272                         "test3.txt",
01273                         "test4.txt",
01274                         "test5.txt",
01275                         "sub/test0.txt",
01276                         "sub/120-px-file.txt",
01277                 );
01278                 sort( $expected );
01279 
01280                 // Actual listing (no trailing slash)
01281                 $list = array();
01282                 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/subdir2/subdir" ) );
01283                 foreach ( $iter as $file ) {
01284                         $list[] = $file;
01285                 }
01286                 sort( $list );
01287 
01288                 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01289 
01290                 // Actual listing (with trailing slash)
01291                 $list = array();
01292                 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/subdir2/subdir/" ) );
01293                 foreach ( $iter as $file ) {
01294                         $list[] = $file;
01295                 }
01296                 sort( $list );
01297 
01298                 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01299 
01300                 // Actual listing (using iterator second time)
01301                 $list = array();
01302                 foreach ( $iter as $file ) {
01303                         $list[] = $file;
01304                 }
01305                 sort( $list );
01306 
01307                 $this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
01308 
01309                 foreach ( $files as $file ) { // clean up
01310                         $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
01311                 }
01312 
01313                 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
01314                 foreach ( $iter as $iter ) {} // no errors
01315         }
01316 
01317         // test helper wrapper for backend prepare() function
01318         private function prepare( array $params ) {
01319                 $this->dirsToPrune[] = $params['dir'];
01320                 return $this->backend->prepare( $params );
01321         }
01322 
01323         function tearDownFiles() {
01324                 foreach ( $this->filesToPrune as $file ) {
01325                         @unlink( $file );
01326                 }
01327                 $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont3' );
01328                 foreach ( $containers as $container ) {
01329                         $this->deleteFiles( $container );
01330                 }
01331                 foreach ( $this->dirsToPrune as $dir ) {
01332                         $this->recursiveClean( $dir );
01333                 }
01334                 $this->filesToPrune = $this->dirsToPrune = array();
01335         }
01336 
01337         private function deleteFiles( $container ) {
01338                 $base = $this->baseStorePath();
01339                 $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
01340                 if ( $iter ) {
01341                         foreach ( $iter as $file ) {
01342                                 $this->backend->delete( array( 'src' => "$base/$container/$file" ), array( 'force' => 1 ) );
01343                         }
01344                 }
01345         }
01346 
01347         private function recursiveClean( $dir ) {
01348                 do {
01349                         if ( !$this->backend->clean( array( 'dir' => $dir ) )->isOK() ) {
01350                                 break;
01351                         }
01352                 } while ( $dir = FileBackend::parentStoragePath( $dir ) );
01353         }
01354 
01355         function tearDown() {
01356                 parent::tearDown();
01357         }
01358 }