Index: captcha.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.admin.inc,v
retrieving revision 1.32
diff -u -b -u -p -r1.32 captcha.admin.inc
--- captcha.admin.inc	27 Oct 2009 16:11:17 -0000	1.32
+++ captcha.admin.inc	6 Jun 2010 23:30:50 -0000
@@ -173,7 +173,8 @@ function captcha_admin_settings() {
     '#title' => t('Persistence'),
     '#default_value' => variable_get('captcha_persistence', CAPTCHA_PERSISTENCE_SHOW_ALWAYS),
     '#options' => array(
-      CAPTCHA_PERSISTENCE_SHOW_ALWAYS => t('Always add a challenge.'),
+      CAPTCHA_PERSISTENCE_SHOW_ALWAYS => t('Always add a challenge (including every page of a multipage form).'),
+      CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE => t('Add a challenge to every fresh form, but for multipage workflows only one challenge is required.'),
       CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM => t('Omit challenges for a form once the user has successfully responded to a challenge for that form.'),
       CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL => t('Omit challenges for all forms once the user has successfully responded to a challenge.'),
     ),
Index: captcha.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.inc,v
retrieving revision 1.11.2.2
diff -u -b -u -p -r1.11.2.2 captcha.inc
--- captcha.inc	19 May 2010 06:14:31 -0000	1.11.2.2
+++ captcha.inc	6 Jun 2010 23:30:50 -0000
@@ -106,25 +106,38 @@ function _captcha_update_captcha_session
 
 /**
  * Helper function for checking if CAPTCHA is required for user,
- * based on CAPTCHA session ID and user session info.
+ * based on the CAPTCHA persistence setting, the CAPTCHA session ID and
+ * user session info.
  */
 function _captcha_required_for_user($captcha_sid, $form_id) {
+  // Get the CAPTCHA persistence setting.
+  $captcha_persistence = variable_get('captcha_persistence', CAPTCHA_PERSISTENCE_SHOW_ALWAYS);
+
+  // First check: should we always add a CAPTCHA?
+  if ($captcha_persistence == CAPTCHA_PERSISTENCE_SHOW_ALWAYS) {
+    return TRUE;
+  }
+
+  // Get the status of the current CAPTCHA session.
   $captcha_session_status = db_result(db_query("SELECT status FROM {captcha_sessions} WHERE csid = %d", $captcha_sid));
+  // Second check: if the current session is already solved: omit further CAPTCHAs.
+  if ($captcha_session_status === CAPTCHA_STATUS_SOLVED) {
+    return FALSE;
+  }
 
+  // Third check: look at the persistence level (per form instance, per form or per user).
+  if ($captcha_persistence == CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE) {
+    return TRUE;
+  }
+  else {
   $captcha_success_form_ids = isset($_SESSION['captcha_success_form_ids']) ? (array)($_SESSION['captcha_success_form_ids']) : array();
-
-  switch (variable_get('captcha_persistence', CAPTCHA_PERSISTENCE_SHOW_ALWAYS)) {
+    switch ($captcha_persistence) {
     case CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL:
-      $captcha_persistence_status = (count($captcha_success_form_ids) > 0);
-      break;
+        return (count($captcha_success_form_ids) == 0);
     case CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM:
-      $captcha_persistence_status = isset($captcha_success_form_ids[$form_id]);
-      break;
-    default:
-      $captcha_persistence_status = FALSE;
+        return !isset($captcha_success_form_ids[$form_id]);
+    }
   }
-
-  return ($captcha_session_status == CAPTCHA_STATUS_UNSOLVED) && !$captcha_persistence_status;
 }
 
 /**
Index: captcha.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.module,v
retrieving revision 1.103.2.6
diff -u -b -u -p -r1.103.2.6 captcha.module
--- captcha.module	1 Jun 2010 21:39:22 -0000	1.103.2.6
+++ captcha.module	6 Jun 2010 23:30:50 -0000
@@ -11,8 +11,13 @@
  */
 
 
-define('CAPTCHA_PERSISTENCE_SHOW_ALWAYS', 1);
+/// Always add a CAPTCHA (even on every page of a multipage workflow).
+define('CAPTCHA_PERSISTENCE_SHOW_ALWAYS', 0);
+/// Only one CAPTCHA has to be solved per form instance/multipage workflow
+define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE', 1);
+/// Once the user answered correctly for a CAPTCHA on a certain form, no more CAPTCHAs will be offered anymore for that form
 define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM', 2);
+/// Once the user answered correctly for a CAPTCHA, no more CAPTCHAs will be offered anymore
 define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL', 3);
 
 define('CAPTCHA_STATUS_UNSOLVED', 0);
Index: captcha.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.test,v
retrieving revision 1.14.2.2
diff -u -b -u -p -r1.14.2.2 captcha.test
--- captcha.test	1 Jun 2010 21:39:22 -0000	1.14.2.2
+++ captcha.test	6 Jun 2010 23:30:50 -0000
@@ -6,7 +6,6 @@
  * Tests for CAPTCHA module.
  */
 
-// TODO: write test to check persistence
 // TODO: write test for CAPTCHAs on admin pages
 // TODO: test for default challenge type
 // TODO: test about placement (comment form, node forms, log in form, etc)
@@ -460,6 +459,184 @@ class CapchaAdminTestCase extends Drupal
 
 
 
+class CapchaPersistenceTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => t('CAPTCHA persistence functionality'),
+      'description' => t('Testing of the CAPTCHA persistence functionality.'),
+      'group' => t('CAPTCHA'),
+    );
+  }
+
+  function setUp() {
+    // Load two modules: the captcha module itself and the comment module for testing anonymous comments.
+    parent::setUp('captcha', 'comment');
+    module_load_include('inc', 'captcha');
+
+    // Create a normal user.
+    $permissions = array(
+      'access comments', 'post comments', 'post comments without approval',
+    );
+    $this->normal_user = $this->drupalCreateUser($permissions);
+
+    // Create an admin user.
+    $permissions[] = 'administer CAPTCHA settings';
+    $permissions[] = 'skip CAPTCHA';
+    $this->admin_user = $this->drupalCreateUser($permissions);
+  }
+
+  /**
+   * Set up the persistence and CAPTCHA settings.
+   * @param int $persistence the persistence value.
+   */
+  private function setUpPersistence($persistence) {
+    // Log in as admin
+    $this->drupalLogin($this->admin_user);
+    // Set persistence.
+    $edit = array('captcha_persistence' => $persistence);
+    $this->drupalPost('admin/user/captcha', $edit, 'Save configuration');
+    // Log admin out.
+    $this->drupalLogout();
+
+    // Set the Test123 CAPTCHA on user register and comment form.
+    // We have to do this with the function captcha_set_form_id_setting()
+    // (because the CATCHA admin form does not show the Test123 option).
+    // We also have to do this after all usage of the CAPTCHA admin form
+    // (because posting the CAPTCHA admin form would set the CAPTCHA to 'none').
+    captcha_set_form_id_setting('user_login', 'captcha/Test');
+    captcha_set_form_id_setting('user_register', 'captcha/Test');
+    captcha_set_form_id_setting('comment_form', 'captcha/Test');
+  }
+
+  function testPersistenceAlways(){
+    // Set up of persistence and CAPTCHAs.
+    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SHOW_ALWAYS);
+
+    // Go to login form and check if there is a CAPTCHA on the login form (look for the title).
+    $this->drupalGet('user');
+    $captcha = captcha_captcha('generate', 'Test');
+    $captcha_text = $captcha['form']['captcha_response']['#title'];
+    $this->assertText($captcha_text, 'First view of form should have CAPTCHA.', 'CAPTCHA');
+
+    // Try to with wrong user name and password, but correct CAPTCHA.
+    $edit = array(
+      'name' => 'foobar',
+      'pass' => 'bazlaz',
+      'captcha_response' => 'Test 123',
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    // Check that there was no error message for the CAPTCHA.
+    $this->assertNoText(t('The answer you entered for the CAPTCHA was not correct.'),
+      'CAPTCHA answer should be correct', 'CAPTCHA');
+
+    // Name and password were wrong, we should get an updated form with a fresh CAPTCHA.
+    $this->assertText($captcha_text, 'Updated form (after correct CAPTCHA) should have another CAPTCHA.', 'CAPTCHA');
+  }
+
+  function testPersistencePerFormInstance(){
+    // Set up of persistence and CAPTCHAs.
+    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
+
+    // Go to login form and check if there is a CAPTCHA on the login form.
+    $this->drupalGet('user');
+    $captcha = captcha_captcha('generate', 'Test');
+    $captcha_text = $captcha['form']['captcha_response']['#title'];
+    $this->assertText($captcha_text, 'First view of new form session should have CAPTCHA.', 'CAPTCHA');
+
+    // Try to with wrong user name and password, but correct CAPTCHA.
+    $edit = array(
+      'name' => 'foobar',
+      'pass' => 'bazlaz',
+      'captcha_response' => 'Test 123',
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    // Check that there was no error message for the CAPTCHA.
+    $this->assertNoText(t('The answer you entered for the CAPTCHA was not correct.'),
+      'CAPTCHA answer should be correct', 'CAPTCHA');
+    // There shouldn't be a CAPTCHA on the new form.
+    $this->assertNoText($captcha_text, 'Updated form (after correct CAPTCHA) should have no CAPTCHA.', 'CAPTCHA');
+
+    // Start a new form instance/session
+    $this->drupalGet('node');
+    $this->drupalGet('user');
+    $this->assertText($captcha_text, 'A new session should have a CAPTCHA again.', 'CAPTCHA');
+
+    // Check another form
+    $this->drupalGet('user/register');
+    $this->assertText($captcha_text, 'Another form should still have a CAPTCHA.', 'CAPTCHA');
+  }
+
+  function testPersistencePerForm(){
+    // Set up of persistence and CAPTCHAs.
+    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM);
+
+    // Go to login form and check if there is a CAPTCHA on the login form.
+    $this->drupalGet('user');
+    $captcha = captcha_captcha('generate', 'Test');
+    $captcha_text = $captcha['form']['captcha_response']['#title'];
+    $this->assertText($captcha_text, 'First view of new form session should have CAPTCHA.', 'CAPTCHA');
+
+    // Try to with wrong user name and password, but correct CAPTCHA.
+    $edit = array(
+      'name' => 'foobar',
+      'pass' => 'bazlaz',
+      'captcha_response' => 'Test 123',
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    // Check that there was no error message for the CAPTCHA.
+    $this->assertNoText(t('The answer you entered for the CAPTCHA was not correct.'),
+      'CAPTCHA answer should be correct', 'CAPTCHA');
+    // There shouldn't be a CAPTCHA on the new form.
+    $this->assertNoText($captcha_text, 'Updated form (after correct CAPTCHA) should have no CAPTCHA.', 'CAPTCHA');
+
+    // Start a new form instance/session
+    $this->drupalGet('node');
+    $this->drupalGet('user');
+    $this->assertNoText($captcha_text, 'A new session should also have no CAPTCHA anymore.', 'CAPTCHA');
+
+    // Check another form
+    $this->drupalGet('user/register');
+    $this->assertText($captcha_text, 'Another form should still have a CAPTCHA.', 'CAPTCHA');
+  }
+
+  function testPersistenceOnlyOnce(){
+    // Set up of persistence and CAPTCHAs.
+    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL);
+
+    // Go to login form and check if there is a CAPTCHA on the login form.
+    $this->drupalGet('user');
+    $captcha = captcha_captcha('generate', 'Test');
+    $captcha_text = $captcha['form']['captcha_response']['#title'];
+    $this->assertText($captcha_text, 'First view of new form session should have CAPTCHA.', 'CAPTCHA');
+
+    // Try to with wrong user name and password, but correct CAPTCHA.
+    $edit = array(
+      'name' => 'foobar',
+      'pass' => 'bazlaz',
+      'captcha_response' => 'Test 123',
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    // Check that there was no error message for the CAPTCHA.
+    $this->assertNoText(t('The answer you entered for the CAPTCHA was not correct.'),
+      'CAPTCHA answer should be correct', 'CAPTCHA');
+    // There shouldn't be a CAPTCHA on the new form.
+    $this->assertNoText($captcha_text, 'Updated form (after correct CAPTCHA) should have no CAPTCHA.', 'CAPTCHA');
+
+    // Start a new form instance/session
+    $this->drupalGet('node');
+    $this->drupalGet('user');
+    $this->assertNoText($captcha_text, 'A new session should also have no CAPTCHA anymore.', 'CAPTCHA');
+
+    // Check another form
+    $this->drupalGet('user/register');
+    $this->assertNoText($captcha_text, 'Another form should also have no CAPTCHA.', 'CAPTCHA');
+  }
+
+
+
+}
+
 // Some tricks to debug:
 // drupal_debug($data) // from devel module
 // file_put_contents('tmp.simpletest.html', $this->drupalGetContent());
