<?php
/**
+ * @group medium
+ * ^---- causes phpunit to use a higher timeout threshold
+ *
* @group FileRepo
* @group FileBackend
*/
class FileBackendTest extends MediaWikiTestCase {
private $backend, $multiBackend;
private $filesToPrune = array();
- private $dirsToPrune = array();
private static $backendToUse;
function setUp() {
$this->singleBackend = new FSFileBackend( array(
'name' => 'localtesting',
'lockManager' => 'fsLockManager',
+ #'parallelize' => 'implicit',
'containerPaths' => array(
'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1",
'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" )
$this->multiBackend = new FileBackendMultiWrite( array(
'name' => 'localtesting',
'lockManager' => 'fsLockManager',
+ 'parallelize' => 'implicit',
'backends' => array(
array(
'name' => 'localmutlitesting1',
$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)." );
$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'] ) ) {
$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)." );
$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'] ) ) {
}
$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)." );
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)." );
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)." );
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
*/
}
$status = $this->backend->doOperations( $ops );
- $this->assertEquals( array(), $status->errors,
+ $this->assertGoodStatus( $status,
"Creation of source files succeeded ($backendName)." );
$dest = $params['dst'];
// 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)." );
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 ) );
$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 ) );
$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)." );
$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 ) );
$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 ) );
$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)." );
$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)." );
$this->tearDownFiles();
}
- function doTestRecursiveClean() {
+ private function doTestRecursiveClean() {
$backendName = $this->backendClass();
$base = $this->baseStorePath();
);
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)." );
}
$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 ) {
$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();
}
private function doTestDoOperations() {
$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(
// 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" );
"Correct file SHA-1 of $fileC" );
}
+ // 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();
$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 ),
$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)." );
$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)." );
// 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 );
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 ) {
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() {