Merge "DatabaseMssql: Don't duplicate body of makeList()"
[mediawiki.git] / tests / phpunit / includes / filebackend / FileBackendTest.php
blobb40d2d213ef27e3c6a96ca2818299d67c3d0a6ca
1 <?php
3 /**
4 * @group FileRepo
5 * @group FileBackend
6 * @group medium
7 */
8 class FileBackendTest extends MediaWikiTestCase {
10 /** @var FileBackend */
11 private $backend;
12 /** @var FileBackendMultiWrite */
13 private $multiBackend;
14 /** @var FSFileBackend */
15 public $singleBackend;
16 private static $backendToUse;
18 protected function setUp() {
19 global $wgFileBackends;
20 parent::setUp();
21 $uniqueId = time() . '-' . mt_rand();
22 $tmpDir = $this->getNewTempDirectory();
23 if ( $this->getCliArg( 'use-filebackend' ) ) {
24 if ( self::$backendToUse ) {
25 $this->singleBackend = self::$backendToUse;
26 } else {
27 $name = $this->getCliArg( 'use-filebackend' );
28 $useConfig = array();
29 foreach ( $wgFileBackends as $conf ) {
30 if ( $conf['name'] == $name ) {
31 $useConfig = $conf;
32 break;
35 $useConfig['name'] = 'localtesting'; // swap name
36 $useConfig['shardViaHashLevels'] = array( // test sharding
37 'unittest-cont1' => array( 'levels' => 1, 'base' => 16, 'repeat' => 1 )
39 if ( isset( $useConfig['fileJournal'] ) ) {
40 $useConfig['fileJournal'] = FileJournal::factory( $useConfig['fileJournal'], $name );
42 $useConfig['lockManager'] = LockManagerGroup::singleton()->get( $useConfig['lockManager'] );
43 $class = $useConfig['class'];
44 self::$backendToUse = new $class( $useConfig );
45 $this->singleBackend = self::$backendToUse;
47 } else {
48 $this->singleBackend = new FSFileBackend( array(
49 'name' => 'localtesting',
50 'lockManager' => LockManagerGroup::singleton()->get( 'fsLockManager' ),
51 'wikiId' => wfWikiID(),
52 'containerPaths' => array(
53 'unittest-cont1' => "{$tmpDir}/localtesting-cont1",
54 'unittest-cont2' => "{$tmpDir}/localtesting-cont2" )
55 ) );
57 $this->multiBackend = new FileBackendMultiWrite( array(
58 'name' => 'localtesting',
59 'lockManager' => LockManagerGroup::singleton()->get( 'fsLockManager' ),
60 'parallelize' => 'implicit',
61 'wikiId' => wfWikiId() . $uniqueId,
62 'backends' => array(
63 array(
64 'name' => 'localmultitesting1',
65 'class' => 'FSFileBackend',
66 'containerPaths' => array(
67 'unittest-cont1' => "{$tmpDir}/localtestingmulti1-cont1",
68 'unittest-cont2' => "{$tmpDir}/localtestingmulti1-cont2" ),
69 'isMultiMaster' => false
71 array(
72 'name' => 'localmultitesting2',
73 'class' => 'FSFileBackend',
74 'containerPaths' => array(
75 'unittest-cont1' => "{$tmpDir}/localtestingmulti2-cont1",
76 'unittest-cont2' => "{$tmpDir}/localtestingmulti2-cont2" ),
77 'isMultiMaster' => true
80 ) );
83 private static function baseStorePath() {
84 return 'mwstore://localtesting';
87 private function backendClass() {
88 return get_class( $this->backend );
91 /**
92 * @dataProvider provider_testIsStoragePath
93 * @covers FileBackend::isStoragePath
95 public function testIsStoragePath( $path, $isStorePath ) {
96 $this->assertEquals( $isStorePath, FileBackend::isStoragePath( $path ),
97 "FileBackend::isStoragePath on path '$path'" );
100 public static function provider_testIsStoragePath() {
101 return array(
102 array( 'mwstore://', true ),
103 array( 'mwstore://backend', true ),
104 array( 'mwstore://backend/container', true ),
105 array( 'mwstore://backend/container/', true ),
106 array( 'mwstore://backend/container/path', true ),
107 array( 'mwstore://backend//container/', true ),
108 array( 'mwstore://backend//container//', true ),
109 array( 'mwstore://backend//container//path', true ),
110 array( 'mwstore:///', true ),
111 array( 'mwstore:/', false ),
112 array( 'mwstore:', false ),
117 * @dataProvider provider_testSplitStoragePath
118 * @covers FileBackend::splitStoragePath
120 public function testSplitStoragePath( $path, $res ) {
121 $this->assertEquals( $res, FileBackend::splitStoragePath( $path ),
122 "FileBackend::splitStoragePath on path '$path'" );
125 public static function provider_testSplitStoragePath() {
126 return array(
127 array( 'mwstore://backend/container', array( 'backend', 'container', '' ) ),
128 array( 'mwstore://backend/container/', array( 'backend', 'container', '' ) ),
129 array( 'mwstore://backend/container/path', array( 'backend', 'container', 'path' ) ),
130 array( 'mwstore://backend/container//path', array( 'backend', 'container', '/path' ) ),
131 array( 'mwstore://backend//container/path', array( null, null, null ) ),
132 array( 'mwstore://backend//container//path', array( null, null, null ) ),
133 array( 'mwstore://', array( null, null, null ) ),
134 array( 'mwstore://backend', array( null, null, null ) ),
135 array( 'mwstore:///', array( null, null, null ) ),
136 array( 'mwstore:/', array( null, null, null ) ),
137 array( 'mwstore:', array( null, null, null ) )
142 * @dataProvider provider_normalizeStoragePath
143 * @covers FileBackend::normalizeStoragePath
145 public function testNormalizeStoragePath( $path, $res ) {
146 $this->assertEquals( $res, FileBackend::normalizeStoragePath( $path ),
147 "FileBackend::normalizeStoragePath on path '$path'" );
150 public static function provider_normalizeStoragePath() {
151 return array(
152 array( 'mwstore://backend/container', 'mwstore://backend/container' ),
153 array( 'mwstore://backend/container/', 'mwstore://backend/container' ),
154 array( 'mwstore://backend/container/path', 'mwstore://backend/container/path' ),
155 array( 'mwstore://backend/container//path', 'mwstore://backend/container/path' ),
156 array( 'mwstore://backend/container///path', 'mwstore://backend/container/path' ),
157 array(
158 'mwstore://backend/container///path//to///obj',
159 'mwstore://backend/container/path/to/obj'
161 array( 'mwstore://', null ),
162 array( 'mwstore://backend', null ),
163 array( 'mwstore://backend//container/path', null ),
164 array( 'mwstore://backend//container//path', null ),
165 array( 'mwstore:///', null ),
166 array( 'mwstore:/', null ),
167 array( 'mwstore:', null ),
172 * @dataProvider provider_testParentStoragePath
173 * @covers FileBackend::parentStoragePath
175 public function testParentStoragePath( $path, $res ) {
176 $this->assertEquals( $res, FileBackend::parentStoragePath( $path ),
177 "FileBackend::parentStoragePath on path '$path'" );
180 public static function provider_testParentStoragePath() {
181 return array(
182 array( 'mwstore://backend/container/path/to/obj', 'mwstore://backend/container/path/to' ),
183 array( 'mwstore://backend/container/path/to', 'mwstore://backend/container/path' ),
184 array( 'mwstore://backend/container/path', 'mwstore://backend/container' ),
185 array( 'mwstore://backend/container', null ),
186 array( 'mwstore://backend/container/path/to/obj/', 'mwstore://backend/container/path/to' ),
187 array( 'mwstore://backend/container/path/to/', 'mwstore://backend/container/path' ),
188 array( 'mwstore://backend/container/path/', 'mwstore://backend/container' ),
189 array( 'mwstore://backend/container/', null ),
194 * @dataProvider provider_testExtensionFromPath
195 * @covers FileBackend::extensionFromPath
197 public function testExtensionFromPath( $path, $res ) {
198 $this->assertEquals( $res, FileBackend::extensionFromPath( $path ),
199 "FileBackend::extensionFromPath on path '$path'" );
202 public static function provider_testExtensionFromPath() {
203 return array(
204 array( 'mwstore://backend/container/path.txt', 'txt' ),
205 array( 'mwstore://backend/container/path.svg.png', 'png' ),
206 array( 'mwstore://backend/container/path', '' ),
207 array( 'mwstore://backend/container/path.', '' ),
212 * @dataProvider provider_testStore
214 public function testStore( $op ) {
215 $this->addTmpFiles( $op['src'] );
217 $this->backend = $this->singleBackend;
218 $this->tearDownFiles();
219 $this->doTestStore( $op );
220 $this->tearDownFiles();
222 $this->backend = $this->multiBackend;
223 $this->tearDownFiles();
224 $this->doTestStore( $op );
225 $this->tearDownFiles();
229 * @covers FileBackend::doOperation
231 private function doTestStore( $op ) {
232 $backendName = $this->backendClass();
234 $source = $op['src'];
235 $dest = $op['dst'];
236 $this->prepare( array( 'dir' => dirname( $dest ) ) );
238 file_put_contents( $source, "Unit test file" );
240 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
241 $this->backend->store( $op );
244 $status = $this->backend->doOperation( $op );
246 $this->assertGoodStatus( $status,
247 "Store from $source to $dest succeeded without warnings ($backendName)." );
248 $this->assertEquals( true, $status->isOK(),
249 "Store from $source to $dest succeeded ($backendName)." );
250 $this->assertEquals( array( 0 => true ), $status->success,
251 "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
252 $this->assertEquals( true, file_exists( $source ),
253 "Source file $source still exists ($backendName)." );
254 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
255 "Destination file $dest exists ($backendName)." );
257 $this->assertEquals( filesize( $source ),
258 $this->backend->getFileSize( array( 'src' => $dest ) ),
259 "Destination file $dest has correct size ($backendName)." );
261 $props1 = FSFile::getPropsFromPath( $source );
262 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
263 $this->assertEquals( $props1, $props2,
264 "Source and destination have the same props ($backendName)." );
266 $this->assertBackendPathsConsistent( array( $dest ) );
269 public static function provider_testStore() {
270 $cases = array();
272 $tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath();
273 $toPath = self::baseStorePath() . '/unittest-cont1/e/fun/obj1.txt';
274 $op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath );
275 $cases[] = array( $op );
277 $op2 = $op;
278 $op2['overwrite'] = true;
279 $cases[] = array( $op2 );
281 $op3 = $op;
282 $op3['overwriteSame'] = true;
283 $cases[] = array( $op3 );
285 return $cases;
289 * @dataProvider provider_testCopy
290 * @covers FileBackend::doOperation
292 public function testCopy( $op ) {
293 $this->backend = $this->singleBackend;
294 $this->tearDownFiles();
295 $this->doTestCopy( $op );
296 $this->tearDownFiles();
298 $this->backend = $this->multiBackend;
299 $this->tearDownFiles();
300 $this->doTestCopy( $op );
301 $this->tearDownFiles();
304 private function doTestCopy( $op ) {
305 $backendName = $this->backendClass();
307 $source = $op['src'];
308 $dest = $op['dst'];
309 $this->prepare( array( 'dir' => dirname( $source ) ) );
310 $this->prepare( array( 'dir' => dirname( $dest ) ) );
312 if ( isset( $op['ignoreMissingSource'] ) ) {
313 $status = $this->backend->doOperation( $op );
314 $this->assertGoodStatus( $status,
315 "Move from $source to $dest succeeded without warnings ($backendName)." );
316 $this->assertEquals( array( 0 => true ), $status->success,
317 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
318 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
319 "Source file $source does not exist ($backendName)." );
320 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $dest ) ),
321 "Destination file $dest does not exist ($backendName)." );
323 return; // done
326 $status = $this->backend->doOperation(
327 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
328 $this->assertGoodStatus( $status,
329 "Creation of file at $source succeeded ($backendName)." );
331 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
332 $this->backend->copy( $op );
335 $status = $this->backend->doOperation( $op );
337 $this->assertGoodStatus( $status,
338 "Copy from $source to $dest succeeded without warnings ($backendName)." );
339 $this->assertEquals( true, $status->isOK(),
340 "Copy from $source to $dest succeeded ($backendName)." );
341 $this->assertEquals( array( 0 => true ), $status->success,
342 "Copy from $source to $dest has proper 'success' field in Status ($backendName)." );
343 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $source ) ),
344 "Source file $source still exists ($backendName)." );
345 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
346 "Destination file $dest exists after copy ($backendName)." );
348 $this->assertEquals(
349 $this->backend->getFileSize( array( 'src' => $source ) ),
350 $this->backend->getFileSize( array( 'src' => $dest ) ),
351 "Destination file $dest has correct size ($backendName)." );
353 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
354 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
355 $this->assertEquals( $props1, $props2,
356 "Source and destination have the same props ($backendName)." );
358 $this->assertBackendPathsConsistent( array( $source, $dest ) );
361 public static function provider_testCopy() {
362 $cases = array();
364 $source = self::baseStorePath() . '/unittest-cont1/e/file.txt';
365 $dest = self::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
367 $op = array( 'op' => 'copy', 'src' => $source, 'dst' => $dest );
368 $cases[] = array(
369 $op, // operation
370 $source, // source
371 $dest, // dest
374 $op2 = $op;
375 $op2['overwrite'] = true;
376 $cases[] = array(
377 $op2, // operation
378 $source, // source
379 $dest, // dest
382 $op2 = $op;
383 $op2['overwriteSame'] = true;
384 $cases[] = array(
385 $op2, // operation
386 $source, // source
387 $dest, // dest
390 $op2 = $op;
391 $op2['ignoreMissingSource'] = true;
392 $cases[] = array(
393 $op2, // operation
394 $source, // source
395 $dest, // dest
398 $op2 = $op;
399 $op2['ignoreMissingSource'] = true;
400 $cases[] = array(
401 $op2, // operation
402 self::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
403 $dest, // dest
406 return $cases;
410 * @dataProvider provider_testMove
411 * @covers FileBackend::doOperation
413 public function testMove( $op ) {
414 $this->backend = $this->singleBackend;
415 $this->tearDownFiles();
416 $this->doTestMove( $op );
417 $this->tearDownFiles();
419 $this->backend = $this->multiBackend;
420 $this->tearDownFiles();
421 $this->doTestMove( $op );
422 $this->tearDownFiles();
425 private function doTestMove( $op ) {
426 $backendName = $this->backendClass();
428 $source = $op['src'];
429 $dest = $op['dst'];
430 $this->prepare( array( 'dir' => dirname( $source ) ) );
431 $this->prepare( array( 'dir' => dirname( $dest ) ) );
433 if ( isset( $op['ignoreMissingSource'] ) ) {
434 $status = $this->backend->doOperation( $op );
435 $this->assertGoodStatus( $status,
436 "Move from $source to $dest succeeded without warnings ($backendName)." );
437 $this->assertEquals( array( 0 => true ), $status->success,
438 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
439 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
440 "Source file $source does not exist ($backendName)." );
441 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $dest ) ),
442 "Destination file $dest does not exist ($backendName)." );
444 return; // done
447 $status = $this->backend->doOperation(
448 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
449 $this->assertGoodStatus( $status,
450 "Creation of file at $source succeeded ($backendName)." );
452 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
453 $this->backend->copy( $op );
456 $status = $this->backend->doOperation( $op );
457 $this->assertGoodStatus( $status,
458 "Move from $source to $dest succeeded without warnings ($backendName)." );
459 $this->assertEquals( true, $status->isOK(),
460 "Move from $source to $dest succeeded ($backendName)." );
461 $this->assertEquals( array( 0 => true ), $status->success,
462 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
463 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
464 "Source file $source does not still exists ($backendName)." );
465 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
466 "Destination file $dest exists after move ($backendName)." );
468 $this->assertNotEquals(
469 $this->backend->getFileSize( array( 'src' => $source ) ),
470 $this->backend->getFileSize( array( 'src' => $dest ) ),
471 "Destination file $dest has correct size ($backendName)." );
473 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
474 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
475 $this->assertEquals( false, $props1['fileExists'],
476 "Source file does not exist accourding to props ($backendName)." );
477 $this->assertEquals( true, $props2['fileExists'],
478 "Destination file exists accourding to props ($backendName)." );
480 $this->assertBackendPathsConsistent( array( $source, $dest ) );
483 public static function provider_testMove() {
484 $cases = array();
486 $source = self::baseStorePath() . '/unittest-cont1/e/file.txt';
487 $dest = self::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
489 $op = array( 'op' => 'move', 'src' => $source, 'dst' => $dest );
490 $cases[] = array(
491 $op, // operation
492 $source, // source
493 $dest, // dest
496 $op2 = $op;
497 $op2['overwrite'] = true;
498 $cases[] = array(
499 $op2, // operation
500 $source, // source
501 $dest, // dest
504 $op2 = $op;
505 $op2['overwriteSame'] = true;
506 $cases[] = array(
507 $op2, // operation
508 $source, // source
509 $dest, // dest
512 $op2 = $op;
513 $op2['ignoreMissingSource'] = true;
514 $cases[] = array(
515 $op2, // operation
516 $source, // source
517 $dest, // dest
520 $op2 = $op;
521 $op2['ignoreMissingSource'] = true;
522 $cases[] = array(
523 $op2, // operation
524 self::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
525 $dest, // dest
528 return $cases;
532 * @dataProvider provider_testDelete
533 * @covers FileBackend::doOperation
535 public function testDelete( $op, $withSource, $okStatus ) {
536 $this->backend = $this->singleBackend;
537 $this->tearDownFiles();
538 $this->doTestDelete( $op, $withSource, $okStatus );
539 $this->tearDownFiles();
541 $this->backend = $this->multiBackend;
542 $this->tearDownFiles();
543 $this->doTestDelete( $op, $withSource, $okStatus );
544 $this->tearDownFiles();
547 private function doTestDelete( $op, $withSource, $okStatus ) {
548 $backendName = $this->backendClass();
550 $source = $op['src'];
551 $this->prepare( array( 'dir' => dirname( $source ) ) );
553 if ( $withSource ) {
554 $status = $this->backend->doOperation(
555 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
556 $this->assertGoodStatus( $status,
557 "Creation of file at $source succeeded ($backendName)." );
560 $status = $this->backend->doOperation( $op );
561 if ( $okStatus ) {
562 $this->assertGoodStatus( $status,
563 "Deletion of file at $source succeeded without warnings ($backendName)." );
564 $this->assertEquals( true, $status->isOK(),
565 "Deletion of file at $source succeeded ($backendName)." );
566 $this->assertEquals( array( 0 => true ), $status->success,
567 "Deletion of file at $source has proper 'success' field in Status ($backendName)." );
568 } else {
569 $this->assertEquals( false, $status->isOK(),
570 "Deletion of file at $source failed ($backendName)." );
573 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
574 "Source file $source does not exist after move ($backendName)." );
576 $this->assertFalse(
577 $this->backend->getFileSize( array( 'src' => $source ) ),
578 "Source file $source has correct size (false) ($backendName)." );
580 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
581 $this->assertFalse( $props1['fileExists'],
582 "Source file $source does not exist according to props ($backendName)." );
584 $this->assertBackendPathsConsistent( array( $source ) );
587 public static function provider_testDelete() {
588 $cases = array();
590 $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
592 $op = array( 'op' => 'delete', 'src' => $source );
593 $cases[] = array(
594 $op, // operation
595 true, // with source
596 true // succeeds
599 $cases[] = array(
600 $op, // operation
601 false, // without source
602 false // fails
605 $op['ignoreMissingSource'] = true;
606 $cases[] = array(
607 $op, // operation
608 false, // without source
609 true // succeeds
612 $op['ignoreMissingSource'] = true;
613 $op['src'] = self::baseStorePath() . '/unittest-cont-bad/e/file.txt';
614 $cases[] = array(
615 $op, // operation
616 false, // without source
617 true // succeeds
620 return $cases;
624 * @dataProvider provider_testDescribe
625 * @covers FileBackend::doOperation
627 public function testDescribe( $op, $withSource, $okStatus ) {
628 $this->backend = $this->singleBackend;
629 $this->tearDownFiles();
630 $this->doTestDescribe( $op, $withSource, $okStatus );
631 $this->tearDownFiles();
633 $this->backend = $this->multiBackend;
634 $this->tearDownFiles();
635 $this->doTestDescribe( $op, $withSource, $okStatus );
636 $this->tearDownFiles();
639 private function doTestDescribe( $op, $withSource, $okStatus ) {
640 $backendName = $this->backendClass();
642 $source = $op['src'];
643 $this->prepare( array( 'dir' => dirname( $source ) ) );
645 if ( $withSource ) {
646 $status = $this->backend->doOperation(
647 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source,
648 'headers' => array( 'Content-Disposition' => 'xxx' ) ) );
649 $this->assertGoodStatus( $status,
650 "Creation of file at $source succeeded ($backendName)." );
651 if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
652 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
653 $this->assertHasHeaders( array( 'Content-Disposition' => 'xxx' ), $attr );
656 $status = $this->backend->describe( array( 'src' => $source,
657 'headers' => array( 'Content-Disposition' => '' ) ) ); // remove
658 $this->assertGoodStatus( $status,
659 "Removal of header for $source succeeded ($backendName)." );
661 if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
662 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
663 $this->assertFalse( isset( $attr['headers']['content-disposition'] ),
664 "File 'Content-Disposition' header removed." );
668 $status = $this->backend->doOperation( $op );
669 if ( $okStatus ) {
670 $this->assertGoodStatus( $status,
671 "Describe of file at $source succeeded without warnings ($backendName)." );
672 $this->assertEquals( true, $status->isOK(),
673 "Describe of file at $source succeeded ($backendName)." );
674 $this->assertEquals( array( 0 => true ), $status->success,
675 "Describe of file at $source has proper 'success' field in Status ($backendName)." );
676 if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
677 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
678 $this->assertHasHeaders( $op['headers'], $attr );
680 } else {
681 $this->assertEquals( false, $status->isOK(),
682 "Describe of file at $source failed ($backendName)." );
685 $this->assertBackendPathsConsistent( array( $source ) );
688 private function assertHasHeaders( array $headers, array $attr ) {
689 foreach ( $headers as $n => $v ) {
690 if ( $n !== '' ) {
691 $this->assertTrue( isset( $attr['headers'][strtolower( $n )] ),
692 "File has '$n' header." );
693 $this->assertEquals( $v, $attr['headers'][strtolower( $n )],
694 "File has '$n' header value." );
695 } else {
696 $this->assertFalse( isset( $attr['headers'][strtolower( $n )] ),
697 "File does not have '$n' header." );
702 public static function provider_testDescribe() {
703 $cases = array();
705 $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
707 $op = array( 'op' => 'describe', 'src' => $source,
708 'headers' => array( 'Content-Disposition' => 'inline' ), );
709 $cases[] = array(
710 $op, // operation
711 true, // with source
712 true // succeeds
715 $cases[] = array(
716 $op, // operation
717 false, // without source
718 false // fails
721 return $cases;
725 * @dataProvider provider_testCreate
726 * @covers FileBackend::doOperation
728 public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
729 $this->backend = $this->singleBackend;
730 $this->tearDownFiles();
731 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
732 $this->tearDownFiles();
734 $this->backend = $this->multiBackend;
735 $this->tearDownFiles();
736 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
737 $this->tearDownFiles();
740 private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
741 $backendName = $this->backendClass();
743 $dest = $op['dst'];
744 $this->prepare( array( 'dir' => dirname( $dest ) ) );
746 $oldText = 'blah...blah...waahwaah';
747 if ( $alreadyExists ) {
748 $status = $this->backend->doOperation(
749 array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
750 $this->assertGoodStatus( $status,
751 "Creation of file at $dest succeeded ($backendName)." );
754 $status = $this->backend->doOperation( $op );
755 if ( $okStatus ) {
756 $this->assertGoodStatus( $status,
757 "Creation of file at $dest succeeded without warnings ($backendName)." );
758 $this->assertEquals( true, $status->isOK(),
759 "Creation of file at $dest succeeded ($backendName)." );
760 $this->assertEquals( array( 0 => true ), $status->success,
761 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
762 } else {
763 $this->assertEquals( false, $status->isOK(),
764 "Creation of file at $dest failed ($backendName)." );
767 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
768 "Destination file $dest exists after creation ($backendName)." );
770 $props1 = $this->backend->getFileProps( array( 'src' => $dest ) );
771 $this->assertEquals( true, $props1['fileExists'],
772 "Destination file $dest exists according to props ($backendName)." );
773 if ( $okStatus ) { // file content is what we saved
774 $this->assertEquals( $newSize, $props1['size'],
775 "Destination file $dest has expected size according to props ($backendName)." );
776 $this->assertEquals( $newSize,
777 $this->backend->getFileSize( array( 'src' => $dest ) ),
778 "Destination file $dest has correct size ($backendName)." );
779 } else { // file content is some other previous text
780 $this->assertEquals( strlen( $oldText ), $props1['size'],
781 "Destination file $dest has original size according to props ($backendName)." );
782 $this->assertEquals( strlen( $oldText ),
783 $this->backend->getFileSize( array( 'src' => $dest ) ),
784 "Destination file $dest has original size according to props ($backendName)." );
787 $this->assertBackendPathsConsistent( array( $dest ) );
791 * @dataProvider provider_testCreate
793 public static function provider_testCreate() {
794 $cases = array();
796 $dest = self::baseStorePath() . '/unittest-cont2/a/myspacefile.txt';
798 $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
799 $cases[] = array(
800 $op, // operation
801 false, // no dest already exists
802 true, // succeeds
803 strlen( $op['content'] )
806 $op2 = $op;
807 $op2['content'] = "\n";
808 $cases[] = array(
809 $op2, // operation
810 false, // no dest already exists
811 true, // succeeds
812 strlen( $op2['content'] )
815 $op2 = $op;
816 $op2['content'] = "fsf\n waf 3kt";
817 $cases[] = array(
818 $op2, // operation
819 true, // dest already exists
820 false, // fails
821 strlen( $op2['content'] )
824 $op2 = $op;
825 $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
826 $op2['overwrite'] = true;
827 $cases[] = array(
828 $op2, // operation
829 true, // dest already exists
830 true, // succeeds
831 strlen( $op2['content'] )
834 $op2 = $op;
835 $op2['content'] = "39qjmg3-qg";
836 $op2['overwriteSame'] = true;
837 $cases[] = array(
838 $op2, // operation
839 true, // dest already exists
840 false, // succeeds
841 strlen( $op2['content'] )
844 return $cases;
848 * @covers FileBackend::doQuickOperations
850 public function testDoQuickOperations() {
851 $this->backend = $this->singleBackend;
852 $this->doTestDoQuickOperations();
853 $this->tearDownFiles();
855 $this->backend = $this->multiBackend;
856 $this->doTestDoQuickOperations();
857 $this->tearDownFiles();
860 private function doTestDoQuickOperations() {
861 $backendName = $this->backendClass();
863 $base = self::baseStorePath();
864 $files = array(
865 "$base/unittest-cont1/e/fileA.a",
866 "$base/unittest-cont1/e/fileB.a",
867 "$base/unittest-cont1/e/fileC.a"
869 $createOps = array();
870 $purgeOps = array();
871 foreach ( $files as $path ) {
872 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
873 $this->assertGoodStatus( $status,
874 "Preparing $path succeeded without warnings ($backendName)." );
875 $createOps[] = array( 'op' => 'create', 'dst' => $path, 'content' => mt_rand( 0, 50000 ) );
876 $copyOps[] = array( 'op' => 'copy', 'src' => $path, 'dst' => "$path-2" );
877 $moveOps[] = array( 'op' => 'move', 'src' => "$path-2", 'dst' => "$path-3" );
878 $purgeOps[] = array( 'op' => 'delete', 'src' => $path );
879 $purgeOps[] = array( 'op' => 'delete', 'src' => "$path-3" );
881 $purgeOps[] = array( 'op' => 'null' );
883 $this->assertGoodStatus(
884 $this->backend->doQuickOperations( $createOps ),
885 "Creation of source files succeeded ($backendName)." );
886 foreach ( $files as $file ) {
887 $this->assertTrue( $this->backend->fileExists( array( 'src' => $file ) ),
888 "File $file exists." );
891 $this->assertGoodStatus(
892 $this->backend->doQuickOperations( $copyOps ),
893 "Quick copy of source files succeeded ($backendName)." );
894 foreach ( $files as $file ) {
895 $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
896 "File $file-2 exists." );
899 $this->assertGoodStatus(
900 $this->backend->doQuickOperations( $moveOps ),
901 "Quick move of source files succeeded ($backendName)." );
902 foreach ( $files as $file ) {
903 $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
904 "File $file-3 move in." );
905 $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
906 "File $file-2 moved away." );
909 $this->assertGoodStatus(
910 $this->backend->quickCopy( array( 'src' => $files[0], 'dst' => $files[0] ) ),
911 "Copy of file {$files[0]} over itself succeeded ($backendName)." );
912 $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
913 "File {$files[0]} still exists." );
915 $this->assertGoodStatus(
916 $this->backend->quickMove( array( 'src' => $files[0], 'dst' => $files[0] ) ),
917 "Move of file {$files[0]} over itself succeeded ($backendName)." );
918 $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
919 "File {$files[0]} still exists." );
921 $this->assertGoodStatus(
922 $this->backend->doQuickOperations( $purgeOps ),
923 "Quick deletion of source files succeeded ($backendName)." );
924 foreach ( $files as $file ) {
925 $this->assertFalse( $this->backend->fileExists( array( 'src' => $file ) ),
926 "File $file purged." );
927 $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
928 "File $file-3 purged." );
933 * @dataProvider provider_testConcatenate
935 public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
936 $this->backend = $this->singleBackend;
937 $this->tearDownFiles();
938 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
939 $this->tearDownFiles();
941 $this->backend = $this->multiBackend;
942 $this->tearDownFiles();
943 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
944 $this->tearDownFiles();
947 private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
948 $backendName = $this->backendClass();
950 $expContent = '';
951 // Create sources
952 $ops = array();
953 foreach ( $srcs as $i => $source ) {
954 $this->prepare( array( 'dir' => dirname( $source ) ) );
955 $ops[] = array(
956 'op' => 'create', // operation
957 'dst' => $source, // source
958 'content' => $srcsContent[$i]
960 $expContent .= $srcsContent[$i];
962 $status = $this->backend->doOperations( $ops );
964 $this->assertGoodStatus( $status,
965 "Creation of source files succeeded ($backendName)." );
967 $dest = $params['dst'] = $this->getNewTempFile();
968 if ( $alreadyExists ) {
969 $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
970 $this->assertEquals( true, $ok,
971 "Creation of file at $dest succeeded ($backendName)." );
972 } else {
973 $ok = file_put_contents( $dest, '' ) !== false;
974 $this->assertEquals( true, $ok,
975 "Creation of 0-byte file at $dest succeeded ($backendName)." );
978 // Combine the files into one
979 $status = $this->backend->concatenate( $params );
980 if ( $okStatus ) {
981 $this->assertGoodStatus( $status,
982 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
983 $this->assertEquals( true, $status->isOK(),
984 "Creation of concat file at $dest succeeded ($backendName)." );
985 } else {
986 $this->assertEquals( false, $status->isOK(),
987 "Creation of concat file at $dest failed ($backendName)." );
990 if ( $okStatus ) {
991 $this->assertEquals( true, is_file( $dest ),
992 "Dest concat file $dest exists after creation ($backendName)." );
993 } else {
994 $this->assertEquals( true, is_file( $dest ),
995 "Dest concat file $dest exists after failed creation ($backendName)." );
998 $contents = file_get_contents( $dest );
999 $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
1001 if ( $okStatus ) {
1002 $this->assertEquals( $expContent, $contents,
1003 "Concat file at $dest has correct contents ($backendName)." );
1004 } else {
1005 $this->assertNotEquals( $expContent, $contents,
1006 "Concat file at $dest has correct contents ($backendName)." );
1010 public static function provider_testConcatenate() {
1011 $cases = array();
1013 $srcs = array(
1014 self::baseStorePath() . '/unittest-cont1/e/file1.txt',
1015 self::baseStorePath() . '/unittest-cont1/e/file2.txt',
1016 self::baseStorePath() . '/unittest-cont1/e/file3.txt',
1017 self::baseStorePath() . '/unittest-cont1/e/file4.txt',
1018 self::baseStorePath() . '/unittest-cont1/e/file5.txt',
1019 self::baseStorePath() . '/unittest-cont1/e/file6.txt',
1020 self::baseStorePath() . '/unittest-cont1/e/file7.txt',
1021 self::baseStorePath() . '/unittest-cont1/e/file8.txt',
1022 self::baseStorePath() . '/unittest-cont1/e/file9.txt',
1023 self::baseStorePath() . '/unittest-cont1/e/file10.txt'
1025 $content = array(
1026 'egfage',
1027 'ageageag',
1028 'rhokohlr',
1029 'shgmslkg',
1030 'kenga',
1031 'owagmal',
1032 'kgmae',
1033 'g eak;g',
1034 'lkaem;a',
1035 'legma'
1037 $params = array( 'srcs' => $srcs );
1039 $cases[] = array(
1040 $params, // operation
1041 $srcs, // sources
1042 $content, // content for each source
1043 false, // no dest already exists
1044 true, // succeeds
1047 $cases[] = array(
1048 $params, // operation
1049 $srcs, // sources
1050 $content, // content for each source
1051 true, // dest already exists
1052 false, // succeeds
1055 return $cases;
1059 * @dataProvider provider_testGetFileStat
1060 * @covers FileBackend::getFileStat
1062 public function testGetFileStat( $path, $content, $alreadyExists ) {
1063 $this->backend = $this->singleBackend;
1064 $this->tearDownFiles();
1065 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1066 $this->tearDownFiles();
1068 $this->backend = $this->multiBackend;
1069 $this->tearDownFiles();
1070 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1071 $this->tearDownFiles();
1074 private function doTestGetFileStat( $path, $content, $alreadyExists ) {
1075 $backendName = $this->backendClass();
1077 if ( $alreadyExists ) {
1078 $this->prepare( array( 'dir' => dirname( $path ) ) );
1079 $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
1080 $this->assertGoodStatus( $status,
1081 "Creation of file at $path succeeded ($backendName)." );
1083 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1084 $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
1085 $stat = $this->backend->getFileStat( array( 'src' => $path ) );
1087 $this->assertEquals( strlen( $content ), $size,
1088 "Correct file size of '$path'" );
1089 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
1090 "Correct file timestamp of '$path'" );
1092 $size = $stat['size'];
1093 $time = $stat['mtime'];
1094 $this->assertEquals( strlen( $content ), $size,
1095 "Correct file size of '$path'" );
1096 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
1097 "Correct file timestamp of '$path'" );
1099 $this->backend->clearCache( array( $path ) );
1101 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1103 $this->assertEquals( strlen( $content ), $size,
1104 "Correct file size of '$path'" );
1106 $this->backend->preloadCache( array( $path ) );
1108 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1110 $this->assertEquals( strlen( $content ), $size,
1111 "Correct file size of '$path'" );
1112 } else {
1113 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1114 $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
1115 $stat = $this->backend->getFileStat( array( 'src' => $path ) );
1117 $this->assertFalse( $size, "Correct file size of '$path'" );
1118 $this->assertFalse( $time, "Correct file timestamp of '$path'" );
1119 $this->assertFalse( $stat, "Correct file stat of '$path'" );
1123 public static function provider_testGetFileStat() {
1124 $cases = array();
1126 $base = self::baseStorePath();
1127 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents", true );
1128 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "", true );
1129 $cases[] = array( "$base/unittest-cont1/e/b/some-diff_file.txt", null, false );
1131 return $cases;
1135 * @dataProvider provider_testGetFileStat
1136 * @covers FileBackend::streamFile
1138 public function testStreamFile( $path, $content, $alreadyExists ) {
1139 $this->backend = $this->singleBackend;
1140 $this->tearDownFiles();
1141 $this->doTestStreamFile( $path, $content, $alreadyExists );
1142 $this->tearDownFiles();
1145 private function doTestStreamFile( $path, $content ) {
1146 $backendName = $this->backendClass();
1148 // Test doStreamFile() directly to avoid header madness
1149 $class = new ReflectionClass( $this->backend );
1150 $method = $class->getMethod( 'doStreamFile' );
1151 $method->setAccessible( true );
1153 if ( $content !== null ) {
1154 $this->prepare( array( 'dir' => dirname( $path ) ) );
1155 $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
1156 $this->assertGoodStatus( $status,
1157 "Creation of file at $path succeeded ($backendName)." );
1159 ob_start();
1160 $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
1161 $data = ob_get_contents();
1162 ob_end_clean();
1164 $this->assertEquals( $content, $data, "Correct content streamed from '$path'" );
1165 } else { // 404 case
1166 ob_start();
1167 $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
1168 $data = ob_get_contents();
1169 ob_end_clean();
1171 $this->assertEquals( '', $data, "Correct content streamed from '$path' ($backendName)" );
1175 public static function provider_testStreamFile() {
1176 $cases = array();
1178 $base = self::baseStorePath();
1179 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
1180 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", null );
1182 return $cases;
1186 * @dataProvider provider_testGetFileContents
1187 * @covers FileBackend::getFileContents
1188 * @covers FileBackend::getFileContentsMulti
1190 public function testGetFileContents( $source, $content ) {
1191 $this->backend = $this->singleBackend;
1192 $this->tearDownFiles();
1193 $this->doTestGetFileContents( $source, $content );
1194 $this->tearDownFiles();
1196 $this->backend = $this->multiBackend;
1197 $this->tearDownFiles();
1198 $this->doTestGetFileContents( $source, $content );
1199 $this->tearDownFiles();
1202 private function doTestGetFileContents( $source, $content ) {
1203 $backendName = $this->backendClass();
1205 $srcs = (array)$source;
1206 $content = (array)$content;
1207 foreach ( $srcs as $i => $src ) {
1208 $this->prepare( array( 'dir' => dirname( $src ) ) );
1209 $status = $this->backend->doOperation(
1210 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1211 $this->assertGoodStatus( $status,
1212 "Creation of file at $src succeeded ($backendName)." );
1215 if ( is_array( $source ) ) {
1216 $contents = $this->backend->getFileContentsMulti( array( 'srcs' => $source ) );
1217 foreach ( $contents as $path => $data ) {
1218 $this->assertNotEquals( false, $data, "Contents of $path exists ($backendName)." );
1219 $this->assertEquals(
1220 current( $content ),
1221 $data,
1222 "Contents of $path is correct ($backendName)."
1224 next( $content );
1226 $this->assertEquals(
1227 $source,
1228 array_keys( $contents ),
1229 "Contents in right order ($backendName)."
1231 $this->assertEquals(
1232 count( $source ),
1233 count( $contents ),
1234 "Contents array size correct ($backendName)."
1236 } else {
1237 $data = $this->backend->getFileContents( array( 'src' => $source ) );
1238 $this->assertNotEquals( false, $data, "Contents of $source exists ($backendName)." );
1239 $this->assertEquals( $content[0], $data, "Contents of $source is correct ($backendName)." );
1243 public static function provider_testGetFileContents() {
1244 $cases = array();
1246 $base = self::baseStorePath();
1247 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
1248 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "more file contents" );
1249 $cases[] = array(
1250 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1251 "$base/unittest-cont1/e/a/z.txt" ),
1252 array( "contents xx", "contents xy", "contents xz" )
1255 return $cases;
1259 * @dataProvider provider_testGetLocalCopy
1260 * @covers FileBackend::getLocalCopy
1262 public function testGetLocalCopy( $source, $content ) {
1263 $this->backend = $this->singleBackend;
1264 $this->tearDownFiles();
1265 $this->doTestGetLocalCopy( $source, $content );
1266 $this->tearDownFiles();
1268 $this->backend = $this->multiBackend;
1269 $this->tearDownFiles();
1270 $this->doTestGetLocalCopy( $source, $content );
1271 $this->tearDownFiles();
1274 private function doTestGetLocalCopy( $source, $content ) {
1275 $backendName = $this->backendClass();
1277 $srcs = (array)$source;
1278 $content = (array)$content;
1279 foreach ( $srcs as $i => $src ) {
1280 $this->prepare( array( 'dir' => dirname( $src ) ) );
1281 $status = $this->backend->doOperation(
1282 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1283 $this->assertGoodStatus( $status,
1284 "Creation of file at $src succeeded ($backendName)." );
1287 if ( is_array( $source ) ) {
1288 $tmpFiles = $this->backend->getLocalCopyMulti( array( 'srcs' => $source ) );
1289 foreach ( $tmpFiles as $path => $tmpFile ) {
1290 $this->assertNotNull( $tmpFile,
1291 "Creation of local copy of $path succeeded ($backendName)." );
1292 $contents = file_get_contents( $tmpFile->getPath() );
1293 $this->assertNotEquals( false, $contents, "Local copy of $path exists ($backendName)." );
1294 $this->assertEquals(
1295 current( $content ),
1296 $contents,
1297 "Local copy of $path is correct ($backendName)."
1299 next( $content );
1301 $this->assertEquals(
1302 $source,
1303 array_keys( $tmpFiles ),
1304 "Local copies in right order ($backendName)."
1306 $this->assertEquals(
1307 count( $source ),
1308 count( $tmpFiles ),
1309 "Local copies array size correct ($backendName)."
1311 } else {
1312 $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
1313 $this->assertNotNull( $tmpFile,
1314 "Creation of local copy of $source succeeded ($backendName)." );
1315 $contents = file_get_contents( $tmpFile->getPath() );
1316 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
1317 $this->assertEquals(
1318 $content[0],
1319 $contents,
1320 "Local copy of $source is correct ($backendName)."
1324 $obj = new stdClass();
1325 $tmpFile->bind( $obj );
1328 public static function provider_testGetLocalCopy() {
1329 $cases = array();
1331 $base = self::baseStorePath();
1332 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1333 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1334 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1335 $cases[] = array(
1336 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1337 "$base/unittest-cont1/e/a/z.txt" ),
1338 array( "contents xx $", "contents xy 111", "contents xz" )
1341 return $cases;
1345 * @dataProvider provider_testGetLocalReference
1346 * @covers FileBackend::getLocalReference
1348 public function testGetLocalReference( $source, $content ) {
1349 $this->backend = $this->singleBackend;
1350 $this->tearDownFiles();
1351 $this->doTestGetLocalReference( $source, $content );
1352 $this->tearDownFiles();
1354 $this->backend = $this->multiBackend;
1355 $this->tearDownFiles();
1356 $this->doTestGetLocalReference( $source, $content );
1357 $this->tearDownFiles();
1360 private function doTestGetLocalReference( $source, $content ) {
1361 $backendName = $this->backendClass();
1363 $srcs = (array)$source;
1364 $content = (array)$content;
1365 foreach ( $srcs as $i => $src ) {
1366 $this->prepare( array( 'dir' => dirname( $src ) ) );
1367 $status = $this->backend->doOperation(
1368 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1369 $this->assertGoodStatus( $status,
1370 "Creation of file at $src succeeded ($backendName)." );
1373 if ( is_array( $source ) ) {
1374 $tmpFiles = $this->backend->getLocalReferenceMulti( array( 'srcs' => $source ) );
1375 foreach ( $tmpFiles as $path => $tmpFile ) {
1376 $this->assertNotNull( $tmpFile,
1377 "Creation of local copy of $path succeeded ($backendName)." );
1378 $contents = file_get_contents( $tmpFile->getPath() );
1379 $this->assertNotEquals( false, $contents, "Local ref of $path exists ($backendName)." );
1380 $this->assertEquals(
1381 current( $content ),
1382 $contents,
1383 "Local ref of $path is correct ($backendName)."
1385 next( $content );
1387 $this->assertEquals(
1388 $source,
1389 array_keys( $tmpFiles ),
1390 "Local refs in right order ($backendName)."
1392 $this->assertEquals(
1393 count( $source ),
1394 count( $tmpFiles ),
1395 "Local refs array size correct ($backendName)."
1397 } else {
1398 $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
1399 $this->assertNotNull( $tmpFile,
1400 "Creation of local copy of $source succeeded ($backendName)." );
1401 $contents = file_get_contents( $tmpFile->getPath() );
1402 $this->assertNotEquals( false, $contents, "Local ref of $source exists ($backendName)." );
1403 $this->assertEquals( $content[0], $contents, "Local ref of $source is correct ($backendName)." );
1407 public static function provider_testGetLocalReference() {
1408 $cases = array();
1410 $base = self::baseStorePath();
1411 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1412 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1413 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1414 $cases[] = array(
1415 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1416 "$base/unittest-cont1/e/a/z.txt" ),
1417 array( "contents xx 1111", "contents xy %", "contents xz $" )
1420 return $cases;
1424 * @covers FileBackend::getLocalCopy
1425 * @covers FileBackend::getLocalReference
1427 public function testGetLocalCopyAndReference404() {
1428 $this->backend = $this->singleBackend;
1429 $this->tearDownFiles();
1430 $this->doTestGetLocalCopyAndReference404();
1431 $this->tearDownFiles();
1433 $this->backend = $this->multiBackend;
1434 $this->tearDownFiles();
1435 $this->doTestGetLocalCopyAndReference404();
1436 $this->tearDownFiles();
1439 public function doTestGetLocalCopyAndReference404() {
1440 $backendName = $this->backendClass();
1442 $base = self::baseStorePath();
1444 $tmpFile = $this->backend->getLocalCopy( array(
1445 'src' => "$base/unittest-cont1/not-there" ) );
1446 $this->assertEquals( null, $tmpFile, "Local copy of not existing file is null ($backendName)." );
1448 $tmpFile = $this->backend->getLocalReference( array(
1449 'src' => "$base/unittest-cont1/not-there" ) );
1450 $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
1454 * @dataProvider provider_testGetFileHttpUrl
1455 * @covers FileBackend::getFileHttpUrl
1457 public function testGetFileHttpUrl( $source, $content ) {
1458 $this->backend = $this->singleBackend;
1459 $this->tearDownFiles();
1460 $this->doTestGetFileHttpUrl( $source, $content );
1461 $this->tearDownFiles();
1463 $this->backend = $this->multiBackend;
1464 $this->tearDownFiles();
1465 $this->doTestGetFileHttpUrl( $source, $content );
1466 $this->tearDownFiles();
1469 private function doTestGetFileHttpUrl( $source, $content ) {
1470 $backendName = $this->backendClass();
1472 $this->prepare( array( 'dir' => dirname( $source ) ) );
1473 $status = $this->backend->doOperation(
1474 array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
1475 $this->assertGoodStatus( $status,
1476 "Creation of file at $source succeeded ($backendName)." );
1478 $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) );
1480 if ( $url !== null ) { // supported
1481 $data = Http::request( "GET", $url );
1482 $this->assertEquals( $content, $data,
1483 "HTTP GET of URL has right contents ($backendName)." );
1487 public static function provider_testGetFileHttpUrl() {
1488 $cases = array();
1490 $base = self::baseStorePath();
1491 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1492 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1493 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1495 return $cases;
1499 * @dataProvider provider_testPrepareAndClean
1500 * @covers FileBackend::prepare
1501 * @covers FileBackend::clean
1503 public function testPrepareAndClean( $path, $isOK ) {
1504 $this->backend = $this->singleBackend;
1505 $this->doTestPrepareAndClean( $path, $isOK );
1506 $this->tearDownFiles();
1508 $this->backend = $this->multiBackend;
1509 $this->doTestPrepareAndClean( $path, $isOK );
1510 $this->tearDownFiles();
1513 public static function provider_testPrepareAndClean() {
1514 $base = self::baseStorePath();
1516 return array(
1517 array( "$base/unittest-cont1/e/a/z/some_file1.txt", true ),
1518 array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
1519 # Specific to FS backend with no basePath field set
1520 #array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
1524 private function doTestPrepareAndClean( $path, $isOK ) {
1525 $backendName = $this->backendClass();
1527 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
1528 if ( $isOK ) {
1529 $this->assertGoodStatus( $status,
1530 "Preparing dir $path succeeded without warnings ($backendName)." );
1531 $this->assertEquals( true, $status->isOK(),
1532 "Preparing dir $path succeeded ($backendName)." );
1533 } else {
1534 $this->assertEquals( false, $status->isOK(),
1535 "Preparing dir $path failed ($backendName)." );
1538 $status = $this->backend->secure( array( 'dir' => dirname( $path ) ) );
1539 if ( $isOK ) {
1540 $this->assertGoodStatus( $status,
1541 "Securing dir $path succeeded without warnings ($backendName)." );
1542 $this->assertEquals( true, $status->isOK(),
1543 "Securing dir $path succeeded ($backendName)." );
1544 } else {
1545 $this->assertEquals( false, $status->isOK(),
1546 "Securing dir $path failed ($backendName)." );
1549 $status = $this->backend->publish( array( 'dir' => dirname( $path ) ) );
1550 if ( $isOK ) {
1551 $this->assertGoodStatus( $status,
1552 "Publishing dir $path succeeded without warnings ($backendName)." );
1553 $this->assertEquals( true, $status->isOK(),
1554 "Publishing dir $path succeeded ($backendName)." );
1555 } else {
1556 $this->assertEquals( false, $status->isOK(),
1557 "Publishing dir $path failed ($backendName)." );
1560 $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
1561 if ( $isOK ) {
1562 $this->assertGoodStatus( $status,
1563 "Cleaning dir $path succeeded without warnings ($backendName)." );
1564 $this->assertEquals( true, $status->isOK(),
1565 "Cleaning dir $path succeeded ($backendName)." );
1566 } else {
1567 $this->assertEquals( false, $status->isOK(),
1568 "Cleaning dir $path failed ($backendName)." );
1572 public function testRecursiveClean() {
1573 $this->backend = $this->singleBackend;
1574 $this->doTestRecursiveClean();
1575 $this->tearDownFiles();
1577 $this->backend = $this->multiBackend;
1578 $this->doTestRecursiveClean();
1579 $this->tearDownFiles();
1583 * @covers FileBackend::clean
1585 private function doTestRecursiveClean() {
1586 $backendName = $this->backendClass();
1588 $base = self::baseStorePath();
1589 $dirs = array(
1590 "$base/unittest-cont1",
1591 "$base/unittest-cont1/e",
1592 "$base/unittest-cont1/e/a",
1593 "$base/unittest-cont1/e/a/b",
1594 "$base/unittest-cont1/e/a/b/c",
1595 "$base/unittest-cont1/e/a/b/c/d0",
1596 "$base/unittest-cont1/e/a/b/c/d1",
1597 "$base/unittest-cont1/e/a/b/c/d2",
1598 "$base/unittest-cont1/e/a/b/c/d0/1",
1599 "$base/unittest-cont1/e/a/b/c/d0/2",
1600 "$base/unittest-cont1/e/a/b/c/d1/3",
1601 "$base/unittest-cont1/e/a/b/c/d1/4",
1602 "$base/unittest-cont1/e/a/b/c/d2/5",
1603 "$base/unittest-cont1/e/a/b/c/d2/6"
1605 foreach ( $dirs as $dir ) {
1606 $status = $this->prepare( array( 'dir' => $dir ) );
1607 $this->assertGoodStatus( $status,
1608 "Preparing dir $dir succeeded without warnings ($backendName)." );
1611 if ( $this->backend instanceof FSFileBackend ) {
1612 foreach ( $dirs as $dir ) {
1613 $this->assertEquals( true, $this->backend->directoryExists( array( 'dir' => $dir ) ),
1614 "Dir $dir exists ($backendName)." );
1618 $status = $this->backend->clean(
1619 array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
1620 $this->assertGoodStatus( $status,
1621 "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
1623 foreach ( $dirs as $dir ) {
1624 $this->assertEquals( false, $this->backend->directoryExists( array( 'dir' => $dir ) ),
1625 "Dir $dir no longer exists ($backendName)." );
1630 * @covers FileBackend::doOperations
1632 public function testDoOperations() {
1633 $this->backend = $this->singleBackend;
1634 $this->tearDownFiles();
1635 $this->doTestDoOperations();
1636 $this->tearDownFiles();
1638 $this->backend = $this->multiBackend;
1639 $this->tearDownFiles();
1640 $this->doTestDoOperations();
1641 $this->tearDownFiles();
1644 private function doTestDoOperations() {
1645 $base = self::baseStorePath();
1647 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1648 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1649 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1650 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1651 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1652 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1653 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1655 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1656 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1657 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1658 $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
1659 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1660 $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
1661 $this->prepare( array( 'dir' => dirname( $fileD ) ) );
1663 $status = $this->backend->doOperations( array(
1664 array( 'op' => 'describe', 'src' => $fileA,
1665 'headers' => array( 'X-Content-Length' => '91.3' ), 'disposition' => 'inline' ),
1666 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1667 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1668 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1669 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1670 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
1671 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1672 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
1673 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1674 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
1675 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1676 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
1677 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1678 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
1679 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1680 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
1681 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1682 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1683 // Does nothing
1684 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1685 // Does nothing
1686 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1687 // Does nothing
1688 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1689 // Does nothing
1690 array( 'op' => 'null' ),
1691 // Does nothing
1692 ) );
1694 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1695 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1696 $this->assertEquals( 14, count( $status->success ),
1697 "Operation batch has correct success array" );
1699 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
1700 "File does not exist at $fileA" );
1701 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1702 "File does not exist at $fileB" );
1703 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1704 "File does not exist at $fileD" );
1706 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1707 "File exists at $fileC" );
1708 $this->assertEquals( $fileBContents,
1709 $this->backend->getFileContents( array( 'src' => $fileC ) ),
1710 "Correct file contents of $fileC" );
1711 $this->assertEquals( strlen( $fileBContents ),
1712 $this->backend->getFileSize( array( 'src' => $fileC ) ),
1713 "Correct file size of $fileC" );
1714 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1715 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
1716 "Correct file SHA-1 of $fileC" );
1720 * @covers FileBackend::doOperations
1722 public function testDoOperationsPipeline() {
1723 $this->backend = $this->singleBackend;
1724 $this->tearDownFiles();
1725 $this->doTestDoOperationsPipeline();
1726 $this->tearDownFiles();
1728 $this->backend = $this->multiBackend;
1729 $this->tearDownFiles();
1730 $this->doTestDoOperationsPipeline();
1731 $this->tearDownFiles();
1734 // concurrency orientated
1735 private function doTestDoOperationsPipeline() {
1736 $base = self::baseStorePath();
1738 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1739 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1740 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1742 $tmpNameA = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1743 $tmpNameB = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1744 $tmpNameC = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1745 $this->addTmpFiles( array( $tmpNameA, $tmpNameB, $tmpNameC ) );
1746 file_put_contents( $tmpNameA, $fileAContents );
1747 file_put_contents( $tmpNameB, $fileBContents );
1748 file_put_contents( $tmpNameC, $fileCContents );
1750 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1751 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1752 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1753 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1755 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1756 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1757 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1758 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1759 $this->prepare( array( 'dir' => dirname( $fileD ) ) );
1761 $status = $this->backend->doOperations( array(
1762 array( 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ),
1763 array( 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ),
1764 array( 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ),
1765 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1766 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1767 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1768 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1769 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
1770 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1771 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
1772 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1773 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
1774 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1775 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
1776 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1777 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
1778 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1779 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
1780 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1781 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1782 // Does nothing
1783 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1784 // Does nothing
1785 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1786 // Does nothing
1787 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1788 // Does nothing
1789 array( 'op' => 'null' ),
1790 // Does nothing
1791 ) );
1793 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1794 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1795 $this->assertEquals( 16, count( $status->success ),
1796 "Operation batch has correct success array" );
1798 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
1799 "File does not exist at $fileA" );
1800 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1801 "File does not exist at $fileB" );
1802 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1803 "File does not exist at $fileD" );
1805 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1806 "File exists at $fileC" );
1807 $this->assertEquals( $fileBContents,
1808 $this->backend->getFileContents( array( 'src' => $fileC ) ),
1809 "Correct file contents of $fileC" );
1810 $this->assertEquals( strlen( $fileBContents ),
1811 $this->backend->getFileSize( array( 'src' => $fileC ) ),
1812 "Correct file size of $fileC" );
1813 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1814 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
1815 "Correct file SHA-1 of $fileC" );
1819 * @covers FileBackend::doOperations
1821 public function testDoOperationsFailing() {
1822 $this->backend = $this->singleBackend;
1823 $this->tearDownFiles();
1824 $this->doTestDoOperationsFailing();
1825 $this->tearDownFiles();
1827 $this->backend = $this->multiBackend;
1828 $this->tearDownFiles();
1829 $this->doTestDoOperationsFailing();
1830 $this->tearDownFiles();
1833 private function doTestDoOperationsFailing() {
1834 $base = self::baseStorePath();
1836 $fileA = "$base/unittest-cont2/a/b/fileA.txt";
1837 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1838 $fileB = "$base/unittest-cont2/a/b/fileB.txt";
1839 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1840 $fileC = "$base/unittest-cont2/a/b/fileC.txt";
1841 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1842 $fileD = "$base/unittest-cont2/a/b/fileD.txt";
1844 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1845 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1846 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1847 $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
1848 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1849 $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
1851 $status = $this->backend->doOperations( array(
1852 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1853 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1854 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1855 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1856 array( 'op' => 'copy', 'src' => $fileB, 'dst' => $fileD, 'overwrite' => 1 ),
1857 // Now: A:<A>, B:<B>, C:<A>, D:<B>
1858 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD ),
1859 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1860 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC, 'overwriteSame' => 1 ),
1861 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1862 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileA, 'overwrite' => 1 ),
1863 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1864 array( 'op' => 'delete', 'src' => $fileD ),
1865 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1866 array( 'op' => 'null' ),
1867 // Does nothing
1868 ), array( 'force' => 1 ) );
1870 $this->assertNotEquals( array(), $status->errors, "Operation had warnings" );
1871 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1872 $this->assertEquals( 8, count( $status->success ),
1873 "Operation batch has correct success array" );
1875 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1876 "File does not exist at $fileB" );
1877 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1878 "File does not exist at $fileD" );
1880 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileA ) ),
1881 "File does not exist at $fileA" );
1882 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1883 "File exists at $fileC" );
1884 $this->assertEquals( $fileBContents,
1885 $this->backend->getFileContents( array( 'src' => $fileA ) ),
1886 "Correct file contents of $fileA" );
1887 $this->assertEquals( strlen( $fileBContents ),
1888 $this->backend->getFileSize( array( 'src' => $fileA ) ),
1889 "Correct file size of $fileA" );
1890 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1891 $this->backend->getFileSha1Base36( array( 'src' => $fileA ) ),
1892 "Correct file SHA-1 of $fileA" );
1896 * @covers FileBackend::getFileList
1898 public function testGetFileList() {
1899 $this->backend = $this->singleBackend;
1900 $this->tearDownFiles();
1901 $this->doTestGetFileList();
1902 $this->tearDownFiles();
1904 $this->backend = $this->multiBackend;
1905 $this->tearDownFiles();
1906 $this->doTestGetFileList();
1907 $this->tearDownFiles();
1910 private function doTestGetFileList() {
1911 $backendName = $this->backendClass();
1912 $base = self::baseStorePath();
1914 // Should have no errors
1915 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
1917 $files = array(
1918 "$base/unittest-cont1/e/test1.txt",
1919 "$base/unittest-cont1/e/test2.txt",
1920 "$base/unittest-cont1/e/test3.txt",
1921 "$base/unittest-cont1/e/subdir1/test1.txt",
1922 "$base/unittest-cont1/e/subdir1/test2.txt",
1923 "$base/unittest-cont1/e/subdir2/test3.txt",
1924 "$base/unittest-cont1/e/subdir2/test4.txt",
1925 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
1926 "$base/unittest-cont1/e/subdir2/subdir/test2.txt",
1927 "$base/unittest-cont1/e/subdir2/subdir/test3.txt",
1928 "$base/unittest-cont1/e/subdir2/subdir/test4.txt",
1929 "$base/unittest-cont1/e/subdir2/subdir/test5.txt",
1930 "$base/unittest-cont1/e/subdir2/subdir/sub/test0.txt",
1931 "$base/unittest-cont1/e/subdir2/subdir/sub/120-px-file.txt",
1934 // Add the files
1935 $ops = array();
1936 foreach ( $files as $file ) {
1937 $this->prepare( array( 'dir' => dirname( $file ) ) );
1938 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
1940 $status = $this->backend->doQuickOperations( $ops );
1941 $this->assertGoodStatus( $status,
1942 "Creation of files succeeded ($backendName)." );
1943 $this->assertEquals( true, $status->isOK(),
1944 "Creation of files succeeded with OK status ($backendName)." );
1946 // Expected listing at root
1947 $expected = array(
1948 "e/test1.txt",
1949 "e/test2.txt",
1950 "e/test3.txt",
1951 "e/subdir1/test1.txt",
1952 "e/subdir1/test2.txt",
1953 "e/subdir2/test3.txt",
1954 "e/subdir2/test4.txt",
1955 "e/subdir2/subdir/test1.txt",
1956 "e/subdir2/subdir/test2.txt",
1957 "e/subdir2/subdir/test3.txt",
1958 "e/subdir2/subdir/test4.txt",
1959 "e/subdir2/subdir/test5.txt",
1960 "e/subdir2/subdir/sub/test0.txt",
1961 "e/subdir2/subdir/sub/120-px-file.txt",
1963 sort( $expected );
1965 // Actual listing (no trailing slash) at root
1966 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1" ) );
1967 $list = $this->listToArray( $iter );
1968 sort( $list );
1969 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1971 // Actual listing (no trailing slash) at root with advise
1972 $iter = $this->backend->getFileList( array(
1973 'dir' => "$base/unittest-cont1",
1974 'adviseStat' => 1
1975 ) );
1976 $list = $this->listToArray( $iter );
1977 sort( $list );
1978 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1980 // Actual listing (with trailing slash) at root
1981 $list = array();
1982 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/" ) );
1983 foreach ( $iter as $file ) {
1984 $list[] = $file;
1986 sort( $list );
1987 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1989 // Expected listing at subdir
1990 $expected = array(
1991 "test1.txt",
1992 "test2.txt",
1993 "test3.txt",
1994 "test4.txt",
1995 "test5.txt",
1996 "sub/test0.txt",
1997 "sub/120-px-file.txt",
1999 sort( $expected );
2001 // Actual listing (no trailing slash) at subdir
2002 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
2003 $list = $this->listToArray( $iter );
2004 sort( $list );
2005 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2007 // Actual listing (no trailing slash) at subdir with advise
2008 $iter = $this->backend->getFileList( array(
2009 'dir' => "$base/unittest-cont1/e/subdir2/subdir",
2010 'adviseStat' => 1
2011 ) );
2012 $list = $this->listToArray( $iter );
2013 sort( $list );
2014 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2016 // Actual listing (with trailing slash) at subdir
2017 $list = array();
2018 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir/" ) );
2019 foreach ( $iter as $file ) {
2020 $list[] = $file;
2022 sort( $list );
2023 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2025 // Actual listing (using iterator second time)
2026 $list = $this->listToArray( $iter );
2027 sort( $list );
2028 $this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
2030 // Actual listing (top files only) at root
2031 $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1" ) );
2032 $list = $this->listToArray( $iter );
2033 sort( $list );
2034 $this->assertEquals( array(), $list, "Correct top file listing ($backendName)." );
2036 // Expected listing (top files only) at subdir
2037 $expected = array(
2038 "test1.txt",
2039 "test2.txt",
2040 "test3.txt",
2041 "test4.txt",
2042 "test5.txt"
2044 sort( $expected );
2046 // Actual listing (top files only) at subdir
2047 $iter = $this->backend->getTopFileList(
2048 array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" )
2050 $list = $this->listToArray( $iter );
2051 sort( $list );
2052 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2054 // Actual listing (top files only) at subdir with advise
2055 $iter = $this->backend->getTopFileList( array(
2056 'dir' => "$base/unittest-cont1/e/subdir2/subdir",
2057 'adviseStat' => 1
2058 ) );
2059 $list = $this->listToArray( $iter );
2060 sort( $list );
2061 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2063 foreach ( $files as $file ) { // clean up
2064 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
2067 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
2068 foreach ( $iter as $iter ) {
2069 // no errors
2074 * @covers FileBackend::getTopDirectoryList
2075 * @covers FileBackend::getDirectoryList
2077 public function testGetDirectoryList() {
2078 $this->backend = $this->singleBackend;
2079 $this->tearDownFiles();
2080 $this->doTestGetDirectoryList();
2081 $this->tearDownFiles();
2083 $this->backend = $this->multiBackend;
2084 $this->tearDownFiles();
2085 $this->doTestGetDirectoryList();
2086 $this->tearDownFiles();
2089 private function doTestGetDirectoryList() {
2090 $backendName = $this->backendClass();
2092 $base = self::baseStorePath();
2093 $files = array(
2094 "$base/unittest-cont1/e/test1.txt",
2095 "$base/unittest-cont1/e/test2.txt",
2096 "$base/unittest-cont1/e/test3.txt",
2097 "$base/unittest-cont1/e/subdir1/test1.txt",
2098 "$base/unittest-cont1/e/subdir1/test2.txt",
2099 "$base/unittest-cont1/e/subdir2/test3.txt",
2100 "$base/unittest-cont1/e/subdir2/test4.txt",
2101 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
2102 "$base/unittest-cont1/e/subdir3/subdir/test2.txt",
2103 "$base/unittest-cont1/e/subdir4/subdir/test3.txt",
2104 "$base/unittest-cont1/e/subdir4/subdir/test4.txt",
2105 "$base/unittest-cont1/e/subdir4/subdir/test5.txt",
2106 "$base/unittest-cont1/e/subdir4/subdir/sub/test0.txt",
2107 "$base/unittest-cont1/e/subdir4/subdir/sub/120-px-file.txt",
2110 // Add the files
2111 $ops = array();
2112 foreach ( $files as $file ) {
2113 $this->prepare( array( 'dir' => dirname( $file ) ) );
2114 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
2116 $status = $this->backend->doQuickOperations( $ops );
2117 $this->assertGoodStatus( $status,
2118 "Creation of files succeeded ($backendName)." );
2119 $this->assertEquals( true, $status->isOK(),
2120 "Creation of files succeeded with OK status ($backendName)." );
2122 $this->assertEquals( true,
2123 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) ),
2124 "Directory exists in ($backendName)." );
2125 $this->assertEquals( true,
2126 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) ),
2127 "Directory exists in ($backendName)." );
2128 $this->assertEquals( false,
2129 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/test1.txt" ) ),
2130 "Directory does not exists in ($backendName)." );
2132 // Expected listing
2133 $expected = array(
2134 "e",
2136 sort( $expected );
2138 // Actual listing (no trailing slash)
2139 $list = array();
2140 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1" ) );
2141 foreach ( $iter as $file ) {
2142 $list[] = $file;
2144 sort( $list );
2146 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2148 // Expected listing
2149 $expected = array(
2150 "subdir1",
2151 "subdir2",
2152 "subdir3",
2153 "subdir4",
2155 sort( $expected );
2157 // Actual listing (no trailing slash)
2158 $list = array();
2159 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e" ) );
2160 foreach ( $iter as $file ) {
2161 $list[] = $file;
2163 sort( $list );
2165 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2167 // Actual listing (with trailing slash)
2168 $list = array();
2169 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/" ) );
2170 foreach ( $iter as $file ) {
2171 $list[] = $file;
2173 sort( $list );
2175 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2177 // Expected listing
2178 $expected = array(
2179 "subdir",
2181 sort( $expected );
2183 // Actual listing (no trailing slash)
2184 $list = array();
2185 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2" ) );
2186 foreach ( $iter as $file ) {
2187 $list[] = $file;
2189 sort( $list );
2191 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2193 // Actual listing (with trailing slash)
2194 $list = array();
2195 $iter = $this->backend->getTopDirectoryList(
2196 array( 'dir' => "$base/unittest-cont1/e/subdir2/" )
2199 foreach ( $iter as $file ) {
2200 $list[] = $file;
2202 sort( $list );
2204 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2206 // Actual listing (using iterator second time)
2207 $list = array();
2208 foreach ( $iter as $file ) {
2209 $list[] = $file;
2211 sort( $list );
2213 $this->assertEquals(
2214 $expected,
2215 $list,
2216 "Correct top dir listing ($backendName), second iteration."
2219 // Expected listing (recursive)
2220 $expected = array(
2221 "e",
2222 "e/subdir1",
2223 "e/subdir2",
2224 "e/subdir3",
2225 "e/subdir4",
2226 "e/subdir2/subdir",
2227 "e/subdir3/subdir",
2228 "e/subdir4/subdir",
2229 "e/subdir4/subdir/sub",
2231 sort( $expected );
2233 // Actual listing (recursive)
2234 $list = array();
2235 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/" ) );
2236 foreach ( $iter as $file ) {
2237 $list[] = $file;
2239 sort( $list );
2241 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2243 // Expected listing (recursive)
2244 $expected = array(
2245 "subdir",
2246 "subdir/sub",
2248 sort( $expected );
2250 // Actual listing (recursive)
2251 $list = array();
2252 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir4" ) );
2253 foreach ( $iter as $file ) {
2254 $list[] = $file;
2256 sort( $list );
2258 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2260 // Actual listing (recursive, second time)
2261 $list = array();
2262 foreach ( $iter as $file ) {
2263 $list[] = $file;
2265 sort( $list );
2267 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2269 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) );
2270 $items = $this->listToArray( $iter );
2271 $this->assertEquals( array(), $items, "Directory listing is empty." );
2273 foreach ( $files as $file ) { // clean up
2274 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
2277 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
2278 foreach ( $iter as $file ) {
2279 // no errors
2282 $items = $this->listToArray( $iter );
2283 $this->assertEquals( array(), $items, "Directory listing is empty." );
2285 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/not/exists" ) );
2286 $items = $this->listToArray( $iter );
2287 $this->assertEquals( array(), $items, "Directory listing is empty." );
2291 * @covers FileBackend::lockFiles
2292 * @covers FileBackend::unlockFiles
2294 public function testLockCalls() {
2295 $this->backend = $this->singleBackend;
2296 $this->doTestLockCalls();
2299 private function doTestLockCalls() {
2300 $backendName = $this->backendClass();
2302 $paths = array(
2303 "test1.txt",
2304 "test2.txt",
2305 "test3.txt",
2306 "subdir1",
2307 "subdir1", // duplicate
2308 "subdir1/test1.txt",
2309 "subdir1/test2.txt",
2310 "subdir2",
2311 "subdir2", // duplicate
2312 "subdir2/test3.txt",
2313 "subdir2/test4.txt",
2314 "subdir2/subdir",
2315 "subdir2/subdir/test1.txt",
2316 "subdir2/subdir/test2.txt",
2317 "subdir2/subdir/test3.txt",
2318 "subdir2/subdir/test4.txt",
2319 "subdir2/subdir/test5.txt",
2320 "subdir2/subdir/sub",
2321 "subdir2/subdir/sub/test0.txt",
2322 "subdir2/subdir/sub/120-px-file.txt",
2325 for ( $i = 0; $i < 25; $i++ ) {
2326 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
2327 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2328 "Locking of files succeeded ($backendName) ($i)." );
2329 $this->assertEquals( true, $status->isOK(),
2330 "Locking of files succeeded with OK status ($backendName) ($i)." );
2332 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
2333 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2334 "Locking of files succeeded ($backendName) ($i)." );
2335 $this->assertEquals( true, $status->isOK(),
2336 "Locking of files succeeded with OK status ($backendName) ($i)." );
2338 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
2339 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2340 "Locking of files succeeded ($backendName) ($i)." );
2341 $this->assertEquals( true, $status->isOK(),
2342 "Locking of files succeeded with OK status ($backendName) ($i)." );
2344 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
2345 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2346 "Locking of files succeeded ($backendName). ($i)" );
2347 $this->assertEquals( true, $status->isOK(),
2348 "Locking of files succeeded with OK status ($backendName) ($i)." );
2350 ## Flip the acquire/release ordering around ##
2352 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
2353 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2354 "Locking of files succeeded ($backendName) ($i)." );
2355 $this->assertEquals( true, $status->isOK(),
2356 "Locking of files succeeded with OK status ($backendName) ($i)." );
2358 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
2359 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2360 "Locking of files succeeded ($backendName) ($i)." );
2361 $this->assertEquals( true, $status->isOK(),
2362 "Locking of files succeeded with OK status ($backendName) ($i)." );
2364 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
2365 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, 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->unlockFiles( $paths, LockManager::LOCK_SH );
2371 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2372 "Locking of files succeeded ($backendName) ($i)." );
2373 $this->assertEquals( true, $status->isOK(),
2374 "Locking of files succeeded with OK status ($backendName) ($i)." );
2377 $status = Status::newGood();
2378 $sl = $this->backend->getScopedFileLocks( $paths, LockManager::LOCK_EX, $status );
2379 $this->assertType( 'ScopedLock', $sl,
2380 "Scoped locking of files succeeded ($backendName)." );
2381 $this->assertEquals( array(), $status->errors,
2382 "Scoped locking of files succeeded ($backendName)." );
2383 $this->assertEquals( true, $status->isOK(),
2384 "Scoped locking of files succeeded with OK status ($backendName)." );
2386 ScopedLock::release( $sl );
2387 $this->assertEquals( null, $sl,
2388 "Scoped unlocking of files succeeded ($backendName)." );
2389 $this->assertEquals( array(), $status->errors,
2390 "Scoped unlocking of files succeeded ($backendName)." );
2391 $this->assertEquals( true, $status->isOK(),
2392 "Scoped unlocking of files succeeded with OK status ($backendName)." );
2395 // helper function
2396 private function listToArray( $iter ) {
2397 return is_array( $iter ) ? $iter : iterator_to_array( $iter );
2400 // test helper wrapper for backend prepare() function
2401 private function prepare( array $params ) {
2402 return $this->backend->prepare( $params );
2405 // test helper wrapper for backend prepare() function
2406 private function create( array $params ) {
2407 $params['op'] = 'create';
2409 return $this->backend->doQuickOperations( array( $params ) );
2412 function tearDownFiles() {
2413 $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont-bad' );
2414 foreach ( $containers as $container ) {
2415 $this->deleteFiles( $container );
2419 private function deleteFiles( $container ) {
2420 $base = self::baseStorePath();
2421 $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
2422 if ( $iter ) {
2423 foreach ( $iter as $file ) {
2424 $this->backend->quickDelete( array( 'src' => "$base/$container/$file" ) );
2426 // free the directory, to avoid Permission denied under windows on rmdir
2427 unset( $iter );
2429 $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
2432 function assertBackendPathsConsistent( array $paths ) {
2433 if ( $this->backend instanceof FileBackendMultiWrite ) {
2434 $status = $this->backend->consistencyCheck( $paths );
2435 $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
2439 function assertGoodStatus( $status, $msg ) {
2440 $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );