3 namespace Drupal\Tests\views\Kernel\Plugin;
5 use Drupal\Core\Render\RenderContext;
6 use Drupal\node\Entity\Node;
7 use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
8 use Drupal\views\Views;
9 use Drupal\views_test_data\Plugin\views\filter\FilterTest as FilterPlugin;
12 * Tests pluggable caching for views.
15 * @see views_plugin_cache
17 class CacheTest extends ViewsKernelTestBase {
20 * Views used by this test.
24 public static $testViews = ['test_view', 'test_cache', 'test_groupwise_term_ui', 'test_display', 'test_filter'];
31 public static $modules = ['taxonomy', 'text', 'user', 'node'];
36 protected function setUp($import_test_views = TRUE) {
37 parent::setUp($import_test_views);
39 $this->installEntitySchema('node');
40 $this->installEntitySchema('taxonomy_term');
41 $this->installEntitySchema('user');
43 // Setup the current time properly.
44 \Drupal::request()->server->set('REQUEST_TIME', time());
50 protected function viewsData() {
51 $data = parent::viewsData();
53 $data['views_test_data']['test_cache_context'] = [
54 'real field' => 'name',
55 'title' => 'Test cache context',
57 'id' => 'views_test_test_cache_context',
66 * Tests time based caching.
68 * @see views_plugin_cache_time
70 public function testTimeResultCaching() {
71 $view = Views::getView('test_cache');
73 $view->display_handler->overrideOption('cache', [
76 'results_lifespan' => '3600',
77 'output_lifespan' => '3600',
81 // Test the default (non-paged) display.
82 $this->executeView($view);
84 $this->assertEqual(5, count($view->result), 'The number of returned rows match.');
86 // Add another man to the beatles.
88 'name' => 'Rod Davis',
92 db_insert('views_test_data')->fields($record)->execute();
94 // The result should be the same as before, because of the caching. (Note
95 // that views_test_data records don't have associated cache tags, and hence
96 // the results cache items aren't invalidated.)
98 $this->executeView($view);
100 $this->assertEqual(5, count($view->result), 'The number of returned rows match.');
104 * Tests result caching with filters.
106 * @see views_plugin_cache_time
108 public function testTimeResultCachingWithFilter() {
109 // Check that we can find the test filter plugin.
110 $plugin = $this->container->get('plugin.manager.views.filter')->createInstance('test_filter');
111 $this->assertTrue($plugin instanceof FilterPlugin, 'Test filter plugin found.');
113 $view = Views::getView('test_filter');
114 $view->initDisplay();
115 $view->display_handler->overrideOption('cache', [
118 'results_lifespan' => '3600',
119 'output_lifespan' => '3600',
123 // Change the filtering.
124 $view->displayHandlers->get('default')->overrideOption('filters', [
126 'id' => 'test_filter',
127 'table' => 'views_test_data',
135 $this->executeView($view);
137 // Get the cache item.
138 $cid1 = $view->display_handler->getPlugin('cache')->generateResultsKey();
140 // Build the expected result.
141 $dataset = [['name' => 'John']];
143 // Verify the result.
144 $this->assertEqual(1, count($view->result), 'The number of returned rows match.');
145 $this->assertIdenticalResultSet($view, $dataset, [
146 'views_test_data_name' => 'name',
151 $view->initDisplay();
153 // Change the filtering.
154 $view->displayHandlers->get('default')->overrideOption('filters', [
156 'id' => 'test_filter',
157 'table' => 'views_test_data',
165 $this->executeView($view);
167 // Get the cache item.
168 $cid2 = $view->display_handler->getPlugin('cache')->generateResultsKey();
169 $this->assertNotEqual($cid1, $cid2, "Results keys are different.");
171 // Build the expected result.
172 $dataset = [['name' => 'Ringo']];
174 // Verify the result.
175 $this->assertEqual(1, count($view->result), 'The number of returned rows match.');
176 $this->assertIdenticalResultSet($view, $dataset, [
177 'views_test_data_name' => 'name',
182 * Tests result caching with a pager.
184 public function testTimeResultCachingWithPager() {
185 $view = Views::getView('test_cache');
187 $view->display_handler->overrideOption('cache', [
190 'results_lifespan' => '3600',
191 'output_lifespan' => '3600',
195 $mapping = ['views_test_data_name' => 'name'];
197 $view->setDisplay('page_1');
198 $view->setCurrentPage(0);
199 $this->executeView($view);
200 $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping);
203 $view->setDisplay('page_1');
204 $view->setCurrentPage(1);
205 $this->executeView($view);
206 $this->assertIdenticalResultset($view, [['name' => 'Ringo'], ['name' => 'Paul']], $mapping);
209 $view->setDisplay('page_1');
210 $view->setCurrentPage(0);
211 $this->executeView($view);
212 $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping);
215 $view->setDisplay('page_1');
216 $view->setCurrentPage(2);
217 $this->executeView($view);
218 $this->assertIdenticalResultset($view, [['name' => 'Meredith']], $mapping);
225 * @see views_plugin_cache_time
227 public function testNoneResultCaching() {
228 // Create a basic result which just 2 results.
229 $view = Views::getView('test_cache');
231 $view->display_handler->overrideOption('cache', [
236 $this->executeView($view);
237 // Verify the result.
238 $this->assertEqual(5, count($view->result), 'The number of returned rows match.');
240 // Add another man to the beatles.
242 'name' => 'Rod Davis',
246 db_insert('views_test_data')->fields($record)->execute();
248 // The Result changes, because the view is not cached.
249 $view = Views::getView('test_cache');
251 $view->display_handler->overrideOption('cache', [
256 $this->executeView($view);
257 // Verify the result.
258 $this->assertEqual(6, count($view->result), 'The number of returned rows match.');
262 * Tests css/js storage and restoring mechanism.
264 public function testHeaderStorage() {
265 // Create a view with output caching enabled.
266 // Some hook_views_pre_render in views_test_data.module adds the test css/js file.
267 // so they should be added to the css/js storage.
268 $view = Views::getView('test_view');
270 $view->storage->set('id', 'test_cache_header_storage');
271 $view->display_handler->overrideOption('cache', [
274 'output_lifespan' => '3600',
278 $output = $view->buildRenderable();
279 /** @var \Drupal\Core\Render\RendererInterface $renderer */
280 $renderer = \Drupal::service('renderer');
281 $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
282 return $renderer->render($output);
285 unset($view->pre_render_called);
289 $output = $view->buildRenderable();
290 $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
291 return $renderer->render($output);
294 $this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
295 $this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');
296 // Note: views_test_data_views_pre_render() adds some cache tags.
297 $this->assertEqual(['config:views.view.test_cache_header_storage', 'views_test_data:1'], $output['#cache']['tags']);
298 $this->assertEqual(['non-existing-placeholder-just-for-testing-purposes' => ['#lazy_builder' => ['views_test_data_placeholders', ['bar']]]], $output['#attached']['placeholders']);
299 $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
303 * Tests that Subqueries are cached as expected.
305 public function testSubqueryStringCache() {
307 $view = Views::getView('test_groupwise_term_ui');
309 $this->executeView($view);
310 // Request for the cache.
311 $cid = 'views_relationship_groupwise_max:test_groupwise_term_ui:default:tid_representative';
312 $cache = \Drupal::cache('data')->get($cid);
313 $this->assertEqual($cid, $cache->cid, 'Subquery String cached as expected.');
317 * Tests the data contained in cached items.
319 public function testCacheData() {
320 for ($i = 1; $i <= 5; $i++) {
322 'title' => $this->randomMachineName(8),
327 $view = Views::getView('test_display');
329 $view->display_handler->overrideOption('cache', [
332 'results_lifespan' => '3600',
333 'output_lifespan' => '3600',
336 $this->executeView($view);
338 // Get the cache item.
339 $cid = $view->display_handler->getPlugin('cache')->generateResultsKey();
340 $cache = \Drupal::cache('data')->get($cid);
342 // Assert there are results, empty results would mean this test case would
344 $this->assertTrue(count($cache->data['result']), 'Results saved in cached data.');
346 // Assert each row doesn't contain '_entity' or '_relationship_entities'
348 foreach ($cache->data['result'] as $row) {
349 $this->assertIdentical($row->_entity, NULL, 'Cached row "_entity" property is NULL');
350 $this->assertIdentical($row->_relationship_entities, [], 'Cached row "_relationship_entities" property is empty');
355 * Tests the cache context integration for views result cache.
357 public function testCacheContextIntegration() {
358 $view = Views::getView('test_cache');
359 $view->setDisplay('page_2');
360 \Drupal::state()->set('views_test_cache_context', 'George');
361 $this->executeView($view);
363 $map = ['views_test_data_name' => 'name'];
364 $this->assertIdenticalResultset($view, [['name' => 'George']], $map);
366 // Update the entry in the DB to ensure that result caching works.
367 \Drupal::database()->update('views_test_data')
368 ->condition('name', 'George')
369 ->fields(['name' => 'egroeG'])
372 $view = Views::getView('test_cache');
373 $view->setDisplay('page_2');
374 $this->executeView($view);
375 $this->assertIdenticalResultset($view, [['name' => 'George']], $map);
377 // Now change the cache context value, a different query should be executed.
378 $view = Views::getView('test_cache');
379 $view->setDisplay('page_2');
380 \Drupal::state()->set('views_test_cache_context', 'Paul');
381 $this->executeView($view);
383 $this->assertIdenticalResultset($view, [['name' => 'Paul']], $map);
387 * Tests that cacheability metadata is carried over from argument defaults.
389 public function testArgumentDefaultCache() {
390 $view = Views::getView('test_view');
392 // Add a new argument and set the test plugin for the argument_default.
394 'default_argument_type' => 'argument_default_test',
395 'default_argument_options' => [
398 'default_action' => 'default'
400 $view->addHandler('default', 'argument', 'views_test_data', 'name', $options);
401 $view->initHandlers();
403 $output = $view->preview();
405 /** @var \Drupal\Core\Render\RendererInterface $renderer */
406 $renderer = \Drupal::service('renderer');
408 $renderer->renderPlain($output);
409 $this->assertEquals(['config:views.view.test_view', 'example_tag'], $output['#cache']['tags']);