Commit 4d15b638 authored by catch's avatar catch

Issue #1774388 by sun, chx: Unit Tests Remastered™.

parent d2699d34
......@@ -157,8 +157,6 @@ function config_import() {
try {
$remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage);
config_sync_changes($remaining_changes, $source_storage, $target_storage);
// Flush all caches and reset static variables after a successful import.
drupal_flush_all_caches();
}
catch (ConfigException $e) {
watchdog_exception('config_import', $e);
......
......@@ -183,7 +183,7 @@ function system_list($type) {
// Drupal installations, which might have modules installed in different
// locations in the file system. The ordering here must also be
// consistent with the one used in module_implements().
$enabled_modules = config('system.module')->get('enabled');
$enabled_modules = (array) config('system.module')->get('enabled');
$module_files = state()->get('system.module.files');
foreach ($enabled_modules as $name => $weight) {
// Build a list of all enabled modules.
......@@ -197,7 +197,7 @@ function system_list($type) {
}
// Build a list of themes.
$enabled_themes = config('system.theme')->get('enabled');
$enabled_themes = (array) config('system.theme')->get('enabled');
// @todo Themes include all themes, including disabled/uninstalled. This
// system.theme.data state will go away entirely as soon as themes have
// a proper installation status.
......
......@@ -7,12 +7,12 @@
namespace Drupal\config\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests CRUD operations on configuration objects.
*/
class ConfigCRUDTest extends WebTestBase {
class ConfigCRUDTest extends DrupalUnitTestBase {
public static function getInfo() {
return array(
'name' => 'CRUD operations',
......
......@@ -8,12 +8,12 @@
namespace Drupal\config\Tests;
use Drupal\Core\Config\FileStorage;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests reading and writing file contents.
*/
class ConfigFileContentTest extends WebTestBase {
class ConfigFileContentTest extends DrupalUnitTestBase {
public static function getInfo() {
return array(
'name' => 'File content',
......
......@@ -7,12 +7,12 @@
namespace Drupal\config\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests importing configuration from files into active configuration.
*/
class ConfigImportTest extends WebTestBase {
class ConfigImportTest extends DrupalUnitTestBase {
/**
* Modules to enable.
......@@ -32,7 +32,10 @@ public static function getInfo() {
function setUp() {
parent::setUp();
// Clear out any possibly existing hook invocation records.
config_install_default_config('module', 'config_test');
// Installing config_test's default configuration pollutes the global
// variable being used for recording hook invocations by this test already,
// so it has to be cleared out manually.
unset($GLOBALS['hook_config_test']);
}
......
......@@ -7,12 +7,12 @@
namespace Drupal\config\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests installation of configuration objects in installation functionality.
*/
class ConfigInstallTest extends WebTestBase {
class ConfigInstallTest extends DrupalUnitTestBase {
public static function getInfo() {
return array(
'name' => 'Installation functionality',
......@@ -21,6 +21,14 @@ public static function getInfo() {
);
}
function setUp() {
parent::setUp();
// Ensure the global variable being asserted by this test does not exist;
// a previous test executed in this request/process might have set it.
unset($GLOBALS['hook_config_test']);
}
/**
* Tests module installation.
*/
......@@ -35,7 +43,7 @@ function testModuleInstallation() {
$this->assertIdentical($config->isNew(), TRUE);
// Install the test module.
module_enable(array('config_test'));
$this->enableModules(array('config_test'));
// Verify that default module config exists.
$config = config($default_config);
......
......@@ -7,12 +7,12 @@
namespace Drupal\config\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests configuration overrides via $conf in settings.php.
*/
class ConfigOverrideTest extends WebTestBase {
class ConfigOverrideTest extends DrupalUnitTestBase {
/**
* Modules to enable.
......@@ -29,6 +29,12 @@ public static function getInfo() {
);
}
function setUp() {
parent::setUp();
config_install_default_config('module', 'config_test');
}
/**
* Tests configuration override.
*/
......
......@@ -7,7 +7,7 @@
namespace Drupal\config\Tests\Storage;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Base class for testing storage controller operations.
......@@ -21,7 +21,7 @@
* supply the necessary helper methods to interact with the raw/native storage
* directly.
*/
abstract class ConfigStorageTestBase extends WebTestBase {
abstract class ConfigStorageTestBase extends DrupalUnitTestBase {
/**
* Tests storage controller CRUD operations.
......
<?php
/**
* @file
* Contains Drupal\simpletest\DrupalUnitTestBase.
*/
namespace Drupal\simpletest;
use Drupal\Core\Database\Database;
/**
* Base test case class for Drupal unit tests.
*
* Tests extending this base class can access files and the database, but the
* entire environment is initially empty. Drupal runs in a minimal mocked
* environment, comparable to the one in the installer or update.php.
*
* The module/hook system is functional and operates on a fixed module list.
* Additional modules needed in a test may be loaded and added to the fixed
* module list.
*
* @see DrupalUnitTestBase::$modules
* @see DrupalUnitTestBase::enableModules()
*/
abstract class DrupalUnitTestBase extends UnitTestBase {
/**
* Modules to enable.
*
* Test classes extending this class, and any classes in the hierarchy up to
* this class, may specify individual lists of modules to enable by setting
* this property. The values of all properties in all classes in the hierarchy
* are merged.
*
* Unlike UnitTestBase::setUp(), any modules specified in the $modules
* property are automatically loaded and set as the fixed module list.
*
* Unlike WebTestBase::setUp(), the specified modules are loaded only, but not
* automatically installed. Modules need to be installed manually, if needed.
*
* @see DrupalUnitTestBase::enableModules()
* @see DrupalUnitTestBase::setUp()
*
* @var array
*/
public static $modules = array();
/**
* Fixed module list being used by this test.
*
* @var array
* An associative array containing the required data for the $fixed_list
* argument of module_list().
*
* @see UnitTestBase::setUp()
* @see UnitTestBase::enableModules()
*/
private $moduleList = array();
private $moduleFiles;
private $themeFiles;
private $themeData;
/**
* Sets up Drupal unit test environment.
*
* @see DrupalUnitTestBase::$modules
* @see DrupalUnitTestBase
*/
protected function setUp() {
global $conf;
// Copy/prime extension file lists once to avoid filesystem scans.
if (!isset($this->moduleFiles)) {
$this->moduleFiles = state()->get('system.module.files') ?: array();
$this->themeFiles = state()->get('system.theme.files') ?: array();
$this->themeData = state()->get('system.theme.data') ?: array();
}
parent::setUp();
// Provide a minimal, partially mocked environment for unit tests.
$conf['lock_backend'] = 'Drupal\Core\Lock\NullLockBackend';
$conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\MemoryBackend');
$this->container
->register('config.storage', 'Drupal\Core\Config\FileStorage')
->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
$conf['keyvalue_default'] = 'keyvalue.memory';
$this->container
->register('keyvalue.memory', 'Drupal\Core\KeyValueStore\KeyValueMemoryFactory');
state()->set('system.module.files', $this->moduleFiles);
state()->set('system.theme.files', $this->themeFiles);
state()->set('system.theme.data', $this->themeData);
// Ensure that the module list is initially empty.
$this->moduleList = array();
// Collect and set a fixed module list.
$class = get_class($this);
$modules = array();
while ($class) {
if (property_exists($class, 'modules')) {
$modules = array_merge($modules, $class::$modules);
}
$class = get_parent_class($class);
}
$this->enableModules($modules, FALSE);
}
/**
* Installs a specific table from a module schema definition.
*
* @param string $module
* The name of the module that defines the table's schema.
* @param string $table
* The name of the table to install.
*/
protected function installSchema($module, $table) {
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$module.install";
$function = $module . '_schema';
$schema = $function();
Database::getConnection()->schema()->createTable($table, $schema[$table]);
}
/**
* Enables modules for this test.
*
* Callbacks invoked by module_enable() may need to access information
* provided by info hooks of the new modules already. However, module_enable()
* enables the new modules in the system.module configuration only, but that
* has no effect, since we are operating with a fixed module list.
*
* @param array $modules
* A list of modules to enable.
* @param bool $install
* (optional) Whether to install the list of modules via module_enable().
* Defaults to TRUE. If FALSE, the new modules are only added to the fixed
* module list and loaded.
*
* @todo Remove this method as soon as there is an Extensions service
* implementation that is able to manage a fixed module list.
*/
protected function enableModules(array $modules, $install = TRUE) {
// Set the modules in the fixed module_list().
foreach ($modules as $module) {
$this->moduleList[$module]['filename'] = drupal_get_filename('module', $module);
}
module_list(NULL, $this->moduleList);
// Call module_enable() to enable (install) the new modules.
if ($install) {
module_enable($modules);
}
// Otherwise, only ensure that the new modules are loaded.
else {
module_load_all(FALSE, TRUE);
}
}
}
......@@ -856,22 +856,23 @@ protected function prepareEnvironment() {
// a test-prefix-specific directory within the public files directory.
// @see config_get_config_directory()
$GLOBALS['config_directories'] = array();
foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) {
$GLOBALS['config_directories'][$type]['path'] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type;
}
// Reset and create a new service container.
$this->container = drupal_container(NULL, TRUE);
$this->configDirectories = array();
include_once DRUPAL_ROOT . '/core/includes/install.inc';
foreach ($GLOBALS['config_directories'] as $type => $directory) {
foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) {
// Assign the relative path to the global variable.
$path = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type;
$GLOBALS['config_directories'][$type]['path'] = $path;
// Ensure the directory can be created and is writeable.
if (!install_ensure_config_directory($type)) {
return FALSE;
}
$this->configDirectories[$type] = $this->originalFileDirectory . '/' . $directory['path'];
// Provide the already resolved path for tests.
$this->configDirectories[$type] = $this->originalFileDirectory . '/' . $path;
}
// Reset and create a new service container.
$this->container = drupal_container(NULL, TRUE);
// Unset globals.
unset($GLOBALS['theme_key']);
unset($GLOBALS['theme']);
......@@ -1028,6 +1029,7 @@ protected function tearDown() {
*/
public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
if ($severity & error_reporting()) {
require_once DRUPAL_ROOT . '/core/includes/errors.inc';
$error_map = array(
E_STRICT => 'Run-time notice',
E_WARNING => 'Warning',
......@@ -1041,6 +1043,14 @@ public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
);
$backtrace = debug_backtrace();
// Add verbose backtrace for errors, but not for debug() messages.
if ($severity !== E_USER_NOTICE) {
$verbose_backtrace = $backtrace;
array_shift($verbose_backtrace);
$message .= '<pre class="backtrace">' . format_backtrace($verbose_backtrace) . '</pre>';
}
$this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace));
}
return TRUE;
......@@ -1052,15 +1062,19 @@ public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
* @see set_exception_handler
*/
protected function exceptionHandler($exception) {
require_once DRUPAL_ROOT . '/core/includes/errors.inc';
$backtrace = $exception->getTrace();
$verbose_backtrace = $backtrace;
// Push on top of the backtrace the call that generated the exception.
array_unshift($backtrace, array(
'line' => $exception->getLine(),
'file' => $exception->getFile(),
));
require_once DRUPAL_ROOT . '/core/includes/errors.inc';
// The exception message is run through check_plain() by _drupal_decode_exception().
$this->error(t('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception)), 'Uncaught exception', _drupal_get_last_caller($backtrace));
$message = format_string('%type: !message in %function (line %line of %file). <pre class="backtrace">!backtrace</pre>', _drupal_decode_exception($exception) + array(
'!backtrace' => format_backtrace($verbose_backtrace),
));
$this->error($message, 'Uncaught exception', _drupal_get_last_caller($backtrace));
}
/**
......
......@@ -11,7 +11,7 @@
use Drupal\Core\Database\ConnectionNotDefinedException;
/**
* Test case for Drupal unit tests.
* Base test case class for unit tests.
*
* These tests can not access the database nor files. Calling any Drupal
* function that needs the database will throw exceptions. These include
......@@ -46,7 +46,6 @@ protected function setUp() {
if (!$this->setupEnvironment) {
return FALSE;
}
$this->originalThemeRegistry = theme_get_registry(FALSE);
// Reset all statics and variables to perform tests in a clean environment.
$conf = array();
......
......@@ -2858,8 +2858,8 @@ function system_rebuild_module_data() {
$files = array();
ksort($modules);
// Add name, status, weight, and schema version.
$enabled_modules = config('system.module')->get('enabled');
$disabled_modules = config('system.module.disabled')->get();
$enabled_modules = (array) config('system.module')->get('enabled');
$disabled_modules = (array) config('system.module.disabled')->get();
$all_modules = $enabled_modules + $disabled_modules;
foreach ($modules as $module => $record) {
$record->name = $module;
......@@ -3031,7 +3031,7 @@ function system_rebuild_theme_data() {
// based on the current config. Remove this code when themes have a proper
// installation status.
// @see http://drupal.org/node/1067408
$enabled_themes = config('system.theme')->get('enabled');
$enabled_themes = (array) config('system.theme')->get('enabled');
$files = array();
foreach ($themes as $name => $theme) {
$theme->status = (int) isset($enabled_themes[$name]);
......
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