3 class FileTest
extends MediaWikiMediaTestCase
{
6 * @param string $filename
7 * @param bool $expected
8 * @dataProvider providerCanAnimate
10 function testCanAnimateThumbIfAppropriate( $filename, $expected ) {
11 $this->setMwGlobals( 'wgMaxAnimatedGifArea', 9000 );
12 $file = $this->dataFile( $filename );
13 $this->assertEquals( $file->canAnimateThumbIfAppropriate(), $expected );
16 function providerCanAnimate() {
18 [ 'nonanimated.gif', true ],
19 [ 'jpeg-comment-utf.jpg', true ],
20 [ 'test.tiff', true ],
21 [ 'Animated_PNG_example_bouncing_beach_ball.png', false ],
22 [ 'greyscale-png.png', true ],
23 [ 'Toll_Texas_1.svg', true ],
24 [ 'LoremIpsum.djvu', true ],
25 [ '80x60-2layers.xcf', true ],
26 [ 'Soccer_ball_animated.svg', false ],
27 [ 'Bishzilla_blink.gif', false ],
28 [ 'animated.gif', true ],
33 * @dataProvider getThumbnailBucketProvider
34 * @covers File::getThumbnailBucket
36 public function testGetThumbnailBucket( $data ) {
37 $this->setMwGlobals( 'wgThumbnailBuckets', $data['buckets'] );
38 $this->setMwGlobals( 'wgThumbnailMinimumBucketDistance', $data['minimumBucketDistance'] );
40 $fileMock = $this->getMockBuilder( 'File' )
41 ->setConstructorArgs( [ 'fileMock', false ] )
42 ->setMethods( [ 'getWidth' ] )
43 ->getMockForAbstractClass();
45 $fileMock->expects( $this->any() )
46 ->method( 'getWidth' )
47 ->will( $this->returnValue( $data['width'] ) );
50 $data['expectedBucket'],
51 $fileMock->getThumbnailBucket( $data['requestedWidth'] ),
55 public function getThumbnailBucketProvider() {
56 $defaultBuckets = [ 256, 512, 1024, 2048, 4096 ];
60 'buckets' => $defaultBuckets,
61 'minimumBucketDistance' => 0,
63 'requestedWidth' => 120,
64 'expectedBucket' => 256,
65 'message' => 'Picking bucket bigger than requested size'
68 'buckets' => $defaultBuckets,
69 'minimumBucketDistance' => 0,
71 'requestedWidth' => 300,
72 'expectedBucket' => 512,
73 'message' => 'Picking bucket bigger than requested size'
76 'buckets' => $defaultBuckets,
77 'minimumBucketDistance' => 0,
79 'requestedWidth' => 1024,
80 'expectedBucket' => 2048,
81 'message' => 'Picking bucket bigger than requested size'
84 'buckets' => $defaultBuckets,
85 'minimumBucketDistance' => 0,
87 'requestedWidth' => 2048,
88 'expectedBucket' => false,
89 'message' => 'Picking no bucket because none is bigger than the requested size'
92 'buckets' => $defaultBuckets,
93 'minimumBucketDistance' => 0,
95 'requestedWidth' => 3500,
96 'expectedBucket' => false,
97 'message' => 'Picking no bucket because requested size is bigger than original'
100 'buckets' => [ 1024 ],
101 'minimumBucketDistance' => 0,
103 'requestedWidth' => 1024,
104 'expectedBucket' => false,
105 'message' => 'Picking no bucket because requested size equals biggest bucket'
109 'minimumBucketDistance' => 0,
111 'requestedWidth' => 1024,
112 'expectedBucket' => false,
113 'message' => 'Picking no bucket because no buckets have been specified'
116 'buckets' => [ 256, 512 ],
117 'minimumBucketDistance' => 10,
119 'requestedWidth' => 245,
120 'expectedBucket' => 256,
121 'message' => 'Requested width is distant enough from next bucket for it to be picked'
124 'buckets' => [ 256, 512 ],
125 'minimumBucketDistance' => 10,
127 'requestedWidth' => 246,
128 'expectedBucket' => 512,
129 'message' => 'Requested width is too close to next bucket, picking next one'
135 * @dataProvider getThumbnailSourceProvider
136 * @covers File::getThumbnailSource
138 public function testGetThumbnailSource( $data ) {
139 $backendMock = $this->getMockBuilder( 'FSFileBackend' )
140 ->setConstructorArgs( [ [ 'name' => 'backendMock', 'wikiId' => wfWikiID() ] ] )
143 $repoMock = $this->getMockBuilder( 'FileRepo' )
144 ->setConstructorArgs( [ [ 'name' => 'repoMock', 'backend' => $backendMock ] ] )
145 ->setMethods( [ 'fileExists', 'getLocalReference' ] )
148 $fsFile = new FSFile( 'fsFilePath' );
150 $repoMock->expects( $this->any() )
151 ->method( 'fileExists' )
152 ->will( $this->returnValue( true ) );
154 $repoMock->expects( $this->any() )
155 ->method( 'getLocalReference' )
156 ->will( $this->returnValue( $fsFile ) );
158 $handlerMock = $this->getMock( 'BitmapHandler', [ 'supportsBucketing' ] );
159 $handlerMock->expects( $this->any() )
160 ->method( 'supportsBucketing' )
161 ->will( $this->returnValue( $data['supportsBucketing'] ) );
163 $fileMock = $this->getMockBuilder( 'File' )
164 ->setConstructorArgs( [ 'fileMock', $repoMock ] )
165 ->setMethods( [ 'getThumbnailBucket', 'getLocalRefPath', 'getHandler' ] )
166 ->getMockForAbstractClass();
168 $fileMock->expects( $this->any() )
169 ->method( 'getThumbnailBucket' )
170 ->will( $this->returnValue( $data['thumbnailBucket'] ) );
172 $fileMock->expects( $this->any() )
173 ->method( 'getLocalRefPath' )
174 ->will( $this->returnValue( 'localRefPath' ) );
176 $fileMock->expects( $this->any() )
177 ->method( 'getHandler' )
178 ->will( $this->returnValue( $handlerMock ) );
180 $reflection = new ReflectionClass( $fileMock );
181 $reflection_property = $reflection->getProperty( 'handler' );
182 $reflection_property->setAccessible( true );
183 $reflection_property->setValue( $fileMock, $handlerMock );
185 if ( !is_null( $data['tmpBucketedThumbCache'] ) ) {
186 $reflection_property = $reflection->getProperty( 'tmpBucketedThumbCache' );
187 $reflection_property->setAccessible( true );
188 $reflection_property->setValue( $fileMock, $data['tmpBucketedThumbCache'] );
191 $result = $fileMock->getThumbnailSource(
192 [ 'physicalWidth' => $data['physicalWidth'] ] );
194 $this->assertEquals( $data['expectedPath'], $result['path'], $data['message'] );
197 public function getThumbnailSourceProvider() {
200 'supportsBucketing' => true,
201 'tmpBucketedThumbCache' => null,
202 'thumbnailBucket' => 1024,
203 'physicalWidth' => 2048,
204 'expectedPath' => 'fsFilePath',
205 'message' => 'Path downloaded from storage'
208 'supportsBucketing' => true,
209 'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' . rand() ],
210 'thumbnailBucket' => 1024,
211 'physicalWidth' => 2048,
212 'expectedPath' => 'fsFilePath',
213 'message' => 'Path downloaded from storage because temp file is missing'
216 'supportsBucketing' => true,
217 'tmpBucketedThumbCache' => [ 1024 => '/tmp' ],
218 'thumbnailBucket' => 1024,
219 'physicalWidth' => 2048,
220 'expectedPath' => '/tmp',
221 'message' => 'Temporary path because temp file was found'
224 'supportsBucketing' => false,
225 'tmpBucketedThumbCache' => null,
226 'thumbnailBucket' => 1024,
227 'physicalWidth' => 2048,
228 'expectedPath' => 'localRefPath',
229 'message' => 'Original file path because bucketing is unsupported by handler'
232 'supportsBucketing' => true,
233 'tmpBucketedThumbCache' => null,
234 'thumbnailBucket' => false,
235 'physicalWidth' => 2048,
236 'expectedPath' => 'localRefPath',
237 'message' => 'Original file path because no width provided'
243 * @dataProvider generateBucketsIfNeededProvider
244 * @covers File::generateBucketsIfNeeded
246 public function testGenerateBucketsIfNeeded( $data ) {
247 $this->setMwGlobals( 'wgThumbnailBuckets', $data['buckets'] );
249 $backendMock = $this->getMockBuilder( 'FSFileBackend' )
250 ->setConstructorArgs( [ [ 'name' => 'backendMock', 'wikiId' => wfWikiID() ] ] )
253 $repoMock = $this->getMockBuilder( 'FileRepo' )
254 ->setConstructorArgs( [ [ 'name' => 'repoMock', 'backend' => $backendMock ] ] )
255 ->setMethods( [ 'fileExists', 'getLocalReference' ] )
258 $fileMock = $this->getMockBuilder( 'File' )
259 ->setConstructorArgs( [ 'fileMock', $repoMock ] )
260 ->setMethods( [ 'getWidth', 'getBucketThumbPath', 'makeTransformTmpFile',
261 'generateAndSaveThumb', 'getHandler' ] )
262 ->getMockForAbstractClass();
264 $handlerMock = $this->getMock( 'JpegHandler', [ 'supportsBucketing' ] );
265 $handlerMock->expects( $this->any() )
266 ->method( 'supportsBucketing' )
267 ->will( $this->returnValue( true ) );
269 $fileMock->expects( $this->any() )
270 ->method( 'getHandler' )
271 ->will( $this->returnValue( $handlerMock ) );
273 $reflectionMethod = new ReflectionMethod( 'File', 'generateBucketsIfNeeded' );
274 $reflectionMethod->setAccessible( true );
276 $fileMock->expects( $this->any() )
277 ->method( 'getWidth' )
278 ->will( $this->returnValue( $data['width'] ) );
280 $fileMock->expects( $data['expectedGetBucketThumbPathCalls'] )
281 ->method( 'getBucketThumbPath' );
283 $repoMock->expects( $data['expectedFileExistsCalls'] )
284 ->method( 'fileExists' )
285 ->will( $this->returnValue( $data['fileExistsReturn'] ) );
287 $fileMock->expects( $data['expectedMakeTransformTmpFile'] )
288 ->method( 'makeTransformTmpFile' )
289 ->will( $this->returnValue( $data['makeTransformTmpFileReturn'] ) );
291 $fileMock->expects( $data['expectedGenerateAndSaveThumb'] )
292 ->method( 'generateAndSaveThumb' )
293 ->will( $this->returnValue( $data['generateAndSaveThumbReturn'] ) );
295 $this->assertEquals( $data['expectedResult'],
296 $reflectionMethod->invoke(
299 'physicalWidth' => $data['physicalWidth'],
300 'physicalHeight' => $data['physicalHeight'] ]
305 public function generateBucketsIfNeededProvider() {
306 $defaultBuckets = [ 256, 512, 1024, 2048, 4096 ];
310 'buckets' => $defaultBuckets,
312 'physicalWidth' => 256,
313 'physicalHeight' => 100,
314 'expectedGetBucketThumbPathCalls' => $this->never(),
315 'expectedFileExistsCalls' => $this->never(),
316 'fileExistsReturn' => null,
317 'expectedMakeTransformTmpFile' => $this->never(),
318 'makeTransformTmpFileReturn' => false,
319 'expectedGenerateAndSaveThumb' => $this->never(),
320 'generateAndSaveThumbReturn' => false,
321 'expectedResult' => false,
322 'message' => 'No bucket found, nothing to generate'
325 'buckets' => $defaultBuckets,
327 'physicalWidth' => 300,
328 'physicalHeight' => 200,
329 'expectedGetBucketThumbPathCalls' => $this->once(),
330 'expectedFileExistsCalls' => $this->once(),
331 'fileExistsReturn' => true,
332 'expectedMakeTransformTmpFile' => $this->never(),
333 'makeTransformTmpFileReturn' => false,
334 'expectedGenerateAndSaveThumb' => $this->never(),
335 'generateAndSaveThumbReturn' => false,
336 'expectedResult' => false,
337 'message' => 'File already exists, no reason to generate buckets'
340 'buckets' => $defaultBuckets,
342 'physicalWidth' => 300,
343 'physicalHeight' => 200,
344 'expectedGetBucketThumbPathCalls' => $this->once(),
345 'expectedFileExistsCalls' => $this->once(),
346 'fileExistsReturn' => false,
347 'expectedMakeTransformTmpFile' => $this->once(),
348 'makeTransformTmpFileReturn' => false,
349 'expectedGenerateAndSaveThumb' => $this->never(),
350 'generateAndSaveThumbReturn' => false,
351 'expectedResult' => false,
352 'message' => 'Cannot generate temp file for bucket'
355 'buckets' => $defaultBuckets,
357 'physicalWidth' => 300,
358 'physicalHeight' => 200,
359 'expectedGetBucketThumbPathCalls' => $this->once(),
360 'expectedFileExistsCalls' => $this->once(),
361 'fileExistsReturn' => false,
362 'expectedMakeTransformTmpFile' => $this->once(),
363 'makeTransformTmpFileReturn' => new TempFSFile( '/tmp/foo' ),
364 'expectedGenerateAndSaveThumb' => $this->once(),
365 'generateAndSaveThumbReturn' => false,
366 'expectedResult' => false,
367 'message' => 'Bucket image could not be generated'
370 'buckets' => $defaultBuckets,
372 'physicalWidth' => 300,
373 'physicalHeight' => 200,
374 'expectedGetBucketThumbPathCalls' => $this->once(),
375 'expectedFileExistsCalls' => $this->once(),
376 'fileExistsReturn' => false,
377 'expectedMakeTransformTmpFile' => $this->once(),
378 'makeTransformTmpFileReturn' => new TempFSFile( '/tmp/foo' ),
379 'expectedGenerateAndSaveThumb' => $this->once(),
380 'generateAndSaveThumbReturn' => new ThumbnailImage( false, 'bar', false, false ),
381 'expectedResult' => true,
382 'message' => 'Bucket image could not be generated'