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