Index: drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/drupal_web_test_case.php,v
retrieving revision 1.3.2.4
diff -u -r1.3.2.4 drupal_web_test_case.php
--- drupal_web_test_case.php	9 Nov 2009 20:57:16 -0000	1.3.2.4
+++ drupal_web_test_case.php	25 Nov 2009 23:58:12 -0000
@@ -398,7 +398,8 @@
     $username = variable_get('simpletest_username', NULL);
     $password = variable_get('simpletest_password', NULL);
     if ($username && $password) {
-      $this->httpauth_credentials = $username . ':' . $password;
+      $this->httpauth_username = $username;
+      $this->httpauth_password = $password;
     }
 
     set_error_handler(array($this, 'errorHandler'));
@@ -571,6 +572,14 @@
  * Test case for typical Drupal tests.
  */
 class DrupalWebTestCase extends DrupalTestCase {
+
+  /**
+   * Browser instance.
+   *
+   * @var object
+   */
+  protected $browser;
+
   /**
    * The URL currently loaded in the internal browser.
    *
@@ -662,6 +671,7 @@
    */
   function __construct($test_id = NULL) {
     parent::__construct($test_id);
+
     $this->skipClasses[__CLASS__] = TRUE;
   }
 
@@ -1212,136 +1222,63 @@
       if ($this->originalLanguageDefault) {
         $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
       }
-
-      // Close the CURL handler.
-      $this->curlClose();
     }
   }
 
   /**
-   * Initializes the cURL connection.
-   *
-   * If the simpletest_httpauth_credentials variable is set, this function will
-   * add HTTP authentication headers. This is necessary for testing sites that
-   * are protected by login credentials from public access.
-   * See the description of $curl_options for other options.
+   * Initialize browser.
    */
-  protected function curlInitialize() {
-    global $base_url, $db_prefix;
+  protected function browserInitialize() {
+    global $db_prefix;
 
-    if (!isset($this->curlHandle)) {
-      $this->curlHandle = curl_init();
-      $curl_options = $this->additionalCurlOptions + array(
-        CURLOPT_COOKIEJAR => $this->cookieFile,
-        CURLOPT_URL => $base_url,
-        CURLOPT_FOLLOWLOCATION => TRUE,
-        CURLOPT_MAXREDIRS => 5,
-        CURLOPT_RETURNTRANSFER => TRUE,
-        CURLOPT_SSL_VERIFYPEER => FALSE, // Required to make the tests run on https.
-        CURLOPT_SSL_VERIFYHOST => FALSE, // Required to make the tests run on https.
-        CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
-      );
-      if (isset($this->httpauth_credentials)) {
-        $curl_options[CURLOPT_USERPWD] = $this->httpauth_credentials;
+    if (!isset($this->browser)) {
+      module_load_include('inc', 'browser');
+      $this->browser = new Browser();
+      $this->browser->setPageListener(array($this, 'browserPageListener'));
+
+      if (isset($this->httpauth_username)) {
+        $this->browser->setHttpAuthentication($this->httpauth_username, $this->httpauth_password);
       }
-      curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
 
       // By default, the child session name should be the same as the parent.
-      $this->session_name = session_name();
+      $this->session_name = session_name(); // TODO
     }
-    // We set the user agent header on each request so as to use the current
-    // time and a new uniqid.
+
+    // Generate a user agent based on the db prefix.
     if (preg_match('/simpletest\d+/', $db_prefix, $matches)) {
-      curl_setopt($this->curlHandle, CURLOPT_USERAGENT, drupal_generate_test_ua($matches[0]));
+      $this->browser->setUserAgent(drupal_generate_test_ua($matches[0]));
     }
+
+    $this->browser->setRequestHeaders(array());
+    return $this->browser;
   }
 
   /**
-   * Performs a cURL exec with the specified options after calling curlConnect().
-   *
-   * @param $curl_options
-   *   Custom cURL options.
-   * @return
-   *   Content returned from the exec.
+   * Browser page callback.
    */
-  protected function curlExec($curl_options) {
-    $this->curlInitialize();
-    $url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL];
-    if (!empty($curl_options[CURLOPT_POST])) {
-      // This is a fix for the Curl library to prevent Expect: 100-continue
-      // headers in POST requests, that may cause unexpected HTTP response
-      // codes from some webservers (like lighttpd that returns a 417 error
-      // code). It is done by setting an empty "Expect" header field that is
-      // not overwritten by Curl.
-      $curl_options[CURLOPT_HTTPHEADER][] = 'Expect:';
-    }
-    curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
-
-    // Reset headers and the session ID.
-    $this->session_id = NULL;
-    $this->headers = array();
-
-    $this->drupalSetContent(curl_exec($this->curlHandle), curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL));
+  public function browserPageListener(array $state, Browser $browser) {
     $message_vars = array(
-      '!method' => !empty($curl_options[CURLOPT_NOBODY]) ? 'HEAD' : (empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST'),
-      '@url' => $url,
-      '@status' => curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE),
-      '!length' => format_size(strlen($this->content))
+      '!method' => $browser->getMethod(),
+      '@url' => $state['url'],
+      '@status' => $state['code'],
+      '!length' => format_size(strlen($state['content'])),
     );
     $message = t('!method @url returned @status (!length).', $message_vars);
     $this->assertTrue($this->content !== FALSE, $message, t('Browser'));
-    return $this->drupalGetContent();
-  }
 
-  /**
-   * Reads headers and registers errors received from the tested site.
-   *
-   * @see _drupal_log_error().
-   *
-   * @param $curlHandler
-   *   The cURL handler.
-   * @param $header
-   *   An header.
-   */
-  protected function curlHeaderCallback($curlHandler, $header) {
-    $this->headers[] = $header;
-
-    // Errors are being sent via X-Drupal-Assertion-* headers,
-    // generated by _drupal_log_error() in the exact form required
-    // by DrupalWebTestCase::error().
-    if (preg_match('/^X-Drupal-Assertion-[0-9]+: (.*)$/', $header, $matches)) {
-      // Call DrupalWebTestCase::error() with the parameters from the header.
-      call_user_func_array(array(&$this, 'error'), unserialize(urldecode($matches[1])));
+    if ($browser->getMethod() == 'GET') {
+      $this->verbose('GET request to: ' . $this->path .
+        '<hr />Ending URL: ' . $state['url'] .
+        '<hr />' . $state['content']);
     }
-
-    // Save cookies.
-    if (preg_match('/^Set-Cookie: ([^=]+)=(.+)/', $header, $matches)) {
-      $name = $matches[1];
-      $parts = array_map('trim', explode(';', $matches[2]));
-      $value = array_shift($parts);
-      $this->cookies[$name] = array('value' => $value, 'secure' => in_array('secure', $parts));
-      if ($name == $this->session_name) {
-        if ($value != 'deleted') {
-          $this->session_id = $value;
-        }
-        else {
-          $this->session_id = NULL;
-        }
-      }
+    else {
+      $this->verbose('POST request to: ' . $this->path .
+        '<hr />Ending URL: ' . $state['url'] .
+        '<hr />Fields: ' . highlight_string('<?php ' . var_export($this->browser->getPost(), TRUE), TRUE) .
+        '<hr />' . $state['content']);
     }
 
-    // This is required by cURL.
-    return strlen($header);
-  }
-
-  /**
-   * Close the cURL handler and unset the handler.
-   */
-  protected function curlClose() {
-    if (isset($this->curlHandle)) {
-      curl_close($this->curlHandle);
-      unset($this->curlHandle);
-    }
+    $this->path = '[previous]';
   }
 
   /**
@@ -1383,22 +1320,18 @@
    *   The retrieved HTML string, also available as $this->drupalGetContent()
    */
   protected function drupalGet($path, array $options = array(), array $headers = array()) {
+    $this->path = $path;
+
+    // The browser requires the URL to be absolute.
     $options['absolute'] = TRUE;
 
-    // We re-using a CURL connection here. If that connection still has certain
-    // options set, it might change the GET into a POST. Make sure we clear out
-    // previous options.
-    $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers));
-    $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up.
+    // Make the GET request.
+    $state = $this->browserInitialize()->get(url($path, $options));
 
-    // Replace original page output with new output from redirected page(s).
-    if (($new = $this->checkForMetaRefresh())) {
-      $out = $new;
-    }
-    $this->verbose('GET request to: ' . $path .
-                   '<hr />Ending URL: ' . $this->getUrl() .
-                   '<hr />' . $out);
-    return $out;
+    // Ensure that any changes to variables in the other thread are picked up.
+    $this->refreshVariables();
+
+    return $state['content'];
   }
 
   /**
@@ -1437,68 +1370,28 @@
    *   "name: value".
    */
   protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array()) {
-    $submit_matches = FALSE;
-    if (isset($path)) {
-      $html = $this->drupalGet($path, $options);
-    }
-    if ($this->parse()) {
-      $edit_save = $edit;
-      // Let's iterate over all the forms.
-      $forms = $this->xpath('//form');
-      foreach ($forms as $form) {
-        // We try to set the fields of this form as specified in $edit.
-        $edit = $edit_save;
-        $post = array();
-        $upload = array();
-        $submit_matches = $this->handleForm($post, $edit, $upload, $submit, $form);
-        $action = isset($form['action']) ? $this->getAbsoluteUrl($form['action']) : $this->getUrl();
-
-        // We post only if we managed to handle every field in edit and the
-        // submit button matches.
-        if (!$edit && $submit_matches) {
-          $post_array = $post;
-          if ($upload) {
-            // TODO: cURL handles file uploads for us, but the implementation
-            // is broken. This is a less than elegant workaround. Alternatives
-            // are being explored at #253506.
-            foreach ($upload as $key => $file) {
-              $file = drupal_realpath($file);
-              if ($file && is_file($file)) {
-                $post[$key] = '@' . $file;
-              }
-            }
-          }
-          else {
-            foreach ($post as $key => $value) {
-              // Encode according to application/x-www-form-urlencoded
-              // Both names and values needs to be urlencoded, according to
-              // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
-              $post[$key] = urlencode($key) . '=' . urlencode($value);
-            }
-            $post = implode('&', $post);
-          }
-          $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers));
-          // Ensure that any changes to variables in the other thread are picked up.
-          $this->refreshVariables();
-
-          // Replace original page output with new output from redirected page(s).
-          if (($new = $this->checkForMetaRefresh())) {
-            $out = $new;
-          }
-          $this->verbose('POST request to: ' . $path .
-                         '<hr />Ending URL: ' . $this->getUrl() .
-                         '<hr />Fields: ' . highlight_string('<?php ' . var_export($post_array, TRUE), TRUE) .
-                         '<hr />' . $out);
-          return $out;
-        }
-      }
-      // We have not found a form which contained all fields of $edit.
-      foreach ($edit as $name => $value) {
-        $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value)));
-      }
-      $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit)));
-      $this->fail(t('Found the requested form fields at @path', array('@path' => $path)));
+    $this->path = $path;
+
+    // The browser requires the URL to be absolute.
+    $options['absolute'] = TRUE;
+
+    $this->browserInitialize();
+
+    foreach ($headers as $header) {
+      $parts = explode(':', $header);
+      $this->browser->setRequestHeader(trim($parts[0]), trim($parts[1]));
     }
+    $state = $this->browser->post(url($path, $options), $edit, $submit);
+
+    if ($state) {
+      // Ensure that any changes to variables in the other thread are picked up.
+      $this->refreshVariables();
+
+      return $state['content'];
+    }
+
+    $this->fail(t('Found the requested form fields at [@path]', array('@path' => $path)));
+    return FALSE;
   }
 
   /**
@@ -1509,28 +1402,6 @@
   }
 
   /**
-   * Check for meta refresh tag and if found call drupalGet() recursively. This
-   * function looks for the http-equiv attribute to be set to "Refresh"
-   * and is case-sensitive.
-   *
-   * @return
-   *   Either the new page content or FALSE.
-   */
-  protected function checkForMetaRefresh() {
-    if ($this->drupalGetContent() != '' && $this->parse()) {
-      $refresh = $this->xpath('//meta[@http-equiv="Refresh"]');
-      if (!empty($refresh)) {
-        // Parse the content attribute of the meta tag for the format:
-        // "[delay]: URL=[page_to_redirect_to]".
-        if (preg_match('/\d+;\s*URL=(?P<url>.*)/i', $refresh[0]['content'], $match)) {
-          return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url'])));
-        }
-      }
-    }
-    return FALSE;
-  }
-
-  /**
    * Retrieves only the headers for a Drupal path or an absolute path.
    *
    * @param $path
@@ -1551,137 +1422,6 @@
   }
 
   /**
-   * Handle form input related to drupalPost(). Ensure that the specified fields
-   * exist and attempt to create POST data in the correct manner for the particular
-   * field type.
-   *
-   * @param $post
-   *   Reference to array of post values.
-   * @param $edit
-   *   Reference to array of edit values to be checked against the form.
-   * @param $submit
-   *   Form submit button value.
-   * @param $form
-   *   Array of form elements.
-   * @return
-   *   Submit value matches a valid submit input in the form.
-   */
-  protected function handleForm(&$post, &$edit, &$upload, $submit, $form) {
-    // Retrieve the form elements.
-    $elements = $form->xpath('.//input|.//textarea|.//select');
-    $submit_matches = FALSE;
-    foreach ($elements as $element) {
-      // SimpleXML objects need string casting all the time.
-      $name = (string) $element['name'];
-      // This can either be the type of <input> or the name of the tag itself
-      // for <select> or <textarea>.
-      $type = isset($element['type']) ? (string)$element['type'] : $element->getName();
-      $value = isset($element['value']) ? (string)$element['value'] : '';
-      $done = FALSE;
-      if (isset($edit[$name])) {
-        switch ($type) {
-          case 'text':
-          case 'textarea':
-          case 'hidden':
-          case 'password':
-            $post[$name] = $edit[$name];
-            unset($edit[$name]);
-            break;
-          case 'radio':
-            if ($edit[$name] == $value) {
-              $post[$name] = $edit[$name];
-              unset($edit[$name]);
-            }
-            break;
-          case 'checkbox':
-            // To prevent checkbox from being checked.pass in a FALSE,
-            // otherwise the checkbox will be set to its value regardless
-            // of $edit.
-            if ($edit[$name] === FALSE) {
-              unset($edit[$name]);
-              continue 2;
-            }
-            else {
-              unset($edit[$name]);
-              $post[$name] = $value;
-            }
-            break;
-          case 'select':
-            $new_value = $edit[$name];
-            $index = 0;
-            $key = preg_replace('/\[\]$/', '', $name);
-            $options = $this->getAllOptions($element);
-            foreach ($options as $option) {
-              if (is_array($new_value)) {
-                $option_value= (string)$option['value'];
-                if (in_array($option_value, $new_value)) {
-                  $post[$key . '[' . $index++ . ']'] = $option_value;
-                  $done = TRUE;
-                  unset($edit[$name]);
-                }
-              }
-              elseif ($new_value == $option['value']) {
-                $post[$name] = $new_value;
-                unset($edit[$name]);
-                $done = TRUE;
-              }
-            }
-            break;
-          case 'file':
-            $upload[$name] = $edit[$name];
-            unset($edit[$name]);
-            break;
-        }
-      }
-      if (!isset($post[$name]) && !$done) {
-        switch ($type) {
-          case 'textarea':
-            $post[$name] = (string)$element;
-            break;
-          case 'select':
-            $single = empty($element['multiple']);
-            $first = TRUE;
-            $index = 0;
-            $key = preg_replace('/\[\]$/', '', $name);
-            $options = $this->getAllOptions($element);
-            foreach ($options as $option) {
-              // For single select, we load the first option, if there is a
-              // selected option that will overwrite it later.
-              if ($option['selected'] || ($first && $single)) {
-                $first = FALSE;
-                if ($single) {
-                  $post[$name] = (string)$option['value'];
-                }
-                else {
-                  $post[$key . '[' . $index++ . ']'] = (string)$option['value'];
-                }
-              }
-            }
-            break;
-          case 'file':
-            break;
-          case 'submit':
-          case 'image':
-            if ($submit == $value) {
-              $post[$name] = $value;
-              $submit_matches = TRUE;
-            }
-            break;
-          case 'radio':
-          case 'checkbox':
-            if (!isset($element['checked'])) {
-              break;
-            }
-            // Deliberate no break.
-          default:
-            $post[$name] = $value;
-        }
-      }
-    }
-    return $submit_matches;
-  }
-
-  /**
    * Perform an xpath search on the contents of the internal browser. The search
    * is relative to the root element (HTML tag normally) of the page.
    *
@@ -1693,37 +1433,13 @@
    *   http://us.php.net/manual/function.simplexml-element-xpath.php.
    */
   protected function xpath($xpath) {
-    if ($this->parse()) {
-      return $this->elements->xpath($xpath);
+    if ($page = $this->browser->getPage()) {
+      return $page->xpath($xpath);
     }
     return FALSE;
   }
 
   /**
-   * Get all option elements, including nested options, in a select.
-   *
-   * @param $element
-   *   The element for which to get the options.
-   * @return
-   *   Option elements in select.
-   */
-  protected function getAllOptions(SimpleXMLElement $element) {
-    $options = array();
-    // Add all options items.
-    foreach ($element->option as $option) {
-      $options[] = $option;
-    }
-
-    // Search option group children.
-    if (isset($element->optgroup)) {
-      foreach ($element->optgroup as $group) {
-        $options = array_merge($options, $this->getAllOptions($group));
-      }
-    }
-    return $options;
-  }
-
-  /**
    * Pass if a link with the specified label is found, and optional with the
    * specified index.
    *
@@ -1796,43 +1512,13 @@
   }
 
   /**
-   * Takes a path and returns an absolute path.
-   *
-   * @param $path
-   *   A path from the internal browser content.
-   * @return
-   *   The $path with $base_url prepended, if necessary.
-   */
-  protected function getAbsoluteUrl($path) {
-    global $base_url, $base_path;
-
-    $parts = parse_url($path);
-    if (empty($parts['host'])) {
-      // Ensure that we have a string (and no xpath object).
-      $path = (string) $path;
-      // Strip $base_path, if existent.
-      $length = strlen($base_path);
-      if (substr($path, 0, $length) === $base_path) {
-        $path = substr($path, $length);
-      }
-      // Ensure that we have an absolute path.
-      if ($path[0] !== '/') {
-        $path = '/' . $path;
-      }
-      // Finally, prepend the $base_url.
-      $path = $base_url . $path;
-    }
-    return $path;
-  }
-
-  /**
    * Get the current url from the cURL handler.
    *
    * @return
    *   The current url.
    */
   protected function getUrl() {
-    return $this->url;
+    return $this->browser->getUrl();
   }
 
   /**
@@ -1925,7 +1611,7 @@
    * Gets the current raw HTML of requested page.
    */
   protected function drupalGetContent() {
-    return $this->content;
+    return $this->browser->getContent();
   }
 
   /**
@@ -2263,28 +1949,6 @@
   }
 
   /**
-   * Get the selected value from a select field.
-   *
-   * @param $element
-   *   SimpleXMLElement select element.
-   * @return
-   *   The selected value or FALSE.
-   */
-  protected function getSelectedItem(SimpleXMLElement $element) {
-    foreach ($element->children() as $item) {
-      if (isset($item['selected'])) {
-        return $item['value'];
-      }
-      elseif ($item->getName() == 'optgroup') {
-        if ($value = $this->getSelectedItem($item)) {
-          return $value;
-        }
-      }
-    }
-    return FALSE;
-  }
-
-  /**
    * Assert that a field does not exist in the current page by the given XPath.
    *
    * @param $xpath
Index: simpletest.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.info,v
retrieving revision 1.8.2.7
diff -u -r1.8.2.7 simpletest.info
--- simpletest.info	10 Nov 2009 03:24:15 -0000	1.8.2.7
+++ simpletest.info	25 Nov 2009 23:58:12 -0000
@@ -9,4 +9,5 @@
 files[] = simpletest.install
 files[] = simpletest.test
 files[] = drupal_web_test_case.php
+dependencies[] = browser
 testing_api = 2.x
