diff --git a/core/modules/system/src/Tests/Form/RadioTest.php b/core/modules/system/src/Tests/Form/RadioTest.php
new file mode 100644
index 0000000..6664fc3
--- /dev/null
+++ b/core/modules/system/src/Tests/Form/RadioTest.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Form\RadioTest.
+ */
+
+namespace Drupal\system\Tests\Form;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests form API radio handling of various combinations of #default_value
+ * and #return_value.
+ *
+ * @group Form
+ */
+class RadioTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('form_test');
+
+  function testFormRadio() {
+    // Ensure that the checked state is determined and rendered correctly for
+    // tricky combinations of default and return values.
+    foreach (array(FALSE, NULL, TRUE, 0, '0', '', 1, '1', 'foobar', '1foobar') as $default_value) {
+      // Only values that can be used for array indices are supported for
+      // #return_value, with the exception of integer 0, which is not supported.
+      // @see \Drupal\Core\Render\Element\Radio::processRadio().
+      foreach (array('0', '', 1, '1', 'foobar', '1foobar') as $return_value) {
+        $form_array = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestRadioTypeJugglingForm', $default_value, $return_value);
+        $form = \Drupal::service('renderer')->renderRoot($form_array);
+        if ($default_value === TRUE) {
+          $checked = TRUE;
+        }
+        elseif ($return_value === '0') {
+          $checked = ($default_value === '0');
+        }
+        elseif ($return_value === '') {
+          $checked = ($default_value === '');
+        }
+        elseif ($return_value === 1 || $return_value === '1') {
+          $checked = ($default_value === 1 || $default_value === '1');
+        }
+        elseif ($return_value === 'foobar') {
+          $checked = ($default_value === 'foobar');
+        }
+        elseif ($return_value === '1foobar') {
+          $checked = ($default_value === '1foobar');
+        }
+        $checked_in_html = strpos($form, 'checked') !== FALSE;
+        $message = format_string('#default_value is %default_value #return_value is %return_value.', array('%default_value' => var_export($default_value, TRUE), '%return_value' => var_export($return_value, TRUE)));
+        $this->assertIdentical($checked, $checked_in_html, $message);
+      }
+    }
+
+    // Ensure that $form_state->getValues() is populated correctly for a radios
+    // group that includes a 0-indexed array of options.
+    $results = json_decode($this->drupalPostForm('form-test/radios-zero', array(), 'Save'));
+    $this->assertIdentical($results->radio_off, NULL, 'All three in radio_off are zeroes: off.');
+    $this->assertIdentical($results->radio_zero_default, 0, 'The first choice is on in radio_zero_default');
+    $this->assertIdentical($results->radio_string_zero_default, '0', 'The first choice is on in radio_string_zero_default');
+    $edit = array('radio_off' => '0');
+    $results = json_decode($this->drupalPostForm('form-test/radios-zero', $edit, 'Save'));
+    $this->assertIdentical($results->radio_off, '0', 'The first choice is on in radio_off but the rest is not');
+
+    // Ensure that each radio is rendered correctly for a radios group
+    // that includes a 0-indexed array of options.
+    $this->drupalPostForm('form-test/radios-zero/0', array(), 'Save');
+    $radios = $this->xpath('//input[@type="radio"]');
+    foreach ($radios as $radio) {
+      $checked = isset($radio['checked']);
+      $name = (string) $radio['name'];
+      $this->assertIdentical($checked, $name == 'radio_zero_default' || $name == 'radio_string_zero_default', format_string('Radio %name correctly checked', array('%name' => $name)));
+    }
+    $edit = array('radio_off' => '0');
+    $this->drupalPostForm('form-test/radios-zero/0', $edit, 'Save');
+    $radios = $this->xpath('//input[@type="radio"]');
+    foreach ($radios as $radio) {
+      $checked = isset($radio['checked']);
+      $name = (string) $radio['name'];
+      $this->assertIdentical($checked, $name == 'radio_off' || $name == 'radio_zero_default' || $name == 'radio_string_zero_default', format_string('Radio %name correctly checked', array('%name' => $name)));
+    }
+  }
+}
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index 8a200a3..3f5ad2a 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -370,6 +370,15 @@ form_test.checkboxes_zero:
   requirements:
     _access: 'TRUE'
 
+form_test.radios_zero:
+  path: '/form-test/radios-zero/{json}'
+  defaults:
+    _form: '\Drupal\form_test\Form\FormTestRadiosZeroForm'
+    _title: 'FAPI test involving radios and zero'
+    json: TRUE
+  requirements:
+    _access: 'TRUE'
+
 form_test.required:
   path: '/form-test/required-attribute'
   defaults:
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRadioTypeJugglingForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRadioTypeJugglingForm.php
new file mode 100644
index 0000000..09d59e2
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRadioTypeJugglingForm.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\form_test\Form\FormTestradioTypeJugglingForm.
+ */
+
+namespace Drupal\form_test\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Builds a form to test return values for radios.
+ */
+class FormTestRadioTypeJugglingForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'form_test_radio_type_juggling';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $default_value = NULL, $return_value = NULL) {
+    $form['radio'] = array(
+      '#title' => t('Radio'),
+      '#type' => 'radio',
+      '#return_value' => $return_value,
+      '#default_value' => $default_value,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRadiosZeroForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRadiosZeroForm.php
new file mode 100644
index 0000000..2f8073c
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRadiosZeroForm.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\form_test\Form\FormTestRadiosZeroForm.
+ */
+
+namespace Drupal\form_test\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+/**
+ * Tests radios zero.
+ */
+class FormTestRadiosZeroForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'form_test_radios_zero';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $json = TRUE) {
+    $form_state->set('json', $json);
+    $form['radio_off'] = array(
+      '#title' => t('Radio off'),
+      '#type' => 'radios',
+      '#options' => array('foo', 'bar', 'baz'),
+    );
+    $form['radio_zero_default'] = array(
+      '#title' => t('Zero default'),
+      '#type' => 'radios',
+      '#options' => array('foo', 'bar', 'baz'),
+      '#default_value' => 0,
+    );
+    $form['radio_string_zero_default'] = array(
+      '#title' => t('Zero default (string)'),
+      '#type' => 'radios',
+      '#options' => array('foo', 'bar', 'baz'),
+      '#default_value' => '0',
+    );
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => 'Save',
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->has('json')) {
+      $form_state->setResponse(new JsonResponse($form_state->getValues()));
+    }
+    else {
+      $form_state->disableRedirect();
+    }
+  }
+
+}
