diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php index ae28e75..495d978 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php @@ -111,8 +111,9 @@ function testEnableRequirementsFailureDependency() { } /** - * Tests that module dependencies are enabled in the correct order via the - * UI. Dependencies should be enabled before their dependents. + * Tests that module dependencies are enabled in the correct order in the UI. + * + * Dependencies should be enabled before their dependents. */ function testModuleEnableOrder() { \Drupal::moduleHandler()->install(array('module_test'), FALSE); @@ -120,34 +121,22 @@ function testModuleEnableOrder() { $this->assertModules(array('module_test'), TRUE); \Drupal::state()->set('module_test.dependency', 'dependency'); // module_test creates a dependency chain: - // - forum depends on node, taxonomy, comment, datetime, history, and - // ban (via module_test) - // - node depends on text - // - text depends on filter - // - taxonomy depends on options - // - ban depends on xmlrpc (via module_test) - // The correct enable order is: - $expected_order = array('filter', 'text', 'options', 'xmlrpc', 'ban', 'node', 'datetime', 'comment', 'history', 'taxonomy', 'forum'); + // - color depends on config + // - config depends on help + $expected_order = array('help', 'config', 'color'); // Enable the modules through the UI, verifying that the dependency chain // is correct. $edit = array(); - $edit['modules[Core][forum][enable]'] = 'forum'; + $edit['modules[Core][color][enable]'] = 'color'; $this->drupalPostForm('admin/modules', $edit, t('Save configuration')); - $this->assertModules(array('forum'), FALSE); - $this->assertText(t('You must enable the Node, Text, Filter, History, Taxonomy, Options, Comment, Datetime, Ban, XML-RPC modules to install Forum.')); - $edit['modules[Core][node][enable]'] = 'node'; - $edit['modules[Field types][text][enable]'] = 'text'; - $edit['modules[Core][filter][enable]'] = 'filter'; - $edit['modules[Core][history][enable]'] = 'history'; - $edit['modules[Field types][options][enable]'] = 'options'; - $edit['modules[Core][taxonomy][enable]'] = 'taxonomy'; - $edit['modules[Core][comment][enable]'] = 'comment'; - $edit['modules[Field types][datetime][enable]'] = 'datetime'; - $edit['modules[Core][ban][enable]'] = 'ban'; - $edit['modules[Core][xmlrpc][enable]'] = 'xmlrpc'; + $this->assertModules(array('color'), FALSE); + $this->assertText(t('You must enable the Help, Configuration Manager modules to install Color.')); + + $edit['modules[Core][config][enable]'] = 'config'; + $edit['modules[Core][help][enable]'] = 'help'; $this->drupalPostForm('admin/modules', $edit, t('Save configuration')); - $this->assertModules(array('forum', 'ban', 'node', 'text', 'filter', 'xmlrpc', 'datetime', 'comment', 'history', 'taxonomy', 'options'), TRUE); + $this->assertModules(array('color', 'config', 'help'), TRUE); // Check the actual order which is saved by module_test_modules_enabled(). $module_order = \Drupal::state()->get('module_test.install_order') ?: array(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php index 5c2a43a..313a7cb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Module; +use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\simpletest\DrupalUnitTestBase; use Symfony\Component\HttpFoundation\Response; @@ -23,6 +24,12 @@ public static function getInfo() { ); } + public function containerBuild(ContainerBuilder $container) { + parent::containerBuild($container); + $container + ->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper'); + } + /** * The basic functionality of retrieving enabled modules. */ @@ -42,7 +49,7 @@ function testModuleList() { $this->assertModuleList($module_list, t('Testing profile')); // Try to install a new module. - \Drupal::moduleHandler()->install(array('ban')); + $this->moduleHandler()->install(array('ban')); $module_list[] = 'ban'; sort($module_list); $this->assertModuleList($module_list, t('After adding a module')); @@ -60,7 +67,7 @@ function testModuleList() { 'system' => 'core/modules/system/system.module', 'menu' => 'core/modules/menu/menu.module', ); - $this->container->get('module_handler')->setModuleList($fixed_list); + $this->moduleHandler()->setModuleList($fixed_list); $new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list)); $this->assertModuleList($new_module_list, t('When using a fixed list')); @@ -74,19 +81,20 @@ 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 = array_keys($this->moduleHandler()->getModuleList()); $enabled_modules = sort($enabled_modules); $this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition))); } /** - * Test \Drupal::moduleHandler()->getImplementations() caching. + * Tests ModuleHandler::getImplementations() caching. */ function testModuleImplements() { // Clear the cache. \Drupal::cache('bootstrap')->delete('module_implements'); $this->assertFalse(\Drupal::cache('bootstrap')->get('module_implements'), 'The module implements cache is empty.'); - \Drupal::moduleHandler()->getImplementations('foo'); + $this->moduleHandler()->getImplementations('foo'); + // @see \Drupal\Core\Extension\CachedModuleHandler::writeCache() // @see \Drupal\Core\EventSubscriber\RequestCloseSubscriber $this->kernel->terminate($this->container->get('request'), new Response()); @@ -98,98 +106,95 @@ function testModuleImplements() { // the hook implementation cache, as that is expected to happen implicitly // by setting the module list. This verifies that the hook implementation // cache is cleared whenever setModuleList() is called. - $module_handler = \Drupal::moduleHandler(); + $module_handler = $this->moduleHandler(); $module_handler->invokeAll('test'); // Make sure group include files are detected properly even when the file is // already loaded when the cache is rebuilt. // For that activate the module_test which provides the file to load. - \Drupal::moduleHandler()->install(array('module_test')); + $this->moduleHandler()->install(array('module_test')); $modules = $module_handler->getImplementations('test_hook'); $this->assertTrue(in_array('module_test', $modules), 'Hook found.'); } /** - * Test that moduleHandler()->invoke() can load a hook defined in - * hook_hook_info(). + * Tests that ModuleHandler::invoke() can load a hook defined in hook_hook_info(). */ function testModuleInvoke() { - \Drupal::moduleHandler()->install(array('module_test'), FALSE); - $actual = \Drupal::moduleHandler()->invoke('module_test', 'test_hook', array()); + $this->moduleHandler()->install(array('module_test'), FALSE); + $actual = $this->moduleHandler()->invoke('module_test', 'test_hook', array()); $this->assertEqual(array('module_test' => 'success!'), $actual); } /** - * Test that \Drupal::moduleHandler()->invokeAll() can load a hook defined in hook_hook_info(). + * Tests that ModuleHandler::invokeAll() can load a hook defined in hook_hook_info(). */ function testModuleInvokeAll() { - \Drupal::moduleHandler()->install(array('module_test'), FALSE); - $actual = \Drupal::moduleHandler()->invokeAll('test_hook', array()); + $this->moduleHandler()->install(array('module_test'), FALSE); + $actual = $this->moduleHandler()->invokeAll('test_hook', array()); $this->assertEqual(array('module_test' => 'success!'), $actual); } /** - * Test dependency resolution. + * Tests dependency resolution. + * + * Intentionally using fake dependencies added via hook_system_info_alter() + * for modules that normally do not have any dependencies. + * + * To simplify things further, all of the manipulated modules are either + * purely UI-facing or live at the "bottom" of all dependency chains. + * + * @see module_test_system_info_alter() + * @see https://drupal.org/files/issues/dep.gv__0.png */ function testDependencyResolution() { - // Enable the test module, and make sure that other modules we are testing - // are not already enabled. (If they were, the tests below would not work - // correctly.) - \Drupal::moduleHandler()->install(array('module_test'), FALSE); - $this->assertTrue(\Drupal::moduleHandler()->moduleExists('module_test'), 'Test module is enabled.'); - $this->assertFalse(\Drupal::moduleHandler()->moduleExists('forum'), 'Forum module is disabled.'); - $this->assertFalse(\Drupal::moduleHandler()->moduleExists('ban'), 'Ban module is disabled.'); - $this->assertFalse(\Drupal::moduleHandler()->moduleExists('xmlrpc'), 'XML-RPC module is disabled.'); - - // First, create a fake missing dependency. Forum depends on ban, which - // depends on a made-up module, foo. Nothing should be installed. + $this->enableModules(array('module_test')); + $this->assertTrue($this->moduleHandler()->moduleExists('module_test'), 'Test module is enabled.'); + + // Ensure that modules are not enabled. + $this->assertFalse($this->moduleHandler()->moduleExists('color'), 'Color module is disabled.'); + $this->assertFalse($this->moduleHandler()->moduleExists('config'), 'Config module is disabled.'); + $this->assertFalse($this->moduleHandler()->moduleExists('help'), 'Help module is disabled.'); + + // Create a missing fake dependency. + // Color will depend on Config, which depends on a non-existing module Foo. + // Nothing should be installed. \Drupal::state()->set('module_test.dependency', 'missing dependency'); drupal_static_reset('system_rebuild_module_data'); - $result = \Drupal::moduleHandler()->install(array('forum')); - $this->assertFalse($result, '\Drupal\Core\Extension\ModuleHandler::install() returns FALSE if dependencies are missing.'); - $this->assertFalse(\Drupal::moduleHandler()->moduleExists('forum'), '\Drupal\Core\Extension\ModuleHandler::install() aborts if dependencies are missing.'); - - // @todo This makes no sense... Split the dependency resolution - // functionality in ModuleHandler::install() into an own method instead. - $this->enableModules(array('system', 'user', 'entity', 'field')); - $this->installSchema('system', array('router')); - $this->installSchema('user', array('users', 'users_roles')); - $this->container->get('module_handler')->invoke('user', 'install'); - - // Now, fix the missing dependency. Forum module depends on ban, but ban - // depends on the XML-RPC module. - // \Drupal\Core\Extension\ModuleHandler::install() should work. + + $result = $this->moduleHandler()->install(array('color')); + $this->assertFalse($result, 'ModuleHandler::install() returns FALSE if dependencies are missing.'); + $this->assertFalse($this->moduleHandler()->moduleExists('color'), 'ModuleHandler::install() aborts if dependencies are missing.'); + + // Fix the missing dependency. + // Forum module depends on Ban. Ban depends on XML-RPC module. \Drupal::state()->set('module_test.dependency', 'dependency'); drupal_static_reset('system_rebuild_module_data'); - $result = \Drupal::moduleHandler()->install(array('forum')); - $this->assertTrue($result, '\Drupal\Core\Extension\ModuleHandler::install() returns the correct value.'); + + $result = $this->moduleHandler()->install(array('color')); + $this->assertTrue($result, 'ModuleHandler::install() returns the correct value.'); + // Verify that the fake dependency chain was installed. - $this->assertTrue(\Drupal::moduleHandler()->moduleExists('ban') && \Drupal::moduleHandler()->moduleExists('xmlrpc'), 'Dependency chain was installed.'); + $this->assertTrue($this->moduleHandler()->moduleExists('config') && $this->moduleHandler()->moduleExists('help'), 'Dependency chain was installed.'); + // Verify that the original module was installed. - $this->assertTrue(\Drupal::moduleHandler()->moduleExists('forum'), 'Module installation with unlisted dependencies succeeded.'); - // Finally, verify that the modules were enabled in the correct order. + $this->assertTrue($this->moduleHandler()->moduleExists('color'), 'Module installation with dependencies succeeded.'); + + // Verify that the modules were enabled in the correct order. $module_order = \Drupal::state()->get('module_test.install_order') ?: array(); - $this->assertEqual($module_order, array('xmlrpc', 'ban', 'forum')); + $this->assertEqual($module_order, array('help', 'config', 'color')); - // Now uninstall all three modules explicitly, but in the incorrect order, - // and make sure that drupal_uninstall_modules() uninstalled them in the + // Uninstall all three modules explicitly, but in the incorrect order, + // and make sure that ModuleHandler::uninstall() uninstalled them in the // correct sequence. - $result = module_uninstall(array('ban', 'xmlrpc', 'forum')); - $this->assertTrue($result, 'module_uninstall() returns the correct value.'); - foreach (array('forum', 'ban', 'xmlrpc') as $module) { - $this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, format_string('The @module module was uninstalled.', array('@module' => $module))); - } - $uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array(); - $this->assertEqual($uninstalled_modules, array('forum', 'ban', 'xmlrpc'), 'Modules were uninstalled in the correct order by module_uninstall().'); + $result = $this->moduleHandler()->uninstall(array('config', 'help', 'color')); + $this->assertTrue($result, 'ModuleHandler::uninstall() returned TRUE.'); - // Uninstall the profile module from above, and make sure that the profile - // itself is not on the list of dependent modules to be uninstalled. - $result = module_uninstall(array('comment')); - $this->assertTrue($result, 'module_uninstall() returns the correct value.'); - $this->assertEqual(drupal_get_installed_schema_version('comment'), SCHEMA_UNINSTALLED, 'Comment module was uninstalled.'); + foreach (array('color', 'config', 'help') as $module) { + $this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, "$module module was uninstalled."); + } $uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array(); - $this->assertTrue(in_array('comment', $uninstalled_modules), 'Comment module is in the list of uninstalled modules.'); - $this->assertFalse(in_array($this->profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.'); + $this->assertEqual($uninstalled_modules, array('color', 'config', 'help'), 'Modules were uninstalled in the correct order.'); // Enable forum module again, which should enable both the ban module and // XML-RPC module. But, this time do it with ban module declaring a @@ -197,23 +202,55 @@ function testDependencyResolution() { // sure that Drupal\Core\Extension\ModuleHandler::install() still works. \Drupal::state()->set('module_test.dependency', 'version dependency'); drupal_static_reset('system_rebuild_module_data'); - $result = \Drupal::moduleHandler()->install(array('forum')); - $this->assertTrue($result, '\Drupal\Core\Extension\ModuleHandler::install() returns the correct value.'); + + $result = $this->moduleHandler()->install(array('color')); + $this->assertTrue($result, 'ModuleHandler::install() returns the correct value.'); + // Verify that the fake dependency chain was installed. - $this->assertTrue(\Drupal::moduleHandler()->moduleExists('ban') && \Drupal::moduleHandler()->moduleExists('xmlrpc'), 'Dependency chain was installed.'); + $this->assertTrue($this->moduleHandler()->moduleExists('config') && $this->moduleHandler()->moduleExists('help'), 'Dependency chain was installed.'); + // Verify that the original module was installed. - $this->assertTrue(\Drupal::moduleHandler()->moduleExists('forum'), 'Module installation with version dependencies succeeded.'); + $this->assertTrue($this->moduleHandler()->moduleExists('color'), 'Module installation with version dependencies succeeded.'); + // Finally, verify that the modules were enabled in the correct order. $enable_order = \Drupal::state()->get('module_test.install_order') ?: array(); - $xmlrpc_position = array_search('xmlrpc', $enable_order); - $ban_position = array_search('ban', $enable_order); - $forum_position = array_search('forum', $enable_order); + $xmlrpc_position = array_search('help', $enable_order); + $ban_position = array_search('config', $enable_order); + $forum_position = array_search('color', $enable_order); $xmlrpc_before_ban = $xmlrpc_position !== FALSE && $ban_position !== FALSE && $xmlrpc_position < $ban_position; $ban_before_forum = $ban_position !== FALSE && $forum_position !== FALSE && $ban_position < $forum_position; $this->assertTrue($xmlrpc_before_ban && $ban_before_forum, 'Modules were enabled in the correct order.'); } /** + * Tests uninstalling a module that is a "dependency" of a profile. + */ + function testUninstallProfileDependency() { + $profile = 'minimal'; + $dependency = 'dblog'; + $this->settingsSet('install_profile', $profile); + $this->enableModules(array('module_test', $profile)); + + drupal_static_reset('system_rebuild_module_data'); + $data = system_rebuild_module_data(); + $this->assertTrue(isset($data[$profile]->requires[$dependency])); + + $this->moduleHandler()->install(array($dependency)); + $this->assertTrue($this->moduleHandler()->moduleExists($dependency)); + + // Uninstall the profile module "dependency". + $result = $this->moduleHandler()->uninstall(array($dependency)); + $this->assertTrue($result, 'ModuleHandler::uninstall() returns TRUE.'); + $this->assertFalse($this->moduleHandler()->moduleExists($dependency)); + $this->assertEqual(drupal_get_installed_schema_version($dependency), SCHEMA_UNINSTALLED, "$dependency module was uninstalled."); + + // Verify that the installation profile itself was not uninstalled. + $uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array(); + $this->assertTrue(in_array($dependency, $uninstalled_modules), "$dependency module is in the list of uninstalled modules."); + $this->assertFalse(in_array($profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.'); + } + + /** * Tests whether the correct module metadata is returned. */ function testModuleMetaData() { @@ -240,4 +277,14 @@ function testThemeMetaData() { // Ensure the mtime field contains a number that is greater than zero. $this->assertTrue(is_numeric($test_mtime) && ($test_mtime > 0), 'The bartik.info.yml file modification time field contains a timestamp.'); } + + /** + * Returns the ModuleHandler. + * + * @return Drupal\Core\Extension\ModuleHandlerInterface + */ + protected function moduleHandler() { + return $this->container->get('module_handler'); + } + } diff --git a/core/modules/system/tests/modules/module_test/module_test.module b/core/modules/system/tests/modules/module_test/module_test.module index 0968c68..5081ff7 100644 --- a/core/modules/system/tests/modules/module_test/module_test.module +++ b/core/modules/system/tests/modules/module_test/module_test.module @@ -18,36 +18,36 @@ function module_test_permission() { */ function module_test_system_info_alter(&$info, Extension $file, $type) { if (\Drupal::state()->get('module_test.dependency') == 'missing dependency') { - if ($file->getName() == 'forum') { - // Make forum module depend on ban. - $info['dependencies'][] = 'ban'; + if ($file->getName() == 'color') { + // Make color module depend on config. + $info['dependencies'][] = 'config'; } - elseif ($file->getName() == 'ban') { - // Make ban depend on a made-up module. + elseif ($file->getName() == 'config') { + // Make config module depend on a non-existing module. $info['dependencies'][] = 'foo'; } } elseif (\Drupal::state()->get('module_test.dependency') == 'dependency') { - if ($file->getName() == 'forum') { - // Make the forum module depend on ban. - $info['dependencies'][] = 'ban'; + if ($file->getName() == 'color') { + // Make color module depend on config. + $info['dependencies'][] = 'config'; } - elseif ($file->getName() == 'ban') { - // Make ban depend on xmlrpc module. - $info['dependencies'][] = 'xmlrpc'; + elseif ($file->getName() == 'config') { + // Make config module depend on help module. + $info['dependencies'][] = 'help'; } } elseif (\Drupal::state()->get('module_test.dependency') == 'version dependency') { - if ($file->getName() == 'forum') { - // Make the forum module depend on ban. - $info['dependencies'][] = 'ban'; + if ($file->getName() == 'color') { + // Make color module depend on config. + $info['dependencies'][] = 'config'; } - elseif ($file->getName() == 'ban') { - // Make ban depend on a specific version of xmlrpc module. - $info['dependencies'][] = 'xmlrpc (1.x)'; + elseif ($file->getName() == 'config') { + // Make config module depend on a specific version of help module. + $info['dependencies'][] = 'help (1.x)'; } - elseif ($file->getName() == 'xmlrpc') { - // Set xmlrpc module to a version compatible with the above. + elseif ($file->getName() == 'help') { + // Set help module to a version compatible with the above. $info['version'] = '8.x-1.0'; } } diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index b377f15..7089239 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -64,7 +64,7 @@ exit; } -$test_list = array_fill(0, 1, 'Drupal\system\Tests\Module\ModuleApiTest'); +$test_list = array('Drupal\system\Tests\Module\ModuleApiTest', 'Drupal\system\Tests\Module\DependencyTest'); // Try to allocate unlimited time to run the tests. drupal_set_time_limit(0);