diff --git a/core/includes/install.inc b/core/includes/install.inc
index 88ca1e2a..f4d78e8 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -990,11 +990,32 @@ function drupal_check_module($module) {
     // Print any error messages
     foreach ($requirements as $requirement) {
       if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
-        $message = $requirement['description'];
+        $message = [];
+
+        // If the requirement description is already a render array, add it to
+        // the message array.
+        if (is_array($requirement['description'])) {
+          $message['description'] = $requirement['description'];
+        }
+        // Otherwise, add a #markup element for the description string.
+        else {
+          $message['description'] = ['#markup' => $requirement['description']];
+        }
+        // Make sure the description is first, even if the description render
+        // array had an existing weight.
+        $message['description']['#weight'] = 0;
+
+        // If a required value was provided for the item, append it to the error
+        // message.
         if (isset($requirement['value']) && $requirement['value']) {
-          $message = t('@requirements_message (Currently using @item version @version)', array('@requirements_message' => $requirement['description'], '@item' => $requirement['title'], '@version' => $requirement['value']));
+          $message['version'] = [
+            '#prefix' => ' (',
+            '#markup' => t('Requirement: @title : @value', array('@title' => $requirement['title'], '@value' => $requirement['value'])),
+            '#suffix' => ')',
+            '#weight' => 1,
+          ];
         }
-        drupal_set_message($message, 'error');
+        drupal_set_message(\Drupal::service('renderer')->renderPlain($message), 'error');
       }
     }
     return FALSE;
diff --git a/core/modules/system/src/Tests/Module/DependencyTest.php b/core/modules/system/src/Tests/Module/DependencyTest.php
index 8b219b2..7fa17d1 100644
--- a/core/modules/system/src/Tests/Module/DependencyTest.php
+++ b/core/modules/system/src/Tests/Module/DependencyTest.php
@@ -99,6 +99,10 @@ function testEnableRequirementsFailureDependency() {
     $this->assertModules(array('requirements1_test'), FALSE);
     $this->assertModules(array('requirements2_test'), FALSE);
 
+    // Configure requirements1_test to fail installation.
+    \Drupal::state()->set('requirements1_test.phase', 'install');
+    \Drupal::state()->set('requirements1_test.severity', REQUIREMENT_ERROR);
+
     // Attempt to install both modules at the same time.
     $edit = array();
     $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test';
@@ -106,7 +110,7 @@ function testEnableRequirementsFailureDependency() {
     $this->drupalPostForm('admin/modules', $edit, t('Install'));
 
     // Makes sure the modules were NOT installed.
-    $this->assertText(t('Requirements 1 Test failed requirements'), 'Modules status has been updated.');
+    $this->assertText('Requirements 1 string');
     $this->assertModules(array('requirements1_test'), FALSE);
     $this->assertModules(array('requirements2_test'), FALSE);
 
diff --git a/core/modules/system/src/Tests/Module/HookRequirementsTest.php b/core/modules/system/src/Tests/Module/HookRequirementsTest.php
index ccbbff9..03fb4a6 100644
--- a/core/modules/system/src/Tests/Module/HookRequirementsTest.php
+++ b/core/modules/system/src/Tests/Module/HookRequirementsTest.php
@@ -3,25 +3,269 @@
 namespace Drupal\system\Tests\Module;
 
 /**
- * Attempts enabling a module that fails hook_requirements('install').
+ * Tests hook_requirements().
  *
  * @group Module
  */
 class HookRequirementsTest extends ModuleTestBase {
+
+  /**
+   * The requirement phase.
+   *
+   * @var string
+   */
+  protected $phase;
+
+  /**
+   * The requirement severity.
+   *
+   * @var int
+   */
+  protected $severity;
+
+  /**
+   * Whether to use a render array for the requirement description text.
+   *
+   * @var bool
+   */
+  protected $descriptionArray;
+
+  /**
+   * Whether to use a requirement value.
+   *
+   * @var bool
+   */
+  protected $useValue;
+
+  /**
+   * Tests the result hook_requirements() during install and runtime phases.
+   *
+   * @see requirements1_test_requirements()
+   */
+  function testHookRequirements() {
+    // Test installing the module with all possible combinations of values in
+    // the hook_requirements() hook.
+
+    // Test all allowed severities.
+    foreach ([REQUIREMENT_ERROR, REQUIREMENT_WARNING, REQUIREMENT_OK, REQUIREMENT_INFO] as $this->severity) {
+      // Test all allowed phases.
+      foreach (['install', 'update', 'runtime'] as $this->phase) {
+        // Test with a render array description and a string description.
+        foreach ([TRUE, FALSE] as $this->descriptionArray) {
+          // Test with and without a 'value' key.
+          foreach ([TRUE, FALSE] as $this->useValue) {
+            $this->doInstallTest();
+          }
+        }
+      }
+    }
+
+    // Configure the requirements hook to allow installation, and install the
+    // module.
+    \Drupal::state()->set('requirements1_test.severity', REQUIREMENT_INFO);
+    $this->container->get('module_installer')->install(['requirements1_test']);
+
+    // Test the status report with all possible combinations of values in the
+    // hook_requirements() hook.
+    // Test all allowed severities.
+    foreach ([REQUIREMENT_ERROR, REQUIREMENT_WARNING, REQUIREMENT_OK, REQUIREMENT_INFO] as $this->severity) {
+      // Test all allowed phases.
+      foreach (['install', 'update', 'runtime'] as $this->phase) {
+        // Test with a render array description and a string description.
+        foreach ([TRUE, FALSE] as $this->descriptionArray) {
+          // Test with and without a 'value' key.
+          foreach ([TRUE, FALSE] as $this->useValue) {
+            // Test the runtime messages.
+            $this->doRuntimeTest();
+          }
+        }
+      }
+    }
+  }
+
   /**
-   * Assert that a module cannot be installed if it fails hook_requirements().
+   * Tests hook_requirements() message version string is after the description.
+   *
+   * @see requirements1_test_requirements()
+   * @see hook_requirements()
    */
-  function testHookRequirementsFailure() {
-    $this->assertModules(array('requirements1_test'), FALSE);
+  public function testVersionRequirements() {
+    // Make sure the test module is not installed.
+    $this->assertModules(['requirements1_test'], FALSE);
 
-    // Attempt to install the requirements1_test module.
-    $edit = array();
+    // Configure the module's hook_requirements(). Testing with render array on
+    // and use value on.
+    \Drupal::state()->set('requirements1_test.phase', "install");
+    \Drupal::state()->set('requirements1_test.severity', REQUIREMENT_ERROR);
+    \Drupal::state()->set('requirements1_test.description_array', TRUE);
+    \Drupal::state()->set('requirements1_test.use_value', TRUE);
+
+    // Attempt to install the module.
+    $edit = [];
     $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test';
     $this->drupalPostForm('admin/modules', $edit, t('Install'));
 
-    // Makes sure the module was NOT installed.
-    $this->assertText(t('Requirements 1 Test failed requirements'), 'Modules status has been updated.');
-    $this->assertModules(array('requirements1_test'), FALSE);
+    // Verify the module did not install.
+    $this->assertModules(['requirements1_test'], FALSE);
+
+    // Check that the rendered description (from a render array) is followed by
+    // the value string.
+    parent::assertText('second item (Requirement');
+
+    // Re-configure the module's hook_requirements(). Testing with render array
+    // off (using a string) and use value on.
+    \Drupal::state()->set('requirements1_test.phase', "install");
+    \Drupal::state()->set('requirements1_test.severity', REQUIREMENT_ERROR);
+    \Drupal::state()->set('requirements1_test.description_array', FALSE);
+    \Drupal::state()->set('requirements1_test.use_value', TRUE);
+
+    // Attempt to install the module.
+    $this->drupalPostForm('admin/modules', $edit, t('Install'));
+
+    // Check that the rendered description (from a render array) is followed by
+    // the value string.
+    parent::assertText('markup!. (Requirement:');
+  }
+
+  /**
+   * Tests installing the module with the current hook_requirements() values.
+   *
+   * @see requirements1_test_requirements()
+   */
+  protected function doInstallTest() {
+    // Only install phase requirement errors should fail installation.
+    if (($this->phase == 'install') && ($this->severity == REQUIREMENT_ERROR)) {
+      $success = FALSE;
+    }
+    else {
+      $success = TRUE;
+    }
+
+    // Ensure that the module is not currently installed.
+    $this->assertModules(['requirements1_test'], FALSE);
+
+    // Configure the module's hook_requirements().
+    \Drupal::state()->set('requirements1_test.phase', $this->phase);
+    \Drupal::state()->set('requirements1_test.severity', $this->severity);
+    \Drupal::state()->set('requirements1_test.description_array', $this->descriptionArray);
+    \Drupal::state()->set('requirements1_test.use_value', $this->useValue);
+
+    // Attempt to install the module.
+    $edit = [];
+    $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test';
+    $this->drupalPostForm('admin/modules', $edit, t('Install'));
+
+    // Assert the expected success or failure.
+    $this->assertModules(['requirements1_test'], $success);
+
+    // If the installation was successful, no requirements message should be
+    // displayed. A message is only displayed during the install phase when
+    // installation fails. See the hook_requirements() documentation.
+    if ($success) {
+      $this->assertNoRequirementsMessages();
+
+      // Uninstall the module to prepare for the next test.
+      $this->container->get('module_installer')->uninstall(['requirements1_test']);
+    }
+
+    // Otherwise, if the installation was not successful, the requirements
+    // error should be displayed.
+    else {
+      $this->assertExpectedRequirementsMessages();
+    }
+  }
+
+  /**
+   * Tests the status report with the current hook_requirements() values.
+   *
+   * @see requirements1_test_requirements()
+   */
+  protected function doRuntimeTest() {
+    // Configure the module's hook_requirements().
+    \Drupal::state()->set('requirements1_test.phase', $this->phase);
+    \Drupal::state()->set('requirements1_test.severity', $this->severity);
+    \Drupal::state()->set('requirements1_test.description_array', $this->descriptionArray);
+    \Drupal::state()->set('requirements1_test.use_value', $this->useValue);
+
+    // Visit the reports page.
+    $this->drupalGet('admin/reports/status');
+
+    // If the runtime phase was used, we can expect messages for any severity.
+    if ($this->phase == 'runtime') {
+      $this->assertExpectedRequirementsMessages();
+    }
+    else {
+      // For other phase values, none of the messages should be displayed.
+      $this->assertNoRequirementsMessages();
+    }
+  }
+
+  /**
+   * Asserts the expected hook messages with the current seettings.
+   *
+   * @see requirements1_test_requirements()
+   */
+  protected function assertExpectedRequirementsMessages() {
+    // @todo Currently, the 'title' is only displayed during the install phase
+    //   if the 'value' is also defined. This may not be intentional. Fix in
+    //   https://www.drupal.org/node/2549803.
+    if (($this->phase == 'install') && !$this->useValue) {
+      $this->assertNoText('Requirements 1 title');
+    }
+    else {
+      // Normally, the requirement title should always be displayed.
+      $this->assertRaw('Requirements 1 title with <em>markup!</em>');
+    }
+
+    // If the requirements description was a render array, check that it was
+    // rendered correctly.
+    if ($this->descriptionArray) {
+      $this->assertRaw('Requirements 1 render array with <em>markup!</em>');
+      $this->assertText('Requirements 1 first item');
+      $this->assertText('Requirements 1 second item');
+    }
+    // Otherwise, test for the string message.
+    else {
+      $this->assertRaw('Requirements 1 string with <em>markup!</em>');
+    }
+
+    // The value should be displayed if it was set.
+    if ($this->useValue) {
+      $this->assertRaw('Requirements 1 value text with <em>markup!</em>');
+    }
+    else {
+      $this->assertNoText('Requirements 1 value text');
+    }
+  }
+
+  /**
+   * Asserts that none of the possible requirements messages are displayed.
+   *
+   * @see requirements1_test_requirements()
+   */
+  protected function assertNoRequirementsMessages() {
+    $this->assertNoText('Requirements 1 title');
+    $this->assertNoText('Requirements 1 string');
+    $this->assertNoText('Requirements 1 render array');
+    $this->assertNoText('Requirements 1 first item');
+    $this->assertNoText('Requirements 1 second item');
+    $this->assertNoText('Requirements 1 value text');
+  }
+
+  /**
+   * {@inheritoc}
+   */
+  protected function assertText($text, $message = '', $group = 'Other') {
+    $message = "Phase {$this->phase}, severity {$this->severity}, value {$this->useValue}: $text found.";
+    parent::assertText($text, $message, $group);
+  }
+
+  /**
+   * {@inheritoc}
+   */
+  protected function assertNoText($text, $message = '', $group = 'Other') {
+    $message = "Phase {$this->phase}, severity {$this->severity}, value {$this->useValue}: $text not found.";
+    parent::assertNoText($text, $message, $group);
   }
 
 }
diff --git a/core/modules/system/src/Tests/Module/InstallerRequirementsTest.php b/core/modules/system/src/Tests/Module/InstallerRequirementsTest.php
new file mode 100644
index 0000000..6decf95
--- /dev/null
+++ b/core/modules/system/src/Tests/Module/InstallerRequirementsTest.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\system\Tests\Module;
+
+use Drupal\Component\Serialization\Yaml;
+use Drupal\simpletest\InstallerTestBase;
+
+/**
+ * Tests the reporting of requirements in the installer script.
+ * Creates an installation profile to tackle the issue.
+ *
+ * @group Installer
+ */
+class InstallerRequirementsTest extends InstallerTestBase {
+
+  /**
+   * The distribution profile info.
+   *
+   * @var array
+   */
+  protected $info;
+
+  protected function setUp() {
+    $this->info = array(
+      'type' => 'profile',
+      'core' => \Drupal::CORE_COMPATIBILITY,
+      'name' => 'Requirements profile',
+      'distribution' => array(
+        'name' => 'Installer Requirements Test',
+        'install' => array(
+          'module' => 'requirements_installer_test',
+        ),
+      ),
+    );
+
+    $path = $this->siteDirectory . '/profiles/mydistro';
+    mkdir($path, 0777, TRUE);
+    file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info));
+
+    parent::setUp();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpProfile() {
+    // This step is skipped, because there is a distribution profile.
+  }
+
+  /**
+   * Confirms that the requirements message is printed.
+   */
+  public function testRequirementsMessage() {
+
+
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Module/ModuleTestBase.php b/core/modules/system/src/Tests/Module/ModuleTestBase.php
index e212617..8c5443d 100644
--- a/core/modules/system/src/Tests/Module/ModuleTestBase.php
+++ b/core/modules/system/src/Tests/Module/ModuleTestBase.php
@@ -25,7 +25,7 @@
   protected function setUp() {
     parent::setUp();
 
-    $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
+    $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer modules', 'administer site configuration'));
     $this->drupalLogin($this->adminUser);
   }
 
diff --git a/core/modules/system/tests/modules/requirements1_test/requirements1_test.install b/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
index c884893..80b5aac 100644
--- a/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
+++ b/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
@@ -7,18 +7,53 @@
 
 /**
  * Implements hook_requirements().
+ *
+ * Test modules may use four state variables to control this hook:
+ * - requirements1_test.phase: Whether to add the message for 'install',
+ *   'update', or 'runtime' phase.
+ * - requirements1_test.severity: The value for the 'severity' of the
+ *   requirement, e.g. REQUIREMENT_ERROR
+ * - requirements1_test.description_array: Boolean indicating whether to use a
+ *   render array for the description message (TRUE) or a plain string (FALSE).
+ * - requirements1_test.use_value: Whether to use the fantastic 'value' key,
+ *   which has different meanings in different contexts!
+ *
+ * @see hook_requirements()
+ * @see \Drupal\system\Tests\Module\HookRequirementsTest
  */
 function requirements1_test_requirements($phase) {
   $requirements = array();
 
-  // Always fails requirements.
-  if ('install' == $phase) {
-    $requirements['requirements1_test'] = array(
-      'title' => t('Requirements 1 Test'),
-      'severity' => REQUIREMENT_ERROR,
-      'description' => t('Requirements 1 Test failed requirements.'),
-    );
+  // Add the requirement only for the phase set in the test.
+  if ($phase != \Drupal::state()->get('requirements1_test.phase')) {
+    return $requirements;
   }
 
+  // Prepare the requirement message.
+  $requirement = [];
+  $requirement['title'] = t('Requirements 1 title with <em>markup!</em>');
+
+  // Use the severity level set in the test.
+  $requirement['severity'] = \Drupal::state()->get('requirements1_test.severity');
+
+  // Add a value to the requirement if indicated in the test.
+  if (\Drupal::state()->get('requirements1_test.use_value')) {
+    $requirement['value'] = t('Requirements 1 value text with <em>markup!</em>');
+  }
+
+  // Use a render array or a string for the description based on the value set
+  // in the test.
+  if (\Drupal::state()->get('requirements1_test.description_array')) {
+    $requirement['description'] = [
+      '#theme' => 'item_list',
+      '#title' => t('Requirements 1 render array with <em>markup!</em>'),
+      '#items' => ['Requirements 1 first item', 'Requirements 1 second item'],
+    ];
+  }
+  else {
+    $requirement['description'] = t('Requirements 1 string with <em>markup!</em>.');
+  }
+
+  $requirements['requirements1_test'] = $requirement;
   return $requirements;
 }
diff --git a/core/modules/system/tests/modules/requirements_installer_test/requirements_installer_test.info.yml b/core/modules/system/tests/modules/requirements_installer_test/requirements_installer_test.info.yml
new file mode 100644
index 0000000..557c95c
--- /dev/null
+++ b/core/modules/system/tests/modules/requirements_installer_test/requirements_installer_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Requirements Installer Profile Test'
+type: module
+description: 'Profile module to aid in testing that the a requirements message is properly displayed.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/system/tests/modules/requirements_installer_test/requirements_installer_test.install b/core/modules/system/tests/modules/requirements_installer_test/requirements_installer_test.install
new file mode 100644
index 0000000..289e26b
--- /dev/null
+++ b/core/modules/system/tests/modules/requirements_installer_test/requirements_installer_test.install
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Install hooks for test module.
+ */
+
+function requirements_installer_test_requirements($phase) {
+
+  switch ($phase) {
+    case "install":
+      $req = array(
+        'title' => t("Test Requirements"),
+        'value' => \Drupal::VERSION,
+        'severity' => REQUIREMENT_ERROR,
+      );
+  }
+
+  return array(
+    'r_installer_test' => $req,
+  );
+
+}
\ No newline at end of file
