diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
index 3d68561..627e4fe 100644
--- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
+++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
@@ -84,7 +84,7 @@ class ExtensionDiscovery {
   protected $profileDirectories;
 
   /**
-   * The app root.
+   * The app root for the current operation.
    *
    * @var string
    */
@@ -165,8 +165,9 @@ public function __construct($root, $use_file_cache = TRUE, $profile_directories
   public function scan($type, $include_tests = NULL) {
     // Determine the installation profile directories to scan for extensions,
     // unless explicit profile directories have been set. Exclude profiles as we
-    // cannot have profiles within profiles.
-    if (!isset($this->profileDirectories) && $type != 'profile') {
+    // cannot have profiles within profiles. If no profiles are specified in the
+    // constructor, profileDirectories will be NULL.
+    if (($this->profileDirectories === NULL) && $type != 'profile') {
       $this->setProfileDirectoriesFromSettings();
     }
 
@@ -214,12 +215,12 @@ public function scan($type, $include_tests = NULL) {
     $files = array();
     foreach ($searchdirs as $dir) {
       // Discover all extensions in the directory, unless we did already.
-      if (!isset(static::$files[$dir][$include_tests])) {
-        static::$files[$dir][$include_tests] = $this->scanDirectory($dir, $include_tests);
+      if (!isset(static::$files[$this->root][$dir][$include_tests])) {
+        static::$files[$this->root][$dir][$include_tests] = $this->scanDirectory($dir, $include_tests);
       }
       // Only return extensions of the requested type.
-      if (isset(static::$files[$dir][$include_tests][$type])) {
-        $files += static::$files[$dir][$include_tests][$type];
+      if (isset(static::$files[$this->root][$dir][$include_tests][$type])) {
+        $files += static::$files[$this->root][$dir][$include_tests][$type];
       }
     }
 
diff --git a/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php
new file mode 100644
index 0000000..bcc6dc8
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Extension\ExtensionDiscoveryTest.
+ */
+
+namespace Drupal\Tests\Core\Extension;
+
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Tests\UnitTestCase;
+use org\bovigo\vfs\vfsStream;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Extension\ExtensionDiscovery
+ * @group Extension
+ */
+class ExtensionDiscoveryTest extends UnitTestCase {
+
+  /**
+   * Provides a stub file system for a single module in sites/all.
+   *
+   * @return array
+   *   An array representing a virtual filesystem containing a module.
+   */
+  public function providerModule() {
+    $info_a = <<<'EOS'
+type: module
+name: Module A
+core: 8.x
+EOS;
+    return [
+      'sites' => [
+        'default' => [
+          'modules' => [
+            'module_a' => [
+              'module_a.info.yml' => $info_a
+            ],
+          ]
+        ]
+      ],
+    ];
+  }
+
+  /**
+   * Check that the cache is loaded and used.
+   *
+   * @covers ::scan
+   *
+   * @dataProvider providerModule
+   */
+  public function testScanCache($filesystem) {
+    vfsStream::setup('test_a');
+    vfsStream::create($filesystem);
+
+    $expected = ['test_scanned_directory'];
+
+    // Set up a mocked ExtensionDiscovery object so we can mock the
+    // scanDirectory() method.
+    $mock_extension_discovery = $this->getMockBuilder(ExtensionDiscovery::class)
+      ->setConstructorArgs([vfsStream::url('test_a'), TRUE, ['no/profile'], 'sites/default'])
+      ->setMethods(['scanDirectory'])
+      ->getMock();
+    // The scanDirectory() method gets called 4 times while traversing the file
+    // system once, one each for ORIGIN_CORE, ORIGIN_SITE_ALL, etc.
+    $mock_extension_discovery->expects($this->exactly(4))
+      ->method('scanDirectory')
+      ->willReturn($expected);
+    // Use up our 4 calls to scanDirectory().
+    $mock_extension_discovery->scan('module', FALSE);
+
+    // Subsequent scans should not call scanDirectory().
+    $mock_extension_discovery->expects($this->exactly(0))
+      ->method('scanDirectory')
+      ->willReturn($expected);
+    $mock_extension_discovery->scan('module', FALSE);
+  }
+
+  /**
+   * Check that the cache is invalidated when needed.
+   *
+   * @covers ::scan
+   *
+   * @dataProvider providerModule
+   */
+  public function testScanCacheInvalidate($filesystem) {
+    vfsStream::setup('root');
+
+    // Create the same filesystem in two different directories.
+    vfsStream::create($filesystem, vfsStream::newDirectory('test1'));
+    vfsStream::create($filesystem, vfsStream::newDirectory('test2'));
+
+    $expected = ['test_scanned_directory'];
+
+    // Set up a mocked ExtensionDiscovery object so we can mock the
+    // scanDirectory() method. We'll use the test1 directory first.
+    $test1_mock_extension_discovery = $this->getMockBuilder(ExtensionDiscovery::class)
+      ->setConstructorArgs([vfsStream::url('root/test1'), TRUE, ['no/profile'], 'sites/default'])
+      ->setMethods(['scanDirectory'])
+      ->getMock();
+    // The scanDirectory() method gets called 4 times while traversing the file
+    // system once, one each for ORIGIN_CORE, ORIGIN_SITE_ALL, etc.
+    $test1_mock_extension_discovery->expects($this->exactly(4))
+      ->method('scanDirectory')
+      ->willReturn($expected);
+
+    // Use up our 4 calls to scanDirectory().
+    $test1_mock_extension_discovery->scan('module', FALSE);
+
+    // Verify that when we mock ExtensionDiscovery again, the static is still
+    // present.
+    $test1_mock_extension_discovery = $this->getMockBuilder(ExtensionDiscovery::class)
+      ->setConstructorArgs([vfsStream::url('root/test1'), TRUE, ['no/profile'], 'sites/default'])
+      ->setMethods(['scanDirectory'])
+      ->getMock();
+    // scanDirectory() should be called 0 times if the cache is still there.
+    $test1_mock_extension_discovery->expects($this->exactly(0))
+      ->method('scanDirectory')
+      ->willReturn($expected);
+    $test1_mock_extension_discovery->scan('module', FALSE);
+
+    // Now use test2 in order to check that the cache was invalidated.
+    $test2_mock_extension_discovery = $this->getMockBuilder(ExtensionDiscovery::class)
+      ->setConstructorArgs([vfsStream::url('root/test2'), TRUE, ['no/profile'], 'sites/default'])
+      ->setMethods(['scanDirectory'])
+      ->getMock();
+    $test2_mock_extension_discovery->expects($this->exactly(4))
+      ->method('scanDirectory')
+      ->willReturn($expected);
+    $test2_mock_extension_discovery->scan('module', FALSE);
+  }
+
+}
