2019-08-25 11:15:53 +00:00
|
|
|
<?php
|
|
|
|
|
|
2020-12-06 20:14:00 +00:00
|
|
|
// phpcs:disable MediaWiki.Commenting.FunctionComment.ObjectTypeHintParam
|
2021-08-04 19:44:49 +00:00
|
|
|
// phpcs:disable MediaWiki.Commenting.FunctionComment.ObjectTypeHintReturn
|
2020-12-06 20:14:00 +00:00
|
|
|
|
2019-08-22 15:39:26 +00:00
|
|
|
use Wikimedia\Assert\PostconditionException;
|
|
|
|
|
|
2019-08-25 11:15:53 +00:00
|
|
|
/**
|
|
|
|
|
* Code to test the getFallbackFor, getFallbacksFor, and getFallbacksIncludingSiteLanguage methods
|
|
|
|
|
* that have historically been static methods of the Language class. It can be used to test any
|
|
|
|
|
* class or object that implements those three methods.
|
|
|
|
|
*/
|
|
|
|
|
trait LanguageFallbackTestTrait {
|
|
|
|
|
/**
|
|
|
|
|
* @param array $options Valid keys:
|
|
|
|
|
* * expectedGets: How many times we expect to hit the localisation cache. (This can be
|
|
|
|
|
* ignored in integration tests -- it's enough to test in unit tests.)
|
2019-08-22 15:39:26 +00:00
|
|
|
* * fallbackMap: A map of language codes to fallback sequences to use.
|
2019-08-25 11:15:53 +00:00
|
|
|
* * siteLangCode
|
2019-08-22 15:39:26 +00:00
|
|
|
* @return string|object Name of class or object with the three methods getFirst, getAll, and
|
|
|
|
|
* getAllIncludingSiteLanguage (or getFallbackFor, getFallbacksFor, and
|
|
|
|
|
* getFallbacksIncludingSiteLanguage if callMethod() is suitably overridden).
|
2019-08-25 11:15:53 +00:00
|
|
|
*/
|
|
|
|
|
abstract protected function getCallee( array $options = [] );
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return int Value that was historically in Language::MESSAGES_FALLBACKS
|
|
|
|
|
*/
|
|
|
|
|
abstract protected function getMessagesKey();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return int Value that was historically in Language::STRICT_FALLBACKS
|
|
|
|
|
*/
|
|
|
|
|
abstract protected function getStrictKey();
|
|
|
|
|
|
2019-08-22 15:39:26 +00:00
|
|
|
/**
|
|
|
|
|
* @param int $expectedGets How many times it's expected that 'getItem' will be called
|
|
|
|
|
* @param array $map Map language codes to fallback arrays to return
|
|
|
|
|
* @return LocalisationCache
|
|
|
|
|
*/
|
|
|
|
|
protected function getMockLocalisationCache( $expectedGets, $map ) {
|
|
|
|
|
$mockLocCache = $this->createMock( LocalisationCache::class );
|
|
|
|
|
$mockLocCache->expects( $this->exactly( $expectedGets ) )->method( 'getItem' )
|
|
|
|
|
->with( $this->anything(),
|
|
|
|
|
$this->logicalOr( 'fallbackSequence', 'originalFallbackSequence' ) )
|
2022-06-05 23:39:02 +00:00
|
|
|
->willReturnCallback( static function ( $code, $key ) use ( $map ) {
|
2019-08-22 15:39:26 +00:00
|
|
|
if ( $key === 'originalFallbackSequence' || $code === 'en' ) {
|
|
|
|
|
return $map[$code];
|
|
|
|
|
}
|
|
|
|
|
$fallbacks = $map[$code];
|
|
|
|
|
if ( !$fallbacks || $fallbacks[count( $fallbacks ) - 1] !== 'en' ) {
|
|
|
|
|
$fallbacks[] = 'en';
|
|
|
|
|
}
|
|
|
|
|
return $fallbacks;
|
2022-06-05 23:39:02 +00:00
|
|
|
} );
|
2019-08-22 15:39:26 +00:00
|
|
|
$mockLocCache->expects( $this->never() )->method( $this->anythingBut( 'getItem' ) );
|
|
|
|
|
return $mockLocCache;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-25 11:15:53 +00:00
|
|
|
/**
|
|
|
|
|
* Convenience/readability wrapper to call a method on a class or object.
|
|
|
|
|
*
|
2019-08-22 15:39:26 +00:00
|
|
|
* @param string|object $callee As in return value of getCallee()
|
2019-08-25 11:15:53 +00:00
|
|
|
* @param string $method Name of method to call
|
|
|
|
|
* @param mixed ...$params To pass to method
|
|
|
|
|
* @return mixed Return value of method
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function callMethod( $callee, $method, ...$params ) {
|
2019-08-25 11:15:53 +00:00
|
|
|
return [ $callee, $method ]( ...$params );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $code
|
|
|
|
|
* @param array $expected
|
|
|
|
|
* @param array $options
|
2019-08-22 15:39:26 +00:00
|
|
|
* @dataProvider provideGetAll
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getFirst
|
2019-08-25 11:15:53 +00:00
|
|
|
* @covers Language::getFallbackFor
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function testGetFirst( $code, array $expected, array $options = [] ) {
|
2019-08-25 11:15:53 +00:00
|
|
|
$callee = $this->getCallee( $options );
|
|
|
|
|
// One behavior difference between the old static methods and the new instance methods:
|
|
|
|
|
// returning null instead of false.
|
|
|
|
|
$defaultExpected = is_object( $callee ) ? null : false;
|
|
|
|
|
$this->assertSame( $expected[0] ?? $defaultExpected,
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getFirst', $code ) );
|
2019-08-25 11:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $code
|
|
|
|
|
* @param array $expected
|
|
|
|
|
* @param array $options
|
2019-08-22 15:39:26 +00:00
|
|
|
* @dataProvider provideGetAll
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAll
|
2019-08-25 11:15:53 +00:00
|
|
|
* @covers Language::getFallbacksFor
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function testGetAll( $code, array $expected, array $options = [] ) {
|
2019-08-25 11:15:53 +00:00
|
|
|
$this->assertSame( $expected,
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $this->getCallee( $options ), 'getAll', $code ) );
|
2019-08-25 11:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $code
|
|
|
|
|
* @param array $expected
|
|
|
|
|
* @param array $options
|
2019-08-22 15:39:26 +00:00
|
|
|
* @dataProvider provideGetAll
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAll
|
2019-08-25 11:15:53 +00:00
|
|
|
* @covers Language::getFallbacksFor
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function testGetAll_messages( $code, array $expected, array $options = [] ) {
|
2019-08-25 11:15:53 +00:00
|
|
|
$this->assertSame( $expected,
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $this->getCallee( $options ), 'getAll',
|
2019-08-25 11:15:53 +00:00
|
|
|
$code, $this->getMessagesKey() ) );
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-22 15:39:26 +00:00
|
|
|
public static function provideGetAll() {
|
2019-08-25 11:15:53 +00:00
|
|
|
return [
|
|
|
|
|
'en' => [ 'en', [], [ 'expectedGets' => 0 ] ],
|
|
|
|
|
'fr' => [ 'fr', [ 'en' ] ],
|
|
|
|
|
'sco' => [ 'sco', [ 'en' ] ],
|
|
|
|
|
'yi' => [ 'yi', [ 'he', 'en' ] ],
|
|
|
|
|
'ruq' => [ 'ruq', [ 'ruq-latn', 'ro', 'en' ] ],
|
|
|
|
|
'sh' => [ 'sh', [ 'bs', 'sr-el', 'hr', 'en' ] ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $code
|
|
|
|
|
* @param array $expected
|
|
|
|
|
* @param array $options
|
2019-08-22 15:39:26 +00:00
|
|
|
* @dataProvider provideGetAll_strict
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAll
|
2019-08-25 11:15:53 +00:00
|
|
|
* @covers Language::getFallbacksFor
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function testGetAll_strict( $code, array $expected, array $options = [] ) {
|
2019-08-25 11:15:53 +00:00
|
|
|
$this->assertSame( $expected,
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $this->getCallee( $options ), 'getAll',
|
2019-08-25 11:15:53 +00:00
|
|
|
$code, $this->getStrictKey() ) );
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-22 15:39:26 +00:00
|
|
|
public static function provideGetAll_strict() {
|
2019-08-25 11:15:53 +00:00
|
|
|
return [
|
|
|
|
|
'en' => [ 'en', [], [ 'expectedGets' => 0 ] ],
|
|
|
|
|
'fr' => [ 'fr', [] ],
|
|
|
|
|
'sco' => [ 'sco', [ 'en' ] ],
|
|
|
|
|
'yi' => [ 'yi', [ 'he' ] ],
|
|
|
|
|
'ruq' => [ 'ruq', [ 'ruq-latn', 'ro' ] ],
|
|
|
|
|
'sh' => [ 'sh', [ 'bs', 'sr-el', 'hr' ] ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-08-22 15:39:26 +00:00
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAll
|
2019-08-25 11:15:53 +00:00
|
|
|
* @covers Language::getFallbacksFor
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function testGetAll_invalidMode() {
|
|
|
|
|
$this->expectException( InvalidArgumentException::class );
|
2019-10-05 05:21:11 +00:00
|
|
|
$this->expectExceptionMessage( 'Invalid fallback mode "7"' );
|
2019-08-25 11:15:53 +00:00
|
|
|
|
|
|
|
|
$callee = $this->getCallee( [ 'expectedGets' => 0 ] );
|
|
|
|
|
|
|
|
|
|
// These should not throw, because of short-circuiting. If they do, it will fail the test,
|
|
|
|
|
// because we pass 5 and 6 instead of 7.
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getAll', 'en', 5 );
|
|
|
|
|
$this->callMethod( $callee, 'getAll', '!!!', 6 );
|
2019-08-25 11:15:53 +00:00
|
|
|
|
|
|
|
|
// This is the one that should throw.
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getAll', 'fr', 7 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAll
|
|
|
|
|
* @covers Language::getFallbacksFor
|
|
|
|
|
*/
|
|
|
|
|
public function testGetAll_invalidFallback() {
|
|
|
|
|
$callee = $this->getCallee( [ 'fallbackMap' => [ 'qqz' => [ 'fr', 'de', '!!!', 'hi' ] ] ] );
|
|
|
|
|
|
2019-10-12 21:03:18 +00:00
|
|
|
$this->expectException( PostconditionException::class );
|
|
|
|
|
$this->expectExceptionMessage( "Invalid fallback code '!!!' in fallback sequence for 'qqz'" );
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getAll', 'qqz' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAll
|
|
|
|
|
* @covers Language::getFallbacksFor
|
|
|
|
|
*/
|
|
|
|
|
public function testGetAll_invalidFallback_strict() {
|
|
|
|
|
$callee = $this->getCallee( [ 'fallbackMap' => [ 'qqz' => [ 'fr', 'de', '!!!', 'hi' ] ] ] );
|
|
|
|
|
|
2019-10-12 21:03:18 +00:00
|
|
|
$this->expectException( PostconditionException::class );
|
|
|
|
|
$this->expectExceptionMessage( "Invalid fallback code '!!!' in fallback sequence for 'qqz'" );
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getAll', 'qqz', $this->getStrictKey() );
|
2019-08-25 11:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $code
|
|
|
|
|
* @param string $siteLangCode
|
|
|
|
|
* @param array $expected
|
|
|
|
|
* @param int $expectedGets
|
2019-08-22 15:39:26 +00:00
|
|
|
* @dataProvider provideGetAllIncludingSiteLanguage
|
|
|
|
|
* @covers MediaWiki\Languages\LanguageFallback::getAllIncludingSiteLanguage
|
2019-08-25 11:15:53 +00:00
|
|
|
* @covers Language::getFallbacksIncludingSiteLanguage
|
|
|
|
|
*/
|
2019-08-22 15:39:26 +00:00
|
|
|
public function testGetAllIncludingSiteLanguage(
|
2019-08-25 11:15:53 +00:00
|
|
|
$code, $siteLangCode, array $expected, $expectedGets = 1
|
|
|
|
|
) {
|
|
|
|
|
$callee = $this->getCallee(
|
|
|
|
|
[ 'siteLangCode' => $siteLangCode, 'expectedGets' => $expectedGets ] );
|
|
|
|
|
$this->assertSame( $expected,
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getAllIncludingSiteLanguage', $code ) );
|
2019-08-25 11:15:53 +00:00
|
|
|
|
|
|
|
|
// Call again to make sure we don't call LocalisationCache again
|
2019-08-22 15:39:26 +00:00
|
|
|
$this->callMethod( $callee, 'getAllIncludingSiteLanguage', $code );
|
2019-08-25 11:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 15:39:26 +00:00
|
|
|
public static function provideGetAllIncludingSiteLanguage() {
|
2019-08-25 11:15:53 +00:00
|
|
|
return [
|
|
|
|
|
'en on en' => [ 'en', 'en', [ [], [ 'en' ] ], 0 ],
|
|
|
|
|
'fr on en' => [ 'fr', 'en', [ [ 'en' ], [] ] ],
|
|
|
|
|
'en on fr' => [ 'en', 'fr', [ [], [ 'fr', 'en' ] ] ],
|
|
|
|
|
'fr on fr' => [ 'fr', 'fr', [ [ 'en' ], [ 'fr' ] ] ],
|
|
|
|
|
|
|
|
|
|
'sco on en' => [ 'sco', 'en', [ [ 'en' ], [] ] ],
|
|
|
|
|
'en on sco' => [ 'en', 'sco', [ [], [ 'sco', 'en' ] ] ],
|
|
|
|
|
'sco on sco' => [ 'sco', 'sco', [ [ 'en' ], [ 'sco' ] ] ],
|
|
|
|
|
|
|
|
|
|
'fr on sco' => [ 'fr', 'sco', [ [ 'en' ], [ 'sco' ] ], 2 ],
|
|
|
|
|
'sco on fr' => [ 'sco', 'fr', [ [ 'en' ], [ 'fr' ] ], 2 ],
|
|
|
|
|
|
|
|
|
|
'fr on yi' => [ 'fr', 'yi', [ [ 'en' ], [ 'yi', 'he' ] ], 2 ],
|
|
|
|
|
'yi on fr' => [ 'yi', 'fr', [ [ 'he', 'en' ], [ 'fr' ] ], 2 ],
|
|
|
|
|
'yi on yi' => [ 'yi', 'yi', [ [ 'he', 'en' ], [ 'yi' ] ] ],
|
|
|
|
|
|
|
|
|
|
'sh on ruq' => [ 'sh', 'ruq',
|
|
|
|
|
[ [ 'bs', 'sr-el', 'hr', 'en' ], [ 'ruq', 'ruq-latn', 'ro' ] ], 2 ],
|
|
|
|
|
'ruq on sh' => [ 'ruq', 'sh',
|
|
|
|
|
[ [ 'ruq-latn', 'ro', 'en' ], [ 'sh', 'bs', 'sr-el', 'hr' ] ], 2 ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|