MediaWiki  REL1_23
FileBackendTest.php
Go to the documentation of this file.
00001 <?php
00002 
00008 class FileBackendTest extends MediaWikiTestCase {
00009 
00011     private $backend;
00013     private $multiBackend;
00015     public $singleBackend;
00016     private $filesToPrune = array();
00017     private static $backendToUse;
00018 
00019     protected function setUp() {
00020         global $wgFileBackends;
00021         parent::setUp();
00022         $uniqueId = time() . '-' . mt_rand();
00023         $tmpPrefix = wfTempDir() . '/filebackend-unittest-' . $uniqueId;
00024         if ( $this->getCliArg( 'use-filebackend=' ) ) {
00025             if ( self::$backendToUse ) {
00026                 $this->singleBackend = self::$backendToUse;
00027             } else {
00028                 $name = $this->getCliArg( 'use-filebackend=' );
00029                 $useConfig = array();
00030                 foreach ( $wgFileBackends as $conf ) {
00031                     if ( $conf['name'] == $name ) {
00032                         $useConfig = $conf;
00033                         break;
00034                     }
00035                 }
00036                 $useConfig['name'] = 'localtesting'; // swap name
00037                 $useConfig['shardViaHashLevels'] = array( // test sharding
00038                     'unittest-cont1' => array( 'levels' => 1, 'base' => 16, 'repeat' => 1 )
00039                 );
00040                 if ( isset( $useConfig['fileJournal'] ) ) {
00041                     $useConfig['fileJournal'] = FileJournal::factory( $useConfig['fileJournal'], $name );
00042                 }
00043                 $useConfig['lockManager'] = LockManagerGroup::singleton()->get( $useConfig['lockManager'] );
00044                 $class = $useConfig['class'];
00045                 self::$backendToUse = new $class( $useConfig );
00046                 $this->singleBackend = self::$backendToUse;
00047             }
00048         } else {
00049             $this->singleBackend = new FSFileBackend( array(
00050                 'name' => 'localtesting',
00051                 'lockManager' => LockManagerGroup::singleton()->get( 'fsLockManager' ),
00052                 'wikiId' => wfWikiID(),
00053                 'containerPaths' => array(
00054                     'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1",
00055                     'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" )
00056             ) );
00057         }
00058         $this->multiBackend = new FileBackendMultiWrite( array(
00059             'name' => 'localtesting',
00060             'lockManager' => LockManagerGroup::singleton()->get( 'fsLockManager' ),
00061             'parallelize' => 'implicit',
00062             'wikiId' => wfWikiId() . $uniqueId,
00063             'backends' => array(
00064                 array(
00065                     'name' => 'localmultitesting1',
00066                     'class' => 'FSFileBackend',
00067                     'containerPaths' => array(
00068                         'unittest-cont1' => "{$tmpPrefix}-localtestingmulti1-cont1",
00069                         'unittest-cont2' => "{$tmpPrefix}-localtestingmulti1-cont2" ),
00070                     'isMultiMaster' => false
00071                 ),
00072                 array(
00073                     'name' => 'localmultitesting2',
00074                     'class' => 'FSFileBackend',
00075                     'containerPaths' => array(
00076                         'unittest-cont1' => "{$tmpPrefix}-localtestingmulti2-cont1",
00077                         'unittest-cont2' => "{$tmpPrefix}-localtestingmulti2-cont2" ),
00078                     'isMultiMaster' => true
00079                 )
00080             )
00081         ) );
00082         $this->filesToPrune = array();
00083     }
00084 
00085     private static function baseStorePath() {
00086         return 'mwstore://localtesting';
00087     }
00088 
00089     private function backendClass() {
00090         return get_class( $this->backend );
00091     }
00092 
00097     public function testIsStoragePath( $path, $isStorePath ) {
00098         $this->assertEquals( $isStorePath, FileBackend::isStoragePath( $path ),
00099             "FileBackend::isStoragePath on path '$path'" );
00100     }
00101 
00102     public static function provider_testIsStoragePath() {
00103         return array(
00104             array( 'mwstore://', true ),
00105             array( 'mwstore://backend', true ),
00106             array( 'mwstore://backend/container', true ),
00107             array( 'mwstore://backend/container/', true ),
00108             array( 'mwstore://backend/container/path', true ),
00109             array( 'mwstore://backend//container/', true ),
00110             array( 'mwstore://backend//container//', true ),
00111             array( 'mwstore://backend//container//path', true ),
00112             array( 'mwstore:///', true ),
00113             array( 'mwstore:/', false ),
00114             array( 'mwstore:', false ),
00115         );
00116     }
00117 
00122     public function testSplitStoragePath( $path, $res ) {
00123         $this->assertEquals( $res, FileBackend::splitStoragePath( $path ),
00124             "FileBackend::splitStoragePath on path '$path'" );
00125     }
00126 
00127     public static function provider_testSplitStoragePath() {
00128         return array(
00129             array( 'mwstore://backend/container', array( 'backend', 'container', '' ) ),
00130             array( 'mwstore://backend/container/', array( 'backend', 'container', '' ) ),
00131             array( 'mwstore://backend/container/path', array( 'backend', 'container', 'path' ) ),
00132             array( 'mwstore://backend/container//path', array( 'backend', 'container', '/path' ) ),
00133             array( 'mwstore://backend//container/path', array( null, null, null ) ),
00134             array( 'mwstore://backend//container//path', array( null, null, null ) ),
00135             array( 'mwstore://', array( null, null, null ) ),
00136             array( 'mwstore://backend', array( null, null, null ) ),
00137             array( 'mwstore:///', array( null, null, null ) ),
00138             array( 'mwstore:/', array( null, null, null ) ),
00139             array( 'mwstore:', array( null, null, null ) )
00140         );
00141     }
00142 
00147     public function testNormalizeStoragePath( $path, $res ) {
00148         $this->assertEquals( $res, FileBackend::normalizeStoragePath( $path ),
00149             "FileBackend::normalizeStoragePath on path '$path'" );
00150     }
00151 
00152     public static function provider_normalizeStoragePath() {
00153         return array(
00154             array( 'mwstore://backend/container', 'mwstore://backend/container' ),
00155             array( 'mwstore://backend/container/', 'mwstore://backend/container' ),
00156             array( 'mwstore://backend/container/path', 'mwstore://backend/container/path' ),
00157             array( 'mwstore://backend/container//path', 'mwstore://backend/container/path' ),
00158             array( 'mwstore://backend/container///path', 'mwstore://backend/container/path' ),
00159             array( 'mwstore://backend/container///path//to///obj', 'mwstore://backend/container/path/to/obj' ),
00160             array( 'mwstore://', null ),
00161             array( 'mwstore://backend', null ),
00162             array( 'mwstore://backend//container/path', null ),
00163             array( 'mwstore://backend//container//path', null ),
00164             array( 'mwstore:///', null ),
00165             array( 'mwstore:/', null ),
00166             array( 'mwstore:', null ),
00167         );
00168     }
00169 
00174     public function testParentStoragePath( $path, $res ) {
00175         $this->assertEquals( $res, FileBackend::parentStoragePath( $path ),
00176             "FileBackend::parentStoragePath on path '$path'" );
00177     }
00178 
00179     public static function provider_testParentStoragePath() {
00180         return array(
00181             array( 'mwstore://backend/container/path/to/obj', 'mwstore://backend/container/path/to' ),
00182             array( 'mwstore://backend/container/path/to', 'mwstore://backend/container/path' ),
00183             array( 'mwstore://backend/container/path', 'mwstore://backend/container' ),
00184             array( 'mwstore://backend/container', null ),
00185             array( 'mwstore://backend/container/path/to/obj/', 'mwstore://backend/container/path/to' ),
00186             array( 'mwstore://backend/container/path/to/', 'mwstore://backend/container/path' ),
00187             array( 'mwstore://backend/container/path/', 'mwstore://backend/container' ),
00188             array( 'mwstore://backend/container/', null ),
00189         );
00190     }
00191 
00196     public function testExtensionFromPath( $path, $res ) {
00197         $this->assertEquals( $res, FileBackend::extensionFromPath( $path ),
00198             "FileBackend::extensionFromPath on path '$path'" );
00199     }
00200 
00201     public static function provider_testExtensionFromPath() {
00202         return array(
00203             array( 'mwstore://backend/container/path.txt', 'txt' ),
00204             array( 'mwstore://backend/container/path.svg.png', 'png' ),
00205             array( 'mwstore://backend/container/path', '' ),
00206             array( 'mwstore://backend/container/path.', '' ),
00207         );
00208     }
00209 
00213     public function testStore( $op ) {
00214         $this->filesToPrune[] = $op['src'];
00215 
00216         $this->backend = $this->singleBackend;
00217         $this->tearDownFiles();
00218         $this->doTestStore( $op );
00219         $this->tearDownFiles();
00220 
00221         $this->backend = $this->multiBackend;
00222         $this->tearDownFiles();
00223         $this->doTestStore( $op );
00224         $this->filesToPrune[] = $op['src']; # avoid file leaking
00225         $this->tearDownFiles();
00226     }
00227 
00231     private function doTestStore( $op ) {
00232         $backendName = $this->backendClass();
00233 
00234         $source = $op['src'];
00235         $dest = $op['dst'];
00236         $this->prepare( array( 'dir' => dirname( $dest ) ) );
00237 
00238         file_put_contents( $source, "Unit test file" );
00239 
00240         if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
00241             $this->backend->store( $op );
00242         }
00243 
00244         $status = $this->backend->doOperation( $op );
00245 
00246         $this->assertGoodStatus( $status,
00247             "Store from $source to $dest succeeded without warnings ($backendName)." );
00248         $this->assertEquals( true, $status->isOK(),
00249             "Store from $source to $dest succeeded ($backendName)." );
00250         $this->assertEquals( array( 0 => true ), $status->success,
00251             "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
00252         $this->assertEquals( true, file_exists( $source ),
00253             "Source file $source still exists ($backendName)." );
00254         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00255             "Destination file $dest exists ($backendName)." );
00256 
00257         $this->assertEquals( filesize( $source ),
00258             $this->backend->getFileSize( array( 'src' => $dest ) ),
00259             "Destination file $dest has correct size ($backendName)." );
00260 
00261         $props1 = FSFile::getPropsFromPath( $source );
00262         $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
00263         $this->assertEquals( $props1, $props2,
00264             "Source and destination have the same props ($backendName)." );
00265 
00266         $this->assertBackendPathsConsistent( array( $dest ) );
00267     }
00268 
00269     public static function provider_testStore() {
00270         $cases = array();
00271 
00272         $tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath();
00273         $toPath = self::baseStorePath() . '/unittest-cont1/e/fun/obj1.txt';
00274         $op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath );
00275         $cases[] = array(
00276             $op, // operation
00277             $tmpName, // source
00278             $toPath, // dest
00279         );
00280 
00281         $op2 = $op;
00282         $op2['overwrite'] = true;
00283         $cases[] = array(
00284             $op2, // operation
00285             $tmpName, // source
00286             $toPath, // dest
00287         );
00288 
00289         $op2 = $op;
00290         $op2['overwriteSame'] = true;
00291         $cases[] = array(
00292             $op2, // operation
00293             $tmpName, // source
00294             $toPath, // dest
00295         );
00296 
00297         return $cases;
00298     }
00299 
00304     public function testCopy( $op ) {
00305         $this->backend = $this->singleBackend;
00306         $this->tearDownFiles();
00307         $this->doTestCopy( $op );
00308         $this->tearDownFiles();
00309 
00310         $this->backend = $this->multiBackend;
00311         $this->tearDownFiles();
00312         $this->doTestCopy( $op );
00313         $this->tearDownFiles();
00314     }
00315 
00316     private function doTestCopy( $op ) {
00317         $backendName = $this->backendClass();
00318 
00319         $source = $op['src'];
00320         $dest = $op['dst'];
00321         $this->prepare( array( 'dir' => dirname( $source ) ) );
00322         $this->prepare( array( 'dir' => dirname( $dest ) ) );
00323 
00324         if ( isset( $op['ignoreMissingSource'] ) ) {
00325             $status = $this->backend->doOperation( $op );
00326             $this->assertGoodStatus( $status,
00327                 "Move from $source to $dest succeeded without warnings ($backendName)." );
00328             $this->assertEquals( array( 0 => true ), $status->success,
00329                 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
00330             $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
00331                 "Source file $source does not exist ($backendName)." );
00332             $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $dest ) ),
00333                 "Destination file $dest does not exist ($backendName)." );
00334 
00335             return; // done
00336         }
00337 
00338         $status = $this->backend->doOperation(
00339             array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
00340         $this->assertGoodStatus( $status,
00341             "Creation of file at $source succeeded ($backendName)." );
00342 
00343         if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
00344             $this->backend->copy( $op );
00345         }
00346 
00347         $status = $this->backend->doOperation( $op );
00348 
00349         $this->assertGoodStatus( $status,
00350             "Copy from $source to $dest succeeded without warnings ($backendName)." );
00351         $this->assertEquals( true, $status->isOK(),
00352             "Copy from $source to $dest succeeded ($backendName)." );
00353         $this->assertEquals( array( 0 => true ), $status->success,
00354             "Copy from $source to $dest has proper 'success' field in Status ($backendName)." );
00355         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $source ) ),
00356             "Source file $source still exists ($backendName)." );
00357         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00358             "Destination file $dest exists after copy ($backendName)." );
00359 
00360         $this->assertEquals(
00361             $this->backend->getFileSize( array( 'src' => $source ) ),
00362             $this->backend->getFileSize( array( 'src' => $dest ) ),
00363             "Destination file $dest has correct size ($backendName)." );
00364 
00365         $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
00366         $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
00367         $this->assertEquals( $props1, $props2,
00368             "Source and destination have the same props ($backendName)." );
00369 
00370         $this->assertBackendPathsConsistent( array( $source, $dest ) );
00371     }
00372 
00373     public static function provider_testCopy() {
00374         $cases = array();
00375 
00376         $source = self::baseStorePath() . '/unittest-cont1/e/file.txt';
00377         $dest = self::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
00378 
00379         $op = array( 'op' => 'copy', 'src' => $source, 'dst' => $dest );
00380         $cases[] = array(
00381             $op, // operation
00382             $source, // source
00383             $dest, // dest
00384         );
00385 
00386         $op2 = $op;
00387         $op2['overwrite'] = true;
00388         $cases[] = array(
00389             $op2, // operation
00390             $source, // source
00391             $dest, // dest
00392         );
00393 
00394         $op2 = $op;
00395         $op2['overwriteSame'] = true;
00396         $cases[] = array(
00397             $op2, // operation
00398             $source, // source
00399             $dest, // dest
00400         );
00401 
00402         $op2 = $op;
00403         $op2['ignoreMissingSource'] = true;
00404         $cases[] = array(
00405             $op2, // operation
00406             $source, // source
00407             $dest, // dest
00408         );
00409 
00410         $op2 = $op;
00411         $op2['ignoreMissingSource'] = true;
00412         $cases[] = array(
00413             $op2, // operation
00414             self::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
00415             $dest, // dest
00416         );
00417 
00418         return $cases;
00419     }
00420 
00425     public function testMove( $op ) {
00426         $this->backend = $this->singleBackend;
00427         $this->tearDownFiles();
00428         $this->doTestMove( $op );
00429         $this->tearDownFiles();
00430 
00431         $this->backend = $this->multiBackend;
00432         $this->tearDownFiles();
00433         $this->doTestMove( $op );
00434         $this->tearDownFiles();
00435     }
00436 
00437     private function doTestMove( $op ) {
00438         $backendName = $this->backendClass();
00439 
00440         $source = $op['src'];
00441         $dest = $op['dst'];
00442         $this->prepare( array( 'dir' => dirname( $source ) ) );
00443         $this->prepare( array( 'dir' => dirname( $dest ) ) );
00444 
00445         if ( isset( $op['ignoreMissingSource'] ) ) {
00446             $status = $this->backend->doOperation( $op );
00447             $this->assertGoodStatus( $status,
00448                 "Move from $source to $dest succeeded without warnings ($backendName)." );
00449             $this->assertEquals( array( 0 => true ), $status->success,
00450                 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
00451             $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
00452                 "Source file $source does not exist ($backendName)." );
00453             $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $dest ) ),
00454                 "Destination file $dest does not exist ($backendName)." );
00455 
00456             return; // done
00457         }
00458 
00459         $status = $this->backend->doOperation(
00460             array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
00461         $this->assertGoodStatus( $status,
00462             "Creation of file at $source succeeded ($backendName)." );
00463 
00464         if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
00465             $this->backend->copy( $op );
00466         }
00467 
00468         $status = $this->backend->doOperation( $op );
00469         $this->assertGoodStatus( $status,
00470             "Move from $source to $dest succeeded without warnings ($backendName)." );
00471         $this->assertEquals( true, $status->isOK(),
00472             "Move from $source to $dest succeeded ($backendName)." );
00473         $this->assertEquals( array( 0 => true ), $status->success,
00474             "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
00475         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
00476             "Source file $source does not still exists ($backendName)." );
00477         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00478             "Destination file $dest exists after move ($backendName)." );
00479 
00480         $this->assertNotEquals(
00481             $this->backend->getFileSize( array( 'src' => $source ) ),
00482             $this->backend->getFileSize( array( 'src' => $dest ) ),
00483             "Destination file $dest has correct size ($backendName)." );
00484 
00485         $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
00486         $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
00487         $this->assertEquals( false, $props1['fileExists'],
00488             "Source file does not exist accourding to props ($backendName)." );
00489         $this->assertEquals( true, $props2['fileExists'],
00490             "Destination file exists accourding to props ($backendName)." );
00491 
00492         $this->assertBackendPathsConsistent( array( $source, $dest ) );
00493     }
00494 
00495     public static function provider_testMove() {
00496         $cases = array();
00497 
00498         $source = self::baseStorePath() . '/unittest-cont1/e/file.txt';
00499         $dest = self::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
00500 
00501         $op = array( 'op' => 'move', 'src' => $source, 'dst' => $dest );
00502         $cases[] = array(
00503             $op, // operation
00504             $source, // source
00505             $dest, // dest
00506         );
00507 
00508         $op2 = $op;
00509         $op2['overwrite'] = true;
00510         $cases[] = array(
00511             $op2, // operation
00512             $source, // source
00513             $dest, // dest
00514         );
00515 
00516         $op2 = $op;
00517         $op2['overwriteSame'] = true;
00518         $cases[] = array(
00519             $op2, // operation
00520             $source, // source
00521             $dest, // dest
00522         );
00523 
00524         $op2 = $op;
00525         $op2['ignoreMissingSource'] = true;
00526         $cases[] = array(
00527             $op2, // operation
00528             $source, // source
00529             $dest, // dest
00530         );
00531 
00532         $op2 = $op;
00533         $op2['ignoreMissingSource'] = true;
00534         $cases[] = array(
00535             $op2, // operation
00536             self::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
00537             $dest, // dest
00538         );
00539 
00540         return $cases;
00541     }
00542 
00547     public function testDelete( $op, $withSource, $okStatus ) {
00548         $this->backend = $this->singleBackend;
00549         $this->tearDownFiles();
00550         $this->doTestDelete( $op, $withSource, $okStatus );
00551         $this->tearDownFiles();
00552 
00553         $this->backend = $this->multiBackend;
00554         $this->tearDownFiles();
00555         $this->doTestDelete( $op, $withSource, $okStatus );
00556         $this->tearDownFiles();
00557     }
00558 
00559     private function doTestDelete( $op, $withSource, $okStatus ) {
00560         $backendName = $this->backendClass();
00561 
00562         $source = $op['src'];
00563         $this->prepare( array( 'dir' => dirname( $source ) ) );
00564 
00565         if ( $withSource ) {
00566             $status = $this->backend->doOperation(
00567                 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
00568             $this->assertGoodStatus( $status,
00569                 "Creation of file at $source succeeded ($backendName)." );
00570         }
00571 
00572         $status = $this->backend->doOperation( $op );
00573         if ( $okStatus ) {
00574             $this->assertGoodStatus( $status,
00575                 "Deletion of file at $source succeeded without warnings ($backendName)." );
00576             $this->assertEquals( true, $status->isOK(),
00577                 "Deletion of file at $source succeeded ($backendName)." );
00578             $this->assertEquals( array( 0 => true ), $status->success,
00579                 "Deletion of file at $source has proper 'success' field in Status ($backendName)." );
00580         } else {
00581             $this->assertEquals( false, $status->isOK(),
00582                 "Deletion of file at $source failed ($backendName)." );
00583         }
00584 
00585         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
00586             "Source file $source does not exist after move ($backendName)." );
00587 
00588         $this->assertFalse(
00589             $this->backend->getFileSize( array( 'src' => $source ) ),
00590             "Source file $source has correct size (false) ($backendName)." );
00591 
00592         $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
00593         $this->assertFalse( $props1['fileExists'],
00594             "Source file $source does not exist according to props ($backendName)." );
00595 
00596         $this->assertBackendPathsConsistent( array( $source ) );
00597     }
00598 
00599     public static function provider_testDelete() {
00600         $cases = array();
00601 
00602         $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
00603 
00604         $op = array( 'op' => 'delete', 'src' => $source );
00605         $cases[] = array(
00606             $op, // operation
00607             true, // with source
00608             true // succeeds
00609         );
00610 
00611         $cases[] = array(
00612             $op, // operation
00613             false, // without source
00614             false // fails
00615         );
00616 
00617         $op['ignoreMissingSource'] = true;
00618         $cases[] = array(
00619             $op, // operation
00620             false, // without source
00621             true // succeeds
00622         );
00623 
00624         $op['ignoreMissingSource'] = true;
00625         $op['src'] = self::baseStorePath() . '/unittest-cont-bad/e/file.txt';
00626         $cases[] = array(
00627             $op, // operation
00628             false, // without source
00629             true // succeeds
00630         );
00631 
00632         return $cases;
00633     }
00634 
00639     public function testDescribe( $op, $withSource, $okStatus ) {
00640         $this->backend = $this->singleBackend;
00641         $this->tearDownFiles();
00642         $this->doTestDescribe( $op, $withSource, $okStatus );
00643         $this->tearDownFiles();
00644 
00645         $this->backend = $this->multiBackend;
00646         $this->tearDownFiles();
00647         $this->doTestDescribe( $op, $withSource, $okStatus );
00648         $this->tearDownFiles();
00649     }
00650 
00651     private function doTestDescribe( $op, $withSource, $okStatus ) {
00652         $backendName = $this->backendClass();
00653 
00654         $source = $op['src'];
00655         $this->prepare( array( 'dir' => dirname( $source ) ) );
00656 
00657         if ( $withSource ) {
00658             $status = $this->backend->doOperation(
00659                 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source,
00660                     'headers' => array( 'Content-Disposition' => 'xxx' ) ) );
00661             $this->assertGoodStatus( $status,
00662                 "Creation of file at $source succeeded ($backendName)." );
00663             if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
00664                 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
00665                 $this->assertHasHeaders( array( 'Content-Disposition' => 'xxx' ), $attr );
00666             }
00667 
00668             $status = $this->backend->describe( array( 'src' => $source,
00669                 'headers' => array( 'Content-Disposition' => '' ) ) ); // remove
00670             $this->assertGoodStatus( $status,
00671                 "Removal of header for $source succeeded ($backendName)." );
00672 
00673             if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
00674                 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
00675                 $this->assertFalse( isset( $attr['headers']['content-disposition'] ),
00676                     "File 'Content-Disposition' header removed." );
00677             }
00678         }
00679 
00680         $status = $this->backend->doOperation( $op );
00681         if ( $okStatus ) {
00682             $this->assertGoodStatus( $status,
00683                 "Describe of file at $source succeeded without warnings ($backendName)." );
00684             $this->assertEquals( true, $status->isOK(),
00685                 "Describe of file at $source succeeded ($backendName)." );
00686             $this->assertEquals( array( 0 => true ), $status->success,
00687                 "Describe of file at $source has proper 'success' field in Status ($backendName)." );
00688             if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
00689                 $this->assertHasHeaders( $op['headers'], $attr );
00690             }
00691         } else {
00692             $this->assertEquals( false, $status->isOK(),
00693                 "Describe of file at $source failed ($backendName)." );
00694         }
00695 
00696         $this->assertBackendPathsConsistent( array( $source ) );
00697     }
00698 
00699     private function assertHasHeaders( array $headers, array $attr ) {
00700         foreach ( $headers as $n => $v ) {
00701             if ( $n !== '' ) {
00702                 $this->assertTrue( isset( $attr['headers'][strtolower( $n )] ),
00703                     "File has '$n' header." );
00704                 $this->assertEquals( $v, $attr['headers'][strtolower( $n )],
00705                     "File has '$n' header value." );
00706             } else {
00707                 $this->assertFalse( isset( $attr['headers'][strtolower( $n )] ),
00708                     "File does not have '$n' header." );
00709             }
00710         }
00711     }
00712 
00713     public static function provider_testDescribe() {
00714         $cases = array();
00715 
00716         $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
00717 
00718         $op = array( 'op' => 'describe', 'src' => $source,
00719             'headers' => array( 'Content-Disposition' => 'inline' ), );
00720         $cases[] = array(
00721             $op, // operation
00722             true, // with source
00723             true // succeeds
00724         );
00725 
00726         $cases[] = array(
00727             $op, // operation
00728             false, // without source
00729             false // fails
00730         );
00731 
00732         return $cases;
00733     }
00734 
00739     public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
00740         $this->backend = $this->singleBackend;
00741         $this->tearDownFiles();
00742         $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
00743         $this->tearDownFiles();
00744 
00745         $this->backend = $this->multiBackend;
00746         $this->tearDownFiles();
00747         $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
00748         $this->tearDownFiles();
00749     }
00750 
00751     private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
00752         $backendName = $this->backendClass();
00753 
00754         $dest = $op['dst'];
00755         $this->prepare( array( 'dir' => dirname( $dest ) ) );
00756 
00757         $oldText = 'blah...blah...waahwaah';
00758         if ( $alreadyExists ) {
00759             $status = $this->backend->doOperation(
00760                 array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
00761             $this->assertGoodStatus( $status,
00762                 "Creation of file at $dest succeeded ($backendName)." );
00763         }
00764 
00765         $status = $this->backend->doOperation( $op );
00766         if ( $okStatus ) {
00767             $this->assertGoodStatus( $status,
00768                 "Creation of file at $dest succeeded without warnings ($backendName)." );
00769             $this->assertEquals( true, $status->isOK(),
00770                 "Creation of file at $dest succeeded ($backendName)." );
00771             $this->assertEquals( array( 0 => true ), $status->success,
00772                 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
00773         } else {
00774             $this->assertEquals( false, $status->isOK(),
00775                 "Creation of file at $dest failed ($backendName)." );
00776         }
00777 
00778         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
00779             "Destination file $dest exists after creation ($backendName)." );
00780 
00781         $props1 = $this->backend->getFileProps( array( 'src' => $dest ) );
00782         $this->assertEquals( true, $props1['fileExists'],
00783             "Destination file $dest exists according to props ($backendName)." );
00784         if ( $okStatus ) { // file content is what we saved
00785             $this->assertEquals( $newSize, $props1['size'],
00786                 "Destination file $dest has expected size according to props ($backendName)." );
00787             $this->assertEquals( $newSize,
00788                 $this->backend->getFileSize( array( 'src' => $dest ) ),
00789                 "Destination file $dest has correct size ($backendName)." );
00790         } else { // file content is some other previous text
00791             $this->assertEquals( strlen( $oldText ), $props1['size'],
00792                 "Destination file $dest has original size according to props ($backendName)." );
00793             $this->assertEquals( strlen( $oldText ),
00794                 $this->backend->getFileSize( array( 'src' => $dest ) ),
00795                 "Destination file $dest has original size according to props ($backendName)." );
00796         }
00797 
00798         $this->assertBackendPathsConsistent( array( $dest ) );
00799     }
00800 
00804     public static function provider_testCreate() {
00805         $cases = array();
00806 
00807         $dest = self::baseStorePath() . '/unittest-cont2/a/myspacefile.txt';
00808 
00809         $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
00810         $cases[] = array(
00811             $op, // operation
00812             false, // no dest already exists
00813             true, // succeeds
00814             strlen( $op['content'] )
00815         );
00816 
00817         $op2 = $op;
00818         $op2['content'] = "\n";
00819         $cases[] = array(
00820             $op2, // operation
00821             false, // no dest already exists
00822             true, // succeeds
00823             strlen( $op2['content'] )
00824         );
00825 
00826         $op2 = $op;
00827         $op2['content'] = "fsf\n waf 3kt";
00828         $cases[] = array(
00829             $op2, // operation
00830             true, // dest already exists
00831             false, // fails
00832             strlen( $op2['content'] )
00833         );
00834 
00835         $op2 = $op;
00836         $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
00837         $op2['overwrite'] = true;
00838         $cases[] = array(
00839             $op2, // operation
00840             true, // dest already exists
00841             true, // succeeds
00842             strlen( $op2['content'] )
00843         );
00844 
00845         $op2 = $op;
00846         $op2['content'] = "39qjmg3-qg";
00847         $op2['overwriteSame'] = true;
00848         $cases[] = array(
00849             $op2, // operation
00850             true, // dest already exists
00851             false, // succeeds
00852             strlen( $op2['content'] )
00853         );
00854 
00855         return $cases;
00856     }
00857 
00861     public function testDoQuickOperations() {
00862         $this->backend = $this->singleBackend;
00863         $this->doTestDoQuickOperations();
00864         $this->tearDownFiles();
00865 
00866         $this->backend = $this->multiBackend;
00867         $this->doTestDoQuickOperations();
00868         $this->tearDownFiles();
00869     }
00870 
00871     private function doTestDoQuickOperations() {
00872         $backendName = $this->backendClass();
00873 
00874         $base = self::baseStorePath();
00875         $files = array(
00876             "$base/unittest-cont1/e/fileA.a",
00877             "$base/unittest-cont1/e/fileB.a",
00878             "$base/unittest-cont1/e/fileC.a"
00879         );
00880         $createOps = array();
00881         $purgeOps = array();
00882         foreach ( $files as $path ) {
00883             $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
00884             $this->assertGoodStatus( $status,
00885                 "Preparing $path succeeded without warnings ($backendName)." );
00886             $createOps[] = array( 'op' => 'create', 'dst' => $path, 'content' => mt_rand( 0, 50000 ) );
00887             $copyOps[] = array( 'op' => 'copy', 'src' => $path, 'dst' => "$path-2" );
00888             $moveOps[] = array( 'op' => 'move', 'src' => "$path-2", 'dst' => "$path-3" );
00889             $purgeOps[] = array( 'op' => 'delete', 'src' => $path );
00890             $purgeOps[] = array( 'op' => 'delete', 'src' => "$path-3" );
00891         }
00892         $purgeOps[] = array( 'op' => 'null' );
00893 
00894         $this->assertGoodStatus(
00895             $this->backend->doQuickOperations( $createOps ),
00896             "Creation of source files succeeded ($backendName)." );
00897         foreach ( $files as $file ) {
00898             $this->assertTrue( $this->backend->fileExists( array( 'src' => $file ) ),
00899                 "File $file exists." );
00900         }
00901 
00902         $this->assertGoodStatus(
00903             $this->backend->doQuickOperations( $copyOps ),
00904             "Quick copy of source files succeeded ($backendName)." );
00905         foreach ( $files as $file ) {
00906             $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
00907                 "File $file-2 exists." );
00908         }
00909 
00910         $this->assertGoodStatus(
00911             $this->backend->doQuickOperations( $moveOps ),
00912             "Quick move of source files succeeded ($backendName)." );
00913         foreach ( $files as $file ) {
00914             $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
00915                 "File $file-3 move in." );
00916             $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
00917                 "File $file-2 moved away." );
00918         }
00919 
00920         $this->assertGoodStatus(
00921             $this->backend->quickCopy( array( 'src' => $files[0], 'dst' => $files[0] ) ),
00922             "Copy of file {$files[0]} over itself succeeded ($backendName)." );
00923         $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
00924             "File {$files[0]} still exists." );
00925 
00926         $this->assertGoodStatus(
00927             $this->backend->quickMove( array( 'src' => $files[0], 'dst' => $files[0] ) ),
00928             "Move of file {$files[0]} over itself succeeded ($backendName)." );
00929         $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
00930             "File {$files[0]} still exists." );
00931 
00932         $this->assertGoodStatus(
00933             $this->backend->doQuickOperations( $purgeOps ),
00934             "Quick deletion of source files succeeded ($backendName)." );
00935         foreach ( $files as $file ) {
00936             $this->assertFalse( $this->backend->fileExists( array( 'src' => $file ) ),
00937                 "File $file purged." );
00938             $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
00939                 "File $file-3 purged." );
00940         }
00941     }
00942 
00946     public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
00947         $this->filesToPrune[] = $op['dst'];
00948 
00949         $this->backend = $this->singleBackend;
00950         $this->tearDownFiles();
00951         $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
00952         $this->filesToPrune[] = $op['dst']; # avoid file leaking
00953         $this->tearDownFiles();
00954 
00955         $this->backend = $this->multiBackend;
00956         $this->tearDownFiles();
00957         $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
00958         $this->filesToPrune[] = $op['dst']; # avoid file leaking
00959         $this->tearDownFiles();
00960     }
00961 
00962     private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
00963         $backendName = $this->backendClass();
00964 
00965         $expContent = '';
00966         // Create sources
00967         $ops = array();
00968         foreach ( $srcs as $i => $source ) {
00969             $this->prepare( array( 'dir' => dirname( $source ) ) );
00970             $ops[] = array(
00971                 'op' => 'create', // operation
00972                 'dst' => $source, // source
00973                 'content' => $srcsContent[$i]
00974             );
00975             $expContent .= $srcsContent[$i];
00976         }
00977         $status = $this->backend->doOperations( $ops );
00978 
00979         $this->assertGoodStatus( $status,
00980             "Creation of source files succeeded ($backendName)." );
00981 
00982         $dest = $params['dst'];
00983         if ( $alreadyExists ) {
00984             $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
00985             $this->assertEquals( true, $ok,
00986                 "Creation of file at $dest succeeded ($backendName)." );
00987         } else {
00988             $ok = file_put_contents( $dest, '' ) !== false;
00989             $this->assertEquals( true, $ok,
00990                 "Creation of 0-byte file at $dest succeeded ($backendName)." );
00991         }
00992 
00993         // Combine the files into one
00994         $status = $this->backend->concatenate( $params );
00995         if ( $okStatus ) {
00996             $this->assertGoodStatus( $status,
00997                 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
00998             $this->assertEquals( true, $status->isOK(),
00999                 "Creation of concat file at $dest succeeded ($backendName)." );
01000         } else {
01001             $this->assertEquals( false, $status->isOK(),
01002                 "Creation of concat file at $dest failed ($backendName)." );
01003         }
01004 
01005         if ( $okStatus ) {
01006             $this->assertEquals( true, is_file( $dest ),
01007                 "Dest concat file $dest exists after creation ($backendName)." );
01008         } else {
01009             $this->assertEquals( true, is_file( $dest ),
01010                 "Dest concat file $dest exists after failed creation ($backendName)." );
01011         }
01012 
01013         $contents = file_get_contents( $dest );
01014         $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
01015 
01016         if ( $okStatus ) {
01017             $this->assertEquals( $expContent, $contents,
01018                 "Concat file at $dest has correct contents ($backendName)." );
01019         } else {
01020             $this->assertNotEquals( $expContent, $contents,
01021                 "Concat file at $dest has correct contents ($backendName)." );
01022         }
01023     }
01024 
01025     public static function provider_testConcatenate() {
01026         $cases = array();
01027 
01028         $rand = mt_rand( 0, 2000000000 ) . time();
01029         $dest = wfTempDir() . "/randomfile!$rand.txt";
01030         $srcs = array(
01031             self::baseStorePath() . '/unittest-cont1/e/file1.txt',
01032             self::baseStorePath() . '/unittest-cont1/e/file2.txt',
01033             self::baseStorePath() . '/unittest-cont1/e/file3.txt',
01034             self::baseStorePath() . '/unittest-cont1/e/file4.txt',
01035             self::baseStorePath() . '/unittest-cont1/e/file5.txt',
01036             self::baseStorePath() . '/unittest-cont1/e/file6.txt',
01037             self::baseStorePath() . '/unittest-cont1/e/file7.txt',
01038             self::baseStorePath() . '/unittest-cont1/e/file8.txt',
01039             self::baseStorePath() . '/unittest-cont1/e/file9.txt',
01040             self::baseStorePath() . '/unittest-cont1/e/file10.txt'
01041         );
01042         $content = array(
01043             'egfage',
01044             'ageageag',
01045             'rhokohlr',
01046             'shgmslkg',
01047             'kenga',
01048             'owagmal',
01049             'kgmae',
01050             'g eak;g',
01051             'lkaem;a',
01052             'legma'
01053         );
01054         $params = array( 'srcs' => $srcs, 'dst' => $dest );
01055 
01056         $cases[] = array(
01057             $params, // operation
01058             $srcs, // sources
01059             $content, // content for each source
01060             false, // no dest already exists
01061             true, // succeeds
01062         );
01063 
01064         $cases[] = array(
01065             $params, // operation
01066             $srcs, // sources
01067             $content, // content for each source
01068             true, // dest already exists
01069             false, // succeeds
01070         );
01071 
01072         return $cases;
01073     }
01074 
01079     public function testGetFileStat( $path, $content, $alreadyExists ) {
01080         $this->backend = $this->singleBackend;
01081         $this->tearDownFiles();
01082         $this->doTestGetFileStat( $path, $content, $alreadyExists );
01083         $this->tearDownFiles();
01084 
01085         $this->backend = $this->multiBackend;
01086         $this->tearDownFiles();
01087         $this->doTestGetFileStat( $path, $content, $alreadyExists );
01088         $this->tearDownFiles();
01089     }
01090 
01091     private function doTestGetFileStat( $path, $content, $alreadyExists ) {
01092         $backendName = $this->backendClass();
01093 
01094         if ( $alreadyExists ) {
01095             $this->prepare( array( 'dir' => dirname( $path ) ) );
01096             $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
01097             $this->assertGoodStatus( $status,
01098                 "Creation of file at $path succeeded ($backendName)." );
01099 
01100             $size = $this->backend->getFileSize( array( 'src' => $path ) );
01101             $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
01102             $stat = $this->backend->getFileStat( array( 'src' => $path ) );
01103 
01104             $this->assertEquals( strlen( $content ), $size,
01105                 "Correct file size of '$path'" );
01106             $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
01107                 "Correct file timestamp of '$path'" );
01108 
01109             $size = $stat['size'];
01110             $time = $stat['mtime'];
01111             $this->assertEquals( strlen( $content ), $size,
01112                 "Correct file size of '$path'" );
01113             $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
01114                 "Correct file timestamp of '$path'" );
01115 
01116             $this->backend->clearCache( array( $path ) );
01117 
01118             $size = $this->backend->getFileSize( array( 'src' => $path ) );
01119 
01120             $this->assertEquals( strlen( $content ), $size,
01121                 "Correct file size of '$path'" );
01122 
01123             $this->backend->preloadCache( array( $path ) );
01124 
01125             $size = $this->backend->getFileSize( array( 'src' => $path ) );
01126 
01127             $this->assertEquals( strlen( $content ), $size,
01128                 "Correct file size of '$path'" );
01129         } else {
01130             $size = $this->backend->getFileSize( array( 'src' => $path ) );
01131             $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
01132             $stat = $this->backend->getFileStat( array( 'src' => $path ) );
01133 
01134             $this->assertFalse( $size, "Correct file size of '$path'" );
01135             $this->assertFalse( $time, "Correct file timestamp of '$path'" );
01136             $this->assertFalse( $stat, "Correct file stat of '$path'" );
01137         }
01138     }
01139 
01140     public static function provider_testGetFileStat() {
01141         $cases = array();
01142 
01143         $base = self::baseStorePath();
01144         $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents", true );
01145         $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "", true );
01146         $cases[] = array( "$base/unittest-cont1/e/b/some-diff_file.txt", null, false );
01147 
01148         return $cases;
01149     }
01150 
01155     public function testStreamFile( $path, $content, $alreadyExists ) {
01156         $this->backend = $this->singleBackend;
01157         $this->tearDownFiles();
01158         $this->doTestStreamFile( $path, $content, $alreadyExists );
01159         $this->tearDownFiles();
01160     }
01161 
01162     private function doTestStreamFile( $path, $content ) {
01163         $backendName = $this->backendClass();
01164 
01165         // Test doStreamFile() directly to avoid header madness
01166         $class = new ReflectionClass( $this->backend );
01167         $method = $class->getMethod( 'doStreamFile' );
01168         $method->setAccessible( true );
01169 
01170         if ( $content !== null ) {
01171             $this->prepare( array( 'dir' => dirname( $path ) ) );
01172             $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
01173             $this->assertGoodStatus( $status,
01174                 "Creation of file at $path succeeded ($backendName)." );
01175 
01176             ob_start();
01177             $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
01178             $data = ob_get_contents();
01179             ob_end_clean();
01180 
01181             $this->assertEquals( $content, $data, "Correct content streamed from '$path'" );
01182         } else { // 404 case
01183             ob_start();
01184             $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
01185             $data = ob_get_contents();
01186             ob_end_clean();
01187 
01188             $this->assertEquals( '', $data, "Correct content streamed from '$path' ($backendName)" );
01189         }
01190     }
01191 
01192     public static function provider_testStreamFile() {
01193         $cases = array();
01194 
01195         $base = self::baseStorePath();
01196         $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
01197         $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", null );
01198 
01199         return $cases;
01200     }
01201 
01207     public function testGetFileContents( $source, $content ) {
01208         $this->backend = $this->singleBackend;
01209         $this->tearDownFiles();
01210         $this->doTestGetFileContents( $source, $content );
01211         $this->tearDownFiles();
01212 
01213         $this->backend = $this->multiBackend;
01214         $this->tearDownFiles();
01215         $this->doTestGetFileContents( $source, $content );
01216         $this->tearDownFiles();
01217     }
01218 
01219     private function doTestGetFileContents( $source, $content ) {
01220         $backendName = $this->backendClass();
01221 
01222         $srcs = (array)$source;
01223         $content = (array)$content;
01224         foreach ( $srcs as $i => $src ) {
01225             $this->prepare( array( 'dir' => dirname( $src ) ) );
01226             $status = $this->backend->doOperation(
01227                 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
01228             $this->assertGoodStatus( $status,
01229                 "Creation of file at $src succeeded ($backendName)." );
01230         }
01231 
01232         if ( is_array( $source ) ) {
01233             $contents = $this->backend->getFileContentsMulti( array( 'srcs' => $source ) );
01234             foreach ( $contents as $path => $data ) {
01235                 $this->assertNotEquals( false, $data, "Contents of $path exists ($backendName)." );
01236                 $this->assertEquals( current( $content ), $data, "Contents of $path is correct ($backendName)." );
01237                 next( $content );
01238             }
01239             $this->assertEquals( $source, array_keys( $contents ), "Contents in right order ($backendName)." );
01240             $this->assertEquals( count( $source ), count( $contents ), "Contents array size correct ($backendName)." );
01241         } else {
01242             $data = $this->backend->getFileContents( array( 'src' => $source ) );
01243             $this->assertNotEquals( false, $data, "Contents of $source exists ($backendName)." );
01244             $this->assertEquals( $content[0], $data, "Contents of $source is correct ($backendName)." );
01245         }
01246     }
01247 
01248     public static function provider_testGetFileContents() {
01249         $cases = array();
01250 
01251         $base = self::baseStorePath();
01252         $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
01253         $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "more file contents" );
01254         $cases[] = array(
01255             array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
01256                 "$base/unittest-cont1/e/a/z.txt" ),
01257             array( "contents xx", "contents xy", "contents xz" )
01258         );
01259 
01260         return $cases;
01261     }
01262 
01267     public function testGetLocalCopy( $source, $content ) {
01268         $this->backend = $this->singleBackend;
01269         $this->tearDownFiles();
01270         $this->doTestGetLocalCopy( $source, $content );
01271         $this->tearDownFiles();
01272 
01273         $this->backend = $this->multiBackend;
01274         $this->tearDownFiles();
01275         $this->doTestGetLocalCopy( $source, $content );
01276         $this->tearDownFiles();
01277     }
01278 
01279     private function doTestGetLocalCopy( $source, $content ) {
01280         $backendName = $this->backendClass();
01281 
01282         $srcs = (array)$source;
01283         $content = (array)$content;
01284         foreach ( $srcs as $i => $src ) {
01285             $this->prepare( array( 'dir' => dirname( $src ) ) );
01286             $status = $this->backend->doOperation(
01287                 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
01288             $this->assertGoodStatus( $status,
01289                 "Creation of file at $src succeeded ($backendName)." );
01290         }
01291 
01292         if ( is_array( $source ) ) {
01293             $tmpFiles = $this->backend->getLocalCopyMulti( array( 'srcs' => $source ) );
01294             foreach ( $tmpFiles as $path => $tmpFile ) {
01295                 $this->assertNotNull( $tmpFile,
01296                     "Creation of local copy of $path succeeded ($backendName)." );
01297                 $contents = file_get_contents( $tmpFile->getPath() );
01298                 $this->assertNotEquals( false, $contents, "Local copy of $path exists ($backendName)." );
01299                 $this->assertEquals( current( $content ), $contents, "Local copy of $path is correct ($backendName)." );
01300                 next( $content );
01301             }
01302             $this->assertEquals( $source, array_keys( $tmpFiles ), "Local copies in right order ($backendName)." );
01303             $this->assertEquals( count( $source ), count( $tmpFiles ), "Local copies array size correct ($backendName)." );
01304         } else {
01305             $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
01306             $this->assertNotNull( $tmpFile,
01307                 "Creation of local copy of $source succeeded ($backendName)." );
01308             $contents = file_get_contents( $tmpFile->getPath() );
01309             $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
01310             $this->assertEquals( $content[0], $contents, "Local copy of $source is correct ($backendName)." );
01311         }
01312 
01313         $obj = new stdClass();
01314         $tmpFile->bind( $obj );
01315     }
01316 
01317     public static function provider_testGetLocalCopy() {
01318         $cases = array();
01319 
01320         $base = self::baseStorePath();
01321         $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
01322         $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
01323         $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
01324         $cases[] = array(
01325             array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
01326                 "$base/unittest-cont1/e/a/z.txt" ),
01327             array( "contents xx $", "contents xy 111", "contents xz" )
01328         );
01329 
01330         return $cases;
01331     }
01332 
01337     public function testGetLocalReference( $source, $content ) {
01338         $this->backend = $this->singleBackend;
01339         $this->tearDownFiles();
01340         $this->doTestGetLocalReference( $source, $content );
01341         $this->tearDownFiles();
01342 
01343         $this->backend = $this->multiBackend;
01344         $this->tearDownFiles();
01345         $this->doTestGetLocalReference( $source, $content );
01346         $this->tearDownFiles();
01347     }
01348 
01349     private function doTestGetLocalReference( $source, $content ) {
01350         $backendName = $this->backendClass();
01351 
01352         $srcs = (array)$source;
01353         $content = (array)$content;
01354         foreach ( $srcs as $i => $src ) {
01355             $this->prepare( array( 'dir' => dirname( $src ) ) );
01356             $status = $this->backend->doOperation(
01357                 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
01358             $this->assertGoodStatus( $status,
01359                 "Creation of file at $src succeeded ($backendName)." );
01360         }
01361 
01362         if ( is_array( $source ) ) {
01363             $tmpFiles = $this->backend->getLocalReferenceMulti( array( 'srcs' => $source ) );
01364             foreach ( $tmpFiles as $path => $tmpFile ) {
01365                 $this->assertNotNull( $tmpFile,
01366                     "Creation of local copy of $path succeeded ($backendName)." );
01367                 $contents = file_get_contents( $tmpFile->getPath() );
01368                 $this->assertNotEquals( false, $contents, "Local ref of $path exists ($backendName)." );
01369                 $this->assertEquals( current( $content ), $contents, "Local ref of $path is correct ($backendName)." );
01370                 next( $content );
01371             }
01372             $this->assertEquals( $source, array_keys( $tmpFiles ), "Local refs in right order ($backendName)." );
01373             $this->assertEquals( count( $source ), count( $tmpFiles ), "Local refs array size correct ($backendName)." );
01374         } else {
01375             $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
01376             $this->assertNotNull( $tmpFile,
01377                 "Creation of local copy of $source succeeded ($backendName)." );
01378             $contents = file_get_contents( $tmpFile->getPath() );
01379             $this->assertNotEquals( false, $contents, "Local ref of $source exists ($backendName)." );
01380             $this->assertEquals( $content[0], $contents, "Local ref of $source is correct ($backendName)." );
01381         }
01382     }
01383 
01384     public static function provider_testGetLocalReference() {
01385         $cases = array();
01386 
01387         $base = self::baseStorePath();
01388         $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
01389         $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
01390         $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
01391         $cases[] = array(
01392             array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
01393                 "$base/unittest-cont1/e/a/z.txt" ),
01394             array( "contents xx 1111", "contents xy %", "contents xz $" )
01395         );
01396 
01397         return $cases;
01398     }
01399 
01404     public function testGetLocalCopyAndReference404() {
01405         $this->backend = $this->singleBackend;
01406         $this->tearDownFiles();
01407         $this->doTestGetLocalCopyAndReference404();
01408         $this->tearDownFiles();
01409 
01410         $this->backend = $this->multiBackend;
01411         $this->tearDownFiles();
01412         $this->doTestGetLocalCopyAndReference404();
01413         $this->tearDownFiles();
01414     }
01415 
01416     public function doTestGetLocalCopyAndReference404() {
01417         $backendName = $this->backendClass();
01418 
01419         $base = self::baseStorePath();
01420 
01421         $tmpFile = $this->backend->getLocalCopy( array(
01422             'src' => "$base/unittest-cont1/not-there" ) );
01423         $this->assertEquals( null, $tmpFile, "Local copy of not existing file is null ($backendName)." );
01424 
01425         $tmpFile = $this->backend->getLocalReference( array(
01426             'src' => "$base/unittest-cont1/not-there" ) );
01427         $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
01428     }
01429 
01434     public function testGetFileHttpUrl( $source, $content ) {
01435         $this->backend = $this->singleBackend;
01436         $this->tearDownFiles();
01437         $this->doTestGetFileHttpUrl( $source, $content );
01438         $this->tearDownFiles();
01439 
01440         $this->backend = $this->multiBackend;
01441         $this->tearDownFiles();
01442         $this->doTestGetFileHttpUrl( $source, $content );
01443         $this->tearDownFiles();
01444     }
01445 
01446     private function doTestGetFileHttpUrl( $source, $content ) {
01447         $backendName = $this->backendClass();
01448 
01449         $this->prepare( array( 'dir' => dirname( $source ) ) );
01450         $status = $this->backend->doOperation(
01451             array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
01452         $this->assertGoodStatus( $status,
01453             "Creation of file at $source succeeded ($backendName)." );
01454 
01455         $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) );
01456 
01457         if ( $url !== null ) { // supported
01458             $data = Http::request( "GET", $url );
01459             $this->assertEquals( $content, $data,
01460                 "HTTP GET of URL has right contents ($backendName)." );
01461         }
01462     }
01463 
01464     public static function provider_testGetFileHttpUrl() {
01465         $cases = array();
01466 
01467         $base = self::baseStorePath();
01468         $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
01469         $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
01470         $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
01471 
01472         return $cases;
01473     }
01474 
01480     public function testPrepareAndClean( $path, $isOK ) {
01481         $this->backend = $this->singleBackend;
01482         $this->doTestPrepareAndClean( $path, $isOK );
01483         $this->tearDownFiles();
01484 
01485         $this->backend = $this->multiBackend;
01486         $this->doTestPrepareAndClean( $path, $isOK );
01487         $this->tearDownFiles();
01488     }
01489 
01490     public static function provider_testPrepareAndClean() {
01491         $base = self::baseStorePath();
01492 
01493         return array(
01494             array( "$base/unittest-cont1/e/a/z/some_file1.txt", true ),
01495             array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
01496             # Specific to FS backend with no basePath field set
01497             #array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
01498         );
01499     }
01500 
01501     private function doTestPrepareAndClean( $path, $isOK ) {
01502         $backendName = $this->backendClass();
01503 
01504         $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
01505         if ( $isOK ) {
01506             $this->assertGoodStatus( $status,
01507                 "Preparing dir $path succeeded without warnings ($backendName)." );
01508             $this->assertEquals( true, $status->isOK(),
01509                 "Preparing dir $path succeeded ($backendName)." );
01510         } else {
01511             $this->assertEquals( false, $status->isOK(),
01512                 "Preparing dir $path failed ($backendName)." );
01513         }
01514 
01515         $status = $this->backend->secure( array( 'dir' => dirname( $path ) ) );
01516         if ( $isOK ) {
01517             $this->assertGoodStatus( $status,
01518                 "Securing dir $path succeeded without warnings ($backendName)." );
01519             $this->assertEquals( true, $status->isOK(),
01520                 "Securing dir $path succeeded ($backendName)." );
01521         } else {
01522             $this->assertEquals( false, $status->isOK(),
01523                 "Securing dir $path failed ($backendName)." );
01524         }
01525 
01526         $status = $this->backend->publish( array( 'dir' => dirname( $path ) ) );
01527         if ( $isOK ) {
01528             $this->assertGoodStatus( $status,
01529                 "Publishing dir $path succeeded without warnings ($backendName)." );
01530             $this->assertEquals( true, $status->isOK(),
01531                 "Publishing dir $path succeeded ($backendName)." );
01532         } else {
01533             $this->assertEquals( false, $status->isOK(),
01534                 "Publishing dir $path failed ($backendName)." );
01535         }
01536 
01537         $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
01538         if ( $isOK ) {
01539             $this->assertGoodStatus( $status,
01540                 "Cleaning dir $path succeeded without warnings ($backendName)." );
01541             $this->assertEquals( true, $status->isOK(),
01542                 "Cleaning dir $path succeeded ($backendName)." );
01543         } else {
01544             $this->assertEquals( false, $status->isOK(),
01545                 "Cleaning dir $path failed ($backendName)." );
01546         }
01547     }
01548 
01549     public function testRecursiveClean() {
01550         $this->backend = $this->singleBackend;
01551         $this->doTestRecursiveClean();
01552         $this->tearDownFiles();
01553 
01554         $this->backend = $this->multiBackend;
01555         $this->doTestRecursiveClean();
01556         $this->tearDownFiles();
01557     }
01558 
01562     private function doTestRecursiveClean() {
01563         $backendName = $this->backendClass();
01564 
01565         $base = self::baseStorePath();
01566         $dirs = array(
01567             "$base/unittest-cont1",
01568             "$base/unittest-cont1/e",
01569             "$base/unittest-cont1/e/a",
01570             "$base/unittest-cont1/e/a/b",
01571             "$base/unittest-cont1/e/a/b/c",
01572             "$base/unittest-cont1/e/a/b/c/d0",
01573             "$base/unittest-cont1/e/a/b/c/d1",
01574             "$base/unittest-cont1/e/a/b/c/d2",
01575             "$base/unittest-cont1/e/a/b/c/d0/1",
01576             "$base/unittest-cont1/e/a/b/c/d0/2",
01577             "$base/unittest-cont1/e/a/b/c/d1/3",
01578             "$base/unittest-cont1/e/a/b/c/d1/4",
01579             "$base/unittest-cont1/e/a/b/c/d2/5",
01580             "$base/unittest-cont1/e/a/b/c/d2/6"
01581         );
01582         foreach ( $dirs as $dir ) {
01583             $status = $this->prepare( array( 'dir' => $dir ) );
01584             $this->assertGoodStatus( $status,
01585                 "Preparing dir $dir succeeded without warnings ($backendName)." );
01586         }
01587 
01588         if ( $this->backend instanceof FSFileBackend ) {
01589             foreach ( $dirs as $dir ) {
01590                 $this->assertEquals( true, $this->backend->directoryExists( array( 'dir' => $dir ) ),
01591                     "Dir $dir exists ($backendName)." );
01592             }
01593         }
01594 
01595         $status = $this->backend->clean(
01596             array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
01597         $this->assertGoodStatus( $status,
01598             "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
01599 
01600         foreach ( $dirs as $dir ) {
01601             $this->assertEquals( false, $this->backend->directoryExists( array( 'dir' => $dir ) ),
01602                 "Dir $dir no longer exists ($backendName)." );
01603         }
01604     }
01605 
01609     public function testDoOperations() {
01610         $this->backend = $this->singleBackend;
01611         $this->tearDownFiles();
01612         $this->doTestDoOperations();
01613         $this->tearDownFiles();
01614 
01615         $this->backend = $this->multiBackend;
01616         $this->tearDownFiles();
01617         $this->doTestDoOperations();
01618         $this->tearDownFiles();
01619     }
01620 
01621     private function doTestDoOperations() {
01622         $base = self::baseStorePath();
01623 
01624         $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
01625         $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
01626         $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
01627         $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
01628         $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
01629         $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
01630         $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
01631 
01632         $this->prepare( array( 'dir' => dirname( $fileA ) ) );
01633         $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
01634         $this->prepare( array( 'dir' => dirname( $fileB ) ) );
01635         $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
01636         $this->prepare( array( 'dir' => dirname( $fileC ) ) );
01637         $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
01638         $this->prepare( array( 'dir' => dirname( $fileD ) ) );
01639 
01640         $status = $this->backend->doOperations( array(
01641             array( 'op' => 'describe', 'src' => $fileA,
01642                 'headers' => array( 'X-Content-Length' => '91.3' ), 'disposition' => 'inline' ),
01643             array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
01644             // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
01645             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
01646             // Now: A:<A>, B:<B>, C:<A>, D:<empty>
01647             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
01648             // Now: A:<A>, B:<B>, C:<empty>, D:<A>
01649             array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
01650             // Now: A:<A>, B:<empty>, C:<B>, D:<A>
01651             array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
01652             // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
01653             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
01654             // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
01655             array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
01656             // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
01657             array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
01658             // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
01659             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
01660             // Does nothing
01661             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
01662             // Does nothing
01663             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
01664             // Does nothing
01665             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
01666             // Does nothing
01667             array( 'op' => 'null' ),
01668             // Does nothing
01669         ) );
01670 
01671         $this->assertGoodStatus( $status, "Operation batch succeeded" );
01672         $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
01673         $this->assertEquals( 14, count( $status->success ),
01674             "Operation batch has correct success array" );
01675 
01676         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
01677             "File does not exist at $fileA" );
01678         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
01679             "File does not exist at $fileB" );
01680         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
01681             "File does not exist at $fileD" );
01682 
01683         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
01684             "File exists at $fileC" );
01685         $this->assertEquals( $fileBContents,
01686             $this->backend->getFileContents( array( 'src' => $fileC ) ),
01687             "Correct file contents of $fileC" );
01688         $this->assertEquals( strlen( $fileBContents ),
01689             $this->backend->getFileSize( array( 'src' => $fileC ) ),
01690             "Correct file size of $fileC" );
01691         $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
01692             $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
01693             "Correct file SHA-1 of $fileC" );
01694     }
01695 
01699     public function testDoOperationsPipeline() {
01700         $this->backend = $this->singleBackend;
01701         $this->tearDownFiles();
01702         $this->doTestDoOperationsPipeline();
01703         $this->tearDownFiles();
01704 
01705         $this->backend = $this->multiBackend;
01706         $this->tearDownFiles();
01707         $this->doTestDoOperationsPipeline();
01708         $this->tearDownFiles();
01709     }
01710 
01711     // concurrency orientated
01712     private function doTestDoOperationsPipeline() {
01713         $base = self::baseStorePath();
01714 
01715         $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
01716         $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
01717         $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
01718 
01719         $tmpNameA = TempFSFile::factory( "unittests_", 'txt' )->getPath();
01720         file_put_contents( $tmpNameA, $fileAContents );
01721         $tmpNameB = TempFSFile::factory( "unittests_", 'txt' )->getPath();
01722         file_put_contents( $tmpNameB, $fileBContents );
01723         $tmpNameC = TempFSFile::factory( "unittests_", 'txt' )->getPath();
01724         file_put_contents( $tmpNameC, $fileCContents );
01725 
01726         $this->filesToPrune[] = $tmpNameA; # avoid file leaking
01727         $this->filesToPrune[] = $tmpNameB; # avoid file leaking
01728         $this->filesToPrune[] = $tmpNameC; # avoid file leaking
01729 
01730         $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
01731         $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
01732         $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
01733         $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
01734 
01735         $this->prepare( array( 'dir' => dirname( $fileA ) ) );
01736         $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
01737         $this->prepare( array( 'dir' => dirname( $fileB ) ) );
01738         $this->prepare( array( 'dir' => dirname( $fileC ) ) );
01739         $this->prepare( array( 'dir' => dirname( $fileD ) ) );
01740 
01741         $status = $this->backend->doOperations( array(
01742             array( 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ),
01743             array( 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ),
01744             array( 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ),
01745             array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
01746             // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
01747             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
01748             // Now: A:<A>, B:<B>, C:<A>, D:<empty>
01749             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
01750             // Now: A:<A>, B:<B>, C:<empty>, D:<A>
01751             array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
01752             // Now: A:<A>, B:<empty>, C:<B>, D:<A>
01753             array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
01754             // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
01755             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
01756             // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
01757             array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
01758             // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
01759             array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
01760             // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
01761             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
01762             // Does nothing
01763             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
01764             // Does nothing
01765             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
01766             // Does nothing
01767             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
01768             // Does nothing
01769             array( 'op' => 'null' ),
01770             // Does nothing
01771         ) );
01772 
01773         $this->assertGoodStatus( $status, "Operation batch succeeded" );
01774         $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
01775         $this->assertEquals( 16, count( $status->success ),
01776             "Operation batch has correct success array" );
01777 
01778         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
01779             "File does not exist at $fileA" );
01780         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
01781             "File does not exist at $fileB" );
01782         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
01783             "File does not exist at $fileD" );
01784 
01785         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
01786             "File exists at $fileC" );
01787         $this->assertEquals( $fileBContents,
01788             $this->backend->getFileContents( array( 'src' => $fileC ) ),
01789             "Correct file contents of $fileC" );
01790         $this->assertEquals( strlen( $fileBContents ),
01791             $this->backend->getFileSize( array( 'src' => $fileC ) ),
01792             "Correct file size of $fileC" );
01793         $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
01794             $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
01795             "Correct file SHA-1 of $fileC" );
01796     }
01797 
01801     public function testDoOperationsFailing() {
01802         $this->backend = $this->singleBackend;
01803         $this->tearDownFiles();
01804         $this->doTestDoOperationsFailing();
01805         $this->tearDownFiles();
01806 
01807         $this->backend = $this->multiBackend;
01808         $this->tearDownFiles();
01809         $this->doTestDoOperationsFailing();
01810         $this->tearDownFiles();
01811     }
01812 
01813     private function doTestDoOperationsFailing() {
01814         $base = self::baseStorePath();
01815 
01816         $fileA = "$base/unittest-cont2/a/b/fileA.txt";
01817         $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
01818         $fileB = "$base/unittest-cont2/a/b/fileB.txt";
01819         $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
01820         $fileC = "$base/unittest-cont2/a/b/fileC.txt";
01821         $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
01822         $fileD = "$base/unittest-cont2/a/b/fileD.txt";
01823 
01824         $this->prepare( array( 'dir' => dirname( $fileA ) ) );
01825         $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
01826         $this->prepare( array( 'dir' => dirname( $fileB ) ) );
01827         $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
01828         $this->prepare( array( 'dir' => dirname( $fileC ) ) );
01829         $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
01830 
01831         $status = $this->backend->doOperations( array(
01832             array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
01833             // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
01834             array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
01835             // Now: A:<A>, B:<B>, C:<A>, D:<empty>
01836             array( 'op' => 'copy', 'src' => $fileB, 'dst' => $fileD, 'overwrite' => 1 ),
01837             // Now: A:<A>, B:<B>, C:<A>, D:<B>
01838             array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD ),
01839             // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
01840             array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC, 'overwriteSame' => 1 ),
01841             // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
01842             array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileA, 'overwrite' => 1 ),
01843             // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
01844             array( 'op' => 'delete', 'src' => $fileD ),
01845             // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
01846             array( 'op' => 'null' ),
01847             // Does nothing
01848         ), array( 'force' => 1 ) );
01849 
01850         $this->assertNotEquals( array(), $status->errors, "Operation had warnings" );
01851         $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
01852         $this->assertEquals( 8, count( $status->success ),
01853             "Operation batch has correct success array" );
01854 
01855         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
01856             "File does not exist at $fileB" );
01857         $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
01858             "File does not exist at $fileD" );
01859 
01860         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileA ) ),
01861             "File does not exist at $fileA" );
01862         $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
01863             "File exists at $fileC" );
01864         $this->assertEquals( $fileBContents,
01865             $this->backend->getFileContents( array( 'src' => $fileA ) ),
01866             "Correct file contents of $fileA" );
01867         $this->assertEquals( strlen( $fileBContents ),
01868             $this->backend->getFileSize( array( 'src' => $fileA ) ),
01869             "Correct file size of $fileA" );
01870         $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
01871             $this->backend->getFileSha1Base36( array( 'src' => $fileA ) ),
01872             "Correct file SHA-1 of $fileA" );
01873     }
01874 
01878     public function testGetFileList() {
01879         $this->backend = $this->singleBackend;
01880         $this->tearDownFiles();
01881         $this->doTestGetFileList();
01882         $this->tearDownFiles();
01883 
01884         $this->backend = $this->multiBackend;
01885         $this->tearDownFiles();
01886         $this->doTestGetFileList();
01887         $this->tearDownFiles();
01888     }
01889 
01890     private function doTestGetFileList() {
01891         $backendName = $this->backendClass();
01892         $base = self::baseStorePath();
01893 
01894         // Should have no errors
01895         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
01896 
01897         $files = array(
01898             "$base/unittest-cont1/e/test1.txt",
01899             "$base/unittest-cont1/e/test2.txt",
01900             "$base/unittest-cont1/e/test3.txt",
01901             "$base/unittest-cont1/e/subdir1/test1.txt",
01902             "$base/unittest-cont1/e/subdir1/test2.txt",
01903             "$base/unittest-cont1/e/subdir2/test3.txt",
01904             "$base/unittest-cont1/e/subdir2/test4.txt",
01905             "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
01906             "$base/unittest-cont1/e/subdir2/subdir/test2.txt",
01907             "$base/unittest-cont1/e/subdir2/subdir/test3.txt",
01908             "$base/unittest-cont1/e/subdir2/subdir/test4.txt",
01909             "$base/unittest-cont1/e/subdir2/subdir/test5.txt",
01910             "$base/unittest-cont1/e/subdir2/subdir/sub/test0.txt",
01911             "$base/unittest-cont1/e/subdir2/subdir/sub/120-px-file.txt",
01912         );
01913 
01914         // Add the files
01915         $ops = array();
01916         foreach ( $files as $file ) {
01917             $this->prepare( array( 'dir' => dirname( $file ) ) );
01918             $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
01919         }
01920         $status = $this->backend->doQuickOperations( $ops );
01921         $this->assertGoodStatus( $status,
01922             "Creation of files succeeded ($backendName)." );
01923         $this->assertEquals( true, $status->isOK(),
01924             "Creation of files succeeded with OK status ($backendName)." );
01925 
01926         // Expected listing at root
01927         $expected = array(
01928             "e/test1.txt",
01929             "e/test2.txt",
01930             "e/test3.txt",
01931             "e/subdir1/test1.txt",
01932             "e/subdir1/test2.txt",
01933             "e/subdir2/test3.txt",
01934             "e/subdir2/test4.txt",
01935             "e/subdir2/subdir/test1.txt",
01936             "e/subdir2/subdir/test2.txt",
01937             "e/subdir2/subdir/test3.txt",
01938             "e/subdir2/subdir/test4.txt",
01939             "e/subdir2/subdir/test5.txt",
01940             "e/subdir2/subdir/sub/test0.txt",
01941             "e/subdir2/subdir/sub/120-px-file.txt",
01942         );
01943         sort( $expected );
01944 
01945         // Actual listing (no trailing slash) at root
01946         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1" ) );
01947         $list = $this->listToArray( $iter );
01948         sort( $list );
01949         $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01950 
01951         // Actual listing (no trailing slash) at root with advise
01952         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1", 'adviseStat' => 1 ) );
01953         $list = $this->listToArray( $iter );
01954         sort( $list );
01955         $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01956 
01957         // Actual listing (with trailing slash) at root
01958         $list = array();
01959         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/" ) );
01960         foreach ( $iter as $file ) {
01961             $list[] = $file;
01962         }
01963         sort( $list );
01964         $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01965 
01966         // Expected listing at subdir
01967         $expected = array(
01968             "test1.txt",
01969             "test2.txt",
01970             "test3.txt",
01971             "test4.txt",
01972             "test5.txt",
01973             "sub/test0.txt",
01974             "sub/120-px-file.txt",
01975         );
01976         sort( $expected );
01977 
01978         // Actual listing (no trailing slash) at subdir
01979         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
01980         $list = $this->listToArray( $iter );
01981         sort( $list );
01982         $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01983 
01984         // Actual listing (no trailing slash) at subdir with advise
01985         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir", 'adviseStat' => 1 ) );
01986         $list = $this->listToArray( $iter );
01987         sort( $list );
01988         $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01989 
01990         // Actual listing (with trailing slash) at subdir
01991         $list = array();
01992         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir/" ) );
01993         foreach ( $iter as $file ) {
01994             $list[] = $file;
01995         }
01996         sort( $list );
01997         $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
01998 
01999         // Actual listing (using iterator second time)
02000         $list = $this->listToArray( $iter );
02001         sort( $list );
02002         $this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
02003 
02004         // Actual listing (top files only) at root
02005         $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1" ) );
02006         $list = $this->listToArray( $iter );
02007         sort( $list );
02008         $this->assertEquals( array(), $list, "Correct top file listing ($backendName)." );
02009 
02010         // Expected listing (top files only) at subdir
02011         $expected = array(
02012             "test1.txt",
02013             "test2.txt",
02014             "test3.txt",
02015             "test4.txt",
02016             "test5.txt"
02017         );
02018         sort( $expected );
02019 
02020         // Actual listing (top files only) at subdir
02021         $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
02022         $list = $this->listToArray( $iter );
02023         sort( $list );
02024         $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
02025 
02026         // Actual listing (top files only) at subdir with advise
02027         $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir", 'adviseStat' => 1 ) );
02028         $list = $this->listToArray( $iter );
02029         sort( $list );
02030         $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
02031 
02032         foreach ( $files as $file ) { // clean up
02033             $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
02034         }
02035 
02036         $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
02037         foreach ( $iter as $iter ) {
02038             // no errors
02039         }
02040     }
02041 
02046     public function testGetDirectoryList() {
02047         $this->backend = $this->singleBackend;
02048         $this->tearDownFiles();
02049         $this->doTestGetDirectoryList();
02050         $this->tearDownFiles();
02051 
02052         $this->backend = $this->multiBackend;
02053         $this->tearDownFiles();
02054         $this->doTestGetDirectoryList();
02055         $this->tearDownFiles();
02056     }
02057 
02058     private function doTestGetDirectoryList() {
02059         $backendName = $this->backendClass();
02060 
02061         $base = self::baseStorePath();
02062         $files = array(
02063             "$base/unittest-cont1/e/test1.txt",
02064             "$base/unittest-cont1/e/test2.txt",
02065             "$base/unittest-cont1/e/test3.txt",
02066             "$base/unittest-cont1/e/subdir1/test1.txt",
02067             "$base/unittest-cont1/e/subdir1/test2.txt",
02068             "$base/unittest-cont1/e/subdir2/test3.txt",
02069             "$base/unittest-cont1/e/subdir2/test4.txt",
02070             "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
02071             "$base/unittest-cont1/e/subdir3/subdir/test2.txt",
02072             "$base/unittest-cont1/e/subdir4/subdir/test3.txt",
02073             "$base/unittest-cont1/e/subdir4/subdir/test4.txt",
02074             "$base/unittest-cont1/e/subdir4/subdir/test5.txt",
02075             "$base/unittest-cont1/e/subdir4/subdir/sub/test0.txt",
02076             "$base/unittest-cont1/e/subdir4/subdir/sub/120-px-file.txt",
02077         );
02078 
02079         // Add the files
02080         $ops = array();
02081         foreach ( $files as $file ) {
02082             $this->prepare( array( 'dir' => dirname( $file ) ) );
02083             $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
02084         }
02085         $status = $this->backend->doQuickOperations( $ops );
02086         $this->assertGoodStatus( $status,
02087             "Creation of files succeeded ($backendName)." );
02088         $this->assertEquals( true, $status->isOK(),
02089             "Creation of files succeeded with OK status ($backendName)." );
02090 
02091         $this->assertEquals( true,
02092             $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) ),
02093             "Directory exists in ($backendName)." );
02094         $this->assertEquals( true,
02095             $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) ),
02096             "Directory exists in ($backendName)." );
02097         $this->assertEquals( false,
02098             $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/test1.txt" ) ),
02099             "Directory does not exists in ($backendName)." );
02100 
02101         // Expected listing
02102         $expected = array(
02103             "e",
02104         );
02105         sort( $expected );
02106 
02107         // Actual listing (no trailing slash)
02108         $list = array();
02109         $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1" ) );
02110         foreach ( $iter as $file ) {
02111             $list[] = $file;
02112         }
02113         sort( $list );
02114 
02115         $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
02116 
02117         // Expected listing
02118         $expected = array(
02119             "subdir1",
02120             "subdir2",
02121             "subdir3",
02122             "subdir4",
02123         );
02124         sort( $expected );
02125 
02126         // Actual listing (no trailing slash)
02127         $list = array();
02128         $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e" ) );
02129         foreach ( $iter as $file ) {
02130             $list[] = $file;
02131         }
02132         sort( $list );
02133 
02134         $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
02135 
02136         // Actual listing (with trailing slash)
02137         $list = array();
02138         $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/" ) );
02139         foreach ( $iter as $file ) {
02140             $list[] = $file;
02141         }
02142         sort( $list );
02143 
02144         $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
02145 
02146         // Expected listing
02147         $expected = array(
02148             "subdir",
02149         );
02150         sort( $expected );
02151 
02152         // Actual listing (no trailing slash)
02153         $list = array();
02154         $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2" ) );
02155         foreach ( $iter as $file ) {
02156             $list[] = $file;
02157         }
02158         sort( $list );
02159 
02160         $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
02161 
02162         // Actual listing (with trailing slash)
02163         $list = array();
02164         $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2/" ) );
02165         foreach ( $iter as $file ) {
02166             $list[] = $file;
02167         }
02168         sort( $list );
02169 
02170         $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
02171 
02172         // Actual listing (using iterator second time)
02173         $list = array();
02174         foreach ( $iter as $file ) {
02175             $list[] = $file;
02176         }
02177         sort( $list );
02178 
02179         $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName), second iteration." );
02180 
02181         // Expected listing (recursive)
02182         $expected = array(
02183             "e",
02184             "e/subdir1",
02185             "e/subdir2",
02186             "e/subdir3",
02187             "e/subdir4",
02188             "e/subdir2/subdir",
02189             "e/subdir3/subdir",
02190             "e/subdir4/subdir",
02191             "e/subdir4/subdir/sub",
02192         );
02193         sort( $expected );
02194 
02195         // Actual listing (recursive)
02196         $list = array();
02197         $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/" ) );
02198         foreach ( $iter as $file ) {
02199             $list[] = $file;
02200         }
02201         sort( $list );
02202 
02203         $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
02204 
02205         // Expected listing (recursive)
02206         $expected = array(
02207             "subdir",
02208             "subdir/sub",
02209         );
02210         sort( $expected );
02211 
02212         // Actual listing (recursive)
02213         $list = array();
02214         $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir4" ) );
02215         foreach ( $iter as $file ) {
02216             $list[] = $file;
02217         }
02218         sort( $list );
02219 
02220         $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
02221 
02222         // Actual listing (recursive, second time)
02223         $list = array();
02224         foreach ( $iter as $file ) {
02225             $list[] = $file;
02226         }
02227         sort( $list );
02228 
02229         $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
02230 
02231         $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) );
02232         $items = $this->listToArray( $iter );
02233         $this->assertEquals( array(), $items, "Directory listing is empty." );
02234 
02235         foreach ( $files as $file ) { // clean up
02236             $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
02237         }
02238 
02239         $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
02240         foreach ( $iter as $file ) {
02241             // no errors
02242         }
02243 
02244         $items = $this->listToArray( $iter );
02245         $this->assertEquals( array(), $items, "Directory listing is empty." );
02246 
02247         $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/not/exists" ) );
02248         $items = $this->listToArray( $iter );
02249         $this->assertEquals( array(), $items, "Directory listing is empty." );
02250     }
02251 
02256     public function testLockCalls() {
02257         $this->backend = $this->singleBackend;
02258         $this->doTestLockCalls();
02259     }
02260 
02261     private function doTestLockCalls() {
02262         $backendName = $this->backendClass();
02263 
02264         $paths = array(
02265             "test1.txt",
02266             "test2.txt",
02267             "test3.txt",
02268             "subdir1",
02269             "subdir1", // duplicate
02270             "subdir1/test1.txt",
02271             "subdir1/test2.txt",
02272             "subdir2",
02273             "subdir2", // duplicate
02274             "subdir2/test3.txt",
02275             "subdir2/test4.txt",
02276             "subdir2/subdir",
02277             "subdir2/subdir/test1.txt",
02278             "subdir2/subdir/test2.txt",
02279             "subdir2/subdir/test3.txt",
02280             "subdir2/subdir/test4.txt",
02281             "subdir2/subdir/test5.txt",
02282             "subdir2/subdir/sub",
02283             "subdir2/subdir/sub/test0.txt",
02284             "subdir2/subdir/sub/120-px-file.txt",
02285         );
02286 
02287         for ( $i = 0; $i < 25; $i++ ) {
02288             $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
02289             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02290                 "Locking of files succeeded ($backendName) ($i)." );
02291             $this->assertEquals( true, $status->isOK(),
02292                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02293 
02294             $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
02295             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02296                 "Locking of files succeeded ($backendName) ($i)." );
02297             $this->assertEquals( true, $status->isOK(),
02298                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02299 
02300             $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
02301             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02302                 "Locking of files succeeded ($backendName) ($i)." );
02303             $this->assertEquals( true, $status->isOK(),
02304                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02305 
02306             $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
02307             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02308                 "Locking of files succeeded ($backendName). ($i)" );
02309             $this->assertEquals( true, $status->isOK(),
02310                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02311 
02312             ## Flip the acquire/release ordering around ##
02313 
02314             $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
02315             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02316                 "Locking of files succeeded ($backendName) ($i)." );
02317             $this->assertEquals( true, $status->isOK(),
02318                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02319 
02320             $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
02321             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02322                 "Locking of files succeeded ($backendName) ($i)." );
02323             $this->assertEquals( true, $status->isOK(),
02324                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02325 
02326             $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
02327             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02328                 "Locking of files succeeded ($backendName). ($i)" );
02329             $this->assertEquals( true, $status->isOK(),
02330                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02331 
02332             $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
02333             $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
02334                 "Locking of files succeeded ($backendName) ($i)." );
02335             $this->assertEquals( true, $status->isOK(),
02336                 "Locking of files succeeded with OK status ($backendName) ($i)." );
02337         }
02338 
02339         $status = Status::newGood();
02340         $sl = $this->backend->getScopedFileLocks( $paths, LockManager::LOCK_EX, $status );
02341         $this->assertType( 'ScopedLock', $sl,
02342             "Scoped locking of files succeeded ($backendName)." );
02343         $this->assertEquals( array(), $status->errors,
02344             "Scoped locking of files succeeded ($backendName)." );
02345         $this->assertEquals( true, $status->isOK(),
02346             "Scoped locking of files succeeded with OK status ($backendName)." );
02347 
02348         ScopedLock::release( $sl );
02349         $this->assertEquals( null, $sl,
02350             "Scoped unlocking of files succeeded ($backendName)." );
02351         $this->assertEquals( array(), $status->errors,
02352             "Scoped unlocking of files succeeded ($backendName)." );
02353         $this->assertEquals( true, $status->isOK(),
02354             "Scoped unlocking of files succeeded with OK status ($backendName)." );
02355     }
02356 
02357     // helper function
02358     private function listToArray( $iter ) {
02359         return is_array( $iter ) ? $iter : iterator_to_array( $iter );
02360     }
02361 
02362     // test helper wrapper for backend prepare() function
02363     private function prepare( array $params ) {
02364         return $this->backend->prepare( $params );
02365     }
02366 
02367     // test helper wrapper for backend prepare() function
02368     private function create( array $params ) {
02369         $params['op'] = 'create';
02370 
02371         return $this->backend->doQuickOperations( array( $params ) );
02372     }
02373 
02374     function tearDownFiles() {
02375         foreach ( $this->filesToPrune as $file ) {
02376             if ( is_file( $file ) ) {
02377                 unlink( $file );
02378             }
02379         }
02380         $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont-bad' );
02381         foreach ( $containers as $container ) {
02382             $this->deleteFiles( $container );
02383         }
02384         $this->filesToPrune = array();
02385     }
02386 
02387     private function deleteFiles( $container ) {
02388         $base = self::baseStorePath();
02389         $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
02390         if ( $iter ) {
02391             foreach ( $iter as $file ) {
02392                 $this->backend->quickDelete( array( 'src' => "$base/$container/$file" ) );
02393             }
02394             // free the directory, to avoid Permission denied under windows on rmdir
02395             unset( $iter );
02396         }
02397         $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
02398     }
02399 
02400     function assertBackendPathsConsistent( array $paths ) {
02401         if ( $this->backend instanceof FileBackendMultiWrite ) {
02402             $status = $this->backend->consistencyCheck( $paths );
02403             $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
02404         }
02405     }
02406 
02407     function assertGoodStatus( $status, $msg ) {
02408         $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );
02409     }
02410 }