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