Index: mollom.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.css,v
retrieving revision 1.3
diff -u -p -r1.3 mollom.css
--- mollom.css	4 Aug 2010 03:58:46 -0000	1.3
+++ mollom.css	10 Sep 2010 19:43:38 -0000
@@ -3,3 +3,7 @@
 #edit-mollom-captcha {
   display: block;
 }
+
+#simpletest-result-form table td {
+  white-space: pre-wrap;
+}
Index: mollom.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.install,v
retrieving revision 1.24
diff -u -p -r1.24 mollom.install
--- mollom.install	10 Aug 2010 19:33:51 -0000	1.24
+++ mollom.install	10 Sep 2010 22:47:44 -0000
@@ -536,3 +536,5 @@ function mollom_update_7005() {
     'not null' => FALSE,
   ));
 }
+
+// @todo Update 'comment_form' to comment_node_TYPE_form.
Index: mollom.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.module,v
retrieving revision 1.71
diff -u -p -r1.71 mollom.module
--- mollom.module	11 Sep 2010 02:22:27 -0000	1.71
+++ mollom.module	11 Sep 2010 02:27:41 -0000
@@ -563,18 +563,21 @@ function mollom_form_alter(&$form, &$for
     return;
   }
 
-  // @todo Show this message on all protected forms, regardless of permissions.
+  // Remind of enabled testing mode on all protected forms.
   if (empty($_POST) && variable_get('mollom_testing_mode', 0)) {
-    $admin_message = '';
-    if (user_access('administer mollom') && $_GET['q'] != 'admin/config/content/mollom/settings') {
-      $admin_message = t('Visit the <a href="@settings-url">Mollom settings page</a> to disable it.', array(
-        '@settings-url' => url('admin/config/content/mollom/settings'),
+    $protected_forms = db_query("SELECT form_id, module FROM {mollom_form}")->fetchAllKeyed();
+    if (isset($protected_forms[$form_id])) {
+      $admin_message = '';
+      if (user_access('administer mollom') && $_GET['q'] != 'admin/config/content/mollom/settings') {
+        $admin_message = t('Visit the <a href="@settings-url">Mollom settings page</a> to disable it.', array(
+          '@settings-url' => url('admin/config/content/mollom/settings'),
+        ));
+      }
+      $message = t('Mollom testing mode is still enabled. !admin-message', array(
+        '!admin-message' => $admin_message,
       ));
+      drupal_set_message($message, 'warning');
     }
-    $message = t('Mollom testing mode is still enabled. !admin-message', array(
-      '!admin-message' => $admin_message,
-    ));
-    drupal_set_message($message, 'warning');
   }
 
   // Site administrators don't have their content checked with Mollom.
@@ -1244,7 +1247,7 @@ function mollom_process_mollom($element,
  * must fall back to a CAPTCHA.
  */
 function mollom_validate_analysis(&$form, &$form_state) {
-  if (!$form_state['mollom']['require_analysis'] || $form_state['mollom']['require_captcha']) {
+  if (!$form_state['mollom']['require_analysis']) {
     return;
   }
 
@@ -1259,7 +1262,6 @@ function mollom_validate_analysis(&$form
   $result = mollom('mollom.checkContent', $data);
 
   // Trigger global fallback behavior if there is no result.
-  // @todo Isn't mollom() invoking _mollom_fallback() already?
   if (!isset($result['session_id'])) {
     return _mollom_fallback();
   }
@@ -1309,6 +1311,7 @@ function mollom_validate_analysis(&$form
         }
         break;
 
+      case MOLLOM_ANALYSIS_UNKNOWN:
       default:
         // If we end up here, something went totally wrong.
         _mollom_fallback();
@@ -1321,19 +1324,15 @@ function mollom_validate_analysis(&$form
  * Form validation handler for CAPTCHA form element.
  */
 function mollom_validate_captcha(&$form, &$form_state) {
-  if (!$form_state['mollom']['require_captcha']) {
-    $form['mollom']['captcha']['#access'] = FALSE;
-    return;
-  }
-
-  // When re-validating a form that already passed a CAPTCHA in a previous
-  // request, we need to re-populate our global variable for mollom_data_save().
-  if ($form_state['mollom']['passed_captcha']) {
+  // Skip this validation, if we do not require a CAPTCHA or already passed one.
+  if (!$form_state['mollom']['require_captcha'] || $form_state['mollom']['passed_captcha']) {
     $form['mollom']['captcha']['#access'] = FALSE;
     return;
   }
 
   // Nothing to validate if there is no value.
+  // @todo The field is #required, so Form API should already handle this. Add a
+  //   test to be sure and remove this code.
   if (empty($form_state['values']['mollom']['captcha'])) {
     return;
   }
@@ -2114,3 +2113,10 @@ function contact_mollom_form_info($form_
 /**
  * @} End of "name mollom_contact".
  */
+
+/**
+ * Implements hook_form_FORMID_alter().
+ */
+function mollom_form_simpletest_result_form_alter(&$form) {
+  $form['#attached']['css'][] = drupal_get_path('module', 'mollom') . '/mollom.css';
+}
Index: tests/mollom.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom.test,v
retrieving revision 1.55
diff -u -p -r1.55 mollom.test
--- tests/mollom.test	11 Sep 2010 02:22:27 -0000	1.55
+++ tests/mollom.test	11 Sep 2010 02:27:41 -0000
@@ -811,6 +811,8 @@ class MollomInstallationTestCase extends
  * Tests low-level XML-RPC communication with Mollom servers.
  */
 class MollomResponseTestCase extends MollomWebTestCase {
+  protected $profile = 'testing';
+
   public static function getInfo() {
     return array(
       'name' => 'Server responses',
@@ -819,14 +821,22 @@ class MollomResponseTestCase extends Mol
     );
   }
 
+  function setUp() {
+    $this->disableDefaultSetup = TRUE;
+    parent::setUp();
+    $this->setKeys();
+    $this->assertValidKeys();
+  }
+
   /**
    * Tests mollom.checkContent().
    */
   function testCheckContent() {
+    $this->web_user = $this->drupalCreateUser(array());
     $data = array(
-      'author_name' => $this->admin_user->name,
-      'author_mail' => $this->admin_user->mail,
-      'author_id' => $this->admin_user->uid,
+      'author_name' => $this->web_user->name,
+      'author_mail' => $this->web_user->mail,
+      'author_id' => $this->web_user->uid,
       'author_ip' => ip_address(),
     );
 
@@ -872,7 +882,19 @@ class MollomResponseTestCase extends Mol
     $this->assertSame('profanity', $result['profanity'], 1);
     $session_id = $this->assertSessionID($result['session_id']);
 
+    // Change the string to contain profanity only.
+    $data['post_body'] = 'profanity';
+    $data['checks'] = 'spam,quality,profanity';
+    $data['session_id'] = $session_id;
+    $result = mollom('mollom.checkContent', $data);
+    $this->assertMollomWatchdogMessages();
+    $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_UNSURE);
+    $this->assertSame('quality', $result['quality'], 0);
+    $this->assertSame('profanity', $result['profanity'], 1);
+    $session_id = $this->assertSessionID($result['session_id']);
+
     // Disable spam checking, only do profanity checking.
+    $data['post_body'] = 'spam profanity';
     $data['checks'] = 'profanity';
     $data['session_id'] = $session_id;
     $result = mollom('mollom.checkContent', $data);
@@ -894,6 +916,46 @@ class MollomResponseTestCase extends Mol
   }
 
   /**
+   * Tests results of mollom.checkContent() across requests for a single session.
+   *
+   * @todo Not sure whether testing this sequence actually makes sense anymore.
+   */
+  function testCheckContentSession() {
+    $this->web_user = $this->drupalCreateUser(array());
+    $data = array(
+      'author_name' => $this->web_user->name,
+      'author_mail' => $this->web_user->mail,
+      'author_id' => $this->web_user->uid,
+      'author_ip' => ip_address(),
+    );
+
+    // Sequence: Post unsure spam, correct CAPTCHA, change post into spam,
+    // expect it to be ham (due to correct CAPTCHA).
+    $data['post_body'] = 'unsure';
+    $result = mollom('mollom.checkContent', $data);
+    $this->assertMollomWatchdogMessages();
+    $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_UNSURE);
+    $data['session_id'] = $this->assertSessionID($result['session_id']);
+
+    $captcha_data = array_intersect_key($data, array_flip(array('session_id', 'author_ip')));
+    $result = mollom('mollom.getImageCaptcha', $captcha_data);
+    $this->assertMollomWatchdogMessages();
+    $data['session_id'] = $this->assertSessionID($result['session_id']);
+
+    $captcha_data = array_intersect_key($data, array_flip(array('session_id', 'author_id', 'author_ip')));
+    $captcha_data['captcha_result'] = 'correct';
+    $result = mollom('mollom.checkCaptcha', $captcha_data);
+    $this->assertMollomWatchdogMessages();
+    $this->assertIdentical($result, TRUE, t('CAPTCHA response was correct.'));
+
+    $data['post_body'] = 'spam';
+    $result = mollom('mollom.checkContent', $data);
+    $this->assertMollomWatchdogMessages();
+    $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_HAM);
+    $data['session_id'] = $this->assertSessionID($result['session_id']);
+  }
+
+  /**
    * Tests mollom.getImageCaptcha().
    */
   function testGetImageCaptcha() {
@@ -1379,26 +1441,24 @@ class MollomBlacklistTestCase extends Mo
 class MollomProfanityTestCase extends MollomWebTestCase {
   public static function getInfo() {
     return array(
-      'name' => 'Profanity filtering',
-      'description' => 'Verify that forms can be properly protected and unprotected.',
+      'name' => 'Profanity checking',
+      'description' => 'Tests form protection with text analysis checking for profanity.',
       'group' => 'Mollom',
     );
   }
 
   function setUp() {
     parent::setUp('mollom_test');
-    // Re-route Mollom communication to this testing site.
-    variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version='));
 
-    $this->drupalLogin($this->admin_user);
+    user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access comments', 'post comments', 'post comments without approval'));
   }
 
   /**
-   * Test the different levels of profanity filtering. With our test Mollom
-   * server, the profanity keyword is 'Joomla'.
+   * Tests text analysis profanity checking.
    */
   function testProfanityFiltering() {
     // Protect Mollom test form but do not enable the profanity filter.
+    $this->drupalLogin($this->admin_user);
     $edit_config = array(
       'mollom[checks][profanity]' => FALSE,
     );
@@ -1437,6 +1497,68 @@ class MollomProfanityTestCase extends Mo
     $this->assertText('Successful form submission.');
     $this->assertNoText($this->profanity_message);
   }
+
+  function testExaminer() {
+    variable_set('comment_preview_article', DRUPAL_OPTIONAL);
+    $node = $this->drupalCreateNode(array('type' => 'article'));
+    $langcode = LANGUAGE_NONE;
+
+    // Enable spam and profanity checking for the article node comment form.
+    $this->drupalLogin($this->admin_user);
+    $edit_config = array(
+      'mollom[checks][profanity]' => TRUE,
+      'mollom[checks][spam]' => TRUE,
+    );
+    $this->setProtection('comment_node_article_form', MOLLOM_MODE_ANALYSIS, NULL, $edit_config);
+    $this->drupalLogout();
+
+    // Test plan 1: Post profanity (ham), remove profanity, and expect that to
+    // be accepted.
+    $edit = array(
+      'subject' => $this->randomName(),
+    );
+    $this->drupalGet("node/{$node->nid}");
+    $this->assertNoCaptchaField();
+    $this->assertPrivacyLink();
+
+    $edit["comment_body[$langcode][0][value]"] = 'profanity ham';
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertText($this->profanity_message);
+    $this->assertNoText(t('Your comment has been posted.'));
+    $session_id = $this->assertSessionIDInForm();
+
+    $edit["comment_body[$langcode][0][value]"] = 'not profane ham';
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertNoText($this->profanity_message);
+    $this->assertText(t('Your comment has been posted.'));
+    $this->assertRaw('<p>' . $edit["comment_body[$langcode][0][value]"] . '</p>', t('Comment previously containing profanity was found.'));
+    $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => $edit['subject']))->fetchField();
+    $this->assertMollomData('comment', $cid, $session_id);
+
+    // Test plan 2: Post unsure spam, post profanity along with correct CAPTCHA,
+    // and expect that to be rejected.
+    $this->web_user = $this->drupalCreateUser();
+    $this->drupalLogin($this->web_user);
+    $edit = array(
+      'subject' => $this->randomName(),
+    );
+    $this->drupalGet("node/{$node->nid}");
+    $this->assertNoCaptchaField();
+    $this->assertPrivacyLink();
+
+    $edit["comment_body[$langcode][0][value]"] = 'unsure';
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertCaptchaField();
+    $this->assertNoText($this->profanity_message);
+    $this->assertNoText(t('Your comment has been posted.'));
+    $session_id = $this->assertSessionIDInForm();
+
+    $edit["comment_body[$langcode][0][value]"] = 'unsure profanity';
+    $this->postCorrectCaptcha(NULL, $edit, t('Save'));
+    $this->assertNoCaptchaField();
+    $this->assertText($this->profanity_message);
+    $this->assertNoText(t('Your comment has been posted.'));
+  }
 }
 
 /**
