* In NewParserTest, made it possible to use the --use-filebackend param to run the...
[lhc/web/wiklou.git] / tests / phpunit / includes / filerepo / FileBackendTest.php
1 <?php
2
3 /**
4 * @group FileRepo
5 */
6 class FileBackendTest extends MediaWikiTestCase {
7 private $backend, $multiBackend;
8 private $filesToPrune = array();
9 private $dirsToPrune = array();
10 private static $backendToUse;
11
12 function setUp() {
13 global $wgFileBackends;
14 parent::setUp();
15 $tmpPrefix = wfTempDir() . '/filebackend-unittest-' . time() . '-' . mt_rand();
16 if ( $this->getCliArg( 'use-filebackend=' ) ) {
17 if ( self::$backendToUse ) {
18 $this->singleBackend = self::$backendToUse;
19 } else {
20 $name = $this->getCliArg( 'use-filebackend=' );
21 $useConfig = array();
22 foreach ( $wgFileBackends as $conf ) {
23 if ( $conf['name'] == $name ) {
24 $useConfig = $conf;
25 }
26 }
27 $useConfig['name'] = 'localtesting'; // swap name
28 $class = $conf['class'];
29 self::$backendToUse = new $class( $useConfig );
30 $this->singleBackend = self::$backendToUse;
31 }
32 } else {
33 $this->singleBackend = new FSFileBackend( array(
34 'name' => 'localtesting',
35 'lockManager' => 'fsLockManager',
36 'containerPaths' => array(
37 'unittest-cont1' => "{$tmpPrefix}-localtesting-cont1",
38 'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2" )
39 ) );
40 }
41 $this->multiBackend = new FileBackendMultiWrite( array(
42 'name' => 'localtesting',
43 'lockManager' => 'fsLockManager',
44 'backends' => array(
45 array(
46 'name' => 'localmutlitesting1',
47 'class' => 'FSFileBackend',
48 'lockManager' => 'nullLockManager',
49 'containerPaths' => array(
50 'unittest-cont1' => "{$tmpPrefix}-localtestingmulti1-cont1",
51 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti1-cont2" ),
52 'isMultiMaster' => false
53 ),
54 array(
55 'name' => 'localmutlitesting2',
56 'class' => 'FSFileBackend',
57 'lockManager' => 'nullLockManager',
58 'containerPaths' => array(
59 'unittest-cont1' => "{$tmpPrefix}-localtestingmulti2-cont1",
60 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti2-cont2" ),
61 'isMultiMaster' => true
62 )
63 )
64 ) );
65 $this->filesToPrune = array();
66 }
67
68 private function baseStorePath() {
69 return 'mwstore://localtesting';
70 }
71
72 private function backendClass() {
73 return get_class( $this->backend );
74 }
75
76 /**
77 * @dataProvider provider_testStore
78 */
79 public function testStore( $op ) {
80 $this->filesToPrune[] = $op['src'];
81
82 $this->backend = $this->singleBackend;
83 $this->tearDownFiles();
84 $this->doTestStore( $op );
85 $this->tearDownFiles();
86
87 $this->backend = $this->multiBackend;
88 $this->tearDownFiles();
89 $this->doTestStore( $op );
90 $this->tearDownFiles();
91 }
92
93 function doTestStore( $op ) {
94 $backendName = $this->backendClass();
95
96 $source = $op['src'];
97 $dest = $op['dst'];
98 $this->prepare( array( 'dir' => dirname( $dest ) ) );
99
100 file_put_contents( $source, "Unit test file" );
101
102 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
103 $this->backend->store( $op );
104 }
105
106 $status = $this->backend->doOperation( $op );
107
108 $this->assertEquals( array(), $status->errors,
109 "Store from $source to $dest succeeded without warnings ($backendName)." );
110 $this->assertEquals( array(), $status->errors,
111 "Store from $source to $dest succeeded ($backendName)." );
112 $this->assertEquals( array( 0 => true ), $status->success,
113 "Store from $source to $dest has proper 'success' field in Status ($backendName)." );
114 $this->assertEquals( true, file_exists( $source ),
115 "Source file $source still exists ($backendName)." );
116 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
117 "Destination file $dest exists ($backendName)." );
118
119 $this->assertEquals( filesize( $source ),
120 $this->backend->getFileSize( array( 'src' => $dest ) ),
121 "Destination file $dest has correct size ($backendName)." );
122
123 $props1 = FSFile::getPropsFromPath( $source );
124 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
125 $this->assertEquals( $props1, $props2,
126 "Source and destination have the same props ($backendName)." );
127 }
128
129 public function provider_testStore() {
130 $cases = array();
131
132 $tmpName = TempFSFile::factory( "unittests_", 'txt' )->getPath();
133 $toPath = $this->baseStorePath() . '/unittest-cont1/fun/obj1.txt';
134 $op = array( 'op' => 'store', 'src' => $tmpName, 'dst' => $toPath );
135 $cases[] = array(
136 $op, // operation
137 $tmpName, // source
138 $toPath, // dest
139 );
140
141 $op2 = $op;
142 $op2['overwrite'] = true;
143 $cases[] = array(
144 $op2, // operation
145 $tmpName, // source
146 $toPath, // dest
147 );
148
149 $op2 = $op;
150 $op2['overwriteSame'] = true;
151 $cases[] = array(
152 $op2, // operation
153 $tmpName, // source
154 $toPath, // dest
155 );
156
157 return $cases;
158 }
159
160 /**
161 * @dataProvider provider_testCopy
162 */
163 public function testCopy( $op ) {
164 $this->backend = $this->singleBackend;
165 $this->tearDownFiles();
166 $this->doTestCopy( $op );
167 $this->tearDownFiles();
168
169 $this->backend = $this->multiBackend;
170 $this->tearDownFiles();
171 $this->doTestCopy( $op );
172 $this->tearDownFiles();
173 }
174
175 function doTestCopy( $op ) {
176 $backendName = $this->backendClass();
177
178 $source = $op['src'];
179 $dest = $op['dst'];
180 $this->prepare( array( 'dir' => dirname( $source ) ) );
181 $this->prepare( array( 'dir' => dirname( $dest ) ) );
182
183 $status = $this->backend->doOperation(
184 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
185 $this->assertEquals( array(), $status->errors,
186 "Creation of file at $source succeeded ($backendName)." );
187
188 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
189 $this->backend->copy( $op );
190 }
191
192 $status = $this->backend->doOperation( $op );
193
194 $this->assertEquals( array(), $status->errors,
195 "Copy from $source to $dest succeeded without warnings ($backendName)." );
196 $this->assertEquals( true, $status->isOK(),
197 "Copy from $source to $dest succeeded ($backendName)." );
198 $this->assertEquals( array( 0 => true ), $status->success,
199 "Copy from $source to $dest has proper 'success' field in Status ($backendName)." );
200 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $source ) ),
201 "Source file $source still exists ($backendName)." );
202 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
203 "Destination file $dest exists after copy ($backendName)." );
204
205 $this->assertEquals(
206 $this->backend->getFileSize( array( 'src' => $source ) ),
207 $this->backend->getFileSize( array( 'src' => $dest ) ),
208 "Destination file $dest has correct size ($backendName)." );
209
210 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
211 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
212 $this->assertEquals( $props1, $props2,
213 "Source and destination have the same props ($backendName)." );
214 }
215
216 public function provider_testCopy() {
217 $cases = array();
218
219 $source = $this->baseStorePath() . '/unittest-cont1/file.txt';
220 $dest = $this->baseStorePath() . '/unittest-cont2/fileMoved.txt';
221
222 $op = array( 'op' => 'copy', 'src' => $source, 'dst' => $dest );
223 $cases[] = array(
224 $op, // operation
225 $source, // source
226 $dest, // dest
227 );
228
229 $op2 = $op;
230 $op2['overwrite'] = true;
231 $cases[] = array(
232 $op2, // operation
233 $source, // source
234 $dest, // dest
235 );
236
237 $op2 = $op;
238 $op2['overwriteSame'] = true;
239 $cases[] = array(
240 $op2, // operation
241 $source, // source
242 $dest, // dest
243 );
244
245 return $cases;
246 }
247
248 /**
249 * @dataProvider provider_testMove
250 */
251 public function testMove( $op ) {
252 $this->backend = $this->singleBackend;
253 $this->tearDownFiles();
254 $this->doTestMove( $op );
255 $this->tearDownFiles();
256
257 $this->backend = $this->multiBackend;
258 $this->tearDownFiles();
259 $this->doTestMove( $op );
260 $this->tearDownFiles();
261 }
262
263 private function doTestMove( $op ) {
264 $backendName = $this->backendClass();
265
266 $source = $op['src'];
267 $dest = $op['dst'];
268 $this->prepare( array( 'dir' => dirname( $source ) ) );
269 $this->prepare( array( 'dir' => dirname( $dest ) ) );
270
271 $status = $this->backend->doOperation(
272 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
273 $this->assertEquals( array(), $status->errors,
274 "Creation of file at $source succeeded ($backendName)." );
275
276 if ( isset( $op['overwrite'] ) || isset( $op['overwriteSame'] ) ) {
277 $this->backend->copy( $op );
278 }
279
280 $status = $this->backend->doOperation( $op );
281 $this->assertEquals( array(), $status->errors,
282 "Move from $source to $dest succeeded without warnings ($backendName)." );
283 $this->assertEquals( true, $status->isOK(),
284 "Move from $source to $dest succeeded ($backendName)." );
285 $this->assertEquals( array( 0 => true ), $status->success,
286 "Move from $source to $dest has proper 'success' field in Status ($backendName)." );
287 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
288 "Source file $source does not still exists ($backendName)." );
289 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
290 "Destination file $dest exists after move ($backendName)." );
291
292 $this->assertNotEquals(
293 $this->backend->getFileSize( array( 'src' => $source ) ),
294 $this->backend->getFileSize( array( 'src' => $dest ) ),
295 "Destination file $dest has correct size ($backendName)." );
296
297 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
298 $props2 = $this->backend->getFileProps( array( 'src' => $dest ) );
299 $this->assertEquals( false, $props1['fileExists'],
300 "Source file does not exist accourding to props ($backendName)." );
301 $this->assertEquals( true, $props2['fileExists'],
302 "Destination file exists accourding to props ($backendName)." );
303 }
304
305 public function provider_testMove() {
306 $cases = array();
307
308 $source = $this->baseStorePath() . '/unittest-cont1/file.txt';
309 $dest = $this->baseStorePath() . '/unittest-cont2/fileMoved.txt';
310
311 $op = array( 'op' => 'move', 'src' => $source, 'dst' => $dest );
312 $cases[] = array(
313 $op, // operation
314 $source, // source
315 $dest, // dest
316 );
317
318 $op2 = $op;
319 $op2['overwrite'] = true;
320 $cases[] = array(
321 $op2, // operation
322 $source, // source
323 $dest, // dest
324 );
325
326 $op2 = $op;
327 $op2['overwriteSame'] = true;
328 $cases[] = array(
329 $op2, // operation
330 $source, // source
331 $dest, // dest
332 );
333
334 return $cases;
335 }
336
337 /**
338 * @dataProvider provider_testDelete
339 */
340 public function testDelete( $op, $withSource, $okStatus ) {
341 $this->backend = $this->singleBackend;
342 $this->tearDownFiles();
343 $this->doTestDelete( $op, $withSource, $okStatus );
344 $this->tearDownFiles();
345
346 $this->backend = $this->multiBackend;
347 $this->tearDownFiles();
348 $this->doTestDelete( $op, $withSource, $okStatus );
349 $this->tearDownFiles();
350 }
351
352 private function doTestDelete( $op, $withSource, $okStatus ) {
353 $backendName = $this->backendClass();
354
355 $source = $op['src'];
356 $this->prepare( array( 'dir' => dirname( $source ) ) );
357
358 if ( $withSource ) {
359 $status = $this->backend->doOperation(
360 array( 'op' => 'create', 'content' => 'blahblah', 'dst' => $source ) );
361 $this->assertEquals( array(), $status->errors,
362 "Creation of file at $source succeeded ($backendName)." );
363 }
364
365 $status = $this->backend->doOperation( $op );
366 if ( $okStatus ) {
367 $this->assertEquals( array(), $status->errors,
368 "Deletion of file at $source succeeded without warnings ($backendName)." );
369 $this->assertEquals( true, $status->isOK(),
370 "Deletion of file at $source succeeded ($backendName)." );
371 $this->assertEquals( array( 0 => true ), $status->success,
372 "Deletion of file at $source has proper 'success' field in Status ($backendName)." );
373 } else {
374 $this->assertEquals( false, $status->isOK(),
375 "Deletion of file at $source failed ($backendName)." );
376 }
377
378 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $source ) ),
379 "Source file $source does not exist after move ($backendName)." );
380
381 $this->assertFalse(
382 $this->backend->getFileSize( array( 'src' => $source ) ),
383 "Source file $source has correct size (false) ($backendName)." );
384
385 $props1 = $this->backend->getFileProps( array( 'src' => $source ) );
386 $this->assertFalse( $props1['fileExists'],
387 "Source file $source does not exist according to props ($backendName)." );
388 }
389
390 public function provider_testDelete() {
391 $cases = array();
392
393 $source = $this->baseStorePath() . '/unittest-cont1/myfacefile.txt';
394
395 $op = array( 'op' => 'delete', 'src' => $source );
396 $cases[] = array(
397 $op, // operation
398 true, // with source
399 true // succeeds
400 );
401
402 $cases[] = array(
403 $op, // operation
404 false, // without source
405 false // fails
406 );
407
408 $op['ignoreMissingSource'] = true;
409 $cases[] = array(
410 $op, // operation
411 false, // without source
412 true // succeeds
413 );
414
415 return $cases;
416 }
417
418 /**
419 * @dataProvider provider_testCreate
420 */
421 public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
422 $this->backend = $this->singleBackend;
423 $this->tearDownFiles();
424 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
425 $this->tearDownFiles();
426
427 $this->backend = $this->multiBackend;
428 $this->tearDownFiles();
429 $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize );
430 $this->tearDownFiles();
431 }
432
433 private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) {
434 $backendName = $this->backendClass();
435
436 $dest = $op['dst'];
437 $this->prepare( array( 'dir' => dirname( $dest ) ) );
438
439 $oldText = 'blah...blah...waahwaah';
440 if ( $alreadyExists ) {
441 $status = $this->backend->doOperation(
442 array( 'op' => 'create', 'content' => $oldText, 'dst' => $dest ) );
443 $this->assertEquals( array(), $status->errors,
444 "Creation of file at $dest succeeded ($backendName)." );
445 }
446
447 $status = $this->backend->doOperation( $op );
448 if ( $okStatus ) {
449 $this->assertEquals( array(), $status->errors,
450 "Creation of file at $dest succeeded without warnings ($backendName)." );
451 $this->assertEquals( true, $status->isOK(),
452 "Creation of file at $dest succeeded ($backendName)." );
453 $this->assertEquals( array( 0 => true ), $status->success,
454 "Creation of file at $dest has proper 'success' field in Status ($backendName)." );
455 } else {
456 $this->assertEquals( false, $status->isOK(),
457 "Creation of file at $dest failed ($backendName)." );
458 }
459
460 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $dest ) ),
461 "Destination file $dest exists after creation ($backendName)." );
462
463 $props1 = $this->backend->getFileProps( array( 'src' => $dest ) );
464 $this->assertEquals( true, $props1['fileExists'],
465 "Destination file $dest exists according to props ($backendName)." );
466 if ( $okStatus ) { // file content is what we saved
467 $this->assertEquals( $newSize, $props1['size'],
468 "Destination file $dest has expected size according to props ($backendName)." );
469 $this->assertEquals( $newSize,
470 $this->backend->getFileSize( array( 'src' => $dest ) ),
471 "Destination file $dest has correct size ($backendName)." );
472 } else { // file content is some other previous text
473 $this->assertEquals( strlen( $oldText ), $props1['size'],
474 "Destination file $dest has original size according to props ($backendName)." );
475 $this->assertEquals( strlen( $oldText ),
476 $this->backend->getFileSize( array( 'src' => $dest ) ),
477 "Destination file $dest has original size according to props ($backendName)." );
478 }
479 }
480
481 /**
482 * @dataProvider provider_testCreate
483 */
484 public function provider_testCreate() {
485 $cases = array();
486
487 $dest = $this->baseStorePath() . '/unittest-cont2/myspacefile.txt';
488
489 $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest );
490 $cases[] = array(
491 $op, // operation
492 false, // no dest already exists
493 true, // succeeds
494 strlen( $op['content'] )
495 );
496
497 $op2 = $op;
498 $op2['content'] = "\n";
499 $cases[] = array(
500 $op2, // operation
501 false, // no dest already exists
502 true, // succeeds
503 strlen( $op2['content'] )
504 );
505
506 $op2 = $op;
507 $op2['content'] = "fsf\n waf 3kt";
508 $cases[] = array(
509 $op2, // operation
510 true, // dest already exists
511 false, // fails
512 strlen( $op2['content'] )
513 );
514
515 $op2 = $op;
516 $op2['content'] = "egm'g gkpe gpqg eqwgwqg";
517 $op2['overwrite'] = true;
518 $cases[] = array(
519 $op2, // operation
520 true, // dest already exists
521 true, // succeeds
522 strlen( $op2['content'] )
523 );
524
525 $op2 = $op;
526 $op2['content'] = "39qjmg3-qg";
527 $op2['overwriteSame'] = true;
528 $cases[] = array(
529 $op2, // operation
530 true, // dest already exists
531 false, // succeeds
532 strlen( $op2['content'] )
533 );
534
535 return $cases;
536 }
537
538 /**
539 * @dataProvider provider_testConcatenate
540 */
541 public function testConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
542 $this->filesToPrune[] = $op['dst'];
543
544 $this->backend = $this->singleBackend;
545 $this->tearDownFiles();
546 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
547 $this->tearDownFiles();
548
549 $this->backend = $this->multiBackend;
550 $this->tearDownFiles();
551 $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus );
552 $this->tearDownFiles();
553 }
554
555 public function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) {
556 $backendName = $this->backendClass();
557
558 $expContent = '';
559 // Create sources
560 $ops = array();
561 foreach ( $srcs as $i => $source ) {
562 $this->prepare( array( 'dir' => dirname( $source ) ) );
563 $ops[] = array(
564 'op' => 'create', // operation
565 'dst' => $source, // source
566 'content' => $srcsContent[$i]
567 );
568 $expContent .= $srcsContent[$i];
569 }
570 $status = $this->backend->doOperations( $ops );
571
572 $this->assertEquals( array(), $status->errors,
573 "Creation of source files succeeded ($backendName)." );
574
575 $dest = $params['dst'];
576 if ( $alreadyExists ) {
577 $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false;
578 $this->assertEquals( true, $ok,
579 "Creation of file at $dest succeeded ($backendName)." );
580 } else {
581 $ok = file_put_contents( $dest, '' ) !== false;
582 $this->assertEquals( true, $ok,
583 "Creation of 0-byte file at $dest succeeded ($backendName)." );
584 }
585
586 // Combine the files into one
587 $status = $this->backend->concatenate( $params );
588 if ( $okStatus ) {
589 $this->assertEquals( array(), $status->errors,
590 "Creation of concat file at $dest succeeded without warnings ($backendName)." );
591 $this->assertEquals( true, $status->isOK(),
592 "Creation of concat file at $dest succeeded ($backendName)." );
593 } else {
594 $this->assertEquals( false, $status->isOK(),
595 "Creation of concat file at $dest failed ($backendName)." );
596 }
597
598 if ( $okStatus ) {
599 $this->assertEquals( true, is_file( $dest ),
600 "Dest concat file $dest exists after creation ($backendName)." );
601 } else {
602 $this->assertEquals( true, is_file( $dest ),
603 "Dest concat file $dest exists after failed creation ($backendName)." );
604 }
605
606 $contents = file_get_contents( $dest );
607 $this->assertNotEquals( false, $contents, "File at $dest exists ($backendName)." );
608
609 if ( $okStatus ) {
610 $this->assertEquals( $expContent, $contents,
611 "Concat file at $dest has correct contents ($backendName)." );
612 } else {
613 $this->assertNotEquals( $expContent, $contents,
614 "Concat file at $dest has correct contents ($backendName)." );
615 }
616 }
617
618 function provider_testConcatenate() {
619 $cases = array();
620
621 $rand = mt_rand( 0, 2000000000 ) . time();
622 $dest = wfTempDir() . "/randomfile!$rand.txt";
623 $srcs = array(
624 $this->baseStorePath() . '/unittest-cont1/file1.txt',
625 $this->baseStorePath() . '/unittest-cont1/file2.txt',
626 $this->baseStorePath() . '/unittest-cont1/file3.txt',
627 $this->baseStorePath() . '/unittest-cont1/file4.txt',
628 $this->baseStorePath() . '/unittest-cont1/file5.txt',
629 $this->baseStorePath() . '/unittest-cont1/file6.txt',
630 $this->baseStorePath() . '/unittest-cont1/file7.txt',
631 $this->baseStorePath() . '/unittest-cont1/file8.txt',
632 $this->baseStorePath() . '/unittest-cont1/file9.txt',
633 $this->baseStorePath() . '/unittest-cont1/file10.txt'
634 );
635 $content = array(
636 'egfage',
637 'ageageag',
638 'rhokohlr',
639 'shgmslkg',
640 'kenga',
641 'owagmal',
642 'kgmae',
643 'g eak;g',
644 'lkaem;a',
645 'legma'
646 );
647 $params = array( 'srcs' => $srcs, 'dst' => $dest );
648
649 $cases[] = array(
650 $params, // operation
651 $srcs, // sources
652 $content, // content for each source
653 false, // no dest already exists
654 true, // succeeds
655 );
656
657 $cases[] = array(
658 $params, // operation
659 $srcs, // sources
660 $content, // content for each source
661 true, // dest already exists
662 false, // succeeds
663 );
664
665 return $cases;
666 }
667
668 /**
669 * @dataProvider provider_testGetFileContents
670 */
671 public function testGetFileContents( $source, $content ) {
672 $this->backend = $this->singleBackend;
673 $this->tearDownFiles();
674 $this->doTestGetFileContents( $source, $content );
675 $this->tearDownFiles();
676
677 $this->backend = $this->multiBackend;
678 $this->tearDownFiles();
679 $this->doTestGetFileContents( $source, $content );
680 $this->tearDownFiles();
681 }
682
683 /**
684 * @dataProvider provider_testGetFileContents
685 */
686 public function doTestGetFileContents( $source, $content ) {
687 $backendName = $this->backendClass();
688
689 $this->prepare( array( 'dir' => dirname( $source ) ) );
690
691 $status = $this->backend->doOperation(
692 array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
693 $this->assertEquals( array(), $status->errors,
694 "Creation of file at $source succeeded ($backendName)." );
695 $this->assertEquals( true, $status->isOK(),
696 "Creation of file at $source succeeded with OK status ($backendName)." );
697
698 $newContents = $this->backend->getFileContents( array( 'src' => $source, 'latest' => 1 ) );
699 $this->assertNotEquals( false, $newContents,
700 "Read of file at $source succeeded ($backendName)." );
701
702 $this->assertEquals( $content, $newContents,
703 "Contents read match data at $source ($backendName)." );
704 }
705
706 function provider_testGetFileContents() {
707 $cases = array();
708
709 $base = $this->baseStorePath();
710 $cases[] = array( "$base/unittest-cont1/b/z/some_file.txt", "some file contents" );
711 $cases[] = array( "$base/unittest-cont1/b/some-other_file.txt", "more file contents" );
712
713 return $cases;
714 }
715
716 /**
717 * @dataProvider provider_testGetLocalCopy
718 */
719 public function testGetLocalCopy( $source, $content ) {
720 $this->backend = $this->singleBackend;
721 $this->tearDownFiles();
722 $this->doTestGetLocalCopy( $source, $content );
723 $this->tearDownFiles();
724
725 $this->backend = $this->multiBackend;
726 $this->tearDownFiles();
727 $this->doTestGetLocalCopy( $source, $content );
728 $this->tearDownFiles();
729 }
730
731 public function doTestGetLocalCopy( $source, $content ) {
732 $backendName = $this->backendClass();
733
734 $this->prepare( array( 'dir' => dirname( $source ) ) );
735
736 $status = $this->backend->doOperation(
737 array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
738 $this->assertEquals( array(), $status->errors,
739 "Creation of file at $source succeeded ($backendName)." );
740
741 $tmpFile = $this->backend->getLocalCopy( array( 'src' => $source ) );
742 $this->assertNotNull( $tmpFile,
743 "Creation of local copy of $source succeeded ($backendName)." );
744
745 $contents = file_get_contents( $tmpFile->getPath() );
746 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
747 }
748
749 function provider_testGetLocalCopy() {
750 $cases = array();
751
752 $base = $this->baseStorePath();
753 $cases[] = array( "$base/unittest-cont1/a/z/some_file.txt", "some file contents" );
754 $cases[] = array( "$base/unittest-cont1/a/some-other_file.txt", "more file contents" );
755
756 return $cases;
757 }
758
759 /**
760 * @dataProvider provider_testGetLocalReference
761 */
762 public function testGetLocalReference( $source, $content ) {
763 $this->backend = $this->singleBackend;
764 $this->tearDownFiles();
765 $this->doTestGetLocalReference( $source, $content );
766 $this->tearDownFiles();
767
768 $this->backend = $this->multiBackend;
769 $this->tearDownFiles();
770 $this->doTestGetLocalReference( $source, $content );
771 $this->tearDownFiles();
772 }
773
774 private function doTestGetLocalReference( $source, $content ) {
775 $backendName = $this->backendClass();
776
777 $this->prepare( array( 'dir' => dirname( $source ) ) );
778
779 $status = $this->backend->doOperation(
780 array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
781 $this->assertEquals( array(), $status->errors,
782 "Creation of file at $source succeeded ($backendName)." );
783
784 $tmpFile = $this->backend->getLocalReference( array( 'src' => $source ) );
785 $this->assertNotNull( $tmpFile,
786 "Creation of local copy of $source succeeded ($backendName)." );
787
788 $contents = file_get_contents( $tmpFile->getPath() );
789 $this->assertNotEquals( false, $contents, "Local copy of $source exists ($backendName)." );
790 }
791
792 function provider_testGetLocalReference() {
793 $cases = array();
794
795 $base = $this->baseStorePath();
796 $cases[] = array( "$base/unittest-cont1/a/z/some_file.txt", "some file contents" );
797 $cases[] = array( "$base/unittest-cont1/a/some-other_file.txt", "more file contents" );
798
799 return $cases;
800 }
801
802 /**
803 * @dataProvider provider_testPrepareAndClean
804 */
805 public function testPrepareAndClean( $path, $isOK ) {
806 $this->backend = $this->singleBackend;
807 $this->doTestPrepareAndClean( $path, $isOK );
808 $this->tearDownFiles();
809
810 $this->backend = $this->multiBackend;
811 $this->doTestPrepareAndClean( $path, $isOK );
812 $this->tearDownFiles();
813 }
814
815 function provider_testPrepareAndClean() {
816 $base = $this->baseStorePath();
817 return array(
818 array( "$base/unittest-cont1/a/z/some_file1.txt", true ),
819 array( "$base/unittest-cont2/a/z/some_file2.txt", true ),
820 # Specific to FS backend with no basePath field set
821 #array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
822 );
823 }
824
825 function doTestPrepareAndClean( $path, $isOK ) {
826 $backendName = $this->backendClass();
827
828 $status = $this->prepare( array( 'dir' => dirname( $path ) ) );
829 if ( $isOK ) {
830 $this->assertEquals( array(), $status->errors,
831 "Preparing dir $path succeeded without warnings ($backendName)." );
832 $this->assertEquals( true, $status->isOK(),
833 "Preparing dir $path succeeded ($backendName)." );
834 } else {
835 $this->assertEquals( false, $status->isOK(),
836 "Preparing dir $path failed ($backendName)." );
837 }
838
839 $status = $this->backend->clean( array( 'dir' => dirname( $path ) ) );
840 if ( $isOK ) {
841 $this->assertEquals( array(), $status->errors,
842 "Cleaning dir $path succeeded without warnings ($backendName)." );
843 $this->assertEquals( true, $status->isOK(),
844 "Cleaning dir $path succeeded ($backendName)." );
845 } else {
846 $this->assertEquals( false, $status->isOK(),
847 "Cleaning dir $path failed ($backendName)." );
848 }
849 }
850
851 // @TODO: testSecure
852
853 public function testDoOperations() {
854 $this->backend = $this->singleBackend;
855 $this->tearDownFiles();
856 $this->doTestDoOperations();
857 $this->tearDownFiles();
858
859 $this->backend = $this->multiBackend;
860 $this->tearDownFiles();
861 $this->doTestDoOperations();
862 $this->tearDownFiles();
863
864 // @TODO: test some cases where the ops should fail
865 }
866
867 function doTestDoOperations() {
868 $base = $this->baseStorePath();
869
870 $fileA = "$base/unittest-cont1/a/b/fileA.txt";
871 $fileAContents = '3tqtmoeatmn4wg4qe-mg3qt3 tq';
872 $fileB = "$base/unittest-cont1/a/b/fileB.txt";
873 $fileBContents = 'g-jmq3gpqgt3qtg q3GT ';
874 $fileC = "$base/unittest-cont1/a/b/fileC.txt";
875 $fileCContents = 'eigna[ogmewt 3qt g3qg flew[ag';
876 $fileD = "$base/unittest-cont1/a/b/fileD.txt";
877
878 $this->prepare( array( 'dir' => dirname( $fileA ) ) );
879 $this->backend->create( array( 'dst' => $fileA, 'content' => $fileAContents ) );
880 $this->prepare( array( 'dir' => dirname( $fileB ) ) );
881 $this->backend->create( array( 'dst' => $fileB, 'content' => $fileBContents ) );
882 $this->prepare( array( 'dir' => dirname( $fileC ) ) );
883 $this->backend->create( array( 'dst' => $fileC, 'content' => $fileCContents ) );
884
885 $status = $this->backend->doOperations( array(
886 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC, 'overwrite' => 1 ),
887 // Now: A:<A>, B:<B>, C:<A>, D:<D> (file:<orginal contents>)
888 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileA, 'overwriteSame' => 1 ),
889 // Now: A:<A>, B:<B>, C:<A>, D:<D>
890 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileD, 'overwrite' => 1 ),
891 // Now: A:<A>, B:<B>, C:<empty>, D:<A>
892 array( 'op' => 'move', 'src' => $fileB, 'dst' => $fileC ),
893 // Now: A:<A>, B:<empty>, C:<B>, D:<A>
894 array( 'op' => 'move', 'src' => $fileD, 'dst' => $fileA, 'overwriteSame' => 1 ),
895 // Now: A:<A>, B:<empty>, C:<B>, D:<empty>
896 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileA, 'overwrite' => 1 ),
897 // Now: A:<B>, B:<empty>, C:<empty>, D:<empty>
898 array( 'op' => 'copy', 'src' => $fileA, 'dst' => $fileC ),
899 // Now: A:<B>, B:<empty>, C:<B>, D:<empty>
900 array( 'op' => 'move', 'src' => $fileA, 'dst' => $fileC, 'overwriteSame' => 1 ),
901 // Now: A:<empty>, B:<empty>, C:<B>, D:<empty>
902 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
903 // Does nothing
904 array( 'op' => 'copy', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
905 // Does nothing
906 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwrite' => 1 ),
907 // Does nothing
908 array( 'op' => 'move', 'src' => $fileC, 'dst' => $fileC, 'overwriteSame' => 1 ),
909 // Does nothing
910 array( 'op' => 'null' ),
911 // Does nothing
912 ) );
913
914 $this->assertEquals( array(), $status->errors, "Operation batch succeeded" );
915 $this->assertEquals( true, $status->isOK(), "Operation batch succeeded" );
916 $this->assertEquals( 13, count( $status->success ),
917 "Operation batch has correct success array" );
918
919 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileA ) ),
920 "File does not exist at $fileA" );
921 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileB ) ),
922 "File does not exist at $fileB" );
923 $this->assertEquals( false, $this->backend->fileExists( array( 'src' => $fileD ) ),
924 "File does not exist at $fileD" );
925
926 $this->assertEquals( true, $this->backend->fileExists( array( 'src' => $fileC ) ),
927 "File exists at $fileC" );
928 $this->assertEquals( $fileBContents,
929 $this->backend->getFileContents( array( 'src' => $fileC ) ),
930 "Correct file contents of $fileC" );
931 $this->assertEquals( strlen( $fileBContents ),
932 $this->backend->getFileSize( array( 'src' => $fileC ) ),
933 "Correct file size of $fileC" );
934 $this->assertEquals( wfBaseConvert( sha1( $fileBContents ), 16, 36, 31 ),
935 $this->backend->getFileSha1Base36( array( 'src' => $fileC ) ),
936 "Correct file SHA-1 of $fileC" );
937 }
938
939 public function testGetFileList() {
940 $this->backend = $this->singleBackend;
941 $this->tearDownFiles();
942 $this->doTestGetFileList();
943 $this->tearDownFiles();
944
945 $this->backend = $this->multiBackend;
946 $this->tearDownFiles();
947 $this->doTestGetFileList();
948 $this->tearDownFiles();
949 }
950
951 private function doTestGetFileList() {
952 $backendName = $this->backendClass();
953
954 $base = $this->baseStorePath();
955 $files = array(
956 "$base/unittest-cont1/test1.txt",
957 "$base/unittest-cont1/test2.txt",
958 "$base/unittest-cont1/test3.txt",
959 "$base/unittest-cont1/subdir1/test1.txt",
960 "$base/unittest-cont1/subdir1/test2.txt",
961 "$base/unittest-cont1/subdir2/test3.txt",
962 "$base/unittest-cont1/subdir2/test4.txt",
963 "$base/unittest-cont1/subdir2/subdir/test1.txt",
964 "$base/unittest-cont1/subdir2/subdir/test2.txt",
965 "$base/unittest-cont1/subdir2/subdir/test3.txt",
966 "$base/unittest-cont1/subdir2/subdir/test4.txt",
967 "$base/unittest-cont1/subdir2/subdir/test5.txt",
968 "$base/unittest-cont1/subdir2/subdir/sub/test0.txt",
969 "$base/unittest-cont1/subdir2/subdir/sub/120-px-file.txt",
970 );
971
972 // Add the files
973 $ops = array();
974 foreach ( $files as $file ) {
975 $this->prepare( array( 'dir' => dirname( $file ) ) );
976 $ops[] = array( 'op' => 'create', 'content' => 'xxy', 'dst' => $file );
977 }
978 $status = $this->backend->doOperations( $ops );
979 $this->assertEquals( array(), $status->errors,
980 "Creation of files succeeded ($backendName)." );
981 $this->assertEquals( true, $status->isOK(),
982 "Creation of files succeeded with OK status ($backendName)." );
983
984 // Expected listing
985 $expected = array(
986 "test1.txt",
987 "test2.txt",
988 "test3.txt",
989 "subdir1/test1.txt",
990 "subdir1/test2.txt",
991 "subdir2/test3.txt",
992 "subdir2/test4.txt",
993 "subdir2/subdir/test1.txt",
994 "subdir2/subdir/test2.txt",
995 "subdir2/subdir/test3.txt",
996 "subdir2/subdir/test4.txt",
997 "subdir2/subdir/test5.txt",
998 "subdir2/subdir/sub/test0.txt",
999 "subdir2/subdir/sub/120-px-file.txt",
1000 );
1001 sort( $expected );
1002
1003 // Actual listing (no trailing slash)
1004 $list = array();
1005 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1" ) );
1006 foreach ( $iter as $file ) {
1007 $list[] = $file;
1008 }
1009 sort( $list );
1010
1011 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1012
1013 // Actual listing (with trailing slash)
1014 $list = array();
1015 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/" ) );
1016 foreach ( $iter as $file ) {
1017 $list[] = $file;
1018 }
1019 sort( $list );
1020
1021 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1022
1023 // Expected listing
1024 $expected = array(
1025 "test1.txt",
1026 "test2.txt",
1027 "test3.txt",
1028 "test4.txt",
1029 "test5.txt",
1030 "sub/test0.txt",
1031 "sub/120-px-file.txt",
1032 );
1033 sort( $expected );
1034
1035 // Actual listing (no trailing slash)
1036 $list = array();
1037 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/subdir2/subdir" ) );
1038 foreach ( $iter as $file ) {
1039 $list[] = $file;
1040 }
1041 sort( $list );
1042
1043 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1044
1045 // Actual listing (with trailing slash)
1046 $list = array();
1047 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/subdir2/subdir/" ) );
1048 foreach ( $iter as $file ) {
1049 $list[] = $file;
1050 }
1051 sort( $list );
1052
1053 $this->assertEquals( $expected, $list, "Correct file listing ($backendName)." );
1054
1055 foreach ( $files as $file ) { // clean up
1056 $this->backend->doOperation( array( 'op' => 'delete', 'src' => $file ) );
1057 }
1058
1059 $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont1/not/exists" ) );
1060 foreach ( $iter as $iter ) {} // no errors
1061 }
1062
1063 private function prepare( array $params ) {
1064 $this->dirsToPrune[] = $params['dir'];
1065 return $this->backend->prepare( $params );
1066 }
1067
1068 function tearDownFiles() {
1069 foreach ( $this->filesToPrune as $file ) {
1070 @unlink( $file );
1071 }
1072 $containers = array( 'unittest-cont1', 'unittest-cont2', 'unittest-cont3' );
1073 foreach ( $containers as $container ) {
1074 $this->deleteFiles( $container );
1075 }
1076 foreach ( $this->dirsToPrune as $dir ) {
1077 $this->recursiveClean( $dir );
1078 }
1079 $this->filesToPrune = $this->dirsToPrune = array();
1080 }
1081
1082 private function deleteFiles( $container ) {
1083 $base = $this->baseStorePath();
1084 $iter = $this->backend->getFileList( array( 'dir' => "$base/$container" ) );
1085 if ( $iter ) {
1086 foreach ( $iter as $file ) {
1087 $this->backend->delete( array( 'src' => "$base/$container/$file" ), array( 'force' => 1 ) );
1088 }
1089 }
1090 }
1091
1092 private function recursiveClean( $dir ) {
1093 do {
1094 if ( !$this->backend->clean( array( 'dir' => $dir ) )->isOK() ) {
1095 break;
1096 }
1097 } while ( $dir = FileBackend::parentStoragePath( $dir ) );
1098 }
1099
1100 function tearDown() {
1101 parent::tearDown();
1102 }
1103 }