diff --git a/core/includes/common.inc b/core/includes/common.inc
index b97cc5e..0a3c96a 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1926,18 +1926,7 @@ function drupal_html_id($id) {
   }
   $seen_ids = &drupal_static(__FUNCTION__, $seen_ids_init);
 
-  $id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
-
-  // As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
-  // only contain letters, digits ([0-9]), hyphens ("-"), underscores ("_"),
-  // colons (":"), and periods ("."). We strip out any character not in that
-  // list. Note that the CSS spec doesn't allow colons or periods in identifiers
-  // (http://www.w3.org/TR/CSS21/syndata.html#characters), so we strip those two
-  // characters as well.
-  $id = preg_replace('/[^A-Za-z0-9\-_]/', '', $id);
-
-  // Removing multiple consecutive hyphens.
-  $id = preg_replace('/\-+/', '-', $id);
+  $id = drupal_clean_id_identifier($id);
   // Ensure IDs are unique by appending a counter after the first occurrence.
   // The counter needs to be appended with a delimiter that does not exist in
   // the base ID. Requiring a unique delimiter helps ensure that we really do
@@ -1954,6 +1943,36 @@ function drupal_html_id($id) {
 }
 
 /**
+ * Prepares a string for use as a valid HTML ID.
+ *
+ * Only use this function when you want to intentionally skip the uniqueness
+ * guarantee of drupal_html_id().
+ *
+ * @param string $id
+ *   The ID to clean.
+ *
+ * @return string
+ *   The cleaned ID.
+ *
+ * @see drupal_html_id()
+ */
+function drupal_clean_id_identifier($id) {
+  $id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
+
+  // As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
+  // only contain letters, digits ([0-9]), hyphens ("-"), underscores ("_"),
+  // colons (":"), and periods ("."). We strip out any character not in that
+  // list. Note that the CSS spec doesn't allow colons or periods in identifiers
+  // (http://www.w3.org/TR/CSS21/syndata.html#characters), so we strip those two
+  // characters as well.
+  $id = preg_replace('/[^A-Za-z0-9\-_]/', '', $id);
+
+  // Removing multiple consecutive hyphens.
+  $id = preg_replace('/\-+/', '-', $id);
+  return $id;
+}
+
+/**
  * Adds a JavaScript file, setting, or inline code to the page.
  *
  * The behavior of this function depends on the parameters it is called with.
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 5d4a943..20831ab 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -517,12 +517,14 @@ function form_type_checkboxes_value($element, $input = FALSE) {
  * @param array|false $input
  *   The incoming input to populate the form element. If this is FALSE,
  *   the element's default value should be returned.
+ * @param array $form_state
+ *   The current state of the form.
  *
  * @return array
  *   The data that will appear in the $form_state['values'] collection
  *   for this element. Return nothing to use the default.
  */
-function form_type_table_value(array $element, $input = FALSE) {
+function form_type_table_value(array $element, $input = FALSE, array $form_state) {
   // If #multiple is FALSE, the regular default value of radio buttons is used.
   if (!empty($element['#tableselect']) && !empty($element['#multiple'])) {
     // Contrary to #type 'checkboxes', the default value of checkboxes in a
@@ -533,9 +535,15 @@ function form_type_table_value(array $element, $input = FALSE) {
       $element += array('#default_value' => array());
       return drupal_map_assoc(array_keys(array_filter($element['#default_value'])));
     }
-    else {
+    elseif ($element['#tree']) {
       return is_array($input) ? drupal_map_assoc($input) : array();
     }
+    else {
+      // If #tree is FALSE, then the selected value(s) are located at the
+      // top-level of the submitted form values.
+      $input = $form_state['input'];
+      return drupal_map_assoc(array_keys(array_filter($input)));
+    }
   }
 }
 
@@ -1697,6 +1705,13 @@ function form_process_table($element, &$form_state) {
     // tableselect element behaves as if it had been of #type checkboxes or
     // radios.
     foreach (element_children($element) as $key) {
+      // Since the #parents of the tableselect form element will equal the
+      // #parents of the row element, prevent FormBuilder from auto-generating
+      // an #id for the row element, since drupal_html_id() would automatically
+      // append a suffix to the tableselect form element's #id otherwise.
+      $element_parents = $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key);
+      $element[$key]['#id'] = drupal_html_id('edit-' . implode('-', $element_parents) . '-row');
+
       // Do not overwrite manually created children.
       if (!isset($element[$key]['select'])) {
         // Determine option label; either an assumed 'title' column, or the
@@ -1734,7 +1749,6 @@ function form_process_table($element, &$form_state) {
           '#return_value' => $key,
           '#attributes' => $element['#attributes'],
         );
-        $element_parents = array_merge($element['#parents'], array($key));
         if ($element['#multiple']) {
           $element[$key]['select']['#default_value'] = isset($value[$key]) ? $key : NULL;
           $element[$key]['select']['#parents'] = $element_parents;
diff --git a/core/modules/simpletest/css/simpletest.module.css b/core/modules/simpletest/css/simpletest.module.css
index 86bd04b..28cfb0a 100644
--- a/core/modules/simpletest/css/simpletest.module.css
+++ b/core/modules/simpletest/css/simpletest.module.css
@@ -3,7 +3,7 @@
 #simpletest-form-table th.select-all {
   width: 1em;
 }
-th.simpletest_test {
+th.simpletest-test-label {
   width: 16em;
 }
 
@@ -19,9 +19,6 @@ th.simpletest_test {
 .simpletest-test-label label {
   margin-left: 1em; /* LTR */
 }
-.simpletest-test-description .description {
-  margin: 0;
-}
 #simpletest-form-table tr td {
   background-color: white;
   color: #494949;
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestTestForm.php b/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestTestForm.php
index c469b52..8e69c79 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestTestForm.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestTestForm.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\simpletest\Form;
 
+use Drupal\Component\Utility\SortArray;
+use Drupal\Component\Utility\String;
 use Drupal\Core\Form\FormBase;
 
 /**
@@ -52,7 +54,46 @@ public function buildForm(array $form, array &$form_state) {
     );
 
     $form['tests']['table'] = array(
-      '#theme' => 'simpletest_test_table',
+      '#type' => 'table',
+      '#id' => 'simpletest-form-table',
+      '#tableselect' => TRUE,
+      '#tree' => FALSE,
+      '#header' => array(
+        array('data' => $this->t('Test'), 'class' => array('simpletest-test-label')),
+        array('data' => $this->t('Description'), 'class' => array('simpletest-test-description')),
+      ),
+      '#empty' => $this->t('No tests to display.'),
+      '#attached' => array(
+        'library' => array(
+          array('simpletest', 'drupal.simpletest'),
+        ),
+      ),
+    );
+
+    // Define the images used to expand/collapse the test groups.
+    $image_collapsed = array(
+      '#theme' => 'image',
+      '#uri' => 'core/misc/menu-collapsed.png',
+      '#width' => '7',
+      '#height' => '7',
+      '#alt' => $this->t('Expand'),
+      '#title' => $this->t('Expand'),
+      '#suffix' => '<a href="#" class="simpletest-collapse">(' . $this->t('Expand') . ')</a>',
+    );
+    $image_extended = array(
+      '#theme' => 'image',
+      '#uri' => 'core/misc/menu-expanded.png',
+      '#width' => '7',
+      '#height' => '7',
+      '#alt' => $this->t('Collapse'),
+      '#title' => $this->t('Collapse'),
+      '#suffix' => '<a href="#" class="simpletest-collapse">(' . $this->t('Collapse') . ')</a>',
+    );
+    $js = array(
+      'images' => array(
+        drupal_render($image_collapsed),
+        drupal_render($image_extended),
+      ),
     );
 
     // Generate the list of tests arranged by group.
@@ -62,22 +103,92 @@ public function buildForm(array $form, array &$form_state) {
 
     foreach ($groups as $group => $tests) {
       $form['tests']['table'][$group] = array(
-        '#collapsed' => TRUE,
+        '#attributes' => array('class' => array('simpletest-group')),
       );
 
+      // Make the class name safe for output on the page by replacing all
+      // non-word/decimal characters with a dash (-).
+      $group_class = 'module-' . strtolower(trim(preg_replace("/[^\w\d]/", "-", $group)));
+
+      // Override tableselect column with custom selector for this group.
+      $form['tests']['table'][$group]['select'] = array(
+        '#wrapper_attributes' => array(
+          'id' => $group_class,
+          'class' => array('simpletest-select-all'),
+        ),
+      );
+
+      // Expand/collapse image and group title.
+      $form['tests']['table'][$group]['title'] = array(
+        '#prefix' => '<div class="simpletest-image" id="simpletest-test-group-' . $group_class . '"></div>',
+        '#markup' => '<label for="' . $group_class . '-select-all" class="simpletest-group-label">' . $group . '</label>',
+        '#wrapper_attributes' => array(
+          'class' => array('simpletest-group-label'),
+        ),
+      );
+      $form['tests']['table'][$group]['description'] = array(
+        '#markup' => '&nbsp;',
+        '#wrapper_attributes' => array(
+          'class' => array('simpletest-group-description'),
+        ),
+      );
+
+      // Add individual tests to group.
+      $current_js = array(
+        'testClass' => $group_class . '-test',
+        'testNames' => array(),
+        // imageDirection maps to the 'images' index in the $js array.
+        'imageDirection' => 0,
+        'clickActive' => FALSE,
+      );
+
+      // Sort test classes within group alphabetically by name/label.
+      uasort($tests, function ($a, $b) {
+        return SortArray::sortByKeyString($a, $b, 'name');
+      });
+
+      // Cycle through each test within the current group.
       foreach ($tests as $class => $info) {
-        $form['tests']['table'][$group][$class] = array(
-          '#type' => 'checkbox',
-          '#title' => $info['name'],
-          '#description' => $info['description'],
+        $test_id = drupal_clean_id_identifier($class);
+        $test_checkbox_id = 'edit-' . $test_id;
+        $current_js['testNames'][] = $test_checkbox_id;
+
+        $form['tests']['table'][$class] = array(
+          '#attributes' => array('class' => array($group_class . '-test', 'js-hide')),
+        );
+        $form['tests']['table'][$class]['title'] = array(
+          '#prefix' => '<label for="' . $test_checkbox_id . '">',
+          '#markup' => $info['name'],
+          '#suffix' => '</label>',
+          '#wrapper_attributes' => array(
+            'class' => array('simpletest-test-label', 'table-filter-text-source'),
+          ),
+        );
+        $form['tests']['table'][$class]['description'] = array(
+          '#markup' => String::format('@description (@class)', array(
+            '@description' => $info['description'],
+            '@class' => $class,
+          )),
+          '#wrapper_attributes' => array(
+            'class' => array('simpletest-test-description', 'table-filter-text-source'),
+          ),
         );
       }
+
+      $js['simpletest-test-group-' . $group_class] = $current_js;
     }
 
+    // Add JavaScript array of settings.
+    $form['tests']['table']['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('simpleTest' => $js),
+    );
+
     // Action buttons.
     $form['tests']['op'] = array(
       '#type' => 'submit',
       '#value' => $this->t('Run tests'),
+      '#tableselect' => TRUE,
     );
     $form['clean'] = array(
       '#type' => 'fieldset',
@@ -97,21 +208,20 @@ public function buildForm(array $form, array &$form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, array &$form_state) {
-    // Get list of tests.
-    $tests_list = array();
     simpletest_classloader_register();
 
     $phpunit_all = array_keys($form_state['storage']['PHPUnit']);
 
+    $tests_list = array();
     foreach ($form_state['values'] as $class_name => $value) {
       // Since class_exists() will likely trigger an autoload lookup,
       // we do the fast check first.
-      if ($value === 1 && class_exists($class_name)) {
+      if ($value === $class_name && class_exists($class_name)) {
         $test_type = in_array($class_name, $phpunit_all) ? 'UnitTest' : 'WebTest';
         $tests_list[$test_type][] = $class_name;
       }
     }
-    if (count($tests_list) > 0 ) {
+    if (!empty($tests_list)) {
       $test_id = simpletest_run_tests($tests_list, 'drupal');
       $form_state['redirect_route'] = array(
         'route_name' => 'simpletest.result_form',
@@ -120,9 +230,6 @@ public function submitForm(array &$form, array &$form_state) {
         ),
       );
     }
-    else {
-      drupal_set_message($this->t('No test(s) selected.'), 'error');
-    }
   }
 
 }
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index c6b53bf..a3d722a 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -79,10 +79,6 @@ function simpletest_permission() {
  */
 function simpletest_theme() {
   return array(
-    'simpletest_test_table' => array(
-      'render element' => 'table',
-      'file' => 'simpletest.theme.inc',
-    ),
     'simpletest_result_summary' => array(
       'render element' => 'form',
       'file' => 'simpletest.theme.inc',
diff --git a/core/modules/simpletest/simpletest.theme.inc b/core/modules/simpletest/simpletest.theme.inc
index 1743b74..9890c62 100644
--- a/core/modules/simpletest/simpletest.theme.inc
+++ b/core/modules/simpletest/simpletest.theme.inc
@@ -6,154 +6,6 @@
  */
 
 /**
- * Returns an HTML table for a test list generated by simpletest_test_form().
- *
- * @param $variables
- *   An associative array containing:
- *   - table: A render element representing the table.
- *
- * @ingroup themeable
- */
-function theme_simpletest_test_table($variables) {
-  $table = $variables['table'];
-
-  drupal_add_library('simpletest', 'drupal.simpletest');
-
-  // Create header for test selection table.
-  $header = array(
-    array('class' => array('select-all')),
-    array('data' => t('Test'), 'class' => array('simpletest_test')),
-    array('data' => t('Description'), 'class' => array('simpletest_description')),
-  );
-
-  // Define the images used to expand/collapse the test groups.
-  $image_collapsed = array(
-    '#theme' => 'image',
-    '#uri' => 'core/misc/menu-collapsed.png',
-    '#width' => '7',
-    '#height' => '7',
-    '#alt' => t('Expand'),
-    '#title' => t('Expand'),
-    '#suffix' => '<a href="#" class="simpletest-collapse">(' . t('Expand') . ')</a>',
-  );
-  $image_extended = array(
-    '#theme' => 'image',
-    '#uri' => 'core/misc/menu-expanded.png',
-    '#width' => '7',
-    '#height' => '7',
-    '#alt' => t('Collapse'),
-    '#title' => t('Collapse'),
-    '#suffix' => ' <a href="#" class="simpletest-collapse">(' . t('Collapse') . ')</a>',
-   );
-  $js = array(
-    'images' => array(
-      drupal_render($image_collapsed),
-      drupal_render($image_extended),
-    ),
-  );
-
-  // Cycle through each test group and create a row.
-  $rows = array();
-  foreach (element_children($table) as $key) {
-    $element = &$table[$key];
-    $row = array();
-
-    // Make the class name safe for output on the page by replacing all
-    // non-word/decimal characters with a dash (-).
-    $test_class = 'module-' . strtolower(trim(preg_replace("/[^\w\d]/", "-", $key)));
-
-    // Select the right "expand"/"collapse" image, depending on whether the
-    // category is expanded (at least one test selected) or not.
-    $collapsed = !empty($element['#collapsed']);
-    $image_index = $collapsed ? 0 : 1;
-
-    // Place-holder for checkboxes to select group of tests.
-    $row[] = array('id' => $test_class, 'class' => array('simpletest-select-all'));
-
-    // Expand/collapse image and group title.
-    $row[] = array(
-      'data' => '<div class="simpletest-image" id="simpletest-test-group-' . $test_class . '"></div>' .
-        '<label for="' . $test_class . '-select-all" class="simpletest-group-label">' . $key . '</label>',
-      'class' => array('simpletest-group-label'),
-    );
-
-    $row[] = array(
-      'data' => '&nbsp;',
-      'class' => array('simpletest-group-description'),
-    );
-
-    $rows[] = array('data' => $row, 'class' => array('simpletest-group'));
-
-    // Add individual tests to group.
-    $current_js = array(
-      'testClass' => $test_class . '-test',
-      'testNames' => array(),
-      'imageDirection' => $image_index,
-      'clickActive' => FALSE,
-    );
-
-    // Sorting $element by children's #title attribute instead of by class name.
-    uasort($element, 'element_sort_by_title');
-
-    // Cycle through each test within the current group.
-    foreach (element_children($element) as $test_name) {
-      $test = $element[$test_name];
-      $row = array();
-
-      $current_js['testNames'][] = $test['#id'];
-
-      // Store test title and description so that checkbox won't render them.
-      $title = $test['#title'];
-      $description = $test['#description'];
-
-      $test['#title_display'] = 'invisible';
-      unset($test['#description']);
-
-      // Test name is used to determine what tests to run.
-      $test['#name'] = $test_name;
-
-      $row[] = array(
-        'data' => drupal_render($test),
-        'class' => array('simpletest-test-select'),
-      );
-      $row[] = array(
-        'data' => '<label for="' . $test['#id'] . '">' . $title . '</label>',
-        'class' => array('simpletest-test-label', 'table-filter-text-source'),
-      );
-      $row[] = array(
-        'data' => '<div class="description">' . format_string('@description (@class)', array('@description' => $description, '@class' => $test_name)) . '</div>',
-        'class' => array('simpletest-test-description', 'table-filter-text-source'),
-      );
-
-      $rows[] = array('data' => $row, 'class' => array($test_class . '-test', ($collapsed ? 'js-hide' : '')));
-    }
-    $js['simpletest-test-group-' . $test_class] = $current_js;
-    unset($table[$key]);
-  }
-
-  // Add js array of settings.
-  $attached = array();
-  $attached['#attached']['js'][] = array(
-    'data' => array('simpleTest' => $js),
-    'type' => 'setting',
-  );
-  drupal_render($attached);
-
-  if (empty($rows)) {
-    return '<strong>' . t('No tests to display.') . '</strong>';
-  }
-  else {
-    $simpletest_form_table = array(
-      '#theme' => 'table',
-      '#header' => $header,
-      '#rows' => $rows,
-      '#attributes' => array('id' => 'simpletest-form-table'),
-    );
-    return drupal_render($simpletest_form_table);
-  }
-}
-
-/**
  * Returns HTML for the summary status of a simpletest result.
  *
  * @param $variables
