Commit d5821399 authored by alexpott's avatar alexpott

Issue #2304461 by dawehner, neclimdul, naveenvalecha, jibran, tim.plunkett,...

Issue #2304461 by dawehner, neclimdul, naveenvalecha, jibran, tim.plunkett, phenaproxima, Xano, hussainweb, sun, hitesh-jain, amateescu, alexpott, daffie, Mile23, Wim Leers, larowlan: KernelTestBaseTNG
parent e76a96f4
......@@ -210,7 +210,7 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
}
$variable_names['$'. $setting] = $setting;
}
$contents = file_get_contents(DRUPAL_ROOT . '/' . $settings_file);
$contents = file_get_contents($settings_file);
if ($contents !== FALSE) {
// Initialize the contents for the settings.php file if it is empty.
if (trim($contents) === '') {
......@@ -315,7 +315,7 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
}
// Write the new settings file.
if (file_put_contents(DRUPAL_ROOT . '/' . $settings_file, $buffer) === FALSE) {
if (file_put_contents($settings_file, $buffer) === FALSE) {
throw new Exception(t('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
}
else {
......
......@@ -193,19 +193,23 @@ public function decode($raw) {
* Implements Drupal\Core\Config\StorageInterface::listAll().
*/
public function listAll($prefix = '') {
// glob() silently ignores the error of a non-existing search directory,
// even with the GLOB_ERR flag.
$dir = $this->getCollectionDirectory();
if (!file_exists($dir)) {
if (!is_dir($dir)) {
return array();
}
$extension = '.' . static::getFileExtension();
// \GlobIterator on Windows requires an absolute path.
$files = new \GlobIterator(realpath($dir) . '/' . $prefix . '*' . $extension);
// glob() directly calls into libc glob(), which is not aware of PHP stream
// wrappers. Same for \GlobIterator (which additionally requires an absolute
// realpath() on Windows).
// @see https://github.com/mikey179/vfsStream/issues/2
$files = scandir($dir);
$names = array();
foreach ($files as $file) {
$names[] = $file->getBasename($extension);
if ($file[0] !== '.' && fnmatch($prefix . '*' . $extension, $file)) {
$names[] = basename($file, $extension);
}
}
return $names;
......@@ -299,13 +303,15 @@ protected function getAllCollectionNamesHelper($directory) {
$collections[] = $collection . '.' . $sub_collection;
}
}
// Check that the collection is valid by searching if for configuration
// Check that the collection is valid by searching it for configuration
// objects. A directory without any configuration objects is not a valid
// collection.
// \GlobIterator on Windows requires an absolute path.
$files = new \GlobIterator(realpath($directory . '/' . $collection) . '/*.' . $this->getFileExtension());
if (count($files)) {
$collections[] = $collection;
// @see \Drupal\Core\Config\FileStorage::listAll()
foreach (scandir($directory . '/' . $collection) as $file) {
if ($file[0] !== '.' && fnmatch('*.' . $this->getFileExtension(), $file)) {
$collections[] = $collection;
break;
}
}
}
}
......
......@@ -195,10 +195,17 @@ public function getComponentNames(array $list) {
// We don't have to use ExtensionDiscovery here because our list of
// extensions was already obtained through an ExtensionDiscovery scan.
$directory = $this->getComponentFolder($extension_object);
if (file_exists($directory)) {
$files = new \GlobIterator(\Drupal::root() . '/' . $directory . '/*' . $extension);
if (is_dir($directory)) {
// glob() directly calls into libc glob(), which is not aware of PHP
// stream wrappers. Same for \GlobIterator (which additionally requires
// an absolute realpath() on Windows).
// @see https://github.com/mikey179/vfsStream/issues/2
$files = scandir($directory);
foreach ($files as $file) {
$folders[$file->getBasename($extension)] = $directory;
if ($file[0] !== '.' && fnmatch('*' . $extension, $file)) {
$folders[basename($file, $extension)] = $directory;
}
}
}
}
......@@ -215,10 +222,17 @@ public function getCoreNames() {
$extension = '.' . $this->getFileExtension();
$folders = array();
$directory = $this->getCoreFolder();
if (file_exists($directory)) {
$files = new \GlobIterator(\Drupal::root() . '/' . $directory . '/*' . $extension);
if (is_dir($directory)) {
// glob() directly calls into libc glob(), which is not aware of PHP
// stream wrappers. Same for \GlobIterator (which additionally requires an
// absolute realpath() on Windows).
// @see https://github.com/mikey179/vfsStream/issues/2
$files = scandir($directory);
foreach ($files as $file) {
$folders[$file->getBasename($extension)] = $directory;
if ($file[0] !== '.' && fnmatch('*' . $extension, $file)) {
$folders[basename($file, $extension)] = $directory;
}
}
}
return $folders;
......
......@@ -15,6 +15,7 @@
use Drupal\Core\Config\NullStorage;
use Drupal\Core\Database\Database;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\Core\DependencyInjection\YamlFileLoader;
use Drupal\Core\Extension\ExtensionDiscovery;
......@@ -151,13 +152,16 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
protected $serviceYamls;
/**
* List of discovered service provider class names.
* List of discovered service provider class names or objects.
*
* This is a nested array whose top-level keys are 'app' and 'site', denoting
* the origin of a service provider. Site-specific providers have to be
* collected separately, because they need to be processed last, so as to be
* able to override services from application service providers.
*
* Allowing objects is for example used to allow
* \Drupal\KernelTests\KernelTestBase to register itself as service provider.
*
* @var array
*/
protected $serviceProviderClasses;
......@@ -427,6 +431,21 @@ public function getContainer() {
return $this->container;
}
/**
* {@inheritdoc}
*/
public function setContainer(ContainerInterface $container = NULL) {
if (isset($this->container)) {
throw new \Exception('The container should not override an existing container.');
}
if ($this->booted) {
throw new \Exception('The container cannot be set after a booted kernel.');
}
$this->container = $container;
return $this;
}
/**
* {@inheritdoc}
*/
......@@ -514,7 +533,7 @@ public function discoverServiceProviders() {
// Add site-specific service providers.
if (!empty($GLOBALS['conf']['container_service_providers'])) {
foreach ($GLOBALS['conf']['container_service_providers'] as $class) {
if (class_exists($class)) {
if ((is_string($class) && class_exists($class)) || (is_object($class) && ($class instanceof ServiceProviderInterface || $class instanceof ServiceModifierInterface))) {
$this->serviceProviderClasses['site'][] = $class;
}
}
......@@ -745,6 +764,13 @@ protected function initializeContainer() {
}
}
// If we haven't booted yet but there is a container, then we're asked to
// boot the container injected via setContainer().
// @see \Drupal\KernelTests\KernelTestBase::setUp()
if (isset($this->container) && !$this->booted) {
$container = $this->container;
}
// If the module list hasn't already been set in updateModules and we are
// not forcing a rebuild, then try and load the container from the disk.
if (empty($this->moduleList) && !$this->containerNeedsRebuild) {
......@@ -760,6 +786,7 @@ protected function initializeContainer() {
}
}
// If there is still no container, build a new one from scratch.
if (!isset($container)) {
$container = $this->compileContainer();
}
......@@ -1149,7 +1176,12 @@ protected function initializeServiceProviders() {
);
foreach ($this->serviceProviderClasses as $origin => $classes) {
foreach ($classes as $name => $class) {
$this->serviceProviders[$origin][$name] = new $class;
if (!is_object($class)) {
$this->serviceProviders[$origin][$name] = new $class;
}
else {
$this->serviceProviders[$origin][$name] = $class;
}
}
}
}
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
......@@ -16,7 +17,7 @@
* This interface extends Symfony's KernelInterface and adds methods for
* responding to modules being enabled or disabled during its lifetime.
*/
interface DrupalKernelInterface extends HttpKernelInterface {
interface DrupalKernelInterface extends HttpKernelInterface, ContainerAwareInterface {
/**
* Boots the current kernel.
......
......@@ -52,7 +52,7 @@ static function get($name) {
$configuration['bin'] = $name;
}
if (!isset($configuration['directory'])) {
$configuration['directory'] = DRUPAL_ROOT . '/' . PublicStream::basePath() . '/php';
$configuration['directory'] = PublicStream::basePath() . '/php';
}
return new $class($configuration);
}
......
......@@ -127,6 +127,15 @@ protected function getLocalPath($uri = NULL) {
$uri = $this->uri;
}
$path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
// In PHPUnit tests, the base path for local streams may be a virtual
// filesystem stream wrapper URI, in which case this local stream acts like
// a proxy. realpath() is not supported by vfsStream, because a virtual
// file system does not have a real filepath.
if (strpos($path, 'vfs://') === 0) {
return $path;
}
$realpath = realpath($path);
if (!$realpath) {
// This file does not yet exist.
......
......@@ -331,10 +331,12 @@ public function getLangcodes() {
if (empty($langcodes)) {
$langcodes = array();
// Collect languages included with CKEditor based on file listing.
$ckeditor_languages = new \GlobIterator(\Drupal::root() . '/core/assets/vendor/ckeditor/lang/*.js');
foreach ($ckeditor_languages as $language_file) {
$langcode = $language_file->getBasename('.js');
$langcodes[$langcode] = $langcode;
$files = scandir('core/assets/vendor/ckeditor/lang');
foreach ($files as $file) {
if ($file[0] !== '.' && fnmatch('*.js', $file)) {
$langcode = basename($file, '.js');
$langcodes[$langcode] = $langcode;
}
}
\Drupal::cache()->set('ckeditor.langcodes', $langcodes);
}
......
......@@ -33,6 +33,9 @@
* Additional modules needed in a test may be loaded and added to the fixed
* module list.
*
* @deprecated in Drupal 8.0.x, will be removed before Drupal 8.2.x. Use
* \Drupal\KernelTests\KernelTestBase instead.
*
* @see \Drupal\simpletest\KernelTestBase::$modules
* @see \Drupal\simpletest\KernelTestBase::enableModules()
*
......
<?php
/**
* @file
* Contains \Drupal\simpletest\RandomGeneratorTrait.
*/
namespace Drupal\simpletest;
use Drupal\Component\Utility\Random;
/**
* Provides random generator utility methods.
*/
trait RandomGeneratorTrait {
/**
* The random generator.
*
* @var \Drupal\Component\Utility\Random
*/
protected $randomGenerator;
/**
* Generates a pseudo-random string of ASCII characters of codes 32 to 126.
*
* Do not use this method when special characters are not possible (e.g., in
* machine or file names that have already been validated); instead, use
* \Drupal\simpletest\TestBase::randomMachineName(). If $length is greater
* than 3 the random string will include at least one ampersand ('&') and
* at least one greater than ('>') character to ensure coverage for special
* characters and avoid the introduction of random test failures.
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Pseudo-randomly generated unique string including special characters.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function randomString($length = 8) {
if ($length < 4) {
return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate'));
}
// To prevent the introduction of random test failures, ensure that the
// returned string contains a character that needs to be escaped in HTML by
// injecting an ampersand into it.
$replacement_pos = floor($length / 2);
// Remove 2 from the length to account for the ampersand and greater than
// characters.
$string = $this->getRandomGenerator()->string($length - 2, TRUE, array($this, 'randomStringValidate'));
return substr_replace($string, '>&', $replacement_pos, 0);
}
/**
* Callback for random string validation.
*
* @see \Drupal\Component\Utility\Random::string()
*
* @param string $string
* The random string to validate.
*
* @return bool
* TRUE if the random string is valid, FALSE if not.
*/
public function randomStringValidate($string) {
// Consecutive spaces causes issues for
// \Drupal\simpletest\WebTestBase::assertLink().
if (preg_match('/\s{2,}/', $string)) {
return FALSE;
}
// Starting or ending with a space means that length might not be what is
// expected.
if (preg_match('/^\s|\s$/', $string)) {
return FALSE;
}
return TRUE;
}
/**
* Generates a unique random string containing letters and numbers.
*
* Do not use this method when testing unvalidated user input. Instead, use
* \Drupal\simpletest\TestBase::randomString().
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Randomly generated unique string.
*
* @see \Drupal\Component\Utility\Random::name()
*/
protected function randomMachineName($length = 8) {
return $this->getRandomGenerator()->name($length, TRUE);
}
/**
* Generates a random PHP object.
*
* @param int $size
* The number of random keys to add to the object.
*
* @return \stdClass
* The generated object, with the specified number of random keys. Each key
* has a random string value.
*
* @see \Drupal\Component\Utility\Random::object()
*/
public function randomObject($size = 4) {
return $this->getRandomGenerator()->object($size);
}
/**
* Gets the random generator for the utility methods.
*
* @return \Drupal\Component\Utility\Random
* The random generator.
*/
protected function getRandomGenerator() {
if (!is_object($this->randomGenerator)) {
$this->randomGenerator = new Random();
}
return $this->randomGenerator;
}
}
......@@ -28,6 +28,7 @@
abstract class TestBase {
use SessionTestTrait;
use RandomGeneratorTrait;
/**
* The test run ID.
......@@ -283,13 +284,6 @@ abstract class TestBase {
*/
protected $configImporter;
/**
* The random generator.
*
* @var \Drupal\Component\Utility\Random
*/
protected $randomGenerator;
/**
* Set to TRUE to strict check all configuration saved.
*
......@@ -1417,113 +1411,6 @@ protected function settingsSet($name, $value) {
new Settings($settings);
}
/**
* Generates a pseudo-random string of ASCII characters of codes 32 to 126.
*
* Do not use this method when special characters are not possible (e.g., in
* machine or file names that have already been validated); instead, use
* \Drupal\simpletest\TestBase::randomMachineName(). If $length is greater
* than 3 the random string will include at least one ampersand ('&') and
* at least one greater than ('>') character to ensure coverage for special
* characters and avoid the introduction of random test failures.
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Pseudo-randomly generated unique string including special characters.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function randomString($length = 8) {
if ($length < 4) {
return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate'));
}
// To prevent the introduction of random test failures, ensure that the
// returned string contains a character that needs to be escaped in HTML by
// injecting an ampersand into it.
$replacement_pos = floor($length / 2);
// Remove 2 from the length to account for the ampersand and greater than
// characters.
$string = $this->getRandomGenerator()->string($length - 2, TRUE, array($this, 'randomStringValidate'));
return substr_replace($string, '>&', $replacement_pos, 0);
}
/**
* Callback for random string validation.
*
* @see \Drupal\Component\Utility\Random::string()
*
* @param string $string
* The random string to validate.
*
* @return bool
* TRUE if the random string is valid, FALSE if not.
*/
public function randomStringValidate($string) {
// Consecutive spaces causes issues for
// Drupal\simpletest\WebTestBase::assertLink().
if (preg_match('/\s{2,}/', $string)) {
return FALSE;
}
// Starting or ending with a space means that length might not be what is
// expected.
if (preg_match('/^\s|\s$/', $string)) {
return FALSE;
}
return TRUE;
}
/**
* Generates a unique random string containing letters and numbers.
*
* Do not use this method when testing unvalidated user input. Instead, use
* \Drupal\simpletest\TestBase::randomString().
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Randomly generated unique string.
*
* @see \Drupal\Component\Utility\Random::name()
*/
public function randomMachineName($length = 8) {
return $this->getRandomGenerator()->name($length, TRUE);
}
/**
* Generates a random PHP object.
*
* @param int $size
* The number of random keys to add to the object.
*
* @return \stdClass
* The generated object, with the specified number of random keys. Each key
* has a random string value.
*
* @see \Drupal\Component\Utility\Random::object()
*/
public function randomObject($size = 4) {
return $this->getRandomGenerator()->object($size);
}
/**
* Gets the random generator for the utility methods.
*
* @return \Drupal\Component\Utility\Random
* The random generator
*/
protected function getRandomGenerator() {
if (!is_object($this->randomGenerator)) {
$this->randomGenerator = new Random();
}
return $this->randomGenerator;
}
/**
* Converts a list of possible parameters into a stack of permutations.
*
......
......@@ -81,6 +81,7 @@ public function registerTestNamespaces() {
// Add PHPUnit test namespaces of Drupal core.
$this->testNamespaces['Drupal\\Tests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/Tests'];
$this->testNamespaces['Drupal\\KernelTests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/KernelTests'];
$this->testNamespaces['Drupal\\FunctionalTests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/FunctionalTests'];
$this->availableExtensions = array();
......@@ -98,6 +99,7 @@ public function registerTestNamespaces() {
// Add PHPUnit test namespaces.
$this->testNamespaces["Drupal\\Tests\\$name\\Unit\\"][] = "$base_path/tests/src/Unit";
$this->testNamespaces["Drupal\\Tests\\$name\\Kernel\\"][] = "$base_path/tests/src/Kernel";
$this->testNamespaces["Drupal\\Tests\\$name\\Functional\\"][] = "$base_path/tests/src/Functional";
}
......
......@@ -117,7 +117,7 @@ public function testRandomString($length) {
$this->assertEquals($length, strlen($string));
// randomString() should always include an ampersand ('&') and a
// greater than ('>') if $length is greater than 3.
if ($length > 3) {
if ($length > 4) {
$this->assertContains('&', $string);
$this->assertContains('>', $string);
}
......
......@@ -5,11 +5,11 @@
* Contains \Drupal\system\Tests\Extension\ModuleHandlerTest.
*/
namespace Drupal\system\Tests\Extension;
namespace Drupal\Tests\system\Kernel\Extension;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\simpletest\KernelTestBase;
use \Drupal\Core\Extension\ModuleUninstallValidatorException;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests ModuleHandler functionality.
......@@ -21,10 +21,13 @@ class ModuleHandlerTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('system');
public function setUp() {
protected function setUp() {
parent::setUp();
// @todo ModuleInstaller calls system_rebuild_module_data which is part of
// system.module, see https://www.drupal.org/node/2208429.
include_once $this->root . '/core/modules/system/system.module';
// Set up the state values so we know where to find the files when running
// drupal_get_filename().
// @todo Remove as part of https://www.drupal.org/node/2186491
......@@ -34,8 +37,8 @@ public function setUp() {
/**
* {@inheritdoc}
*/
public function containerBuild(ContainerBuilder $container) {
parent::containerBuild($container);
public function register(ContainerBuilder $container) {
parent::register($container);
// Put a fake route bumper on the container to be called during uninstall.
$container
->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper');
......@@ -45,24 +48,9 @@ public function containerBuild(ContainerBuilder $container) {
* The basic functionality of retrieving enabled modules.
*/
function testModuleList() {
// Prime the drupal_get_filename() static cache with the location of the
// testing profile as it is not the currently active profile and we don't
// yet have any cached way to retrieve its location.
// @todo Remove as part of https://www.drupal.org/node/2186491
drupal_get_filename('profile', 'testing', 'core/profiles/testing/testing.info.yml');
// Build a list of modules, sorted alphabetically.
$profile_info = install_profile_info('testing', 'en');
$module_list = $profile_info['dependencies'];
$module_list = array();
// Installation profile is a module that is expected to be loaded.
$module_list[] = 'testing';
sort($module_list);
// Compare this list to the one returned by the module handler. We expect
// them to match, since all default profile modules have a weight equal to 0
// (except for block.module, which has a lower weight but comes first in
// the alphabet anyway).
$this->assertModuleList($module_list, 'Testing profile');
$this->assertModuleList($module_list, 'Initial');
// Try to install a new module.
$this->moduleInstaller()->install(array('ban'));
......@@ -98,7 +86,6 @@ function testModuleList() {
protected function assertModuleList(Array $expected_values, $condition) {
$expected_values = array_values(array_unique($expected_values));
$enabled_modules = array_keys($this->container->get('module_handler')->getModuleList());
$enabled_modules = sort($enabled_modules);
$this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition)));
}
......@@ -196,7 +183,7 @@ function testDependencyResolution() {
function testUninstallProfileDependency() {
$profile = 'minimal';
$dependency = 'dblog';
$this->settingsSet('install_profile', $profile);
$this->setSetting('install_profile', $profile);
// Prime the drupal_get_filename() static cache with the location of the
// minimal profile as it is not the currently active profile and we don't
// yet have any cached way to retrieve its location.
......
......@@ -5,14 +5,14 @@
* Contains \Drupal\system\Tests\PhpStorage\PhpStorageFactoryTest.
*/
namespace Drupal\system\Tests\PhpStorage;
namespace Drupal\Tests\system\Kernel\PhpStorage;
use Drupal\Component\PhpStorage\MTimeProtectedFileStorage;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\simpletest\KernelTestBase;
use Drupal\system\PhpStorage\MockPhpStorage;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the PHP storage factory.
......@@ -22,6 +22,18 @@
*/
class PhpStorageFactoryTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Empty the PHP storage settings, as KernelTestBase sets it by default.
$settings = Settings::getAll();
unset($settings['php_storage']);
new Settings($settings);
}
/**
* Tests the get() method with no settings.
*/
......@@ -59,7 +71,7 @@ public function testGetOverride() {
$this->setSettings('test', array('directory' => NULL));
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.');
$this->assertIdentical(\Drupal::root() . '/' . PublicStream::basePath() . '/php', $php->getConfigurationValue('directory'), 'Default file directory was used.');
$this->assertIdentical(PublicStream::basePath() . '/php', $php->getConfigurationValue('directory'), 'Default file directory was used.');
// Test that a default storage class is set if it's empty.
$this->setSettings('test', array('class' => NULL));
......
......@@ -22,6 +22,16 @@
<!-- Exclude Drush tests. -->
<exclude>./drush/tests</exclude>
</testsuite>
<testsuite name="kernel">
<directory>./tests/Drupal/KernelTests</directory>
<directory>./modules/*/tests/src/Kernel</directory>
<directory>../modules/*/tests/src/Kernel</directory>
<directory>../sites/*/modules/*/tests/src/Kernel</directory>
<!-- Exclude Composer's vendor directory so we don't run tests there. -->
<exclude>./vendor</exclude>
<!-- Exclude Drush tests. -->
<exclude>./drush/tests</exclude>
</testsuite>
<testsuite name="functional">
<directory>./tests/Drupal/FunctionalTests</directory>
<directory>./modules/*/tests/src/Functional</directory>
......
......@@ -709,7 +709,7 @@ function simpletest_script_command($test_id, $test_class) {
* @see simpletest_script_ru