Commit c8dbe3a9 authored by catch's avatar catch

Issue #2893117 by Mile23, Berdir: Improve HTML caching of Simpletest UI test form

parent 4838f6d6
......@@ -661,10 +661,6 @@ function simpletest_clean_environment() {
else {
drupal_set_message(t('Clear results is disabled and the test results table will not be cleared.'), 'warning');
}
// Detect test classes that have been added, renamed or deleted.
\Drupal::cache()->delete('simpletest');
\Drupal::cache()->delete('simpletest_phpunit');
}
/**
......
services:
test_discovery:
class: Drupal\simpletest\TestDiscovery
arguments: ['@app.root', '@class_loader', '@module_handler', '@?cache.discovery']
arguments: ['@app.root', '@class_loader', '@module_handler']
cache_context.test_discovery:
class: Drupal\simpletest\Cache\Context\TestDiscoveryCacheContext
arguments: ['@test_discovery', '@private_key']
tags:
- { name: cache.context}
<?php
namespace Drupal\simpletest\Cache\Context;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
use Drupal\Core\PrivateKey;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\TestDiscovery;
/**
* Defines the TestDiscoveryCacheContext service.
*
* Cache context ID: 'test_discovery'.
*/
class TestDiscoveryCacheContext implements CacheContextInterface {
/**
* The test discovery service.
*
* @var \Drupal\simpletest\TestDiscovery
*/
protected $testDiscovery;
/**
* The private key service.
*
* @var \Drupal\Core\PrivateKey
*/
protected $privateKey;
/**
* The hash of discovered test information.
*
* Services should not be stateful, but we only keep this information per
* request. That way we don't perform a file scan every time we need this
* hash. The test scan results are unlikely to change during the request.
*
* @var string
*/
protected $hash;
/**
* Construct a test discovery cache context.
*
* @param \Drupal\simpletest\TestDiscovery $test_discovery
* The test discovery service.
* @param \Drupal\Core\PrivateKey $private_key
* The private key service.
*/
public function __construct(TestDiscovery $test_discovery, PrivateKey $private_key) {
$this->testDiscovery = $test_discovery;
$this->privateKey = $private_key;
}
/**
* {@inheritdoc}
*/
public static function getLabel() {
return t('Test discovery');
}
/**
* {@inheritdoc}
*/
public function getContext() {
if (empty($this->hash)) {
$tests = $this->testDiscovery->getTestClasses();
$this->hash = $this->hash(serialize($tests));
}
return $this->hash;
}
/**
* {@inheritdoc}
*/
public function getCacheableMetadata() {
return new CacheableMetadata();
}
/**
* Hashes the given string.
*
* @param string $identifier
* The string to be hashed.
*
* @return string
* The hash.
*/
protected function hash($identifier) {
return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . $identifier);
}
}
......@@ -110,6 +110,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
];
$form['tests'] = [
'#cache' => [
'keys' => ['simpletest_ui_table'],
'contexts' => ['test_discovery'],
],
'#type' => 'table',
'#id' => 'simpletest-form-table',
'#tableselect' => TRUE,
......
......@@ -6,7 +6,6 @@
use Doctrine\Common\Reflection\StaticReflectionParser;
use Drupal\Component\Annotation\Reflection\MockFileFinder;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\simpletest\Exception\MissingGroupException;
......@@ -25,11 +24,11 @@ class TestDiscovery {
protected $classLoader;
/**
* Backend for caching discovery results.
* Statically cached list of test classes.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
* @var array
*/
protected $cacheBackend;
protected $testClasses;
/**
* Cached map of all test namespaces to respective directories.
......@@ -70,14 +69,11 @@ class TestDiscovery {
* \Symfony\Component\ClassLoader\ApcClassLoader.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* (optional) Backend for caching discovery results.
*/
public function __construct($root, $class_loader, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend = NULL) {
public function __construct($root, $class_loader, ModuleHandlerInterface $module_handler) {
$this->root = $root;
$this->classLoader = $class_loader;
$this->moduleHandler = $module_handler;
$this->cacheBackend = $cache_backend;
}
/**
......@@ -159,9 +155,9 @@ public function getTestClasses($extension = NULL, array $types = []) {
$reader = new SimpleAnnotationReader();
$reader->addNamespace('Drupal\\simpletest\\Annotation');
if (!isset($extension)) {
if ($this->cacheBackend && $cache = $this->cacheBackend->get('simpletest:discovery:classes')) {
return $cache->data;
if (!isset($extension) && empty($types)) {
if (!empty($this->testClasses)) {
return $this->testClasses;
}
}
$list = [];
......@@ -215,10 +211,8 @@ public function getTestClasses($extension = NULL, array $types = []) {
// Allow modules extending core tests to disable originals.
$this->moduleHandler->alter('simpletest', $list);
if (!isset($extension)) {
if ($this->cacheBackend) {
$this->cacheBackend->set('simpletest:discovery:classes', $list);
}
if (!isset($extension) && empty($types)) {
$this->testClasses = $list;
}
if ($types) {
......
<?php
namespace Drupal\Tests\simpletest\Kernel\Cache\Context;
use Drupal\KernelTests\KernelTestBase;
use Drupal\simpletest\Cache\Context\TestDiscoveryCacheContext;
use Drupal\simpletest\TestDiscovery;
/**
* @group simpletest
*/
class TestDiscoveryCacheContextTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['simpletest'];
/**
* Tests that test context hashes are unique.
*/
public function testContext() {
// Mock test discovery.
$discovery = $this->getMockBuilder(TestDiscovery::class)
->setMethods(['getTestClasses'])
->disableOriginalConstructor()
->getMock();
// Set getTestClasses() to return different results on subsequent calls.
// This emulates changed tests in the filesystem.
$discovery->expects($this->any())
->method('getTestClasses')
->willReturnOnConsecutiveCalls(
['group1' => ['Test']],
['group2' => ['Test2']]
);
// Make our cache context object.
$cache_context = new TestDiscoveryCacheContext($discovery, $this->container->get('private_key'));
// Generate a context hash.
$context_hash = $cache_context->getContext();
// Since the context stores the hash, we have to reset it.
$hash_ref = new \ReflectionProperty($cache_context, 'hash');
$hash_ref->setAccessible(TRUE);
$hash_ref->setValue($cache_context, NULL);
// And then assert that we did not generate the same hash for different
// content.
$this->assertNotSame($context_hash, $cache_context->getContext());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment