diff --git a/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php b/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php
new file mode 100644
index 0000000..d9d11d7
--- /dev/null
+++ b/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Common\EarlyRenderingControllerTest.
+ */
+
+namespace Drupal\system\Tests\Common;
+
+use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Verifies that bubbleable metadata of early rendering is not lost.
+ *
+ * @group Common
+ */
+class EarlyRenderingControllerTest extends WebTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $dumpHeaders = TRUE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'early_rendering_controller_test'];
+
+  /**
+   * Tests theme preprocess functions being able to attach assets.
+   */
+  function testEarlyRendering() {
+    // Render array: non-early & early.
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array'));
+    $this->assertResponse(200, 'Hello world!');
+    $this->assertCacheTag('foo');
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array.early'));
+    $this->assertResponse(200, 'Hello world!');
+    $this->assertCacheTag('foo');
+
+    // Basic Response object: non-early & early.
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response'));
+    $this->assertResponse(200, 'Hello world!');
+    $this->assertNoCacheTag('foo');
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response.early'));
+    $this->assertResponse(200, 'Hello world!');
+    $this->assertNoCacheTag('foo');
+
+    // Response object with attachments: non-early & early.
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments'));
+    $this->assertResponse(200, 'Hello world!');
+    $this->assertNoCacheTag('foo');
+    $this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments.early'));
+    $this->assertResponse(500, 'Early rendering is not permitted for controllers returning anything else than render arrays. Response class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.');
+
+    // Cacheable Response object: non-early & early.
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response'));
+    $this->assertResponse(200, 'Hello world!');
+    $this->assertNoCacheTag('foo');
+    $this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
+    $this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response.early'));
+    $this->assertResponse(500, 'Early rendering is not permitted for controllers returning anything else than render arrays. Response class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.');
+
+    // The exceptions are expected. Do not interpret them as a test failure.
+    // Not using File API; a potential error must trigger a PHP warning.
+    unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
+  }
+
+}
diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.info.yml b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.info.yml
new file mode 100644
index 0000000..44ab452
--- /dev/null
+++ b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Early rendering controller test'
+type: module
+description: 'Support module for EarlyRenderingControllerTest.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.routing.yml b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.routing.yml
new file mode 100644
index 0000000..98d70c3
--- /dev/null
+++ b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.routing.yml
@@ -0,0 +1,55 @@
+# Controller returning a render array.
+early_rendering_controller_test.render_array:
+  path: '/early-rendering-controller-test/render-array'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::renderArray'
+  requirements:
+    _access: 'TRUE'
+early_rendering_controller_test.render_array.early:
+  path: '/early-rendering-controller-test/render-array/early'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::renderArrayEarly'
+  requirements:
+    _access: 'TRUE'
+
+# Controller returning a basic Response object.
+early_rendering_controller_test.response:
+  path: '/early-rendering-controller-test/response'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::response'
+  requirements:
+    _access: 'TRUE'
+early_rendering_controller_test.response.early:
+  path: '/early-rendering-controller-test/response/early'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::responseEarly'
+  requirements:
+    _access: 'TRUE'
+
+# Controller returning a Response object with attachments.
+early_rendering_controller_test.response-with-attachments:
+  path: '/early-rendering-controller-test/response-with-attachments'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::responseWithAttachments'
+  requirements:
+    _access: 'TRUE'
+early_rendering_controller_test.response-with-attachments.early:
+  path: '/early-rendering-controller-test/response-with-attachments/early'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::responseWithAttachmentsEarly'
+  requirements:
+    _access: 'TRUE'
+
+# Controller returning a cacheable Response object.
+early_rendering_controller_test.cacheable-response:
+  path: '/early-rendering-controller-test/cacheable-response'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::cacheableResponse'
+  requirements:
+    _access: 'TRUE'
+early_rendering_controller_test.cacheable-response.early:
+  path: '/early-rendering-controller-test/cacheable-response/early'
+  defaults:
+    _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::cacheableResponseEarly'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/src/AttachmentsTestResponse.php b/core/modules/system/tests/modules/early_rendering_controller_test/src/AttachmentsTestResponse.php
new file mode 100644
index 0000000..f8d040e
--- /dev/null
+++ b/core/modules/system/tests/modules/early_rendering_controller_test/src/AttachmentsTestResponse.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\early_rendering_controller_test\AttachmentsTestResponse.
+ */
+
+namespace Drupal\early_rendering_controller_test;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Drupal\Core\Render\AttachmentsInterface;
+use Drupal\Core\Render\AttachmentsTrait;
+use Symfony\Component\HttpFoundation\Response;
+
+class AttachmentsTestResponse extends Response implements AttachmentsInterface {
+
+  use AttachmentsTrait;
+
+}
diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/src/CacheableTestResponse.php b/core/modules/system/tests/modules/early_rendering_controller_test/src/CacheableTestResponse.php
new file mode 100644
index 0000000..06bf3cc
--- /dev/null
+++ b/core/modules/system/tests/modules/early_rendering_controller_test/src/CacheableTestResponse.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\early_rendering_controller_test\CacheableTestResponse.
+ */
+
+namespace Drupal\early_rendering_controller_test;
+
+use Drupal\Core\Cache\CacheableResponseInterface;
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpFoundation\Response;
+
+class CacheableTestResponse extends Response implements CacheableResponseInterface {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php b/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php
new file mode 100644
index 0000000..fb683c8
--- /dev/null
+++ b/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\early_rendering_controller_test\EarlyRenderingTestController.
+ */
+
+namespace Drupal\early_rendering_controller_test;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Controller routines for early_rendering_test routes.
+ */
+class EarlyRenderingTestController extends ControllerBase  {
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs a EarlyRenderingTestController.
+   *
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   */
+  public function __construct(RendererInterface $renderer) {
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('renderer')
+    );
+  }
+
+  protected function earlyRenderContent() {
+    return [
+      '#markup' => 'Hello world!',
+      '#cache' => [
+        'tags' => [
+          'foo',
+        ],
+      ],
+    ];
+  }
+
+  public function renderArray() {
+    return [
+      '#pre_render' => [function () {
+        $elements = $this->earlyRenderContent();
+        return $elements;
+      }],
+    ];
+  }
+
+  public function renderArrayEarly() {
+    $render_array = $this->earlyRenderContent();
+    return [
+      '#markup' => $this->renderer->render($render_array),
+    ];
+  }
+
+  public function response() {
+    return new Response('Hello world!');
+  }
+
+  public function responseEarly() {
+    return new Response($this->renderer->render($render_array));
+  }
+
+  public function responseWithAttachments() {
+    return new AttachmentsTestResponse('Hello world!');
+  }
+
+  public function responseWithAttachmentsEarly() {
+    $render_array = $this->earlyRenderContent();
+    return new AttachmentsTestResponse($this->renderer->render($render_array));
+  }
+
+  public function cacheableResponse() {
+    return new CacheableTestResponse('Hello world!');
+  }
+
+  public function cacheableResponseEarly() {
+    $render_array = $this->earlyRenderContent();
+    return new CacheableTestResponse($this->renderer->render($render_array));
+  }
+
+}
