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