Merge "Improve MIME detection in FileBackend"
[lhc/web/wiklou.git] / tests / phpunit / includes / filebackend / FileBackendTest.php
1 <?php
2
3 /**
4 * @group FileRepo
5 * @group FileBackend
6 * @group medium
7 */
8 class FileBackendTest extends MediaWikiTestCase {
9
10 /** @var FileBackend */
11 private $backend;
12 /** @var FileBackendMultiWrite */
13 private $multiBackend;
14 /** @var FSFileBackend */
15 public $singleBackend;
16 private static $backendToUse;
17
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;
33 }
34 }
35 $useConfig['name'] = 'localtesting'; // swap name
36 $useConfig['shardViaHashLevels'] = array( // test sharding
37 'unittest-cont1' => array( 'levels' => 1, 'base' => 16, 'repeat' => 1 )
38 );
39 if ( isset( $useConfig['fileJournal'] ) ) {
40 $useConfig['fileJournal'] = FileJournal::factory( $useConfig['fileJournal'], $name );
41 }
42 $useConfig['lockManager'] = LockManagerGroup::singleton()->get( $useConfig['lockManager'] );
43 $class = $useConfig['class'];
44 self::$backendToUse = new $class( $useConfig );
45 $this->singleBackend = self::$backendToUse;
46 }
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 ) );
56 }
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
70 ),
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
78 )
79 )
80 ) );
81 }
82
83 protected function tearDown() {
84 parent::tearDown();
85 DeferredUpdates::forceDeferral( false );
86 }
87
88 private static function baseStorePath() {
89 return 'mwstore://localtesting';
90 }
91
92 private function backendClass() {
93 return get_class( $this->backend );
94 }
95
96 /**
97 * @dataProvider provider_testIsStoragePath
98 * @covers FileBackend::isStoragePath
99 */
100 public function testIsStoragePath( $path, $isStorePath ) {
101 $this->assertEquals( $isStorePath, FileBackend::isStoragePath( $path ),
102 "FileBackend::isStoragePath on path '$path'" );
103 }
104
105 public static function provider_testIsStoragePath() {
106 return array(
107 array( 'mwstore://', true ),
108 array( 'mwstore://backend', true ),
109 array( 'mwstore://backend/container', true ),
110 array( 'mwstore://backend/container/', true ),
111 array( 'mwstore://backend/container/path', true ),
112 array( 'mwstore://backend//container/', true ),
113 array( 'mwstore://backend//container//', true ),
114 array( 'mwstore://backend//container//path', true ),
115 array( 'mwstore:///', true ),
116 array( 'mwstore:/', false ),
117 array( 'mwstore:', false ),
118 );
119 }
120
121 /**
122 * @dataProvider provider_testSplitStoragePath
123 * @covers FileBackend::splitStoragePath
124 */
125 public function testSplitStoragePath( $path, $res ) {
126 $this->assertEquals( $res, FileBackend::splitStoragePath( $path ),
127 "FileBackend::splitStoragePath on path '$path'" );
128 }
129
130 public static function provider_testSplitStoragePath() {
131 return array(
132 array( 'mwstore://backend/container', array( 'backend', 'container', '' ) ),
133 array( 'mwstore://backend/container/', array( 'backend', 'container', '' ) ),
134 array( 'mwstore://backend/container/path', array( 'backend', 'container', 'path' ) ),
135 array( 'mwstore://backend/container//path', array( 'backend', 'container', '/path' ) ),
136 array( 'mwstore://backend//container/path', array( null, null, null ) ),
137 array( 'mwstore://backend//container//path', array( null, null, null ) ),
138 array( 'mwstore://', array( null, null, null ) ),
139 array( 'mwstore://backend', array( null, null, null ) ),
140 array( 'mwstore:///', array( null, null, null ) ),
141 array( 'mwstore:/', array( null, null, null ) ),
142 array( 'mwstore:', array( null, null, null ) )
143 );
144 }
145
146 /**
147 * @dataProvider provider_normalizeStoragePath
148 * @covers FileBackend::normalizeStoragePath
149 */
150 public function testNormalizeStoragePath( $path, $res ) {
151 $this->assertEquals( $res, FileBackend::normalizeStoragePath( $path ),
152 "FileBackend::normalizeStoragePath on path '$path'" );
153 }
154
155 public static function provider_normalizeStoragePath() {
156 return array(
157 array( 'mwstore://backend/container', 'mwstore://backend/container' ),
158 array( 'mwstore://backend/container/', 'mwstore://backend/container' ),
159 array( 'mwstore://backend/container/path', 'mwstore://backend/container/path' ),
160 array( 'mwstore://backend/container//path', 'mwstore://backend/container/path' ),
161 array( 'mwstore://backend/container///path', 'mwstore://backend/container/path' ),
162 array(
163 'mwstore://backend/container///path//to///obj',
164 'mwstore://backend/container/path/to/obj'
165 ),
166 array( 'mwstore://', null ),
167 array( 'mwstore://backend', null ),
168 array( 'mwstore://backend//container/path', null ),
169 array( 'mwstore://backend//container//path', null ),
170 array( 'mwstore:///', null ),
171 array( 'mwstore:/', null ),
172 array( 'mwstore:', null ),
173 );
174 }
175
176 /**
177 * @dataProvider provider_testParentStoragePath
178 * @covers FileBackend::parentStoragePath
179 */
180 public function testParentStoragePath( $path, $res ) {
181 $this->assertEquals( $res, FileBackend::parentStoragePath( $path ),
182 "FileBackend::parentStoragePath on path '$path'" );
183 }
184
185 public static function provider_testParentStoragePath() {
186 return array(
187 array( 'mwstore://backend/container/path/to/obj', 'mwstore://backend/container/path/to' ),
188 array( 'mwstore://backend/container/path/to', 'mwstore://backend/container/path' ),
189 array( 'mwstore://backend/container/path', 'mwstore://backend/container' ),
190 array( 'mwstore://backend/container', null ),
191 array( 'mwstore://backend/container/path/to/obj/', 'mwstore://backend/container/path/to' ),
192 array( 'mwstore://backend/container/path/to/', 'mwstore://backend/container/path' ),
193 array( 'mwstore://backend/container/path/', 'mwstore://backend/container' ),
194 array( 'mwstore://backend/container/', null ),
195 );
196 }
197
198 /**
199 * @dataProvider provider_testExtensionFromPath
200 * @covers FileBackend::extensionFromPath
201 */
202 public function testExtensionFromPath( $path, $res ) {
203 $this->assertEquals( $res, FileBackend::extensionFromPath( $path ),
204 "FileBackend::extensionFromPath on path '$path'" );
205 }
206
207 public static function provider_testExtensionFromPath() {
208 return array(
209 array( 'mwstore://backend/container/path.txt', 'txt' ),
210 array( 'mwstore://backend/container/path.svg.png', 'png' ),
211 array( 'mwstore://backend/container/path', '' ),
212 array( 'mwstore://backend/container/path.', '' ),
213 );
214 }
215
216 /**
217 * @dataProvider provider_testStore
218 */
219 public function testStore( $op ) {
220 $this->addTmpFiles( $op['src'] );
221
222 $this->backend = $this->singleBackend;
223 $this->tearDownFiles();
224 $this->doTestStore( $op );
225 $this->tearDownFiles();
226
227 $this->backend = $this->multiBackend;
228 $this->tearDownFiles();
229 $this->doTestStore( $op );
230 $this->tearDownFiles();
231 }
232
233 /**
234 * @covers FileBackend::doOperation
235 */
236 private function doTestStore( $op ) {
237 $backendName = $this->backendClass();
238
239 $source = $op['src'];
240 $dest = $op['dst'];
241 $this->prepare( array( 'dir' => dirname( $dest ) ) );
242
243 file_put_contents( $source, "Unit test file" );
244
245 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
246 $this->backend->store( $op );
247 }
248
249 $status = $this->backend->doOperation( $op );
250
251 $this->assertGoodStatus( $status,
252 "Store from $source to $dest succeeded without warnings ($backendName)." );
253 $this->assertEquals( true, $status->isOK(),
254 "Store from $source to $dest succeeded ($backendName)." );
255 $this->assertEquals( array( 0 => true ), $status->success,
256 "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
257 $this->assertEquals( true, file_exists( $source ),
258 "Source file $source still exists ($backendName)." );
259 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
260 "Destination file $dest exists ($backendName)." );
261
262 $this->assertEquals( filesize( $source ),
263 $this->backend->getFileSize( array( 'src' => $dest ) ),
264 "Destination file $dest has correct size ($backendName)." );
265
266 $props1 = FSFile::getPropsFromPath( $source );
267 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
268 $this->assertEquals( $props1, $props2,
269 "Source and destination have the same props ($backendName)." );
270
271 $this->assertBackendPathsConsistent( array( $dest ) );
272 }
273
274 public static function provider_testStore() {
275 $cases = array();
276
277 $tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath();
278 $toPath = self::baseStorePath() . '/unittest-cont1/e/fun/obj1.txt';
279 $op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath );
280 $cases[] = array( $op );
281
282 $op2 = $op;
283 $op2['overwrite'] = true;
284 $cases[] = array( $op2 );
285
286 $op3 = $op;
287 $op3['overwriteSame'] = true;
288 $cases[] = array( $op3 );
289
290 return $cases;
291 }
292
293 /**
294 * @dataProvider provider_testCopy
295 * @covers FileBackend::doOperation
296 */
297 public function testCopy( $op ) {
298 $this->backend = $this->singleBackend;
299 $this->tearDownFiles();
300 $this->doTestCopy( $op );
301 $this->tearDownFiles();
302
303 $this->backend = $this->multiBackend;
304 $this->tearDownFiles();
305 $this->doTestCopy( $op );
306 $this->tearDownFiles();
307 }
308
309 private function doTestCopy( $op ) {
310 $backendName = $this->backendClass();
311
312 $source = $op['src'];
313 $dest = $op['dst'];
314 $this->prepare( array( 'dir' => dirname( $source ) ) );
315 $this->prepare( array( 'dir' => dirname( $dest ) ) );
316
317 if ( isset( $op['ignoreMissingSource'] ) ) {
318 $status = $this->backend->doOperation( $op );
319 $this->assertGoodStatus( $status,
320 "Move from $source to $dest succeeded without warnings ($backendName)." );
321 $this->assertEquals( array( 0 => true ), $status->success,
322 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
323 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
324 "Source file $source does not exist ($backendName)." );
325 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $dest ) ),
326 "Destination file $dest does not exist ($backendName)." );
327
328 return; // done
329 }
330
331 $status = $this->backend->doOperation(
332 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
333 $this->assertGoodStatus( $status,
334 "Creation of file at $source succeeded ($backendName)." );
335
336 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
337 $this->backend->copy( $op );
338 }
339
340 $status = $this->backend->doOperation( $op );
341
342 $this->assertGoodStatus( $status,
343 "Copy from $source to $dest succeeded without warnings ($backendName)." );
344 $this->assertEquals( true, $status->isOK(),
345 "Copy from $source to $dest succeeded ($backendName)." );
346 $this->assertEquals( array( 0 => true ), $status->success,
347 "Copy from $source to $dest has proper 'success' field in Status ($backendName)." );
348 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $source ) ),
349 "Source file $source still exists ($backendName)." );
350 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
351 "Destination file $dest exists after copy ($backendName)." );
352
353 $this->assertEquals(
354 $this->backend->getFileSize( array( 'src' => $source ) ),
355 $this->backend->getFileSize( array( 'src' => $dest ) ),
356 "Destination file $dest has correct size ($backendName)." );
357
358 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
359 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
360 $this->assertEquals( $props1, $props2,
361 "Source and destination have the same props ($backendName)." );
362
363 $this->assertBackendPathsConsistent( array( $source, $dest ) );
364 }
365
366 public static function provider_testCopy() {
367 $cases = array();
368
369 $source = self::baseStorePath() . '/unittest-cont1/e/file.txt';
370 $dest = self::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
371
372 $op = array( 'op' => 'copy', 'src' => $source, 'dst' => $dest );
373 $cases[] = array(
374 $op, // operation
375 $source, // source
376 $dest, // dest
377 );
378
379 $op2 = $op;
380 $op2['overwrite'] = true;
381 $cases[] = array(
382 $op2, // operation
383 $source, // source
384 $dest, // dest
385 );
386
387 $op2 = $op;
388 $op2['overwriteSame'] = true;
389 $cases[] = array(
390 $op2, // operation
391 $source, // source
392 $dest, // dest
393 );
394
395 $op2 = $op;
396 $op2['ignoreMissingSource'] = true;
397 $cases[] = array(
398 $op2, // operation
399 $source, // source
400 $dest, // dest
401 );
402
403 $op2 = $op;
404 $op2['ignoreMissingSource'] = true;
405 $cases[] = array(
406 $op2, // operation
407 self::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
408 $dest, // dest
409 );
410
411 return $cases;
412 }
413
414 /**
415 * @dataProvider provider_testMove
416 * @covers FileBackend::doOperation
417 */
418 public function testMove( $op ) {
419 $this->backend = $this->singleBackend;
420 $this->tearDownFiles();
421 $this->doTestMove( $op );
422 $this->tearDownFiles();
423
424 $this->backend = $this->multiBackend;
425 $this->tearDownFiles();
426 $this->doTestMove( $op );
427 $this->tearDownFiles();
428 }
429
430 private function doTestMove( $op ) {
431 $backendName = $this->backendClass();
432
433 $source = $op['src'];
434 $dest = $op['dst'];
435 $this->prepare( array( 'dir' => dirname( $source ) ) );
436 $this->prepare( array( 'dir' => dirname( $dest ) ) );
437
438 if ( isset( $op['ignoreMissingSource'] ) ) {
439 $status = $this->backend->doOperation( $op );
440 $this->assertGoodStatus( $status,
441 "Move from $source to $dest succeeded without warnings ($backendName)." );
442 $this->assertEquals( array( 0 => true ), $status->success,
443 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
444 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
445 "Source file $source does not exist ($backendName)." );
446 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $dest ) ),
447 "Destination file $dest does not exist ($backendName)." );
448
449 return; // done
450 }
451
452 $status = $this->backend->doOperation(
453 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
454 $this->assertGoodStatus( $status,
455 "Creation of file at $source succeeded ($backendName)." );
456
457 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
458 $this->backend->copy( $op );
459 }
460
461 $status = $this->backend->doOperation( $op );
462 $this->assertGoodStatus( $status,
463 "Move from $source to $dest succeeded without warnings ($backendName)." );
464 $this->assertEquals( true, $status->isOK(),
465 "Move from $source to $dest succeeded ($backendName)." );
466 $this->assertEquals( array( 0 => true ), $status->success,
467 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
468 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
469 "Source file $source does not still exists ($backendName)." );
470 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
471 "Destination file $dest exists after move ($backendName)." );
472
473 $this->assertNotEquals(
474 $this->backend->getFileSize( array( 'src' => $source ) ),
475 $this->backend->getFileSize( array( 'src' => $dest ) ),
476 "Destination file $dest has correct size ($backendName)." );
477
478 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
479 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
480 $this->assertEquals( false, $props1['fileExists'],
481 "Source file does not exist accourding to props ($backendName)." );
482 $this->assertEquals( true, $props2['fileExists'],
483 "Destination file exists accourding to props ($backendName)." );
484
485 $this->assertBackendPathsConsistent( array( $source, $dest ) );
486 }
487
488 public static function provider_testMove() {
489 $cases = array();
490
491 $source = self::baseStorePath() . '/unittest-cont1/e/file.txt';
492 $dest = self::baseStorePath() . '/unittest-cont2/a/fileMoved.txt';
493
494 $op = array( 'op' => 'move', 'src' => $source, 'dst' => $dest );
495 $cases[] = array(
496 $op, // operation
497 $source, // source
498 $dest, // dest
499 );
500
501 $op2 = $op;
502 $op2['overwrite'] = true;
503 $cases[] = array(
504 $op2, // operation
505 $source, // source
506 $dest, // dest
507 );
508
509 $op2 = $op;
510 $op2['overwriteSame'] = true;
511 $cases[] = array(
512 $op2, // operation
513 $source, // source
514 $dest, // dest
515 );
516
517 $op2 = $op;
518 $op2['ignoreMissingSource'] = true;
519 $cases[] = array(
520 $op2, // operation
521 $source, // source
522 $dest, // dest
523 );
524
525 $op2 = $op;
526 $op2['ignoreMissingSource'] = true;
527 $cases[] = array(
528 $op2, // operation
529 self::baseStorePath() . '/unittest-cont-bad/e/file.txt', // source
530 $dest, // dest
531 );
532
533 return $cases;
534 }
535
536 /**
537 * @dataProvider provider_testDelete
538 * @covers FileBackend::doOperation
539 */
540 public function testDelete( $op, $withSource, $okStatus ) {
541 $this->backend = $this->singleBackend;
542 $this->tearDownFiles();
543 $this->doTestDelete( $op, $withSource, $okStatus );
544 $this->tearDownFiles();
545
546 $this->backend = $this->multiBackend;
547 $this->tearDownFiles();
548 $this->doTestDelete( $op, $withSource, $okStatus );
549 $this->tearDownFiles();
550 }
551
552 private function doTestDelete( $op, $withSource, $okStatus ) {
553 $backendName = $this->backendClass();
554
555 $source = $op['src'];
556 $this->prepare( array( 'dir' => dirname( $source ) ) );
557
558 if ( $withSource ) {
559 $status = $this->backend->doOperation(
560 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
561 $this->assertGoodStatus( $status,
562 "Creation of file at $source succeeded ($backendName)." );
563 }
564
565 $status = $this->backend->doOperation( $op );
566 if ( $okStatus ) {
567 $this->assertGoodStatus( $status,
568 "Deletion of file at $source succeeded without warnings ($backendName)." );
569 $this->assertEquals( true, $status->isOK(),
570 "Deletion of file at $source succeeded ($backendName)." );
571 $this->assertEquals( array( 0 => true ), $status->success,
572 "Deletion of file at $source has proper 'success' field in Status ($backendName)." );
573 } else {
574 $this->assertEquals( false, $status->isOK(),
575 "Deletion of file at $source failed ($backendName)." );
576 }
577
578 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
579 "Source file $source does not exist after move ($backendName)." );
580
581 $this->assertFalse(
582 $this->backend->getFileSize( array( 'src' => $source ) ),
583 "Source file $source has correct size (false) ($backendName)." );
584
585 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
586 $this->assertFalse( $props1['fileExists'],
587 "Source file $source does not exist according to props ($backendName)." );
588
589 $this->assertBackendPathsConsistent( array( $source ) );
590 }
591
592 public static function provider_testDelete() {
593 $cases = array();
594
595 $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
596
597 $op = array( 'op' => 'delete', 'src' => $source );
598 $cases[] = array(
599 $op, // operation
600 true, // with source
601 true // succeeds
602 );
603
604 $cases[] = array(
605 $op, // operation
606 false, // without source
607 false // fails
608 );
609
610 $op['ignoreMissingSource'] = true;
611 $cases[] = array(
612 $op, // operation
613 false, // without source
614 true // succeeds
615 );
616
617 $op['ignoreMissingSource'] = true;
618 $op['src'] = self::baseStorePath() . '/unittest-cont-bad/e/file.txt';
619 $cases[] = array(
620 $op, // operation
621 false, // without source
622 true // succeeds
623 );
624
625 return $cases;
626 }
627
628 /**
629 * @dataProvider provider_testDescribe
630 * @covers FileBackend::doOperation
631 */
632 public function testDescribe( $op, $withSource, $okStatus ) {
633 $this->backend = $this->singleBackend;
634 $this->tearDownFiles();
635 $this->doTestDescribe( $op, $withSource, $okStatus );
636 $this->tearDownFiles();
637
638 $this->backend = $this->multiBackend;
639 $this->tearDownFiles();
640 $this->doTestDescribe( $op, $withSource, $okStatus );
641 $this->tearDownFiles();
642 }
643
644 private function doTestDescribe( $op, $withSource, $okStatus ) {
645 $backendName = $this->backendClass();
646
647 $source = $op['src'];
648 $this->prepare( array( 'dir' => dirname( $source ) ) );
649
650 if ( $withSource ) {
651 $status = $this->backend->doOperation(
652 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source,
653 'headers' => array( 'Content-Disposition' => 'xxx' ) ) );
654 $this->assertGoodStatus( $status,
655 "Creation of file at $source succeeded ($backendName)." );
656 if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
657 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
658 $this->assertHasHeaders( array( 'Content-Disposition' => 'xxx' ), $attr );
659 }
660
661 $status = $this->backend->describe( array( 'src' => $source,
662 'headers' => array( 'Content-Disposition' => '' ) ) ); // remove
663 $this->assertGoodStatus( $status,
664 "Removal of header for $source succeeded ($backendName)." );
665
666 if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
667 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
668 $this->assertFalse( isset( $attr['headers']['content-disposition'] ),
669 "File 'Content-Disposition' header removed." );
670 }
671 }
672
673 $status = $this->backend->doOperation( $op );
674 if ( $okStatus ) {
675 $this->assertGoodStatus( $status,
676 "Describe of file at $source succeeded without warnings ($backendName)." );
677 $this->assertEquals( true, $status->isOK(),
678 "Describe of file at $source succeeded ($backendName)." );
679 $this->assertEquals( array( 0 => true ), $status->success,
680 "Describe of file at $source has proper 'success' field in Status ($backendName)." );
681 if ( $this->backend->hasFeatures( FileBackend::ATTR_HEADERS ) ) {
682 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
683 $this->assertHasHeaders( $op['headers'], $attr );
684 }
685 } else {
686 $this->assertEquals( false, $status->isOK(),
687 "Describe of file at $source failed ($backendName)." );
688 }
689
690 $this->assertBackendPathsConsistent( array( $source ) );
691 }
692
693 private function assertHasHeaders( array $headers, array $attr ) {
694 foreach ( $headers as $n => $v ) {
695 if ( $n !== '' ) {
696 $this->assertTrue( isset( $attr['headers'][strtolower( $n )] ),
697 "File has '$n' header." );
698 $this->assertEquals( $v, $attr['headers'][strtolower( $n )],
699 "File has '$n' header value." );
700 } else {
701 $this->assertFalse( isset( $attr['headers'][strtolower( $n )] ),
702 "File does not have '$n' header." );
703 }
704 }
705 }
706
707 public static function provider_testDescribe() {
708 $cases = array();
709
710 $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
711
712 $op = array( 'op' => 'describe', 'src' => $source,
713 'headers' => array( 'Content-Disposition' => 'inline' ), );
714 $cases[] = array(
715 $op, // operation
716 true, // with source
717 true // succeeds
718 );
719
720 $cases[] = array(
721 $op, // operation
722 false, // without source
723 false // fails
724 );
725
726 return $cases;
727 }
728
729 /**
730 * @dataProvider provider_testCreate
731 * @covers FileBackend::doOperation
732 */
733 public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
734 $this->backend = $this->singleBackend;
735 $this->tearDownFiles();
736 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
737 $this->tearDownFiles();
738
739 $this->backend = $this->multiBackend;
740 $this->tearDownFiles();
741 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
742 $this->tearDownFiles();
743 }
744
745 private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
746 $backendName = $this->backendClass();
747
748 $dest = $op['dst'];
749 $this->prepare( array( 'dir' => dirname( $dest ) ) );
750
751 $oldText = 'blah...blah...waahwaah';
752 if ( $alreadyExists ) {
753 $status = $this->backend->doOperation(
754 array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
755 $this->assertGoodStatus( $status,
756 "Creation of file at $dest succeeded ($backendName)." );
757 }
758
759 $status = $this->backend->doOperation( $op );
760 if ( $okStatus ) {
761 $this->assertGoodStatus( $status,
762 "Creation of file at $dest succeeded without warnings ($backendName)." );
763 $this->assertEquals( true, $status->isOK(),
764 "Creation of file at $dest succeeded ($backendName)." );
765 $this->assertEquals( array( 0 => true ), $status->success,
766 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
767 } else {
768 $this->assertEquals( false, $status->isOK(),
769 "Creation of file at $dest failed ($backendName)." );
770 }
771
772 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
773 "Destination file $dest exists after creation ($backendName)." );
774
775 $props1 = $this->backend->getFileProps( array( 'src' => $dest ) );
776 $this->assertEquals( true, $props1['fileExists'],
777 "Destination file $dest exists according to props ($backendName)." );
778 if ( $okStatus ) { // file content is what we saved
779 $this->assertEquals( $newSize, $props1['size'],
780 "Destination file $dest has expected size according to props ($backendName)." );
781 $this->assertEquals( $newSize,
782 $this->backend->getFileSize( array( 'src' => $dest ) ),
783 "Destination file $dest has correct size ($backendName)." );
784 } else { // file content is some other previous text
785 $this->assertEquals( strlen( $oldText ), $props1['size'],
786 "Destination file $dest has original size according to props ($backendName)." );
787 $this->assertEquals( strlen( $oldText ),
788 $this->backend->getFileSize( array( 'src' => $dest ) ),
789 "Destination file $dest has original size according to props ($backendName)." );
790 }
791
792 $this->assertBackendPathsConsistent( array( $dest ) );
793 }
794
795 /**
796 * @dataProvider provider_testCreate
797 */
798 public static function provider_testCreate() {
799 $cases = array();
800
801 $dest = self::baseStorePath() . '/unittest-cont2/a/myspacefile.txt';
802
803 $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
804 $cases[] = array(
805 $op, // operation
806 false, // no dest already exists
807 true, // succeeds
808 strlen( $op['content'] )
809 );
810
811 $op2 = $op;
812 $op2['content'] = "\n";
813 $cases[] = array(
814 $op2, // operation
815 false, // no dest already exists
816 true, // succeeds
817 strlen( $op2['content'] )
818 );
819
820 $op2 = $op;
821 $op2['content'] = "fsf\n waf 3kt";
822 $cases[] = array(
823 $op2, // operation
824 true, // dest already exists
825 false, // fails
826 strlen( $op2['content'] )
827 );
828
829 $op2 = $op;
830 $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
831 $op2['overwrite'] = true;
832 $cases[] = array(
833 $op2, // operation
834 true, // dest already exists
835 true, // succeeds
836 strlen( $op2['content'] )
837 );
838
839 $op2 = $op;
840 $op2['content'] = "39qjmg3-qg";
841 $op2['overwriteSame'] = true;
842 $cases[] = array(
843 $op2, // operation
844 true, // dest already exists
845 false, // succeeds
846 strlen( $op2['content'] )
847 );
848
849 return $cases;
850 }
851
852 /**
853 * @covers FileBackend::doQuickOperations
854 */
855 public function testDoQuickOperations() {
856 $this->backend = $this->singleBackend;
857 $this->doTestDoQuickOperations();
858 $this->tearDownFiles();
859
860 $this->backend = $this->multiBackend;
861 $this->doTestDoQuickOperations();
862 $this->tearDownFiles();
863 }
864
865 private function doTestDoQuickOperations() {
866 $backendName = $this->backendClass();
867
868 $base = self::baseStorePath();
869 $files = array(
870 "$base/unittest-cont1/e/fileA.a",
871 "$base/unittest-cont1/e/fileB.a",
872 "$base/unittest-cont1/e/fileC.a"
873 );
874 $createOps = array();
875 $purgeOps = array();
876 foreach ( $files as $path ) {
877 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
878 $this->assertGoodStatus( $status,
879 "Preparing $path succeeded without warnings ($backendName)." );
880 $createOps[] = array( 'op' => 'create', 'dst' => $path, 'content' => mt_rand( 0, 50000 ) );
881 $copyOps[] = array( 'op' => 'copy', 'src' => $path, 'dst' => "$path-2" );
882 $moveOps[] = array( 'op' => 'move', 'src' => "$path-2", 'dst' => "$path-3" );
883 $purgeOps[] = array( 'op' => 'delete', 'src' => $path );
884 $purgeOps[] = array( 'op' => 'delete', 'src' => "$path-3" );
885 }
886 $purgeOps[] = array( 'op' => 'null' );
887
888 $this->assertGoodStatus(
889 $this->backend->doQuickOperations( $createOps ),
890 "Creation of source files succeeded ($backendName)." );
891 foreach ( $files as $file ) {
892 $this->assertTrue( $this->backend->fileExists( array( 'src' => $file ) ),
893 "File $file exists." );
894 }
895
896 $this->assertGoodStatus(
897 $this->backend->doQuickOperations( $copyOps ),
898 "Quick copy of source files succeeded ($backendName)." );
899 foreach ( $files as $file ) {
900 $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
901 "File $file-2 exists." );
902 }
903
904 $this->assertGoodStatus(
905 $this->backend->doQuickOperations( $moveOps ),
906 "Quick move of source files succeeded ($backendName)." );
907 foreach ( $files as $file ) {
908 $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
909 "File $file-3 move in." );
910 $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
911 "File $file-2 moved away." );
912 }
913
914 $this->assertGoodStatus(
915 $this->backend->quickCopy( array( 'src' => $files[0], 'dst' => $files[0] ) ),
916 "Copy of file {$files[0]} over itself succeeded ($backendName)." );
917 $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
918 "File {$files[0]} still exists." );
919
920 $this->assertGoodStatus(
921 $this->backend->quickMove( array( 'src' => $files[0], 'dst' => $files[0] ) ),
922 "Move of file {$files[0]} over itself succeeded ($backendName)." );
923 $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
924 "File {$files[0]} still exists." );
925
926 $this->assertGoodStatus(
927 $this->backend->doQuickOperations( $purgeOps ),
928 "Quick deletion of source files succeeded ($backendName)." );
929 foreach ( $files as $file ) {
930 $this->assertFalse( $this->backend->fileExists( array( 'src' => $file ) ),
931 "File $file purged." );
932 $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
933 "File $file-3 purged." );
934 }
935 }
936
937 /**
938 * @dataProvider provider_testConcatenate
939 */
940 public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
941 $this->backend = $this->singleBackend;
942 $this->tearDownFiles();
943 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
944 $this->tearDownFiles();
945
946 $this->backend = $this->multiBackend;
947 $this->tearDownFiles();
948 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
949 $this->tearDownFiles();
950 }
951
952 private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
953 $backendName = $this->backendClass();
954
955 $expContent = '';
956 // Create sources
957 $ops = array();
958 foreach ( $srcs as $i => $source ) {
959 $this->prepare( array( 'dir' => dirname( $source ) ) );
960 $ops[] = array(
961 'op' => 'create', // operation
962 'dst' => $source, // source
963 'content' => $srcsContent[$i]
964 );
965 $expContent .= $srcsContent[$i];
966 }
967 $status = $this->backend->doOperations( $ops );
968
969 $this->assertGoodStatus( $status,
970 "Creation of source files succeeded ($backendName)." );
971
972 $dest = $params['dst'] = $this->getNewTempFile();
973 if ( $alreadyExists ) {
974 $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
975 $this->assertEquals( true, $ok,
976 "Creation of file at $dest succeeded ($backendName)." );
977 } else {
978 $ok = file_put_contents( $dest, '' ) !== false;
979 $this->assertEquals( true, $ok,
980 "Creation of 0-byte file at $dest succeeded ($backendName)." );
981 }
982
983 // Combine the files into one
984 $status = $this->backend->concatenate( $params );
985 if ( $okStatus ) {
986 $this->assertGoodStatus( $status,
987 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
988 $this->assertEquals( true, $status->isOK(),
989 "Creation of concat file at $dest succeeded ($backendName)." );
990 } else {
991 $this->assertEquals( false, $status->isOK(),
992 "Creation of concat file at $dest failed ($backendName)." );
993 }
994
995 if ( $okStatus ) {
996 $this->assertEquals( true, is_file( $dest ),
997 "Dest concat file $dest exists after creation ($backendName)." );
998 } else {
999 $this->assertEquals( true, is_file( $dest ),
1000 "Dest concat file $dest exists after failed creation ($backendName)." );
1001 }
1002
1003 $contents = file_get_contents( $dest );
1004 $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
1005
1006 if ( $okStatus ) {
1007 $this->assertEquals( $expContent, $contents,
1008 "Concat file at $dest has correct contents ($backendName)." );
1009 } else {
1010 $this->assertNotEquals( $expContent, $contents,
1011 "Concat file at $dest has correct contents ($backendName)." );
1012 }
1013 }
1014
1015 public static function provider_testConcatenate() {
1016 $cases = array();
1017
1018 $srcs = array(
1019 self::baseStorePath() . '/unittest-cont1/e/file1.txt',
1020 self::baseStorePath() . '/unittest-cont1/e/file2.txt',
1021 self::baseStorePath() . '/unittest-cont1/e/file3.txt',
1022 self::baseStorePath() . '/unittest-cont1/e/file4.txt',
1023 self::baseStorePath() . '/unittest-cont1/e/file5.txt',
1024 self::baseStorePath() . '/unittest-cont1/e/file6.txt',
1025 self::baseStorePath() . '/unittest-cont1/e/file7.txt',
1026 self::baseStorePath() . '/unittest-cont1/e/file8.txt',
1027 self::baseStorePath() . '/unittest-cont1/e/file9.txt',
1028 self::baseStorePath() . '/unittest-cont1/e/file10.txt'
1029 );
1030 $content = array(
1031 'egfage',
1032 'ageageag',
1033 'rhokohlr',
1034 'shgmslkg',
1035 'kenga',
1036 'owagmal',
1037 'kgmae',
1038 'g eak;g',
1039 'lkaem;a',
1040 'legma'
1041 );
1042 $params = array( 'srcs' => $srcs );
1043
1044 $cases[] = array(
1045 $params, // operation
1046 $srcs, // sources
1047 $content, // content for each source
1048 false, // no dest already exists
1049 true, // succeeds
1050 );
1051
1052 $cases[] = array(
1053 $params, // operation
1054 $srcs, // sources
1055 $content, // content for each source
1056 true, // dest already exists
1057 false, // succeeds
1058 );
1059
1060 return $cases;
1061 }
1062
1063 /**
1064 * @dataProvider provider_testGetFileStat
1065 * @covers FileBackend::getFileStat
1066 */
1067 public function testGetFileStat( $path, $content, $alreadyExists ) {
1068 $this->backend = $this->singleBackend;
1069 $this->tearDownFiles();
1070 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1071 $this->tearDownFiles();
1072
1073 $this->backend = $this->multiBackend;
1074 $this->tearDownFiles();
1075 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1076 $this->tearDownFiles();
1077 }
1078
1079 private function doTestGetFileStat( $path, $content, $alreadyExists ) {
1080 $backendName = $this->backendClass();
1081
1082 if ( $alreadyExists ) {
1083 $this->prepare( array( 'dir' => dirname( $path ) ) );
1084 $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
1085 $this->assertGoodStatus( $status,
1086 "Creation of file at $path succeeded ($backendName)." );
1087
1088 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1089 $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
1090 $stat = $this->backend->getFileStat( array( 'src' => $path ) );
1091
1092 $this->assertEquals( strlen( $content ), $size,
1093 "Correct file size of '$path'" );
1094 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
1095 "Correct file timestamp of '$path'" );
1096
1097 $size = $stat['size'];
1098 $time = $stat['mtime'];
1099 $this->assertEquals( strlen( $content ), $size,
1100 "Correct file size of '$path'" );
1101 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
1102 "Correct file timestamp of '$path'" );
1103
1104 $this->backend->clearCache( array( $path ) );
1105
1106 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1107
1108 $this->assertEquals( strlen( $content ), $size,
1109 "Correct file size of '$path'" );
1110
1111 $this->backend->preloadCache( array( $path ) );
1112
1113 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1114
1115 $this->assertEquals( strlen( $content ), $size,
1116 "Correct file size of '$path'" );
1117 } else {
1118 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1119 $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
1120 $stat = $this->backend->getFileStat( array( 'src' => $path ) );
1121
1122 $this->assertFalse( $size, "Correct file size of '$path'" );
1123 $this->assertFalse( $time, "Correct file timestamp of '$path'" );
1124 $this->assertFalse( $stat, "Correct file stat of '$path'" );
1125 }
1126 }
1127
1128 public static function provider_testGetFileStat() {
1129 $cases = array();
1130
1131 $base = self::baseStorePath();
1132 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents", true );
1133 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "", true );
1134 $cases[] = array( "$base/unittest-cont1/e/b/some-diff_file.txt", null, false );
1135
1136 return $cases;
1137 }
1138
1139 /**
1140 * @dataProvider provider_testGetFileStat
1141 * @covers FileBackend::streamFile
1142 */
1143 public function testStreamFile( $path, $content, $alreadyExists ) {
1144 $this->backend = $this->singleBackend;
1145 $this->tearDownFiles();
1146 $this->doTestStreamFile( $path, $content, $alreadyExists );
1147 $this->tearDownFiles();
1148 }
1149
1150 private function doTestStreamFile( $path, $content ) {
1151 $backendName = $this->backendClass();
1152
1153 // Test doStreamFile() directly to avoid header madness
1154 $class = new ReflectionClass( $this->backend );
1155 $method = $class->getMethod( 'doStreamFile' );
1156 $method->setAccessible( true );
1157
1158 if ( $content !== null ) {
1159 $this->prepare( array( 'dir' => dirname( $path ) ) );
1160 $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
1161 $this->assertGoodStatus( $status,
1162 "Creation of file at $path succeeded ($backendName)." );
1163
1164 ob_start();
1165 $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
1166 $data = ob_get_contents();
1167 ob_end_clean();
1168
1169 $this->assertEquals( $content, $data, "Correct content streamed from '$path'" );
1170 } else { // 404 case
1171 ob_start();
1172 $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
1173 $data = ob_get_contents();
1174 ob_end_clean();
1175
1176 $this->assertEquals( '', $data, "Correct content streamed from '$path' ($backendName)" );
1177 }
1178 }
1179
1180 public static function provider_testStreamFile() {
1181 $cases = array();
1182
1183 $base = self::baseStorePath();
1184 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
1185 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", null );
1186
1187 return $cases;
1188 }
1189
1190 /**
1191 * @dataProvider provider_testGetFileContents
1192 * @covers FileBackend::getFileContents
1193 * @covers FileBackend::getFileContentsMulti
1194 */
1195 public function testGetFileContents( $source, $content ) {
1196 $this->backend = $this->singleBackend;
1197 $this->tearDownFiles();
1198 $this->doTestGetFileContents( $source, $content );
1199 $this->tearDownFiles();
1200
1201 $this->backend = $this->multiBackend;
1202 $this->tearDownFiles();
1203 $this->doTestGetFileContents( $source, $content );
1204 $this->tearDownFiles();
1205 }
1206
1207 private function doTestGetFileContents( $source, $content ) {
1208 $backendName = $this->backendClass();
1209
1210 $srcs = (array)$source;
1211 $content = (array)$content;
1212 foreach ( $srcs as $i => $src ) {
1213 $this->prepare( array( 'dir' => dirname( $src ) ) );
1214 $status = $this->backend->doOperation(
1215 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1216 $this->assertGoodStatus( $status,
1217 "Creation of file at $src succeeded ($backendName)." );
1218 }
1219
1220 if ( is_array( $source ) ) {
1221 $contents = $this->backend->getFileContentsMulti( array( 'srcs' => $source ) );
1222 foreach ( $contents as $path => $data ) {
1223 $this->assertNotEquals( false, $data, "Contents of $path exists ($backendName)." );
1224 $this->assertEquals(
1225 current( $content ),
1226 $data,
1227 "Contents of $path is correct ($backendName)."
1228 );
1229 next( $content );
1230 }
1231 $this->assertEquals(
1232 $source,
1233 array_keys( $contents ),
1234 "Contents in right order ($backendName)."
1235 );
1236 $this->assertEquals(
1237 count( $source ),
1238 count( $contents ),
1239 "Contents array size correct ($backendName)."
1240 );
1241 } else {
1242 $data = $this->backend->getFileContents( array( 'src' => $source ) );
1243 $this->assertNotEquals( false, $data, "Contents of $source exists ($backendName)." );
1244 $this->assertEquals( $content[0], $data, "Contents of $source is correct ($backendName)." );
1245 }
1246 }
1247
1248 public static function provider_testGetFileContents() {
1249 $cases = array();
1250
1251 $base = self::baseStorePath();
1252 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
1253 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "more file contents" );
1254 $cases[] = array(
1255 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1256 "$base/unittest-cont1/e/a/z.txt" ),
1257 array( "contents xx", "contents xy", "contents xz" )
1258 );
1259
1260 return $cases;
1261 }
1262
1263 /**
1264 * @dataProvider provider_testGetLocalCopy
1265 * @covers FileBackend::getLocalCopy
1266 */
1267 public function testGetLocalCopy( $source, $content ) {
1268 $this->backend = $this->singleBackend;
1269 $this->tearDownFiles();
1270 $this->doTestGetLocalCopy( $source, $content );
1271 $this->tearDownFiles();
1272
1273 $this->backend = $this->multiBackend;
1274 $this->tearDownFiles();
1275 $this->doTestGetLocalCopy( $source, $content );
1276 $this->tearDownFiles();
1277 }
1278
1279 private function doTestGetLocalCopy( $source, $content ) {
1280 $backendName = $this->backendClass();
1281
1282 $srcs = (array)$source;
1283 $content = (array)$content;
1284 foreach ( $srcs as $i => $src ) {
1285 $this->prepare( array( 'dir' => dirname( $src ) ) );
1286 $status = $this->backend->doOperation(
1287 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1288 $this->assertGoodStatus( $status,
1289 "Creation of file at $src succeeded ($backendName)." );
1290 }
1291
1292 if ( is_array( $source ) ) {
1293 $tmpFiles = $this->backend->getLocalCopyMulti( array( 'srcs' => $source ) );
1294 foreach ( $tmpFiles as $path => $tmpFile ) {
1295 $this->assertNotNull( $tmpFile,
1296 "Creation of local copy of $path succeeded ($backendName)." );
1297 $contents = file_get_contents( $tmpFile->getPath() );
1298 $this->assertNotEquals( false, $contents, "Local copy of $path exists ($backendName)." );
1299 $this->assertEquals(
1300 current( $content ),
1301 $contents,
1302 "Local copy of $path is correct ($backendName)."
1303 );
1304 next( $content );
1305 }
1306 $this->assertEquals(
1307 $source,
1308 array_keys( $tmpFiles ),
1309 "Local copies in right order ($backendName)."
1310 );
1311 $this->assertEquals(
1312 count( $source ),
1313 count( $tmpFiles ),
1314 "Local copies array size correct ($backendName)."
1315 );
1316 } else {
1317 $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
1318 $this->assertNotNull( $tmpFile,
1319 "Creation of local copy of $source succeeded ($backendName)." );
1320 $contents = file_get_contents( $tmpFile->getPath() );
1321 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
1322 $this->assertEquals(
1323 $content[0],
1324 $contents,
1325 "Local copy of $source is correct ($backendName)."
1326 );
1327 }
1328
1329 $obj = new stdClass();
1330 $tmpFile->bind( $obj );
1331 }
1332
1333 public static function provider_testGetLocalCopy() {
1334 $cases = array();
1335
1336 $base = self::baseStorePath();
1337 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1338 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1339 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1340 $cases[] = array(
1341 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1342 "$base/unittest-cont1/e/a/z.txt" ),
1343 array( "contents xx $", "contents xy 111", "contents xz" )
1344 );
1345
1346 return $cases;
1347 }
1348
1349 /**
1350 * @dataProvider provider_testGetLocalReference
1351 * @covers FileBackend::getLocalReference
1352 */
1353 public function testGetLocalReference( $source, $content ) {
1354 $this->backend = $this->singleBackend;
1355 $this->tearDownFiles();
1356 $this->doTestGetLocalReference( $source, $content );
1357 $this->tearDownFiles();
1358
1359 $this->backend = $this->multiBackend;
1360 $this->tearDownFiles();
1361 $this->doTestGetLocalReference( $source, $content );
1362 $this->tearDownFiles();
1363 }
1364
1365 private function doTestGetLocalReference( $source, $content ) {
1366 $backendName = $this->backendClass();
1367
1368 $srcs = (array)$source;
1369 $content = (array)$content;
1370 foreach ( $srcs as $i => $src ) {
1371 $this->prepare( array( 'dir' => dirname( $src ) ) );
1372 $status = $this->backend->doOperation(
1373 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1374 $this->assertGoodStatus( $status,
1375 "Creation of file at $src succeeded ($backendName)." );
1376 }
1377
1378 if ( is_array( $source ) ) {
1379 $tmpFiles = $this->backend->getLocalReferenceMulti( array( 'srcs' => $source ) );
1380 foreach ( $tmpFiles as $path => $tmpFile ) {
1381 $this->assertNotNull( $tmpFile,
1382 "Creation of local copy of $path succeeded ($backendName)." );
1383 $contents = file_get_contents( $tmpFile->getPath() );
1384 $this->assertNotEquals( false, $contents, "Local ref of $path exists ($backendName)." );
1385 $this->assertEquals(
1386 current( $content ),
1387 $contents,
1388 "Local ref of $path is correct ($backendName)."
1389 );
1390 next( $content );
1391 }
1392 $this->assertEquals(
1393 $source,
1394 array_keys( $tmpFiles ),
1395 "Local refs in right order ($backendName)."
1396 );
1397 $this->assertEquals(
1398 count( $source ),
1399 count( $tmpFiles ),
1400 "Local refs array size correct ($backendName)."
1401 );
1402 } else {
1403 $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
1404 $this->assertNotNull( $tmpFile,
1405 "Creation of local copy of $source succeeded ($backendName)." );
1406 $contents = file_get_contents( $tmpFile->getPath() );
1407 $this->assertNotEquals( false, $contents, "Local ref of $source exists ($backendName)." );
1408 $this->assertEquals( $content[0], $contents, "Local ref of $source is correct ($backendName)." );
1409 }
1410 }
1411
1412 public static function provider_testGetLocalReference() {
1413 $cases = array();
1414
1415 $base = self::baseStorePath();
1416 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1417 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1418 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1419 $cases[] = array(
1420 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1421 "$base/unittest-cont1/e/a/z.txt" ),
1422 array( "contents xx 1111", "contents xy %", "contents xz $" )
1423 );
1424
1425 return $cases;
1426 }
1427
1428 /**
1429 * @covers FileBackend::getLocalCopy
1430 * @covers FileBackend::getLocalReference
1431 */
1432 public function testGetLocalCopyAndReference404() {
1433 $this->backend = $this->singleBackend;
1434 $this->tearDownFiles();
1435 $this->doTestGetLocalCopyAndReference404();
1436 $this->tearDownFiles();
1437
1438 $this->backend = $this->multiBackend;
1439 $this->tearDownFiles();
1440 $this->doTestGetLocalCopyAndReference404();
1441 $this->tearDownFiles();
1442 }
1443
1444 public function doTestGetLocalCopyAndReference404() {
1445 $backendName = $this->backendClass();
1446
1447 $base = self::baseStorePath();
1448
1449 $tmpFile = $this->backend->getLocalCopy( array(
1450 'src' => "$base/unittest-cont1/not-there" ) );
1451 $this->assertEquals( null, $tmpFile, "Local copy of not existing file is null ($backendName)." );
1452
1453 $tmpFile = $this->backend->getLocalReference( array(
1454 'src' => "$base/unittest-cont1/not-there" ) );
1455 $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
1456 }
1457
1458 /**
1459 * @dataProvider provider_testGetFileHttpUrl
1460 * @covers FileBackend::getFileHttpUrl
1461 */
1462 public function testGetFileHttpUrl( $source, $content ) {
1463 $this->backend = $this->singleBackend;
1464 $this->tearDownFiles();
1465 $this->doTestGetFileHttpUrl( $source, $content );
1466 $this->tearDownFiles();
1467
1468 $this->backend = $this->multiBackend;
1469 $this->tearDownFiles();
1470 $this->doTestGetFileHttpUrl( $source, $content );
1471 $this->tearDownFiles();
1472 }
1473
1474 private function doTestGetFileHttpUrl( $source, $content ) {
1475 $backendName = $this->backendClass();
1476
1477 $this->prepare( array( 'dir' => dirname( $source ) ) );
1478 $status = $this->backend->doOperation(
1479 array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
1480 $this->assertGoodStatus( $status,
1481 "Creation of file at $source succeeded ($backendName)." );
1482
1483 $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) );
1484
1485 if ( $url !== null ) { // supported
1486 $data = Http::request( "GET", $url, array(), __METHOD__ );
1487 $this->assertEquals( $content, $data,
1488 "HTTP GET of URL has right contents ($backendName)." );
1489 }
1490 }
1491
1492 public static function provider_testGetFileHttpUrl() {
1493 $cases = array();
1494
1495 $base = self::baseStorePath();
1496 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1497 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1498 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1499
1500 return $cases;
1501 }
1502
1503 /**
1504 * @dataProvider provider_testPrepareAndClean
1505 * @covers FileBackend::prepare
1506 * @covers FileBackend::clean
1507 */
1508 public function testPrepareAndClean( $path, $isOK ) {
1509 $this->backend = $this->singleBackend;
1510 $this->doTestPrepareAndClean( $path, $isOK );
1511 $this->tearDownFiles();
1512
1513 $this->backend = $this->multiBackend;
1514 $this->doTestPrepareAndClean( $path, $isOK );
1515 $this->tearDownFiles();
1516 }
1517
1518 public static function provider_testPrepareAndClean() {
1519 $base = self::baseStorePath();
1520
1521 return array(
1522 array( "$base/unittest-cont1/e/a/z/some_file1.txt", true ),
1523 array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
1524 # Specific to FS backend with no basePath field set
1525 # array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
1526 );
1527 }
1528
1529 private function doTestPrepareAndClean( $path, $isOK ) {
1530 $backendName = $this->backendClass();
1531
1532 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
1533 if ( $isOK ) {
1534 $this->assertGoodStatus( $status,
1535 "Preparing dir $path succeeded without warnings ($backendName)." );
1536 $this->assertEquals( true, $status->isOK(),
1537 "Preparing dir $path succeeded ($backendName)." );
1538 } else {
1539 $this->assertEquals( false, $status->isOK(),
1540 "Preparing dir $path failed ($backendName)." );
1541 }
1542
1543 $status = $this->backend->secure( array( 'dir' => dirname( $path ) ) );
1544 if ( $isOK ) {
1545 $this->assertGoodStatus( $status,
1546 "Securing dir $path succeeded without warnings ($backendName)." );
1547 $this->assertEquals( true, $status->isOK(),
1548 "Securing dir $path succeeded ($backendName)." );
1549 } else {
1550 $this->assertEquals( false, $status->isOK(),
1551 "Securing dir $path failed ($backendName)." );
1552 }
1553
1554 $status = $this->backend->publish( array( 'dir' => dirname( $path ) ) );
1555 if ( $isOK ) {
1556 $this->assertGoodStatus( $status,
1557 "Publishing dir $path succeeded without warnings ($backendName)." );
1558 $this->assertEquals( true, $status->isOK(),
1559 "Publishing dir $path succeeded ($backendName)." );
1560 } else {
1561 $this->assertEquals( false, $status->isOK(),
1562 "Publishing dir $path failed ($backendName)." );
1563 }
1564
1565 $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
1566 if ( $isOK ) {
1567 $this->assertGoodStatus( $status,
1568 "Cleaning dir $path succeeded without warnings ($backendName)." );
1569 $this->assertEquals( true, $status->isOK(),
1570 "Cleaning dir $path succeeded ($backendName)." );
1571 } else {
1572 $this->assertEquals( false, $status->isOK(),
1573 "Cleaning dir $path failed ($backendName)." );
1574 }
1575 }
1576
1577 public function testRecursiveClean() {
1578 $this->backend = $this->singleBackend;
1579 $this->doTestRecursiveClean();
1580 $this->tearDownFiles();
1581
1582 $this->backend = $this->multiBackend;
1583 $this->doTestRecursiveClean();
1584 $this->tearDownFiles();
1585 }
1586
1587 /**
1588 * @covers FileBackend::clean
1589 */
1590 private function doTestRecursiveClean() {
1591 $backendName = $this->backendClass();
1592
1593 $base = self::baseStorePath();
1594 $dirs = array(
1595 "$base/unittest-cont1",
1596 "$base/unittest-cont1/e",
1597 "$base/unittest-cont1/e/a",
1598 "$base/unittest-cont1/e/a/b",
1599 "$base/unittest-cont1/e/a/b/c",
1600 "$base/unittest-cont1/e/a/b/c/d0",
1601 "$base/unittest-cont1/e/a/b/c/d1",
1602 "$base/unittest-cont1/e/a/b/c/d2",
1603 "$base/unittest-cont1/e/a/b/c/d0/1",
1604 "$base/unittest-cont1/e/a/b/c/d0/2",
1605 "$base/unittest-cont1/e/a/b/c/d1/3",
1606 "$base/unittest-cont1/e/a/b/c/d1/4",
1607 "$base/unittest-cont1/e/a/b/c/d2/5",
1608 "$base/unittest-cont1/e/a/b/c/d2/6"
1609 );
1610 foreach ( $dirs as $dir ) {
1611 $status = $this->prepare( array( 'dir' => $dir ) );
1612 $this->assertGoodStatus( $status,
1613 "Preparing dir $dir succeeded without warnings ($backendName)." );
1614 }
1615
1616 if ( $this->backend instanceof FSFileBackend ) {
1617 foreach ( $dirs as $dir ) {
1618 $this->assertEquals( true, $this->backend->directoryExists( array( 'dir' => $dir ) ),
1619 "Dir $dir exists ($backendName)." );
1620 }
1621 }
1622
1623 $status = $this->backend->clean(
1624 array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
1625 $this->assertGoodStatus( $status,
1626 "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
1627
1628 foreach ( $dirs as $dir ) {
1629 $this->assertEquals( false, $this->backend->directoryExists( array( 'dir' => $dir ) ),
1630 "Dir $dir no longer exists ($backendName)." );
1631 }
1632 }
1633
1634 /**
1635 * @covers FileBackend::doOperations
1636 */
1637 public function testDoOperations() {
1638 $this->backend = $this->singleBackend;
1639 $this->tearDownFiles();
1640 $this->doTestDoOperations();
1641 $this->tearDownFiles();
1642
1643 $this->backend = $this->multiBackend;
1644 $this->tearDownFiles();
1645 $this->doTestDoOperations();
1646 $this->tearDownFiles();
1647 }
1648
1649 private function doTestDoOperations() {
1650 $base = self::baseStorePath();
1651
1652 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1653 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1654 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1655 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1656 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1657 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1658 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1659
1660 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1661 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1662 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1663 $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
1664 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1665 $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
1666 $this->prepare( array( 'dir' => dirname( $fileD ) ) );
1667
1668 $status = $this->backend->doOperations( array(
1669 array( 'op' => 'describe', 'src' => $fileA,
1670 'headers' => array( 'X-Content-Length' => '91.3' ), 'disposition' => 'inline' ),
1671 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1672 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1673 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1674 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1675 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
1676 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1677 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
1678 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1679 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
1680 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1681 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
1682 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1683 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
1684 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1685 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
1686 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1687 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1688 // Does nothing
1689 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1690 // Does nothing
1691 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1692 // Does nothing
1693 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1694 // Does nothing
1695 array( 'op' => 'null' ),
1696 // Does nothing
1697 ) );
1698
1699 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1700 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1701 $this->assertEquals( 14, count( $status->success ),
1702 "Operation batch has correct success array" );
1703
1704 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
1705 "File does not exist at $fileA" );
1706 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1707 "File does not exist at $fileB" );
1708 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1709 "File does not exist at $fileD" );
1710
1711 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1712 "File exists at $fileC" );
1713 $this->assertEquals( $fileBContents,
1714 $this->backend->getFileContents( array( 'src' => $fileC ) ),
1715 "Correct file contents of $fileC" );
1716 $this->assertEquals( strlen( $fileBContents ),
1717 $this->backend->getFileSize( array( 'src' => $fileC ) ),
1718 "Correct file size of $fileC" );
1719 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1720 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
1721 "Correct file SHA-1 of $fileC" );
1722 }
1723
1724 /**
1725 * @covers FileBackend::doOperations
1726 */
1727 public function testDoOperationsPipeline() {
1728 $this->backend = $this->singleBackend;
1729 $this->tearDownFiles();
1730 $this->doTestDoOperationsPipeline();
1731 $this->tearDownFiles();
1732
1733 $this->backend = $this->multiBackend;
1734 $this->tearDownFiles();
1735 $this->doTestDoOperationsPipeline();
1736 $this->tearDownFiles();
1737 }
1738
1739 // concurrency orientated
1740 private function doTestDoOperationsPipeline() {
1741 $base = self::baseStorePath();
1742
1743 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1744 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1745 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1746
1747 $tmpNameA = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1748 $tmpNameB = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1749 $tmpNameC = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1750 $this->addTmpFiles( array( $tmpNameA, $tmpNameB, $tmpNameC ) );
1751 file_put_contents( $tmpNameA, $fileAContents );
1752 file_put_contents( $tmpNameB, $fileBContents );
1753 file_put_contents( $tmpNameC, $fileCContents );
1754
1755 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1756 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1757 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1758 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1759
1760 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1761 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1762 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1763 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1764 $this->prepare( array( 'dir' => dirname( $fileD ) ) );
1765
1766 $status = $this->backend->doOperations( array(
1767 array( 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ),
1768 array( 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ),
1769 array( 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ),
1770 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1771 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1772 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1773 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1774 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
1775 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1776 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
1777 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1778 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
1779 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1780 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
1781 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1782 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
1783 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1784 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
1785 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1786 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1787 // Does nothing
1788 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1789 // Does nothing
1790 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1791 // Does nothing
1792 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1793 // Does nothing
1794 array( 'op' => 'null' ),
1795 // Does nothing
1796 ) );
1797
1798 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1799 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1800 $this->assertEquals( 16, count( $status->success ),
1801 "Operation batch has correct success array" );
1802
1803 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
1804 "File does not exist at $fileA" );
1805 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1806 "File does not exist at $fileB" );
1807 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1808 "File does not exist at $fileD" );
1809
1810 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1811 "File exists at $fileC" );
1812 $this->assertEquals( $fileBContents,
1813 $this->backend->getFileContents( array( 'src' => $fileC ) ),
1814 "Correct file contents of $fileC" );
1815 $this->assertEquals( strlen( $fileBContents ),
1816 $this->backend->getFileSize( array( 'src' => $fileC ) ),
1817 "Correct file size of $fileC" );
1818 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1819 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
1820 "Correct file SHA-1 of $fileC" );
1821 }
1822
1823 /**
1824 * @covers FileBackend::doOperations
1825 */
1826 public function testDoOperationsFailing() {
1827 $this->backend = $this->singleBackend;
1828 $this->tearDownFiles();
1829 $this->doTestDoOperationsFailing();
1830 $this->tearDownFiles();
1831
1832 $this->backend = $this->multiBackend;
1833 $this->tearDownFiles();
1834 $this->doTestDoOperationsFailing();
1835 $this->tearDownFiles();
1836 }
1837
1838 private function doTestDoOperationsFailing() {
1839 $base = self::baseStorePath();
1840
1841 $fileA = "$base/unittest-cont2/a/b/fileA.txt";
1842 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1843 $fileB = "$base/unittest-cont2/a/b/fileB.txt";
1844 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1845 $fileC = "$base/unittest-cont2/a/b/fileC.txt";
1846 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1847 $fileD = "$base/unittest-cont2/a/b/fileD.txt";
1848
1849 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1850 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1851 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1852 $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
1853 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1854 $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
1855
1856 $status = $this->backend->doOperations( array(
1857 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1858 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1859 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1860 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1861 array( 'op' => 'copy', 'src' => $fileB, 'dst' => $fileD, 'overwrite' => 1 ),
1862 // Now: A:<A>, B:<B>, C:<A>, D:<B>
1863 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD ),
1864 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1865 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC, 'overwriteSame' => 1 ),
1866 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1867 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileA, 'overwrite' => 1 ),
1868 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1869 array( 'op' => 'delete', 'src' => $fileD ),
1870 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1871 array( 'op' => 'null' ),
1872 // Does nothing
1873 ), array( 'force' => 1 ) );
1874
1875 $this->assertNotEquals( array(), $status->errors, "Operation had warnings" );
1876 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1877 $this->assertEquals( 8, count( $status->success ),
1878 "Operation batch has correct success array" );
1879
1880 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1881 "File does not exist at $fileB" );
1882 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1883 "File does not exist at $fileD" );
1884
1885 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileA ) ),
1886 "File does not exist at $fileA" );
1887 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1888 "File exists at $fileC" );
1889 $this->assertEquals( $fileBContents,
1890 $this->backend->getFileContents( array( 'src' => $fileA ) ),
1891 "Correct file contents of $fileA" );
1892 $this->assertEquals( strlen( $fileBContents ),
1893 $this->backend->getFileSize( array( 'src' => $fileA ) ),
1894 "Correct file size of $fileA" );
1895 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1896 $this->backend->getFileSha1Base36( array( 'src' => $fileA ) ),
1897 "Correct file SHA-1 of $fileA" );
1898 }
1899
1900 /**
1901 * @covers FileBackend::getFileList
1902 */
1903 public function testGetFileList() {
1904 $this->backend = $this->singleBackend;
1905 $this->tearDownFiles();
1906 $this->doTestGetFileList();
1907 $this->tearDownFiles();
1908
1909 $this->backend = $this->multiBackend;
1910 $this->tearDownFiles();
1911 $this->doTestGetFileList();
1912 $this->tearDownFiles();
1913 }
1914
1915 private function doTestGetFileList() {
1916 $backendName = $this->backendClass();
1917 $base = self::baseStorePath();
1918
1919 // Should have no errors
1920 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
1921
1922 $files = array(
1923 "$base/unittest-cont1/e/test1.txt",
1924 "$base/unittest-cont1/e/test2.txt",
1925 "$base/unittest-cont1/e/test3.txt",
1926 "$base/unittest-cont1/e/subdir1/test1.txt",
1927 "$base/unittest-cont1/e/subdir1/test2.txt",
1928 "$base/unittest-cont1/e/subdir2/test3.txt",
1929 "$base/unittest-cont1/e/subdir2/test4.txt",
1930 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
1931 "$base/unittest-cont1/e/subdir2/subdir/test2.txt",
1932 "$base/unittest-cont1/e/subdir2/subdir/test3.txt",
1933 "$base/unittest-cont1/e/subdir2/subdir/test4.txt",
1934 "$base/unittest-cont1/e/subdir2/subdir/test5.txt",
1935 "$base/unittest-cont1/e/subdir2/subdir/sub/test0.txt",
1936 "$base/unittest-cont1/e/subdir2/subdir/sub/120-px-file.txt",
1937 );
1938
1939 // Add the files
1940 $ops = array();
1941 foreach ( $files as $file ) {
1942 $this->prepare( array( 'dir' => dirname( $file ) ) );
1943 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
1944 }
1945 $status = $this->backend->doQuickOperations( $ops );
1946 $this->assertGoodStatus( $status,
1947 "Creation of files succeeded ($backendName)." );
1948 $this->assertEquals( true, $status->isOK(),
1949 "Creation of files succeeded with OK status ($backendName)." );
1950
1951 // Expected listing at root
1952 $expected = array(
1953 "e/test1.txt",
1954 "e/test2.txt",
1955 "e/test3.txt",
1956 "e/subdir1/test1.txt",
1957 "e/subdir1/test2.txt",
1958 "e/subdir2/test3.txt",
1959 "e/subdir2/test4.txt",
1960 "e/subdir2/subdir/test1.txt",
1961 "e/subdir2/subdir/test2.txt",
1962 "e/subdir2/subdir/test3.txt",
1963 "e/subdir2/subdir/test4.txt",
1964 "e/subdir2/subdir/test5.txt",
1965 "e/subdir2/subdir/sub/test0.txt",
1966 "e/subdir2/subdir/sub/120-px-file.txt",
1967 );
1968 sort( $expected );
1969
1970 // Actual listing (no trailing slash) at root
1971 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1" ) );
1972 $list = $this->listToArray( $iter );
1973 sort( $list );
1974 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1975
1976 // Actual listing (no trailing slash) at root with advise
1977 $iter = $this->backend->getFileList( array(
1978 'dir' => "$base/unittest-cont1",
1979 'adviseStat' => 1
1980 ) );
1981 $list = $this->listToArray( $iter );
1982 sort( $list );
1983 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1984
1985 // Actual listing (with trailing slash) at root
1986 $list = array();
1987 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/" ) );
1988 foreach ( $iter as $file ) {
1989 $list[] = $file;
1990 }
1991 sort( $list );
1992 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1993
1994 // Expected listing at subdir
1995 $expected = array(
1996 "test1.txt",
1997 "test2.txt",
1998 "test3.txt",
1999 "test4.txt",
2000 "test5.txt",
2001 "sub/test0.txt",
2002 "sub/120-px-file.txt",
2003 );
2004 sort( $expected );
2005
2006 // Actual listing (no trailing slash) at subdir
2007 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
2008 $list = $this->listToArray( $iter );
2009 sort( $list );
2010 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2011
2012 // Actual listing (no trailing slash) at subdir with advise
2013 $iter = $this->backend->getFileList( array(
2014 'dir' => "$base/unittest-cont1/e/subdir2/subdir",
2015 'adviseStat' => 1
2016 ) );
2017 $list = $this->listToArray( $iter );
2018 sort( $list );
2019 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2020
2021 // Actual listing (with trailing slash) at subdir
2022 $list = array();
2023 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir/" ) );
2024 foreach ( $iter as $file ) {
2025 $list[] = $file;
2026 }
2027 sort( $list );
2028 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
2029
2030 // Actual listing (using iterator second time)
2031 $list = $this->listToArray( $iter );
2032 sort( $list );
2033 $this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
2034
2035 // Actual listing (top files only) at root
2036 $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1" ) );
2037 $list = $this->listToArray( $iter );
2038 sort( $list );
2039 $this->assertEquals( array(), $list, "Correct top file listing ($backendName)." );
2040
2041 // Expected listing (top files only) at subdir
2042 $expected = array(
2043 "test1.txt",
2044 "test2.txt",
2045 "test3.txt",
2046 "test4.txt",
2047 "test5.txt"
2048 );
2049 sort( $expected );
2050
2051 // Actual listing (top files only) at subdir
2052 $iter = $this->backend->getTopFileList(
2053 array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" )
2054 );
2055 $list = $this->listToArray( $iter );
2056 sort( $list );
2057 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2058
2059 // Actual listing (top files only) at subdir with advise
2060 $iter = $this->backend->getTopFileList( array(
2061 'dir' => "$base/unittest-cont1/e/subdir2/subdir",
2062 'adviseStat' => 1
2063 ) );
2064 $list = $this->listToArray( $iter );
2065 sort( $list );
2066 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2067
2068 foreach ( $files as $file ) { // clean up
2069 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
2070 }
2071
2072 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
2073 foreach ( $iter as $iter ) {
2074 // no errors
2075 }
2076 }
2077
2078 /**
2079 * @covers FileBackend::getTopDirectoryList
2080 * @covers FileBackend::getDirectoryList
2081 */
2082 public function testGetDirectoryList() {
2083 $this->backend = $this->singleBackend;
2084 $this->tearDownFiles();
2085 $this->doTestGetDirectoryList();
2086 $this->tearDownFiles();
2087
2088 $this->backend = $this->multiBackend;
2089 $this->tearDownFiles();
2090 $this->doTestGetDirectoryList();
2091 $this->tearDownFiles();
2092 }
2093
2094 private function doTestGetDirectoryList() {
2095 $backendName = $this->backendClass();
2096
2097 $base = self::baseStorePath();
2098 $files = array(
2099 "$base/unittest-cont1/e/test1.txt",
2100 "$base/unittest-cont1/e/test2.txt",
2101 "$base/unittest-cont1/e/test3.txt",
2102 "$base/unittest-cont1/e/subdir1/test1.txt",
2103 "$base/unittest-cont1/e/subdir1/test2.txt",
2104 "$base/unittest-cont1/e/subdir2/test3.txt",
2105 "$base/unittest-cont1/e/subdir2/test4.txt",
2106 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
2107 "$base/unittest-cont1/e/subdir3/subdir/test2.txt",
2108 "$base/unittest-cont1/e/subdir4/subdir/test3.txt",
2109 "$base/unittest-cont1/e/subdir4/subdir/test4.txt",
2110 "$base/unittest-cont1/e/subdir4/subdir/test5.txt",
2111 "$base/unittest-cont1/e/subdir4/subdir/sub/test0.txt",
2112 "$base/unittest-cont1/e/subdir4/subdir/sub/120-px-file.txt",
2113 );
2114
2115 // Add the files
2116 $ops = array();
2117 foreach ( $files as $file ) {
2118 $this->prepare( array( 'dir' => dirname( $file ) ) );
2119 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
2120 }
2121 $status = $this->backend->doQuickOperations( $ops );
2122 $this->assertGoodStatus( $status,
2123 "Creation of files succeeded ($backendName)." );
2124 $this->assertEquals( true, $status->isOK(),
2125 "Creation of files succeeded with OK status ($backendName)." );
2126
2127 $this->assertEquals( true,
2128 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) ),
2129 "Directory exists in ($backendName)." );
2130 $this->assertEquals( true,
2131 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) ),
2132 "Directory exists in ($backendName)." );
2133 $this->assertEquals( false,
2134 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/test1.txt" ) ),
2135 "Directory does not exists in ($backendName)." );
2136
2137 // Expected listing
2138 $expected = array(
2139 "e",
2140 );
2141 sort( $expected );
2142
2143 // Actual listing (no trailing slash)
2144 $list = array();
2145 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1" ) );
2146 foreach ( $iter as $file ) {
2147 $list[] = $file;
2148 }
2149 sort( $list );
2150
2151 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2152
2153 // Expected listing
2154 $expected = array(
2155 "subdir1",
2156 "subdir2",
2157 "subdir3",
2158 "subdir4",
2159 );
2160 sort( $expected );
2161
2162 // Actual listing (no trailing slash)
2163 $list = array();
2164 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e" ) );
2165 foreach ( $iter as $file ) {
2166 $list[] = $file;
2167 }
2168 sort( $list );
2169
2170 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2171
2172 // Actual listing (with trailing slash)
2173 $list = array();
2174 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/" ) );
2175 foreach ( $iter as $file ) {
2176 $list[] = $file;
2177 }
2178 sort( $list );
2179
2180 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2181
2182 // Expected listing
2183 $expected = array(
2184 "subdir",
2185 );
2186 sort( $expected );
2187
2188 // Actual listing (no trailing slash)
2189 $list = array();
2190 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2" ) );
2191 foreach ( $iter as $file ) {
2192 $list[] = $file;
2193 }
2194 sort( $list );
2195
2196 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2197
2198 // Actual listing (with trailing slash)
2199 $list = array();
2200 $iter = $this->backend->getTopDirectoryList(
2201 array( 'dir' => "$base/unittest-cont1/e/subdir2/" )
2202 );
2203
2204 foreach ( $iter as $file ) {
2205 $list[] = $file;
2206 }
2207 sort( $list );
2208
2209 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2210
2211 // Actual listing (using iterator second time)
2212 $list = array();
2213 foreach ( $iter as $file ) {
2214 $list[] = $file;
2215 }
2216 sort( $list );
2217
2218 $this->assertEquals(
2219 $expected,
2220 $list,
2221 "Correct top dir listing ($backendName), second iteration."
2222 );
2223
2224 // Expected listing (recursive)
2225 $expected = array(
2226 "e",
2227 "e/subdir1",
2228 "e/subdir2",
2229 "e/subdir3",
2230 "e/subdir4",
2231 "e/subdir2/subdir",
2232 "e/subdir3/subdir",
2233 "e/subdir4/subdir",
2234 "e/subdir4/subdir/sub",
2235 );
2236 sort( $expected );
2237
2238 // Actual listing (recursive)
2239 $list = array();
2240 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/" ) );
2241 foreach ( $iter as $file ) {
2242 $list[] = $file;
2243 }
2244 sort( $list );
2245
2246 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2247
2248 // Expected listing (recursive)
2249 $expected = array(
2250 "subdir",
2251 "subdir/sub",
2252 );
2253 sort( $expected );
2254
2255 // Actual listing (recursive)
2256 $list = array();
2257 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir4" ) );
2258 foreach ( $iter as $file ) {
2259 $list[] = $file;
2260 }
2261 sort( $list );
2262
2263 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2264
2265 // Actual listing (recursive, second time)
2266 $list = array();
2267 foreach ( $iter as $file ) {
2268 $list[] = $file;
2269 }
2270 sort( $list );
2271
2272 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2273
2274 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) );
2275 $items = $this->listToArray( $iter );
2276 $this->assertEquals( array(), $items, "Directory listing is empty." );
2277
2278 foreach ( $files as $file ) { // clean up
2279 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
2280 }
2281
2282 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
2283 foreach ( $iter as $file ) {
2284 // no errors
2285 }
2286
2287 $items = $this->listToArray( $iter );
2288 $this->assertEquals( array(), $items, "Directory listing is empty." );
2289
2290 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/not/exists" ) );
2291 $items = $this->listToArray( $iter );
2292 $this->assertEquals( array(), $items, "Directory listing is empty." );
2293 }
2294
2295 /**
2296 * @covers FileBackend::lockFiles
2297 * @covers FileBackend::unlockFiles
2298 */
2299 public function testLockCalls() {
2300 $this->backend = $this->singleBackend;
2301 $this->doTestLockCalls();
2302 }
2303
2304 private function doTestLockCalls() {
2305 $backendName = $this->backendClass();
2306
2307 $paths = array(
2308 "test1.txt",
2309 "test2.txt",
2310 "test3.txt",
2311 "subdir1",
2312 "subdir1", // duplicate
2313 "subdir1/test1.txt",
2314 "subdir1/test2.txt",
2315 "subdir2",
2316 "subdir2", // duplicate
2317 "subdir2/test3.txt",
2318 "subdir2/test4.txt",
2319 "subdir2/subdir",
2320 "subdir2/subdir/test1.txt",
2321 "subdir2/subdir/test2.txt",
2322 "subdir2/subdir/test3.txt",
2323 "subdir2/subdir/test4.txt",
2324 "subdir2/subdir/test5.txt",
2325 "subdir2/subdir/sub",
2326 "subdir2/subdir/sub/test0.txt",
2327 "subdir2/subdir/sub/120-px-file.txt",
2328 );
2329
2330 for ( $i = 0; $i < 25; $i++ ) {
2331 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
2332 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2333 "Locking of files succeeded ($backendName) ($i)." );
2334 $this->assertEquals( true, $status->isOK(),
2335 "Locking of files succeeded with OK status ($backendName) ($i)." );
2336
2337 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
2338 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2339 "Locking of files succeeded ($backendName) ($i)." );
2340 $this->assertEquals( true, $status->isOK(),
2341 "Locking of files succeeded with OK status ($backendName) ($i)." );
2342
2343 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
2344 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2345 "Locking of files succeeded ($backendName) ($i)." );
2346 $this->assertEquals( true, $status->isOK(),
2347 "Locking of files succeeded with OK status ($backendName) ($i)." );
2348
2349 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
2350 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2351 "Locking of files succeeded ($backendName). ($i)" );
2352 $this->assertEquals( true, $status->isOK(),
2353 "Locking of files succeeded with OK status ($backendName) ($i)." );
2354
2355 # # Flip the acquire/release ordering around ##
2356
2357 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
2358 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2359 "Locking of files succeeded ($backendName) ($i)." );
2360 $this->assertEquals( true, $status->isOK(),
2361 "Locking of files succeeded with OK status ($backendName) ($i)." );
2362
2363 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
2364 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2365 "Locking of files succeeded ($backendName) ($i)." );
2366 $this->assertEquals( true, $status->isOK(),
2367 "Locking of files succeeded with OK status ($backendName) ($i)." );
2368
2369 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
2370 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2371 "Locking of files succeeded ($backendName). ($i)" );
2372 $this->assertEquals( true, $status->isOK(),
2373 "Locking of files succeeded with OK status ($backendName) ($i)." );
2374
2375 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
2376 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2377 "Locking of files succeeded ($backendName) ($i)." );
2378 $this->assertEquals( true, $status->isOK(),
2379 "Locking of files succeeded with OK status ($backendName) ($i)." );
2380 }
2381
2382 $status = Status::newGood();
2383 $sl = $this->backend->getScopedFileLocks( $paths, LockManager::LOCK_EX, $status );
2384 $this->assertInstanceOf( 'ScopedLock', $sl,
2385 "Scoped locking of files succeeded ($backendName)." );
2386 $this->assertEquals( array(), $status->errors,
2387 "Scoped locking of files succeeded ($backendName)." );
2388 $this->assertEquals( true, $status->isOK(),
2389 "Scoped locking of files succeeded with OK status ($backendName)." );
2390
2391 ScopedLock::release( $sl );
2392 $this->assertEquals( null, $sl,
2393 "Scoped unlocking of files succeeded ($backendName)." );
2394 $this->assertEquals( array(), $status->errors,
2395 "Scoped unlocking of files succeeded ($backendName)." );
2396 $this->assertEquals( true, $status->isOK(),
2397 "Scoped unlocking of files succeeded with OK status ($backendName)." );
2398 }
2399
2400 /**
2401 * @dataProvider provider_testGetContentType
2402 */
2403 public function testGetContentType( $mimeCallback, $mimeFromString ) {
2404 global $IP;
2405
2406 $be = TestingAccessWrapper::newFromObject( new MemoryFileBackend(
2407 array(
2408 'name' => 'testing',
2409 'class' => 'MemoryFileBackend',
2410 'wikiId' => 'meow',
2411 'mimeCallback' => $mimeCallback
2412 )
2413 ) );
2414
2415 $dst = 'mwstore://testing/container/path/to/file_no_ext';
2416 $src = "$IP/tests/phpunit/data/media/srgb.jpg";
2417 $this->assertEquals( 'image/jpeg', $be->getContentType( $dst, null, $src ) );
2418 $this->assertEquals(
2419 $mimeFromString ? 'image/jpeg' : 'unknown/unknown',
2420 $be->getContentType( $dst, file_get_contents( $src ), null ) );
2421
2422 $src = "$IP/tests/phpunit/data/media/Png-native-test.png";
2423 $this->assertEquals( 'image/png', $be->getContentType( $dst, null, $src ) );
2424 $this->assertEquals(
2425 $mimeFromString ? 'image/png' : 'unknown/unknown',
2426 $be->getContentType( $dst, file_get_contents( $src ), null ) );
2427 }
2428
2429 public static function provider_testGetContentType() {
2430 return array(
2431 array( null, false ),
2432 array( array( FileBackendGroup::singleton(), 'guessMimeInternal' ), true )
2433 );
2434 }
2435
2436 public function testReadAffinity() {
2437 $be = TestingAccessWrapper::newFromObject(
2438 new FileBackendMultiWrite( array(
2439 'name' => 'localtesting',
2440 'wikiId' => wfWikiId() . mt_rand(),
2441 'backends' => array(
2442 array( // backend 0
2443 'name' => 'multitesting0',
2444 'class' => 'MemoryFileBackend',
2445 'isMultiMaster' => false,
2446 'readAffinity' => true
2447 ),
2448 array( // backend 1
2449 'name' => 'multitesting1',
2450 'class' => 'MemoryFileBackend',
2451 'isMultiMaster' => true
2452 )
2453 )
2454 ) )
2455 );
2456
2457 $this->assertEquals(
2458 1,
2459 $be->getReadIndexFromParams( array( 'latest' => 1 ) ),
2460 'Reads with "latest" flag use backend 1'
2461 );
2462 $this->assertEquals(
2463 0,
2464 $be->getReadIndexFromParams( array( 'latest' => 0 ) ),
2465 'Reads without "latest" flag use backend 0'
2466 );
2467
2468 $p = 'container/test-cont/file.txt';
2469 $be->backends[0]->quickCreate( array(
2470 'dst' => "mwstore://multitesting0/$p", 'content' => 'cattitude' ) );
2471 $be->backends[1]->quickCreate( array(
2472 'dst' => "mwstore://multitesting1/$p", 'content' => 'princess of power' ) );
2473
2474 $this->assertEquals(
2475 'cattitude',
2476 $be->getFileContents( array( 'src' => "mwstore://localtesting/$p" ) ),
2477 "Non-latest read came from backend 0"
2478 );
2479 $this->assertEquals(
2480 'princess of power',
2481 $be->getFileContents( array( 'src' => "mwstore://localtesting/$p", 'latest' => 1 ) ),
2482 "Latest read came from backend1"
2483 );
2484 }
2485
2486 public function testAsyncWrites() {
2487 $be = TestingAccessWrapper::newFromObject(
2488 new FileBackendMultiWrite( array(
2489 'name' => 'localtesting',
2490 'wikiId' => wfWikiId() . mt_rand(),
2491 'backends' => array(
2492 array( // backend 0
2493 'name' => 'multitesting0',
2494 'class' => 'MemoryFileBackend',
2495 'isMultiMaster' => false
2496 ),
2497 array( // backend 1
2498 'name' => 'multitesting1',
2499 'class' => 'MemoryFileBackend',
2500 'isMultiMaster' => true
2501 )
2502 ),
2503 'replication' => 'async'
2504 ) )
2505 );
2506
2507 DeferredUpdates::forceDeferral( true );
2508
2509 $p = 'container/test-cont/file.txt';
2510 $be->quickCreate( array(
2511 'dst' => "mwstore://localtesting/$p", 'content' => 'cattitude' ) );
2512
2513 $this->assertEquals(
2514 false,
2515 $be->backends[0]->getFileContents( array( 'src' => "mwstore://multitesting0/$p" ) ),
2516 "File not yet written to backend 0"
2517 );
2518 $this->assertEquals(
2519 'cattitude',
2520 $be->backends[1]->getFileContents( array( 'src' => "mwstore://multitesting1/$p" ) ),
2521 "File already written to backend 1"
2522 );
2523
2524 DeferredUpdates::doUpdates();
2525 DeferredUpdates::forceDeferral( false );
2526
2527 $this->assertEquals(
2528 'cattitude',
2529 $be->backends[0]->getFileContents( array( 'src' => "mwstore://multitesting0/$p" ) ),
2530 "File now written to backend 0"
2531 );
2532 }
2533
2534 public function testSanitizeOpHeaders() {
2535 $be = TestingAccessWrapper::newFromObject( new MemoryFileBackend( array(
2536 'name' => 'localtesting',
2537 'wikiId' => wfWikiID()
2538 ) ) );
2539
2540 $name = wfRandomString( 300 );
2541
2542 $input = array(
2543 'headers' => array(
2544 'content-Disposition' => FileBackend::makeContentDisposition( 'inline', $name ),
2545 'Content-dUration' => 25.6,
2546 'X-LONG-VALUE' => str_pad( '0', 300 ),
2547 'CONTENT-LENGTH' => 855055,
2548 )
2549 );
2550 $expected = array(
2551 'headers' => array(
2552 'content-disposition' => FileBackend::makeContentDisposition( 'inline', $name ),
2553 'content-duration' => 25.6,
2554 'content-length' => 855055
2555 )
2556 );
2557
2558 MediaWiki\suppressWarnings();
2559 $actual = $be->sanitizeOpHeaders( $input );
2560 MediaWiki\restoreWarnings();
2561
2562 $this->assertEquals( $expected, $actual, "Header sanitized properly" );
2563 }
2564
2565 // helper function
2566 private function listToArray( $iter ) {
2567 return is_array( $iter ) ? $iter : iterator_to_array( $iter );
2568 }
2569
2570 // test helper wrapper for backend prepare() function
2571 private function prepare( array $params ) {
2572 return $this->backend->prepare( $params );
2573 }
2574
2575 // test helper wrapper for backend prepare() function
2576 private function create( array $params ) {
2577 $params['op'] = 'create';
2578
2579 return $this->backend->doQuickOperations( array( $params ) );
2580 }
2581
2582 function tearDownFiles() {
2583 $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont-bad' );
2584 foreach ( $containers as $container ) {
2585 $this->deleteFiles( $container );
2586 }
2587 }
2588
2589 private function deleteFiles( $container ) {
2590 $base = self::baseStorePath();
2591 $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
2592 if ( $iter ) {
2593 foreach ( $iter as $file ) {
2594 $this->backend->quickDelete( array( 'src' => "$base/$container/$file" ) );
2595 }
2596 // free the directory, to avoid Permission denied under windows on rmdir
2597 unset( $iter );
2598 }
2599 $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
2600 }
2601
2602 function assertBackendPathsConsistent( array $paths ) {
2603 if ( $this->backend instanceof FileBackendMultiWrite ) {
2604 $status = $this->backend->consistencyCheck( $paths );
2605 $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
2606 }
2607 }
2608
2609 function assertGoodStatus( $status, $msg ) {
2610 $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );
2611 }
2612 }