3e71ae74ec78793ae9611beaebb82732c8afc7f4
[yaffs-website] / vendor / symfony / translation / Tests / TranslatorCacheTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Translation\Tests;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
16 use Symfony\Component\Translation\Loader\ArrayLoader;
17 use Symfony\Component\Translation\Loader\LoaderInterface;
18 use Symfony\Component\Translation\MessageCatalogue;
19 use Symfony\Component\Translation\Translator;
20
21 class TranslatorCacheTest extends TestCase
22 {
23     protected $tmpDir;
24
25     protected function setUp()
26     {
27         $this->tmpDir = sys_get_temp_dir().'/sf2_translation';
28         $this->deleteTmpDir();
29     }
30
31     protected function tearDown()
32     {
33         $this->deleteTmpDir();
34     }
35
36     protected function deleteTmpDir()
37     {
38         if (!file_exists($dir = $this->tmpDir)) {
39             return;
40         }
41
42         $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir), \RecursiveIteratorIterator::CHILD_FIRST);
43         foreach ($iterator as $path) {
44             if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) {
45                 continue;
46             }
47             if ($path->isDir()) {
48                 rmdir($path->__toString());
49             } else {
50                 unlink($path->__toString());
51             }
52         }
53         rmdir($this->tmpDir);
54     }
55
56     /**
57      * @dataProvider runForDebugAndProduction
58      */
59     public function testThatACacheIsUsed($debug)
60     {
61         $locale = 'any_locale';
62         $format = 'some_format';
63         $msgid = 'test';
64
65         // Prime the cache
66         $translator = new Translator($locale, null, $this->tmpDir, $debug);
67         $translator->addLoader($format, new ArrayLoader());
68         $translator->addResource($format, array($msgid => 'OK'), $locale);
69         $translator->trans($msgid);
70
71         // Try again and see we get a valid result whilst no loader can be used
72         $translator = new Translator($locale, null, $this->tmpDir, $debug);
73         $translator->addLoader($format, $this->createFailingLoader());
74         $translator->addResource($format, array($msgid => 'OK'), $locale);
75         $this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
76     }
77
78     public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
79     {
80         /*
81          * The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache
82          * is fresh.
83          *
84          * Now we add a Resource that is never fresh and make sure that the
85          * cache is discarded (the loader is called twice).
86          *
87          * We need to run this for debug=true only because in production the cache
88          * will never be revalidated.
89          */
90
91         $locale = 'any_locale';
92         $format = 'some_format';
93         $msgid = 'test';
94
95         $catalogue = new MessageCatalogue($locale, array());
96         $catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
97
98         /** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */
99         $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
100         $loader
101             ->expects($this->exactly(2))
102             ->method('load')
103             ->will($this->returnValue($catalogue))
104         ;
105
106         // 1st pass
107         $translator = new Translator($locale, null, $this->tmpDir, true);
108         $translator->addLoader($format, $loader);
109         $translator->addResource($format, null, $locale);
110         $translator->trans($msgid);
111
112         // 2nd pass
113         $translator = new Translator($locale, null, $this->tmpDir, true);
114         $translator->addLoader($format, $loader);
115         $translator->addResource($format, null, $locale);
116         $translator->trans($msgid);
117     }
118
119     /**
120      * @dataProvider runForDebugAndProduction
121      */
122     public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
123     {
124         /*
125          * Similar to the previous test. After we used the second translator, make
126          * sure there's still a useable cache for the first one.
127          */
128
129         $locale = 'any_locale';
130         $format = 'some_format';
131         $msgid = 'test';
132
133         // Create a Translator and prime its cache
134         $translator = new Translator($locale, null, $this->tmpDir, $debug);
135         $translator->addLoader($format, new ArrayLoader());
136         $translator->addResource($format, array($msgid => 'OK'), $locale);
137         $translator->trans($msgid);
138
139         // Create another Translator with a different catalogue for the same locale
140         $translator = new Translator($locale, null, $this->tmpDir, $debug);
141         $translator->addLoader($format, new ArrayLoader());
142         $translator->addResource($format, array($msgid => 'FAIL'), $locale);
143         $translator->trans($msgid);
144
145         // Now the first translator must still have a useable cache.
146         $translator = new Translator($locale, null, $this->tmpDir, $debug);
147         $translator->addLoader($format, $this->createFailingLoader());
148         $translator->addResource($format, array($msgid => 'OK'), $locale);
149         $this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
150     }
151
152     public function testGeneratedCacheFilesAreOnlyBelongRequestedLocales()
153     {
154         $translator = new Translator('a', null, $this->tmpDir);
155         $translator->setFallbackLocales(array('b'));
156         $translator->trans('bar');
157
158         $cachedFiles = glob($this->tmpDir.'/*.php');
159
160         $this->assertCount(1, $cachedFiles);
161     }
162
163     public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
164     {
165         /*
166          * Because the cache file contains a catalogue including all of its fallback
167          * catalogues, we must take the set of fallback locales into consideration when
168          * loading a catalogue from the cache.
169          */
170         $translator = new Translator('a', null, $this->tmpDir);
171         $translator->setFallbackLocales(array('b'));
172
173         $translator->addLoader('array', new ArrayLoader());
174         $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
175         $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
176
177         $this->assertEquals('bar (b)', $translator->trans('bar'));
178
179         // Remove fallback locale
180         $translator->setFallbackLocales(array());
181         $this->assertEquals('bar', $translator->trans('bar'));
182
183         // Use a fresh translator with no fallback locales, result should be the same
184         $translator = new Translator('a', null, $this->tmpDir);
185
186         $translator->addLoader('array', new ArrayLoader());
187         $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
188         $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
189
190         $this->assertEquals('bar', $translator->trans('bar'));
191     }
192
193     public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
194     {
195         /*
196          * As a safeguard against potential BC breaks, make sure that primary and fallback
197          * catalogues (reachable via getFallbackCatalogue()) always contain the full set of
198          * messages provided by the loader. This must also be the case when these catalogues
199          * are (internally) read from a cache.
200          *
201          * Optimizations inside the translator must not change this behaviour.
202          */
203
204         /*
205          * Create a translator that loads two catalogues for two different locales.
206          * The catalogues contain distinct sets of messages.
207          */
208         $translator = new Translator('a', null, $this->tmpDir);
209         $translator->setFallbackLocales(array('b'));
210
211         $translator->addLoader('array', new ArrayLoader());
212         $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
213         $translator->addResource('array', array('foo' => 'foo (b)'), 'b');
214         $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
215
216         $catalogue = $translator->getCatalogue('a');
217         $this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message.
218
219         $fallback = $catalogue->getFallbackCatalogue();
220         $this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b"
221
222         /*
223          * Now, repeat the same test.
224          * Behind the scenes, the cache is used. But that should not matter, right?
225          */
226         $translator = new Translator('a', null, $this->tmpDir);
227         $translator->setFallbackLocales(array('b'));
228
229         $translator->addLoader('array', new ArrayLoader());
230         $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
231         $translator->addResource('array', array('foo' => 'foo (b)'), 'b');
232         $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
233
234         $catalogue = $translator->getCatalogue('a');
235         $this->assertFalse($catalogue->defines('bar'));
236
237         $fallback = $catalogue->getFallbackCatalogue();
238         $this->assertTrue($fallback->defines('foo'));
239     }
240
241     public function testRefreshCacheWhenResourcesAreNoLongerFresh()
242     {
243         $resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
244         $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
245         $resource->method('isFresh')->will($this->returnValue(false));
246         $loader
247             ->expects($this->exactly(2))
248             ->method('load')
249             ->will($this->returnValue($this->getCatalogue('fr', array(), array($resource))));
250
251         // prime the cache
252         $translator = new Translator('fr', null, $this->tmpDir, true);
253         $translator->addLoader('loader', $loader);
254         $translator->addResource('loader', 'foo', 'fr');
255         $translator->trans('foo');
256
257         // prime the cache second time
258         $translator = new Translator('fr', null, $this->tmpDir, true);
259         $translator->addLoader('loader', $loader);
260         $translator->addResource('loader', 'foo', 'fr');
261         $translator->trans('foo');
262     }
263
264     protected function getCatalogue($locale, $messages, $resources = array())
265     {
266         $catalogue = new MessageCatalogue($locale);
267         foreach ($messages as $key => $translation) {
268             $catalogue->set($key, $translation);
269         }
270         foreach ($resources as $resource) {
271             $catalogue->addResource($resource);
272         }
273
274         return $catalogue;
275     }
276
277     public function runForDebugAndProduction()
278     {
279         return array(array(true), array(false));
280     }
281
282     /**
283      * @return LoaderInterface
284      */
285     private function createFailingLoader()
286     {
287         $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
288         $loader
289             ->expects($this->never())
290             ->method('load');
291
292         return $loader;
293     }
294 }
295
296 class StaleResource implements SelfCheckingResourceInterface
297 {
298     public function isFresh($timestamp)
299     {
300         return false;
301     }
302
303     public function getResource()
304     {
305     }
306
307     public function __toString()
308     {
309         return '';
310     }
311 }