3 use MediaWiki\MediaWikiServices
;
4 use Wikimedia\TestingAccessWrapper
;
14 * @covers CreateFileOp
15 * @covers DeleteFileOp
16 * @covers DescribeFileOp
18 * @covers FSFileBackend
19 * @covers FSFileBackendDirList
20 * @covers FSFileBackendFileList
21 * @covers FSFileBackendList
22 * @covers FSFileOpHandle
23 * @covers FileBackendDBRepoWrapper
24 * @covers FileBackendError
25 * @covers FileBackendGroup
26 * @covers FileBackendMultiWrite
27 * @covers FileBackendStore
28 * @covers FileBackendStoreOpHandle
29 * @covers FileBackendStoreShardDirIterator
30 * @covers FileBackendStoreShardFileIterator
31 * @covers FileBackendStoreShardListIterator
35 * @covers HTTPFileStreamer
36 * @covers LockManagerGroup
37 * @covers MemoryFileBackend
39 * @covers MySqlLockManager
40 * @covers NullFileJournal
45 * @covers FSLockManager
47 * @covers NullLockManager
49 class FileBackendTest
extends MediaWikiTestCase
{
51 /** @var FileBackend */
53 /** @var FileBackendMultiWrite */
54 private $multiBackend;
55 /** @var FSFileBackend */
56 public $singleBackend;
57 private static $backendToUse;
59 protected function setUp() {
60 global $wgFileBackends;
62 $tmpDir = $this->getNewTempDirectory();
63 if ( $this->getCliArg( 'use-filebackend' ) ) {
64 if ( self
::$backendToUse ) {
65 $this->singleBackend
= self
::$backendToUse;
67 $name = $this->getCliArg( 'use-filebackend' );
69 foreach ( $wgFileBackends as $conf ) {
70 if ( $conf['name'] == $name ) {
75 $useConfig['name'] = 'localtesting'; // swap name
76 $useConfig['shardViaHashLevels'] = [ // test sharding
77 'unittest-cont1' => [ 'levels' => 1, 'base' => 16, 'repeat' => 1 ]
79 if ( isset( $useConfig['fileJournal'] ) ) {
80 $useConfig['fileJournal'] = FileJournal
::factory( $useConfig['fileJournal'], $name );
82 $useConfig['lockManager'] = LockManagerGroup
::singleton()->get( $useConfig['lockManager'] );
83 $class = $useConfig['class'];
84 self
::$backendToUse = new $class( $useConfig );
85 $this->singleBackend
= self
::$backendToUse;
88 $this->singleBackend
= new FSFileBackend( [
89 'name' => 'localtesting',
90 'lockManager' => LockManagerGroup
::singleton()->get( 'fsLockManager' ),
91 'wikiId' => wfWikiID(),
93 'unittest-cont1' => "{$tmpDir}/localtesting-cont1",
94 'unittest-cont2' => "{$tmpDir}/localtesting-cont2" ]
97 $this->multiBackend
= new FileBackendMultiWrite( [
98 'name' => 'localtesting',
99 'lockManager' => LockManagerGroup
::singleton()->get( 'fsLockManager' ),
100 'parallelize' => 'implicit',
101 'wikiId' => 'testdb',
104 'name' => 'localmultitesting1',
105 'class' => FSFileBackend
::class,
106 'containerPaths' => [
107 'unittest-cont1' => "{$tmpDir}/localtestingmulti1-cont1",
108 'unittest-cont2' => "{$tmpDir}/localtestingmulti1-cont2" ],
109 'isMultiMaster' => false
112 'name' => 'localmultitesting2',
113 'class' => FSFileBackend
::class,
114 'containerPaths' => [
115 'unittest-cont1' => "{$tmpDir}/localtestingmulti2-cont1",
116 'unittest-cont2' => "{$tmpDir}/localtestingmulti2-cont2" ],
117 'isMultiMaster' => true
123 private static function baseStorePath() {
124 return 'mwstore://localtesting';
127 private function backendClass() {
128 return get_class( $this->backend
);
132 * @dataProvider provider_testIsStoragePath
134 public function testIsStoragePath( $path, $isStorePath ) {
135 $this->assertEquals( $isStorePath, FileBackend
::isStoragePath( $path ),
136 "FileBackend::isStoragePath on path '$path'" );
139 public static function provider_testIsStoragePath() {
141 [ 'mwstore://', true ],
142 [ 'mwstore://backend', true ],
143 [ 'mwstore://backend/container', true ],
144 [ 'mwstore://backend/container/', true ],
145 [ 'mwstore://backend/container/path', true ],
146 [ 'mwstore://backend//container/', true ],
147 [ 'mwstore://backend//container//', true ],
148 [ 'mwstore://backend//container//path', true ],
149 [ 'mwstore:///', true ],
150 [ 'mwstore:/', false ],
151 [ 'mwstore:', false ],
156 * @dataProvider provider_testSplitStoragePath
158 public function testSplitStoragePath( $path, $res ) {
159 $this->assertEquals( $res, FileBackend
::splitStoragePath( $path ),
160 "FileBackend::splitStoragePath on path '$path'" );
163 public static function provider_testSplitStoragePath() {
165 [ 'mwstore://backend/container', [ 'backend', 'container', '' ] ],
166 [ 'mwstore://backend/container/', [ 'backend', 'container', '' ] ],
167 [ 'mwstore://backend/container/path', [ 'backend', 'container', 'path' ] ],
168 [ 'mwstore://backend/container//path', [ 'backend', 'container', '/path' ] ],
169 [ 'mwstore://backend//container/path', [ null, null, null ] ],
170 [ 'mwstore://backend//container//path', [ null, null, null ] ],
171 [ 'mwstore://', [ null, null, null ] ],
172 [ 'mwstore://backend', [ null, null, null ] ],
173 [ 'mwstore:///', [ null, null, null ] ],
174 [ 'mwstore:/', [ null, null, null ] ],
175 [ 'mwstore:', [ null, null, null ] ]
180 * @dataProvider provider_normalizeStoragePath
182 public function testNormalizeStoragePath( $path, $res ) {
183 $this->assertEquals( $res, FileBackend
::normalizeStoragePath( $path ),
184 "FileBackend::normalizeStoragePath on path '$path'" );
187 public static function provider_normalizeStoragePath() {
189 [ 'mwstore://backend/container', 'mwstore://backend/container' ],
190 [ 'mwstore://backend/container/', 'mwstore://backend/container' ],
191 [ 'mwstore://backend/container/path', 'mwstore://backend/container/path' ],
192 [ 'mwstore://backend/container//path', 'mwstore://backend/container/path' ],
193 [ 'mwstore://backend/container///path', 'mwstore://backend/container/path' ],
195 'mwstore://backend/container///path//to///obj',
196 'mwstore://backend/container/path/to/obj'
198 [ 'mwstore://', null ],
199 [ 'mwstore://backend', null ],
200 [ 'mwstore://backend//container/path', null ],
201 [ 'mwstore://backend//container//path', null ],
202 [ 'mwstore:///', null ],
203 [ 'mwstore:/', null ],
204 [ 'mwstore:', null ],
209 * @dataProvider provider_testParentStoragePath
211 public function testParentStoragePath( $path, $res ) {
212 $this->assertEquals( $res, FileBackend
::parentStoragePath( $path ),
213 "FileBackend::parentStoragePath on path '$path'" );
216 public static function provider_testParentStoragePath() {
218 [ 'mwstore://backend/container/path/to/obj', 'mwstore://backend/container/path/to' ],
219 [ 'mwstore://backend/container/path/to', 'mwstore://backend/container/path' ],
220 [ 'mwstore://backend/container/path', 'mwstore://backend/container' ],
221 [ 'mwstore://backend/container', null ],
222 [ 'mwstore://backend/container/path/to/obj/', 'mwstore://backend/container/path/to' ],
223 [ 'mwstore://backend/container/path/to/', 'mwstore://backend/container/path' ],
224 [ 'mwstore://backend/container/path/', 'mwstore://backend/container' ],
225 [ 'mwstore://backend/container/', null ],
230 * @dataProvider provider_testExtensionFromPath
232 public function testExtensionFromPath( $path, $res ) {
233 $this->assertEquals( $res, FileBackend
::extensionFromPath( $path ),
234 "FileBackend::extensionFromPath on path '$path'" );
237 public static function provider_testExtensionFromPath() {
239 [ 'mwstore://backend/container/path.txt', 'txt' ],
240 [ 'mwstore://backend/container/path.svg.png', 'png' ],
241 [ 'mwstore://backend/container/path', '' ],
242 [ 'mwstore://backend/container/path.', '' ],
247 * @dataProvider provider_testStore
249 public function testStore( $op ) {
250 $this->addTmpFiles( $op['src'] );
252 $this->backend
= $this->singleBackend
;
253 $this->tearDownFiles();
254 $this->doTestStore( $op );
255 $this->tearDownFiles();
257 $this->backend
= $this->multiBackend
;
258 $this->tearDownFiles();
259 $this->doTestStore( $op );
260 $this->tearDownFiles();
263 private function doTestStore( $op ) {
264 $backendName = $this->backendClass();
266 $source = $op['src'];
268 $this->prepare( [ 'dir' => dirname( $dest ) ] );
270 file_put_contents( $source, "Unit test file" );
272 if ( isset( $op['overwrite'] ) ||
isset( $op['overwriteSame'] ) ) {
273 $this->backend
->store( $op );
276 $status = $this->backend
->doOperation( $op );
278 $this->assertGoodStatus( $status,
279 "Store from $source to $dest succeeded without warnings ($backendName)." );
280 $this->assertEquals( true, $status->isOK(),
281 "Store from $source to $dest succeeded ($backendName)." );
282 $this->assertEquals( [ 0 => true ], $status->success
,
283 "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
284 $this->assertEquals( true, file_exists( $source ),
285 "Source file $source still exists ($backendName)." );
286 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $dest ] ),
287 "Destination file $dest exists ($backendName)." );
289 $this->assertEquals( filesize( $source ),
290 $this->backend
->getFileSize( [ 'src' => $dest ] ),
291 "Destination file $dest has correct size ($backendName)." );
293 $props1 = FSFile
::getPropsFromPath( $source );
294 $props2 = $this->backend
->getFileProps( [ 'src' => $dest ] );
295 $this->assertEquals( $props1, $props2,
296 "Source and destination have the same props ($backendName)." );
298 $this->assertBackendPathsConsistent( [ $dest ] );
301 public static function provider_testStore() {
304 $tmpName = TempFSFile
::factory( "unittests_", 'txt', wfTempDir() )->getPath();
305 $toPath = self
::baseStorePath() . '/unittest-cont1/e/fun/obj1.txt';
306 $op = [ 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath ];
310 $op2['overwrite'] = true;
314 $op3['overwriteSame'] = true;
321 * @dataProvider provider_testCopy
323 public function testCopy( $op ) {
324 $this->backend
= $this->singleBackend
;
325 $this->tearDownFiles();
326 $this->doTestCopy( $op );
327 $this->tearDownFiles();
329 $this->backend
= $this->multiBackend
;
330 $this->tearDownFiles();
331 $this->doTestCopy( $op );
332 $this->tearDownFiles();
335 private function doTestCopy( $op ) {
336 $backendName = $this->backendClass();
338 $source = $op['src'];
340 $this->prepare( [ 'dir' => dirname( $source ) ] );
341 $this->prepare( [ 'dir' => dirname( $dest ) ] );
343 if ( isset( $op['ignoreMissingSource'] ) ) {
344 $status = $this->backend
->doOperation( $op );
345 $this->assertGoodStatus( $status,
346 "Move from $source to $dest succeeded without warnings ($backendName)." );
347 $this->assertEquals( [ 0 => true ], $status->success
,
348 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
349 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $source ] ),
350 "Source file $source does not exist ($backendName)." );
351 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $dest ] ),
352 "Destination file $dest does not exist ($backendName)." );
357 $status = $this->backend
->doOperation(
358 [ 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ] );
359 $this->assertGoodStatus( $status,
360 "Creation of file at $source succeeded ($backendName)." );
362 if ( isset( $op['overwrite'] ) ||
isset( $op['overwriteSame'] ) ) {
363 $this->backend
->copy( $op );
366 $status = $this->backend
->doOperation( $op );
368 $this->assertGoodStatus( $status,
369 "Copy from $source to $dest succeeded without warnings ($backendName)." );
370 $this->assertEquals( true, $status->isOK(),
371 "Copy from $source to $dest succeeded ($backendName)." );
372 $this->assertEquals( [ 0 => true ], $status->success
,
373 "Copy from $source to $dest has proper 'success' field in Status ($backendName)." );
374 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $source ] ),
375 "Source file $source still exists ($backendName)." );
376 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $dest ] ),
377 "Destination file $dest exists after copy ($backendName)." );
380 $this->backend
->getFileSize( [ 'src' => $source ] ),
381 $this->backend
->getFileSize( [ 'src' => $dest ] ),
382 "Destination file $dest has correct size ($backendName)." );
384 $props1 = $this->backend
->getFileProps( [ 'src' => $source ] );
385 $props2 = $this->backend
->getFileProps( [ 'src' => $dest ] );
386 $this->assertEquals( $props1, $props2,
387 "Source and destination have the same props ($backendName)." );
389 $this->assertBackendPathsConsistent( [ $source, $dest ] );
392 public static function provider_testCopy() {
395 $source = self
::baseStorePath() . '/unittest-cont1/e/file.txt';
396 $dest = self
::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
398 $op = [ 'op' => 'copy', 'src' => $source, 'dst' => $dest ];
406 $op2['overwrite'] = true;
414 $op2['overwriteSame'] = true;
422 $op2['ignoreMissingSource'] = true;
430 $op2['ignoreMissingSource'] = true;
433 self
::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
441 * @dataProvider provider_testMove
443 public function testMove( $op ) {
444 $this->backend
= $this->singleBackend
;
445 $this->tearDownFiles();
446 $this->doTestMove( $op );
447 $this->tearDownFiles();
449 $this->backend
= $this->multiBackend
;
450 $this->tearDownFiles();
451 $this->doTestMove( $op );
452 $this->tearDownFiles();
455 private function doTestMove( $op ) {
456 $backendName = $this->backendClass();
458 $source = $op['src'];
460 $this->prepare( [ 'dir' => dirname( $source ) ] );
461 $this->prepare( [ 'dir' => dirname( $dest ) ] );
463 if ( isset( $op['ignoreMissingSource'] ) ) {
464 $status = $this->backend
->doOperation( $op );
465 $this->assertGoodStatus( $status,
466 "Move from $source to $dest succeeded without warnings ($backendName)." );
467 $this->assertEquals( [ 0 => true ], $status->success
,
468 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
469 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $source ] ),
470 "Source file $source does not exist ($backendName)." );
471 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $dest ] ),
472 "Destination file $dest does not exist ($backendName)." );
477 $status = $this->backend
->doOperation(
478 [ 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ] );
479 $this->assertGoodStatus( $status,
480 "Creation of file at $source succeeded ($backendName)." );
482 if ( isset( $op['overwrite'] ) ||
isset( $op['overwriteSame'] ) ) {
483 $this->backend
->copy( $op );
486 $status = $this->backend
->doOperation( $op );
487 $this->assertGoodStatus( $status,
488 "Move from $source to $dest succeeded without warnings ($backendName)." );
489 $this->assertEquals( true, $status->isOK(),
490 "Move from $source to $dest succeeded ($backendName)." );
491 $this->assertEquals( [ 0 => true ], $status->success
,
492 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
493 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $source ] ),
494 "Source file $source does not still exists ($backendName)." );
495 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $dest ] ),
496 "Destination file $dest exists after move ($backendName)." );
498 $this->assertNotEquals(
499 $this->backend
->getFileSize( [ 'src' => $source ] ),
500 $this->backend
->getFileSize( [ 'src' => $dest ] ),
501 "Destination file $dest has correct size ($backendName)." );
503 $props1 = $this->backend
->getFileProps( [ 'src' => $source ] );
504 $props2 = $this->backend
->getFileProps( [ 'src' => $dest ] );
505 $this->assertEquals( false, $props1['fileExists'],
506 "Source file does not exist accourding to props ($backendName)." );
507 $this->assertEquals( true, $props2['fileExists'],
508 "Destination file exists accourding to props ($backendName)." );
510 $this->assertBackendPathsConsistent( [ $source, $dest ] );
513 public static function provider_testMove() {
516 $source = self
::baseStorePath() . '/unittest-cont1/e/file.txt';
517 $dest = self
::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
519 $op = [ 'op' => 'move', 'src' => $source, 'dst' => $dest ];
527 $op2['overwrite'] = true;
535 $op2['overwriteSame'] = true;
543 $op2['ignoreMissingSource'] = true;
551 $op2['ignoreMissingSource'] = true;
554 self
::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
562 * @dataProvider provider_testDelete
564 public function testDelete( $op, $withSource, $okStatus ) {
565 $this->backend
= $this->singleBackend
;
566 $this->tearDownFiles();
567 $this->doTestDelete( $op, $withSource, $okStatus );
568 $this->tearDownFiles();
570 $this->backend
= $this->multiBackend
;
571 $this->tearDownFiles();
572 $this->doTestDelete( $op, $withSource, $okStatus );
573 $this->tearDownFiles();
576 private function doTestDelete( $op, $withSource, $okStatus ) {
577 $backendName = $this->backendClass();
579 $source = $op['src'];
580 $this->prepare( [ 'dir' => dirname( $source ) ] );
583 $status = $this->backend
->doOperation(
584 [ 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ] );
585 $this->assertGoodStatus( $status,
586 "Creation of file at $source succeeded ($backendName)." );
589 $status = $this->backend
->doOperation( $op );
591 $this->assertGoodStatus( $status,
592 "Deletion of file at $source succeeded without warnings ($backendName)." );
593 $this->assertEquals( true, $status->isOK(),
594 "Deletion of file at $source succeeded ($backendName)." );
595 $this->assertEquals( [ 0 => true ], $status->success
,
596 "Deletion of file at $source has proper 'success' field in Status ($backendName)." );
598 $this->assertEquals( false, $status->isOK(),
599 "Deletion of file at $source failed ($backendName)." );
602 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $source ] ),
603 "Source file $source does not exist after move ($backendName)." );
606 $this->backend
->getFileSize( [ 'src' => $source ] ),
607 "Source file $source has correct size (false) ($backendName)." );
609 $props1 = $this->backend
->getFileProps( [ 'src' => $source ] );
610 $this->assertFalse( $props1['fileExists'],
611 "Source file $source does not exist according to props ($backendName)." );
613 $this->assertBackendPathsConsistent( [ $source ] );
616 public static function provider_testDelete() {
619 $source = self
::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
621 $op = [ 'op' => 'delete', 'src' => $source ];
630 false, // without source
634 $op['ignoreMissingSource'] = true;
637 false, // without source
641 $op['ignoreMissingSource'] = true;
642 $op['src'] = self
::baseStorePath() . '/unittest-cont-bad/e/file.txt';
645 false, // without source
653 * @dataProvider provider_testDescribe
655 public function testDescribe( $op, $withSource, $okStatus ) {
656 $this->backend
= $this->singleBackend
;
657 $this->tearDownFiles();
658 $this->doTestDescribe( $op, $withSource, $okStatus );
659 $this->tearDownFiles();
661 $this->backend
= $this->multiBackend
;
662 $this->tearDownFiles();
663 $this->doTestDescribe( $op, $withSource, $okStatus );
664 $this->tearDownFiles();
667 private function doTestDescribe( $op, $withSource, $okStatus ) {
668 $backendName = $this->backendClass();
670 $source = $op['src'];
671 $this->prepare( [ 'dir' => dirname( $source ) ] );
674 $status = $this->backend
->doOperation(
675 [ 'op' => 'create', 'content' => 'blahblah', 'dst' => $source,
676 'headers' => [ 'Content-Disposition' => 'xxx' ] ] );
677 $this->assertGoodStatus( $status,
678 "Creation of file at $source succeeded ($backendName)." );
679 if ( $this->backend
->hasFeatures( FileBackend
::ATTR_HEADERS
) ) {
680 $attr = $this->backend
->getFileXAttributes( [ 'src' => $source ] );
681 $this->assertHasHeaders( [ 'Content-Disposition' => 'xxx' ], $attr );
684 $status = $this->backend
->describe( [ 'src' => $source,
685 'headers' => [ 'Content-Disposition' => '' ] ] ); // remove
686 $this->assertGoodStatus( $status,
687 "Removal of header for $source succeeded ($backendName)." );
689 if ( $this->backend
->hasFeatures( FileBackend
::ATTR_HEADERS
) ) {
690 $attr = $this->backend
->getFileXAttributes( [ 'src' => $source ] );
691 $this->assertFalse( isset( $attr['headers']['content-disposition'] ),
692 "File 'Content-Disposition' header removed." );
696 $status = $this->backend
->doOperation( $op );
698 $this->assertGoodStatus( $status,
699 "Describe of file at $source succeeded without warnings ($backendName)." );
700 $this->assertEquals( true, $status->isOK(),
701 "Describe of file at $source succeeded ($backendName)." );
702 $this->assertEquals( [ 0 => true ], $status->success
,
703 "Describe of file at $source has proper 'success' field in Status ($backendName)." );
704 if ( $this->backend
->hasFeatures( FileBackend
::ATTR_HEADERS
) ) {
705 $attr = $this->backend
->getFileXAttributes( [ 'src' => $source ] );
706 $this->assertHasHeaders( $op['headers'], $attr );
709 $this->assertEquals( false, $status->isOK(),
710 "Describe of file at $source failed ($backendName)." );
713 $this->assertBackendPathsConsistent( [ $source ] );
716 private function assertHasHeaders( array $headers, array $attr ) {
717 foreach ( $headers as $n => $v ) {
719 $this->assertTrue( isset( $attr['headers'][strtolower( $n )] ),
720 "File has '$n' header." );
721 $this->assertEquals( $v, $attr['headers'][strtolower( $n )],
722 "File has '$n' header value." );
724 $this->assertFalse( isset( $attr['headers'][strtolower( $n )] ),
725 "File does not have '$n' header." );
730 public static function provider_testDescribe() {
733 $source = self
::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
735 $op = [ 'op' => 'describe', 'src' => $source,
736 'headers' => [ 'Content-Disposition' => 'inline' ], ];
745 false, // without source
753 * @dataProvider provider_testCreate
755 public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
756 $this->backend
= $this->singleBackend
;
757 $this->tearDownFiles();
758 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
759 $this->tearDownFiles();
761 $this->backend
= $this->multiBackend
;
762 $this->tearDownFiles();
763 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
764 $this->tearDownFiles();
767 private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
768 $backendName = $this->backendClass();
771 $this->prepare( [ 'dir' => dirname( $dest ) ] );
773 $oldText = 'blah...blah...waahwaah';
774 if ( $alreadyExists ) {
775 $status = $this->backend
->doOperation(
776 [ 'op' => 'create', 'content' => $oldText, 'dst' => $dest ] );
777 $this->assertGoodStatus( $status,
778 "Creation of file at $dest succeeded ($backendName)." );
781 $status = $this->backend
->doOperation( $op );
783 $this->assertGoodStatus( $status,
784 "Creation of file at $dest succeeded without warnings ($backendName)." );
785 $this->assertEquals( true, $status->isOK(),
786 "Creation of file at $dest succeeded ($backendName)." );
787 $this->assertEquals( [ 0 => true ], $status->success
,
788 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
790 $this->assertEquals( false, $status->isOK(),
791 "Creation of file at $dest failed ($backendName)." );
794 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $dest ] ),
795 "Destination file $dest exists after creation ($backendName)." );
797 $props1 = $this->backend
->getFileProps( [ 'src' => $dest ] );
798 $this->assertEquals( true, $props1['fileExists'],
799 "Destination file $dest exists according to props ($backendName)." );
800 if ( $okStatus ) { // file content is what we saved
801 $this->assertEquals( $newSize, $props1['size'],
802 "Destination file $dest has expected size according to props ($backendName)." );
803 $this->assertEquals( $newSize,
804 $this->backend
->getFileSize( [ 'src' => $dest ] ),
805 "Destination file $dest has correct size ($backendName)." );
806 } else { // file content is some other previous text
807 $this->assertEquals( strlen( $oldText ), $props1['size'],
808 "Destination file $dest has original size according to props ($backendName)." );
809 $this->assertEquals( strlen( $oldText ),
810 $this->backend
->getFileSize( [ 'src' => $dest ] ),
811 "Destination file $dest has original size according to props ($backendName)." );
814 $this->assertBackendPathsConsistent( [ $dest ] );
818 * @dataProvider provider_testCreate
820 public static function provider_testCreate() {
823 $dest = self
::baseStorePath() . '/unittest-cont2/a/myspacefile.txt';
825 $op = [ 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest ];
828 false, // no dest already exists
830 strlen( $op['content'] )
834 $op2['content'] = "\n";
837 false, // no dest already exists
839 strlen( $op2['content'] )
843 $op2['content'] = "fsf\n waf 3kt";
846 true, // dest already exists
848 strlen( $op2['content'] )
852 $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
853 $op2['overwrite'] = true;
856 true, // dest already exists
858 strlen( $op2['content'] )
862 $op2['content'] = "39qjmg3-qg";
863 $op2['overwriteSame'] = true;
866 true, // dest already exists
868 strlen( $op2['content'] )
874 public function testDoQuickOperations() {
875 $this->backend
= $this->singleBackend
;
876 $this->doTestDoQuickOperations();
877 $this->tearDownFiles();
879 $this->backend
= $this->multiBackend
;
880 $this->doTestDoQuickOperations();
881 $this->tearDownFiles();
884 private function doTestDoQuickOperations() {
885 $backendName = $this->backendClass();
887 $base = self
::baseStorePath();
889 "$base/unittest-cont1/e/fileA.a",
890 "$base/unittest-cont1/e/fileB.a",
891 "$base/unittest-cont1/e/fileC.a"
895 foreach ( $files as $path ) {
896 $status = $this->prepare( [ 'dir' => dirname( $path ) ] );
897 $this->assertGoodStatus( $status,
898 "Preparing $path succeeded without warnings ($backendName)." );
899 $createOps[] = [ 'op' => 'create', 'dst' => $path, 'content' => mt_rand( 0, 50000 ) ];
900 $copyOps[] = [ 'op' => 'copy', 'src' => $path, 'dst' => "$path-2" ];
901 $moveOps[] = [ 'op' => 'move', 'src' => "$path-2", 'dst' => "$path-3" ];
902 $purgeOps[] = [ 'op' => 'delete', 'src' => $path ];
903 $purgeOps[] = [ 'op' => 'delete', 'src' => "$path-3" ];
905 $purgeOps[] = [ 'op' => 'null' ];
907 $this->assertGoodStatus(
908 $this->backend
->doQuickOperations( $createOps ),
909 "Creation of source files succeeded ($backendName)." );
910 foreach ( $files as $file ) {
911 $this->assertTrue( $this->backend
->fileExists( [ 'src' => $file ] ),
912 "File $file exists." );
915 $this->assertGoodStatus(
916 $this->backend
->doQuickOperations( $copyOps ),
917 "Quick copy of source files succeeded ($backendName)." );
918 foreach ( $files as $file ) {
919 $this->assertTrue( $this->backend
->fileExists( [ 'src' => "$file-2" ] ),
920 "File $file-2 exists." );
923 $this->assertGoodStatus(
924 $this->backend
->doQuickOperations( $moveOps ),
925 "Quick move of source files succeeded ($backendName)." );
926 foreach ( $files as $file ) {
927 $this->assertTrue( $this->backend
->fileExists( [ 'src' => "$file-3" ] ),
928 "File $file-3 move in." );
929 $this->assertFalse( $this->backend
->fileExists( [ 'src' => "$file-2" ] ),
930 "File $file-2 moved away." );
933 $this->assertGoodStatus(
934 $this->backend
->quickCopy( [ 'src' => $files[0], 'dst' => $files[0] ] ),
935 "Copy of file {$files[0]} over itself succeeded ($backendName)." );
936 $this->assertTrue( $this->backend
->fileExists( [ 'src' => $files[0] ] ),
937 "File {$files[0]} still exists." );
939 $this->assertGoodStatus(
940 $this->backend
->quickMove( [ 'src' => $files[0], 'dst' => $files[0] ] ),
941 "Move of file {$files[0]} over itself succeeded ($backendName)." );
942 $this->assertTrue( $this->backend
->fileExists( [ 'src' => $files[0] ] ),
943 "File {$files[0]} still exists." );
945 $this->assertGoodStatus(
946 $this->backend
->doQuickOperations( $purgeOps ),
947 "Quick deletion of source files succeeded ($backendName)." );
948 foreach ( $files as $file ) {
949 $this->assertFalse( $this->backend
->fileExists( [ 'src' => $file ] ),
950 "File $file purged." );
951 $this->assertFalse( $this->backend
->fileExists( [ 'src' => "$file-3" ] ),
952 "File $file-3 purged." );
957 * @dataProvider provider_testConcatenate
959 public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
960 $this->backend
= $this->singleBackend
;
961 $this->tearDownFiles();
962 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
963 $this->tearDownFiles();
965 $this->backend
= $this->multiBackend
;
966 $this->tearDownFiles();
967 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
968 $this->tearDownFiles();
971 private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
972 $backendName = $this->backendClass();
977 foreach ( $srcs as $i => $source ) {
978 $this->prepare( [ 'dir' => dirname( $source ) ] );
980 'op' => 'create', // operation
981 'dst' => $source, // source
982 'content' => $srcsContent[$i]
984 $expContent .= $srcsContent[$i];
986 $status = $this->backend
->doOperations( $ops );
988 $this->assertGoodStatus( $status,
989 "Creation of source files succeeded ($backendName)." );
991 $dest = $params['dst'] = $this->getNewTempFile();
992 if ( $alreadyExists ) {
993 $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
994 $this->assertEquals( true, $ok,
995 "Creation of file at $dest succeeded ($backendName)." );
997 $ok = file_put_contents( $dest, '' ) !== false;
998 $this->assertEquals( true, $ok,
999 "Creation of 0-byte file at $dest succeeded ($backendName)." );
1002 // Combine the files into one
1003 $status = $this->backend
->concatenate( $params );
1005 $this->assertGoodStatus( $status,
1006 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
1007 $this->assertEquals( true, $status->isOK(),
1008 "Creation of concat file at $dest succeeded ($backendName)." );
1010 $this->assertEquals( false, $status->isOK(),
1011 "Creation of concat file at $dest failed ($backendName)." );
1015 $this->assertEquals( true, is_file( $dest ),
1016 "Dest concat file $dest exists after creation ($backendName)." );
1018 $this->assertEquals( true, is_file( $dest ),
1019 "Dest concat file $dest exists after failed creation ($backendName)." );
1022 $contents = file_get_contents( $dest );
1023 $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
1026 $this->assertEquals( $expContent, $contents,
1027 "Concat file at $dest has correct contents ($backendName)." );
1029 $this->assertNotEquals( $expContent, $contents,
1030 "Concat file at $dest has correct contents ($backendName)." );
1034 public static function provider_testConcatenate() {
1038 self
::baseStorePath() . '/unittest-cont1/e/file1.txt',
1039 self
::baseStorePath() . '/unittest-cont1/e/file2.txt',
1040 self
::baseStorePath() . '/unittest-cont1/e/file3.txt',
1041 self
::baseStorePath() . '/unittest-cont1/e/file4.txt',
1042 self
::baseStorePath() . '/unittest-cont1/e/file5.txt',
1043 self
::baseStorePath() . '/unittest-cont1/e/file6.txt',
1044 self
::baseStorePath() . '/unittest-cont1/e/file7.txt',
1045 self
::baseStorePath() . '/unittest-cont1/e/file8.txt',
1046 self
::baseStorePath() . '/unittest-cont1/e/file9.txt',
1047 self
::baseStorePath() . '/unittest-cont1/e/file10.txt'
1061 $params = [ 'srcs' => $srcs ];
1064 $params, // operation
1066 $content, // content for each source
1067 false, // no dest already exists
1072 $params, // operation
1074 $content, // content for each source
1075 true, // dest already exists
1083 * @dataProvider provider_testGetFileStat
1085 public function testGetFileStat( $path, $content, $alreadyExists ) {
1086 $this->backend
= $this->singleBackend
;
1087 $this->tearDownFiles();
1088 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1089 $this->tearDownFiles();
1091 $this->backend
= $this->multiBackend
;
1092 $this->tearDownFiles();
1093 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1094 $this->tearDownFiles();
1097 private function doTestGetFileStat( $path, $content, $alreadyExists ) {
1098 $backendName = $this->backendClass();
1100 if ( $alreadyExists ) {
1101 $this->prepare( [ 'dir' => dirname( $path ) ] );
1102 $status = $this->create( [ 'dst' => $path, 'content' => $content ] );
1103 $this->assertGoodStatus( $status,
1104 "Creation of file at $path succeeded ($backendName)." );
1106 $size = $this->backend
->getFileSize( [ 'src' => $path ] );
1107 $time = $this->backend
->getFileTimestamp( [ 'src' => $path ] );
1108 $stat = $this->backend
->getFileStat( [ 'src' => $path ] );
1110 $this->assertEquals( strlen( $content ), $size,
1111 "Correct file size of '$path'" );
1112 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX
, $time ) ) < 10,
1113 "Correct file timestamp of '$path'" );
1115 $size = $stat['size'];
1116 $time = $stat['mtime'];
1117 $this->assertEquals( strlen( $content ), $size,
1118 "Correct file size of '$path'" );
1119 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX
, $time ) ) < 10,
1120 "Correct file timestamp of '$path'" );
1122 $this->backend
->clearCache( [ $path ] );
1124 $size = $this->backend
->getFileSize( [ 'src' => $path ] );
1126 $this->assertEquals( strlen( $content ), $size,
1127 "Correct file size of '$path'" );
1129 $this->backend
->preloadCache( [ $path ] );
1131 $size = $this->backend
->getFileSize( [ 'src' => $path ] );
1133 $this->assertEquals( strlen( $content ), $size,
1134 "Correct file size of '$path'" );
1136 $size = $this->backend
->getFileSize( [ 'src' => $path ] );
1137 $time = $this->backend
->getFileTimestamp( [ 'src' => $path ] );
1138 $stat = $this->backend
->getFileStat( [ 'src' => $path ] );
1140 $this->assertFalse( $size, "Correct file size of '$path'" );
1141 $this->assertFalse( $time, "Correct file timestamp of '$path'" );
1142 $this->assertFalse( $stat, "Correct file stat of '$path'" );
1146 public static function provider_testGetFileStat() {
1149 $base = self
::baseStorePath();
1150 $cases[] = [ "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents", true ];
1151 $cases[] = [ "$base/unittest-cont1/e/b/some-other_file.txt", "", true ];
1152 $cases[] = [ "$base/unittest-cont1/e/b/some-diff_file.txt", null, false ];
1158 * @dataProvider provider_testGetFileStat
1160 public function testStreamFile( $path, $content, $alreadyExists ) {
1161 $this->backend
= $this->singleBackend
;
1162 $this->tearDownFiles();
1163 $this->doTestStreamFile( $path, $content, $alreadyExists );
1164 $this->tearDownFiles();
1166 $this->backend
= $this->multiBackend
;
1167 $this->tearDownFiles();
1168 $this->doTestStreamFile( $path, $content, $alreadyExists );
1169 $this->tearDownFiles();
1172 private function doTestStreamFile( $path, $content ) {
1173 $backendName = $this->backendClass();
1175 if ( $content !== null ) {
1176 $this->prepare( [ 'dir' => dirname( $path ) ] );
1177 $status = $this->create( [ 'dst' => $path, 'content' => $content ] );
1178 $this->assertGoodStatus( $status,
1179 "Creation of file at $path succeeded ($backendName)." );
1182 $this->backend
->streamFile( [ 'src' => $path, 'headless' => 1, 'allowOB' => 1 ] );
1183 $data = ob_get_contents();
1186 $this->assertEquals( $content, $data, "Correct content streamed from '$path'" );
1187 } else { // 404 case
1189 $this->backend
->streamFile( [ 'src' => $path, 'headless' => 1, 'allowOB' => 1 ] );
1190 $data = ob_get_contents();
1193 $this->assertRegExp( '#<h1>File not found</h1>#', $data,
1194 "Correct content streamed from '$path' ($backendName)" );
1198 public static function provider_testStreamFile() {
1201 $base = self
::baseStorePath();
1202 $cases[] = [ "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" ];
1203 $cases[] = [ "$base/unittest-cont1/e/b/some-other_file.txt", null ];
1208 public function testStreamFileRange() {
1209 $this->backend
= $this->singleBackend
;
1210 $this->tearDownFiles();
1211 $this->doTestStreamFileRange();
1212 $this->tearDownFiles();
1214 $this->backend
= $this->multiBackend
;
1215 $this->tearDownFiles();
1216 $this->doTestStreamFileRange();
1217 $this->tearDownFiles();
1220 private function doTestStreamFileRange() {
1221 $backendName = $this->backendClass();
1223 $base = self
::baseStorePath();
1224 $path = "$base/unittest-cont1/e/b/z/range_file.txt";
1225 $content = "0123456789ABCDEF";
1227 $this->prepare( [ 'dir' => dirname( $path ) ] );
1228 $status = $this->create( [ 'dst' => $path, 'content' => $content ] );
1229 $this->assertGoodStatus( $status,
1230 "Creation of file at $path succeeded ($backendName)." );
1234 'bytes=0-3' => '0123',
1235 'bytes=4-8' => '45678',
1236 'bytes=15-15' => 'F',
1237 'bytes=14-15' => 'EF',
1238 'bytes=-5' => 'BCDEF',
1240 'bytes=10-16' => 'ABCDEF',
1241 'bytes=10-99' => 'ABCDEF',
1244 foreach ( $ranges as $range => $chunk ) {
1246 $this->backend
->streamFile( [ 'src' => $path, 'headless' => 1, 'allowOB' => 1,
1247 'options' => [ 'range' => $range ] ] );
1248 $data = ob_get_contents();
1251 $this->assertEquals( $chunk, $data, "Correct chunk streamed from '$path' for '$range'" );
1256 * @dataProvider provider_testGetFileContents
1258 public function testGetFileContents( $source, $content ) {
1259 $this->backend
= $this->singleBackend
;
1260 $this->tearDownFiles();
1261 $this->doTestGetFileContents( $source, $content );
1262 $this->tearDownFiles();
1264 $this->backend
= $this->multiBackend
;
1265 $this->tearDownFiles();
1266 $this->doTestGetFileContents( $source, $content );
1267 $this->tearDownFiles();
1270 private function doTestGetFileContents( $source, $content ) {
1271 $backendName = $this->backendClass();
1273 $srcs = (array)$source;
1274 $content = (array)$content;
1275 foreach ( $srcs as $i => $src ) {
1276 $this->prepare( [ 'dir' => dirname( $src ) ] );
1277 $status = $this->backend
->doOperation(
1278 [ 'op' => 'create', 'content' => $content[$i], 'dst' => $src ] );
1279 $this->assertGoodStatus( $status,
1280 "Creation of file at $src succeeded ($backendName)." );
1283 if ( is_array( $source ) ) {
1284 $contents = $this->backend
->getFileContentsMulti( [ 'srcs' => $source ] );
1285 foreach ( $contents as $path => $data ) {
1286 $this->assertNotEquals( false, $data, "Contents of $path exists ($backendName)." );
1287 $this->assertEquals(
1288 current( $content ),
1290 "Contents of $path is correct ($backendName)."
1294 $this->assertEquals(
1296 array_keys( $contents ),
1297 "Contents in right order ($backendName)."
1299 $this->assertEquals(
1302 "Contents array size correct ($backendName)."
1305 $data = $this->backend
->getFileContents( [ 'src' => $source ] );
1306 $this->assertNotEquals( false, $data, "Contents of $source exists ($backendName)." );
1307 $this->assertEquals( $content[0], $data, "Contents of $source is correct ($backendName)." );
1311 public static function provider_testGetFileContents() {
1314 $base = self
::baseStorePath();
1315 $cases[] = [ "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" ];
1316 $cases[] = [ "$base/unittest-cont1/e/b/some-other_file.txt", "more file contents" ];
1318 [ "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1319 "$base/unittest-cont1/e/a/z.txt" ],
1320 [ "contents xx", "contents xy", "contents xz" ]
1327 * @dataProvider provider_testGetLocalCopy
1329 public function testGetLocalCopy( $source, $content ) {
1330 $this->backend
= $this->singleBackend
;
1331 $this->tearDownFiles();
1332 $this->doTestGetLocalCopy( $source, $content );
1333 $this->tearDownFiles();
1335 $this->backend
= $this->multiBackend
;
1336 $this->tearDownFiles();
1337 $this->doTestGetLocalCopy( $source, $content );
1338 $this->tearDownFiles();
1341 private function doTestGetLocalCopy( $source, $content ) {
1342 $backendName = $this->backendClass();
1344 $srcs = (array)$source;
1345 $content = (array)$content;
1346 foreach ( $srcs as $i => $src ) {
1347 $this->prepare( [ 'dir' => dirname( $src ) ] );
1348 $status = $this->backend
->doOperation(
1349 [ 'op' => 'create', 'content' => $content[$i], 'dst' => $src ] );
1350 $this->assertGoodStatus( $status,
1351 "Creation of file at $src succeeded ($backendName)." );
1354 if ( is_array( $source ) ) {
1355 $tmpFiles = $this->backend
->getLocalCopyMulti( [ 'srcs' => $source ] );
1356 foreach ( $tmpFiles as $path => $tmpFile ) {
1357 $this->assertNotNull( $tmpFile,
1358 "Creation of local copy of $path succeeded ($backendName)." );
1359 $contents = file_get_contents( $tmpFile->getPath() );
1360 $this->assertNotEquals( false, $contents, "Local copy of $path exists ($backendName)." );
1361 $this->assertEquals(
1362 current( $content ),
1364 "Local copy of $path is correct ($backendName)."
1368 $this->assertEquals(
1370 array_keys( $tmpFiles ),
1371 "Local copies in right order ($backendName)."
1373 $this->assertEquals(
1376 "Local copies array size correct ($backendName)."
1379 $tmpFile = $this->backend
->getLocalCopy( [ 'src' => $source ] );
1380 $this->assertNotNull( $tmpFile,
1381 "Creation of local copy of $source succeeded ($backendName)." );
1382 $contents = file_get_contents( $tmpFile->getPath() );
1383 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
1384 $this->assertEquals(
1387 "Local copy of $source is correct ($backendName)."
1391 $obj = new stdClass();
1392 $tmpFile->bind( $obj );
1395 public static function provider_testGetLocalCopy() {
1398 $base = self
::baseStorePath();
1399 $cases[] = [ "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" ];
1400 $cases[] = [ "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" ];
1401 $cases[] = [ "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" ];
1403 [ "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1404 "$base/unittest-cont1/e/a/z.txt" ],
1405 [ "contents xx $", "contents xy 111", "contents xz" ]
1412 * @dataProvider provider_testGetLocalReference
1414 public function testGetLocalReference( $source, $content ) {
1415 $this->backend
= $this->singleBackend
;
1416 $this->tearDownFiles();
1417 $this->doTestGetLocalReference( $source, $content );
1418 $this->tearDownFiles();
1420 $this->backend
= $this->multiBackend
;
1421 $this->tearDownFiles();
1422 $this->doTestGetLocalReference( $source, $content );
1423 $this->tearDownFiles();
1426 private function doTestGetLocalReference( $source, $content ) {
1427 $backendName = $this->backendClass();
1429 $srcs = (array)$source;
1430 $content = (array)$content;
1431 foreach ( $srcs as $i => $src ) {
1432 $this->prepare( [ 'dir' => dirname( $src ) ] );
1433 $status = $this->backend
->doOperation(
1434 [ 'op' => 'create', 'content' => $content[$i], 'dst' => $src ] );
1435 $this->assertGoodStatus( $status,
1436 "Creation of file at $src succeeded ($backendName)." );
1439 if ( is_array( $source ) ) {
1440 $tmpFiles = $this->backend
->getLocalReferenceMulti( [ 'srcs' => $source ] );
1441 foreach ( $tmpFiles as $path => $tmpFile ) {
1442 $this->assertNotNull( $tmpFile,
1443 "Creation of local copy of $path succeeded ($backendName)." );
1444 $contents = file_get_contents( $tmpFile->getPath() );
1445 $this->assertNotEquals( false, $contents, "Local ref of $path exists ($backendName)." );
1446 $this->assertEquals(
1447 current( $content ),
1449 "Local ref of $path is correct ($backendName)."
1453 $this->assertEquals(
1455 array_keys( $tmpFiles ),
1456 "Local refs in right order ($backendName)."
1458 $this->assertEquals(
1461 "Local refs array size correct ($backendName)."
1464 $tmpFile = $this->backend
->getLocalReference( [ 'src' => $source ] );
1465 $this->assertNotNull( $tmpFile,
1466 "Creation of local copy of $source succeeded ($backendName)." );
1467 $contents = file_get_contents( $tmpFile->getPath() );
1468 $this->assertNotEquals( false, $contents, "Local ref of $source exists ($backendName)." );
1469 $this->assertEquals( $content[0], $contents, "Local ref of $source is correct ($backendName)." );
1473 public static function provider_testGetLocalReference() {
1476 $base = self
::baseStorePath();
1477 $cases[] = [ "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" ];
1478 $cases[] = [ "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" ];
1479 $cases[] = [ "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" ];
1481 [ "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1482 "$base/unittest-cont1/e/a/z.txt" ],
1483 [ "contents xx 1111", "contents xy %", "contents xz $" ]
1489 public function testGetLocalCopyAndReference404() {
1490 $this->backend
= $this->singleBackend
;
1491 $this->tearDownFiles();
1492 $this->doTestGetLocalCopyAndReference404();
1493 $this->tearDownFiles();
1495 $this->backend
= $this->multiBackend
;
1496 $this->tearDownFiles();
1497 $this->doTestGetLocalCopyAndReference404();
1498 $this->tearDownFiles();
1501 public function doTestGetLocalCopyAndReference404() {
1502 $backendName = $this->backendClass();
1504 $base = self
::baseStorePath();
1506 $tmpFile = $this->backend
->getLocalCopy( [
1507 'src' => "$base/unittest-cont1/not-there" ] );
1508 $this->assertEquals( null, $tmpFile, "Local copy of not existing file is null ($backendName)." );
1510 $tmpFile = $this->backend
->getLocalReference( [
1511 'src' => "$base/unittest-cont1/not-there" ] );
1512 $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
1516 * @dataProvider provider_testGetFileHttpUrl
1518 public function testGetFileHttpUrl( $source, $content ) {
1519 $this->backend
= $this->singleBackend
;
1520 $this->tearDownFiles();
1521 $this->doTestGetFileHttpUrl( $source, $content );
1522 $this->tearDownFiles();
1524 $this->backend
= $this->multiBackend
;
1525 $this->tearDownFiles();
1526 $this->doTestGetFileHttpUrl( $source, $content );
1527 $this->tearDownFiles();
1530 private function doTestGetFileHttpUrl( $source, $content ) {
1531 $backendName = $this->backendClass();
1533 $this->prepare( [ 'dir' => dirname( $source ) ] );
1534 $status = $this->backend
->doOperation(
1535 [ 'op' => 'create', 'content' => $content, 'dst' => $source ] );
1536 $this->assertGoodStatus( $status,
1537 "Creation of file at $source succeeded ($backendName)." );
1539 $url = $this->backend
->getFileHttpUrl( [ 'src' => $source ] );
1541 if ( $url !== null ) { // supported
1542 $data = MediaWikiServices
::getInstance()->getHttpRequestFactory()->
1543 get( $url, [], __METHOD__
);
1544 $this->assertEquals( $content, $data,
1545 "HTTP GET of URL has right contents ($backendName)." );
1549 public static function provider_testGetFileHttpUrl() {
1552 $base = self
::baseStorePath();
1553 $cases[] = [ "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" ];
1554 $cases[] = [ "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" ];
1555 $cases[] = [ "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" ];
1561 * @dataProvider provider_testPrepareAndClean
1563 public function testPrepareAndClean( $path, $isOK ) {
1564 $this->backend
= $this->singleBackend
;
1565 $this->doTestPrepareAndClean( $path, $isOK );
1566 $this->tearDownFiles();
1568 $this->backend
= $this->multiBackend
;
1569 $this->doTestPrepareAndClean( $path, $isOK );
1570 $this->tearDownFiles();
1573 public static function provider_testPrepareAndClean() {
1574 $base = self
::baseStorePath();
1577 [ "$base/unittest-cont1/e/a/z/some_file1.txt", true ],
1578 [ "$base/unittest-cont2/a/z/some_file2.txt", true ],
1579 # Specific to FS backend with no basePath field set
1580 # [ "$base/unittest-cont3/a/z/some_file3.txt", false ],
1584 private function doTestPrepareAndClean( $path, $isOK ) {
1585 $backendName = $this->backendClass();
1587 $status = $this->prepare( [ 'dir' => dirname( $path ) ] );
1589 $this->assertGoodStatus( $status,
1590 "Preparing dir $path succeeded without warnings ($backendName)." );
1591 $this->assertEquals( true, $status->isOK(),
1592 "Preparing dir $path succeeded ($backendName)." );
1594 $this->assertEquals( false, $status->isOK(),
1595 "Preparing dir $path failed ($backendName)." );
1598 $status = $this->backend
->secure( [ 'dir' => dirname( $path ) ] );
1600 $this->assertGoodStatus( $status,
1601 "Securing dir $path succeeded without warnings ($backendName)." );
1602 $this->assertEquals( true, $status->isOK(),
1603 "Securing dir $path succeeded ($backendName)." );
1605 $this->assertEquals( false, $status->isOK(),
1606 "Securing dir $path failed ($backendName)." );
1609 $status = $this->backend
->publish( [ 'dir' => dirname( $path ) ] );
1611 $this->assertGoodStatus( $status,
1612 "Publishing dir $path succeeded without warnings ($backendName)." );
1613 $this->assertEquals( true, $status->isOK(),
1614 "Publishing dir $path succeeded ($backendName)." );
1616 $this->assertEquals( false, $status->isOK(),
1617 "Publishing dir $path failed ($backendName)." );
1620 $status = $this->backend
->clean( [ 'dir' => dirname( $path ) ] );
1622 $this->assertGoodStatus( $status,
1623 "Cleaning dir $path succeeded without warnings ($backendName)." );
1624 $this->assertEquals( true, $status->isOK(),
1625 "Cleaning dir $path succeeded ($backendName)." );
1627 $this->assertEquals( false, $status->isOK(),
1628 "Cleaning dir $path failed ($backendName)." );
1632 public function testRecursiveClean() {
1633 $this->backend
= $this->singleBackend
;
1634 $this->doTestRecursiveClean();
1635 $this->tearDownFiles();
1637 $this->backend
= $this->multiBackend
;
1638 $this->doTestRecursiveClean();
1639 $this->tearDownFiles();
1642 private function doTestRecursiveClean() {
1643 $backendName = $this->backendClass();
1645 $base = self
::baseStorePath();
1647 "$base/unittest-cont1",
1648 "$base/unittest-cont1/e",
1649 "$base/unittest-cont1/e/a",
1650 "$base/unittest-cont1/e/a/b",
1651 "$base/unittest-cont1/e/a/b/c",
1652 "$base/unittest-cont1/e/a/b/c/d0",
1653 "$base/unittest-cont1/e/a/b/c/d1",
1654 "$base/unittest-cont1/e/a/b/c/d2",
1655 "$base/unittest-cont1/e/a/b/c/d0/1",
1656 "$base/unittest-cont1/e/a/b/c/d0/2",
1657 "$base/unittest-cont1/e/a/b/c/d1/3",
1658 "$base/unittest-cont1/e/a/b/c/d1/4",
1659 "$base/unittest-cont1/e/a/b/c/d2/5",
1660 "$base/unittest-cont1/e/a/b/c/d2/6"
1662 foreach ( $dirs as $dir ) {
1663 $status = $this->prepare( [ 'dir' => $dir ] );
1664 $this->assertGoodStatus( $status,
1665 "Preparing dir $dir succeeded without warnings ($backendName)." );
1668 if ( $this->backend
instanceof FSFileBackend
) {
1669 foreach ( $dirs as $dir ) {
1670 $this->assertEquals( true, $this->backend
->directoryExists( [ 'dir' => $dir ] ),
1671 "Dir $dir exists ($backendName)." );
1675 $status = $this->backend
->clean(
1676 [ 'dir' => "$base/unittest-cont1", 'recursive' => 1 ] );
1677 $this->assertGoodStatus( $status,
1678 "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
1680 foreach ( $dirs as $dir ) {
1681 $this->assertEquals( false, $this->backend
->directoryExists( [ 'dir' => $dir ] ),
1682 "Dir $dir no longer exists ($backendName)." );
1686 public function testDoOperations() {
1687 $this->backend
= $this->singleBackend
;
1688 $this->tearDownFiles();
1689 $this->doTestDoOperations();
1690 $this->tearDownFiles();
1692 $this->backend
= $this->multiBackend
;
1693 $this->tearDownFiles();
1694 $this->doTestDoOperations();
1695 $this->tearDownFiles();
1698 private function doTestDoOperations() {
1699 $base = self
::baseStorePath();
1701 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1702 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1703 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1704 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1705 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1706 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1707 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1709 $this->prepare( [ 'dir' => dirname( $fileA ) ] );
1710 $this->create( [ 'dst' => $fileA, 'content' => $fileAContents ] );
1711 $this->prepare( [ 'dir' => dirname( $fileB ) ] );
1712 $this->create( [ 'dst' => $fileB, 'content' => $fileBContents ] );
1713 $this->prepare( [ 'dir' => dirname( $fileC ) ] );
1714 $this->create( [ 'dst' => $fileC, 'content' => $fileCContents ] );
1715 $this->prepare( [ 'dir' => dirname( $fileD ) ] );
1717 $status = $this->backend
->doOperations( [
1718 [ 'op' => 'describe', 'src' => $fileA,
1719 'headers' => [ 'X-Content-Length' => '91.3' ], 'disposition' => 'inline' ],
1720 [ 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ],
1721 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1722 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ],
1723 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1724 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ],
1725 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1726 [ 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ],
1727 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1728 [ 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ],
1729 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1730 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ],
1731 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1732 [ 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ],
1733 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1734 [ 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ],
1735 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1736 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ],
1738 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ],
1740 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ],
1742 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ],
1748 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1749 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1750 $this->assertEquals( 14, count( $status->success
),
1751 "Operation batch has correct success array" );
1753 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileA ] ),
1754 "File does not exist at $fileA" );
1755 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileB ] ),
1756 "File does not exist at $fileB" );
1757 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileD ] ),
1758 "File does not exist at $fileD" );
1760 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $fileC ] ),
1761 "File exists at $fileC" );
1762 $this->assertEquals( $fileBContents,
1763 $this->backend
->getFileContents( [ 'src' => $fileC ] ),
1764 "Correct file contents of $fileC" );
1765 $this->assertEquals( strlen( $fileBContents ),
1766 $this->backend
->getFileSize( [ 'src' => $fileC ] ),
1767 "Correct file size of $fileC" );
1768 $this->assertEquals( Wikimedia\base_convert
( sha1( $fileBContents ), 16, 36, 31 ),
1769 $this->backend
->getFileSha1Base36( [ 'src' => $fileC ] ),
1770 "Correct file SHA-1 of $fileC" );
1773 public function testDoOperationsPipeline() {
1774 $this->backend
= $this->singleBackend
;
1775 $this->tearDownFiles();
1776 $this->doTestDoOperationsPipeline();
1777 $this->tearDownFiles();
1779 $this->backend
= $this->multiBackend
;
1780 $this->tearDownFiles();
1781 $this->doTestDoOperationsPipeline();
1782 $this->tearDownFiles();
1785 // concurrency orientated
1786 private function doTestDoOperationsPipeline() {
1787 $base = self
::baseStorePath();
1789 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1790 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1791 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1793 $tmpNameA = TempFSFile
::factory( "unittests_", 'txt', wfTempDir() )->getPath();
1794 $tmpNameB = TempFSFile
::factory( "unittests_", 'txt', wfTempDir() )->getPath();
1795 $tmpNameC = TempFSFile
::factory( "unittests_", 'txt', wfTempDir() )->getPath();
1796 $this->addTmpFiles( [ $tmpNameA, $tmpNameB, $tmpNameC ] );
1797 file_put_contents( $tmpNameA, $fileAContents );
1798 file_put_contents( $tmpNameB, $fileBContents );
1799 file_put_contents( $tmpNameC, $fileCContents );
1801 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1802 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1803 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1804 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1806 $this->prepare( [ 'dir' => dirname( $fileA ) ] );
1807 $this->create( [ 'dst' => $fileA, 'content' => $fileAContents ] );
1808 $this->prepare( [ 'dir' => dirname( $fileB ) ] );
1809 $this->prepare( [ 'dir' => dirname( $fileC ) ] );
1810 $this->prepare( [ 'dir' => dirname( $fileD ) ] );
1812 $status = $this->backend
->doOperations( [
1813 [ 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ],
1814 [ 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ],
1815 [ 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ],
1816 [ 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ],
1817 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1818 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ],
1819 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1820 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ],
1821 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1822 [ 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ],
1823 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1824 [ 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ],
1825 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1826 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ],
1827 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1828 [ 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ],
1829 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1830 [ 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ],
1831 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1832 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ],
1834 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ],
1836 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ],
1838 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ],
1844 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1845 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1846 $this->assertEquals( 16, count( $status->success
),
1847 "Operation batch has correct success array" );
1849 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileA ] ),
1850 "File does not exist at $fileA" );
1851 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileB ] ),
1852 "File does not exist at $fileB" );
1853 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileD ] ),
1854 "File does not exist at $fileD" );
1856 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $fileC ] ),
1857 "File exists at $fileC" );
1858 $this->assertEquals( $fileBContents,
1859 $this->backend
->getFileContents( [ 'src' => $fileC ] ),
1860 "Correct file contents of $fileC" );
1861 $this->assertEquals( strlen( $fileBContents ),
1862 $this->backend
->getFileSize( [ 'src' => $fileC ] ),
1863 "Correct file size of $fileC" );
1864 $this->assertEquals( Wikimedia\base_convert
( sha1( $fileBContents ), 16, 36, 31 ),
1865 $this->backend
->getFileSha1Base36( [ 'src' => $fileC ] ),
1866 "Correct file SHA-1 of $fileC" );
1869 public function testDoOperationsFailing() {
1870 $this->backend
= $this->singleBackend
;
1871 $this->tearDownFiles();
1872 $this->doTestDoOperationsFailing();
1873 $this->tearDownFiles();
1875 $this->backend
= $this->multiBackend
;
1876 $this->tearDownFiles();
1877 $this->doTestDoOperationsFailing();
1878 $this->tearDownFiles();
1881 private function doTestDoOperationsFailing() {
1882 $base = self
::baseStorePath();
1884 $fileA = "$base/unittest-cont2/a/b/fileA.txt";
1885 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1886 $fileB = "$base/unittest-cont2/a/b/fileB.txt";
1887 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1888 $fileC = "$base/unittest-cont2/a/b/fileC.txt";
1889 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1890 $fileD = "$base/unittest-cont2/a/b/fileD.txt";
1892 $this->prepare( [ 'dir' => dirname( $fileA ) ] );
1893 $this->create( [ 'dst' => $fileA, 'content' => $fileAContents ] );
1894 $this->prepare( [ 'dir' => dirname( $fileB ) ] );
1895 $this->create( [ 'dst' => $fileB, 'content' => $fileBContents ] );
1896 $this->prepare( [ 'dir' => dirname( $fileC ) ] );
1897 $this->create( [ 'dst' => $fileC, 'content' => $fileCContents ] );
1899 $status = $this->backend
->doOperations( [
1900 [ 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ],
1901 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1902 [ 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ],
1903 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1904 [ 'op' => 'copy', 'src' => $fileB, 'dst' => $fileD, 'overwrite' => 1 ],
1905 // Now: A:<A>, B:<B>, C:<A>, D:<B>
1906 [ 'op' => 'move', 'src' => $fileC, 'dst' => $fileD ],
1907 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1908 [ 'op' => 'move', 'src' => $fileB, 'dst' => $fileC, 'overwriteSame' => 1 ],
1909 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1910 [ 'op' => 'move', 'src' => $fileB, 'dst' => $fileA, 'overwrite' => 1 ],
1911 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1912 [ 'op' => 'delete', 'src' => $fileD ],
1913 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1916 ], [ 'force' => 1 ] );
1918 $this->assertNotEquals( [], $status->getErrors(), "Operation had warnings" );
1919 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1920 $this->assertEquals( 8, count( $status->success
),
1921 "Operation batch has correct success array" );
1923 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileB ] ),
1924 "File does not exist at $fileB" );
1925 $this->assertEquals( false, $this->backend
->fileExists( [ 'src' => $fileD ] ),
1926 "File does not exist at $fileD" );
1928 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $fileA ] ),
1929 "File does not exist at $fileA" );
1930 $this->assertEquals( true, $this->backend
->fileExists( [ 'src' => $fileC ] ),
1931 "File exists at $fileC" );
1932 $this->assertEquals( $fileBContents,
1933 $this->backend
->getFileContents( [ 'src' => $fileA ] ),
1934 "Correct file contents of $fileA" );
1935 $this->assertEquals( strlen( $fileBContents ),
1936 $this->backend
->getFileSize( [ 'src' => $fileA ] ),
1937 "Correct file size of $fileA" );
1938 $this->assertEquals( Wikimedia\base_convert
( sha1( $fileBContents ), 16, 36, 31 ),
1939 $this->backend
->getFileSha1Base36( [ 'src' => $fileA ] ),
1940 "Correct file SHA-1 of $fileA" );
1943 public function testGetFileList() {
1944 $this->backend
= $this->singleBackend
;
1945 $this->tearDownFiles();
1946 $this->doTestGetFileList();
1947 $this->tearDownFiles();
1949 $this->backend
= $this->multiBackend
;
1950 $this->tearDownFiles();
1951 $this->doTestGetFileList();
1952 $this->tearDownFiles();
1955 private function doTestGetFileList() {
1956 $backendName = $this->backendClass();
1957 $base = self
::baseStorePath();
1959 // Should have no errors
1960 $iter = $this->backend
->getFileList( [ 'dir' => "$base/unittest-cont-notexists" ] );
1963 "$base/unittest-cont1/e/test1.txt",
1964 "$base/unittest-cont1/e/test2.txt",
1965 "$base/unittest-cont1/e/test3.txt",
1966 "$base/unittest-cont1/e/subdir1/test1.txt",
1967 "$base/unittest-cont1/e/subdir1/test2.txt",
1968 "$base/unittest-cont1/e/subdir2/test3.txt",
1969 "$base/unittest-cont1/e/subdir2/test4.txt",
1970 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
1971 "$base/unittest-cont1/e/subdir2/subdir/test2.txt",
1972 "$base/unittest-cont1/e/subdir2/subdir/test3.txt",
1973 "$base/unittest-cont1/e/subdir2/subdir/test4.txt",
1974 "$base/unittest-cont1/e/subdir2/subdir/test5.txt",
1975 "$base/unittest-cont1/e/subdir2/subdir/sub/test0.txt",
1976 "$base/unittest-cont1/e/subdir2/subdir/sub/120-px-file.txt",
1981 foreach ( $files as $file ) {
1982 $this->prepare( [ 'dir' => dirname( $file ) ] );
1983 $ops[] = [ 'op' => 'create', 'content' => 'xxy', 'dst' => $file ];
1985 $status = $this->backend
->doQuickOperations( $ops );
1986 $this->assertGoodStatus( $status,
1987 "Creation of files succeeded ($backendName)." );
1988 $this->assertEquals( true, $status->isOK(),
1989 "Creation of files succeeded with OK status ($backendName)." );
1991 // Expected listing at root
1996 "e/subdir1/test1.txt",
1997 "e/subdir1/test2.txt",
1998 "e/subdir2/test3.txt",
1999 "e/subdir2/test4.txt",
2000 "e/subdir2/subdir/test1.txt",
2001 "e/subdir2/subdir/test2.txt",
2002 "e/subdir2/subdir/test3.txt",
2003 "e/subdir2/subdir/test4.txt",
2004 "e/subdir2/subdir/test5.txt",
2005 "e/subdir2/subdir/sub/test0.txt",
2006 "e/subdir2/subdir/sub/120-px-file.txt",
2010 // Actual listing (no trailing slash) at root
2011 $iter = $this->backend
->getFileList( [ 'dir' => "$base/unittest-cont1" ] );
2012 $list = $this->listToArray( $iter );
2014 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2016 // Actual listing (no trailing slash) at root with advise
2017 $iter = $this->backend
->getFileList( [
2018 'dir' => "$base/unittest-cont1",
2021 $list = $this->listToArray( $iter );
2023 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2025 // Actual listing (with trailing slash) at root
2027 $iter = $this->backend
->getFileList( [ 'dir' => "$base/unittest-cont1/" ] );
2028 foreach ( $iter as $file ) {
2032 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2034 // Expected listing at subdir
2042 "sub/120-px-file.txt",
2046 // Actual listing (no trailing slash) at subdir
2047 $iter = $this->backend
->getFileList( [ 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ] );
2048 $list = $this->listToArray( $iter );
2050 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2052 // Actual listing (no trailing slash) at subdir with advise
2053 $iter = $this->backend
->getFileList( [
2054 'dir' => "$base/unittest-cont1/e/subdir2/subdir",
2057 $list = $this->listToArray( $iter );
2059 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2061 // Actual listing (with trailing slash) at subdir
2063 $iter = $this->backend
->getFileList( [ 'dir' => "$base/unittest-cont1/e/subdir2/subdir/" ] );
2064 foreach ( $iter as $file ) {
2068 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2070 // Actual listing (using iterator second time)
2071 $list = $this->listToArray( $iter );
2073 $this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
2075 // Actual listing (top files only) at root
2076 $iter = $this->backend
->getTopFileList( [ 'dir' => "$base/unittest-cont1" ] );
2077 $list = $this->listToArray( $iter );
2079 $this->assertEquals( [], $list, "Correct top file listing ($backendName)." );
2081 // Expected listing (top files only) at subdir
2091 // Actual listing (top files only) at subdir
2092 $iter = $this->backend
->getTopFileList(
2093 [ 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ]
2095 $list = $this->listToArray( $iter );
2097 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2099 // Actual listing (top files only) at subdir with advise
2100 $iter = $this->backend
->getTopFileList( [
2101 'dir' => "$base/unittest-cont1/e/subdir2/subdir",
2104 $list = $this->listToArray( $iter );
2106 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2108 foreach ( $files as $file ) { // clean up
2109 $this->backend
->doOperation( [ 'op' => 'delete', 'src' => $file ] );
2112 $iter = $this->backend
->getFileList( [ 'dir' => "$base/unittest-cont1/not/exists" ] );
2113 foreach ( $iter as $iter ) {
2118 public function testGetDirectoryList() {
2119 $this->backend
= $this->singleBackend
;
2120 $this->tearDownFiles();
2121 $this->doTestGetDirectoryList();
2122 $this->tearDownFiles();
2124 $this->backend
= $this->multiBackend
;
2125 $this->tearDownFiles();
2126 $this->doTestGetDirectoryList();
2127 $this->tearDownFiles();
2130 private function doTestGetDirectoryList() {
2131 $backendName = $this->backendClass();
2133 $base = self
::baseStorePath();
2135 "$base/unittest-cont1/e/test1.txt",
2136 "$base/unittest-cont1/e/test2.txt",
2137 "$base/unittest-cont1/e/test3.txt",
2138 "$base/unittest-cont1/e/subdir1/test1.txt",
2139 "$base/unittest-cont1/e/subdir1/test2.txt",
2140 "$base/unittest-cont1/e/subdir2/test3.txt",
2141 "$base/unittest-cont1/e/subdir2/test4.txt",
2142 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
2143 "$base/unittest-cont1/e/subdir3/subdir/test2.txt",
2144 "$base/unittest-cont1/e/subdir4/subdir/test3.txt",
2145 "$base/unittest-cont1/e/subdir4/subdir/test4.txt",
2146 "$base/unittest-cont1/e/subdir4/subdir/test5.txt",
2147 "$base/unittest-cont1/e/subdir4/subdir/sub/test0.txt",
2148 "$base/unittest-cont1/e/subdir4/subdir/sub/120-px-file.txt",
2153 foreach ( $files as $file ) {
2154 $this->prepare( [ 'dir' => dirname( $file ) ] );
2155 $ops[] = [ 'op' => 'create', 'content' => 'xxy', 'dst' => $file ];
2157 $status = $this->backend
->doQuickOperations( $ops );
2158 $this->assertGoodStatus( $status,
2159 "Creation of files succeeded ($backendName)." );
2160 $this->assertEquals( true, $status->isOK(),
2161 "Creation of files succeeded with OK status ($backendName)." );
2163 $this->assertEquals( true,
2164 $this->backend
->directoryExists( [ 'dir' => "$base/unittest-cont1/e/subdir1" ] ),
2165 "Directory exists in ($backendName)." );
2166 $this->assertEquals( true,
2167 $this->backend
->directoryExists( [ 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ] ),
2168 "Directory exists in ($backendName)." );
2169 $this->assertEquals( false,
2170 $this->backend
->directoryExists( [ 'dir' => "$base/unittest-cont1/e/subdir2/test1.txt" ] ),
2171 "Directory does not exists in ($backendName)." );
2179 // Actual listing (no trailing slash)
2181 $iter = $this->backend
->getTopDirectoryList( [ 'dir' => "$base/unittest-cont1" ] );
2182 foreach ( $iter as $file ) {
2187 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2198 // Actual listing (no trailing slash)
2200 $iter = $this->backend
->getTopDirectoryList( [ 'dir' => "$base/unittest-cont1/e" ] );
2201 foreach ( $iter as $file ) {
2206 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2208 // Actual listing (with trailing slash)
2210 $iter = $this->backend
->getTopDirectoryList( [ 'dir' => "$base/unittest-cont1/e/" ] );
2211 foreach ( $iter as $file ) {
2216 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2224 // Actual listing (no trailing slash)
2226 $iter = $this->backend
->getTopDirectoryList( [ 'dir' => "$base/unittest-cont1/e/subdir2" ] );
2227 foreach ( $iter as $file ) {
2232 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2234 // Actual listing (with trailing slash)
2236 $iter = $this->backend
->getTopDirectoryList(
2237 [ 'dir' => "$base/unittest-cont1/e/subdir2/" ]
2240 foreach ( $iter as $file ) {
2245 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2247 // Actual listing (using iterator second time)
2249 foreach ( $iter as $file ) {
2254 $this->assertEquals(
2257 "Correct top dir listing ($backendName), second iteration."
2260 // Expected listing (recursive)
2270 "e/subdir4/subdir/sub",
2274 // Actual listing (recursive)
2276 $iter = $this->backend
->getDirectoryList( [ 'dir' => "$base/unittest-cont1/" ] );
2277 foreach ( $iter as $file ) {
2282 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2284 // Expected listing (recursive)
2291 // Actual listing (recursive)
2293 $iter = $this->backend
->getDirectoryList( [ 'dir' => "$base/unittest-cont1/e/subdir4" ] );
2294 foreach ( $iter as $file ) {
2299 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2301 // Actual listing (recursive, second time)
2303 foreach ( $iter as $file ) {
2308 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2310 $iter = $this->backend
->getDirectoryList( [ 'dir' => "$base/unittest-cont1/e/subdir1" ] );
2311 $items = $this->listToArray( $iter );
2312 $this->assertEquals( [], $items, "Directory listing is empty." );
2314 foreach ( $files as $file ) { // clean up
2315 $this->backend
->doOperation( [ 'op' => 'delete', 'src' => $file ] );
2318 $iter = $this->backend
->getDirectoryList( [ 'dir' => "$base/unittest-cont1/not/exists" ] );
2319 foreach ( $iter as $file ) {
2323 $items = $this->listToArray( $iter );
2324 $this->assertEquals( [], $items, "Directory listing is empty." );
2326 $iter = $this->backend
->getDirectoryList( [ 'dir' => "$base/unittest-cont1/e/not/exists" ] );
2327 $items = $this->listToArray( $iter );
2328 $this->assertEquals( [], $items, "Directory listing is empty." );
2331 public function testLockCalls() {
2332 $this->backend
= $this->singleBackend
;
2333 $this->doTestLockCalls();
2336 private function doTestLockCalls() {
2337 $backendName = $this->backendClass();
2338 $base = $this->backend
->getContainerStoragePath( 'test' );
2345 "$base/subdir1", // duplicate
2346 "$base/subdir1/test1.txt",
2347 "$base/subdir1/test2.txt",
2349 "$base/subdir2", // duplicate
2350 "$base/subdir2/test3.txt",
2351 "$base/subdir2/test4.txt",
2352 "$base/subdir2/subdir",
2353 "$base/subdir2/subdir/test1.txt",
2354 "$base/subdir2/subdir/test2.txt",
2355 "$base/subdir2/subdir/test3.txt",
2356 "$base/subdir2/subdir/test4.txt",
2357 "$base/subdir2/subdir/test5.txt",
2358 "$base/subdir2/subdir/sub",
2359 "$base/subdir2/subdir/sub/test0.txt",
2360 "$base/subdir2/subdir/sub/120-px-file.txt",
2363 for ( $i = 0; $i < 25; $i++
) {
2364 $status = $this->backend
->lockFiles( $paths, LockManager
::LOCK_EX
);
2365 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2366 "Locking of files succeeded ($backendName) ($i)." );
2367 $this->assertEquals( true, $status->isOK(),
2368 "Locking of files succeeded with OK status ($backendName) ($i)." );
2370 $status = $this->backend
->lockFiles( $paths, LockManager
::LOCK_SH
);
2371 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2372 "Locking of files succeeded ($backendName) ($i)." );
2373 $this->assertEquals( true, $status->isOK(),
2374 "Locking of files succeeded with OK status ($backendName) ($i)." );
2376 $status = $this->backend
->unlockFiles( $paths, LockManager
::LOCK_SH
);
2377 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2378 "Locking of files succeeded ($backendName) ($i)." );
2379 $this->assertEquals( true, $status->isOK(),
2380 "Locking of files succeeded with OK status ($backendName) ($i)." );
2382 $status = $this->backend
->unlockFiles( $paths, LockManager
::LOCK_EX
);
2383 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2384 "Locking of files succeeded ($backendName). ($i)" );
2385 $this->assertEquals( true, $status->isOK(),
2386 "Locking of files succeeded with OK status ($backendName) ($i)." );
2388 # # Flip the acquire/release ordering around ##
2390 $status = $this->backend
->lockFiles( $paths, LockManager
::LOCK_SH
);
2391 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2392 "Locking of files succeeded ($backendName) ($i)." );
2393 $this->assertEquals( true, $status->isOK(),
2394 "Locking of files succeeded with OK status ($backendName) ($i)." );
2396 $status = $this->backend
->lockFiles( $paths, LockManager
::LOCK_EX
);
2397 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2398 "Locking of files succeeded ($backendName) ($i)." );
2399 $this->assertEquals( true, $status->isOK(),
2400 "Locking of files succeeded with OK status ($backendName) ($i)." );
2402 $status = $this->backend
->unlockFiles( $paths, LockManager
::LOCK_EX
);
2403 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2404 "Locking of files succeeded ($backendName). ($i)" );
2405 $this->assertEquals( true, $status->isOK(),
2406 "Locking of files succeeded with OK status ($backendName) ($i)." );
2408 $status = $this->backend
->unlockFiles( $paths, LockManager
::LOCK_SH
);
2409 $this->assertEquals( print_r( [], true ), print_r( $status->getErrors(), true ),
2410 "Locking of files succeeded ($backendName) ($i)." );
2411 $this->assertEquals( true, $status->isOK(),
2412 "Locking of files succeeded with OK status ($backendName) ($i)." );
2415 $status = Status
::newGood();
2416 $sl = $this->backend
->getScopedFileLocks( $paths, LockManager
::LOCK_EX
, $status );
2417 $this->assertInstanceOf( ScopedLock
::class, $sl,
2418 "Scoped locking of files succeeded ($backendName)." );
2419 $this->assertEquals( [], $status->getErrors(),
2420 "Scoped locking of files succeeded ($backendName)." );
2421 $this->assertEquals( true, $status->isOK(),
2422 "Scoped locking of files succeeded with OK status ($backendName)." );
2424 ScopedLock
::release( $sl );
2425 $this->assertEquals( null, $sl,
2426 "Scoped unlocking of files succeeded ($backendName)." );
2427 $this->assertEquals( [], $status->getErrors(),
2428 "Scoped unlocking of files succeeded ($backendName)." );
2429 $this->assertEquals( true, $status->isOK(),
2430 "Scoped unlocking of files succeeded with OK status ($backendName)." );
2434 * @dataProvider provider_testGetContentType
2436 public function testGetContentType( $mimeCallback, $mimeFromString ) {
2439 $be = TestingAccessWrapper
::newFromObject( new MemoryFileBackend(
2441 'name' => 'testing',
2442 'class' => MemoryFileBackend
::class,
2444 'mimeCallback' => $mimeCallback
2448 $dst = 'mwstore://testing/container/path/to/file_no_ext';
2449 $src = "$IP/tests/phpunit/data/media/srgb.jpg";
2450 $this->assertEquals( 'image/jpeg', $be->getContentType( $dst, null, $src ) );
2451 $this->assertEquals(
2452 $mimeFromString ?
'image/jpeg' : 'unknown/unknown',
2453 $be->getContentType( $dst, file_get_contents( $src ), null ) );
2455 $src = "$IP/tests/phpunit/data/media/Png-native-test.png";
2456 $this->assertEquals( 'image/png', $be->getContentType( $dst, null, $src ) );
2457 $this->assertEquals(
2458 $mimeFromString ?
'image/png' : 'unknown/unknown',
2459 $be->getContentType( $dst, file_get_contents( $src ), null ) );
2462 public static function provider_testGetContentType() {
2465 [ [ FileBackendGroup
::singleton(), 'guessMimeInternal' ], true ]
2469 public function testReadAffinity() {
2470 $be = TestingAccessWrapper
::newFromObject(
2471 new FileBackendMultiWrite( [
2472 'name' => 'localtesting',
2473 'wikiId' => wfWikiID() . mt_rand(),
2476 'name' => 'multitesting0',
2477 'class' => MemoryFileBackend
::class,
2478 'isMultiMaster' => false,
2479 'readAffinity' => true
2482 'name' => 'multitesting1',
2483 'class' => MemoryFileBackend
::class,
2484 'isMultiMaster' => true
2490 $this->assertEquals(
2492 $be->getReadIndexFromParams( [ 'latest' => 1 ] ),
2493 'Reads with "latest" flag use backend 1'
2495 $this->assertEquals(
2497 $be->getReadIndexFromParams( [ 'latest' => 0 ] ),
2498 'Reads without "latest" flag use backend 0'
2501 $p = 'container/test-cont/file.txt';
2502 $be->backends
[0]->quickCreate( [
2503 'dst' => "mwstore://multitesting0/$p", 'content' => 'cattitude' ] );
2504 $be->backends
[1]->quickCreate( [
2505 'dst' => "mwstore://multitesting1/$p", 'content' => 'princess of power' ] );
2507 $this->assertEquals(
2509 $be->getFileContents( [ 'src' => "mwstore://localtesting/$p" ] ),
2510 "Non-latest read came from backend 0"
2512 $this->assertEquals(
2513 'princess of power',
2514 $be->getFileContents( [ 'src' => "mwstore://localtesting/$p", 'latest' => 1 ] ),
2515 "Latest read came from backend1"
2519 public function testAsyncWrites() {
2520 $be = TestingAccessWrapper
::newFromObject(
2521 new FileBackendMultiWrite( [
2522 'name' => 'localtesting',
2523 'wikiId' => wfWikiID() . mt_rand(),
2526 'name' => 'multitesting0',
2527 'class' => MemoryFileBackend
::class,
2528 'isMultiMaster' => false
2531 'name' => 'multitesting1',
2532 'class' => MemoryFileBackend
::class,
2533 'isMultiMaster' => true
2536 'replication' => 'async'
2540 $this->setMwGlobals( 'wgCommandLineMode', false );
2542 $p = 'container/test-cont/file.txt';
2544 'dst' => "mwstore://localtesting/$p", 'content' => 'cattitude' ] );
2546 $this->assertEquals(
2548 $be->backends
[0]->getFileContents( [ 'src' => "mwstore://multitesting0/$p" ] ),
2549 "File not yet written to backend 0"
2551 $this->assertEquals(
2553 $be->backends
[1]->getFileContents( [ 'src' => "mwstore://multitesting1/$p" ] ),
2554 "File already written to backend 1"
2557 DeferredUpdates
::doUpdates();
2559 $this->assertEquals(
2561 $be->backends
[0]->getFileContents( [ 'src' => "mwstore://multitesting0/$p" ] ),
2562 "File now written to backend 0"
2566 public function testSanitizeOpHeaders() {
2567 $be = TestingAccessWrapper
::newFromObject( new MemoryFileBackend( [
2568 'name' => 'localtesting',
2569 'wikiId' => wfWikiID()
2574 'content-Disposition' => FileBackend
::makeContentDisposition( 'inline', 'name' ),
2575 'Content-dUration' => 25.6,
2576 'X-LONG-VALUE' => str_pad( '0', 300 ),
2577 'CONTENT-LENGTH' => 855055,
2582 'content-disposition' => FileBackend
::makeContentDisposition( 'inline', 'name' ),
2583 'content-duration' => 25.6,
2584 'content-length' => 855055
2588 Wikimedia\
suppressWarnings();
2589 $actual = $be->sanitizeOpHeaders( $input );
2590 Wikimedia\restoreWarnings
();
2592 $this->assertEquals( $expected, $actual, "Header sanitized properly" );
2596 private function listToArray( $iter ) {
2597 return is_array( $iter ) ?
$iter : iterator_to_array( $iter );
2600 // test helper wrapper for backend prepare() function
2601 private function prepare( array $params ) {
2602 return $this->backend
->prepare( $params );
2605 // test helper wrapper for backend prepare() function
2606 private function create( array $params ) {
2607 $params['op'] = 'create';
2609 return $this->backend
->doQuickOperations( [ $params ] );
2612 function tearDownFiles() {
2613 $containers = [ 'unittest-cont1', 'unittest-cont2', 'unittest-cont-bad' ];
2614 foreach ( $containers as $container ) {
2615 $this->deleteFiles( $container );
2619 private function deleteFiles( $container ) {
2620 $base = self
::baseStorePath();
2621 $iter = $this->backend
->getFileList( [ 'dir' => "$base/$container" ] );
2623 foreach ( $iter as $file ) {
2624 $this->backend
->quickDelete( [ 'src' => "$base/$container/$file" ] );
2626 // free the directory, to avoid Permission denied under windows on rmdir
2629 $this->backend
->clean( [ 'dir' => "$base/$container", 'recursive' => 1 ] );
2632 function assertBackendPathsConsistent( array $paths ) {
2633 if ( $this->backend
instanceof FileBackendMultiWrite
) {
2634 $status = $this->backend
->consistencyCheck( $paths );
2635 $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
2639 function assertGoodStatus( StatusValue
$status, $msg ) {
2640 $this->assertEquals( print_r( [], 1 ), print_r( $status->getErrors(), 1 ), $msg );