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',
65 * Tests time based caching.
67 * @see views_plugin_cache_time
69 public function testTimeResultCaching() {
70 $view = Views::getView('test_cache');
72 $view->display_handler->overrideOption('cache', [
75 'results_lifespan' => '3600',
76 'output_lifespan' => '3600',
80 // Test the default (non-paged) display.
81 $this->executeView($view);
83 $this->assertEqual(5, count($view->result), 'The number of returned rows match.');
85 // Add another man to the beatles.
87 'name' => 'Rod Davis',
91 db_insert('views_test_data')->fields($record)->execute();
93 // The result should be the same as before, because of the caching. (Note
94 // that views_test_data records don't have associated cache tags, and hence
95 // the results cache items aren't invalidated.)
97 $this->executeView($view);
99 $this->assertEqual(5, count($view->result), 'The number of returned rows match.');
103 * Tests result caching with filters.
105 * @see views_plugin_cache_time
107 public function testTimeResultCachingWithFilter() {
108 // Check that we can find the test filter plugin.
109 $plugin = $this->container->get('plugin.manager.views.filter')->createInstance('test_filter');
110 $this->assertTrue($plugin instanceof FilterPlugin, 'Test filter plugin found.');
112 $view = Views::getView('test_filter');
113 $view->initDisplay();
114 $view->display_handler->overrideOption('cache', [
117 'results_lifespan' => '3600',
118 'output_lifespan' => '3600',
122 // Change the filtering.
123 $view->displayHandlers->get('default')->overrideOption('filters', [
125 'id' => 'test_filter',
126 'table' => 'views_test_data',
134 $this->executeView($view);
136 // Get the cache item.
137 $cid1 = $view->display_handler->getPlugin('cache')->generateResultsKey();
139 // Build the expected result.
140 $dataset = [['name' => 'John']];
142 // Verify the result.
143 $this->assertEqual(1, count($view->result), 'The number of returned rows match.');
144 $this->assertIdenticalResultSet($view, $dataset, [
145 'views_test_data_name' => 'name',
150 $view->initDisplay();
152 // Change the filtering.
153 $view->displayHandlers->get('default')->overrideOption('filters', [
155 'id' => 'test_filter',
156 'table' => 'views_test_data',
164 $this->executeView($view);
166 // Get the cache item.
167 $cid2 = $view->display_handler->getPlugin('cache')->generateResultsKey();
168 $this->assertNotEqual($cid1, $cid2, "Results keys are different.");
170 // Build the expected result.
171 $dataset = [['name' => 'Ringo']];
173 // Verify the result.
174 $this->assertEqual(1, count($view->result), 'The number of returned rows match.');
175 $this->assertIdenticalResultSet($view, $dataset, [
176 'views_test_data_name' => 'name',
181 * Tests result caching with a pager.
183 public function testTimeResultCachingWithPager() {
184 $view = Views::getView('test_cache');
186 $view->display_handler->overrideOption('cache', [
189 'results_lifespan' => '3600',
190 'output_lifespan' => '3600',
194 $mapping = ['views_test_data_name' => 'name'];
196 $view->setDisplay('page_1');
197 $view->setCurrentPage(0);
198 $this->executeView($view);
199 $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping);
202 $view->setDisplay('page_1');
203 $view->setCurrentPage(1);
204 $this->executeView($view);
205 $this->assertIdenticalResultset($view, [['name' => 'Ringo'], ['name' => 'Paul']], $mapping);
208 $view->setDisplay('page_1');
209 $view->setCurrentPage(0);
210 $this->executeView($view);
211 $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping);
214 $view->setDisplay('page_1');
215 $view->setCurrentPage(2);
216 $this->executeView($view);
217 $this->assertIdenticalResultset($view, [['name' => 'Meredith']], $mapping);
224 * @see views_plugin_cache_time
226 public function testNoneResultCaching() {
227 // Create a basic result which just 2 results.
228 $view = Views::getView('test_cache');
230 $view->display_handler->overrideOption('cache', [
235 $this->executeView($view);
236 // Verify the result.
237 $this->assertEqual(5, count($view->result), 'The number of returned rows match.');
239 // Add another man to the beatles.
241 'name' => 'Rod Davis',
245 db_insert('views_test_data')->fields($record)->execute();
247 // The Result changes, because the view is not cached.
248 $view = Views::getView('test_cache');
250 $view->display_handler->overrideOption('cache', [
255 $this->executeView($view);
256 // Verify the result.
257 $this->assertEqual(6, count($view->result), 'The number of returned rows match.');
261 * Tests css/js storage and restoring mechanism.
263 public function testHeaderStorage() {
264 // Create a view with output caching enabled.
265 // Some hook_views_pre_render in views_test_data.module adds the test css/js file.
266 // so they should be added to the css/js storage.
267 $view = Views::getView('test_view');
269 $view->storage->set('id', 'test_cache_header_storage');
270 $view->display_handler->overrideOption('cache', [
273 'output_lifespan' => '3600',
277 $output = $view->buildRenderable();
278 /** @var \Drupal\Core\Render\RendererInterface $renderer */
279 $renderer = \Drupal::service('renderer');
280 $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
281 return $renderer->render($output);
284 unset($view->pre_render_called);
288 $output = $view->buildRenderable();
289 $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
290 return $renderer->render($output);
293 $this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
294 $this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');
295 // Note: views_test_data_views_pre_render() adds some cache tags.
296 $this->assertEqual(['config:views.view.test_cache_header_storage', 'views_test_data:1'], $output['#cache']['tags']);
297 $this->assertEqual(['non-existing-placeholder-just-for-testing-purposes' => ['#lazy_builder' => ['views_test_data_placeholders', ['bar']]]], $output['#attached']['placeholders']);
298 $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
302 * Tests that Subqueries are cached as expected.
304 public function testSubqueryStringCache() {
306 $view = Views::getView('test_groupwise_term_ui');
308 $this->executeView($view);
309 // Request for the cache.
310 $cid = 'views_relationship_groupwise_max:test_groupwise_term_ui:default:tid_representative';
311 $cache = \Drupal::cache('data')->get($cid);
312 $this->assertEqual($cid, $cache->cid, 'Subquery String cached as expected.');
316 * Tests the data contained in cached items.
318 public function testCacheData() {
319 for ($i = 1; $i <= 5; $i++) {
321 'title' => $this->randomMachineName(8),
326 $view = Views::getView('test_display');
328 $view->display_handler->overrideOption('cache', [
331 'results_lifespan' => '3600',
332 'output_lifespan' => '3600',
335 $this->executeView($view);
337 // Get the cache item.
338 $cid = $view->display_handler->getPlugin('cache')->generateResultsKey();
339 $cache = \Drupal::cache('data')->get($cid);
341 // Assert there are results, empty results would mean this test case would
343 $this->assertTrue(count($cache->data['result']), 'Results saved in cached data.');
345 // Assert each row doesn't contain '_entity' or '_relationship_entities'
347 foreach ($cache->data['result'] as $row) {
348 $this->assertIdentical($row->_entity, NULL, 'Cached row "_entity" property is NULL');
349 $this->assertIdentical($row->_relationship_entities, [], 'Cached row "_relationship_entities" property is empty');
354 * Tests the cache context integration for views result cache.
356 public function testCacheContextIntegration() {
357 $view = Views::getView('test_cache');
358 $view->setDisplay('page_2');
359 \Drupal::state()->set('views_test_cache_context', 'George');
360 $this->executeView($view);
362 $map = ['views_test_data_name' => 'name'];
363 $this->assertIdenticalResultset($view, [['name' => 'George']], $map);
365 // Update the entry in the DB to ensure that result caching works.
366 \Drupal::database()->update('views_test_data')
367 ->condition('name', 'George')
368 ->fields(['name' => 'egroeG'])
371 $view = Views::getView('test_cache');
372 $view->setDisplay('page_2');
373 $this->executeView($view);
374 $this->assertIdenticalResultset($view, [['name' => 'George']], $map);
376 // Now change the cache context value, a different query should be executed.
377 $view = Views::getView('test_cache');
378 $view->setDisplay('page_2');
379 \Drupal::state()->set('views_test_cache_context', 'Paul');
380 $this->executeView($view);
382 $this->assertIdenticalResultset($view, [['name' => 'Paul']], $map);
386 * Tests that cacheability metadata is carried over from argument defaults.
388 public function testArgumentDefaultCache() {
389 $view = Views::getView('test_view');
391 // Add a new argument and set the test plugin for the argument_default.
393 'default_argument_type' => 'argument_default_test',
394 'default_argument_options' => [
397 'default_action' => 'default',
399 $view->addHandler('default', 'argument', 'views_test_data', 'name', $options);
400 $view->initHandlers();
402 $output = $view->preview();
404 /** @var \Drupal\Core\Render\RendererInterface $renderer */
405 $renderer = \Drupal::service('renderer');
407 $renderer->renderPlain($output);
408 $this->assertEquals(['config:views.view.test_view', 'example_tag'], $output['#cache']['tags']);