Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\simpletest;
|
Chris@0
|
4
|
Chris@18
|
5 @trigger_error(__NAMESPACE__ . '\KernelTestBase is deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use \Drupal\KernelTests\KernelTestBase instead.', E_USER_DEPRECATED);
|
Chris@18
|
6
|
Chris@0
|
7 use Drupal\Component\Utility\Html;
|
Chris@17
|
8 use Drupal\Component\Render\FormattableMarkup;
|
Chris@0
|
9 use Drupal\Component\Utility\Variable;
|
Chris@0
|
10 use Drupal\Core\Config\Development\ConfigSchemaChecker;
|
Chris@0
|
11 use Drupal\Core\Database\Database;
|
Chris@0
|
12 use Drupal\Core\DependencyInjection\ContainerBuilder;
|
Chris@0
|
13 use Drupal\Core\DrupalKernel;
|
Chris@0
|
14 use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
|
Chris@0
|
15 use Drupal\Core\Extension\ExtensionDiscovery;
|
Chris@18
|
16 use Drupal\Core\File\FileSystemInterface;
|
Chris@0
|
17 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
|
Chris@0
|
18 use Drupal\Core\Language\Language;
|
Chris@0
|
19 use Drupal\Core\Site\Settings;
|
Chris@17
|
20 use Drupal\KernelTests\TestServiceProvider;
|
Chris@0
|
21 use Symfony\Component\DependencyInjection\Parameter;
|
Chris@0
|
22 use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
Chris@0
|
23 use Symfony\Component\DependencyInjection\Reference;
|
Chris@0
|
24 use Symfony\Component\HttpFoundation\Request;
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * Base class for functional integration tests.
|
Chris@0
|
28 *
|
Chris@0
|
29 * This base class should be useful for testing some types of integrations which
|
Chris@0
|
30 * don't require the overhead of a fully-installed Drupal instance, but which
|
Chris@0
|
31 * have many dependencies on parts of Drupal which can't or shouldn't be mocked.
|
Chris@0
|
32 *
|
Chris@0
|
33 * This base class partially boots a fixture Drupal. The state of the fixture
|
Chris@0
|
34 * Drupal is comparable to the state of a system during the early part of the
|
Chris@0
|
35 * installation process.
|
Chris@0
|
36 *
|
Chris@0
|
37 * Tests extending this base class can access services and the database, but the
|
Chris@0
|
38 * system is initially empty. This Drupal runs in a minimal mocked filesystem
|
Chris@0
|
39 * which operates within vfsStream.
|
Chris@0
|
40 *
|
Chris@0
|
41 * Modules specified in the $modules property are added to the service container
|
Chris@0
|
42 * for each test. The module/hook system is functional. Additional modules
|
Chris@0
|
43 * needed in a test should override $modules. Modules specified in this way will
|
Chris@0
|
44 * be added to those specified in superclasses.
|
Chris@0
|
45 *
|
Chris@0
|
46 * Unlike \Drupal\Tests\BrowserTestBase, the modules are not installed. They are
|
Chris@0
|
47 * loaded such that their services and hooks are available, but the install
|
Chris@0
|
48 * process has not been performed.
|
Chris@0
|
49 *
|
Chris@0
|
50 * Other modules can be made available in this way using
|
Chris@0
|
51 * KernelTestBase::enableModules().
|
Chris@0
|
52 *
|
Chris@0
|
53 * Some modules can be brought into a fully-installed state using
|
Chris@0
|
54 * KernelTestBase::installConfig(), KernelTestBase::installSchema(), and
|
Chris@0
|
55 * KernelTestBase::installEntitySchema(). Alternately, tests which need modules
|
Chris@0
|
56 * to be fully installed could inherit from \Drupal\Tests\BrowserTestBase.
|
Chris@0
|
57 *
|
Chris@0
|
58 * @see \Drupal\Tests\KernelTestBase::$modules
|
Chris@0
|
59 * @see \Drupal\Tests\KernelTestBase::enableModules()
|
Chris@0
|
60 * @see \Drupal\Tests\KernelTestBase::installConfig()
|
Chris@0
|
61 * @see \Drupal\Tests\KernelTestBase::installEntitySchema()
|
Chris@0
|
62 * @see \Drupal\Tests\KernelTestBase::installSchema()
|
Chris@0
|
63 * @see \Drupal\Tests\BrowserTestBase
|
Chris@0
|
64 *
|
Chris@0
|
65 * @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
Chris@0
|
66 * \Drupal\KernelTests\KernelTestBase instead.
|
Chris@0
|
67 *
|
Chris@0
|
68 * @ingroup testing
|
Chris@0
|
69 */
|
Chris@0
|
70 abstract class KernelTestBase extends TestBase {
|
Chris@0
|
71
|
Chris@0
|
72 use AssertContentTrait;
|
Chris@0
|
73
|
Chris@0
|
74 /**
|
Chris@0
|
75 * Modules to enable.
|
Chris@0
|
76 *
|
Chris@0
|
77 * Test classes extending this class, and any classes in the hierarchy up to
|
Chris@0
|
78 * this class, may specify individual lists of modules to enable by setting
|
Chris@0
|
79 * this property. The values of all properties in all classes in the hierarchy
|
Chris@0
|
80 * are merged.
|
Chris@0
|
81 *
|
Chris@0
|
82 * Any modules specified in the $modules property are automatically loaded and
|
Chris@0
|
83 * set as the fixed module list.
|
Chris@0
|
84 *
|
Chris@0
|
85 * Unlike WebTestBase::setUp(), the specified modules are loaded only, but not
|
Chris@0
|
86 * automatically installed. Modules need to be installed manually, if needed.
|
Chris@0
|
87 *
|
Chris@0
|
88 * @see \Drupal\simpletest\KernelTestBase::enableModules()
|
Chris@0
|
89 * @see \Drupal\simpletest\KernelTestBase::setUp()
|
Chris@0
|
90 *
|
Chris@0
|
91 * @var array
|
Chris@0
|
92 */
|
Chris@0
|
93 public static $modules = [];
|
Chris@0
|
94
|
Chris@0
|
95 private $moduleFiles;
|
Chris@0
|
96 private $themeFiles;
|
Chris@0
|
97
|
Chris@0
|
98 /**
|
Chris@0
|
99 * The configuration directories for this test run.
|
Chris@0
|
100 *
|
Chris@0
|
101 * @var array
|
Chris@0
|
102 */
|
Chris@0
|
103 protected $configDirectories = [];
|
Chris@0
|
104
|
Chris@0
|
105 /**
|
Chris@0
|
106 * A KeyValueMemoryFactory instance to use when building the container.
|
Chris@0
|
107 *
|
Chris@17
|
108 * @var \Drupal\Core\KeyValueStore\KeyValueMemoryFactory
|
Chris@0
|
109 */
|
Chris@0
|
110 protected $keyValueFactory;
|
Chris@0
|
111
|
Chris@0
|
112 /**
|
Chris@0
|
113 * Array of registered stream wrappers.
|
Chris@0
|
114 *
|
Chris@0
|
115 * @var array
|
Chris@0
|
116 */
|
Chris@0
|
117 protected $streamWrappers = [];
|
Chris@0
|
118
|
Chris@0
|
119 /**
|
Chris@0
|
120 * {@inheritdoc}
|
Chris@0
|
121 */
|
Chris@0
|
122 public function __construct($test_id = NULL) {
|
Chris@0
|
123 parent::__construct($test_id);
|
Chris@0
|
124 $this->skipClasses[__CLASS__] = TRUE;
|
Chris@0
|
125 }
|
Chris@0
|
126
|
Chris@0
|
127 /**
|
Chris@0
|
128 * {@inheritdoc}
|
Chris@0
|
129 */
|
Chris@0
|
130 protected function beforePrepareEnvironment() {
|
Chris@0
|
131 // Copy/prime extension file lists once to avoid filesystem scans.
|
Chris@0
|
132 if (!isset($this->moduleFiles)) {
|
Chris@0
|
133 $this->moduleFiles = \Drupal::state()->get('system.module.files') ?: [];
|
Chris@0
|
134 $this->themeFiles = \Drupal::state()->get('system.theme.files') ?: [];
|
Chris@0
|
135 }
|
Chris@0
|
136 }
|
Chris@0
|
137
|
Chris@0
|
138 /**
|
Chris@0
|
139 * Create and set new configuration directories.
|
Chris@0
|
140 *
|
Chris@0
|
141 * @see config_get_config_directory()
|
Chris@0
|
142 *
|
Chris@0
|
143 * @throws \RuntimeException
|
Chris@0
|
144 * Thrown when CONFIG_SYNC_DIRECTORY cannot be created or made writable.
|
Chris@0
|
145 */
|
Chris@0
|
146 protected function prepareConfigDirectories() {
|
Chris@0
|
147 $this->configDirectories = [];
|
Chris@0
|
148 include_once DRUPAL_ROOT . '/core/includes/install.inc';
|
Chris@0
|
149 // Assign the relative path to the global variable.
|
Chris@0
|
150 $path = $this->siteDirectory . '/config_' . CONFIG_SYNC_DIRECTORY;
|
Chris@0
|
151 $GLOBALS['config_directories'][CONFIG_SYNC_DIRECTORY] = $path;
|
Chris@0
|
152 // Ensure the directory can be created and is writeable.
|
Chris@18
|
153 if (!\Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
|
Chris@0
|
154 throw new \RuntimeException("Failed to create '" . CONFIG_SYNC_DIRECTORY . "' config directory $path");
|
Chris@0
|
155 }
|
Chris@0
|
156 // Provide the already resolved path for tests.
|
Chris@0
|
157 $this->configDirectories[CONFIG_SYNC_DIRECTORY] = $path;
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 /**
|
Chris@0
|
161 * {@inheritdoc}
|
Chris@0
|
162 */
|
Chris@0
|
163 protected function setUp() {
|
Chris@0
|
164 $this->keyValueFactory = new KeyValueMemoryFactory();
|
Chris@0
|
165
|
Chris@0
|
166 // Back up settings from TestBase::prepareEnvironment().
|
Chris@0
|
167 $settings = Settings::getAll();
|
Chris@0
|
168
|
Chris@0
|
169 // Allow for test-specific overrides.
|
Chris@0
|
170 $directory = DRUPAL_ROOT . '/' . $this->siteDirectory;
|
Chris@0
|
171 $settings_services_file = DRUPAL_ROOT . '/' . $this->originalSite . '/testing.services.yml';
|
Chris@0
|
172 $container_yamls = [];
|
Chris@0
|
173 if (file_exists($settings_services_file)) {
|
Chris@0
|
174 // Copy the testing-specific service overrides in place.
|
Chris@0
|
175 $testing_services_file = $directory . '/services.yml';
|
Chris@0
|
176 copy($settings_services_file, $testing_services_file);
|
Chris@0
|
177 $container_yamls[] = $testing_services_file;
|
Chris@0
|
178 }
|
Chris@0
|
179 $settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSite . '/settings.testing.php';
|
Chris@0
|
180 if (file_exists($settings_testing_file)) {
|
Chris@0
|
181 // Copy the testing-specific settings.php overrides in place.
|
Chris@0
|
182 copy($settings_testing_file, $directory . '/settings.testing.php');
|
Chris@0
|
183 }
|
Chris@0
|
184
|
Chris@0
|
185 if (file_exists($directory . '/settings.testing.php')) {
|
Chris@0
|
186 // Add the name of the testing class to settings.php and include the
|
Chris@0
|
187 // testing specific overrides
|
Chris@0
|
188 $hash_salt = Settings::getHashSalt();
|
Chris@0
|
189 $test_class = get_class($this);
|
Chris@0
|
190 $container_yamls_export = Variable::export($container_yamls);
|
Chris@0
|
191 $php = <<<EOD
|
Chris@0
|
192 <?php
|
Chris@0
|
193
|
Chris@0
|
194 \$settings['hash_salt'] = '$hash_salt';
|
Chris@0
|
195 \$settings['container_yamls'] = $container_yamls_export;
|
Chris@0
|
196
|
Chris@0
|
197 \$test_class = '$test_class';
|
Chris@0
|
198 include DRUPAL_ROOT . '/' . \$site_path . '/settings.testing.php';
|
Chris@0
|
199 EOD;
|
Chris@0
|
200 file_put_contents($directory . '/settings.php', $php);
|
Chris@0
|
201 }
|
Chris@0
|
202
|
Chris@0
|
203 // Add this test class as a service provider.
|
Chris@0
|
204 // @todo Remove the indirection; implement ServiceProviderInterface instead.
|
Chris@17
|
205 $GLOBALS['conf']['container_service_providers']['TestServiceProvider'] = TestServiceProvider::class;
|
Chris@0
|
206
|
Chris@0
|
207 // Bootstrap a new kernel.
|
Chris@0
|
208 $class_loader = require DRUPAL_ROOT . '/autoload.php';
|
Chris@0
|
209 $this->kernel = new DrupalKernel('testing', $class_loader, FALSE);
|
Chris@0
|
210 $request = Request::create('/');
|
Chris@0
|
211 $site_path = DrupalKernel::findSitePath($request);
|
Chris@0
|
212 $this->kernel->setSitePath($site_path);
|
Chris@0
|
213 if (file_exists($directory . '/settings.testing.php')) {
|
Chris@0
|
214 Settings::initialize(DRUPAL_ROOT, $site_path, $class_loader);
|
Chris@0
|
215 }
|
Chris@0
|
216 $this->kernel->boot();
|
Chris@0
|
217
|
Chris@0
|
218 // Ensure database install tasks have been run.
|
Chris@0
|
219 require_once __DIR__ . '/../../../includes/install.inc';
|
Chris@0
|
220 $connection = Database::getConnection();
|
Chris@0
|
221 $errors = db_installer_object($connection->driver())->runTasks();
|
Chris@0
|
222 if (!empty($errors)) {
|
Chris@0
|
223 $this->fail('Failed to run installer database tasks: ' . implode(', ', $errors));
|
Chris@0
|
224 }
|
Chris@0
|
225
|
Chris@0
|
226 // Reboot the kernel because the container might contain a connection to the
|
Chris@0
|
227 // database that has been closed during the database install tasks. This
|
Chris@0
|
228 // prevents any services created during the first boot from having stale
|
Chris@0
|
229 // database connections, for example, \Drupal\Core\Config\DatabaseStorage.
|
Chris@0
|
230 $this->kernel->shutdown();
|
Chris@0
|
231 $this->kernel->boot();
|
Chris@0
|
232
|
Chris@0
|
233 // Save the original site directory path, so that extensions in the
|
Chris@0
|
234 // site-specific directory can still be discovered in the test site
|
Chris@0
|
235 // environment.
|
Chris@0
|
236 // @see \Drupal\Core\Extension\ExtensionDiscovery::scan()
|
Chris@0
|
237 $settings['test_parent_site'] = $this->originalSite;
|
Chris@0
|
238
|
Chris@0
|
239 // Restore and merge settings.
|
Chris@0
|
240 // DrupalKernel::boot() initializes new Settings, and the containerBuild()
|
Chris@0
|
241 // method sets additional settings.
|
Chris@0
|
242 new Settings($settings + Settings::getAll());
|
Chris@0
|
243
|
Chris@0
|
244 // Create and set new configuration directories.
|
Chris@0
|
245 $this->prepareConfigDirectories();
|
Chris@0
|
246
|
Chris@0
|
247 // Set the request scope.
|
Chris@0
|
248 $this->container = $this->kernel->getContainer();
|
Chris@0
|
249 $this->container->get('request_stack')->push($request);
|
Chris@0
|
250
|
Chris@0
|
251 // Re-inject extension file listings into state, unless the key/value
|
Chris@0
|
252 // service was overridden (in which case its storage does not exist yet).
|
Chris@0
|
253 if ($this->container->get('keyvalue') instanceof KeyValueMemoryFactory) {
|
Chris@0
|
254 $this->container->get('state')->set('system.module.files', $this->moduleFiles);
|
Chris@0
|
255 $this->container->get('state')->set('system.theme.files', $this->themeFiles);
|
Chris@0
|
256 }
|
Chris@0
|
257
|
Chris@0
|
258 // Create a minimal core.extension configuration object so that the list of
|
Chris@0
|
259 // enabled modules can be maintained allowing
|
Chris@0
|
260 // \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work.
|
Chris@0
|
261 // Write directly to active storage to avoid early instantiation of
|
Chris@0
|
262 // the event dispatcher which can prevent modules from registering events.
|
Chris@0
|
263 \Drupal::service('config.storage')->write('core.extension', ['module' => [], 'theme' => [], 'profile' => '']);
|
Chris@0
|
264
|
Chris@0
|
265 // Collect and set a fixed module list.
|
Chris@0
|
266 $class = get_class($this);
|
Chris@0
|
267 $modules = [];
|
Chris@0
|
268 while ($class) {
|
Chris@0
|
269 if (property_exists($class, 'modules')) {
|
Chris@0
|
270 // Only add the modules, if the $modules property was not inherited.
|
Chris@0
|
271 $rp = new \ReflectionProperty($class, 'modules');
|
Chris@0
|
272 if ($rp->class == $class) {
|
Chris@0
|
273 $modules[$class] = $class::$modules;
|
Chris@0
|
274 }
|
Chris@0
|
275 }
|
Chris@0
|
276 $class = get_parent_class($class);
|
Chris@0
|
277 }
|
Chris@0
|
278 // Modules have been collected in reverse class hierarchy order; modules
|
Chris@0
|
279 // defined by base classes should be sorted first. Then, merge the results
|
Chris@0
|
280 // together.
|
Chris@0
|
281 $modules = array_reverse($modules);
|
Chris@0
|
282 $modules = call_user_func_array('array_merge_recursive', $modules);
|
Chris@0
|
283 if ($modules) {
|
Chris@0
|
284 $this->enableModules($modules);
|
Chris@0
|
285 }
|
Chris@0
|
286
|
Chris@0
|
287 // Tests based on this class are entitled to use Drupal's File and
|
Chris@0
|
288 // StreamWrapper APIs.
|
Chris@18
|
289 \Drupal::service('file_system')->prepareDirectory($this->publicFilesDirectory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
|
Chris@0
|
290 $this->settingsSet('file_public_path', $this->publicFilesDirectory);
|
Chris@0
|
291 $this->streamWrappers = [];
|
Chris@0
|
292 $this->registerStreamWrapper('public', 'Drupal\Core\StreamWrapper\PublicStream');
|
Chris@0
|
293 // The temporary stream wrapper is able to operate both with and without
|
Chris@0
|
294 // configuration.
|
Chris@0
|
295 $this->registerStreamWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream');
|
Chris@0
|
296
|
Chris@0
|
297 // Manually configure the test mail collector implementation to prevent
|
Chris@0
|
298 // tests from sending out emails and collect them in state instead.
|
Chris@0
|
299 // While this should be enforced via settings.php prior to installation,
|
Chris@0
|
300 // some tests expect to be able to test mail system implementations.
|
Chris@0
|
301 $GLOBALS['config']['system.mail']['interface']['default'] = 'test_mail_collector';
|
Chris@0
|
302 }
|
Chris@0
|
303
|
Chris@0
|
304 /**
|
Chris@0
|
305 * {@inheritdoc}
|
Chris@0
|
306 */
|
Chris@0
|
307 protected function tearDown() {
|
Chris@0
|
308 if ($this->kernel instanceof DrupalKernel) {
|
Chris@0
|
309 $this->kernel->shutdown();
|
Chris@0
|
310 }
|
Chris@0
|
311 // Before tearing down the test environment, ensure that no stream wrapper
|
Chris@0
|
312 // of this test leaks into the parent environment. Unlike all other global
|
Chris@0
|
313 // state variables in Drupal, stream wrappers are a global state construct
|
Chris@0
|
314 // of PHP core, which has to be maintained manually.
|
Chris@0
|
315 // @todo Move StreamWrapper management into DrupalKernel.
|
Chris@0
|
316 // @see https://www.drupal.org/node/2028109
|
Chris@0
|
317 foreach ($this->streamWrappers as $scheme => $type) {
|
Chris@0
|
318 $this->unregisterStreamWrapper($scheme, $type);
|
Chris@0
|
319 }
|
Chris@0
|
320 parent::tearDown();
|
Chris@0
|
321 }
|
Chris@0
|
322
|
Chris@0
|
323 /**
|
Chris@0
|
324 * Sets up the base service container for this test.
|
Chris@0
|
325 *
|
Chris@0
|
326 * Extend this method in your test to register additional service overrides
|
Chris@0
|
327 * that need to persist a DrupalKernel reboot. This method is called whenever
|
Chris@0
|
328 * the kernel is rebuilt.
|
Chris@0
|
329 *
|
Chris@0
|
330 * @see \Drupal\simpletest\KernelTestBase::setUp()
|
Chris@0
|
331 * @see \Drupal\simpletest\KernelTestBase::enableModules()
|
Chris@0
|
332 * @see \Drupal\simpletest\KernelTestBase::disableModules()
|
Chris@0
|
333 */
|
Chris@0
|
334 public function containerBuild(ContainerBuilder $container) {
|
Chris@0
|
335 // Keep the container object around for tests.
|
Chris@0
|
336 $this->container = $container;
|
Chris@0
|
337
|
Chris@0
|
338 // Set the default language on the minimal container.
|
Chris@0
|
339 $this->container->setParameter('language.default_values', $this->defaultLanguageData());
|
Chris@0
|
340
|
Chris@0
|
341 $container->register('lock', 'Drupal\Core\Lock\NullLockBackend');
|
Chris@0
|
342 $container->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory');
|
Chris@0
|
343
|
Chris@0
|
344 $container
|
Chris@0
|
345 ->register('config.storage', 'Drupal\Core\Config\DatabaseStorage')
|
Chris@0
|
346 ->addArgument(Database::getConnection())
|
Chris@0
|
347 ->addArgument('config');
|
Chris@0
|
348
|
Chris@0
|
349 if ($this->strictConfigSchema) {
|
Chris@0
|
350 $container
|
Chris@0
|
351 ->register('simpletest.config_schema_checker', ConfigSchemaChecker::class)
|
Chris@0
|
352 ->addArgument(new Reference('config.typed'))
|
Chris@0
|
353 ->addArgument($this->getConfigSchemaExclusions())
|
Chris@0
|
354 ->addTag('event_subscriber');
|
Chris@0
|
355 }
|
Chris@0
|
356
|
Chris@0
|
357 $keyvalue_options = $container->getParameter('factory.keyvalue') ?: [];
|
Chris@0
|
358 $keyvalue_options['default'] = 'keyvalue.memory';
|
Chris@0
|
359 $container->setParameter('factory.keyvalue', $keyvalue_options);
|
Chris@0
|
360 $container->set('keyvalue.memory', $this->keyValueFactory);
|
Chris@0
|
361 if (!$container->has('keyvalue')) {
|
Chris@0
|
362 // TestBase::setUp puts a completely empty container in
|
Chris@0
|
363 // $this->container which is somewhat the mirror of the empty
|
Chris@0
|
364 // environment being set up. Unit tests need not to waste time with
|
Chris@0
|
365 // getting a container set up for them. Drupal Unit Tests might just get
|
Chris@0
|
366 // away with a simple container holding the absolute bare minimum. When
|
Chris@0
|
367 // a kernel is overridden then there's no need to re-register the keyvalue
|
Chris@0
|
368 // service but when a test is happy with the superminimal container put
|
Chris@0
|
369 // together here, it still might a keyvalue storage for anything using
|
Chris@0
|
370 // \Drupal::state() -- that's why a memory service was added in the first
|
Chris@0
|
371 // place.
|
Chris@0
|
372 $container->register('settings', 'Drupal\Core\Site\Settings')
|
Chris@0
|
373 ->setFactoryClass('Drupal\Core\Site\Settings')
|
Chris@0
|
374 ->setFactoryMethod('getInstance');
|
Chris@0
|
375
|
Chris@0
|
376 $container
|
Chris@0
|
377 ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory')
|
Chris@0
|
378 ->addArgument(new Reference('service_container'))
|
Chris@0
|
379 ->addArgument(new Parameter('factory.keyvalue'));
|
Chris@0
|
380
|
Chris@0
|
381 $container->register('state', 'Drupal\Core\State\State')
|
Chris@0
|
382 ->addArgument(new Reference('keyvalue'));
|
Chris@0
|
383 }
|
Chris@0
|
384
|
Chris@0
|
385 if ($container->hasDefinition('path_processor_alias')) {
|
Chris@0
|
386 // Prevent the alias-based path processor, which requires a url_alias db
|
Chris@0
|
387 // table, from being registered to the path processor manager. We do this
|
Chris@0
|
388 // by removing the tags that the compiler pass looks for. This means the
|
Chris@0
|
389 // url generator can safely be used within tests.
|
Chris@0
|
390 $definition = $container->getDefinition('path_processor_alias');
|
Chris@0
|
391 $definition->clearTag('path_processor_inbound')->clearTag('path_processor_outbound');
|
Chris@0
|
392 }
|
Chris@0
|
393
|
Chris@0
|
394 if ($container->hasDefinition('password')) {
|
Chris@0
|
395 $container->getDefinition('password')->setArguments([1]);
|
Chris@0
|
396 }
|
Chris@0
|
397
|
Chris@0
|
398 // Register the stream wrapper manager.
|
Chris@0
|
399 $container
|
Chris@0
|
400 ->register('stream_wrapper_manager', 'Drupal\Core\StreamWrapper\StreamWrapperManager')
|
Chris@0
|
401 ->addArgument(new Reference('module_handler'))
|
Chris@0
|
402 ->addMethodCall('setContainer', [new Reference('service_container')]);
|
Chris@0
|
403
|
Chris@0
|
404 $request = Request::create('/');
|
Chris@0
|
405 $container->get('request_stack')->push($request);
|
Chris@0
|
406 }
|
Chris@0
|
407
|
Chris@0
|
408 /**
|
Chris@0
|
409 * Provides the data for setting the default language on the container.
|
Chris@0
|
410 *
|
Chris@0
|
411 * @return array
|
Chris@0
|
412 * The data array for the default language.
|
Chris@0
|
413 */
|
Chris@0
|
414 protected function defaultLanguageData() {
|
Chris@0
|
415 return Language::$defaultValues;
|
Chris@0
|
416 }
|
Chris@0
|
417
|
Chris@0
|
418 /**
|
Chris@0
|
419 * Installs default configuration for a given list of modules.
|
Chris@0
|
420 *
|
Chris@0
|
421 * @param array $modules
|
Chris@0
|
422 * A list of modules for which to install default configuration.
|
Chris@0
|
423 *
|
Chris@0
|
424 * @throws \RuntimeException
|
Chris@0
|
425 * Thrown when any module listed in $modules is not enabled.
|
Chris@0
|
426 */
|
Chris@0
|
427 protected function installConfig(array $modules) {
|
Chris@0
|
428 foreach ($modules as $module) {
|
Chris@0
|
429 if (!$this->container->get('module_handler')->moduleExists($module)) {
|
Chris@0
|
430 throw new \RuntimeException("'$module' module is not enabled");
|
Chris@0
|
431 }
|
Chris@0
|
432 \Drupal::service('config.installer')->installDefaultConfig('module', $module);
|
Chris@0
|
433 }
|
Chris@0
|
434 $this->pass(format_string('Installed default config: %modules.', [
|
Chris@0
|
435 '%modules' => implode(', ', $modules),
|
Chris@0
|
436 ]));
|
Chris@0
|
437 }
|
Chris@0
|
438
|
Chris@0
|
439 /**
|
Chris@0
|
440 * Installs a specific table from a module schema definition.
|
Chris@0
|
441 *
|
Chris@0
|
442 * @param string $module
|
Chris@0
|
443 * The name of the module that defines the table's schema.
|
Chris@0
|
444 * @param string|array $tables
|
Chris@0
|
445 * The name or an array of the names of the tables to install.
|
Chris@0
|
446 *
|
Chris@0
|
447 * @throws \RuntimeException
|
Chris@0
|
448 * Thrown when $module is not enabled or when the table schema cannot be
|
Chris@0
|
449 * found in the module specified.
|
Chris@0
|
450 */
|
Chris@0
|
451 protected function installSchema($module, $tables) {
|
Chris@0
|
452 // drupal_get_module_schema() is technically able to install a schema
|
Chris@0
|
453 // of a non-enabled module, but its ability to load the module's .install
|
Chris@0
|
454 // file depends on many other factors. To prevent differences in test
|
Chris@0
|
455 // behavior and non-reproducible test failures, we only allow the schema of
|
Chris@0
|
456 // explicitly loaded/enabled modules to be installed.
|
Chris@0
|
457 if (!$this->container->get('module_handler')->moduleExists($module)) {
|
Chris@0
|
458 throw new \RuntimeException("'$module' module is not enabled");
|
Chris@0
|
459 }
|
Chris@0
|
460
|
Chris@0
|
461 $tables = (array) $tables;
|
Chris@0
|
462 foreach ($tables as $table) {
|
Chris@0
|
463 $schema = drupal_get_module_schema($module, $table);
|
Chris@0
|
464 if (empty($schema)) {
|
Chris@0
|
465 // BC layer to avoid some contrib tests to fail.
|
Chris@0
|
466 // @todo Remove the BC layer before 8.1.x release.
|
Chris@0
|
467 // @see https://www.drupal.org/node/2670360
|
Chris@0
|
468 // @see https://www.drupal.org/node/2670454
|
Chris@0
|
469 if ($module == 'system') {
|
Chris@0
|
470 continue;
|
Chris@0
|
471 }
|
Chris@0
|
472 throw new \RuntimeException("Unknown '$table' table schema in '$module' module.");
|
Chris@0
|
473 }
|
Chris@0
|
474 $this->container->get('database')->schema()->createTable($table, $schema);
|
Chris@0
|
475 }
|
Chris@0
|
476 $this->pass(format_string('Installed %module tables: %tables.', [
|
Chris@0
|
477 '%tables' => '{' . implode('}, {', $tables) . '}',
|
Chris@0
|
478 '%module' => $module,
|
Chris@0
|
479 ]));
|
Chris@0
|
480 }
|
Chris@0
|
481
|
Chris@0
|
482 /**
|
Chris@0
|
483 * Installs the storage schema for a specific entity type.
|
Chris@0
|
484 *
|
Chris@0
|
485 * @param string $entity_type_id
|
Chris@0
|
486 * The ID of the entity type.
|
Chris@0
|
487 */
|
Chris@0
|
488 protected function installEntitySchema($entity_type_id) {
|
Chris@0
|
489 /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
|
Chris@0
|
490 $entity_manager = $this->container->get('entity.manager');
|
Chris@0
|
491 $entity_type = $entity_manager->getDefinition($entity_type_id);
|
Chris@0
|
492 $entity_manager->onEntityTypeCreate($entity_type);
|
Chris@0
|
493
|
Chris@0
|
494 // For test runs, the most common storage backend is a SQL database. For
|
Chris@0
|
495 // this case, ensure the tables got created.
|
Chris@0
|
496 $storage = $entity_manager->getStorage($entity_type_id);
|
Chris@0
|
497 if ($storage instanceof SqlEntityStorageInterface) {
|
Chris@0
|
498 $tables = $storage->getTableMapping()->getTableNames();
|
Chris@0
|
499 $db_schema = $this->container->get('database')->schema();
|
Chris@0
|
500 $all_tables_exist = TRUE;
|
Chris@0
|
501 foreach ($tables as $table) {
|
Chris@0
|
502 if (!$db_schema->tableExists($table)) {
|
Chris@17
|
503 $this->fail(new FormattableMarkup('Installed entity type table for the %entity_type entity type: %table', [
|
Chris@0
|
504 '%entity_type' => $entity_type_id,
|
Chris@0
|
505 '%table' => $table,
|
Chris@0
|
506 ]));
|
Chris@0
|
507 $all_tables_exist = FALSE;
|
Chris@0
|
508 }
|
Chris@0
|
509 }
|
Chris@0
|
510 if ($all_tables_exist) {
|
Chris@17
|
511 $this->pass(new FormattableMarkup('Installed entity type tables for the %entity_type entity type: %tables', [
|
Chris@0
|
512 '%entity_type' => $entity_type_id,
|
Chris@0
|
513 '%tables' => '{' . implode('}, {', $tables) . '}',
|
Chris@0
|
514 ]));
|
Chris@0
|
515 }
|
Chris@0
|
516 }
|
Chris@0
|
517 }
|
Chris@0
|
518
|
Chris@0
|
519 /**
|
Chris@0
|
520 * Enables modules for this test.
|
Chris@0
|
521 *
|
Chris@0
|
522 * To install test modules outside of the testing environment, add
|
Chris@0
|
523 * @code
|
Chris@0
|
524 * $settings['extension_discovery_scan_tests'] = TRUE;
|
Chris@0
|
525 * @endcode
|
Chris@0
|
526 * to your settings.php.
|
Chris@0
|
527 *
|
Chris@0
|
528 * @param array $modules
|
Chris@0
|
529 * A list of modules to enable. Dependencies are not resolved; i.e.,
|
Chris@0
|
530 * multiple modules have to be specified with dependent modules first.
|
Chris@0
|
531 * The new modules are only added to the active module list and loaded.
|
Chris@0
|
532 */
|
Chris@0
|
533 protected function enableModules(array $modules) {
|
Chris@0
|
534 // Perform an ExtensionDiscovery scan as this function may receive a
|
Chris@0
|
535 // profile that is not the current profile, and we don't yet have a cached
|
Chris@0
|
536 // way to receive inactive profile information.
|
Chris@0
|
537 // @todo Remove as part of https://www.drupal.org/node/2186491
|
Chris@0
|
538 $listing = new ExtensionDiscovery(\Drupal::root());
|
Chris@0
|
539 $module_list = $listing->scan('module');
|
Chris@0
|
540 // In ModuleHandlerTest we pass in a profile as if it were a module.
|
Chris@0
|
541 $module_list += $listing->scan('profile');
|
Chris@0
|
542 // Set the list of modules in the extension handler.
|
Chris@0
|
543 $module_handler = $this->container->get('module_handler');
|
Chris@0
|
544
|
Chris@0
|
545 // Write directly to active storage to avoid early instantiation of
|
Chris@0
|
546 // the event dispatcher which can prevent modules from registering events.
|
Chris@0
|
547 $active_storage = \Drupal::service('config.storage');
|
Chris@0
|
548 $extensions = $active_storage->read('core.extension');
|
Chris@0
|
549
|
Chris@0
|
550 foreach ($modules as $module) {
|
Chris@0
|
551 $module_handler->addModule($module, $module_list[$module]->getPath());
|
Chris@0
|
552 // Maintain the list of enabled modules in configuration.
|
Chris@0
|
553 $extensions['module'][$module] = 0;
|
Chris@0
|
554 }
|
Chris@0
|
555 $active_storage->write('core.extension', $extensions);
|
Chris@0
|
556
|
Chris@0
|
557 // Update the kernel to make their services available.
|
Chris@0
|
558 $module_filenames = $module_handler->getModuleList();
|
Chris@0
|
559 $this->kernel->updateModules($module_filenames, $module_filenames);
|
Chris@0
|
560
|
Chris@0
|
561 // Ensure isLoaded() is TRUE in order to make
|
Chris@0
|
562 // \Drupal\Core\Theme\ThemeManagerInterface::render() work.
|
Chris@0
|
563 // Note that the kernel has rebuilt the container; this $module_handler is
|
Chris@0
|
564 // no longer the $module_handler instance from above.
|
Chris@0
|
565 $this->container->get('module_handler')->reload();
|
Chris@0
|
566 $this->pass(format_string('Enabled modules: %modules.', [
|
Chris@0
|
567 '%modules' => implode(', ', $modules),
|
Chris@0
|
568 ]));
|
Chris@0
|
569 }
|
Chris@0
|
570
|
Chris@0
|
571 /**
|
Chris@0
|
572 * Disables modules for this test.
|
Chris@0
|
573 *
|
Chris@0
|
574 * @param array $modules
|
Chris@0
|
575 * A list of modules to disable. Dependencies are not resolved; i.e.,
|
Chris@0
|
576 * multiple modules have to be specified with dependent modules first.
|
Chris@0
|
577 * Code of previously active modules is still loaded. The modules are only
|
Chris@0
|
578 * removed from the active module list.
|
Chris@0
|
579 */
|
Chris@0
|
580 protected function disableModules(array $modules) {
|
Chris@0
|
581 // Unset the list of modules in the extension handler.
|
Chris@0
|
582 $module_handler = $this->container->get('module_handler');
|
Chris@0
|
583 $module_filenames = $module_handler->getModuleList();
|
Chris@0
|
584 $extension_config = $this->config('core.extension');
|
Chris@0
|
585 foreach ($modules as $module) {
|
Chris@0
|
586 unset($module_filenames[$module]);
|
Chris@0
|
587 $extension_config->clear('module.' . $module);
|
Chris@0
|
588 }
|
Chris@0
|
589 $extension_config->save();
|
Chris@0
|
590 $module_handler->setModuleList($module_filenames);
|
Chris@0
|
591 $module_handler->resetImplementations();
|
Chris@0
|
592 // Update the kernel to remove their services.
|
Chris@0
|
593 $this->kernel->updateModules($module_filenames, $module_filenames);
|
Chris@0
|
594
|
Chris@0
|
595 // Ensure isLoaded() is TRUE in order to make
|
Chris@0
|
596 // \Drupal\Core\Theme\ThemeManagerInterface::render() work.
|
Chris@0
|
597 // Note that the kernel has rebuilt the container; this $module_handler is
|
Chris@0
|
598 // no longer the $module_handler instance from above.
|
Chris@0
|
599 $module_handler = $this->container->get('module_handler');
|
Chris@0
|
600 $module_handler->reload();
|
Chris@0
|
601 $this->pass(format_string('Disabled modules: %modules.', [
|
Chris@0
|
602 '%modules' => implode(', ', $modules),
|
Chris@0
|
603 ]));
|
Chris@0
|
604 }
|
Chris@0
|
605
|
Chris@0
|
606 /**
|
Chris@0
|
607 * Registers a stream wrapper for this test.
|
Chris@0
|
608 *
|
Chris@0
|
609 * @param string $scheme
|
Chris@0
|
610 * The scheme to register.
|
Chris@0
|
611 * @param string $class
|
Chris@0
|
612 * The fully qualified class name to register.
|
Chris@0
|
613 * @param int $type
|
Chris@0
|
614 * The Drupal Stream Wrapper API type. Defaults to
|
Chris@0
|
615 * StreamWrapperInterface::NORMAL.
|
Chris@0
|
616 */
|
Chris@0
|
617 protected function registerStreamWrapper($scheme, $class, $type = StreamWrapperInterface::NORMAL) {
|
Chris@0
|
618 $this->container->get('stream_wrapper_manager')->registerWrapper($scheme, $class, $type);
|
Chris@0
|
619 }
|
Chris@0
|
620
|
Chris@0
|
621 /**
|
Chris@0
|
622 * Renders a render array.
|
Chris@0
|
623 *
|
Chris@0
|
624 * @param array $elements
|
Chris@0
|
625 * The elements to render.
|
Chris@0
|
626 *
|
Chris@0
|
627 * @return string
|
Chris@0
|
628 * The rendered string output (typically HTML).
|
Chris@0
|
629 */
|
Chris@0
|
630 protected function render(array &$elements) {
|
Chris@0
|
631 // Use the bare HTML page renderer to render our links.
|
Chris@0
|
632 $renderer = $this->container->get('bare_html_page_renderer');
|
Chris@0
|
633 $response = $renderer->renderBarePage($elements, '', 'maintenance_page');
|
Chris@0
|
634
|
Chris@0
|
635 // Glean the content from the response object.
|
Chris@0
|
636 $content = $response->getContent();
|
Chris@0
|
637 $this->setRawContent($content);
|
Chris@0
|
638 $this->verbose('<pre style="white-space: pre-wrap">' . Html::escape($content));
|
Chris@0
|
639 return $content;
|
Chris@0
|
640 }
|
Chris@0
|
641
|
Chris@0
|
642 }
|