diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index d45bd36..460fe82 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -172,14 +172,17 @@ function simpletest_run_tests($test_list) {
  * @param $unescaped_test_classnames
  *   An array of test class names, including full namespaces, to be passed as
  *   a regular expression to PHPUnit's --filter option.
+ * @param int $status
+ *   (optional) The exit status code of the PHPUnit process will be assigned to
+ *   this variable.
  *
  * @return array
  *   The parsed results of PHPUnit's JUnit XML output, in the format of
  *   {simpletest}'s schema.
  */
-function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames) {
+function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames, &$status = NULL) {
   $phpunit_file = simpletest_phpunit_xml_filepath($test_id);
-  simpletest_phpunit_run_command($unescaped_test_classnames, $phpunit_file);
+  simpletest_phpunit_run_command($unescaped_test_classnames, $phpunit_file, $status);
   return simpletest_phpunit_xml_to_rows($test_id, $phpunit_file);
 }

@@ -234,11 +237,14 @@ function simpletest_phpunit_configuration_filepath() {
  *   a regular expression to PHPUnit's --filter option.
  * @param string $phpunit_file
  *   A filepath to use for PHPUnit's --log-junit option.
+ * @param int $status
+ *   (optional) The exit status code of the PHPUnit process will be assigned to
+ *   this variable.
  *
  * @return string
  *  The results as returned by exec().
  */
-function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file) {
+function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file, &$status = NULL) {
   $phpunit_bin = simpletest_phpunit_command();

   $command = array(
@@ -272,7 +278,7 @@ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpun

   // exec in a subshell so that the environment is isolated when running tests
   // via the simpletest UI.
-  $ret = exec(join($command, " "));
+  $ret = exec(join($command, " "), $output, $status);
   chdir($old_cwd);
   return $ret;
 }
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index fedc6b6..89f9930 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -18,6 +18,10 @@
 const SIMPLETEST_SCRIPT_COLOR_EXCEPTION = 33; // Brown.
 const SIMPLETEST_SCRIPT_SQLITE_VARIABLE_LIMIT = 500;

+const SIMPLETEST_SCRIPT_EXIT_SUCCESS = 0;
+const SIMPLETEST_SCRIPT_EXIT_FAILURE = 1;
+const SIMPLETEST_SCRIPT_EXIT_EXCEPTION = 2;
+
 // Set defaults and get overrides.
 list($args, $count) = simpletest_script_parse_args();

@@ -80,7 +84,7 @@
 }

 // Execute tests.
-simpletest_script_execute_batch($tests_to_run);
+$status = simpletest_script_execute_batch($tests_to_run);

 // Stop the timer.
 simpletest_script_reporter_timer_stop();
@@ -98,7 +102,7 @@
 }

 // Test complete, exit.
-exit;
+exit($status);

 /**
  * Print help text.
@@ -505,6 +509,8 @@ function simpletest_script_setup_database($new = FALSE) {
 function simpletest_script_execute_batch($test_classes) {
   global $args, $test_ids;

+  $total_status = SIMPLETEST_SCRIPT_EXIT_SUCCESS;
+
   // Multi-process execution.
   $children = array();
   while (!empty($test_classes) || !empty($children)) {
@@ -521,7 +527,10 @@ function simpletest_script_execute_batch($test_classes) {
       // Process phpunit tests immediately since they are fast and we don't need
       // to fork for them.
       if (is_subclass_of($test_class, 'Drupal\Tests\UnitTestCase')) {
-        simpletest_script_run_phpunit($test_id, $test_class);
+        $phpunit_status = simpletest_script_run_phpunit($test_id, $test_class);
+        if ($phpunit_status > $total_status) {
+          $total_status = $phpunit_status;
+        }
         continue;
       }

@@ -552,7 +561,13 @@ function simpletest_script_execute_batch($test_classes) {
       if (empty($status['running'])) {
         // The child exited, unregister it.
         proc_close($child['process']);
-        if ($status['exitcode']) {
+        if ($status['exitcode'] == SIMPLETEST_SCRIPT_EXIT_FAILURE) {
+          if ($status['exitcode'] > $total_status) {
+            $total_status = $status['exitcode'];
+          }
+        }
+        elseif ($status['exitcode']) {
+          $total_status = $status['exitcode'];
           echo 'FATAL ' . $child['class'] . ': test runner returned a non-zero error code (' . $status['exitcode'] . ').' . "\n";
           if ($args['die-on-fail']) {
             list($db_prefix, ) = simpletest_last_test_get($child['test_id']);
@@ -573,13 +588,14 @@ function simpletest_script_execute_batch($test_classes) {
       }
     }
   }
+  return $total_status;
 }

 /**
  * Run a group of phpunit tests.
  */
 function simpletest_script_run_phpunit($test_id, $class) {
-  $results = simpletest_run_phpunit_tests($test_id, array($class));
+  $results = simpletest_run_phpunit_tests($test_id, array($class), $status);
   simpletest_process_phpunit_results($results);

   // Map phpunit results to a data structure we can pass to
@@ -614,6 +630,7 @@ function simpletest_script_run_phpunit($test_id, $class) {
   foreach ($summaries as $class => $summary) {
     simpletest_script_reporter_display_summary($class, $summary);
   }
+  return $status;
 }

 /**
@@ -631,13 +648,16 @@ function simpletest_script_run_one_test($test_id, $test_class) {
     simpletest_script_reporter_display_summary($test_class, $test->results);

     // Finished, kill this runner.
+    if ($test->results['#fail'] || $test->results['#exception']) {
+      exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
+    }
     exit(0);
   }
   // DrupalTestCase::run() catches exceptions already, so this is only reached
   // when an exception is thrown in the wrapping test runner environment.
   catch (Exception $e) {
     echo (string) $e;
-    exit(1);
+    exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
   }
 }

