11
11
12
12
namespace Symfony \Component \Translation \Tests ;
13
13
14
+ use Symfony \Component \Config \Resource \ResourceInterface ;
14
15
use Symfony \Component \Translation \Loader \ArrayLoader ;
16
+ use Symfony \Component \Translation \Loader \LoaderInterface ;
15
17
use Symfony \Component \Translation \Translator ;
16
18
use Symfony \Component \Translation \MessageCatalogue ;
17
- use Symfony \Component \Translation \MessageSelector ;
18
19
19
20
class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
20
21
{
@@ -51,91 +52,107 @@ protected function deleteTmpDir()
51
52
rmdir ($ this ->tmpDir );
52
53
}
53
54
54
- public function testTransWithoutCaching ()
55
+ /**
56
+ * @dataProvider runForDebugAndProduction
57
+ */
58
+ public function testThatACacheIsUsed ($ debug )
55
59
{
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 ' ));
69
75
}
70
76
71
- public function testTransWithCaching ()
77
+ public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh ()
72
78
{
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
+ */
104
89
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 ' ;
109
93
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
111
96
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 );
118
116
}
119
117
120
- public function testLoadCatalogueWithCachingWithInvalidLocale ()
118
+ /**
119
+ * @dataProvider runForDebugAndProduction
120
+ */
121
+ public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache ($ debug )
121
122
{
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
+ */
124
127
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 ' ));
131
149
}
132
150
133
151
public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales ()
134
152
{
135
153
/*
136
154
* 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
139
156
* loading a catalogue from the cache.
140
157
*/
141
158
$ translator = new Translator ('a ' , null , $ this ->tmpDir );
@@ -161,6 +178,54 @@ public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales(
161
178
$ this ->assertEquals ('bar ' , $ translator ->trans ('bar ' ));
162
179
}
163
180
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
+
164
229
public function testRefreshCacheWhenResourcesAreNoLongerFresh ()
165
230
{
166
231
$ resource = $ this ->getMock ('Symfony\Component\Config\Resource\ResourceInterface ' );
@@ -197,93 +262,38 @@ protected function getCatalogue($locale, $messages, $resources = array())
197
262
return $ catalogue ;
198
263
}
199
264
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 ()
201
274
{
202
275
$ loader = $ this ->getMock ('Symfony\Component\Translation\Loader\LoaderInterface ' );
203
276
$ 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 ' );
254
279
255
280
return $ loader ;
256
281
}
282
+ }
257
283
258
- public function getTranslator ($ loader , $ cacheDir = null , $ translatorClass = '\Symfony\Component\Translation\Translator ' )
284
+ class StaleResource implements ResourceInterface
285
+ {
286
+ public function isFresh ($ timestamp )
259
287
{
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 ;
272
289
}
273
- }
274
290
275
- class TranslatorWithInvalidLocale extends Translator
276
- {
277
- /**
278
- * {@inheritdoc}
279
- */
280
- public function setLocale ($ locale )
291
+ public function getResource ()
281
292
{
282
- $ this ->locale = $ locale ;
283
293
}
284
294
285
- public function proxyLoadCatalogue ( $ locale )
295
+ public function __toString ( )
286
296
{
287
- $ this -> loadCatalogue ( $ locale ) ;
297
+ return '' ;
288
298
}
289
299
}
0 commit comments