Completed
Push — master ( a9e1ce...416b3d )
by Sam
22s
created

SSViewerCacheBlockTest::getExtraDataObjects()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\View\Tests;
4
5
use SilverStripe\Core\Injector\Injector;
6
use SilverStripe\Versioned\Versioned;
7
use Psr\SimpleCache\CacheInterface;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\Control\Director;
10
use SilverStripe\View\SSViewer;
11
use Symfony\Component\Cache\Simple\FilesystemCache;
12
use Symfony\Component\Cache\Simple\NullCache;
13
14
// Not actually a data object, we just want a ViewableData object that's just for us
15
16
class SSViewerCacheBlockTest extends SapphireTest
17
{
18
    protected $extraDataObjects = array(
19
        SSViewerCacheBlockTest\TestModel::class
20
    );
21
22
    protected function getExtraDataObjects()
23
    {
24
        $classes = parent::getExtraDataObjects();
25
26
        // Add extra classes if versioning is enabled
27
        if (class_exists(Versioned::class)) {
28
            $classes[] = SSViewerCacheBlockTest\VersionedModel::class;
29
        }
30
        return $classes;
31
    }
32
33
    /**
34
     * @var SSViewerCacheBlockTest\TestModel
35
     */
36
    protected $data = null;
37
38
    protected function _reset($cacheOn = true)
39
    {
40
        $this->data = new SSViewerCacheBlockTest\TestModel();
41
42
        $cache = null;
43
        if ($cacheOn) {
44
            $cache = new FilesystemCache('cacheblock', 0, getTempFolder()); // cache indefinitely
45
        } else {
46
            $cache = new NullCache();
47
        }
48
49
        Injector::inst()->registerService($cache, CacheInterface::class . '.cacheblock');
50
        Injector::inst()->get(CacheInterface::class . '.cacheblock')->clear();
51
    }
52
53
    protected function _runtemplate($template, $data = null)
54
    {
55
        if ($data === null) {
56
            $data = $this->data;
57
        }
58
        if (is_array($data)) {
59
            $data = $this->data->customise($data);
60
        }
61
62
        return SSViewer::execute_string($template, $data);
63
    }
64
65
    public function testParsing()
66
    {
67
68
        // ** Trivial checks **
69
70
        // Make sure an empty cached block parses
71
        $this->_reset();
72
        $this->assertEquals($this->_runtemplate('<% cached %><% end_cached %>'), '');
73
74
        // Make sure an empty cacheblock block parses
75
        $this->_reset();
76
        $this->assertEquals($this->_runtemplate('<% cacheblock %><% end_cacheblock %>'), '');
77
78
        // Make sure an empty uncached block parses
79
        $this->_reset();
80
        $this->assertEquals($this->_runtemplate('<% uncached %><% end_uncached %>'), '');
81
82
        // ** Argument checks **
83
84
        // Make sure a simple cacheblock parses
85
        $this->_reset();
86
        $this->assertEquals($this->_runtemplate('<% cached %>Yay<% end_cached %>'), 'Yay');
87
88
        // Make sure a moderately complicated cacheblock parses
89
        $this->_reset();
90
        $this->assertEquals($this->_runtemplate('<% cached \'block\', Foo, "jumping" %>Yay<% end_cached %>'), 'Yay');
91
92
        // Make sure a complicated cacheblock parses
93
        $this->_reset();
94
        $this->assertEquals(
95
            $this->_runtemplate(
96
                '<% cached \'block\', Foo, Test.Test(4).Test(jumping).Foo %>Yay<% end_cached %>'
97
            ),
98
            'Yay'
99
        );
100
101
        // ** Conditional Checks **
102
103
        // Make sure a cacheblock with a simple conditional parses
104
        $this->_reset();
105
        $this->assertEquals($this->_runtemplate('<% cached if true %>Yay<% end_cached %>'), 'Yay');
106
107
        // Make sure a cacheblock with a complex conditional parses
108
        $this->_reset();
109
        $this->assertEquals($this->_runtemplate('<% cached if Test.Test(yank).Foo %>Yay<% end_cached %>'), 'Yay');
110
111
        // Make sure a cacheblock with a complex conditional and arguments parses
112
        $this->_reset();
113
        $this->assertEquals(
114
            $this->_runtemplate(
115
                '<% cached Foo, Test.Test(4).Test(jumping).Foo if Test.Test(yank).Foo %>Yay<% end_cached %>'
116
            ),
117
            'Yay'
118
        );
119
    }
120
121
    /**
122
     * Test that cacheblocks actually cache
123
     */
124
    public function testBlocksCache()
125
    {
126
        // First, run twice without caching, to prove we get two different values
127
        $this->_reset(false);
128
129
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
130
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
131
132
        // Then twice with caching, should get same result each time
133
        $this->_reset(true);
134
135
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
136
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
137
    }
138
139
    /**
140
     * Test that the cacheblocks invalidate when a flush occurs.
141
     */
142
    public function testBlocksInvalidateOnFlush()
143
    {
144
        Director::test('/?flush=1');
145
        $this->_reset(true);
146
147
        // Generate cached value for foo = 1
148
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
149
150
        // Test without flush
151
        Director::test('/');
152
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 3)), '1');
153
154
        // Test with flush
155
        Director::test('/?flush=1');
156
        $this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
157
    }
158
159
    public function testVersionedCache()
160
    {
161
        if (!class_exists(Versioned::class)) {
162
            $this->markTestSkipped('testVersionedCache requires Versioned extension');
163
        }
164
        $origReadingMode = Versioned::get_reading_mode();
165
166
        // Run without caching in stage to prove data is uncached
167
        $this->_reset(false);
168
        Versioned::set_stage(Versioned::DRAFT);
169
        $data = new SSViewerCacheBlockTest\VersionedModel();
170
        $data->setEntropy('default');
171
        $this->assertEquals(
172
            'default Stage.Stage',
173
            SSViewer::execute_string('<% cached %>$Inspect<% end_cached %>', $data)
174
        );
175
        $data = new SSViewerCacheBlockTest\VersionedModel();
176
        $data->setEntropy('first');
177
        $this->assertEquals(
178
            'first Stage.Stage',
179
            SSViewer::execute_string('<% cached %>$Inspect<% end_cached %>', $data)
180
        );
181
182
        // Run without caching in live to prove data is uncached
183
        $this->_reset(false);
184
        Versioned::set_stage(Versioned::LIVE);
185
        $data = new SSViewerCacheBlockTest\VersionedModel();
186
        $data->setEntropy('default');
187
        $this->assertEquals(
188
            'default Stage.Live',
189
            $this->_runtemplate('<% cached %>$Inspect<% end_cached %>', $data)
190
        );
191
        $data = new SSViewerCacheBlockTest\VersionedModel();
192
        $data->setEntropy('first');
193
        $this->assertEquals(
194
            'first Stage.Live',
195
            $this->_runtemplate('<% cached %>$Inspect<% end_cached %>', $data)
196
        );
197
198
        // Then with caching, initially in draft, and then in live, to prove that
199
        // changing the versioned reading mode doesn't cache between modes, but it does
200
        // within them
201
        $this->_reset(true);
202
        Versioned::set_stage(Versioned::DRAFT);
203
        $data = new SSViewerCacheBlockTest\VersionedModel();
204
        $data->setEntropy('default');
205
        $this->assertEquals(
206
            'default Stage.Stage',
207
            $this->_runtemplate('<% cached %>$Inspect<% end_cached %>', $data)
208
        );
209
        $data = new SSViewerCacheBlockTest\VersionedModel();
210
        $data->setEntropy('first');
211
        $this->assertEquals(
212
            'default Stage.Stage', // entropy should be ignored due to caching
213
            $this->_runtemplate('<% cached %>$Inspect<% end_cached %>', $data)
214
        );
215
216
        Versioned::set_stage(Versioned::LIVE);
217
        $data = new SSViewerCacheBlockTest\VersionedModel();
218
        $data->setEntropy('first');
219
        $this->assertEquals(
220
            'first Stage.Live', // First hit in live, so display current entropy
221
            $this->_runtemplate('<% cached %>$Inspect<% end_cached %>', $data)
222
        );
223
        $data = new SSViewerCacheBlockTest\VersionedModel();
224
        $data->setEntropy('second');
225
        $this->assertEquals(
226
            'first Stage.Live', // entropy should be ignored due to caching
227
            $this->_runtemplate('<% cached %>$Inspect<% end_cached %>', $data)
228
        );
229
230
        Versioned::set_reading_mode($origReadingMode);
231
    }
232
233
    /**
234
     * Test that cacheblocks conditionally cache with if
235
     */
236
    public function testBlocksConditionallyCacheWithIf()
237
    {
238
        // First, run twice with caching
239
        $this->_reset(true);
240
241
        $this->assertEquals($this->_runtemplate('<% cached if True %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
242
        $this->assertEquals($this->_runtemplate('<% cached if True %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
243
244
        // Then twice without caching
245
        $this->_reset(true);
246
247
        $this->assertEquals($this->_runtemplate('<% cached if False %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
248
        $this->assertEquals($this->_runtemplate('<% cached if False %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
249
250
        // Then once cached, once not (and the opposite)
251
        $this->_reset(true);
252
253
        $this->assertEquals(
254
            $this->_runtemplate(
255
                '<% cached if Cache %>$Foo<% end_cached %>',
256
                array('Foo' => 1, 'Cache' => true )
257
            ),
258
            '1'
259
        );
260
        $this->assertEquals(
261
            $this->_runtemplate(
262
                '<% cached if Cache %>$Foo<% end_cached %>',
263
                array('Foo' => 2, 'Cache' => false)
264
            ),
265
            '2'
266
        );
267
268
        $this->_reset(true);
269
270
        $this->assertEquals(
271
            $this->_runtemplate(
272
                '<% cached if Cache %>$Foo<% end_cached %>',
273
                array('Foo' => 1, 'Cache' => false)
274
            ),
275
            '1'
276
        );
277
        $this->assertEquals(
278
            $this->_runtemplate(
279
                '<% cached if Cache %>$Foo<% end_cached %>',
280
                array('Foo' => 2, 'Cache' => true )
281
            ),
282
            '2'
283
        );
284
    }
285
286
    /**
287
     * Test that cacheblocks conditionally cache with unless
288
     */
289
    public function testBlocksConditionallyCacheWithUnless()
290
    {
291
        // First, run twice with caching
292
        $this->_reset(true);
293
294
        $this->assertEquals(
295
            $this->_runtemplate(
296
                '<% cached unless False %>$Foo<% end_cached %>',
297
                array('Foo' => 1)
298
            ),
299
            '1'
300
        );
301
        $this->assertEquals(
302
            $this->_runtemplate(
303
                '<% cached unless False %>$Foo<% end_cached %>',
304
                array('Foo' => 2)
305
            ),
306
            '1'
307
        );
308
309
        // Then twice without caching
310
        $this->_reset(true);
311
312
        $this->assertEquals(
313
            $this->_runtemplate(
314
                '<% cached unless True %>$Foo<% end_cached %>',
315
                array('Foo' => 1)
316
            ),
317
            '1'
318
        );
319
        $this->assertEquals(
320
            $this->_runtemplate(
321
                '<% cached unless True %>$Foo<% end_cached %>',
322
                array('Foo' => 2)
323
            ),
324
            '2'
325
        );
326
    }
327
328
    /**
329
     * Test that nested uncached blocks work
330
     */
331
    public function testNestedUncachedBlocks()
332
    {
333
        // First, run twice with caching, to prove we get the same result back normally
334
        $this->_reset(true);
335
336
        $this->assertEquals(
337
            $this->_runtemplate(
338
                '<% cached %> A $Foo B <% end_cached %>',
339
                array('Foo' => 1)
340
            ),
341
            ' A 1 B '
342
        );
343
        $this->assertEquals(
344
            $this->_runtemplate(
345
                '<% cached %> A $Foo B <% end_cached %>',
346
                array('Foo' => 2)
347
            ),
348
            ' A 1 B '
349
        );
350
351
        // Then add uncached to the nested block
352
        $this->_reset(true);
353
354
        $this->assertEquals(
355
            $this->_runtemplate(
356
                '<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>',
357
                array('Foo' => 1)
358
            ),
359
            ' A 1 B '
360
        );
361
        $this->assertEquals(
362
            $this->_runtemplate(
363
                '<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>',
364
                array('Foo' => 2)
365
            ),
366
            ' A 2 B '
367
        );
368
    }
369
370
    /**
371
     * Test that nested blocks with different keys works
372
     */
373
    public function testNestedBlocks()
374
    {
375
        $this->_reset(true);
376
377
        $template = '<% cached Foo %> $Fooa <% cached Bar %>$Bara<% end_cached %> $Foob <% end_cached %>';
378
379
        // Do it the first time to load the cache
380
        $this->assertEquals(
381
            $this->_runtemplate(
382
                $template,
383
                array('Foo' => 1, 'Fooa' => 1, 'Foob' => 3, 'Bar' => 1, 'Bara' => 2)
384
            ),
385
            ' 1 2 3 '
386
        );
387
388
        // Do it again, the input values are ignored as the cache is hit for both elements
389
        $this->assertEquals(
390
            $this->_runtemplate(
391
                $template,
392
                array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 1, 'Bara' => 9)
393
            ),
394
            ' 1 2 3 '
395
        );
396
397
        // Do it again with a new key for Bar, Bara is picked up, Fooa and Foob are not
398
        $this->assertEquals(
399
            $this->_runtemplate(
400
                $template,
401
                array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 9)
402
            ),
403
            ' 1 9 3 '
404
        );
405
406
        // Do it again with a new key for Foo, Fooa and Foob are picked up, Bara are not
407
        $this->assertEquals(
408
            $this->_runtemplate(
409
                $template,
410
                array('Foo' => 2, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 1)
411
            ),
412
            ' 9 9 9 '
413
        );
414
    }
415
416
    public function testNoErrorMessageForControlWithinCached()
417
    {
418
        $this->_reset(true);
419
        $this->assertNotNull($this->_runtemplate('<% cached %><% with Foo %>$Bar<% end_with %><% end_cached %>'));
420
    }
421
422
    /**
423
     * @expectedException \SilverStripe\View\SSTemplateParseException
424
     */
425
    public function testErrorMessageForCachedWithinControlWithinCached()
426
    {
427
        $this->_reset(true);
428
        $this->_runtemplate(
429
            '<% cached %><% with Foo %><% cached %>$Bar<% end_cached %><% end_with %><% end_cached %>'
430
        );
431
    }
432
433
    public function testNoErrorMessageForCachedWithinControlWithinUncached()
434
    {
435
        $this->_reset(true);
436
        $this->assertNotNull(
437
            $this->_runtemplate(
438
                '<% uncached %><% with Foo %><% cached %>$Bar<% end_cached %><% end_with %><% end_uncached %>'
439
            )
440
        );
441
    }
442
443
    /**
444
     * @expectedException \SilverStripe\View\SSTemplateParseException
445
     */
446
    public function testErrorMessageForCachedWithinIf()
447
    {
448
        $this->_reset(true);
449
        $this->_runtemplate('<% cached %><% if Foo %><% cached %>$Bar<% end_cached %><% end_if %><% end_cached %>');
450
    }
451
452
    /**
453
     * @expectedException \SilverStripe\View\SSTemplateParseException
454
     */
455
    public function testErrorMessageForInvalidConditional()
456
    {
457
        $this->_reset(true);
458
        $this->_runtemplate('<% cached Foo if %>$Bar<% end_cached %>');
459
    }
460
}
461