Big oops - merged to wrong branch.
[lhc/web/wiklou.git] / tests / phpunit / includes / filerepo / FileBackendTest.php
index af88bc8..710ad83 100644 (file)
@@ -7,7 +7,6 @@
 class FileBackendTest extends MediaWikiTestCase {
        private $backend, $multiBackend;
        private $filesToPrune = array();
-       private $dirsToPrune = array();
        private static $backendToUse;
 
        function setUp() {
@@ -35,6 +34,7 @@ class FileBackendTest extends MediaWikiTestCase {
                        $this->singleBackend = new FSFileBackend( array(
                                'name'        => 'localtesting',
                                'lockManager' => 'fsLockManager',
+                               #'parallelize' => 'implicit',
                                'containerPaths' => array(
                                        'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1",
                                        'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" )
@@ -43,6 +43,7 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->multiBackend = new FileBackendMultiWrite( array(
                        'name'        => 'localtesting',
                        'lockManager' => 'fsLockManager',
+                       'parallelize' => 'implicit',
                        'backends'    => array(
                                array(
                                        'name'          => 'localmutlitesting1',
@@ -205,7 +206,7 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
-       function doTestStore( $op ) {
+       private function doTestStore( $op ) {
                $backendName = $this->backendClass();
 
                $source = $op['src'];
@@ -220,9 +221,9 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $status = $this->backend->doOperation( $op );
 
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Store from $source to $dest succeeded without warnings ($backendName)." );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertEquals( true, $status->isOK(),
                        "Store from $source to $dest succeeded ($backendName)." );
                $this->assertEquals( array( 0 => true ), $status->success,
                        "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
@@ -287,7 +288,7 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
-       function doTestCopy( $op ) {
+       private function doTestCopy( $op ) {
                $backendName = $this->backendClass();
 
                $source = $op['src'];
@@ -297,7 +298,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $status = $this->backend->doOperation(
                        array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Creation of file at $source succeeded ($backendName)." );
 
                if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
@@ -306,7 +307,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $status = $this->backend->doOperation( $op );
 
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Copy from $source to $dest succeeded without warnings ($backendName)." );
                $this->assertEquals( true, $status->isOK(),
                        "Copy from $source to $dest succeeded ($backendName)." );
@@ -385,7 +386,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $status = $this->backend->doOperation(
                        array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Creation of file at $source succeeded ($backendName)." );
 
                if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
@@ -393,7 +394,7 @@ class FileBackendTest extends MediaWikiTestCase {
                }
 
                $status = $this->backend->doOperation( $op );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Move from $source to $dest succeeded without warnings ($backendName)." );
                $this->assertEquals( true, $status->isOK(),
                        "Move from $source to $dest succeeded ($backendName)." );
@@ -473,13 +474,13 @@ class FileBackendTest extends MediaWikiTestCase {
                if ( $withSource ) {
                        $status = $this->backend->doOperation(
                                array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Creation of file at $source succeeded ($backendName)." );
                }
 
                $status = $this->backend->doOperation( $op );
                if ( $okStatus ) {
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Deletion of file at $source succeeded without warnings ($backendName)." );
                        $this->assertEquals( true, $status->isOK(),
                                "Deletion of file at $source succeeded ($backendName)." );
@@ -555,13 +556,13 @@ class FileBackendTest extends MediaWikiTestCase {
                if ( $alreadyExists ) {
                        $status = $this->backend->doOperation(
                                array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Creation of file at $dest succeeded ($backendName)." );
                }
 
                $status = $this->backend->doOperation( $op );
                if ( $okStatus ) {
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Creation of file at $dest succeeded without warnings ($backendName)." );
                        $this->assertEquals( true, $status->isOK(),
                                "Creation of file at $dest succeeded ($backendName)." );
@@ -650,6 +651,54 @@ class FileBackendTest extends MediaWikiTestCase {
                return $cases;
        }
 
+       public function testDoQuickOperations() {
+               $this->backend = $this->singleBackend;
+               $this->doTestDoQuickOperations();
+               $this->tearDownFiles();
+
+               $this->backend = $this->multiBackend;
+               $this->doTestDoQuickOperations();
+               $this->tearDownFiles();
+       }
+
+       private function doTestDoQuickOperations() {
+               $backendName = $this->backendClass();
+
+               $base = $this->baseStorePath();
+               $files = array(
+                       "$base/unittest-cont1/fileA.a",
+                       "$base/unittest-cont1/fileB.a",
+                       "$base/unittest-cont1/fileC.a"
+               );
+               $ops = array();
+               $purgeOps = array();
+               foreach ( $files as $path ) {
+                       $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
+                       $this->assertGoodStatus( $status,
+                               "Preparing $path succeeded without warnings ($backendName)." );
+                       $ops[] = array( 'op' => 'create', 'dst' => $path, 'content' => mt_rand(0,50000) );
+                       $purgeOps[] = array( 'op' => 'delete', 'src' => $path );
+               }
+               $purgeOps[] = array( 'op' => 'null' );
+               $status = $this->backend->doQuickOperations( $ops );
+               $this->assertGoodStatus( $status,
+                       "Creation of source files succeeded ($backendName)." );
+
+               foreach ( $files as $file ) {
+                       $this->assertTrue( $this->backend->fileExists( array( 'src' => $file ) ),
+                               "File $file exists." );
+               }
+
+               $status = $this->backend->doQuickOperations( $purgeOps );
+               $this->assertGoodStatus( $status,
+                       "Quick deletion of source files succeeded ($backendName)." );
+
+               foreach ( $files as $file ) {
+                       $this->assertFalse( $this->backend->fileExists( array( 'src' => $file ) ),
+                               "File $file purged." );
+               }
+       }
+
        /**
         * @dataProvider provider_testConcatenate
         */
@@ -668,7 +717,7 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
-       public function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
+       private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
                $backendName = $this->backendClass();
 
                $expContent = '';
@@ -685,7 +734,7 @@ class FileBackendTest extends MediaWikiTestCase {
                }
                $status = $this->backend->doOperations( $ops );
 
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Creation of source files succeeded ($backendName)." );
 
                $dest = $params['dst'];
@@ -702,7 +751,7 @@ class FileBackendTest extends MediaWikiTestCase {
                // Combine the files into one
                $status = $this->backend->concatenate( $params );
                if ( $okStatus ) {
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Creation of concat file at $dest succeeded without warnings ($backendName)." );
                        $this->assertEquals( true, $status->isOK(),
                                "Creation of concat file at $dest succeeded ($backendName)." );
@@ -801,8 +850,8 @@ class FileBackendTest extends MediaWikiTestCase {
 
                if ( $alreadyExists ) {
                        $this->prepare( array( 'dir' => dirname( $path ) ) );
-                       $status = $this->backend->create( array( 'dst' => $path, 'content' => $content ) );
-                       $this->assertEquals( array(), $status->errors,
+                       $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
+                       $this->assertGoodStatus( $status,
                                "Creation of file at $path succeeded ($backendName)." );
 
                        $size = $this->backend->getFileSize( array( 'src' => $path ) );
@@ -811,14 +860,14 @@ class FileBackendTest extends MediaWikiTestCase {
 
                        $this->assertEquals( strlen( $content ), $size,
                                "Correct file size of '$path'" );
-                       $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 5,
+                       $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
                                "Correct file timestamp of '$path'" );
 
                        $size = $stat['size'];
                        $time = $stat['mtime'];
                        $this->assertEquals( strlen( $content ), $size,
                                "Correct file size of '$path'" );
-                       $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 5,
+                       $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
                                "Correct file timestamp of '$path'" );
                } else {
                        $size = $this->backend->getFileSize( array( 'src' => $path ) );
@@ -857,14 +906,14 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
-       public function doTestGetFileContents( $source, $content ) {
+       private function doTestGetFileContents( $source, $content ) {
                $backendName = $this->backendClass();
 
                $this->prepare( array( 'dir' => dirname( $source ) ) );
 
                $status = $this->backend->doOperation(
                        array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Creation of file at $source succeeded ($backendName)." );
                $this->assertEquals( true, $status->isOK(),
                        "Creation of file at $source succeeded with OK status ($backendName)." );
@@ -902,14 +951,14 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
-       public function doTestGetLocalCopy( $source, $content ) {
+       private function doTestGetLocalCopy( $source, $content ) {
                $backendName = $this->backendClass();
 
                $this->prepare( array( 'dir' => dirname( $source ) ) );
 
                $status = $this->backend->doOperation(
                        array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Creation of file at $source succeeded ($backendName)." );
 
                $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
@@ -950,9 +999,8 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $this->prepare( array( 'dir' => dirname( $source ) ) );
 
-               $status = $this->backend->doOperation(
-                       array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
-               $this->assertEquals( array(), $status->errors,
+               $status = $this->create( array( 'content' => $content, 'dst' => $source ) );
+               $this->assertGoodStatus( $status,
                        "Creation of file at $source succeeded ($backendName)." );
 
                $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
@@ -996,12 +1044,12 @@ class FileBackendTest extends MediaWikiTestCase {
                );
        }
 
-       function doTestPrepareAndClean( $path, $isOK ) {
+       private function doTestPrepareAndClean( $path, $isOK ) {
                $backendName = $this->backendClass();
 
                $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
                if ( $isOK ) {
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Preparing dir $path succeeded without warnings ($backendName)." );
                        $this->assertEquals( true, $status->isOK(),
                                "Preparing dir $path succeeded ($backendName)." );
@@ -1012,7 +1060,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
                if ( $isOK ) {
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Cleaning dir $path succeeded without warnings ($backendName)." );
                        $this->assertEquals( true, $status->isOK(),
                                "Cleaning dir $path succeeded ($backendName)." );
@@ -1032,7 +1080,7 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
-       function doTestRecursiveClean() {
+       private function doTestRecursiveClean() {
                $backendName = $this->backendClass();
 
                $base = $this->baseStorePath();
@@ -1052,7 +1100,7 @@ class FileBackendTest extends MediaWikiTestCase {
                );
                foreach ( $dirs as $dir ) {
                        $status = $this->prepare( array( 'dir' => $dir ) );
-                       $this->assertEquals( array(), $status->errors,
+                       $this->assertGoodStatus( $status,
                                "Preparing dir $dir succeeded without warnings ($backendName)." );
                }
 
@@ -1065,7 +1113,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $status = $this->backend->clean(
                        array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
-               $this->assertEquals( array(), $status->errors,
+               $this->assertGoodStatus( $status,
                        "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
 
                foreach ( $dirs as $dir ) {
@@ -1089,18 +1137,26 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
-               $this->doTestDoOperationsFailing();
+               $this->doTestDoOperations2();
                $this->tearDownFiles();
 
                $this->backend = $this->multiBackend;
                $this->tearDownFiles();
+               $this->doTestDoOperations2();
+               $this->tearDownFiles();
+
+               $this->backend = $this->singleBackend;
+               $this->tearDownFiles();
                $this->doTestDoOperationsFailing();
                $this->tearDownFiles();
 
-               // @TODO: test some cases where the ops should fail
+               $this->backend = $this->multiBackend;
+               $this->tearDownFiles();
+               $this->doTestDoOperationsFailing();
+               $this->tearDownFiles();
        }
 
-       function doTestDoOperations() {
+       private function doTestDoOperations() {
                $base = $this->baseStorePath();
 
                $fileA = "$base/unittest-cont1/a/b/fileA.txt";
@@ -1112,11 +1168,12 @@ class FileBackendTest extends MediaWikiTestCase {
                $fileD = "$base/unittest-cont1/a/b/fileD.txt";
 
                $this->prepare( array( 'dir' => dirname( $fileA ) ) );
-               $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
+               $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
                $this->prepare( array( 'dir' => dirname( $fileB ) ) );
-               $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
+               $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
                $this->prepare( array( 'dir' => dirname( $fileC ) ) );
-               $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
+               $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
+               $this->prepare( array( 'dir' => dirname( $fileD ) ) );
 
                $status = $this->backend->doOperations( array(
                        array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
@@ -1147,7 +1204,7 @@ class FileBackendTest extends MediaWikiTestCase {
                        // Does nothing
                ) );
 
-               $this->assertEquals( array(), $status->errors, "Operation batch succeeded" );
+               $this->assertGoodStatus( $status, "Operation batch succeeded" );
                $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
                $this->assertEquals( 13, count( $status->success ),
                        "Operation batch has correct success array" );
@@ -1172,7 +1229,94 @@ class FileBackendTest extends MediaWikiTestCase {
                        "Correct file SHA-1 of $fileC" );
        }
 
-       function doTestDoOperationsFailing() {
+       // concurrency orientated
+       private function doTestDoOperations2() {
+               $base = $this->baseStorePath();
+
+               $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
+               $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
+               $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
+
+               $tmpNameA = TempFSFile::factory( "unittests_", 'txt' )->getPath();
+               file_put_contents( $tmpNameA, $fileAContents );
+               $tmpNameB = TempFSFile::factory( "unittests_", 'txt' )->getPath();
+               file_put_contents( $tmpNameB, $fileBContents );
+               $tmpNameC = TempFSFile::factory( "unittests_", 'txt' )->getPath();
+               file_put_contents( $tmpNameC, $fileCContents );
+
+               $this->filesToPrune[] = $tmpNameA; # avoid file leaking
+               $this->filesToPrune[] = $tmpNameB; # avoid file leaking
+               $this->filesToPrune[] = $tmpNameC; # avoid file leaking
+
+               $fileA = "$base/unittest-cont1/a/b/fileA.txt";
+               $fileB = "$base/unittest-cont1/a/b/fileB.txt";
+               $fileC = "$base/unittest-cont1/a/b/fileC.txt";
+               $fileD = "$base/unittest-cont1/a/b/fileD.txt";
+
+               $this->prepare( array( 'dir' => dirname( $fileA ) ) );
+               $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
+               $this->prepare( array( 'dir' => dirname( $fileB ) ) );
+               $this->prepare( array( 'dir' => dirname( $fileC ) ) );
+               $this->prepare( array( 'dir' => dirname( $fileD ) ) );
+
+               $status = $this->backend->doOperations( array(
+                       array( 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ),
+                       array( 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ),
+                       array( 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ),
+                       array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
+                       // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
+                       array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
+                       // Now: A:<A>, B:<B>, C:<A>, D:<empty>
+                       array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
+                       // Now: A:<A>, B:<B>, C:<empty>, D:<A>
+                       array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
+                       // Now: A:<A>, B:<empty>, C:<B>, D:<A>
+                       array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
+                       // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
+                       array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
+                       // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
+                       array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
+                       // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
+                       array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
+                       // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
+                       array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
+                       // Does nothing
+                       array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
+                       // Does nothing
+                       array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
+                       // Does nothing
+                       array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
+                       // Does nothing
+                       array( 'op' => 'null' ),
+                       // Does nothing
+               ) );
+
+               $this->assertGoodStatus( $status, "Operation batch succeeded" );
+               $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
+               $this->assertEquals( 16, count( $status->success ),
+                       "Operation batch has correct success array" );
+
+               $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
+                       "File does not exist at $fileA" );
+               $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
+                       "File does not exist at $fileB" );
+               $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
+                       "File does not exist at $fileD" );
+
+               $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
+                       "File exists at $fileC" );
+               $this->assertEquals( $fileBContents,
+                       $this->backend->getFileContents( array( 'src' => $fileC ) ),
+                       "Correct file contents of $fileC" );
+               $this->assertEquals( strlen( $fileBContents ),
+                       $this->backend->getFileSize( array( 'src' => $fileC ) ),
+                       "Correct file size of $fileC" );
+               $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
+                       $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
+                       "Correct file SHA-1 of $fileC" );
+       }
+
+       private function doTestDoOperationsFailing() {
                $base = $this->baseStorePath();
 
                $fileA = "$base/unittest-cont2/a/b/fileA.txt";
@@ -1184,11 +1328,11 @@ class FileBackendTest extends MediaWikiTestCase {
                $fileD = "$base/unittest-cont2/a/b/fileD.txt";
 
                $this->prepare( array( 'dir' => dirname( $fileA ) ) );
-               $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
+               $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
                $this->prepare( array( 'dir' => dirname( $fileB ) ) );
-               $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
+               $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
                $this->prepare( array( 'dir' => dirname( $fileC ) ) );
-               $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
+               $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
 
                $status = $this->backend->doOperations( array(
                        array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
@@ -1248,8 +1392,11 @@ class FileBackendTest extends MediaWikiTestCase {
 
        private function doTestGetFileList() {
                $backendName = $this->backendClass();
-
                $base = $this->baseStorePath();
+
+               // Should have no errors
+               $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
+
                $files = array(
                        "$base/unittest-cont1/test1.txt",
                        "$base/unittest-cont1/test2.txt",
@@ -1273,8 +1420,8 @@ class FileBackendTest extends MediaWikiTestCase {
                        $this->prepare( array( 'dir' => dirname( $file ) ) );
                        $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
                }
-               $status = $this->backend->doOperations( $ops );
-               $this->assertEquals( array(), $status->errors,
+               $status = $this->backend->doQuickOperations( $ops );
+               $this->assertGoodStatus( $status,
                        "Creation of files succeeded ($backendName)." );
                $this->assertEquals( true, $status->isOK(),
                        "Creation of files succeeded with OK status ($backendName)." );
@@ -1426,8 +1573,8 @@ class FileBackendTest extends MediaWikiTestCase {
                        $this->prepare( array( 'dir' => dirname( $file ) ) );
                        $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
                }
-               $status = $this->backend->doOperations( $ops );
-               $this->assertEquals( array(), $status->errors,
+               $status = $this->backend->doQuickOperations( $ops );
+               $this->assertGoodStatus( $status,
                        "Creation of files succeeded ($backendName)." );
                $this->assertEquals( true, $status->isOK(),
                        "Creation of files succeeded with OK status ($backendName)." );
@@ -1563,12 +1710,75 @@ class FileBackendTest extends MediaWikiTestCase {
                foreach ( $iter as $iter ) {} // no errors
        }
 
+       public function testLockCalls() {
+               $this->backend = $this->singleBackend;
+               $this->doTestLockCalls();
+       }
+
+       private function doTestLockCalls() {
+               $backendName = $this->backendClass();
+
+               for ( $i=0; $i<50; $i++ ) {
+                       $paths = array(
+                               "test1.txt",
+                               "test2.txt",
+                               "test3.txt",
+                               "subdir1",
+                               "subdir1", // duplicate
+                               "subdir1/test1.txt",
+                               "subdir1/test2.txt",
+                               "subdir2",
+                               "subdir2", // duplicate
+                               "subdir2/test3.txt",
+                               "subdir2/test4.txt",
+                               "subdir2/subdir",
+                               "subdir2/subdir/test1.txt",
+                               "subdir2/subdir/test2.txt",
+                               "subdir2/subdir/test3.txt",
+                               "subdir2/subdir/test4.txt",
+                               "subdir2/subdir/test5.txt",
+                               "subdir2/subdir/sub",
+                               "subdir2/subdir/sub/test0.txt",
+                               "subdir2/subdir/sub/120-px-file.txt",
+                       );
+
+                       $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
+                       $this->assertEquals( array(), $status->errors,
+                               "Locking of files succeeded ($backendName)." );
+                       $this->assertEquals( true, $status->isOK(),
+                               "Locking of files succeeded with OK status ($backendName)." );
+
+                       $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
+                       $this->assertEquals( array(), $status->errors,
+                               "Locking of files succeeded ($backendName)." );
+                       $this->assertEquals( true, $status->isOK(),
+                               "Locking of files succeeded with OK status ($backendName)." );
+
+                       $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
+                       $this->assertEquals( array(), $status->errors,
+                               "Locking of files succeeded ($backendName)." );
+                       $this->assertEquals( true, $status->isOK(),
+                               "Locking of files succeeded with OK status ($backendName)." );
+
+                       $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
+                       $this->assertEquals( array(), $status->errors,
+                               "Locking of files succeeded ($backendName)." );
+                       $this->assertEquals( true, $status->isOK(),
+                               "Locking of files succeeded with OK status ($backendName)." );
+               }
+       }
+
        // test helper wrapper for backend prepare() function
        private function prepare( array $params ) {
-               $this->dirsToPrune[] = $params['dir'];
                return $this->backend->prepare( $params );
        }
 
+       // test helper wrapper for backend prepare() function
+       private function create( array $params ) {
+               $params['op'] = 'create';
+               return $this->backend->doQuickOperations( array( $params ) );
+       }
+
        function tearDownFiles() {
                foreach ( $this->filesToPrune as $file ) {
                        @unlink( $file );
@@ -1577,10 +1787,7 @@ class FileBackendTest extends MediaWikiTestCase {
                foreach ( $containers as $container ) {
                        $this->deleteFiles( $container );
                }
-               foreach ( $this->dirsToPrune as $dir ) {
-                       $this->recursiveClean( $dir );
-               }
-               $this->filesToPrune = $this->dirsToPrune = array();
+               $this->filesToPrune = array();
        }
 
        private function deleteFiles( $container ) {
@@ -1588,17 +1795,15 @@ class FileBackendTest extends MediaWikiTestCase {
                $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
                if ( $iter ) {
                        foreach ( $iter as $file ) {
-                               $this->backend->delete( array( 'src' => "$base/$container/$file" ), array( 'force' => 1 ) );
+                               $this->backend->delete( array( 'src' => "$base/$container/$file" ),
+                                       array( 'force' => 1, 'nonLocking' => 1 ) );
                        }
                }
+               $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
        }
 
-       private function recursiveClean( $dir ) {
-               do {
-                       if ( !$this->backend->clean( array( 'dir' => $dir ) )->isOK() ) {
-                               break;
-                       }
-               } while ( $dir = FileBackend::parentStoragePath( $dir ) );
+       function assertGoodStatus( $status, $msg ) {
+               $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );
        }
 
        function tearDown() {