2015-03-29 17:53:47 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @group ResourceLoader
|
resourceloader: Add version to ResourceLoaderImage urls for long-cache
The code previously here did not work well as it merely forwarded the
hash from the current web request. This had numerous issues:
1. It was often null because requests for stylesheets do not cary
a version hash.
2. When requested by JavaScript, the version hash would be a
combination-hash of many unrelated modules, thus when requested as
part of different batches, it would produce different urls which
is not ideal.
The impact of this is minimal currently because we basically never use
these urls, as SVGs are almost always embedded instead of ref'ed by url.
PNG urls are only generated for non-JS modules and then only used in older
browsers not supporting SVG. And, even after all that, for the edge case
of an SVG being ref'ed by url, they would be stored in LocalStorage by
mw.loader with the name+version of the module the image belonged to, not
the version hash of the batch request it came with.
Which means that, yes, localstorage key for "somemodule+someversion" would
have different values for different users, based on which batch the value
came with, because the image urls were using the version hash of the batch
request from ResourceLoaderContext. This is weird, but didn't cause bugs
or inefficiencies because the user would never be exposed to the other
possible urls for that image because we always check LocalStorage first.
It did cause fragmentation server-side in Varnish, though.
This is all fixed now by always including a version, and setting it to
the version of the module. This means there is no more Varnish fragmentation
for these. And it means that browsers are now allowed to cache the images
served from these urls for 30+ days (immutable) instead of only 5min,
which is what happened when they didn't have a version parameter (or set to
null).
Bug: T233343
Change-Id: I4af7fda03698ed4c288d154e7787fb2f3cbbe6c5
2019-09-26 16:26:52 +00:00
|
|
|
* @covers ResourceLoaderImage
|
2015-03-29 17:53:47 +00:00
|
|
|
*/
|
2019-09-24 22:48:48 +00:00
|
|
|
class ResourceLoaderImageTest extends MediaWikiUnitTestCase {
|
2015-03-29 17:53:47 +00:00
|
|
|
|
2019-09-24 22:48:48 +00:00
|
|
|
private $imagesPath;
|
2015-03-29 17:53:47 +00:00
|
|
|
|
2019-10-20 18:11:08 +00:00
|
|
|
protected function setUp() : void {
|
2015-03-29 17:53:47 +00:00
|
|
|
parent::setUp();
|
2019-09-24 22:48:48 +00:00
|
|
|
$this->imagesPath = __DIR__ . '/../../../data/resourceloader';
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-29 17:53:47 +00:00
|
|
|
protected function getTestImage( $name ) {
|
|
|
|
|
$options = ResourceLoaderImageModuleTest::$commonImageData[$name];
|
|
|
|
|
$fileDescriptor = is_string( $options ) ? $options : $options['file'];
|
2017-10-06 22:38:36 +00:00
|
|
|
$allowedVariants = ( is_array( $options ) && isset( $options['variants'] ) ) ?
|
|
|
|
|
$options['variants'] : [];
|
2016-02-17 09:09:32 +00:00
|
|
|
$variants = array_fill_keys( $allowedVariants, [ 'color' => 'red' ] );
|
2015-09-26 19:48:17 +00:00
|
|
|
return new ResourceLoaderImageTestable(
|
|
|
|
|
$name,
|
|
|
|
|
'test',
|
|
|
|
|
$fileDescriptor,
|
|
|
|
|
$this->imagesPath,
|
|
|
|
|
$variants
|
|
|
|
|
);
|
2015-03-29 17:53:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideGetPath() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
2017-06-06 13:04:09 +00:00
|
|
|
[ 'abc', 'en', 'abc.gif' ],
|
|
|
|
|
[ 'abc', 'he', 'abc.gif' ],
|
|
|
|
|
[ 'def', 'en', 'def.svg' ],
|
|
|
|
|
[ 'def', 'he', 'def.svg' ],
|
|
|
|
|
[ 'ghi', 'en', 'ghi.svg' ],
|
|
|
|
|
[ 'ghi', 'he', 'jkl.svg' ],
|
|
|
|
|
[ 'mno', 'en', 'mno-ltr.svg' ],
|
|
|
|
|
[ 'mno', 'ar', 'mno-rtl.svg' ],
|
|
|
|
|
[ 'mno', 'he', 'mno-ltr.svg' ],
|
|
|
|
|
[ 'pqr', 'en', 'pqr-b.svg' ],
|
2018-03-30 19:56:59 +00:00
|
|
|
[ 'pqr', 'en-gb', 'pqr-b.svg' ],
|
2017-06-06 13:04:09 +00:00
|
|
|
[ 'pqr', 'de', 'pqr-f.svg' ],
|
2018-03-30 19:56:59 +00:00
|
|
|
[ 'pqr', 'de-formal', 'pqr-f.svg' ],
|
2017-06-06 13:04:09 +00:00
|
|
|
[ 'pqr', 'ar', 'pqr-f.svg' ],
|
|
|
|
|
[ 'pqr', 'fr', 'pqr-a.svg' ],
|
|
|
|
|
[ 'pqr', 'he', 'pqr-a.svg' ],
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2015-03-29 17:53:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideGetPath
|
|
|
|
|
*/
|
|
|
|
|
public function testGetPath( $imageName, $languageCode, $path ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
static $dirMap = [
|
2015-03-29 17:53:47 +00:00
|
|
|
'en' => 'ltr',
|
2018-03-30 19:56:59 +00:00
|
|
|
'en-gb' => 'ltr',
|
2015-03-29 17:53:47 +00:00
|
|
|
'de' => 'ltr',
|
2018-03-30 19:56:59 +00:00
|
|
|
'de-formal' => 'ltr',
|
2015-03-29 17:53:47 +00:00
|
|
|
'fr' => 'ltr',
|
|
|
|
|
'he' => 'rtl',
|
|
|
|
|
'ar' => 'rtl',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2015-03-29 17:53:47 +00:00
|
|
|
|
|
|
|
|
$image = $this->getTestImage( $imageName );
|
2019-09-24 22:48:48 +00:00
|
|
|
$context = new DerivativeResourceLoaderContext(
|
|
|
|
|
$this->createMock( ResourceLoaderContext::class )
|
|
|
|
|
);
|
|
|
|
|
$context->setLanguage( $languageCode );
|
|
|
|
|
$context->setDirection( $dirMap[$languageCode] );
|
2015-03-29 17:53:47 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( $image->getPath( $context ), $this->imagesPath . '/' . $path );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetExtension() {
|
2017-06-06 13:04:09 +00:00
|
|
|
$image = $this->getTestImage( 'def' );
|
2015-03-29 17:53:47 +00:00
|
|
|
$this->assertEquals( $image->getExtension(), 'svg' );
|
|
|
|
|
$this->assertEquals( $image->getExtension( 'original' ), 'svg' );
|
|
|
|
|
$this->assertEquals( $image->getExtension( 'rasterized' ), 'png' );
|
2017-06-06 13:04:09 +00:00
|
|
|
$image = $this->getTestImage( 'abc' );
|
2015-03-29 17:53:47 +00:00
|
|
|
$this->assertEquals( $image->getExtension(), 'gif' );
|
|
|
|
|
$this->assertEquals( $image->getExtension( 'original' ), 'gif' );
|
|
|
|
|
$this->assertEquals( $image->getExtension( 'rasterized' ), 'gif' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetImageData() {
|
2019-09-24 22:48:48 +00:00
|
|
|
$context = $this->createMock( ResourceLoaderContext::class );
|
2015-03-29 17:53:47 +00:00
|
|
|
|
2017-06-06 13:04:09 +00:00
|
|
|
$image = $this->getTestImage( 'def' );
|
|
|
|
|
$data = file_get_contents( $this->imagesPath . '/def.svg' );
|
|
|
|
|
$dataConstructive = file_get_contents( $this->imagesPath . '/def_variantize.svg' );
|
2015-03-29 17:53:47 +00:00
|
|
|
$this->assertEquals( $image->getImageData( $context, null, 'original' ), $data );
|
2015-09-26 19:48:17 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$image->getImageData( $context, 'destructive', 'original' ),
|
|
|
|
|
$dataConstructive
|
|
|
|
|
);
|
2015-03-29 17:53:47 +00:00
|
|
|
// Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output
|
|
|
|
|
$this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), 'RASTERIZESTUB' );
|
|
|
|
|
|
2017-06-06 13:04:09 +00:00
|
|
|
$image = $this->getTestImage( 'abc' );
|
|
|
|
|
$data = file_get_contents( $this->imagesPath . '/abc.gif' );
|
2015-03-29 17:53:47 +00:00
|
|
|
$this->assertEquals( $image->getImageData( $context, null, 'original' ), $data );
|
|
|
|
|
$this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), $data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testMassageSvgPathdata() {
|
2017-06-06 13:04:09 +00:00
|
|
|
$image = $this->getTestImage( 'ghi' );
|
|
|
|
|
$data = file_get_contents( $this->imagesPath . '/ghi.svg' );
|
|
|
|
|
$dataMassaged = file_get_contents( $this->imagesPath . '/ghi_massage.svg' );
|
2015-03-29 17:53:47 +00:00
|
|
|
$this->assertEquals( $image->massageSvgPathdata( $data ), $dataMassaged );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ResourceLoaderImageTestable extends ResourceLoaderImage {
|
2019-10-18 23:48:10 +00:00
|
|
|
private $mockFallbacks = [
|
|
|
|
|
'en-gb' => [ 'en' ],
|
|
|
|
|
'de-formal' => [ 'de' ],
|
|
|
|
|
];
|
|
|
|
|
|
2015-03-29 17:53:47 +00:00
|
|
|
// Make some protected methods public
|
|
|
|
|
public function massageSvgPathdata( $svg ) {
|
|
|
|
|
return parent::massageSvgPathdata( $svg );
|
|
|
|
|
}
|
2019-05-11 01:17:43 +00:00
|
|
|
|
2015-03-29 17:53:47 +00:00
|
|
|
// Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output
|
|
|
|
|
public function rasterize( $svg ) {
|
|
|
|
|
return 'RASTERIZESTUB';
|
|
|
|
|
}
|
2019-10-18 23:48:10 +00:00
|
|
|
|
|
|
|
|
protected function getLangFallbacks( string $code ) : array {
|
|
|
|
|
return $this->mockFallbacks[$code] ?? [];
|
|
|
|
|
}
|
2015-03-29 17:53:47 +00:00
|
|
|
}
|