Unverified Commit 257efe54 authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3218660 by alexpott: help_topics module can break during module uninstall

parent 2c57cbec
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -523,12 +523,12 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
      // into its statically cached list.
      \Drupal::service('extension.list.module')->reset();

      // Clear plugin manager caches.
      \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions();

      // Update the kernel to exclude the uninstalled modules.
      $this->updateKernel($module_filenames);

      // Clear plugin manager caches.
      \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions();

      // Update the theme registry to remove the newly uninstalled module.
      drupal_theme_rebuild();

+22 −0
Original line number Diff line number Diff line
@@ -263,6 +263,28 @@ public function testUninstall() {
    $this->assertSession()->statusCodeEquals(200);
  }

  /**
   * Tests uninstalling the search module.
   */
  public function testUninstallSearch() {
    // Ensure we can uninstall search and use the help system without
    // breaking.
    $this->drupalLogin($this->rootUser);
    $edit = [];
    $edit['uninstall[search]'] = TRUE;
    $this->drupalGet('admin/modules/uninstall');
    $this->submitForm($edit, 'Uninstall');
    $this->submitForm([], 'Uninstall');
    $this->assertSession()->pageTextContains('The selected modules have been uninstalled.');
    $this->drupalGet('admin/help');
    $this->assertSession()->statusCodeEquals(200);

    // Rebuild the container to reflect the latest changes.
    $this->rebuildContainer();
    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('help_topics'), 'The help_topics module is still installed.');
    $this->assertFalse(\Drupal::moduleHandler()->moduleExists('search'), 'The search module is uninstalled.');
  }

  /**
   * Asserts that help search returned the expected number of results.
   *
+7 −0
Original line number Diff line number Diff line
services:
  # Tests module uninstall
  plugin.manager.module_test.cache_clear_test:
    class: Drupal\module_test\PluginManagerCacheClearer
    arguments: ['@state', '@?logger.dblog']
    tags:
      - { name: plugin_manager_cache_clear }
+47 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\module_test;

use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\State\StateInterface;

/**
 * Helps test module uninstall.
 */
class PluginManagerCacheClearer extends DefaultPluginManager {

  /**
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * An optional service dependency.
   *
   * @var object|null
   */
  protected $optionalService;

  /**
   * PluginManagerCacheClearer constructor.
   *
   * @param \Drupal\Core\State\StateInterface $state
   *   The state service for recording what happens.
   * @param null $optional_service
   *   An optional service for testing.
   */
  public function __construct(StateInterface $state, $optional_service = NULL) {
    $this->state = $state;
    $this->optionalService = $optional_service;
  }

  /**
   * Tests call to CachedDiscoveryInterface::clearCachedDefinitions().
   *
   * @see \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::clearCachedDefinitions()
   */
  public function clearCachedDefinitions() {
    $this->state->set(self::class, isset($this->optionalService));
  }

}
+21 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
namespace Drupal\Tests\system\Kernel\Installer;

use Drupal\KernelTests\KernelTestBase;
use Drupal\module_test\PluginManagerCacheClearer;

/**
 * Tests the uninstallation of modules.
@@ -48,4 +49,24 @@ public function testUninstallMedia() {
    \Drupal::service('module_installer')->uninstall(['file']);
  }

  /**
   * Tests uninstalling a module with a plugin cache clearer service.
   */
  public function testUninstallPluginCacheClear() {
    \Drupal::service('module_installer')->install(['module_test']);
    $this->assertFalse($this->container->get('state')->get(PluginManagerCacheClearer::class));
    \Drupal::service('module_installer')->install(['dblog']);
    $this->assertTrue($this->container->get('state')->get(PluginManagerCacheClearer::class));

    // The plugin cache clearer service should be called during dblog uninstall
    // without the dependency.
    \Drupal::service('module_installer')->uninstall(['dblog']);
    $this->assertFalse($this->container->get('state')->get(PluginManagerCacheClearer::class));

    // The service should not be called during module_test uninstall.
    $this->container->get('state')->delete(PluginManagerCacheClearer::class);
    \Drupal::service('module_installer')->uninstall(['module_test']);
    $this->assertNull($this->container->get('state')->get(PluginManagerCacheClearer::class));
  }

}