Index: modules/simpletest/simpletest.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.test,v
retrieving revision 1.21
diff -u -r1.21 simpletest.test
--- modules/simpletest/simpletest.test	25 May 2009 05:20:16 -0000	1.21
+++ modules/simpletest/simpletest.test	25 May 2009 14:25:32 -0000
@@ -263,3 +263,79 @@
     return preg_match("/^simpletest\d+/", $_SERVER['HTTP_USER_AGENT']);
   }
 }
+
+class SimpleTestMailCaptureTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('SimpleTest e-mail capturing'),
+      'description' => t('Test the SimpleTest e-mail capturing logic, the assertMail assertion and the drupalGetMails function.'),
+      'group' => t('SimpleTest'),
+    );
+  }
+
+  /**
+   * Test to see if the wrapper function is executed correctly.
+   */
+  function testMailSend() {
+    // Create an e-mail.
+    $subject = $this->randomString(64);
+    $body = $this->randomString(128);
+    $message = array(
+      'id' => 'drupal_mail_test',
+      'headers' => array('Content-type'=> 'text/html'),
+      'subject' => $subject,
+      'to' => 'foobar@example.com',
+      'body' => $body,
+    );
+
+    // Before we send the e-mail, drupalGetMails should return an empty array.
+    $captured_emails = $this->drupalGetMails();
+    $this->assertEqual(count($captured_emails), 0, t('The captured e-mails queue is empty.'), t('E-mail'));
+
+    // Send the e-mail.
+    $response = drupal_mail_send($message);
+
+    // Ensure that there is one e-mail in the captured e-mails array.
+    $captured_emails = $this->drupalGetMails();
+    $this->assertEqual(count($captured_emails), 1, t('One e-mail was captured.'), t('E-mail'));
+
+    // Assert that the e-mail was sent by iterating over the message properties
+    // and ensuring that they are captured intact.
+    foreach($message as $field => $value) {
+      $this->assertMail($field, $value, t('The e-mail was sent and the value for property @field is intact.', array('@field' => $field)), t('E-mail'));
+    }
+
+    // Send additional e-mails so more than one e-mail is captured.
+    for ($index = 0; $index < 5; $index++) {
+      $message = array(
+        'id' => 'drupal_mail_test_' . $index,
+        'headers' => array('Content-type'=> 'text/html'),
+        'subject' => $this->randomString(64),
+        'to' => $this->randomName(32) . '@example.com',
+        'body' => $this->randomString(512),
+      );
+      drupal_mail_send($message);
+    }
+
+    // There should now be 6 e-mails captured.
+    $captured_emails = $this->drupalGetMails();
+    $this->assertEqual(count($captured_emails), 6, t('All e-mails were captured.'), t('E-mail'));
+
+    // Test different ways of getting filtered e-mails via drupalGetMails().
+    $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test'));
+    $this->assertEqual(count($captured_emails), 1, t('Only one e-mail is returned when filtering by id.'), t('E-mail'));
+    $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test', 'subject' => $subject));
+    $this->assertEqual(count($captured_emails), 1, t('Only one e-mail is returned when filtering by id and subject.'), t('E-mail'));
+    $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test', 'subject' => $subject, 'from' => 'this_was_not_used@example.com'));
+    $this->assertEqual(count($captured_emails), 0, t('No e-mails are returned when querying with an unused from address.'), t('E-mail'));
+
+    // Send the last e-mail again, so we can confirm that the drupalGetMails-filter
+    // correctly returns all e-mails with a given property/value.
+    drupal_mail_send($message);
+    $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test_4'));
+    $this->assertEqual(count($captured_emails), 2, t('All e-mails with the same id are returned when filtering by id.'), t('E-mail'));
+  }
+}
\ No newline at end of file
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.107
diff -u -r1.107 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	25 May 2009 05:20:16 -0000	1.107
+++ modules/simpletest/drupal_web_test_case.php	25 May 2009 14:25:32 -0000
@@ -409,7 +409,6 @@
     $this->error($exception->getMessage(), 'Uncaught exception', _drupal_get_last_caller($backtrace));
   }
 
-
   /**
    * Generates a random string of ASCII characters of codes 32 to 126.
    *
@@ -476,7 +475,7 @@
     parent::__construct($test_id);
     $this->skipClasses[__CLASS__] = TRUE;
   }
-  
+
   function setUp() {
     global $db_prefix, $conf;
 
@@ -739,7 +738,7 @@
 
     // Make sure type is valid.
     if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) {
-     // Use original file directory instead of one created during setUp().
+      // Use original file directory instead of one created during setUp().
       $path = $this->originalFileDirectory . '/simpletest';
       $files = file_scan_directory($path, '/' . $type . '\-.*/');
 
@@ -994,6 +993,10 @@
     unset($GLOBALS['conf']['language_default']);
     $language = language_default();
 
+    // Make sure our drupal_mail_wrapper function is called instead of the
+    // default mail handler.
+    variable_set('smtp_library', drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php');
+
     // Use temporary files directory with the same prefix as database.
     variable_set('file_directory_path', $this->originalFileDirectory . '/' . $db_prefix);
     $directory = file_directory_path();
@@ -1037,6 +1040,13 @@
    */
   protected function tearDown() {
     global $db_prefix, $user, $language;
+
+    $emailCount = count(variable_get('simpletest_emails', array()));
+    if ($emailCount) {
+      $message = format_plural($emailCount, t('!count e-mail was sent during this test.'), t('!count e-mails were sent during this test.'), array('!count' => $emailCount));
+      $this->pass($message, t('E-mail'));
+    }
+
     if (preg_match('/simpletest\d+/', $db_prefix)) {
       // Delete temporary files directory and reset files directory path.
       file_unmanaged_delete_recursive(file_directory_path());
@@ -1070,7 +1080,7 @@
 
       // Rebuild caches.
       $this->refreshVariables();
-      
+
       // Reset language
       $language = $this->originalLanguage;
       if ($this->originalLanguageDefault) {
@@ -1743,6 +1753,34 @@
   }
 
   /**
+   * Gets an array containing all e-mails sent during this test case.
+   *
+   * @param $filter
+   *   An array containing key/value pairs used to filter the e-mails that are returned.
+   * @return
+   *   An array containing e-mail messages captured during the current test.
+   */
+  protected function drupalGetMails($filter = array()) {
+    $captured_emails = variable_get('simpletest_emails', array());
+    $filtered_emails = array();
+
+    foreach ($captured_emails as $message) {
+      $keep_message = TRUE;
+      foreach ($filter as $key => $value) {
+        if (!isset($message[$key]) || $message[$key] != $value) {
+          $keep_message = FALSE;
+        }
+      }
+
+      if ($keep_message) {
+        $filtered_emails[] = $message;
+      }
+    }
+
+    return $filtered_emails;
+  }
+
+  /**
    * Sets the raw HTML content. This can be useful when a page has been fetched
    * outside of the internal browser and assertions need to be made on the
    * returned page.
@@ -2240,5 +2278,38 @@
     $match = is_array($code) ? in_array($curl_code, $code) : $curl_code == $code;
     return $this->assertTrue($match, $message ? $message : t('HTTP response expected !code, actual !curl_code', array('!code' => $code, '!curl_code' => $curl_code)), t('Browser'));
   }
+
+  /**
+   * Assert that the most recently sent e-mail message has a field with the given value.
+   *
+   * @param $name
+   *   Name of field or message property to assert. Examples: subject, body, id, ...
+   * @param $value
+   *   Value of the field to assert.
+   * @param $message
+   *   Message to display.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertMail($name, $value = '', $message = '') {
+    $captured_emails = variable_get('simpletest_emails', array());
+    $email = end($captured_emails);
+    return $this->assertTrue($email && isset($email[$name]) && $email[$name] == $value, $message, t('E-mail'));
+  }
 }
 
+/**
+ * Wrapper function to override the default mail handler function.
+ *
+ * @param  $message
+ *   An e-mail message. See drupal_mail() for information on how $message is composed.
+ * @return
+ *   Returns TRUE to indicate that the e-mail was successfully accepted for delivery.
+ */
+function drupal_mail_wrapper($message) {
+  $captured_emails = variable_get('simpletest_emails', array());
+  $captured_emails[] = $message;
+  variable_set('simpletest_emails', $captured_emails);
+
+  return TRUE;
+}
