8000 minor #14437 [2.6][Translator] Extend, refactor and simplify Translat… · symfony/symfony@386f733 · GitHub
[go: up one dir, main page]

Skip to content

Commit 386f733

Browse files
committed
minor #14437 [2.6][Translator] Extend, refactor and simplify Translator tests. (mpdude)
This PR was squashed before being merged into the 2.6 branch (closes #14437). Discussion ---------- [2.6][Translator] Extend, refactor and simplify Translator tests. | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Fixed tickets | ~ | Tests pass? | yes | License | MIT These are the improvements from #14291 that we can easily backport to the 2.6 branch as requested [here](#14291 (comment)). Commits ------- a8c4471 [2.6][Translator] Extend, refactor and simplify Translator tests.
2 parents f48cc1b + a8c4471 commit 386f733

File tree

2 files changed

+183
-150
lines changed

2 files changed

+183
-150
lines changed

src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php

Lines changed: 153 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
namespace Symfony\Component\Translation\Tests;
1313

14+
use Symfony\Component\Config\Resource\ResourceInterface;
1415
use Symfony\Component\Translation\Loader\ArrayLoader;
16+
use Symfony\Component\Translation\Loader\LoaderInterface;
1517
use Symfony\Component\Translation\Translator;
1618
use Symfony\Component\Translation\MessageCatalogue;
17-
use Symfony\Component\Translation\MessageSelector;
1819

1920
class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
2021
{
@@ -51,91 +52,107 @@ protected function deleteTmpDir()
5152
rmdir($this->tmpDir);
5253
}
5354

54-
public function testTransWithoutCaching()
55+
/**
56+
* @dataProvider runForDebugAndProduction
57+
*/
58+
public function testThatACacheIsUsed($debug)
5559
{
56-
$translator = $this->getTranslator($this->getLoader());
57-
$translator->setLocale('fr');
58-
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
59-
60-
$this->assertEquals('foo (FR)', $translator->trans('foo'));
61-
$this->assertEquals('bar (EN)', $translator->trans('bar'));
62-
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
63-
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
64-
$this->assertEquals('no translation', $translator->trans('no translation'));
65-
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
66-
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
67-
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
68-
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
60+
$locale = 'any_locale';
61+
$format = 'some_format';
62+
$msgid = 'test';
63+
64+
// Prime the cache
65+
$translator = new Translator($locale, null, $this->tmpDir, $debug);
66+
$translator->addLoader($format, new ArrayLoader());
67+
$translator->addResource($format, array($msgid => 'OK'), $locale);
68+
$translator->trans($msgid);
69+
70+
// Try again and see we get a valid result whilst no loader can be used
71+
$translator = new Translator($locale, null, $this->tmpDir, $debug);
72+
$translator->addLoader($format, $this->createFailingLoader());
73+
$translator->addResource($format, array($msgid => 'OK'), $locale);
74+
$this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
6975
}
7076

71-
public function testTransWithCaching()
77+
public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
7278
{
73-
// prime the cache
74-
$translator = $this->getTranslator($this->getLoader(), $this->tmpDir);
75-
$translator->setLocale('fr');
76-
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
77-
78-
$this->assertEquals('foo (FR)', $translator->trans('foo'));
79-
$this->assertEquals('bar (EN)', $translator->trans('bar'));
80-
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
81-
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
82-
$this->assertEquals('no translation', $translator->trans('no translation'));
83-
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
84-
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
85-
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
86-
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
87-
88-
// do it another time as the cache is primed now
89-
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
90-
$translator = $this->getTranslator($loader, $this->tmpDir);
91-
$translator->setLocale('fr');
92-
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
93-
94-
$this->assertEquals('foo (FR)', $translator->trans('foo'));
95-
$this->assertEquals('bar (EN)', $translator->trans('bar'));
96-
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
97-
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
98-
$this->assertEquals('no translation', $translator->trans('no translation'));
99-
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
100-
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
101-
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
102-
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
103-
}
79+
/*
80+
* The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache
81+
* is fresh.
82+
*
83+
* Now we add a Resource that is never fresh and make sure that the
84+
* cache is discarded (the loader is called twice).
85+
*
86+
* We need to run this for debug=true only because in production the cache
87+
* will never be revalidated.
88+
*/
10489

105-
public function testTransWithCachingWithInvalidLocale()
106-
{
107-
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
108-
$translator = $this->getTranslator($loader, $this->tmpDir, 'Symfony\Component\Translation\Tests\TranslatorWithInvalidLocale');
90+
$locale = 'any_locale';
91+
$format = 'some_format';
92+
$msgid = 'test';
10993

110-
$translator->setLocale('invalid locale');
94+
$catalogue = new MessageCatalogue($locale, array());
95+
$catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
11196

112-
try {
113-
$translator->trans('foo');
114-
$this->fail();
115-
} catch (\InvalidArgumentException $e) {
116-
$this->assertFalse(file_exists($this->tmpDir.'/catalogue.invalid locale.php'));
117-
}
97+
/** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */
98+
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
99+
$loader
100+
->expects($this->exactly(2))
101+
->method('load')
102+
->will($this->returnValue($catalogue))
103+
;
104+
105+
// 1st pass
106+
$translator = new Translator($locale, null, $this->tmpDir, true);
107+
$translator->addLoader($format, $loader);
108+
$translator->addResource($format, null, $locale);
109+
$translator->trans($msgid);
110+
111+
// 2nd pass
112+
$translator = new Translator($locale, null, $this->tmpDir, true);
113+
$translator->addLoader($format, $loader);
114+
$translator->addResource($format, null, $locale);
115+
$translator->trans($msgid);
118116
}
119117

120-
public function testLoadCatalogueWithCachingWithInvalidLocale()
118+
/**
119+
* @dataProvider runForDebugAndProduction
120+
*/
121+
public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
121122
{
122-
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
123-
$translator = $this->getTranslator($loader, $this->tmpDir, 'Symfony\Component\Translation\Tests\TranslatorWithInvalidLocale');
123+
/*
124+
* Similar to the previous test. After we used the second translator, make
125+
* sure there's still a useable cache for the first one.
126+
*/
124127

125-
try {
126-
$translator->proxyLoadCatalogue('invalid locale');
127-
$this->fail();
128-
} catch (\InvalidArgumentException $e) {
129-
$this->assertFalse(file_exists($this->tmpDir.'/catalogue.invalid locale.php'));
130-
}
128+
$locale = 'any_locale';
129+
$format = 'some_format';
130+
$msgid = 'test';
131+
132+
// Create a Translator and prime its cache
133+
$translator = new Translator($locale, null, $this->tmpDir, $debug);
134+
$translator->addLoader($format, new ArrayLoader());
135+
$translator->addResource($format, array($msgid => 'OK'), $locale);
136+
$translator->trans($msgid);
137+
138+
// Create another Translator with a different catalogue for the same locale
139+
$translator = new Translator($locale, null, $this->tmpDir, $debug);
140+
$translator->addLoader($format, new ArrayLoader());
141+
$translator->addResource($format, array($msgid => 'FAIL'), $locale);
142+
$translator->trans($msgid);
143+
144+
// Now the first translator must still have a useable cache.
145+
$translator = new Translator($locale, null, $this->tmpDir, $debug);
146+
$translator->addLoader($format, $this->createFailingLoader());
147+
$translator->addResource($format, array($msgid => 'OK'), $locale);
148+
$this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
131149
}
132150

133151
public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
134152
{
135153
/*
136154
* Because the cache file contains a catalogue including all of its fallback
137-
* catalogues (either "inlined" in Symfony 2.7 production or "standalone"),
138-
* we must take the active set of fallback locales into consideration when
155+
* catalogues, we must take the set of fallback locales into consideration when
139156
* loading a catalogue from the cache.
140157
*/
141158
$translator = new Translator('a', null, $this->tmpDir);
@@ -161,6 +178,54 @@ public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales(
161178
$this->assertEquals('bar', $translator->trans('bar'));
162179
}
163180

181+
public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
182+
{
183+
/*
184+
* As a safeguard against potential BC breaks, make sure that primary and fallback
185+
* catalogues (reachable via getFallbackCatalogue()) always contain the full set of
186+
* messages provided by the loader. This must also be the case when these catalogues
187+
* are (internally) read from a cache.
188+
*
189+
* Optimizations inside the translator must not change this behaviour.
190+
*/
191+
192+
/*
193+
* Create a translator that loads two catalogues for two different locales.
194+
* The catalogues contain distinct sets of messages.
195+
*/
196+
$translator = new Translator('a', null, $this->tmpDir);
197+
$translator->setFallbackLocales(array('b'));
198+
199+
$translator->addLoader('array', new ArrayLoader());
200+
$translator->addResource('array', array('foo' => 'foo (a)'), 'a');
201+
$translator->addResource('array', array('foo' => 'foo (b)'), 'b');
202+
$translator->addResource('array', array('bar' => 'bar (b)'), 'b');
203+
204+
$catalogue = $translator->getCatalogue('a');
205+
$this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message.
206+
207+
$fallback = $catalogue->getFallbackCatalogue();
208+
$this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b"
209+
210+
/*
211+
* Now, repeat the same test.
212+
* Behind the scenes, the cache is used. But that should not matter, right?
213+
*/
214+
$translator = new Translator('a', null, $this->tmpDir);
215+
$translator->setFallbackLocales(array('b'));
216+
217+
$translator->addLoader('array', new ArrayLoader());
218+
$translator->addResource('array', array('foo' => 'foo (a)'), 'a');
219+
$translator->addResource('array', array('foo' => 'foo (b)'), 'b');
220+
$translator->addResource('array', array('bar' => 'bar (b)'), 'b');
221+
222+
$catalogue = $translator->getCatalogue('a');
223+
$this->assertFalse($catalogue->defines('bar'));
224+
225+
$fallback = $catalogue->getFallbackCatalogue();
226+
$this->assertTrue($fallback->defines('foo'));
227+
}
228+
164229
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
165230
{
166231
$resource = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
@@ -197,93 +262,38 @@ protected function getCatalogue($locale, $messages, $resources = array())
197262
return $catalogue;
198263
}
199264

200-
protected function getLoader()
265+
public function runForDebugAndProduction()
266+
{
267+
return array(array(true), array(false));
268+
}
269+
270+
/**
271+
* @return LoaderInterface
272+
*/
273+
private function createFailingLoader()
201274
{
202275
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
203276
$loader
204-
->expects($this->at(0))
205-
->method('load')
206-
->will($this->returnValue($this->getCatalogue('fr', array(
207-
'foo' => 'foo (FR)',
208-
))))
209-
;
210-
$loader
211-
->expects($this->at(1))
212-
->method('load')
213-
->will($this->returnValue($this->getCatalogue('en', array(
214-
'foo' => 'foo (EN)',
215-
'bar' => 'bar (EN)',
216-
'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)',
217-
))))
218-
;
219-
$loader
220-
->expects($this->at(2))
221-
->method('load')
222-
->will($this->returnValue($this->getCatalogue('es', array(
223-
'foobar' => 'foobar (ES)',
224-
))))
225-
;
226-
$loader
227-
->expects($this->at(3))
228-
->method('load')
229-
->will($this->returnValue($this->getCatalogue('pt-PT', array(
230-
'foobarfoo' => 'foobarfoo (PT-PT)',
231-
))))
232-
;
233-
$loader
234-
->expects($this->at(4))
235-
->method('load')
236-
->will($this->returnValue($this->getCatalogue('pt_BR', array(
237-
'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)',
238-
))))
239-
;
240-
$loader
241-
->expects($this->at(5))
242-
->method('load')
243-
->will($this->returnValue($this->getCatalogue('fr.UTF-8', array(
244-
'foobarbaz' => 'foobarbaz (fr.UTF-8)',
245-
))))
246-
;
247-
$loader
248-
->expects($this->at(6))
249-
->method('load')
250-
->will($this->returnValue($this->getCatalogue('sr@latin', array(
251-
'foobarbax' => 'foobarbax (sr@latin)',
252-
))))
253-
;
277+
->expects($this->never())
278+
->method('load');
254279

255280
return $loader;
256281
}
282+
}
257283

258-
public function getTranslator($loader, $cacheDir = null, $translatorClass = '\Symfony\Component\Translation\Translator')
284+
class StaleResource implements ResourceInterface
285+
{
286+
public function isFresh($timestamp)
259287
{
260-
$translator = new $translatorClass('fr', new MessageSelector(), $cacheDir);
261-
262-
$translator->addLoader('loader', $loader);
263-
$translator->addResource('loader', 'foo', 'fr');
264-
$translator->addResource('loader', 'foo', 'en');
265-
$translator->addResource('loader', 'foo', 'es');
266-
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
267-
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
268-
$translator->addResource('loader', 'foo', 'fr.UTF-8');
269-
$translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian
270-
271-
return $translator;
288+
return false;
272289
}
273-
}
274290

275-
class TranslatorWithInvalidLocale extends Translator
276-
{
277-
/**
278-
* {@inheritdoc}
279-
*/
280-
public function setLocale($locale)
291+
public function getResource()
281292
{
282-
$this->locale = $locale;
283293
}
284294

285-
public function proxyLoadCatalogue($locale)
295+
public function __toString()
286296
{
287-
$this->loadCatalogue($locale);
297+
return '';
288298
}
289299
}

0 commit comments

Comments
 (0)
0