Index: modules/simpletest/tests/form_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v
retrieving revision 1.3
diff -u -r1.3 form_test.module
--- modules/simpletest/tests/form_test.module	28 Mar 2009 18:09:11 -0000	1.3
+++ modules/simpletest/tests/form_test.module	15 Apr 2009 14:32:42 -0000
@@ -58,6 +58,14 @@
     'type' => MENU_CALLBACK,
   );
 
+  $items['form_test/form_storage'] = array(
+    'title' => 'Form storage test',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_storage_test_form'),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+
   return $items;
 }
 
@@ -279,3 +287,76 @@
 function form_test_mock_form_submit($form, &$form_state) {
   variable_set('form_test_mock_submit', $form_state['values']['test_value']);
 }
+
+/**
+ * A multistep form for testing the form storage.
+ *
+ * @see form_storage_test_form_submit().
+ */
+function form_storage_test_form(&$form_state) {
+  // Initialize
+  if (!isset($form_state['storage'])) {
+    if (empty($form_state['input'])) {
+      $_SESSION['builds'] = 0;
+    }
+    // Put the initial thing into the storage
+    $form_state['storage'] = array(
+      'thing' => array(
+        'title' => 'none',
+        'value' => '',
+      ),
+    );
+    $form_state['storage'] += array('step' => 1);
+  }
+
+  // Count how often the form is built
+  $_SESSION['builds']++;
+
+  if ($form_state['storage']['step'] == 1) {
+    $form['title'] = array(
+      '#type' => 'textfield',
+      '#title' => 'title',
+      '#default_value' => $form_state['storage']['thing']['title'],
+      '#required' => TRUE,
+    );
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => 'value',
+      '#default_value' => $form_state['storage']['thing']['value'],
+    );
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => 'Continue',
+    );
+  }
+  else {
+    $form['step'] = array('#value' => 'This is the second step.');
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => 'Save',
+    );
+  }
+
+  if (isset($_REQUEST['cache'])) {
+    // Manually activate caching, so we can test that the storage keeps working when
+    // it's enabled.
+    $form['#cache'] = TRUE;
+  }
+
+  return $form;
+}
+
+/**
+ * Multistep form submit callback.
+ */
+function form_storage_test_form_submit($form, &$form_state) {
+  if ($form_state['storage']['step'] == 1) {
+    $form_state['storage']['thing']['title'] = $form_state['values']['title'];
+    $form_state['storage']['thing']['value'] = $form_state['values']['value'];
+  }
+  else {
+    drupal_set_message("Title: ". check_plain($form_state['storage']['thing']['title']));
+  }
+  $form_state['storage']['step']++;
+  drupal_set_message("Form builds: ". $_SESSION['builds']);
+}
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.7
diff -u -r1.7 form.test
--- modules/simpletest/tests/form.test	31 Mar 2009 01:49:53 -0000	1.7
+++ modules/simpletest/tests/form.test	15 Apr 2009 14:32:42 -0000
@@ -383,3 +383,63 @@
   }
 
 }
+
+/**
+ * Test the form storage.
+ */
+class FormsFormStorageTestCase extends DrupalWebTestCase {
+
+  function getInfo() {
+    return array(
+      'name'  => t('Multistep form using form storage'),
+      'description'  => t('Tests a multistep form using form storage and makes sure validation and caching works right.'),
+      'group' => t('Form API'),
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests using the form in a usual way.
+   */
+  function testForm() {
+    
+    $user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($user);
+
+    $this->drupalPost('form_test/form_storage', array('title' => 'new', 'value' => 'value_is_set'), 'Continue');
+    $this->assertText('Form builds: 2', t('The form has been built two times till now.'));
+
+    $this->drupalPost(NULL, array(), 'Save');
+    $this->assertText('Form builds: 3', t('The form has been built three times till now.'));
+    $this->assertText('Title: new', t('The form storage has stored the values.'));
+  }
+
+  /**
+   * Tests using the form with an activated #cache property.
+   */
+  function testFormCached() {
+    $user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($user);
+
+    $this->drupalPost('form_test/form_storage', array('title' => 'new', 'value' => 'value_is_set'), 'Continue', array('query' => 'cache=1'));
+    $this->assertText('Form builds: 1', t('The form has been built one time till now.'));
+    
+    $this->drupalPost(NULL, array(), 'Save');
+    $this->assertText('Form builds: 2', t('The form has been built two times till now.'));
+    $this->assertText('Title: new', t('The form storage has stored the values.'));
+  }
+
+  /**
+   * Tests validation when form storage is used.
+   */
+  function testValidation() {
+    $user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($user);
+
+    $this->drupalPost('form_test/form_storage', array('title' => '', 'value' => 'value_is_set'), 'Continue');
+    $this->assertPattern('/value_is_set/', t("The input values have been kept."));
+  }
+}
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.327
diff -u -r1.327 form.inc
--- includes/form.inc	11 Apr 2009 22:19:44 -0000	1.327
+++ includes/form.inc	15 Apr 2009 14:32:41 -0000
@@ -188,9 +188,8 @@
 
     if ($cacheable && !empty($form['#cache']) && empty($form['#no_cache'])) {
       // Caching is done past drupal_process_form so #process callbacks can
-      // set #cache. By not sending the form state, we avoid storing
-      // $form_state['storage'].
-      form_set_cache($form_build_id, $original_form, NULL);
+      // set #cache.
+      form_set_cache($form_build_id, $original_form, $form_state);
     }
   }
 
@@ -201,14 +200,14 @@
   // the form will simply be re-rendered with the values still in its
   // fields.
   //
-  // If $form_state['storage'] or $form_state['rebuild'] have been
-  // set by any submit or validate handlers, however, we know that
-  // we're in a complex multi-part process of some sort and the form's
-  // workflow is NOT complete. We need to construct a fresh copy of
-  // the form, passing in the latest $form_state in addition to any
-  // other variables passed into drupal_get_form().
+  // If $form_state['storage'] or $form_state['rebuild'] has been set
+  // and the form has been submitted, we know that we're in a complex
+  // multi-part process of some sort and the form's workflow is NOT
+  // complete. We need to construct a fresh copy of the form, passing
+  // in the latest $form_state in addition to any other variables passed
+  // into drupal_get_form().
 
-  if (!empty($form_state['rebuild']) || !empty($form_state['storage'])) {
+  if ((!empty($form_state['storage']) || !empty($form_state['rebuild'])) && !empty($form_state['submitted']) && !form_get_errors()) {
     $form = drupal_rebuild_form($form_id, $form_state);
   }
 

