mediawiki.searchSuggest: Show full article title as a tooltip for each suggestion
[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 $attr = $this->backend->getFileXAttributes( array( 'src' => $source ) );
690 $this->assertHasHeaders( $op['headers'], $attr );
691 }
692 } else {
693 $this->assertEquals( false, $status->isOK(),
694 "Describe of file at $source failed ($backendName)." );
695 }
696
697 $this->assertBackendPathsConsistent( array( $source ) );
698 }
699
700 private function assertHasHeaders( array $headers, array $attr ) {
701 foreach ( $headers as $n => $v ) {
702 if ( $n !== '' ) {
703 $this->assertTrue( isset( $attr['headers'][strtolower( $n )] ),
704 "File has '$n' header." );
705 $this->assertEquals( $v, $attr['headers'][strtolower( $n )],
706 "File has '$n' header value." );
707 } else {
708 $this->assertFalse( isset( $attr['headers'][strtolower( $n )] ),
709 "File does not have '$n' header." );
710 }
711 }
712 }
713
714 public static function provider_testDescribe() {
715 $cases = array();
716
717 $source = self::baseStorePath() . '/unittest-cont1/e/myfacefile.txt';
718
719 $op = array( 'op' => 'describe', 'src' => $source,
720 'headers' => array( 'Content-Disposition' => 'inline' ), );
721 $cases[] = array(
722 $op, // operation
723 true, // with source
724 true // succeeds
725 );
726
727 $cases[] = array(
728 $op, // operation
729 false, // without source
730 false // fails
731 );
732
733 return $cases;
734 }
735
736 /**
737 * @dataProvider provider_testCreate
738 * @covers FileBackend::doOperation
739 */
740 public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
741 $this->backend = $this->singleBackend;
742 $this->tearDownFiles();
743 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
744 $this->tearDownFiles();
745
746 $this->backend = $this->multiBackend;
747 $this->tearDownFiles();
748 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
749 $this->tearDownFiles();
750 }
751
752 private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
753 $backendName = $this->backendClass();
754
755 $dest = $op['dst'];
756 $this->prepare( array( 'dir' => dirname( $dest ) ) );
757
758 $oldText = 'blah...blah...waahwaah';
759 if ( $alreadyExists ) {
760 $status = $this->backend->doOperation(
761 array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
762 $this->assertGoodStatus( $status,
763 "Creation of file at $dest succeeded ($backendName)." );
764 }
765
766 $status = $this->backend->doOperation( $op );
767 if ( $okStatus ) {
768 $this->assertGoodStatus( $status,
769 "Creation of file at $dest succeeded without warnings ($backendName)." );
770 $this->assertEquals( true, $status->isOK(),
771 "Creation of file at $dest succeeded ($backendName)." );
772 $this->assertEquals( array( 0 => true ), $status->success,
773 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
774 } else {
775 $this->assertEquals( false, $status->isOK(),
776 "Creation of file at $dest failed ($backendName)." );
777 }
778
779 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
780 "Destination file $dest exists after creation ($backendName)." );
781
782 $props1 = $this->backend->getFileProps( array( 'src' => $dest ) );
783 $this->assertEquals( true, $props1['fileExists'],
784 "Destination file $dest exists according to props ($backendName)." );
785 if ( $okStatus ) { // file content is what we saved
786 $this->assertEquals( $newSize, $props1['size'],
787 "Destination file $dest has expected size according to props ($backendName)." );
788 $this->assertEquals( $newSize,
789 $this->backend->getFileSize( array( 'src' => $dest ) ),
790 "Destination file $dest has correct size ($backendName)." );
791 } else { // file content is some other previous text
792 $this->assertEquals( strlen( $oldText ), $props1['size'],
793 "Destination file $dest has original size according to props ($backendName)." );
794 $this->assertEquals( strlen( $oldText ),
795 $this->backend->getFileSize( array( 'src' => $dest ) ),
796 "Destination file $dest has original size according to props ($backendName)." );
797 }
798
799 $this->assertBackendPathsConsistent( array( $dest ) );
800 }
801
802 /**
803 * @dataProvider provider_testCreate
804 */
805 public static function provider_testCreate() {
806 $cases = array();
807
808 $dest = self::baseStorePath() . '/unittest-cont2/a/myspacefile.txt';
809
810 $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
811 $cases[] = array(
812 $op, // operation
813 false, // no dest already exists
814 true, // succeeds
815 strlen( $op['content'] )
816 );
817
818 $op2 = $op;
819 $op2['content'] = "\n";
820 $cases[] = array(
821 $op2, // operation
822 false, // no dest already exists
823 true, // succeeds
824 strlen( $op2['content'] )
825 );
826
827 $op2 = $op;
828 $op2['content'] = "fsf\n waf 3kt";
829 $cases[] = array(
830 $op2, // operation
831 true, // dest already exists
832 false, // fails
833 strlen( $op2['content'] )
834 );
835
836 $op2 = $op;
837 $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
838 $op2['overwrite'] = true;
839 $cases[] = array(
840 $op2, // operation
841 true, // dest already exists
842 true, // succeeds
843 strlen( $op2['content'] )
844 );
845
846 $op2 = $op;
847 $op2['content'] = "39qjmg3-qg";
848 $op2['overwriteSame'] = true;
849 $cases[] = array(
850 $op2, // operation
851 true, // dest already exists
852 false, // succeeds
853 strlen( $op2['content'] )
854 );
855
856 return $cases;
857 }
858
859 /**
860 * @covers FileBackend::doQuickOperations
861 */
862 public function testDoQuickOperations() {
863 $this->backend = $this->singleBackend;
864 $this->doTestDoQuickOperations();
865 $this->tearDownFiles();
866
867 $this->backend = $this->multiBackend;
868 $this->doTestDoQuickOperations();
869 $this->tearDownFiles();
870 }
871
872 private function doTestDoQuickOperations() {
873 $backendName = $this->backendClass();
874
875 $base = self::baseStorePath();
876 $files = array(
877 "$base/unittest-cont1/e/fileA.a",
878 "$base/unittest-cont1/e/fileB.a",
879 "$base/unittest-cont1/e/fileC.a"
880 );
881 $createOps = array();
882 $purgeOps = array();
883 foreach ( $files as $path ) {
884 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
885 $this->assertGoodStatus( $status,
886 "Preparing $path succeeded without warnings ($backendName)." );
887 $createOps[] = array( 'op' => 'create', 'dst' => $path, 'content' => mt_rand( 0, 50000 ) );
888 $copyOps[] = array( 'op' => 'copy', 'src' => $path, 'dst' => "$path-2" );
889 $moveOps[] = array( 'op' => 'move', 'src' => "$path-2", 'dst' => "$path-3" );
890 $purgeOps[] = array( 'op' => 'delete', 'src' => $path );
891 $purgeOps[] = array( 'op' => 'delete', 'src' => "$path-3" );
892 }
893 $purgeOps[] = array( 'op' => 'null' );
894
895 $this->assertGoodStatus(
896 $this->backend->doQuickOperations( $createOps ),
897 "Creation of source files succeeded ($backendName)." );
898 foreach ( $files as $file ) {
899 $this->assertTrue( $this->backend->fileExists( array( 'src' => $file ) ),
900 "File $file exists." );
901 }
902
903 $this->assertGoodStatus(
904 $this->backend->doQuickOperations( $copyOps ),
905 "Quick copy of source files succeeded ($backendName)." );
906 foreach ( $files as $file ) {
907 $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
908 "File $file-2 exists." );
909 }
910
911 $this->assertGoodStatus(
912 $this->backend->doQuickOperations( $moveOps ),
913 "Quick move of source files succeeded ($backendName)." );
914 foreach ( $files as $file ) {
915 $this->assertTrue( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
916 "File $file-3 move in." );
917 $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-2" ) ),
918 "File $file-2 moved away." );
919 }
920
921 $this->assertGoodStatus(
922 $this->backend->quickCopy( array( 'src' => $files[0], 'dst' => $files[0] ) ),
923 "Copy of file {$files[0]} over itself succeeded ($backendName)." );
924 $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
925 "File {$files[0]} still exists." );
926
927 $this->assertGoodStatus(
928 $this->backend->quickMove( array( 'src' => $files[0], 'dst' => $files[0] ) ),
929 "Move of file {$files[0]} over itself succeeded ($backendName)." );
930 $this->assertTrue( $this->backend->fileExists( array( 'src' => $files[0] ) ),
931 "File {$files[0]} still exists." );
932
933 $this->assertGoodStatus(
934 $this->backend->doQuickOperations( $purgeOps ),
935 "Quick deletion of source files succeeded ($backendName)." );
936 foreach ( $files as $file ) {
937 $this->assertFalse( $this->backend->fileExists( array( 'src' => $file ) ),
938 "File $file purged." );
939 $this->assertFalse( $this->backend->fileExists( array( 'src' => "$file-3" ) ),
940 "File $file-3 purged." );
941 }
942 }
943
944 /**
945 * @dataProvider provider_testConcatenate
946 */
947 public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
948 $this->filesToPrune[] = $op['dst'];
949
950 $this->backend = $this->singleBackend;
951 $this->tearDownFiles();
952 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
953 $this->filesToPrune[] = $op['dst']; # avoid file leaking
954 $this->tearDownFiles();
955
956 $this->backend = $this->multiBackend;
957 $this->tearDownFiles();
958 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
959 $this->filesToPrune[] = $op['dst']; # avoid file leaking
960 $this->tearDownFiles();
961 }
962
963 private function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
964 $backendName = $this->backendClass();
965
966 $expContent = '';
967 // Create sources
968 $ops = array();
969 foreach ( $srcs as $i => $source ) {
970 $this->prepare( array( 'dir' => dirname( $source ) ) );
971 $ops[] = array(
972 'op' => 'create', // operation
973 'dst' => $source, // source
974 'content' => $srcsContent[$i]
975 );
976 $expContent .= $srcsContent[$i];
977 }
978 $status = $this->backend->doOperations( $ops );
979
980 $this->assertGoodStatus( $status,
981 "Creation of source files succeeded ($backendName)." );
982
983 $dest = $params['dst'];
984 if ( $alreadyExists ) {
985 $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
986 $this->assertEquals( true, $ok,
987 "Creation of file at $dest succeeded ($backendName)." );
988 } else {
989 $ok = file_put_contents( $dest, '' ) !== false;
990 $this->assertEquals( true, $ok,
991 "Creation of 0-byte file at $dest succeeded ($backendName)." );
992 }
993
994 // Combine the files into one
995 $status = $this->backend->concatenate( $params );
996 if ( $okStatus ) {
997 $this->assertGoodStatus( $status,
998 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
999 $this->assertEquals( true, $status->isOK(),
1000 "Creation of concat file at $dest succeeded ($backendName)." );
1001 } else {
1002 $this->assertEquals( false, $status->isOK(),
1003 "Creation of concat file at $dest failed ($backendName)." );
1004 }
1005
1006 if ( $okStatus ) {
1007 $this->assertEquals( true, is_file( $dest ),
1008 "Dest concat file $dest exists after creation ($backendName)." );
1009 } else {
1010 $this->assertEquals( true, is_file( $dest ),
1011 "Dest concat file $dest exists after failed creation ($backendName)." );
1012 }
1013
1014 $contents = file_get_contents( $dest );
1015 $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
1016
1017 if ( $okStatus ) {
1018 $this->assertEquals( $expContent, $contents,
1019 "Concat file at $dest has correct contents ($backendName)." );
1020 } else {
1021 $this->assertNotEquals( $expContent, $contents,
1022 "Concat file at $dest has correct contents ($backendName)." );
1023 }
1024 }
1025
1026 public static function provider_testConcatenate() {
1027 $cases = array();
1028
1029 $rand = mt_rand( 0, 2000000000 ) . time();
1030 $dest = wfTempDir() . "/randomfile!$rand.txt";
1031 $srcs = array(
1032 self::baseStorePath() . '/unittest-cont1/e/file1.txt',
1033 self::baseStorePath() . '/unittest-cont1/e/file2.txt',
1034 self::baseStorePath() . '/unittest-cont1/e/file3.txt',
1035 self::baseStorePath() . '/unittest-cont1/e/file4.txt',
1036 self::baseStorePath() . '/unittest-cont1/e/file5.txt',
1037 self::baseStorePath() . '/unittest-cont1/e/file6.txt',
1038 self::baseStorePath() . '/unittest-cont1/e/file7.txt',
1039 self::baseStorePath() . '/unittest-cont1/e/file8.txt',
1040 self::baseStorePath() . '/unittest-cont1/e/file9.txt',
1041 self::baseStorePath() . '/unittest-cont1/e/file10.txt'
1042 );
1043 $content = array(
1044 'egfage',
1045 'ageageag',
1046 'rhokohlr',
1047 'shgmslkg',
1048 'kenga',
1049 'owagmal',
1050 'kgmae',
1051 'g eak;g',
1052 'lkaem;a',
1053 'legma'
1054 );
1055 $params = array( 'srcs' => $srcs, 'dst' => $dest );
1056
1057 $cases[] = array(
1058 $params, // operation
1059 $srcs, // sources
1060 $content, // content for each source
1061 false, // no dest already exists
1062 true, // succeeds
1063 );
1064
1065 $cases[] = array(
1066 $params, // operation
1067 $srcs, // sources
1068 $content, // content for each source
1069 true, // dest already exists
1070 false, // succeeds
1071 );
1072
1073 return $cases;
1074 }
1075
1076 /**
1077 * @dataProvider provider_testGetFileStat
1078 * @covers FileBackend::getFileStat
1079 */
1080 public function testGetFileStat( $path, $content, $alreadyExists ) {
1081 $this->backend = $this->singleBackend;
1082 $this->tearDownFiles();
1083 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1084 $this->tearDownFiles();
1085
1086 $this->backend = $this->multiBackend;
1087 $this->tearDownFiles();
1088 $this->doTestGetFileStat( $path, $content, $alreadyExists );
1089 $this->tearDownFiles();
1090 }
1091
1092 private function doTestGetFileStat( $path, $content, $alreadyExists ) {
1093 $backendName = $this->backendClass();
1094
1095 if ( $alreadyExists ) {
1096 $this->prepare( array( 'dir' => dirname( $path ) ) );
1097 $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
1098 $this->assertGoodStatus( $status,
1099 "Creation of file at $path succeeded ($backendName)." );
1100
1101 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1102 $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
1103 $stat = $this->backend->getFileStat( array( 'src' => $path ) );
1104
1105 $this->assertEquals( strlen( $content ), $size,
1106 "Correct file size of '$path'" );
1107 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
1108 "Correct file timestamp of '$path'" );
1109
1110 $size = $stat['size'];
1111 $time = $stat['mtime'];
1112 $this->assertEquals( strlen( $content ), $size,
1113 "Correct file size of '$path'" );
1114 $this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
1115 "Correct file timestamp of '$path'" );
1116
1117 $this->backend->clearCache( array( $path ) );
1118
1119 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1120
1121 $this->assertEquals( strlen( $content ), $size,
1122 "Correct file size of '$path'" );
1123
1124 $this->backend->preloadCache( array( $path ) );
1125
1126 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1127
1128 $this->assertEquals( strlen( $content ), $size,
1129 "Correct file size of '$path'" );
1130 } else {
1131 $size = $this->backend->getFileSize( array( 'src' => $path ) );
1132 $time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
1133 $stat = $this->backend->getFileStat( array( 'src' => $path ) );
1134
1135 $this->assertFalse( $size, "Correct file size of '$path'" );
1136 $this->assertFalse( $time, "Correct file timestamp of '$path'" );
1137 $this->assertFalse( $stat, "Correct file stat of '$path'" );
1138 }
1139 }
1140
1141 public static function provider_testGetFileStat() {
1142 $cases = array();
1143
1144 $base = self::baseStorePath();
1145 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents", true );
1146 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "", true );
1147 $cases[] = array( "$base/unittest-cont1/e/b/some-diff_file.txt", null, false );
1148
1149 return $cases;
1150 }
1151
1152 /**
1153 * @dataProvider provider_testGetFileStat
1154 * @covers FileBackend::streamFile
1155 */
1156 public function testStreamFile( $path, $content, $alreadyExists ) {
1157 $this->backend = $this->singleBackend;
1158 $this->tearDownFiles();
1159 $this->doTestStreamFile( $path, $content, $alreadyExists );
1160 $this->tearDownFiles();
1161 }
1162
1163 private function doTestStreamFile( $path, $content ) {
1164 $backendName = $this->backendClass();
1165
1166 // Test doStreamFile() directly to avoid header madness
1167 $class = new ReflectionClass( $this->backend );
1168 $method = $class->getMethod( 'doStreamFile' );
1169 $method->setAccessible( true );
1170
1171 if ( $content !== null ) {
1172 $this->prepare( array( 'dir' => dirname( $path ) ) );
1173 $status = $this->create( array( 'dst' => $path, 'content' => $content ) );
1174 $this->assertGoodStatus( $status,
1175 "Creation of file at $path succeeded ($backendName)." );
1176
1177 ob_start();
1178 $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
1179 $data = ob_get_contents();
1180 ob_end_clean();
1181
1182 $this->assertEquals( $content, $data, "Correct content streamed from '$path'" );
1183 } else { // 404 case
1184 ob_start();
1185 $method->invokeArgs( $this->backend, array( array( 'src' => $path ) ) );
1186 $data = ob_get_contents();
1187 ob_end_clean();
1188
1189 $this->assertEquals( '', $data, "Correct content streamed from '$path' ($backendName)" );
1190 }
1191 }
1192
1193 public static function provider_testStreamFile() {
1194 $cases = array();
1195
1196 $base = self::baseStorePath();
1197 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
1198 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", null );
1199
1200 return $cases;
1201 }
1202
1203 /**
1204 * @dataProvider provider_testGetFileContents
1205 * @covers FileBackend::getFileContents
1206 * @covers FileBackend::getFileContentsMulti
1207 */
1208 public function testGetFileContents( $source, $content ) {
1209 $this->backend = $this->singleBackend;
1210 $this->tearDownFiles();
1211 $this->doTestGetFileContents( $source, $content );
1212 $this->tearDownFiles();
1213
1214 $this->backend = $this->multiBackend;
1215 $this->tearDownFiles();
1216 $this->doTestGetFileContents( $source, $content );
1217 $this->tearDownFiles();
1218 }
1219
1220 private function doTestGetFileContents( $source, $content ) {
1221 $backendName = $this->backendClass();
1222
1223 $srcs = (array)$source;
1224 $content = (array)$content;
1225 foreach ( $srcs as $i => $src ) {
1226 $this->prepare( array( 'dir' => dirname( $src ) ) );
1227 $status = $this->backend->doOperation(
1228 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1229 $this->assertGoodStatus( $status,
1230 "Creation of file at $src succeeded ($backendName)." );
1231 }
1232
1233 if ( is_array( $source ) ) {
1234 $contents = $this->backend->getFileContentsMulti( array( 'srcs' => $source ) );
1235 foreach ( $contents as $path => $data ) {
1236 $this->assertNotEquals( false, $data, "Contents of $path exists ($backendName)." );
1237 $this->assertEquals( current( $content ), $data, "Contents of $path is correct ($backendName)." );
1238 next( $content );
1239 }
1240 $this->assertEquals( $source, array_keys( $contents ), "Contents in right order ($backendName)." );
1241 $this->assertEquals( count( $source ), count( $contents ), "Contents array size correct ($backendName)." );
1242 } else {
1243 $data = $this->backend->getFileContents( array( 'src' => $source ) );
1244 $this->assertNotEquals( false, $data, "Contents of $source exists ($backendName)." );
1245 $this->assertEquals( $content[0], $data, "Contents of $source is correct ($backendName)." );
1246 }
1247 }
1248
1249 public static function provider_testGetFileContents() {
1250 $cases = array();
1251
1252 $base = self::baseStorePath();
1253 $cases[] = array( "$base/unittest-cont1/e/b/z/some_file.txt", "some file contents" );
1254 $cases[] = array( "$base/unittest-cont1/e/b/some-other_file.txt", "more file contents" );
1255 $cases[] = array(
1256 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1257 "$base/unittest-cont1/e/a/z.txt" ),
1258 array( "contents xx", "contents xy", "contents xz" )
1259 );
1260
1261 return $cases;
1262 }
1263
1264 /**
1265 * @dataProvider provider_testGetLocalCopy
1266 * @covers FileBackend::getLocalCopy
1267 */
1268 public function testGetLocalCopy( $source, $content ) {
1269 $this->backend = $this->singleBackend;
1270 $this->tearDownFiles();
1271 $this->doTestGetLocalCopy( $source, $content );
1272 $this->tearDownFiles();
1273
1274 $this->backend = $this->multiBackend;
1275 $this->tearDownFiles();
1276 $this->doTestGetLocalCopy( $source, $content );
1277 $this->tearDownFiles();
1278 }
1279
1280 private function doTestGetLocalCopy( $source, $content ) {
1281 $backendName = $this->backendClass();
1282
1283 $srcs = (array)$source;
1284 $content = (array)$content;
1285 foreach ( $srcs as $i => $src ) {
1286 $this->prepare( array( 'dir' => dirname( $src ) ) );
1287 $status = $this->backend->doOperation(
1288 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1289 $this->assertGoodStatus( $status,
1290 "Creation of file at $src succeeded ($backendName)." );
1291 }
1292
1293 if ( is_array( $source ) ) {
1294 $tmpFiles = $this->backend->getLocalCopyMulti( array( 'srcs' => $source ) );
1295 foreach ( $tmpFiles as $path => $tmpFile ) {
1296 $this->assertNotNull( $tmpFile,
1297 "Creation of local copy of $path succeeded ($backendName)." );
1298 $contents = file_get_contents( $tmpFile->getPath() );
1299 $this->assertNotEquals( false, $contents, "Local copy of $path exists ($backendName)." );
1300 $this->assertEquals( current( $content ), $contents, "Local copy of $path is correct ($backendName)." );
1301 next( $content );
1302 }
1303 $this->assertEquals( $source, array_keys( $tmpFiles ), "Local copies in right order ($backendName)." );
1304 $this->assertEquals( count( $source ), count( $tmpFiles ), "Local copies array size correct ($backendName)." );
1305 } else {
1306 $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
1307 $this->assertNotNull( $tmpFile,
1308 "Creation of local copy of $source succeeded ($backendName)." );
1309 $contents = file_get_contents( $tmpFile->getPath() );
1310 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
1311 $this->assertEquals( $content[0], $contents, "Local copy of $source is correct ($backendName)." );
1312 }
1313
1314 $obj = new stdClass();
1315 $tmpFile->bind( $obj );
1316 }
1317
1318 public static function provider_testGetLocalCopy() {
1319 $cases = array();
1320
1321 $base = self::baseStorePath();
1322 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1323 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1324 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1325 $cases[] = array(
1326 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1327 "$base/unittest-cont1/e/a/z.txt" ),
1328 array( "contents xx $", "contents xy 111", "contents xz" )
1329 );
1330
1331 return $cases;
1332 }
1333
1334 /**
1335 * @dataProvider provider_testGetLocalReference
1336 * @covers FileBackend::getLocalReference
1337 */
1338 public function testGetLocalReference( $source, $content ) {
1339 $this->backend = $this->singleBackend;
1340 $this->tearDownFiles();
1341 $this->doTestGetLocalReference( $source, $content );
1342 $this->tearDownFiles();
1343
1344 $this->backend = $this->multiBackend;
1345 $this->tearDownFiles();
1346 $this->doTestGetLocalReference( $source, $content );
1347 $this->tearDownFiles();
1348 }
1349
1350 private function doTestGetLocalReference( $source, $content ) {
1351 $backendName = $this->backendClass();
1352
1353 $srcs = (array)$source;
1354 $content = (array)$content;
1355 foreach ( $srcs as $i => $src ) {
1356 $this->prepare( array( 'dir' => dirname( $src ) ) );
1357 $status = $this->backend->doOperation(
1358 array( 'op' => 'create', 'content' => $content[$i], 'dst' => $src ) );
1359 $this->assertGoodStatus( $status,
1360 "Creation of file at $src succeeded ($backendName)." );
1361 }
1362
1363 if ( is_array( $source ) ) {
1364 $tmpFiles = $this->backend->getLocalReferenceMulti( array( 'srcs' => $source ) );
1365 foreach ( $tmpFiles as $path => $tmpFile ) {
1366 $this->assertNotNull( $tmpFile,
1367 "Creation of local copy of $path succeeded ($backendName)." );
1368 $contents = file_get_contents( $tmpFile->getPath() );
1369 $this->assertNotEquals( false, $contents, "Local ref of $path exists ($backendName)." );
1370 $this->assertEquals( current( $content ), $contents, "Local ref of $path is correct ($backendName)." );
1371 next( $content );
1372 }
1373 $this->assertEquals( $source, array_keys( $tmpFiles ), "Local refs in right order ($backendName)." );
1374 $this->assertEquals( count( $source ), count( $tmpFiles ), "Local refs array size correct ($backendName)." );
1375 } else {
1376 $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
1377 $this->assertNotNull( $tmpFile,
1378 "Creation of local copy of $source succeeded ($backendName)." );
1379 $contents = file_get_contents( $tmpFile->getPath() );
1380 $this->assertNotEquals( false, $contents, "Local ref of $source exists ($backendName)." );
1381 $this->assertEquals( $content[0], $contents, "Local ref of $source is correct ($backendName)." );
1382 }
1383 }
1384
1385 public static function provider_testGetLocalReference() {
1386 $cases = array();
1387
1388 $base = self::baseStorePath();
1389 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1390 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1391 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1392 $cases[] = array(
1393 array( "$base/unittest-cont1/e/a/x.txt", "$base/unittest-cont1/e/a/y.txt",
1394 "$base/unittest-cont1/e/a/z.txt" ),
1395 array( "contents xx 1111", "contents xy %", "contents xz $" )
1396 );
1397
1398 return $cases;
1399 }
1400
1401 /**
1402 * @covers FileBackend::getLocalCopy
1403 * @covers FileBackend::getLocalReference
1404 */
1405 public function testGetLocalCopyAndReference404() {
1406 $this->backend = $this->singleBackend;
1407 $this->tearDownFiles();
1408 $this->doTestGetLocalCopyAndReference404();
1409 $this->tearDownFiles();
1410
1411 $this->backend = $this->multiBackend;
1412 $this->tearDownFiles();
1413 $this->doTestGetLocalCopyAndReference404();
1414 $this->tearDownFiles();
1415 }
1416
1417 public function doTestGetLocalCopyAndReference404() {
1418 $backendName = $this->backendClass();
1419
1420 $base = self::baseStorePath();
1421
1422 $tmpFile = $this->backend->getLocalCopy( array(
1423 'src' => "$base/unittest-cont1/not-there" ) );
1424 $this->assertEquals( null, $tmpFile, "Local copy of not existing file is null ($backendName)." );
1425
1426 $tmpFile = $this->backend->getLocalReference( array(
1427 'src' => "$base/unittest-cont1/not-there" ) );
1428 $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
1429 }
1430
1431 /**
1432 * @dataProvider provider_testGetFileHttpUrl
1433 * @covers FileBackend::getFileHttpUrl
1434 */
1435 public function testGetFileHttpUrl( $source, $content ) {
1436 $this->backend = $this->singleBackend;
1437 $this->tearDownFiles();
1438 $this->doTestGetFileHttpUrl( $source, $content );
1439 $this->tearDownFiles();
1440
1441 $this->backend = $this->multiBackend;
1442 $this->tearDownFiles();
1443 $this->doTestGetFileHttpUrl( $source, $content );
1444 $this->tearDownFiles();
1445 }
1446
1447 private function doTestGetFileHttpUrl( $source, $content ) {
1448 $backendName = $this->backendClass();
1449
1450 $this->prepare( array( 'dir' => dirname( $source ) ) );
1451 $status = $this->backend->doOperation(
1452 array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
1453 $this->assertGoodStatus( $status,
1454 "Creation of file at $source succeeded ($backendName)." );
1455
1456 $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) );
1457
1458 if ( $url !== null ) { // supported
1459 $data = Http::request( "GET", $url );
1460 $this->assertEquals( $content, $data,
1461 "HTTP GET of URL has right contents ($backendName)." );
1462 }
1463 }
1464
1465 public static function provider_testGetFileHttpUrl() {
1466 $cases = array();
1467
1468 $base = self::baseStorePath();
1469 $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
1470 $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
1471 $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
1472
1473 return $cases;
1474 }
1475
1476 /**
1477 * @dataProvider provider_testPrepareAndClean
1478 * @covers FileBackend::prepare
1479 * @covers FileBackend::clean
1480 */
1481 public function testPrepareAndClean( $path, $isOK ) {
1482 $this->backend = $this->singleBackend;
1483 $this->doTestPrepareAndClean( $path, $isOK );
1484 $this->tearDownFiles();
1485
1486 $this->backend = $this->multiBackend;
1487 $this->doTestPrepareAndClean( $path, $isOK );
1488 $this->tearDownFiles();
1489 }
1490
1491 public static function provider_testPrepareAndClean() {
1492 $base = self::baseStorePath();
1493
1494 return array(
1495 array( "$base/unittest-cont1/e/a/z/some_file1.txt", true ),
1496 array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
1497 # Specific to FS backend with no basePath field set
1498 #array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
1499 );
1500 }
1501
1502 private function doTestPrepareAndClean( $path, $isOK ) {
1503 $backendName = $this->backendClass();
1504
1505 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
1506 if ( $isOK ) {
1507 $this->assertGoodStatus( $status,
1508 "Preparing dir $path succeeded without warnings ($backendName)." );
1509 $this->assertEquals( true, $status->isOK(),
1510 "Preparing dir $path succeeded ($backendName)." );
1511 } else {
1512 $this->assertEquals( false, $status->isOK(),
1513 "Preparing dir $path failed ($backendName)." );
1514 }
1515
1516 $status = $this->backend->secure( array( 'dir' => dirname( $path ) ) );
1517 if ( $isOK ) {
1518 $this->assertGoodStatus( $status,
1519 "Securing dir $path succeeded without warnings ($backendName)." );
1520 $this->assertEquals( true, $status->isOK(),
1521 "Securing dir $path succeeded ($backendName)." );
1522 } else {
1523 $this->assertEquals( false, $status->isOK(),
1524 "Securing dir $path failed ($backendName)." );
1525 }
1526
1527 $status = $this->backend->publish( array( 'dir' => dirname( $path ) ) );
1528 if ( $isOK ) {
1529 $this->assertGoodStatus( $status,
1530 "Publishing dir $path succeeded without warnings ($backendName)." );
1531 $this->assertEquals( true, $status->isOK(),
1532 "Publishing dir $path succeeded ($backendName)." );
1533 } else {
1534 $this->assertEquals( false, $status->isOK(),
1535 "Publishing dir $path failed ($backendName)." );
1536 }
1537
1538 $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
1539 if ( $isOK ) {
1540 $this->assertGoodStatus( $status,
1541 "Cleaning dir $path succeeded without warnings ($backendName)." );
1542 $this->assertEquals( true, $status->isOK(),
1543 "Cleaning dir $path succeeded ($backendName)." );
1544 } else {
1545 $this->assertEquals( false, $status->isOK(),
1546 "Cleaning dir $path failed ($backendName)." );
1547 }
1548 }
1549
1550 public function testRecursiveClean() {
1551 $this->backend = $this->singleBackend;
1552 $this->doTestRecursiveClean();
1553 $this->tearDownFiles();
1554
1555 $this->backend = $this->multiBackend;
1556 $this->doTestRecursiveClean();
1557 $this->tearDownFiles();
1558 }
1559
1560 /**
1561 * @covers FileBackend::clean
1562 */
1563 private function doTestRecursiveClean() {
1564 $backendName = $this->backendClass();
1565
1566 $base = self::baseStorePath();
1567 $dirs = array(
1568 "$base/unittest-cont1",
1569 "$base/unittest-cont1/e",
1570 "$base/unittest-cont1/e/a",
1571 "$base/unittest-cont1/e/a/b",
1572 "$base/unittest-cont1/e/a/b/c",
1573 "$base/unittest-cont1/e/a/b/c/d0",
1574 "$base/unittest-cont1/e/a/b/c/d1",
1575 "$base/unittest-cont1/e/a/b/c/d2",
1576 "$base/unittest-cont1/e/a/b/c/d0/1",
1577 "$base/unittest-cont1/e/a/b/c/d0/2",
1578 "$base/unittest-cont1/e/a/b/c/d1/3",
1579 "$base/unittest-cont1/e/a/b/c/d1/4",
1580 "$base/unittest-cont1/e/a/b/c/d2/5",
1581 "$base/unittest-cont1/e/a/b/c/d2/6"
1582 );
1583 foreach ( $dirs as $dir ) {
1584 $status = $this->prepare( array( 'dir' => $dir ) );
1585 $this->assertGoodStatus( $status,
1586 "Preparing dir $dir succeeded without warnings ($backendName)." );
1587 }
1588
1589 if ( $this->backend instanceof FSFileBackend ) {
1590 foreach ( $dirs as $dir ) {
1591 $this->assertEquals( true, $this->backend->directoryExists( array( 'dir' => $dir ) ),
1592 "Dir $dir exists ($backendName)." );
1593 }
1594 }
1595
1596 $status = $this->backend->clean(
1597 array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
1598 $this->assertGoodStatus( $status,
1599 "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
1600
1601 foreach ( $dirs as $dir ) {
1602 $this->assertEquals( false, $this->backend->directoryExists( array( 'dir' => $dir ) ),
1603 "Dir $dir no longer exists ($backendName)." );
1604 }
1605 }
1606
1607 /**
1608 * @covers FileBackend::doOperations
1609 */
1610 public function testDoOperations() {
1611 $this->backend = $this->singleBackend;
1612 $this->tearDownFiles();
1613 $this->doTestDoOperations();
1614 $this->tearDownFiles();
1615
1616 $this->backend = $this->multiBackend;
1617 $this->tearDownFiles();
1618 $this->doTestDoOperations();
1619 $this->tearDownFiles();
1620 }
1621
1622 private function doTestDoOperations() {
1623 $base = self::baseStorePath();
1624
1625 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1626 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1627 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1628 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1629 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1630 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1631 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1632
1633 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1634 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1635 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1636 $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
1637 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1638 $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
1639 $this->prepare( array( 'dir' => dirname( $fileD ) ) );
1640
1641 $status = $this->backend->doOperations( array(
1642 array( 'op' => 'describe', 'src' => $fileA,
1643 'headers' => array( 'X-Content-Length' => '91.3' ), 'disposition' => 'inline' ),
1644 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1645 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1646 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1647 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1648 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
1649 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1650 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
1651 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1652 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
1653 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1654 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
1655 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1656 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
1657 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1658 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
1659 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1660 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1661 // Does nothing
1662 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1663 // Does nothing
1664 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1665 // Does nothing
1666 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1667 // Does nothing
1668 array( 'op' => 'null' ),
1669 // Does nothing
1670 ) );
1671
1672 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1673 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1674 $this->assertEquals( 14, count( $status->success ),
1675 "Operation batch has correct success array" );
1676
1677 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
1678 "File does not exist at $fileA" );
1679 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1680 "File does not exist at $fileB" );
1681 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1682 "File does not exist at $fileD" );
1683
1684 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1685 "File exists at $fileC" );
1686 $this->assertEquals( $fileBContents,
1687 $this->backend->getFileContents( array( 'src' => $fileC ) ),
1688 "Correct file contents of $fileC" );
1689 $this->assertEquals( strlen( $fileBContents ),
1690 $this->backend->getFileSize( array( 'src' => $fileC ) ),
1691 "Correct file size of $fileC" );
1692 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1693 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
1694 "Correct file SHA-1 of $fileC" );
1695 }
1696
1697 /**
1698 * @covers FileBackend::doOperations
1699 */
1700 public function testDoOperationsPipeline() {
1701 $this->backend = $this->singleBackend;
1702 $this->tearDownFiles();
1703 $this->doTestDoOperationsPipeline();
1704 $this->tearDownFiles();
1705
1706 $this->backend = $this->multiBackend;
1707 $this->tearDownFiles();
1708 $this->doTestDoOperationsPipeline();
1709 $this->tearDownFiles();
1710 }
1711
1712 // concurrency orientated
1713 private function doTestDoOperationsPipeline() {
1714 $base = self::baseStorePath();
1715
1716 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1717 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1718 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1719
1720 $tmpNameA = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1721 file_put_contents( $tmpNameA, $fileAContents );
1722 $tmpNameB = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1723 file_put_contents( $tmpNameB, $fileBContents );
1724 $tmpNameC = TempFSFile::factory( "unittests_", 'txt' )->getPath();
1725 file_put_contents( $tmpNameC, $fileCContents );
1726
1727 $this->filesToPrune[] = $tmpNameA; # avoid file leaking
1728 $this->filesToPrune[] = $tmpNameB; # avoid file leaking
1729 $this->filesToPrune[] = $tmpNameC; # avoid file leaking
1730
1731 $fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
1732 $fileB = "$base/unittest-cont1/e/a/b/fileB.txt";
1733 $fileC = "$base/unittest-cont1/e/a/b/fileC.txt";
1734 $fileD = "$base/unittest-cont1/e/a/b/fileD.txt";
1735
1736 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1737 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1738 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1739 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1740 $this->prepare( array( 'dir' => dirname( $fileD ) ) );
1741
1742 $status = $this->backend->doOperations( array(
1743 array( 'op' => 'store', 'src' => $tmpNameA, 'dst' => $fileA, 'overwriteSame' => 1 ),
1744 array( 'op' => 'store', 'src' => $tmpNameB, 'dst' => $fileB, 'overwrite' => 1 ),
1745 array( 'op' => 'store', 'src' => $tmpNameC, 'dst' => $fileC, 'overwrite' => 1 ),
1746 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1747 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1748 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1749 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1750 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
1751 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
1752 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
1753 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
1754 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
1755 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
1756 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
1757 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
1758 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
1759 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
1760 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
1761 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
1762 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1763 // Does nothing
1764 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1765 // Does nothing
1766 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
1767 // Does nothing
1768 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
1769 // Does nothing
1770 array( 'op' => 'null' ),
1771 // Does nothing
1772 ) );
1773
1774 $this->assertGoodStatus( $status, "Operation batch succeeded" );
1775 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1776 $this->assertEquals( 16, count( $status->success ),
1777 "Operation batch has correct success array" );
1778
1779 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
1780 "File does not exist at $fileA" );
1781 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1782 "File does not exist at $fileB" );
1783 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1784 "File does not exist at $fileD" );
1785
1786 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1787 "File exists at $fileC" );
1788 $this->assertEquals( $fileBContents,
1789 $this->backend->getFileContents( array( 'src' => $fileC ) ),
1790 "Correct file contents of $fileC" );
1791 $this->assertEquals( strlen( $fileBContents ),
1792 $this->backend->getFileSize( array( 'src' => $fileC ) ),
1793 "Correct file size of $fileC" );
1794 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1795 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
1796 "Correct file SHA-1 of $fileC" );
1797 }
1798
1799 /**
1800 * @covers FileBackend::doOperations
1801 */
1802 public function testDoOperationsFailing() {
1803 $this->backend = $this->singleBackend;
1804 $this->tearDownFiles();
1805 $this->doTestDoOperationsFailing();
1806 $this->tearDownFiles();
1807
1808 $this->backend = $this->multiBackend;
1809 $this->tearDownFiles();
1810 $this->doTestDoOperationsFailing();
1811 $this->tearDownFiles();
1812 }
1813
1814 private function doTestDoOperationsFailing() {
1815 $base = self::baseStorePath();
1816
1817 $fileA = "$base/unittest-cont2/a/b/fileA.txt";
1818 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
1819 $fileB = "$base/unittest-cont2/a/b/fileB.txt";
1820 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
1821 $fileC = "$base/unittest-cont2/a/b/fileC.txt";
1822 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
1823 $fileD = "$base/unittest-cont2/a/b/fileD.txt";
1824
1825 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
1826 $this->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
1827 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
1828 $this->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
1829 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
1830 $this->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
1831
1832 $status = $this->backend->doOperations( array(
1833 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
1834 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (file:<orginal contents>)
1835 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
1836 // Now: A:<A>, B:<B>, C:<A>, D:<empty>
1837 array( 'op' => 'copy', 'src' => $fileB, 'dst' => $fileD, 'overwrite' => 1 ),
1838 // Now: A:<A>, B:<B>, C:<A>, D:<B>
1839 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD ),
1840 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1841 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC, 'overwriteSame' => 1 ),
1842 // Now: A:<A>, B:<B>, C:<A>, D:<empty> (failed)
1843 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileA, 'overwrite' => 1 ),
1844 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1845 array( 'op' => 'delete', 'src' => $fileD ),
1846 // Now: A:<B>, B:<empty>, C:<A>, D:<empty>
1847 array( 'op' => 'null' ),
1848 // Does nothing
1849 ), array( 'force' => 1 ) );
1850
1851 $this->assertNotEquals( array(), $status->errors, "Operation had warnings" );
1852 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
1853 $this->assertEquals( 8, count( $status->success ),
1854 "Operation batch has correct success array" );
1855
1856 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
1857 "File does not exist at $fileB" );
1858 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
1859 "File does not exist at $fileD" );
1860
1861 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileA ) ),
1862 "File does not exist at $fileA" );
1863 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
1864 "File exists at $fileC" );
1865 $this->assertEquals( $fileBContents,
1866 $this->backend->getFileContents( array( 'src' => $fileA ) ),
1867 "Correct file contents of $fileA" );
1868 $this->assertEquals( strlen( $fileBContents ),
1869 $this->backend->getFileSize( array( 'src' => $fileA ) ),
1870 "Correct file size of $fileA" );
1871 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
1872 $this->backend->getFileSha1Base36( array( 'src' => $fileA ) ),
1873 "Correct file SHA-1 of $fileA" );
1874 }
1875
1876 /**
1877 * @covers FileBackend::getFileList
1878 */
1879 public function testGetFileList() {
1880 $this->backend = $this->singleBackend;
1881 $this->tearDownFiles();
1882 $this->doTestGetFileList();
1883 $this->tearDownFiles();
1884
1885 $this->backend = $this->multiBackend;
1886 $this->tearDownFiles();
1887 $this->doTestGetFileList();
1888 $this->tearDownFiles();
1889 }
1890
1891 private function doTestGetFileList() {
1892 $backendName = $this->backendClass();
1893 $base = self::baseStorePath();
1894
1895 // Should have no errors
1896 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
1897
1898 $files = array(
1899 "$base/unittest-cont1/e/test1.txt",
1900 "$base/unittest-cont1/e/test2.txt",
1901 "$base/unittest-cont1/e/test3.txt",
1902 "$base/unittest-cont1/e/subdir1/test1.txt",
1903 "$base/unittest-cont1/e/subdir1/test2.txt",
1904 "$base/unittest-cont1/e/subdir2/test3.txt",
1905 "$base/unittest-cont1/e/subdir2/test4.txt",
1906 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
1907 "$base/unittest-cont1/e/subdir2/subdir/test2.txt",
1908 "$base/unittest-cont1/e/subdir2/subdir/test3.txt",
1909 "$base/unittest-cont1/e/subdir2/subdir/test4.txt",
1910 "$base/unittest-cont1/e/subdir2/subdir/test5.txt",
1911 "$base/unittest-cont1/e/subdir2/subdir/sub/test0.txt",
1912 "$base/unittest-cont1/e/subdir2/subdir/sub/120-px-file.txt",
1913 );
1914
1915 // Add the files
1916 $ops = array();
1917 foreach ( $files as $file ) {
1918 $this->prepare( array( 'dir' => dirname( $file ) ) );
1919 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
1920 }
1921 $status = $this->backend->doQuickOperations( $ops );
1922 $this->assertGoodStatus( $status,
1923 "Creation of files succeeded ($backendName)." );
1924 $this->assertEquals( true, $status->isOK(),
1925 "Creation of files succeeded with OK status ($backendName)." );
1926
1927 // Expected listing at root
1928 $expected = array(
1929 "e/test1.txt",
1930 "e/test2.txt",
1931 "e/test3.txt",
1932 "e/subdir1/test1.txt",
1933 "e/subdir1/test2.txt",
1934 "e/subdir2/test3.txt",
1935 "e/subdir2/test4.txt",
1936 "e/subdir2/subdir/test1.txt",
1937 "e/subdir2/subdir/test2.txt",
1938 "e/subdir2/subdir/test3.txt",
1939 "e/subdir2/subdir/test4.txt",
1940 "e/subdir2/subdir/test5.txt",
1941 "e/subdir2/subdir/sub/test0.txt",
1942 "e/subdir2/subdir/sub/120-px-file.txt",
1943 );
1944 sort( $expected );
1945
1946 // Actual listing (no trailing slash) at root
1947 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1" ) );
1948 $list = $this->listToArray( $iter );
1949 sort( $list );
1950 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1951
1952 // Actual listing (no trailing slash) at root with advise
1953 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1", 'adviseStat' => 1 ) );
1954 $list = $this->listToArray( $iter );
1955 sort( $list );
1956 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1957
1958 // Actual listing (with trailing slash) at root
1959 $list = array();
1960 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/" ) );
1961 foreach ( $iter as $file ) {
1962 $list[] = $file;
1963 }
1964 sort( $list );
1965 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1966
1967 // Expected listing at subdir
1968 $expected = array(
1969 "test1.txt",
1970 "test2.txt",
1971 "test3.txt",
1972 "test4.txt",
1973 "test5.txt",
1974 "sub/test0.txt",
1975 "sub/120-px-file.txt",
1976 );
1977 sort( $expected );
1978
1979 // Actual listing (no trailing slash) at subdir
1980 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
1981 $list = $this->listToArray( $iter );
1982 sort( $list );
1983 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1984
1985 // Actual listing (no trailing slash) at subdir with advise
1986 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir", 'adviseStat' => 1 ) );
1987 $list = $this->listToArray( $iter );
1988 sort( $list );
1989 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1990
1991 // Actual listing (with trailing slash) at subdir
1992 $list = array();
1993 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir/" ) );
1994 foreach ( $iter as $file ) {
1995 $list[] = $file;
1996 }
1997 sort( $list );
1998 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1999
2000 // Actual listing (using iterator second time)
2001 $list = $this->listToArray( $iter );
2002 sort( $list );
2003 $this->assertEquals( $expected, $list, "Correct file listing ($backendName), second iteration." );
2004
2005 // Actual listing (top files only) at root
2006 $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1" ) );
2007 $list = $this->listToArray( $iter );
2008 sort( $list );
2009 $this->assertEquals( array(), $list, "Correct top file listing ($backendName)." );
2010
2011 // Expected listing (top files only) at subdir
2012 $expected = array(
2013 "test1.txt",
2014 "test2.txt",
2015 "test3.txt",
2016 "test4.txt",
2017 "test5.txt"
2018 );
2019 sort( $expected );
2020
2021 // Actual listing (top files only) at subdir
2022 $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) );
2023 $list = $this->listToArray( $iter );
2024 sort( $list );
2025 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2026
2027 // Actual listing (top files only) at subdir with advise
2028 $iter = $this->backend->getTopFileList( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir", 'adviseStat' => 1 ) );
2029 $list = $this->listToArray( $iter );
2030 sort( $list );
2031 $this->assertEquals( $expected, $list, "Correct top file listing ($backendName)." );
2032
2033 foreach ( $files as $file ) { // clean up
2034 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
2035 }
2036
2037 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
2038 foreach ( $iter as $iter ) {
2039 // no errors
2040 }
2041 }
2042
2043 /**
2044 * @covers FileBackend::getTopDirectoryList
2045 * @covers FileBackend::getDirectoryList
2046 */
2047 public function testGetDirectoryList() {
2048 $this->backend = $this->singleBackend;
2049 $this->tearDownFiles();
2050 $this->doTestGetDirectoryList();
2051 $this->tearDownFiles();
2052
2053 $this->backend = $this->multiBackend;
2054 $this->tearDownFiles();
2055 $this->doTestGetDirectoryList();
2056 $this->tearDownFiles();
2057 }
2058
2059 private function doTestGetDirectoryList() {
2060 $backendName = $this->backendClass();
2061
2062 $base = self::baseStorePath();
2063 $files = array(
2064 "$base/unittest-cont1/e/test1.txt",
2065 "$base/unittest-cont1/e/test2.txt",
2066 "$base/unittest-cont1/e/test3.txt",
2067 "$base/unittest-cont1/e/subdir1/test1.txt",
2068 "$base/unittest-cont1/e/subdir1/test2.txt",
2069 "$base/unittest-cont1/e/subdir2/test3.txt",
2070 "$base/unittest-cont1/e/subdir2/test4.txt",
2071 "$base/unittest-cont1/e/subdir2/subdir/test1.txt",
2072 "$base/unittest-cont1/e/subdir3/subdir/test2.txt",
2073 "$base/unittest-cont1/e/subdir4/subdir/test3.txt",
2074 "$base/unittest-cont1/e/subdir4/subdir/test4.txt",
2075 "$base/unittest-cont1/e/subdir4/subdir/test5.txt",
2076 "$base/unittest-cont1/e/subdir4/subdir/sub/test0.txt",
2077 "$base/unittest-cont1/e/subdir4/subdir/sub/120-px-file.txt",
2078 );
2079
2080 // Add the files
2081 $ops = array();
2082 foreach ( $files as $file ) {
2083 $this->prepare( array( 'dir' => dirname( $file ) ) );
2084 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
2085 }
2086 $status = $this->backend->doQuickOperations( $ops );
2087 $this->assertGoodStatus( $status,
2088 "Creation of files succeeded ($backendName)." );
2089 $this->assertEquals( true, $status->isOK(),
2090 "Creation of files succeeded with OK status ($backendName)." );
2091
2092 $this->assertEquals( true,
2093 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) ),
2094 "Directory exists in ($backendName)." );
2095 $this->assertEquals( true,
2096 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/subdir" ) ),
2097 "Directory exists in ($backendName)." );
2098 $this->assertEquals( false,
2099 $this->backend->directoryExists( array( 'dir' => "$base/unittest-cont1/e/subdir2/test1.txt" ) ),
2100 "Directory does not exists in ($backendName)." );
2101
2102 // Expected listing
2103 $expected = array(
2104 "e",
2105 );
2106 sort( $expected );
2107
2108 // Actual listing (no trailing slash)
2109 $list = array();
2110 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1" ) );
2111 foreach ( $iter as $file ) {
2112 $list[] = $file;
2113 }
2114 sort( $list );
2115
2116 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2117
2118 // Expected listing
2119 $expected = array(
2120 "subdir1",
2121 "subdir2",
2122 "subdir3",
2123 "subdir4",
2124 );
2125 sort( $expected );
2126
2127 // Actual listing (no trailing slash)
2128 $list = array();
2129 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e" ) );
2130 foreach ( $iter as $file ) {
2131 $list[] = $file;
2132 }
2133 sort( $list );
2134
2135 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2136
2137 // Actual listing (with trailing slash)
2138 $list = array();
2139 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/" ) );
2140 foreach ( $iter as $file ) {
2141 $list[] = $file;
2142 }
2143 sort( $list );
2144
2145 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2146
2147 // Expected listing
2148 $expected = array(
2149 "subdir",
2150 );
2151 sort( $expected );
2152
2153 // Actual listing (no trailing slash)
2154 $list = array();
2155 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2" ) );
2156 foreach ( $iter as $file ) {
2157 $list[] = $file;
2158 }
2159 sort( $list );
2160
2161 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2162
2163 // Actual listing (with trailing slash)
2164 $list = array();
2165 $iter = $this->backend->getTopDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir2/" ) );
2166 foreach ( $iter as $file ) {
2167 $list[] = $file;
2168 }
2169 sort( $list );
2170
2171 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName)." );
2172
2173 // Actual listing (using iterator second time)
2174 $list = array();
2175 foreach ( $iter as $file ) {
2176 $list[] = $file;
2177 }
2178 sort( $list );
2179
2180 $this->assertEquals( $expected, $list, "Correct top dir listing ($backendName), second iteration." );
2181
2182 // Expected listing (recursive)
2183 $expected = array(
2184 "e",
2185 "e/subdir1",
2186 "e/subdir2",
2187 "e/subdir3",
2188 "e/subdir4",
2189 "e/subdir2/subdir",
2190 "e/subdir3/subdir",
2191 "e/subdir4/subdir",
2192 "e/subdir4/subdir/sub",
2193 );
2194 sort( $expected );
2195
2196 // Actual listing (recursive)
2197 $list = array();
2198 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/" ) );
2199 foreach ( $iter as $file ) {
2200 $list[] = $file;
2201 }
2202 sort( $list );
2203
2204 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2205
2206 // Expected listing (recursive)
2207 $expected = array(
2208 "subdir",
2209 "subdir/sub",
2210 );
2211 sort( $expected );
2212
2213 // Actual listing (recursive)
2214 $list = array();
2215 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir4" ) );
2216 foreach ( $iter as $file ) {
2217 $list[] = $file;
2218 }
2219 sort( $list );
2220
2221 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2222
2223 // Actual listing (recursive, second time)
2224 $list = array();
2225 foreach ( $iter as $file ) {
2226 $list[] = $file;
2227 }
2228 sort( $list );
2229
2230 $this->assertEquals( $expected, $list, "Correct dir listing ($backendName)." );
2231
2232 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/subdir1" ) );
2233 $items = $this->listToArray( $iter );
2234 $this->assertEquals( array(), $items, "Directory listing is empty." );
2235
2236 foreach ( $files as $file ) { // clean up
2237 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
2238 }
2239
2240 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
2241 foreach ( $iter as $file ) {
2242 // no errors
2243 }
2244
2245 $items = $this->listToArray( $iter );
2246 $this->assertEquals( array(), $items, "Directory listing is empty." );
2247
2248 $iter = $this->backend->getDirectoryList( array( 'dir' => "$base/unittest-cont1/e/not/exists" ) );
2249 $items = $this->listToArray( $iter );
2250 $this->assertEquals( array(), $items, "Directory listing is empty." );
2251 }
2252
2253 /**
2254 * @covers FileBackend::lockFiles
2255 * @covers FileBackend::unlockFiles
2256 */
2257 public function testLockCalls() {
2258 $this->backend = $this->singleBackend;
2259 $this->doTestLockCalls();
2260 }
2261
2262 private function doTestLockCalls() {
2263 $backendName = $this->backendClass();
2264
2265 $paths = array(
2266 "test1.txt",
2267 "test2.txt",
2268 "test3.txt",
2269 "subdir1",
2270 "subdir1", // duplicate
2271 "subdir1/test1.txt",
2272 "subdir1/test2.txt",
2273 "subdir2",
2274 "subdir2", // duplicate
2275 "subdir2/test3.txt",
2276 "subdir2/test4.txt",
2277 "subdir2/subdir",
2278 "subdir2/subdir/test1.txt",
2279 "subdir2/subdir/test2.txt",
2280 "subdir2/subdir/test3.txt",
2281 "subdir2/subdir/test4.txt",
2282 "subdir2/subdir/test5.txt",
2283 "subdir2/subdir/sub",
2284 "subdir2/subdir/sub/test0.txt",
2285 "subdir2/subdir/sub/120-px-file.txt",
2286 );
2287
2288 for ( $i = 0; $i < 25; $i++ ) {
2289 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
2290 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2291 "Locking of files succeeded ($backendName) ($i)." );
2292 $this->assertEquals( true, $status->isOK(),
2293 "Locking of files succeeded with OK status ($backendName) ($i)." );
2294
2295 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
2296 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2297 "Locking of files succeeded ($backendName) ($i)." );
2298 $this->assertEquals( true, $status->isOK(),
2299 "Locking of files succeeded with OK status ($backendName) ($i)." );
2300
2301 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
2302 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2303 "Locking of files succeeded ($backendName) ($i)." );
2304 $this->assertEquals( true, $status->isOK(),
2305 "Locking of files succeeded with OK status ($backendName) ($i)." );
2306
2307 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
2308 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2309 "Locking of files succeeded ($backendName). ($i)" );
2310 $this->assertEquals( true, $status->isOK(),
2311 "Locking of files succeeded with OK status ($backendName) ($i)." );
2312
2313 ## Flip the acquire/release ordering around ##
2314
2315 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_SH );
2316 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2317 "Locking of files succeeded ($backendName) ($i)." );
2318 $this->assertEquals( true, $status->isOK(),
2319 "Locking of files succeeded with OK status ($backendName) ($i)." );
2320
2321 $status = $this->backend->lockFiles( $paths, LockManager::LOCK_EX );
2322 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2323 "Locking of files succeeded ($backendName) ($i)." );
2324 $this->assertEquals( true, $status->isOK(),
2325 "Locking of files succeeded with OK status ($backendName) ($i)." );
2326
2327 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_EX );
2328 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2329 "Locking of files succeeded ($backendName). ($i)" );
2330 $this->assertEquals( true, $status->isOK(),
2331 "Locking of files succeeded with OK status ($backendName) ($i)." );
2332
2333 $status = $this->backend->unlockFiles( $paths, LockManager::LOCK_SH );
2334 $this->assertEquals( print_r( array(), true ), print_r( $status->errors, true ),
2335 "Locking of files succeeded ($backendName) ($i)." );
2336 $this->assertEquals( true, $status->isOK(),
2337 "Locking of files succeeded with OK status ($backendName) ($i)." );
2338 }
2339
2340 $status = Status::newGood();
2341 $sl = $this->backend->getScopedFileLocks( $paths, LockManager::LOCK_EX, $status );
2342 $this->assertType( 'ScopedLock', $sl,
2343 "Scoped locking of files succeeded ($backendName)." );
2344 $this->assertEquals( array(), $status->errors,
2345 "Scoped locking of files succeeded ($backendName)." );
2346 $this->assertEquals( true, $status->isOK(),
2347 "Scoped locking of files succeeded with OK status ($backendName)." );
2348
2349 ScopedLock::release( $sl );
2350 $this->assertEquals( null, $sl,
2351 "Scoped unlocking of files succeeded ($backendName)." );
2352 $this->assertEquals( array(), $status->errors,
2353 "Scoped unlocking of files succeeded ($backendName)." );
2354 $this->assertEquals( true, $status->isOK(),
2355 "Scoped unlocking of files succeeded with OK status ($backendName)." );
2356 }
2357
2358 // helper function
2359 private function listToArray( $iter ) {
2360 return is_array( $iter ) ? $iter : iterator_to_array( $iter );
2361 }
2362
2363 // test helper wrapper for backend prepare() function
2364 private function prepare( array $params ) {
2365 return $this->backend->prepare( $params );
2366 }
2367
2368 // test helper wrapper for backend prepare() function
2369 private function create( array $params ) {
2370 $params['op'] = 'create';
2371
2372 return $this->backend->doQuickOperations( array( $params ) );
2373 }
2374
2375 function tearDownFiles() {
2376 foreach ( $this->filesToPrune as $file ) {
2377 if ( is_file( $file ) ) {
2378 unlink( $file );
2379 }
2380 }
2381 $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont-bad' );
2382 foreach ( $containers as $container ) {
2383 $this->deleteFiles( $container );
2384 }
2385 $this->filesToPrune = array();
2386 }
2387
2388 private function deleteFiles( $container ) {
2389 $base = self::baseStorePath();
2390 $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
2391 if ( $iter ) {
2392 foreach ( $iter as $file ) {
2393 $this->backend->quickDelete( array( 'src' => "$base/$container/$file" ) );
2394 }
2395 // free the directory, to avoid Permission denied under windows on rmdir
2396 unset( $iter );
2397 }
2398 $this->backend->clean( array( 'dir' => "$base/$container", 'recursive' => 1 ) );
2399 }
2400
2401 function assertBackendPathsConsistent( array $paths ) {
2402 if ( $this->backend instanceof FileBackendMultiWrite ) {
2403 $status = $this->backend->consistencyCheck( $paths );
2404 $this->assertGoodStatus( $status, "Files synced: " . implode( ',', $paths ) );
2405 }
2406 }
2407
2408 function assertGoodStatus( $status, $msg ) {
2409 $this->assertEquals( print_r( array(), 1 ), print_r( $status->errors, 1 ), $msg );
2410 }
2411 }