diff --git a/modules/field/field.test b/modules/field/field.test
new file mode 100644
index 0000000..36d6284
--- /dev/null
+++ b/modules/field/field.test
@@ -0,0 +1,2968 @@
+<?php
+// $Id: field.test,v 1.33 2010/06/17 13:16:57 dries Exp $
+
+/**
+ * @file
+ * Unit test file for fields in core.
+ */
+
+/**
+ * Parent class for Field API tests.
+ */
+class FieldTestCase extends DrupalWebTestCase {
+  var $default_storage = 'field_sql_storage';
+
+  /**
+   * Set the default field storage backend for fields created during tests.
+   */
+  function setUp() {
+    // Since this is a base class for many test cases, support the same
+    // flexibility that DrupalWebTestCase::setUp() has for the modules to be
+    // passed in as either an array or a variable number of string arguments.
+    $modules = func_get_args();
+    if (isset($modules[0]) && is_array($modules[0])) {
+      $modules = $modules[0];
+    }
+    parent::setUp($modules);
+    // Set default storage backend.
+    variable_set('field_storage_default', $this->default_storage);
+  }
+
+  /**
+   * Generate random values for a field_test field.
+   *
+   * @param $cardinality
+   *   Number of values to generate.
+   * @return
+   *  An array of random values, in the format expected for field values.
+   */
+  function _generateTestFieldValues($cardinality) {
+    $values = array();
+    for ($i = 0; $i < $cardinality; $i++) {
+      // field_test fields treat 0 as 'empty value'.
+      $values[$i]['value'] = mt_rand(1, 127);
+    }
+    return $values;
+  }
+
+  /**
+   * Assert that a field has the expected values in an entity.
+   *
+   * This function only checks a single column in the field values.
+   *
+   * @param $entity
+   *   The entity to test.
+   * @param $field_name
+   *   The name of the field to test
+   * @param $langcode
+   *   The language code for the values.
+   * @param $expected_values
+   *   The array of expected values.
+   * @param $column
+   *   (Optional) the name of the column to check.
+   */
+  function assertFieldValues($entity, $field_name, $langcode, $expected_values, $column = 'value') {
+    $e = clone $entity;
+    field_attach_load('test_entity', array($e->ftid => $e));
+    $values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
+    $this->assertEqual(count($values), count($expected_values), t('Expected number of values were saved.'));
+    foreach ($expected_values as $key => $value) {
+      $this->assertEqual($values[$key][$column], $value, t('Value @value was saved correctly.', array('@value' => $value)));
+    }
+  }
+}
+
+class FieldAttachTestCase extends FieldTestCase {
+  function setUp($modules = array()) {
+    // Since this is a base class for many test cases, support the same
+    // flexibility that DrupalWebTestCase::setUp() has for the modules to be
+    // passed in as either an array or a variable number of string arguments.
+    if (!is_array($modules)) {
+      $modules = func_get_args();
+    }
+    if (!in_array('field_test', $modules)) {
+      $modules[] = 'field_test';
+    }
+    parent::setUp($modules);
+
+    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
+    $this->field = field_create_field($this->field);
+    $this->field_id = $this->field['id'];
+    $this->instance = array(
+      'field_name' => $this->field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_instance_setting' => $this->randomName(),
+      ),
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'label' => 'Test Field',
+        'settings' => array(
+          'test_widget_setting' => $this->randomName(),
+        )
+      )
+    );
+    field_create_instance($this->instance);
+  }
+}
+
+/**
+ * Unit test class for storage-related field_attach_* functions.
+ *
+ * All field_attach_* test work with all field_storage plugins and
+ * all hook_field_attach_pre_{load,insert,update}() hooks.
+ */
+class FieldAttachStorageTestCase extends FieldAttachTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field attach tests (storage-related)',
+      'description' => 'Test storage-related Field Attach API functions.',
+      'group' => 'Field API',
+    );
+  }
+
+  /**
+   * Check field values insert, update and load.
+   *
+   * Works independently of the underlying field storage backend. Inserts or
+   * updates random field data and then loads and verifies the data.
+   */
+  function testFieldAttachSaveLoad() {
+    // Configure the instance so that we test hook_field_load() (see
+    // field_test_field_load() in field_test.module).
+    $this->instance['settings']['test_hook_field_load'] = TRUE;
+    field_update_instance($this->instance);
+    $langcode = LANGUAGE_NONE;
+
+    $entity_type = 'test_entity';
+    $values = array();
+
+    // TODO : test empty values filtering and "compression" (store consecutive deltas).
+
+    // Preparation: create three revisions and store them in $revision array.
+    for ($revision_id = 0; $revision_id < 3; $revision_id++) {
+      $revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
+      // Note: we try to insert one extra value.
+      $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+      $current_revision = $revision_id;
+      // If this is the first revision do an insert.
+      if (!$revision_id) {
+        $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
+        field_attach_insert($entity_type, $revision[$revision_id]);
+      }
+      else {
+        // Otherwise do an update.
+        $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
+        field_attach_update($entity_type, $revision[$revision_id]);
+      }
+    }
+
+    // Confirm current revision loads the correct data.
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    field_attach_load($entity_type, array(0 => $entity));
+    // Number of values per field loaded equals the field cardinality.
+    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Current revision: expected number of values'));
+    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+      // The field value loaded matches the one inserted or updated.
+      $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'] , $values[$current_revision][$delta]['value'], t('Current revision: expected value %delta was found.', array('%delta' => $delta)));
+      // The value added in hook_field_load() is found.
+      $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', t('Current revision: extra information for value %delta was found', array('%delta' => $delta)));
+    }
+
+    // Confirm each revision loads the correct data.
+    foreach (array_keys($revision) as $revision_id) {
+      $entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
+      field_attach_load_revision($entity_type, array(0 => $entity));
+      // Number of values per field loaded equals the field cardinality.
+      $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
+      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+        // The field value loaded matches the one inserted or updated.
+        $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $values[$revision_id][$delta]['value'], t('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
+        // The value added in hook_field_load() is found.
+        $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', t('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
+      }
+    }
+  }
+
+  /**
+   * Test the 'multiple' load feature.
+   */
+  function testFieldAttachLoadMultiple() {
+    $entity_type = 'test_entity';
+    $langcode = LANGUAGE_NONE;
+
+    // Define 2 bundles.
+    $bundles = array(
+      1 => 'test_bundle_1',
+      2 => 'test_bundle_2',
+    );
+    field_test_create_bundle($bundles[1]);
+    field_test_create_bundle($bundles[2]);
+    // Define 3 fields:
+    // - field_1 is in bundle_1 and bundle_2,
+    // - field_2 is in bundle_1,
+    // - field_3 is in bundle_2.
+    $field_bundles_map = array(
+      1 => array(1, 2),
+      2 => array(1),
+      3 => array(2),
+    );
+    for ($i = 1; $i <= 3; $i++) {
+      $field_names[$i] = 'field_' . $i;
+      $field = array('field_name' => $field_names[$i], 'type' => 'test_field');
+      $field = field_create_field($field);
+      $field_ids[$i] = $field['id'];
+      foreach ($field_bundles_map[$i] as $bundle) {
+        $instance = array(
+          'field_name' => $field_names[$i],
+          'entity_type' => 'test_entity',
+          'bundle' => $bundles[$bundle],
+          'settings' => array(
+            // Configure the instance so that we test hook_field_load()
+            // (see field_test_field_load() in field_test.module).
+            'test_hook_field_load' => TRUE,
+          ),
+        );
+        field_create_instance($instance);
+      }
+    }
+
+    // Create one test entity per bundle, with random values.
+    foreach ($bundles as $index => $bundle) {
+      $entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
+      $entity = clone($entities[$index]);
+      $instances = field_info_instances('test_entity', $bundle);
+      foreach ($instances as $field_name => $instance) {
+        $values[$index][$field_name] = mt_rand(1, 127);
+        $entity->$field_name = array($langcode => array(array('value' => $values[$index][$field_name])));
+      }
+      field_attach_insert($entity_type, $entity);
+    }
+
+    // Check that a single load correctly loads field values for both entities.
+    field_attach_load($entity_type, $entities);
+    foreach ($entities as $index => $entity) {
+      $instances = field_info_instances($entity_type, $bundles[$index]);
+      foreach ($instances as $field_name => $instance) {
+        // The field value loaded matches the one inserted.
+        $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
+        // The value added in hook_field_load() is found.
+        $this->assertEqual($entity->{$field_name}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
+      }
+    }
+
+    // Check that the single-field load option works.
+    $entity = field_test_create_stub_entity(1, 1, $bundles[1]);
+    field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('field_id' => $field_ids[1]));
+    $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['value'], $values[1][$field_names[1]], t('Entity %index: expected value was found.', array('%index' => 1)));
+    $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => 1)));
+    $this->assert(!isset($entity->{$field_names[2]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
+    $this->assert(!isset($entity->{$field_names[3]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
+  }
+
+  /**
+   * Test saving and loading fields using different storage backends.
+   */
+  function testFieldAttachSaveLoadDifferentStorage() {
+    $entity_type = 'test_entity';
+    $langcode = LANGUAGE_NONE;
+
+    // Create two fields using different storage backends, and their instances.
+    $fields = array(
+      array(
+        'field_name' => 'field_1',
+        'type' => 'test_field',
+        'cardinality' => 4,
+        'storage' => array('type' => 'field_sql_storage')
+      ),
+      array(
+        'field_name' => 'field_2',
+        'type' => 'test_field',
+        'cardinality' => 4,
+        'storage' => array('type' => 'field_test_storage')
+      ),
+    );
+    foreach ($fields as $field) {
+      field_create_field($field);
+      $instance = array(
+        'field_name' => $field['field_name'],
+        'entity_type' => 'test_entity',
+        'bundle' => 'test_bundle',
+      );
+      field_create_instance($instance);
+    }
+
+    $entity_init = field_test_create_stub_entity();
+
+    // Create entity and insert random values.
+    $entity = clone($entity_init);
+    $values = array();
+    foreach ($fields as $field) {
+      $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
+      $entity->{$field['field_name']}[$langcode] = $values[$field['field_name']];
+    }
+    field_attach_insert($entity_type, $entity);
+
+    // Check that values are loaded as expected.
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    foreach ($fields as $field) {
+      $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}[$langcode], t('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
+    }
+  }
+
+  /**
+   * Test storage details alteration.
+   *
+   * @see field_test_storage_details_alter()
+   */
+  function testFieldStorageDetailsAlter() {
+    $field_name = 'field_test_change_my_details';
+    $field = array(
+      'field_name' => $field_name,
+      'type' => 'test_field',
+      'cardinality' => 4,
+      'storage' => array('type' => 'field_test_storage'),
+    );
+    $field = field_create_field($field);
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+    );
+    field_create_instance($instance);
+
+    $field = field_info_field($instance['field_name']);
+    $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
+
+    // The storage details are indexed by a storage engine type.
+    $this->assertTrue(array_key_exists('drupal_variables', $field['storage']['details']), t('The storage type is Drupal variables.'));
+
+    $details = $field['storage']['details']['drupal_variables'];
+
+    // The field_test storage details are indexed by variable name. The details
+    // are altered, so moon and mars are correct for this test.
+    $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), t('Moon is available in the instance array.'));
+    $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), t('Mars is available in the instance array.'));
+
+    // Test current and revision storage details together because the columns
+    // are the same.
+    foreach ((array) $field['columns'] as $column_name => $attributes) {
+      $this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
+      $this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
+    }
+  }
+
+  /**
+   * Tests insert and update with missing or NULL fields.
+   */
+  function testFieldAttachSaveMissingData() {
+    $entity_type = 'test_entity';
+    $entity_init = field_test_create_stub_entity();
+    $langcode = LANGUAGE_NONE;
+
+    // Insert: Field is missing.
+    $entity = clone($entity_init);
+    field_attach_insert($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
+
+    // Insert: Field is NULL.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    $entity->{$this->field_name} = NULL;
+    field_attach_insert($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
+
+    // Add some real data.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    $values = $this->_generateTestFieldValues(1);
+    $entity->{$this->field_name}[$langcode] = $values;
+    field_attach_insert($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
+
+    // Update: Field is missing. Data should survive.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    field_attach_update($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Update: missing field leaves existing values in place'));
+
+    // Update: Field is NULL. Data should be wiped.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    $entity->{$this->field_name} = NULL;
+    field_attach_update($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
+
+    // Re-add some data.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    $values = $this->_generateTestFieldValues(1);
+    $entity->{$this->field_name}[$langcode] = $values;
+    field_attach_update($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
+
+    // Update: Field is empty array. Data should be wiped.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    $entity->{$this->field_name} = array();
+    field_attach_update($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}), t('Update: empty array removes existing values'));
+  }
+
+  /**
+   * Test insert with missing or NULL fields, with default value.
+   */
+  function testFieldAttachSaveMissingDataDefaultValue() {
+    // Add a default value function.
+    $this->instance['default_value_function'] = 'field_test_default_value';
+    field_update_instance($this->instance);
+
+    $entity_type = 'test_entity';
+    $entity_init = field_test_create_stub_entity();
+    $langcode = LANGUAGE_NONE;
+
+    // Insert: Field is NULL.
+    $entity = clone($entity_init);
+    $entity->{$this->field_name}[$langcode] = NULL;
+    field_attach_insert($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertTrue(empty($entity->{$this->field_name}[$langcode]), t('Insert: NULL field results in no value saved'));
+
+    // Insert: Field is missing.
+    field_cache_clear();
+    $entity = clone($entity_init);
+    field_attach_insert($entity_type, $entity);
+
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
+    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Insert: missing field results in default value saved'));
+  }
+
+  /**
+   * Test field_attach_delete().
+   */
+  function testFieldAttachDelete() {
+    $entity_type = 'test_entity';
+    $langcode = LANGUAGE_NONE;
+    $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+
+    // Create revision 0
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+    $rev[0]->{$this->field_name}[$langcode] = $values;
+    field_attach_insert($entity_type, $rev[0]);
+
+    // Create revision 1
+    $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
+    $rev[1]->{$this->field_name}[$langcode] = $values;
+    field_attach_update($entity_type, $rev[1]);
+
+    // Create revision 2
+    $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+    $rev[2]->{$this->field_name}[$langcode] = $values;
+    field_attach_update($entity_type, $rev[2]);
+
+    // Confirm each revision loads
+    foreach (array_keys($rev) as $vid) {
+      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+      field_attach_load_revision($entity_type, array(0 => $read));
+      $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+    }
+
+    // Delete revision 1, confirm the other two still load.
+    field_attach_delete_revision($entity_type, $rev[1]);
+    foreach (array(0, 2) as $vid) {
+      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+      field_attach_load_revision($entity_type, array(0 => $read));
+      $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+    }
+
+    // Confirm the current revision still loads
+    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+    field_attach_load($entity_type, array(0 => $read));
+    $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
+
+    // Delete all field data, confirm nothing loads
+    field_attach_delete($entity_type, $rev[2]);
+    foreach (array(0, 1, 2) as $vid) {
+      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
+      field_attach_load_revision($entity_type, array(0 => $read));
+      $this->assertIdentical($read->{$this->field_name}, array(), "The test entity revision $vid is deleted.");
+    }
+    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
+    field_attach_load($entity_type, array(0 => $read));
+    $this->assertIdentical($read->{$this->field_name}, array(), t('The test entity current revision is deleted.'));
+  }
+
+  /**
+   * Test field_attach_create_bundle() and field_attach_rename_bundle().
+   */
+  function testFieldAttachCreateRenameBundle() {
+    // Create a new bundle. This has to be initiated by the module so that its
+    // hook_entity_info() is consistent.
+    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+    field_test_create_bundle($new_bundle);
+
+    // Add an instance to that bundle.
+    $this->instance['bundle'] = $new_bundle;
+    field_create_instance($this->instance);
+
+    // Save an entity with data in the field.
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    $langcode = LANGUAGE_NONE;
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+    $entity->{$this->field_name}[$langcode] = $values;
+    $entity_type = 'test_entity';
+    field_attach_insert($entity_type, $entity);
+
+    // Verify the field data is present on load.
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    field_attach_load($entity_type, array(0 => $entity));
+    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
+
+    // Rename the bundle. This has to be initiated by the module so that its
+    // hook_entity_info() is consistent.
+    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+    field_test_rename_bundle($this->instance['bundle'], $new_bundle);
+
+    // Check that the instance definition has been updated.
+    $this->instance = field_info_instance($entity_type, $this->field_name, $new_bundle);
+    $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
+
+    // Verify the field data is present on load.
+    $entity = field_test_create_stub_entity(0, 0, $new_bundle);
+    field_attach_load($entity_type, array(0 => $entity));
+    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage");
+  }
+
+  /**
+   * Test field_attach_delete_bundle().
+   */
+  function testFieldAttachDeleteBundle() {
+    // Create a new bundle. This has to be initiated by the module so that its
+    // hook_entity_info() is consistent.
+    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+    field_test_create_bundle($new_bundle);
+
+    // Add an instance to that bundle.
+    $this->instance['bundle'] = $new_bundle;
+    field_create_instance($this->instance);
+
+    // Create a second field for the test bundle
+    $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
+    field_create_field($field);
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => $this->instance['bundle'],
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      // test_field has no instance settings
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'settings' => array(
+          'size' => mt_rand(0, 255))));
+    field_create_instance($instance);
+
+    // Save an entity with data for both fields
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    $langcode = LANGUAGE_NONE;
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+    $entity->{$this->field_name}[$langcode] = $values;
+    $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1);
+    field_attach_insert('test_entity', $entity);
+
+    // Verify the fields are present on load
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    field_attach_load('test_entity', array(0 => $entity));
+    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded');
+    $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded');
+
+    // Delete the bundle. This has to be initiated by the module so that its
+    // hook_entity_info() is consistent.
+    field_test_delete_bundle($this->instance['bundle']);
+
+    // Verify no data gets loaded
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    field_attach_load('test_entity', array(0 => $entity));
+    $this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field');
+    $this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field');
+
+    // Verify that the instances are gone
+    $this->assertFalse(field_read_instance('test_entity', $this->field_name, $this->instance['bundle']), "First field is deleted");
+    $this->assertFalse(field_read_instance('test_entity', $field_name, $instance['bundle']), "Second field is deleted");
+  }
+}
+
+/**
+ * Unit test class for non-storage related field_attach_* functions.
+ */
+class FieldAttachOtherTestCase extends FieldAttachTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field attach tests (other)',
+      'description' => 'Test other Field Attach API functions.',
+      'group' => 'Field API',
+    );
+  }
+
+  /**
+   * Test field_attach_view() and field_attach_prepare_view().
+   */
+  function testFieldAttachView() {
+    $entity_type = 'test_entity';
+    $entity_init = field_test_create_stub_entity();
+    $langcode = LANGUAGE_NONE;
+
+    // Populate values to be displayed.
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+    $entity_init->{$this->field_name}[$langcode] = $values;
+
+    // Simple formatter, label displayed.
+    $entity = clone($entity_init);
+    $formatter_setting = $this->randomName();
+    $this->instance['display'] = array(
+      'full' => array(
+        'label' => 'above',
+        'type' => 'field_test_default',
+        'settings' => array(
+          'test_formatter_setting' => $formatter_setting,
+        )
+      ),
+    );
+    field_update_instance($this->instance);
+    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
+    $entity->content = field_attach_view($entity_type, $entity, 'full');
+    $output = drupal_render($entity->content);
+    $this->content = $output;
+    $this->assertRaw($this->instance['label'], "Label is displayed.");
+    foreach ($values as $delta => $value) {
+      $this->content = $output;
+      $this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
+    }
+
+    // Label hidden.
+    $entity = clone($entity_init);
+    $this->instance['display']['full']['label'] = 'hidden';
+    field_update_instance($this->instance);
+    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
+    $entity->content = field_attach_view($entity_type, $entity, 'full');
+    $output = drupal_render($entity->content);
+    $this->content = $output;
+    $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
+
+    // Field hidden.
+    $entity = clone($entity_init);
+    $this->instance['display'] = array(
+      'full' => array(
+        'label' => 'above',
+        'type' => 'hidden',
+      ),
+    );
+    field_update_instance($this->instance);
+    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
+    $entity->content = field_attach_view($entity_type, $entity, 'full');
+    $output = drupal_render($entity->content);
+    $this->content = $output;
+    $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
+    foreach ($values as $delta => $value) {
+      $this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
+    }
+
+    // Multiple formatter.
+    $entity = clone($entity_init);
+    $formatter_setting = $this->randomName();
+    $this->instance['display'] = array(
+      'full' => array(
+        'label' => 'above',
+        'type' => 'field_test_multiple',
+        'settings' => array(
+          'test_formatter_setting_multiple' => $formatter_setting,
+        )
+      ),
+    );
+    field_update_instance($this->instance);
+    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
+    $entity->content = field_attach_view($entity_type, $entity, 'full');
+    $output = drupal_render($entity->content);
+    $display = $formatter_setting;
+    foreach ($values as $delta => $value) {
+      $display .= "|$delta:{$value['value']}";
+    }
+    $this->content = $output;
+    $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
+
+    // Test a formatter that uses hook_field_formatter_prepare_view().
+    $entity = clone($entity_init);
+    $formatter_setting = $this->randomName();
+    $this->instance['display'] = array(
+      'full' => array(
+        'label' => 'above',
+        'type' => 'field_test_with_prepare_view',
+        'settings' => array(
+          'test_formatter_setting_additional' => $formatter_setting,
+        )
+      ),
+    );
+    field_update_instance($this->instance);
+    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
+    $entity->content = field_attach_view($entity_type, $entity, 'full');
+    $output = drupal_render($entity->content);
+    $this->content = $output;
+    foreach ($values as $delta => $value) {
+      $this->content = $output;
+      $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
+      $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
+    }
+
+    // TODO:
+    // - check display order with several fields
+
+    // Preprocess template.
+    $variables = array();
+    field_attach_preprocess($entity_type, $entity, $entity->content, $variables);
+    $result = TRUE;
+    foreach ($values as $delta => $item) {
+      if ($variables[$this->field_name][$delta]['value'] !== $item['value']) {
+        $result = FALSE;
+        break;
+      }
+    }
+    $this->assertTrue($result, t('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name)));
+  }
+
+  /**
+   * Test field cache.
+   */
+  function testFieldAttachCache() {
+    // Initialize random values and a test entity.
+    $entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
+    $langcode = LANGUAGE_NONE;
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+
+    // Non-cacheable entity type.
+    $entity_type = 'test_entity';
+    $cid = "field:$entity_type:{$entity_init->ftid}";
+
+    // Check that no initial cache entry is present.
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
+
+    // Save, and check that no cache entry is present.
+    $entity = clone($entity_init);
+    $entity->{$this->field_name}[$langcode] = $values;
+    field_attach_insert($entity_type, $entity);
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert'));
+
+    // Load, and check that no cache entry is present.
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load'));
+
+
+    // Cacheable entity type.
+    $entity_type = 'test_cacheable_entity';
+    $cid = "field:$entity_type:{$entity_init->ftid}";
+    $instance = $this->instance;
+    $instance['entity_type'] = $entity_type;
+    field_create_instance($instance);
+
+    // Check that no initial cache entry is present.
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
+
+    // Save, and check that no cache entry is present.
+    $entity = clone($entity_init);
+    $entity->{$this->field_name}[$langcode] = $values;
+    field_attach_insert($entity_type, $entity);
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert'));
+
+    // Load a single field, and check that no cache entry is present.
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id));
+    $cache = cache_get($cid, 'cache_field');
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on loading a single field'));
+
+    // Load, and check that a cache entry is present with the expected values.
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $cache = cache_get($cid, 'cache_field');
+    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
+
+    // Update with different values, and check that the cache entry is wiped.
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+    $entity = clone($entity_init);
+    $entity->{$this->field_name}[$langcode] = $values;
+    field_attach_update($entity_type, $entity);
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update'));
+
+    // Load, and check that a cache entry is present with the expected values.
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $cache = cache_get($cid, 'cache_field');
+    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
+
+    // Create a new revision, and check that the cache entry is wiped.
+    $entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
+    $values = $this->_generateTestFieldValues($this->field['cardinality']);
+    $entity = clone($entity_init);
+    $entity->{$this->field_name}[$langcode] = $values;
+    field_attach_update($entity_type, $entity);
+    $cache = cache_get($cid, 'cache_field');
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation'));
+
+    // Load, and check that a cache entry is present with the expected values.
+    $entity = clone($entity_init);
+    field_attach_load($entity_type, array($entity->ftid => $entity));
+    $cache = cache_get($cid, 'cache_field');
+    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
+
+    // Delete, and check that the cache entry is wiped.
+    field_attach_delete($entity_type, $entity);
+    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete'));
+  }
+
+  /**
+   * Test field_attach_validate().
+   *
+   * Verify that field_attach_validate() invokes the correct
+   * hook_field_validate.
+   */
+  function testFieldAttachValidate() {
+    $entity_type = 'test_entity';
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+    $langcode = LANGUAGE_NONE;
+
+    // Set up values to generate errors
+    $values = array();
+    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+      $values[$delta]['value'] = -1;
+    }
+    // Arrange for item 1 not to generate an error
+    $values[1]['value'] = 1;
+    $entity->{$this->field_name}[$langcode] = $values;
+
+    try {
+      field_attach_validate($entity_type, $entity);
+    }
+    catch (FieldValidationException $e) {
+      $errors = $e->errors;
+    }
+
+    foreach ($values as $delta => $value) {
+      if ($value['value'] != 1) {
+        $this->assertIdentical($errors[$this->field_name][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
+        $this->assertEqual(count($errors[$this->field_name][$langcode][$delta]), 1, "Only one error set on value $delta");
+        unset($errors[$this->field_name][$langcode][$delta]);
+      }
+      else {
+        $this->assertFalse(isset($errors[$this->field_name][$langcode][$delta]), "No error set on value $delta");
+      }
+    }
+    $this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set');
+
+    // Check that cardinality is validated.
+    $entity->{$this->field_name}[$langcode] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+    try {
+      field_attach_validate($entity_type, $entity);
+    }
+    catch (FieldValidationException $e) {
+      $errors = $e->errors;
+    }
+    $this->assertEqual($errors[$this->field_name][$langcode][0][0]['error'], 'field_cardinality', t('Cardinality validation failed.'));
+
+  }
+
+  /**
+   * Test field_attach_form().
+   *
+   * This could be much more thorough, but it does verify that the correct
+   * widgets show up.
+   */
+  function testFieldAttachForm() {
+    $entity_type = 'test_entity';
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+
+    $form = array();
+    $form_state = form_state_defaults();
+    field_attach_form($entity_type, $entity, $form, $form_state);
+
+    $langcode = LANGUAGE_NONE;
+    $this->assertEqual($form[$this->field_name][$langcode]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
+    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+      // field_test_widget uses 'textfield'
+      $this->assertEqual($form[$this->field_name][$langcode][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
+    }
+  }
+
+  /**
+   * Test field_attach_submit().
+   */
+  function testFieldAttachSubmit() {
+    $entity_type = 'test_entity';
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+
+    // Build the form.
+    $form = array();
+    $form_state = form_state_defaults();
+    field_attach_form($entity_type, $entity, $form, $form_state);
+
+    // Simulate incoming values.
+    $values = array();
+    $weights = array();
+    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+      $values[$delta]['value'] = mt_rand(1, 127);
+      // Assign random weight.
+      do {
+        $weight = mt_rand(0, $this->field['cardinality']);
+      } while (in_array($weight, $weights));
+      $weights[$delta] = $weight;
+      $values[$delta]['_weight'] = $weight;
+    }
+    // Leave an empty value. 'field_test' fields are empty if empty().
+    $values[1]['value'] = 0;
+
+    $langcode = LANGUAGE_NONE;
+    // Pretend the form has been built.
+    drupal_prepare_form('field_test_entity_form', $form, $form_state);
+    drupal_process_form('field_test_entity_form', $form, $form_state);
+    $form_state['values'][$this->field_name][$langcode] = $values;
+    field_attach_submit($entity_type, $entity, $form, $form_state);
+
+    asort($weights);
+    $expected_values = array();
+    foreach ($weights as $key => $value) {
+      if ($key != 1) {
+        $expected_values[] = array('value' => $values[$key]['value']);
+      }
+    }
+    $this->assertIdentical($entity->{$this->field_name}[$langcode], $expected_values, 'Submit filters empty values');
+  }
+}
+
+class FieldInfoTestCase extends FieldTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Field info tests',
+      'description' => 'Get information about existing fields, instances and bundles.',
+      'group' => 'Field API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('field_test');
+  }
+
+  /**
+   * Test that field types and field definitions are correcly cached.
+   */
+  function testFieldInfo() {
+    // Test that field_test module's fields, widgets, and formatters show up.
+    $field_test_info = field_test_field_info();
+    $formatter_info = field_test_field_formatter_info();
+    $widget_info = field_test_field_widget_info();
+    $storage_info = field_test_field_storage_info();
+
+    $info = field_info_field_types();
+    foreach ($field_test_info as $t_key => $field_type) {
+      foreach ($field_type as $key => $val) {
+        $this->assertEqual($info[$t_key][$key], $val, t("Field type $t_key key $key is $val"));
+      }
+      $this->assertEqual($info[$t_key]['module'], 'field_test',  t("Field type field_test module appears"));
+    }
+
+    $info = field_info_formatter_types();
+    foreach ($formatter_info as $f_key => $formatter) {
+      foreach ($formatter as $key => $val) {
+        $this->assertEqual($info[$f_key][$key], $val, t("Formatter type $f_key key $key is $val"));
+      }
+      $this->assertEqual($info[$f_key]['module'], 'field_test',  t("Formatter type field_test module appears"));
+    }
+
+    $info = field_info_widget_types();
+    foreach ($widget_info as $w_key => $widget) {
+      foreach ($widget as $key => $val) {
+        $this->assertEqual($info[$w_key][$key], $val, t("Widget type $w_key key $key is $val"));
+      }
+      $this->assertEqual($info[$w_key]['module'], 'field_test',  t("Widget type field_test module appears"));
+    }
+
+    $info = field_info_storage_types();
+    foreach ($storage_info as $s_key => $storage) {
+      foreach ($storage as $key => $val) {
+        $this->assertEqual($info[$s_key][$key], $val, t("Storage type $s_key key $key is $val"));
+      }
+      $this->assertEqual($info[$s_key]['module'], 'field_test',  t("Storage type field_test module appears"));
+    }
+
+    // Verify that no unexpected instances exist.
+    $core_fields = field_info_fields();
+    $instances = field_info_instances('test_entity', 'test_bundle');
+    $this->assertTrue(empty($instances), t('With no instances, info bundles is empty.'));
+
+    // Create a field, verify it shows up.
+    $field = array(
+      'field_name' => drupal_strtolower($this->randomName()),
+      'type' => 'test_field',
+    );
+    field_create_field($field);
+    $fields = field_info_fields();
+    $this->assertEqual(count($fields), count($core_fields) + 1, t('One new field exists'));
+    $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], t('info fields contains field name'));
+    $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], t('info fields contains field type'));
+    $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', t('info fields contains field module'));
+    $settings = array('test_field_setting' => 'dummy test string');
+    foreach ($settings as $key => $val) {
+      $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, t("Field setting $key has correct default value $val"));
+    }
+    $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, t('info fields contains cardinality 1'));
+    $this->assertEqual($fields[$field['field_name']]['active'], 1, t('info fields contains active 1'));
+
+    // Create an instance, verify that it shows up
+    $instance = array(
+      'field_name' => $field['field_name'],
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'label' => $this->randomName(),
+      'description' => $this->randomName(),
+      'weight' => mt_rand(0, 127),
+      // test_field has no instance settings
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'settings' => array(
+          'test_setting' => 999)));
+    field_create_instance($instance);
+
+    $instances = field_info_instances('test_entity', $instance['bundle']);
+    $this->assertEqual(count($instances), 1, t('One instance shows up in info when attached to a bundle.'));
+    $this->assertTrue($instance < $instances[$instance['field_name']], t('Instance appears in info correctly'));
+  }
+
+  /**
+   * Test that cached field definitions are ready for current runtime context.
+   */
+  function testFieldPrepare() {
+    $field_definition = array(
+      'field_name' => 'field',
+      'type' => 'test_field',
+    );
+    field_create_field($field_definition);
+
+    // Simulate a stored field definition missing a field setting (e.g. a
+    // third-party module adding a new field setting has been enabled, and
+    // existing fields do not know the setting yet).
+    $data = db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']))->fetchField();
+    $data = unserialize($data);
+    $data['settings'] = array();
+    db_update('field_config')
+      ->fields(array('data' => serialize($data)))
+      ->condition('field_name', $field_definition['field_name'])
+      ->execute();
+
+    field_cache_clear();
+
+    // Read the field back.
+    $field = field_info_field($field_definition['field_name']);
+
+    // Check that all expected settings are in place.
+    $field_type = field_info_field_types($field_definition['type']);
+    $this->assertIdentical($field['settings'], $field_type['settings'], t('All expected default field settings are present.'));
+  }
+
+  /**
+   * Test that cached instance definitions are ready for current runtime context.
+   */
+  function testInstancePrepare() {
+    $field_definition = array(
+      'field_name' => 'field',
+      'type' => 'test_field',
+    );
+    field_create_field($field_definition);
+    $instance_definition = array(
+      'field_name' => $field_definition['field_name'],
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+    );
+    field_create_instance($instance_definition);
+
+    // Simulate a stored instance definition missing various settings (e.g. a
+    // third-party module adding instance, widget or display settings has been
+    // enabled, but existing instances do not know the new settings).
+    $data = db_query('SELECT data FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $instance_definition['field_name'], ':bundle' => $instance_definition['bundle']))->fetchField();
+    $data = unserialize($data);
+    $data['settings'] = array();
+    $data['widget']['settings'] = 'unavailable_widget';
+    $data['widget']['settings'] = array();
+    $data['display']['default']['type'] = 'unavailable_formatter';
+    $data['display']['default']['settings'] = array();
+    db_update('field_config_instance')
+      ->fields(array('data' => serialize($data)))
+      ->condition('field_name', $instance_definition['field_name'])
+      ->condition('bundle', $instance_definition['bundle'])
+      ->execute();
+
+    field_cache_clear();
+
+    // Read the instance back.
+    $instance = field_info_instance($instance_definition['entity_type'], $instance_definition['field_name'], $instance_definition['bundle']);
+
+    // Check that all expected instance settings are in place.
+    $field_type = field_info_field_types($field_definition['type']);
+    $this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
+
+    // Check that the default widget is used and expected settings are in place.
+    $this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
+    $widget_type = field_info_widget_types($instance['widget']['type']);
+    $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
+
+    // Check that display settings are set for the 'default' mode.
+    $display = $instance['display']['default'];
+    $this->assertIdentical($display['type'], $field_type['default_formatter'], t("Formatter is set for the 'default' view mode"));
+    $formatter_type = field_info_formatter_types($display['type']);
+    $this->assertIdentical($display['settings'], $formatter_type['settings'] , t("Formatter settings are set for the 'default' view mode"));
+  }
+
+  /**
+   * Test that instances on disabled entity types are filtered out.
+   */
+  function testInstanceDisabledEntityType() {
+    // For this test the field type and the entity type must be exposed by
+    // different modules.
+    $field_definition = array(
+      'field_name' => 'field',
+      'type' => 'test_field',
+    );
+    field_create_field($field_definition);
+    $instance_definition = array(
+      'field_name' => 'field',
+      'entity_type' => 'comment',
+      'bundle' => 'comment_node_article',
+    );
+    field_create_instance($instance_definition);
+
+    // Disable coment module. This clears field_info cache.
+    module_disable(array('comment'));
+    $this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), t('No instances are returned on disabled entity types.'));
+  }
+
+  /**
+   * Test that the field_info settings convenience functions work.
+   */
+  function testSettingsInfo() {
+    $info = field_test_field_info();
+    foreach ($info as $type => $data) {
+      $this->assertIdentical(field_info_field_settings($type), $data['settings'], "field_info_field_settings returns {$type}'s field settings");
+      $this->assertIdentical(field_info_instance_settings($type), $data['instance_settings'], "field_info_field_settings returns {$type}'s field instance settings");
+    }
+
+    $info = field_test_field_widget_info();
+    foreach ($info as $type => $data) {
+      $this->assertIdentical(field_info_widget_settings($type), $data['settings'], "field_info_widget_settings returns {$type}'s widget settings");
+    }
+
+    $info = field_test_field_formatter_info();
+    foreach ($info as $type => $data) {
+      $this->assertIdentical(field_info_formatter_settings($type), $data['settings'], "field_info_formatter_settings returns {$type}'s formatter settings");
+    }
+  }
+}
+
+class FieldFormTestCase extends FieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field form tests',
+      'description' => 'Test Field form handling.',
+      'group' => 'Field API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('field_test');
+
+    $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+    $this->drupalLogin($web_user);
+
+    $this->field_single = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field');
+    $this->field_multiple = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field', 'cardinality' => 4);
+    $this->field_unlimited = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
+
+    $this->instance = array(
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_instance_setting' => $this->randomName(),
+      ),
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'label' => 'Test Field',
+        'settings' => array(
+          'test_widget_setting' => $this->randomName(),
+        )
+      )
+    );
+  }
+
+  function testFieldFormSingle() {
+    $this->field = $this->field_single;
+    $this->field_name = $this->field['field_name'];
+    $this->instance['field_name'] = $this->field_name;
+    field_create_field($this->field);
+    field_create_instance($this->instance);
+    $langcode = LANGUAGE_NONE;
+
+    // Display creation form.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
+    $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
+    // TODO : check that the widget is populated with default value ?
+
+    // Submit with invalid value (field-level validation).
+    $edit = array("{$this->field_name}[$langcode][0][value]" => -1);
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertRaw(t('%name does not accept the value -1.', array('%name' => $this->instance['label'])), 'Field validation fails with invalid input.');
+    // TODO : check that the correct field is flagged for error.
+
+    // Create an entity
+    $value = mt_rand(1, 127);
+    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
+    $this->drupalPost(NULL, $edit, t('Save'));
+    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
+    $id = $match[1];
+    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+    $entity = field_test_entity_test_load($id);
+    $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved');
+
+    // Display edit form.
+    $this->drupalGet('test-entity/' . $id . '/edit');
+    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", $value, 'Widget is displayed with the correct default value');
+    $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
+
+    // Update the entity.
+    $value = mt_rand(1, 127);
+    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
+    $entity = field_test_entity_test_load($id);
+    $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was updated');
+
+    // Empty the field.
+    $value = '';
+    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
+    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
+    $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
+    $entity = field_test_entity_test_load($id);
+    $this->assertIdentical($entity->{$this->field_name}, array(), 'Field was emptied');
+
+  }
+
+  function testFieldFormSingleRequired() {
+    $this->field = $this->field_single;
+    $this->field_name = $this->field['field_name'];
+    $this->instance['field_name'] = $this->field_name;
+    $this->instance['required'] = TRUE;
+    field_create_field($this->field);
+    field_create_instance($this->instance);
+    $langcode = LANGUAGE_NONE;
+
+    // Submit with missing required value.
+    $edit = array();
+    $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
+    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+
+    // Create an entity
+    $value = mt_rand(1, 127);
+    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
+    $this->drupalPost(NULL, $edit, t('Save'));
+    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
+    $id = $match[1];
+    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+    $entity = field_test_entity_test_load($id);
+    $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved');
+
+    // Edit with missing required value.
+    $value = '';
+    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
+    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
+    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+  }
+
+//  function testFieldFormMultiple() {
+//    $this->field = $this->field_multiple;
+//    $this->field_name = $this->field['field_name'];
+//    $this->instance['field_name'] = $this->field_name;
+//    field_create_field($this->field);
+//    field_create_instance($this->instance);
+//  }
+
+  function testFieldFormUnlimited() {
+    $this->field = $this->field_unlimited;
+    $this->field_name = $this->field['field_name'];
+    $this->instance['field_name'] = $this->field_name;
+    field_create_field($this->field);
+    field_create_instance($this->instance);
+    $langcode = LANGUAGE_NONE;
+
+    // Display creation form -> 1 widget.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
+    $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
+
+    // Press 'add more' button -> 2 widgets.
+    $this->drupalPost(NULL, array(), t('Add another item'));
+    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
+    $this->assertFieldByName("{$this->field_name}[$langcode][1][value]", '', 'New widget is displayed');
+    $this->assertNoField("{$this->field_name}[$langcode][2][value]", 'No extraneous widget is displayed');
+    // TODO : check that non-field inpurs are preserved ('title')...
+
+    // Yet another time so that we can play with more values -> 3 widgets.
+    $this->drupalPost(NULL, array(), t('Add another item'));
+
+    // Prepare values and weights.
+    $count = 3;
+    $delta_range = $count - 1;
+    $values = $weights = $pattern = $expected_values = $edit = array();
+    for ($delta = 0; $delta <= $delta_range; $delta++) {
+      // Assign unique random weights.
+      do {
+        $weight = mt_rand(-$delta_range, $delta_range);
+      } while (in_array($weight, $weights));
+      $value = mt_rand(1, 127);
+      $edit["$this->field_name[$langcode][$delta][value]"] = $value;
+      $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
+      // We'll need three slightly different formats to check the values.
+      $values[$delta] = $value;
+      $weights[$delta] = $weight;
+      $field_values[$weight]['value'] = (string) $value;
+      $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
+    }
+
+    // Press 'add more' button -> 4 widgets
+    $this->drupalPost(NULL, $edit, t('Add another item'));
+    for ($delta = 0; $delta <= $delta_range; $delta++) {
+      $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
+      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
+    }
+    ksort($pattern);
+    $pattern = implode('.*', array_values($pattern));
+    $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
+    $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
+    $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
+    $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
+
+    // Submit the form and create the entity.
+    $this->drupalPost(NULL, $edit, t('Save'));
+    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
+    $id = $match[1];
+    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+    $entity = field_test_entity_test_load($id);
+    ksort($field_values);
+    $field_values = array_values($field_values);
+    $this->assertIdentical($entity->{$this->field_name}[$langcode], $field_values, 'Field values were saved in the correct order');
+
+    // Display edit form: check that the expected number of widgets is
+    // displayed, with correct values change values, reorder, leave an empty
+    // value in the middle.
+    // Submit: check that the entity is updated with correct values
+    // Re-submit: check that the field can be emptied.
+
+    // Test with several multiple fields in a form
+  }
+
+  function testFieldFormJSAddMore() {
+    $this->field = $this->field_unlimited;
+    $this->field_name = $this->field['field_name'];
+    $this->instance['field_name'] = $this->field_name;
+    field_create_field($this->field);
+    field_create_instance($this->instance);
+    $langcode = LANGUAGE_NONE;
+
+    // Display creation form -> 1 widget.
+    $this->drupalGet('test-entity/add/test-bundle');
+
+    // Press 'add more' button a couple times -> 3 widgets.
+    // drupalPostAJAX() will not work iteratively, so we add those through
+    // non-JS submission.
+    $this->drupalPost(NULL, array(), t('Add another item'));
+    $this->drupalPost(NULL, array(), t('Add another item'));
+
+    // Prepare values and weights.
+    $count = 3;
+    $delta_range = $count - 1;
+    $values = $weights = $pattern = $expected_values = $edit = array();
+    for ($delta = 0; $delta <= $delta_range; $delta++) {
+      // Assign unique random weights.
+      do {
+        $weight = mt_rand(-$delta_range, $delta_range);
+      } while (in_array($weight, $weights));
+      $value = mt_rand(1, 127);
+      $edit["$this->field_name[$langcode][$delta][value]"] = $value;
+      $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
+      // We'll need three slightly different formats to check the values.
+      $values[$delta] = $value;
+      $weights[$delta] = $weight;
+      $field_values[$weight]['value'] = (string) $value;
+      $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
+    }
+    // Press 'add more' button through AJAX, and place the expected HTML result
+    // as the tested content.
+    $commands = $this->drupalPostAJAX(NULL, $edit, $this->field_name . '_add_more');
+    $this->content = $commands[1]['data'];
+
+    for ($delta = 0; $delta <= $delta_range; $delta++) {
+      $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
+      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
+    }
+    ksort($pattern);
+    $pattern = implode('.*', array_values($pattern));
+    $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
+    $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
+    $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
+    $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
+  }
+
+  /**
+   * Tests widgets handling multiple values.
+   */
+  function testFieldFormMultipleWidget() {
+    // Create a field with fixed cardinality and an instance using a multiple
+    // widget.
+    $this->field = $this->field_multiple;
+    $this->field_name = $this->field['field_name'];
+    $this->instance['field_name'] = $this->field_name;
+    $this->instance['widget']['type'] = 'test_field_widget_multiple';
+    field_create_field($this->field);
+    field_create_instance($this->instance);
+    $langcode = LANGUAGE_NONE;
+
+    // Display creation form.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertFieldByName("{$this->field_name}[$langcode]", '', t('Widget is displayed.'));
+
+    // Create entity with three values.
+    $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3');
+    $this->drupalPost(NULL, $edit, t('Save'));
+    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
+    $id = $match[1];
+
+    // Check that the values were saved.
+    $entity_init = field_test_create_stub_entity($id);
+    $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3));
+
+    // Display the form, check that the values are correctly filled in.
+    $this->drupalGet('test-entity/' . $id . '/edit');
+    $this->assertFieldByName("{$this->field_name}[$langcode]", '1, 2, 3', t('Widget is displayed.'));
+
+    // Submit the form with more values than the field accepts.
+    $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3, 4, 5');
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertRaw('this field cannot hold more than 4 values', t('Form validation failed.'));
+    // Check that the field values were not submitted.
+    $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3));
+  }
+
+  /**
+   * Tests fields with no 'edit' access.
+   */
+  function testFieldFormAccess() {
+    // Create a "regular" field.
+    $field = $this->field_single;
+    $field_name = $field['field_name'];
+    $instance = $this->instance;
+    $instance['field_name'] = $field_name;
+    field_create_field($field);
+    field_create_instance($instance);
+
+    // Create a field with no edit access - see field_test_field_access().
+    $field_no_access = array(
+      'field_name' => 'field_no_edit_access',
+      'type' => 'test_field',
+    );
+    $field_name_no_access = $field_no_access['field_name'];
+    $instance_no_access = array(
+      'field_name' => $field_name_no_access,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'default_value' => array(0 => array('value' => 99)),
+    );
+    field_create_field($field_no_access);
+    field_create_instance($instance_no_access);
+
+    $langcode = LANGUAGE_NONE;
+
+    // Display creation form.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertNoFieldByName("{$field_name_no_access}[$langcode][0][value]", '', t('Widget is not displayed if field access is denied.'));
+
+    // Create entity.
+    $edit = array("{$field_name}[$langcode][0][value]" => 1);
+    $this->drupalPost(NULL, $edit, t('Save'));
+    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
+    $id = $match[1];
+
+    // Check that the default value was saved.
+    $entity = field_test_entity_test_load($id);
+    $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('Default value was saved for the field with no edit access.'));
+    $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 1, t('Entered value vas saved for the field with edit access.'));
+
+    // Create a new revision.
+    $edit = array("{$field_name}[$langcode][0][value]" => 2, 'revision' => TRUE);
+    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
+
+    // Check that the new revision has the expected values.
+    $entity = field_test_entity_test_load($id);
+    $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.'));
+    $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
+
+    // Check that the revision is also saved in the revisions table.
+    $entity = field_test_entity_test_load($id, $entity->ftvid);
+    $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.'));
+    $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
+  }
+}
+
+class FieldDisplayAPITestCase extends FieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field Display API tests',
+      'description' => 'Test the display API.',
+      'group' => 'Field API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('field_test');
+
+    // Create a field and instance.
+    $this->field_name = 'test_field';
+    $this->label = $this->randomName();
+    $this->cardinality = 4;
+
+    $this->field = array(
+      'field_name' => $this->field_name,
+      'type' => 'test_field',
+      'cardinality' => $this->cardinality,
+    );
+    $this->instance = array(
+      'field_name' => $this->field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'label' => $this->label,
+      'display' => array(
+        'default' => array(
+          'type' => 'field_test_default',
+          'settings' => array(
+            'test_formatter_setting' => $this->randomName(),
+          ),
+        ),
+        'teaser' => array(
+          'type' => 'field_test_default',
+          'settings' => array(
+            'test_formatter_setting' => $this->randomName(),
+          ),
+        ),
+      ),
+    );
+    field_create_field($this->field);
+    field_create_instance($this->instance);
+
+    // Create an entity with values.
+    $this->values = $this->_generateTestFieldValues($this->cardinality);
+    $this->entity = field_test_create_stub_entity();
+    $this->is_new = TRUE;
+    $this->entity->{$this->field_name}[LANGUAGE_NONE] = $this->values;
+    field_test_entity_save($this->entity);
+  }
+
+  /**
+   * Test the field_view_field() function.
+   */
+  function testFieldViewField() {
+    // No display settings: check that default display settings are used.
+    $output = field_view_field('test_entity', $this->entity, $this->field_name);
+    $this->drupalSetContent(drupal_render($output));
+    $settings = field_info_formatter_settings('field_test_default');
+    $setting = $settings['test_formatter_setting'];
+    $this->assertText($this->label, t('Label was displayed.'));
+    foreach($this->values as $delta => $value) {
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Check that explicit display settings are used.
+    $display = array(
+      'label' => 'hidden',
+      'type' => 'field_test_multiple',
+      'settings' => array(
+        'test_formatter_setting_multiple' => $this->randomName(),
+      ),
+    );
+    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
+    $this->drupalSetContent(drupal_render($output));
+    $setting = $display['settings']['test_formatter_setting_multiple'];
+    $this->assertNoText($this->label, t('Label was not displayed.'));
+    $array = array();
+    foreach($this->values as $delta => $value) {
+      $array[] = $delta . ':' . $value['value'];
+    }
+    $this->assertText($setting . '|' . implode('|', $array), t('Values were displayed with expected setting.'));
+
+    // Check the prepare_view steps are invoked.
+    $display = array(
+      'label' => 'hidden',
+      'type' => 'field_test_with_prepare_view',
+      'settings' => array(
+        'test_formatter_setting_additional' => $this->randomName(),
+      ),
+    );
+    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
+    $view = drupal_render($output);
+    $this->drupalSetContent($view);
+    $setting = $display['settings']['test_formatter_setting_additional'];
+    $this->assertNoText($this->label, t('Label was not displayed.'));
+    foreach ($this->values as $delta => $value) {
+      $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // View mode: check that display settings specified in the instance are
+    // used.
+    $output = field_view_field('test_entity', $this->entity, $this->field_name, 'teaser');
+    $this->drupalSetContent(drupal_render($output));
+    $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting'];
+    $this->assertText($this->label, t('Label was displayed.'));
+    foreach($this->values as $delta => $value) {
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Unknown view mode: check that display settings for 'default' view mode
+    // are used.
+    $output = field_view_field('test_entity', $this->entity, $this->field_name, 'unknown_view_mode');
+    $this->drupalSetContent(drupal_render($output));
+    $setting = $this->instance['display']['default']['settings']['test_formatter_setting'];
+    $this->assertText($this->label, t('Label was displayed.'));
+    foreach($this->values as $delta => $value) {
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+  }
+
+  /**
+   * Test the field_view_value() function.
+   */
+  function testFieldViewValue() {
+    // No display settings: check that default display settings are used.
+    $settings = field_info_formatter_settings('field_test_default');
+    $setting = $settings['test_formatter_setting'];
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item);
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Check that explicit display settings are used.
+    $display = array(
+      'type' => 'field_test_multiple',
+      'settings' => array(
+        'test_formatter_setting_multiple' => $this->randomName(),
+      ),
+    );
+    $setting = $display['settings']['test_formatter_setting_multiple'];
+    $array = array();
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|0:' .  $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Check that prepare_view steps are invoked.
+    $display = array(
+      'type' => 'field_test_with_prepare_view',
+      'settings' => array(
+        'test_formatter_setting_additional' => $this->randomName(),
+      ),
+    );
+    $setting = $display['settings']['test_formatter_setting_additional'];
+    $array = array();
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' .  $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // View mode: check that display settings specified in the instance are
+    // used.
+    $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting'];
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'teaser');
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Unknown view mode: check that display settings for 'default' view mode
+    // are used.
+    $setting = $this->instance['display']['default']['settings']['test_formatter_setting'];
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'unknown_view_mode');
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+  }
+}
+
+class FieldCrudTestCase extends FieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field CRUD tests',
+      'description' => 'Test field create, read, update, and delete.',
+      'group' => 'Field API',
+    );
+  }
+
+  function setUp() {
+    // field_update_field() tests use number.module
+    parent::setUp('field_test', 'number');
+  }
+
+  // TODO : test creation with
+  // - a full fledged $field structure, check that all the values are there
+  // - a minimal $field structure, check all default values are set
+  // defer actual $field comparison to a helper function, used for the two cases above
+
+  /**
+   * Test the creation of a field.
+   */
+  function testCreateField() {
+    $field_definition = array(
+      'field_name' => 'field_2',
+      'type' => 'test_field',
+    );
+    field_test_memorize();
+    $field_definition = field_create_field($field_definition);
+    $mem = field_test_memorize();
+    $this->assertIdentical($mem['field_test_field_create_field'][0][0], $field_definition, 'hook_field_create_field() called with correct arguments.');
+
+    // Read the raw record from the {field_config_instance} table.
+    $result = db_query('SELECT * FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']));
+    $record = $result->fetchAssoc();
+    $record['data'] = unserialize($record['data']);
+
+    // Ensure that basic properties are preserved.
+    $this->assertEqual($record['field_name'], $field_definition['field_name'], t('The field name is properly saved.'));
+    $this->assertEqual($record['type'], $field_definition['type'], t('The field type is properly saved.'));
+
+    // Ensure that cardinality defaults to 1.
+    $this->assertEqual($record['cardinality'], 1, t('Cardinality defaults to 1.'));
+
+    // Ensure that default settings are present.
+    $field_type = field_info_field_types($field_definition['type']);
+    $this->assertIdentical($record['data']['settings'], $field_type['settings'], t('Default field settings have been written.'));
+
+    // Ensure that default storage was set.
+    $this->assertEqual($record['storage_type'], variable_get('field_storage_default'), t('The field type is properly saved.'));
+
+    // Guarantee that the name is unique.
+    try {
+      field_create_field($field_definition);
+      $this->fail(t('Cannot create two fields with the same name.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create two fields with the same name.'));
+    }
+
+    // Check that field type is required.
+    try {
+      $field_definition = array(
+        'field_name' => 'field_1',
+      );
+      field_create_field($field_definition);
+      $this->fail(t('Cannot create a field with no type.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create a field with no type.'));
+    }
+
+    // Check that field name is required.
+    try {
+      $field_definition = array(
+        'type' => 'test_field'
+      );
+      field_create_field($field_definition);
+      $this->fail(t('Cannot create an unnamed field.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create an unnamed field.'));
+    }
+
+    // Check that field name must start with a letter or _.
+    try {
+      $field_definition = array(
+        'field_name' => '2field_2',
+        'type' => 'test_field',
+      );
+      field_create_field($field_definition);
+      $this->fail(t('Cannot create a field with a name starting with a digit.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create a field with a name starting with a digit.'));
+    }
+
+    // Check that field name must only contain lowercase alphanumeric or _.
+    try {
+      $field_definition = array(
+        'field_name' => 'field#_3',
+        'type' => 'test_field',
+      );
+      field_create_field($field_definition);
+      $this->fail(t('Cannot create a field with a name containing an illegal character.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create a field with a name containing an illegal character.'));
+    }
+
+    // Check that field name cannot be longer than 32 characters long.
+    try {
+      $field_definition = array(
+        'field_name' => '_12345678901234567890123456789012',
+        'type' => 'test_field',
+      );
+      field_create_field($field_definition);
+      $this->fail(t('Cannot create a field with a name longer than 32 characters.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create a field with a name longer than 32 characters.'));
+    }
+
+    // Check that field name can not be an entity key.
+    // "ftvid" is known as an entity key from the "test_entity" type.
+    try {
+      $field_definition = array(
+        'type' => 'test_field',
+        'field_name' => 'ftvid',
+      );
+      $field = field_create_field($field_definition);
+      $this->fail(t('Cannot create a field bearing the name of an entity key.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create a field bearing the name of an entity key.'));
+    }
+  }
+
+  /**
+   * Test failure to create a field.
+   */
+  function testCreateFieldFail() {
+    $field_name = 'duplicate';
+    $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
+    $query = db_select('field_config')->condition('field_name', $field_name)->countQuery();
+
+    // The field does not appear in field_config.
+    $count = $query->execute()->fetchField();
+    $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
+
+    // Try to create the field.
+    try {
+      $field = field_create_field($field_definition);
+      $this->assertTrue(FALSE, 'Field creation (correctly) fails.');
+    }
+    catch (Exception $e) {
+      $this->assertTrue(TRUE, 'Field creation (correctly) fails.');
+    }
+
+    // The field does not appear in field_config.
+    $count = $query->execute()->fetchField();
+    $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
+  }
+
+  /**
+   * Test reading back a field definition.
+   */
+  function testReadField() {
+    $field_definition = array(
+      'field_name' => 'field_1',
+      'type' => 'test_field',
+    );
+    field_create_field($field_definition);
+
+    // Read the field back.
+    $field = field_read_field($field_definition['field_name']);
+    $this->assertTrue($field_definition < $field, t('The field was properly read.'));
+  }
+
+  /**
+   * Test creation of indexes on data column.
+   */
+  function testFieldIndexes() {
+    // Check that indexes specified by the field type are used by default.
+    $field_definition = array(
+      'field_name' => 'field_1',
+      'type' => 'test_field',
+    );
+    field_create_field($field_definition);
+    $field = field_read_field($field_definition['field_name']);
+    $expected_indexes = array('value' => array('value'));
+    $this->assertEqual($field['indexes'], $expected_indexes, t('Field type indexes saved by default'));
+
+    // Check that indexes specified by the field definition override the field
+    // type indexes.
+    $field_definition = array(
+      'field_name' => 'field_2',
+      'type' => 'test_field',
+      'indexes' => array(
+        'value' => array(),
+      ),
+    );
+    field_create_field($field_definition);
+    $field = field_read_field($field_definition['field_name']);
+    $expected_indexes = array('value' => array());
+    $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes override field type indexes'));
+
+    // Check that indexes specified by the field definition add to the field
+    // type indexes.
+    $field_definition = array(
+      'field_name' => 'field_3',
+      'type' => 'test_field',
+      'indexes' => array(
+        'value_2' => array('value'),
+      ),
+    );
+    field_create_field($field_definition);
+    $field = field_read_field($field_definition['field_name']);
+    $expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
+    $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes are merged with field type indexes'));
+  }
+
+  /**
+   * Test the deletion of a field.
+   */
+  function testDeleteField() {
+    // TODO: Also test deletion of the data stored in the field ?
+
+    // Create two fields (so we can test that only one is deleted).
+    $this->field = array('field_name' => 'field_1', 'type' => 'test_field');
+    field_create_field($this->field);
+    $this->another_field = array('field_name' => 'field_2', 'type' => 'test_field');
+    field_create_field($this->another_field);
+
+    // Create instances for each.
+    $this->instance_definition = array(
+      'field_name' => $this->field['field_name'],
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'widget' => array(
+        'type' => 'test_field_widget',
+      ),
+    );
+    field_create_instance($this->instance_definition);
+    $this->another_instance_definition = $this->instance_definition;
+    $this->another_instance_definition['field_name'] = $this->another_field['field_name'];
+    field_create_instance($this->another_instance_definition);
+
+    // Test that the first field is not deleted, and then delete it.
+    $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
+    $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field is not marked for deletion.'));
+    field_delete_field($this->field['field_name']);
+
+    // Make sure that the field is marked as deleted when it is specifically
+    // loaded.
+    $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
+    $this->assertTrue(!empty($field['deleted']), t('A deleted field is marked for deletion.'));
+
+    // Make sure that this field's instance is marked as deleted when it is
+    // specifically loaded.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
+    $this->assertTrue(!empty($instance['deleted']), t('An instance for a deleted field is marked for deletion.'));
+
+    // Try to load the field normally and make sure it does not show up.
+    $field = field_read_field($this->field['field_name']);
+    $this->assertTrue(empty($field), t('A deleted field is not loaded by default.'));
+
+    // Try to load the instance normally and make sure it does not show up.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertTrue(empty($instance), t('An instance for a deleted field is not loaded by default.'));
+
+    // Make sure the other field (and its field instance) are not deleted.
+    $another_field = field_read_field($this->another_field['field_name']);
+    $this->assertTrue(!empty($another_field) && empty($another_field['deleted']), t('A non-deleted field is not marked for deletion.'));
+    $another_instance = field_read_instance('test_entity', $this->another_instance_definition['field_name'], $this->another_instance_definition['bundle']);
+    $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('An instance of a non-deleted field is not marked for deletion.'));
+
+    // Try to create a new field the same name as a deleted field and
+    // write data into it.
+    field_create_field($this->field);
+    field_create_instance($this->instance_definition);
+    $field = field_read_field($this->field['field_name']);
+    $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field with a previously used name is created.'));
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new instance for a previously used field name is created.'));
+
+    // Save an entity with data for the field
+    $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+    $langcode = LANGUAGE_NONE;
+    $values[0]['value'] = mt_rand(1, 127);
+    $entity->{$field['field_name']}[$langcode] = $values;
+    $entity_type = 'test_entity';
+    field_attach_insert('test_entity', $entity);
+
+    // Verify the field is present on load
+    $entity = field_test_create_stub_entity(0, 0, $this->instance_definition['bundle']);
+    field_attach_load($entity_type, array(0 => $entity));
+    $this->assertIdentical(count($entity->{$field['field_name']}[$langcode]), count($values), "Data in previously deleted field saves and loads correctly");
+    foreach ($values as $delta => $value) {
+      $this->assertEqual($entity->{$field['field_name']}[$langcode][$delta]['value'], $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
+    }
+  }
+
+  function testUpdateNonExistentField() {
+    $test_field = array('field_name' => 'does_not_exist', 'type' => 'number_decimal');
+    try {
+      field_update_field($test_field);
+      $this->fail(t('Cannot update a field that does not exist.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot update a field that does not exist.'));
+    }
+  }
+
+  function testUpdateFieldType() {
+    $field = array('field_name' => 'field_type', 'type' => 'number_decimal');
+    $field = field_create_field($field);
+
+    $test_field = array('field_name' => 'field_type', 'type' => 'number_integer');
+    try {
+      field_update_field($test_field);
+      $this->fail(t('Cannot update a field to a different type.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot update a field to a different type.'));
+    }
+  }
+
+  /**
+   * Test updating a field.
+   */
+  function testUpdateField() {
+    // Create a decimal 5.2 field.
+    $field = array('field_name' => 'decimal53', 'type' => 'number_decimal', 'cardinality' => 3, 'settings' => array('precision' => 5, 'scale' => 2));
+    $field = field_create_field($field);
+    $instance = array('field_name' => 'decimal53', 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
+    $instance = field_create_instance($instance);
+
+    // Update it to a deciaml 5.3 field.
+    $field['settings']['scale'] = 3;
+    field_update_field($field);
+
+    // Save values with 2, 3, and 4 decimal places.
+    $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+    $entity->decimal53[LANGUAGE_NONE][0]['value'] = '1.23';
+    $entity->decimal53[LANGUAGE_NONE][1]['value'] = '1.235';
+    $entity->decimal53[LANGUAGE_NONE][2]['value'] = '1.2355';
+    field_attach_insert('test_entity', $entity);
+    $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+
+    // Verify that the updated 5.3 field rounds to 3 decimal places.
+    field_attach_load('test_entity', array(0 => $entity));
+    $this->assertEqual($entity->decimal53[LANGUAGE_NONE][0]['value'], '1.23', t('2 decimal places are left alone'));
+    $this->assertEqual($entity->decimal53[LANGUAGE_NONE][1]['value'], '1.235', t('3 decimal places are left alone'));
+    $this->assertEqual($entity->decimal53[LANGUAGE_NONE][2]['value'], '1.236', t('4 decimal places are rounded to 3'));
+  }
+
+  /**
+   * Test field type modules forbidding an update.
+   */
+  function testUpdateFieldForbid() {
+    $field = array('field_name' => 'forbidden', 'type' => 'test_field', 'settings' => array('changeable' => 0, 'unchangeable' => 0));
+    $field = field_create_field($field);
+    $field['settings']['changeable']++;
+    try {
+      field_update_field($field);
+      $this->pass(t("A changeable setting can be updated."));
+    }
+    catch (FieldException $e) {
+      $this->fail(t("An unchangeable setting cannot be updated."));
+    }
+    $field['settings']['unchangeable']++;
+    try {
+      field_update_field($field);
+      $this->fail(t("An unchangeable setting can be updated."));
+    }
+    catch (FieldException $e) {
+      $this->pass(t("An unchangeable setting cannot be updated."));
+    }
+  }
+
+  /**
+   * Test that fields are properly marked active or inactive.
+   */
+  function testActive() {
+    $field_definition = array(
+      'field_name' => 'field_1',
+      'type' => 'test_field',
+      // For this test, we need a storage backend provided by a different
+      // module than field_test.module.
+      'storage' => array(
+        'type' => 'field_sql_storage',
+      ),
+    );
+    field_create_field($field_definition);
+
+    // Test disabling and enabling:
+    // - the field type module,
+    // - the storage module,
+    // - both.
+    $this->_testActiveHelper($field_definition, array('field_test'));
+    $this->_testActiveHelper($field_definition, array('field_sql_storage'));
+    $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
+  }
+
+  /**
+   * Helper function for testActive().
+   *
+   * Test dependency between a field and a set of modules.
+   *
+   * @param $field_definition
+   *   A field definition.
+   * @param $modules
+   *   An aray of module names. The field will be tested to be inactive as long
+   *   as any of those modules is disabled.
+   */
+  function _testActiveHelper($field_definition, $modules) {
+    $field_name = $field_definition['field_name'];
+
+    // Read the field.
+    $field = field_read_field($field_name);
+    $this->assertTrue($field_definition <= $field, t('The field was properly read.'));
+
+    module_disable($modules, FALSE);
+
+    $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
+    $this->assertTrue(isset($fields[$field_name]) && $field_definition < $field, t('The field is properly read when explicitly fetching inactive fields.'));
+
+    // Re-enable modules one by one, and check that the field is still inactive
+    // while some modules remain disabled.
+    while ($modules) {
+      $field = field_read_field($field_name);
+      $this->assertTrue(empty($field), t('%modules disabled. The field is marked inactive.', array('%modules' => implode(', ', $modules))));
+
+      $module = array_shift($modules);
+      module_enable(array($module), FALSE);
+    }
+
+    // Check that the field is active again after all modules have been
+    // enabled.
+    $field = field_read_field($field_name);
+    $this->assertTrue($field_definition <= $field, t('The field was was marked active.'));
+  }
+}
+
+class FieldInstanceCrudTestCase extends FieldTestCase {
+  protected $field;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Field instance CRUD tests',
+      'description' => 'Create field entities by attaching fields to entities.',
+      'group' => 'Field API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('field_test');
+
+    $this->field = array(
+      'field_name' => drupal_strtolower($this->randomName()),
+      'type' => 'test_field',
+    );
+    field_create_field($this->field);
+    $this->instance_definition = array(
+      'field_name' => $this->field['field_name'],
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+    );
+  }
+
+  // TODO : test creation with
+  // - a full fledged $instance structure, check that all the values are there
+  // - a minimal $instance structure, check all default values are set
+  // defer actual $instance comparison to a helper function, used for the two cases above,
+  // and for testUpdateFieldInstance
+
+  /**
+   * Test the creation of a field instance.
+   */
+  function testCreateFieldInstance() {
+    field_create_instance($this->instance_definition);
+
+    // Read the raw record from the {field_config_instance} table.
+    $result = db_query('SELECT * FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $this->instance_definition['field_name'], ':bundle' => $this->instance_definition['bundle']));
+    $record = $result->fetchAssoc();
+    $record['data'] = unserialize($record['data']);
+
+    $field_type = field_info_field_types($this->field['type']);
+    $widget_type = field_info_widget_types($field_type['default_widget']);
+    $formatter_type = field_info_formatter_types($field_type['default_formatter']);
+
+    // Check that default values are set.
+    $this->assertIdentical($record['data']['required'], FALSE, t('Required defaults to false.'));
+    $this->assertIdentical($record['data']['label'], $this->instance_definition['field_name'], t('Label defaults to field name.'));
+    $this->assertIdentical($record['data']['description'], '', t('Description defaults to empty string.'));
+    $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], t('Default widget has been written.'));
+    $this->assertTrue(isset($record['data']['display']['default']), t('Display for "full" view_mode has been written.'));
+    $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], t('Default formatter for "full" view_mode has been written.'));
+
+    // Check that default settings are set.
+    $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , t('Default instance settings have been written.'));
+    $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , t('Default widget settings have been written.'));
+    $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" view_mode have been written.'));
+
+    // Guarantee that the field/bundle combination is unique.
+    try {
+      field_create_instance($this->instance_definition);
+      $this->fail(t('Cannot create two instances with the same field / bundle combination.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create two instances with the same field / bundle combination.'));
+    }
+
+    // Check that the specified field exists.
+    try {
+      $this->instance_definition['field_name'] = $this->randomName();
+      field_create_instance($this->instance_definition);
+      $this->fail(t('Cannot create an instance of a non-existing field.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create an instance of a non-existing field.'));
+    }
+
+    // Create a field restricted to a specific entity type.
+    $field_restricted = array(
+      'field_name' => drupal_strtolower($this->randomName()),
+      'type' => 'test_field',
+      'entity_types' => array('test_cacheable_entity'),
+    );
+    field_create_field($field_restricted);
+
+    // Check that an instance can be added to an entity type allowed
+    // by the field.
+    try {
+      $instance = $this->instance_definition;
+      $instance['field_name'] = $field_restricted['field_name'];
+      $instance['entity_type'] = 'test_cacheable_entity';
+      field_create_instance($instance);
+      $this->pass(t('Can create an instance on an entity type allowed by the field.'));
+    }
+    catch (FieldException $e) {
+      $this->fail(t('Can create an instance on an entity type allowed by the field.'));
+    }
+
+    // Check that an instance cannot be added to an entity type
+    // forbidden by the field.
+    try {
+      $instance = $this->instance_definition;
+      $instance['field_name'] = $field_restricted['field_name'];
+      field_create_instance($instance);
+      $this->fail(t('Cannot create an instance on an entity type forbidden by the field.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot create an instance on an entity type forbidden by the field.'));
+    }
+
+    // TODO: test other failures.
+  }
+
+  /**
+   * Test reading back an instance definition.
+   */
+  function testReadFieldInstance() {
+    field_create_instance($this->instance_definition);
+
+    // Read the instance back.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertTrue($this->instance_definition < $instance, t('The field was properly read.'));
+  }
+
+  /**
+   * Test the update of a field instance.
+   */
+  function testUpdateFieldInstance() {
+    field_create_instance($this->instance_definition);
+    $field_type = field_info_field_types($this->field['type']);
+
+    // Check that basic changes are saved.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $instance['required'] = !$instance['required'];
+    $instance['label'] = $this->randomName();
+    $instance['description'] = $this->randomName();
+    $instance['settings']['test_instance_setting'] = $this->randomName();
+    $instance['widget']['settings']['test_widget_setting'] =$this->randomName();
+    $instance['widget']['weight']++;
+    $instance['display']['default']['settings']['test_formatter_setting'] = $this->randomName();
+    $instance['display']['default']['weight']++;
+    field_update_instance($instance);
+
+    $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertEqual($instance['required'], $instance_new['required'], t('"required" change is saved'));
+    $this->assertEqual($instance['label'], $instance_new['label'], t('"label" change is saved'));
+    $this->assertEqual($instance['description'], $instance_new['description'], t('"description" change is saved'));
+    $this->assertEqual($instance['widget']['settings']['test_widget_setting'], $instance_new['widget']['settings']['test_widget_setting'], t('Widget setting change is saved'));
+    $this->assertEqual($instance['widget']['weight'], $instance_new['widget']['weight'], t('Widget weight change is saved'));
+    $this->assertEqual($instance['display']['default']['settings']['test_formatter_setting'], $instance_new['display']['default']['settings']['test_formatter_setting'], t('Formatter setting change is saved'));
+    $this->assertEqual($instance['display']['default']['weight'], $instance_new['display']['default']['weight'], t('Widget weight change is saved'));
+
+    // Check that changing widget and formatter types updates the default settings.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $instance['widget']['type'] = 'test_field_widget_multiple';
+    $instance['display']['default']['type'] = 'field_test_multiple';
+    field_update_instance($instance);
+
+    $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertEqual($instance['widget']['type'], $instance_new['widget']['type'] , t('Widget type change is saved.'));
+    $settings = field_info_widget_settings($instance_new['widget']['type']);
+    $this->assertIdentical($settings, array_intersect_key($instance_new['widget']['settings'], $settings) , t('Widget type change updates default settings.'));
+    $this->assertEqual($instance['display']['default']['type'], $instance_new['display']['default']['type'] , t('Formatter type change is saved.'));
+    $info = field_info_formatter_types($instance_new['display']['default']['type']);
+    $settings = $info['settings'];
+    $this->assertIdentical($settings, array_intersect_key($instance_new['display']['default']['settings'], $settings) , t('Changing formatter type updates default settings.'));
+
+    // Check that adding a new view mode is saved and gets default settings.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $instance['display']['teaser'] = array();
+    field_update_instance($instance);
+
+    $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertTrue(isset($instance_new['display']['teaser']), t('Display for the new view_mode has been written.'));
+    $this->assertIdentical($instance_new['display']['teaser']['type'], $field_type['default_formatter'], t('Default formatter for the new view_mode has been written.'));
+    $info = field_info_formatter_types($instance_new['display']['teaser']['type']);
+    $settings = $info['settings'];
+    $this->assertIdentical($settings, $instance_new['display']['teaser']['settings'] , t('Default formatter settings for the new view_mode have been written.'));
+
+    // TODO: test failures.
+  }
+
+  /**
+   * Test the deletion of a field instance.
+   */
+  function testDeleteFieldInstance() {
+    // TODO: Test deletion of the data stored in the field also.
+    // Need to check that data for a 'deleted' field / instance doesn't get loaded
+    // Need to check data marked deleted is cleaned on cron (not implemented yet...)
+
+    // Create two instances for the same field so we can test that only one
+    // is deleted.
+    field_create_instance($this->instance_definition);
+    $this->another_instance_definition = $this->instance_definition;
+    $this->another_instance_definition['bundle'] .= '_another_bundle';
+    $instance = field_create_instance($this->another_instance_definition);
+
+    // Test that the first instance is not deleted, and then delete it.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
+    $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new field instance is not marked for deletion.'));
+    field_delete_instance($instance);
+
+    // Make sure the instance is marked as deleted when the instance is
+    // specifically loaded.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
+    $this->assertTrue(!empty($instance['deleted']), t('A deleted field instance is marked for deletion.'));
+
+    // Try to load the instance normally and make sure it does not show up.
+    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
+    $this->assertTrue(empty($instance), t('A deleted field instance is not loaded by default.'));
+
+    // Make sure the other field instance is not deleted.
+    $another_instance = field_read_instance('test_entity', $this->another_instance_definition['field_name'], $this->another_instance_definition['bundle']);
+    $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('A non-deleted field instance is not marked for deletion.'));
+  }
+}
+
+/**
+ * Unit test class for the multilanguage fields logic.
+ *
+ * The following tests will check the multilanguage logic of _field_invoke() and
+ * that only the correct values are returned by field_available_languages().
+ */
+class FieldTranslationsTestCase extends FieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field translations tests',
+      'description' => 'Test multilanguage fields logic.',
+      'group' => 'Field API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('locale', 'field_test');
+
+    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+
+    $this->entity_type = 'test_entity';
+
+    $field = array(
+      'field_name' => $this->field_name,
+      'type' => 'test_field',
+      'cardinality' => 4,
+      'translatable' => TRUE,
+    );
+    field_create_field($field);
+    $this->field = field_read_field($this->field_name);
+
+    $instance = array(
+      'field_name' => $this->field_name,
+      'entity_type' => $this->entity_type,
+      'bundle' => 'test_bundle',
+    );
+    field_create_instance($instance);
+    $this->instance = field_read_instance('test_entity', $this->field_name, 'test_bundle');
+
+    require_once DRUPAL_ROOT . '/includes/locale.inc';
+    for ($i = 0; $i < 3; ++$i) {
+      locale_add_language('l' . $i, $this->randomString(), $this->randomString());
+    }
+  }
+
+  /**
+   * Ensures that only valid values are returned by field_available_languages().
+   */
+  function testFieldAvailableLanguages() {
+    // Test 'translatable' fieldable info.
+    field_test_entity_info_translatable('test_entity', FALSE);
+    $field = $this->field;
+    $field['field_name'] .= '_untranslatable';
+
+    // Enable field translations for the entity.
+    field_test_entity_info_translatable('test_entity', TRUE);
+
+    // Test hook_field_languages() invocation on a translatable field.
+    variable_set('field_test_field_available_languages_alter', TRUE);
+    $enabled_languages = field_content_languages();
+    $available_languages = field_available_languages($this->entity_type, $this->field);
+    foreach ($available_languages as $delta => $langcode) {
+      if ($langcode != 'xx' && $langcode != 'en') {
+        $this->assertTrue(in_array($langcode, $enabled_languages), t('%language is an enabled language.', array('%language' => $langcode)));
+      }
+    }
+    $this->assertTrue(in_array('xx', $available_languages), t('%language was made available.', array('%language' => 'xx')));
+    $this->assertFalse(in_array('en', $available_languages), t('%language was made unavailable.', array('%language' => 'en')));
+
+    // Test field_available_languages() behavior for untranslatable fields.
+    $this->field['translatable'] = FALSE;
+    $this->field_name = $this->field['field_name'] = $this->instance['field_name'] = drupal_strtolower($this->randomName() . '_field_name');
+    $available_languages = field_available_languages($this->entity_type, $this->field);
+    $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === LANGUAGE_NONE, t('For untranslatable fields only LANGUAGE_NONE is available.'));
+  }
+
+  /**
+   * Test the multilanguage logic of _field_invoke().
+   */
+  function testFieldInvoke() {
+    $entity_type = 'test_entity';
+    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+
+    // Populate some extra languages to check if _field_invoke() correctly uses
+    // the result of field_available_languages().
+    $values = array();
+    $extra_languages = mt_rand(1, 4);
+    $languages = $available_languages = field_available_languages($this->entity_type, $this->field);
+    for ($i = 0; $i < $extra_languages; ++$i) {
+      $languages[] = $this->randomString(2);
+    }
+
+    // For each given language provide some random values.
+    foreach ($languages as $langcode) {
+      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+        $values[$langcode][$delta]['value'] = mt_rand(1, 127);
+      }
+    }
+    $entity->{$this->field_name} = $values;
+
+    $results = _field_invoke('test_op', $entity_type, $entity);
+    foreach ($results as $langcode => $result) {
+      $hash = hash('sha256', serialize(array($entity_type, $entity, $this->field_name, $langcode, $values[$langcode])));
+      // Check whether the parameters passed to _field_invoke() were correctly
+      // forwarded to the callback function.
+      $this->assertEqual($hash, $result, t('The result for %language is correctly stored.', array('%language' => $langcode)));
+    }
+    $this->assertEqual(count($results), count($available_languages), t('No unavailable language has been processed.'));
+  }
+
+  /**
+   * Test the multilanguage logic of _field_invoke_multiple().
+   */
+  function testFieldInvokeMultiple() {
+    $values = array();
+    $entities = array();
+    $entity_type = 'test_entity';
+    $entity_count = mt_rand(1, 5);
+    $available_languages = field_available_languages($this->entity_type, $this->field);
+
+    for ($id = 1; $id <= $entity_count; ++$id) {
+      $entity = field_test_create_stub_entity($id, $id, $this->instance['bundle']);
+      $languages = $available_languages;
+
+      // Populate some extra languages to check whether _field_invoke()
+      // correctly uses the result of field_available_languages().
+      $extra_languages = mt_rand(1, 4);
+      for ($i = 0; $i < $extra_languages; ++$i) {
+        $languages[] = $this->randomString(2);
+      }
+
+      // For each given language provide some random values.
+      foreach ($languages as $langcode) {
+        for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+          $values[$id][$langcode][$delta]['value'] = mt_rand(1, 127);
+        }
+      }
+      $entity->{$this->field_name} = $values[$id];
+      $entities[$id] = $entity;
+    }
+
+    $grouped_results = _field_invoke_multiple('test_op_multiple', $entity_type, $entities);
+    foreach ($grouped_results as $id => $results) {
+      foreach ($results as $langcode => $result) {
+        $hash = hash('sha256', serialize(array($entity_type, $entities[$id], $this->field_name, $langcode, $values[$id][$langcode])));
+        // Check whether the parameters passed to _field_invoke() were correctly
+        // forwarded to the callback function.
+        $this->assertEqual($hash, $result, t('The result for entity %id/%language is correctly stored.', array('%id' => $id, '%language' => $langcode)));
+      }
+      $this->assertEqual(count($results), count($available_languages), t('No unavailable language has been processed for entity %id.', array('%id' => $id)));
+    }
+  }
+
+  /**
+   * Test translatable fields storage/retrieval.
+   */
+  function testTranslatableFieldSaveLoad() {
+    // Enable field translations for nodes.
+    field_test_entity_info_translatable('node', TRUE);
+    $entity_info = entity_get_info('node');
+    $this->assertTrue(count($entity_info['translation']), t('Nodes are translatable.'));
+
+    // Prepare the field translations.
+    field_test_entity_info_translatable('test_entity', TRUE);
+    $eid = $evid = 1;
+    $entity_type = 'test_entity';
+    $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
+    $field_translations = array();
+    $available_languages = field_available_languages($entity_type, $this->field);
+    $this->assertTrue(count($available_languages) > 1, t('Field is translatable.'));
+    foreach ($available_languages as $langcode) {
+      $field_translations[$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
+    }
+
+    // Save and reload the field translations.
+    $entity->{$this->field_name} = $field_translations;
+    field_attach_insert($entity_type, $entity);
+    unset($entity->{$this->field_name});
+    field_attach_load($entity_type, array($eid => $entity));
+
+    // Check if the correct values were saved/loaded.
+    foreach ($field_translations as $langcode => $items) {
+      $result = TRUE;
+      foreach ($items as $delta => $item) {
+        $result = $result && $item['value'] == $entity->{$this->field_name}[$langcode][$delta]['value'];
+      }
+      $this->assertTrue($result, t('%language translation correctly handled.', array('%language' => $langcode)));
+    }
+  }
+
+  /**
+   * Tests display language logic for translatable fields.
+   */
+  function testFieldDisplayLanguage() {
+    $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $entity_type = 'test_entity';
+
+    // We need an additional field here to properly test display language
+    // suggestions.
+    $field = array(
+      'field_name' => $field_name,
+      'type' => 'test_field',
+      'cardinality' => 2,
+      'translatable' => TRUE,
+    );
+    field_create_field($field);
+
+    $instance = array(
+      'field_name' => $field['field_name'],
+      'entity_type' => $entity_type,
+      'bundle' => 'test_bundle',
+    );
+    field_create_instance($instance);
+
+    $entity = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
+    list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+    $instances = field_info_instances($entity_type, $bundle);
+
+    $enabled_languages = field_content_languages();
+    $languages = array();
+
+    // Generate field translations for languages different from the first
+    // enabled.
+    foreach ($instances as $instance) {
+      $field_name = $instance['field_name'];
+      $field = field_info_field($field_name);
+      do {
+        // Index 0 is reserved for the requested language, this way we ensure
+        // that no field is actually populated with it.
+        $langcode = $enabled_languages[mt_rand(1, count($enabled_languages) - 1)];
+      }
+      while (isset($languages[$langcode]));
+      $languages[$langcode] = TRUE;
+      $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues($field['cardinality']);
+    }
+
+    // Test multiple-fields display languages for untranslatable entities.
+    field_test_entity_info_translatable($entity_type, FALSE);
+    drupal_static_reset('field_language');
+    $requested_language = $enabled_languages[0];
+    $display_language = field_language($entity_type, $entity, NULL, $requested_language);
+    foreach ($instances as $instance) {
+      $field_name = $instance['field_name'];
+      $this->assertTrue($display_language[$field_name] == LANGUAGE_NONE, t('The display language for field %field_name is %language.', array('%field_name' => $field_name, '%language' => LANGUAGE_NONE)));
+    }
+
+    // Test multiple-fields display languages for translatable entities.
+    field_test_entity_info_translatable($entity_type, TRUE);
+    drupal_static_reset('field_language');
+    $display_language = field_language($entity_type, $entity, NULL, $requested_language);
+
+    foreach ($instances as $instance) {
+      $field_name = $instance['field_name'];
+      $langcode = $display_language[$field_name];
+      // As the requested language was not assinged to any field, if the
+      // returned language is defined for the current field, core fallback rules
+      // were successfully applied.
+      $this->assertTrue(isset($entity->{$field_name}[$langcode]) && $langcode != $requested_language, t('The display language for the field %field_name is %language.', array('%field_name' => $field_name, '%language' => $langcode)));
+    }
+
+    // Test single-field display language.
+    drupal_static_reset('field_language');
+    $langcode = field_language($entity_type, $entity, $this->field_name, $requested_language);
+    $this->assertTrue(isset($entity->{$this->field_name}[$langcode]) && $langcode != $requested_language, t('The display language for the (single) field %field_name is %language.', array('%field_name' => $field_name, '%language' => $langcode)));
+  }
+
+  /**
+   * Tests field translations when creating a new revision.
+   */
+  function testFieldFormTranslationRevisions() {
+    $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+    $this->drupalLogin($web_user);
+
+    // Prepare the field translations.
+    field_test_entity_info_translatable($this->entity_type, TRUE);
+    $eid = 1;
+    $entity = field_test_create_stub_entity($eid, $eid, $this->instance['bundle']);
+    $available_languages = array_flip(field_available_languages($this->entity_type, $this->field));
+    unset($available_languages[LANGUAGE_NONE]);
+    $field_name = $this->field['field_name'];
+
+    // Store the field translations.
+    $entity->is_new = TRUE;
+    foreach ($available_languages as $langcode => $value) {
+      $entity->{$field_name}[$langcode][0]['value'] = $value + 1;
+    }
+    field_test_entity_save($entity);
+
+    // Create a new revision.
+    $langcode = field_valid_language(NULL);
+    $edit = array("{$field_name}[$langcode][0][value]" => $entity->{$field_name}[$langcode][0]['value'], 'revision' => TRUE);
+    $this->drupalPost('test-entity/' . $eid . '/edit', $edit, t('Save'));
+
+    // Check translation revisions.
+    $this->checkTranslationRevisions($eid, $eid, $available_languages);
+    $this->checkTranslationRevisions($eid, $eid + 1, $available_languages);
+  }
+
+  /**
+   * Check if the field translation attached to the entity revision identified
+   * by the passed arguments were correctly stored.
+   */
+  private function checkTranslationRevisions($eid, $evid, $available_languages) {
+    $field_name = $this->field['field_name'];
+    $entity = field_test_entity_test_load($eid, $evid);
+    foreach ($available_languages as $langcode => $value) {
+      $passed = isset($entity->{$field_name}[$langcode]) && $entity->{$field_name}[$langcode][0]['value'] == $value + 1;
+      $this->assertTrue($passed, t('The @language translation for revision @revision was correctly stored', array('@language' => $langcode, '@revision' => $entity->ftvid)));
+    }
+  }
+}
+
+/**
+ * Unit test class for field bulk delete and batch purge functionality.
+ */
+class FieldBulkDeleteTestCase extends FieldTestCase {
+  protected $field;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Field bulk delete tests',
+      'description'=> 'Bulk delete fields and instances, and clean up afterwards.',
+      'group' => 'Field API',
+    );
+  }
+
+  /**
+   * Convenience function for Field API tests.
+   *
+   * Given an array of potentially fully-populated entities and an
+   * optional field name, generate an array of stub entities of the
+   * same fieldable type which contains the data for the field name
+   * (if given).
+   *
+   * @param $entity_type
+   *   The entity type of $entities.
+   * @param $entities
+   *   An array of entities of type $entity_type.
+   * @param $field_name
+   *   Optional; a field name whose data should be copied from
+   *   $entities into the returned stub entities.
+   * @return
+   *   An array of stub entities corresponding to $entities.
+   */
+  function _generateStubEntities($entity_type, $entities, $field_name = NULL) {
+    $stubs = array();
+    foreach ($entities as $entity) {
+      $stub = entity_create_stub_entity($entity_type, entity_extract_ids($entity_type, $entity));
+      if (isset($field_name)) {
+        $stub->{$field_name} = $entity->{$field_name};
+      }
+      $stubs[] = $stub;
+    }
+    return $stubs;
+  }
+
+  function setUp() {
+    parent::setUp('field_test');
+
+    // Clean up data from previous test cases.
+    $this->fields = array();
+    $this->instances = array();
+
+    // Create two bundles.
+    $this->bundles = array('bb_1' => 'bb_1', 'bb_2' => 'bb_2');
+    foreach ($this->bundles as $name => $desc) {
+      field_test_create_bundle($name, $desc);
+    }
+
+    // Create two fields.
+    $field = array('field_name' => 'bf_1', 'type' => 'test_field', 'cardinality' => 1);
+    $this->fields[] = field_create_field($field);
+    $field = array('field_name' => 'bf_2', 'type' => 'test_field', 'cardinality' => 4);
+    $this->fields[] = field_create_field($field);
+
+    // For each bundle, create an instance of each field, and 10
+    // entities with values for each field.
+    $id = 0;
+    $this->entity_type = 'test_entity';
+    foreach ($this->bundles as $bundle) {
+      foreach ($this->fields as $field) {
+        $instance = array(
+          'field_name' => $field['field_name'],
+          'entity_type' => $this->entity_type,
+          'bundle' => $bundle,
+          'widget' => array(
+            'type' => 'test_field_widget',
+          )
+        );
+        $this->instances[] = field_create_instance($instance);
+      }
+
+      for ($i = 0; $i < 10; $i++) {
+        $entity = field_test_create_stub_entity($id, $id, $bundle);
+        foreach ($this->fields as $field) {
+          $entity->{$field['field_name']}[LANGUAGE_NONE] = $this->_generateTestFieldValues($field['cardinality']);
+        }
+        $this->entities[$id] = $entity;
+        field_attach_insert($this->entity_type, $entity);
+        $id++;
+      }
+    }
+  }
+
+  /**
+   * Verify that deleting an instance leaves the field data items in
+   * the database and that the appropriate Field API functions can
+   * operate on the deleted data and instance.
+   *
+   * This tests how EntityFieldQuery interacts with
+   * field_delete_instance() and could be moved to FieldCrudTestCase,
+   * but depends on this class's setUp().
+   */
+  function testDeleteFieldInstance() {
+    $bundle = reset($this->bundles);
+    $field = reset($this->fields);
+
+    // There are 10 entities of this bundle.
+    $query = new EntityFieldQuery();
+    $found = $query
+      ->fieldCondition($field)
+      ->entityCondition('bundle', $bundle)
+      ->execute();
+    $this->assertEqual(count($found['test_entity']), 10, 'Correct number of entities found before deleting');
+
+    // Delete the instance.
+    $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle);
+    field_delete_instance($instance);
+
+    // The instance still exists, deleted.
+    $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1));
+    $this->assertEqual(count($instances), 1, 'There is one deleted instance');
+    $this->assertEqual($instances[0]['bundle'], $bundle, 'The deleted instance is for the correct bundle');
+
+    // There are 0 entities of this bundle with non-deleted data.
+    $query = new EntityFieldQuery();
+    $found = $query
+      ->fieldCondition($field)
+      ->entityCondition('bundle', $bundle)
+      ->execute();
+    $this->assertTrue(!isset($found['test_entity']), 'No entities found after deleting');
+
+    // There are 10 entities of this bundle when deleted fields are allowed, and
+    // their values are correct.
+    $query = new EntityFieldQuery();
+    $found = $query
+      ->fieldCondition($field)
+      ->entityCondition('bundle', $bundle)
+      ->deleted(TRUE)
+      ->execute();
+    field_attach_load($this->entity_type, $found[$this->entity_type], FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1));
+    $this->assertEqual(count($found['test_entity']), 10, 'Correct number of entities found after deleting');
+    foreach ($found['test_entity'] as $id => $entity) {
+      $this->assertEqual($this->entities[$id]->{$field['field_name']}, $entity->{$field['field_name']}, "Entity $id with deleted data loaded correctly");
+    }
+  }
+
+  /**
+   * Verify that field data items and instances are purged when an
+   * instance is deleted.
+   */
+  function testPurgeInstance() {
+    field_test_memorize();
+
+    $bundle = reset($this->bundles);
+    $field = reset($this->fields);
+
+    // Delete the instance.
+    $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle);
+    field_delete_instance($instance);
+
+    // No field hooks were called.
+    $mem = field_test_memorize();
+    $this->assertEqual(count($mem), 0, 'No field hooks were called');
+
+    $batch_size = 2;
+    for ($count = 8; $count >= 0; $count -= 2) {
+      // Purge two entities.
+      field_purge_batch($batch_size);
+
+      // There are $count deleted entities left.
+      $query = new EntityFieldQuery();
+      $found = $query
+        ->fieldCondition($field)
+        ->entityCondition('bundle', $bundle)
+        ->deleted(TRUE)
+        ->execute();
+      $this->assertEqual($count ? count($found['test_entity']) : count($found), $count, 'Correct number of entities found after purging 2');
+    }
+
+    // hook_field_delete() was called on a pseudo-entity for each entity. Each
+    // pseudo entity has a $field property that matches the original entity,
+    // but no others.
+    $mem = field_test_memorize();
+    $this->assertEqual(count($mem['field_test_field_delete']), 10, 'hook_field_delete was called for the right number of entities');
+    $stubs = $this->_generateStubEntities($this->entity_type, $this->entities, $field['field_name']);
+    $count = count($stubs);
+    foreach ($mem['field_test_field_delete'] as $args) {
+      $entity = $args[1];
+      $this->assertEqual($stubs[$entity->ftid], $entity, 'hook_field_delete() called with the correct stub');
+      unset($stubs[$entity->ftid]);
+    }
+    $this->assertEqual(count($stubs), $count-10, 'hook_field_delete was called with each entity once');
+
+    // The instance still exists, deleted.
+    $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1));
+    $this->assertEqual(count($instances), 1, 'There is one deleted instance');
+
+    // Purge the instance.
+    field_purge_batch($batch_size);
+
+    // The instance is gone.
+    $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1));
+    $this->assertEqual(count($instances), 0, 'The instance is gone');
+
+    // The field still exists, not deleted, because it has a second instance.
+    $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
+    $this->assertTrue(isset($fields[$field['id']]), 'The field exists and is not deleted');
+  }
+
+  /**
+   * Verify that fields are preserved and purged correctly as multiple
+   * instances are deleted and purged.
+   */
+  function testPurgeField() {
+    $field = reset($this->fields);
+
+    foreach ($this->bundles as $bundle) {
+      // Delete the instance.
+      $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle);
+      field_delete_instance($instance);
+
+      // Purge the data.
+      field_purge_batch(10);
+
+      // Purge again to purge the instance.
+      field_purge_batch(0);
+
+      // The field still exists, not deleted, because it was never deleted.
+      $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
+      $this->assertTrue(isset($fields[$field['id']]), 'The field exists and is not deleted');
+    }
+
+    // Delete the field.
+    field_delete_field($field['field_name']);
+
+    // The field still exists, deleted.
+    $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
+    $this->assertEqual($fields[$field['id']]['deleted'], 1, 'The field exists and is deleted');
+
+    // Purge the field.
+    field_purge_batch(0);
+
+    // The field is gone.
+    $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
+    $this->assertEqual(count($fields), 0, 'The field is purged.');
+  }
+}
diff --git a/modules/field/modules/list/list.test b/modules/field/modules/list/list.test
new file mode 100644
index 0000000..30db6ee
--- /dev/null
+++ b/modules/field/modules/list/list.test
@@ -0,0 +1,185 @@
+<?php
+// $Id: list.test,v 1.5 2010/03/27 12:49:32 dries Exp $
+
+/**
+ * @file
+ * Tests for the 'List' field types.
+ */
+
+/**
+ * Tests for the 'List' field types.
+ */
+class ListFieldTestCase extends FieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'List field',
+      'description' => 'Test the List field type.',
+      'group' => 'Field types',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('field_test');
+
+    $this->field_name = 'test_list';
+    $this->field = array(
+      'field_name' => $this->field_name,
+      'type' => 'list',
+      'cardinality' => 1,
+      'settings' => array(
+        'allowed_values' => "1|One\n2|Two\n3|Three\n",
+      ),
+    );
+    $this->field = field_create_field($this->field);
+
+    $this->instance = array(
+      'field_name' => $this->field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'widget' => array(
+        'type' => 'options_buttons',
+      ),
+    );
+    $this->instance = field_create_instance($this->instance);
+  }
+
+  /**
+   * Test that allowed values can be updated.
+   */
+  function testUpdateAllowedValues() {
+    $langcode = LANGUAGE_NONE;
+
+    // All three options appear.
+    $entity = field_test_create_stub_entity();
+    $form = drupal_get_form('field_test_entity_form', $entity);
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
+
+    // Removed options do not appear.
+    $this->field['settings']['allowed_values'] = "2|Two";
+    field_update_field($this->field);
+    $entity = field_test_create_stub_entity();
+    $form = drupal_get_form('field_test_entity_form', $entity);
+    $this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
+    $this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
+
+    // Completely new options appear.
+    $this->field['settings']['allowed_values'] = "10|Update\n20|Twenty";
+    field_update_field($this->field);
+    $form = drupal_get_form('field_test_entity_form', $entity);
+    $this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
+    $this->assertTrue(empty($form[$this->field_name][$langcode][2]), t('Option 2 does not exist'));
+    $this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][10]), t('Option 10 exists'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][20]), t('Option 20 exists'));
+
+    // Options are reset when a new field with the same name is created.
+    field_delete_field($this->field_name);
+    unset($this->field['id']);
+    $this->field['settings']['allowed_values'] = "1|One\n2|Two\n3|Three\n";
+    $this->field = field_create_field($this->field);
+    $this->instance = array(
+      'field_name' => $this->field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'widget' => array(
+        'type' => 'options_buttons',
+      ),
+    );
+    $this->instance = field_create_instance($this->instance);
+    $entity = field_test_create_stub_entity();
+    $form = drupal_get_form('field_test_entity_form', $entity);
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
+    $this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
+  }
+}
+
+/**
+* List module UI tests.
+*/
+class ListFieldUITestCase extends FieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'List field UI',
+      'description' => 'Test the List field UI functionality.',
+      'group' => 'Field types',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('field_test', 'field_ui');
+
+    // Create test user.
+    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+    $this->drupalLogin($admin_user);
+
+    // Create content type, with underscores.
+    $type_name = 'test_' . strtolower($this->randomName());
+    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
+    $this->type = $type->type;
+    // Store a valid URL name, with hyphens instead of underscores.
+    $this->hyphen_type = str_replace('_', '-', $this->type);
+
+    // Create random field name.
+    $this->field_label = $this->randomString();
+    $this->field_name = strtolower($this->randomName());
+  }
+
+  /**
+   * Tests that allowed values are properly validated in the UI.
+   */
+  function testAllowedValues() {
+    $element_name = "field[settings][allowed_values]";
+
+    //Test 'List' field type.
+    $admin_path = $this->createListFieldAndEdit('list');
+    //Check that non-integer keys are rejected.
+    $edit = array($element_name => "1.1|one\n");
+    $this->drupalPost($admin_path, $edit, t('Save settings'));
+    $this->assertText("keys must be integers", t('Form validation failed.'));
+
+    // Test 'List (number)' field type.
+    $admin_path = $this->createListFieldAndEdit('list_number');
+    //Check that non-numeric keys are rejected.
+    $edit = array($element_name => "1|one\nB|two");
+    $this->drupalPost($admin_path, $edit, t('Save settings'));
+    $this->assertText("each key must be a valid integer or decimal", t('Form validation failed.'));
+
+    //Test 'List (text)' field type.
+    $admin_path = $this->createListFieldAndEdit('list_text');
+    //Check that over long keys are rejected.
+    $edit = array($element_name => "1|one\n" . $this->randomName(256) . "|two");
+    $this->drupalPost($admin_path, $edit, t('Save settings'));
+    $this->assertText("each key must be a string at most 255 characters long", t('Form validation failed.'));
+  }
+
+  /**
+   * Helper function to create list field of a given type and get the edit page.
+   *
+   * @param string $type
+   *   'list', 'list_boolean', 'list_number', or 'list_text'
+   */
+  private function createListFieldAndEdit($type) {
+    // Create a test field and instance.
+    $field_name = 'test_' . $type;
+    $field = array(
+      'field_name' => $field_name,
+      'type' => $type,
+    );
+    field_create_field($field);
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'node',
+      'bundle' => $this->type,
+    );
+    field_create_instance($instance);
+
+    $admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $field_name;
+    return $admin_path;
+  }
+
+}
+
diff --git a/modules/field/modules/list/tests/list.test b/modules/field/modules/list/tests/list.test
deleted file mode 100644
index 30db6ee..0000000
--- a/modules/field/modules/list/tests/list.test
+++ /dev/null
@@ -1,185 +0,0 @@
-<?php
-// $Id: list.test,v 1.5 2010/03/27 12:49:32 dries Exp $
-
-/**
- * @file
- * Tests for the 'List' field types.
- */
-
-/**
- * Tests for the 'List' field types.
- */
-class ListFieldTestCase extends FieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'List field',
-      'description' => 'Test the List field type.',
-      'group' => 'Field types',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('field_test');
-
-    $this->field_name = 'test_list';
-    $this->field = array(
-      'field_name' => $this->field_name,
-      'type' => 'list',
-      'cardinality' => 1,
-      'settings' => array(
-        'allowed_values' => "1|One\n2|Two\n3|Three\n",
-      ),
-    );
-    $this->field = field_create_field($this->field);
-
-    $this->instance = array(
-      'field_name' => $this->field_name,
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'widget' => array(
-        'type' => 'options_buttons',
-      ),
-    );
-    $this->instance = field_create_instance($this->instance);
-  }
-
-  /**
-   * Test that allowed values can be updated.
-   */
-  function testUpdateAllowedValues() {
-    $langcode = LANGUAGE_NONE;
-
-    // All three options appear.
-    $entity = field_test_create_stub_entity();
-    $form = drupal_get_form('field_test_entity_form', $entity);
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
-
-    // Removed options do not appear.
-    $this->field['settings']['allowed_values'] = "2|Two";
-    field_update_field($this->field);
-    $entity = field_test_create_stub_entity();
-    $form = drupal_get_form('field_test_entity_form', $entity);
-    $this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
-    $this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
-
-    // Completely new options appear.
-    $this->field['settings']['allowed_values'] = "10|Update\n20|Twenty";
-    field_update_field($this->field);
-    $form = drupal_get_form('field_test_entity_form', $entity);
-    $this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
-    $this->assertTrue(empty($form[$this->field_name][$langcode][2]), t('Option 2 does not exist'));
-    $this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][10]), t('Option 10 exists'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][20]), t('Option 20 exists'));
-
-    // Options are reset when a new field with the same name is created.
-    field_delete_field($this->field_name);
-    unset($this->field['id']);
-    $this->field['settings']['allowed_values'] = "1|One\n2|Two\n3|Three\n";
-    $this->field = field_create_field($this->field);
-    $this->instance = array(
-      'field_name' => $this->field_name,
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'widget' => array(
-        'type' => 'options_buttons',
-      ),
-    );
-    $this->instance = field_create_instance($this->instance);
-    $entity = field_test_create_stub_entity();
-    $form = drupal_get_form('field_test_entity_form', $entity);
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
-    $this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
-  }
-}
-
-/**
-* List module UI tests.
-*/
-class ListFieldUITestCase extends FieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'List field UI',
-      'description' => 'Test the List field UI functionality.',
-      'group' => 'Field types',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('field_test', 'field_ui');
-
-    // Create test user.
-    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
-    $this->drupalLogin($admin_user);
-
-    // Create content type, with underscores.
-    $type_name = 'test_' . strtolower($this->randomName());
-    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
-    $this->type = $type->type;
-    // Store a valid URL name, with hyphens instead of underscores.
-    $this->hyphen_type = str_replace('_', '-', $this->type);
-
-    // Create random field name.
-    $this->field_label = $this->randomString();
-    $this->field_name = strtolower($this->randomName());
-  }
-
-  /**
-   * Tests that allowed values are properly validated in the UI.
-   */
-  function testAllowedValues() {
-    $element_name = "field[settings][allowed_values]";
-
-    //Test 'List' field type.
-    $admin_path = $this->createListFieldAndEdit('list');
-    //Check that non-integer keys are rejected.
-    $edit = array($element_name => "1.1|one\n");
-    $this->drupalPost($admin_path, $edit, t('Save settings'));
-    $this->assertText("keys must be integers", t('Form validation failed.'));
-
-    // Test 'List (number)' field type.
-    $admin_path = $this->createListFieldAndEdit('list_number');
-    //Check that non-numeric keys are rejected.
-    $edit = array($element_name => "1|one\nB|two");
-    $this->drupalPost($admin_path, $edit, t('Save settings'));
-    $this->assertText("each key must be a valid integer or decimal", t('Form validation failed.'));
-
-    //Test 'List (text)' field type.
-    $admin_path = $this->createListFieldAndEdit('list_text');
-    //Check that over long keys are rejected.
-    $edit = array($element_name => "1|one\n" . $this->randomName(256) . "|two");
-    $this->drupalPost($admin_path, $edit, t('Save settings'));
-    $this->assertText("each key must be a string at most 255 characters long", t('Form validation failed.'));
-  }
-
-  /**
-   * Helper function to create list field of a given type and get the edit page.
-   *
-   * @param string $type
-   *   'list', 'list_boolean', 'list_number', or 'list_text'
-   */
-  private function createListFieldAndEdit($type) {
-    // Create a test field and instance.
-    $field_name = 'test_' . $type;
-    $field = array(
-      'field_name' => $field_name,
-      'type' => $type,
-    );
-    field_create_field($field);
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => 'node',
-      'bundle' => $this->type,
-    );
-    field_create_instance($instance);
-
-    $admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $field_name;
-    return $admin_path;
-  }
-
-}
-
diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test
deleted file mode 100644
index 36d6284..0000000
--- a/modules/field/tests/field.test
+++ /dev/null
@@ -1,2968 +0,0 @@
-<?php
-// $Id: field.test,v 1.33 2010/06/17 13:16:57 dries Exp $
-
-/**
- * @file
- * Unit test file for fields in core.
- */
-
-/**
- * Parent class for Field API tests.
- */
-class FieldTestCase extends DrupalWebTestCase {
-  var $default_storage = 'field_sql_storage';
-
-  /**
-   * Set the default field storage backend for fields created during tests.
-   */
-  function setUp() {
-    // Since this is a base class for many test cases, support the same
-    // flexibility that DrupalWebTestCase::setUp() has for the modules to be
-    // passed in as either an array or a variable number of string arguments.
-    $modules = func_get_args();
-    if (isset($modules[0]) && is_array($modules[0])) {
-      $modules = $modules[0];
-    }
-    parent::setUp($modules);
-    // Set default storage backend.
-    variable_set('field_storage_default', $this->default_storage);
-  }
-
-  /**
-   * Generate random values for a field_test field.
-   *
-   * @param $cardinality
-   *   Number of values to generate.
-   * @return
-   *  An array of random values, in the format expected for field values.
-   */
-  function _generateTestFieldValues($cardinality) {
-    $values = array();
-    for ($i = 0; $i < $cardinality; $i++) {
-      // field_test fields treat 0 as 'empty value'.
-      $values[$i]['value'] = mt_rand(1, 127);
-    }
-    return $values;
-  }
-
-  /**
-   * Assert that a field has the expected values in an entity.
-   *
-   * This function only checks a single column in the field values.
-   *
-   * @param $entity
-   *   The entity to test.
-   * @param $field_name
-   *   The name of the field to test
-   * @param $langcode
-   *   The language code for the values.
-   * @param $expected_values
-   *   The array of expected values.
-   * @param $column
-   *   (Optional) the name of the column to check.
-   */
-  function assertFieldValues($entity, $field_name, $langcode, $expected_values, $column = 'value') {
-    $e = clone $entity;
-    field_attach_load('test_entity', array($e->ftid => $e));
-    $values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
-    $this->assertEqual(count($values), count($expected_values), t('Expected number of values were saved.'));
-    foreach ($expected_values as $key => $value) {
-      $this->assertEqual($values[$key][$column], $value, t('Value @value was saved correctly.', array('@value' => $value)));
-    }
-  }
-}
-
-class FieldAttachTestCase extends FieldTestCase {
-  function setUp($modules = array()) {
-    // Since this is a base class for many test cases, support the same
-    // flexibility that DrupalWebTestCase::setUp() has for the modules to be
-    // passed in as either an array or a variable number of string arguments.
-    if (!is_array($modules)) {
-      $modules = func_get_args();
-    }
-    if (!in_array('field_test', $modules)) {
-      $modules[] = 'field_test';
-    }
-    parent::setUp($modules);
-
-    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
-    $this->field = field_create_field($this->field);
-    $this->field_id = $this->field['id'];
-    $this->instance = array(
-      'field_name' => $this->field_name,
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      'settings' => array(
-        'test_instance_setting' => $this->randomName(),
-      ),
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'label' => 'Test Field',
-        'settings' => array(
-          'test_widget_setting' => $this->randomName(),
-        )
-      )
-    );
-    field_create_instance($this->instance);
-  }
-}
-
-/**
- * Unit test class for storage-related field_attach_* functions.
- *
- * All field_attach_* test work with all field_storage plugins and
- * all hook_field_attach_pre_{load,insert,update}() hooks.
- */
-class FieldAttachStorageTestCase extends FieldAttachTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Field attach tests (storage-related)',
-      'description' => 'Test storage-related Field Attach API functions.',
-      'group' => 'Field API',
-    );
-  }
-
-  /**
-   * Check field values insert, update and load.
-   *
-   * Works independently of the underlying field storage backend. Inserts or
-   * updates random field data and then loads and verifies the data.
-   */
-  function testFieldAttachSaveLoad() {
-    // Configure the instance so that we test hook_field_load() (see
-    // field_test_field_load() in field_test.module).
-    $this->instance['settings']['test_hook_field_load'] = TRUE;
-    field_update_instance($this->instance);
-    $langcode = LANGUAGE_NONE;
-
-    $entity_type = 'test_entity';
-    $values = array();
-
-    // TODO : test empty values filtering and "compression" (store consecutive deltas).
-
-    // Preparation: create three revisions and store them in $revision array.
-    for ($revision_id = 0; $revision_id < 3; $revision_id++) {
-      $revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
-      // Note: we try to insert one extra value.
-      $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
-      $current_revision = $revision_id;
-      // If this is the first revision do an insert.
-      if (!$revision_id) {
-        $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
-        field_attach_insert($entity_type, $revision[$revision_id]);
-      }
-      else {
-        // Otherwise do an update.
-        $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
-        field_attach_update($entity_type, $revision[$revision_id]);
-      }
-    }
-
-    // Confirm current revision loads the correct data.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    // Number of values per field loaded equals the field cardinality.
-    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Current revision: expected number of values'));
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      // The field value loaded matches the one inserted or updated.
-      $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'] , $values[$current_revision][$delta]['value'], t('Current revision: expected value %delta was found.', array('%delta' => $delta)));
-      // The value added in hook_field_load() is found.
-      $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', t('Current revision: extra information for value %delta was found', array('%delta' => $delta)));
-    }
-
-    // Confirm each revision loads the correct data.
-    foreach (array_keys($revision) as $revision_id) {
-      $entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $entity));
-      // Number of values per field loaded equals the field cardinality.
-      $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
-      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-        // The field value loaded matches the one inserted or updated.
-        $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $values[$revision_id][$delta]['value'], t('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
-        // The value added in hook_field_load() is found.
-        $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', t('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
-      }
-    }
-  }
-
-  /**
-   * Test the 'multiple' load feature.
-   */
-  function testFieldAttachLoadMultiple() {
-    $entity_type = 'test_entity';
-    $langcode = LANGUAGE_NONE;
-
-    // Define 2 bundles.
-    $bundles = array(
-      1 => 'test_bundle_1',
-      2 => 'test_bundle_2',
-    );
-    field_test_create_bundle($bundles[1]);
-    field_test_create_bundle($bundles[2]);
-    // Define 3 fields:
-    // - field_1 is in bundle_1 and bundle_2,
-    // - field_2 is in bundle_1,
-    // - field_3 is in bundle_2.
-    $field_bundles_map = array(
-      1 => array(1, 2),
-      2 => array(1),
-      3 => array(2),
-    );
-    for ($i = 1; $i <= 3; $i++) {
-      $field_names[$i] = 'field_' . $i;
-      $field = array('field_name' => $field_names[$i], 'type' => 'test_field');
-      $field = field_create_field($field);
-      $field_ids[$i] = $field['id'];
-      foreach ($field_bundles_map[$i] as $bundle) {
-        $instance = array(
-          'field_name' => $field_names[$i],
-          'entity_type' => 'test_entity',
-          'bundle' => $bundles[$bundle],
-          'settings' => array(
-            // Configure the instance so that we test hook_field_load()
-            // (see field_test_field_load() in field_test.module).
-            'test_hook_field_load' => TRUE,
-          ),
-        );
-        field_create_instance($instance);
-      }
-    }
-
-    // Create one test entity per bundle, with random values.
-    foreach ($bundles as $index => $bundle) {
-      $entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
-      $entity = clone($entities[$index]);
-      $instances = field_info_instances('test_entity', $bundle);
-      foreach ($instances as $field_name => $instance) {
-        $values[$index][$field_name] = mt_rand(1, 127);
-        $entity->$field_name = array($langcode => array(array('value' => $values[$index][$field_name])));
-      }
-      field_attach_insert($entity_type, $entity);
-    }
-
-    // Check that a single load correctly loads field values for both entities.
-    field_attach_load($entity_type, $entities);
-    foreach ($entities as $index => $entity) {
-      $instances = field_info_instances($entity_type, $bundles[$index]);
-      foreach ($instances as $field_name => $instance) {
-        // The field value loaded matches the one inserted.
-        $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
-        // The value added in hook_field_load() is found.
-        $this->assertEqual($entity->{$field_name}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
-      }
-    }
-
-    // Check that the single-field load option works.
-    $entity = field_test_create_stub_entity(1, 1, $bundles[1]);
-    field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('field_id' => $field_ids[1]));
-    $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['value'], $values[1][$field_names[1]], t('Entity %index: expected value was found.', array('%index' => 1)));
-    $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => 1)));
-    $this->assert(!isset($entity->{$field_names[2]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
-    $this->assert(!isset($entity->{$field_names[3]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
-  }
-
-  /**
-   * Test saving and loading fields using different storage backends.
-   */
-  function testFieldAttachSaveLoadDifferentStorage() {
-    $entity_type = 'test_entity';
-    $langcode = LANGUAGE_NONE;
-
-    // Create two fields using different storage backends, and their instances.
-    $fields = array(
-      array(
-        'field_name' => 'field_1',
-        'type' => 'test_field',
-        'cardinality' => 4,
-        'storage' => array('type' => 'field_sql_storage')
-      ),
-      array(
-        'field_name' => 'field_2',
-        'type' => 'test_field',
-        'cardinality' => 4,
-        'storage' => array('type' => 'field_test_storage')
-      ),
-    );
-    foreach ($fields as $field) {
-      field_create_field($field);
-      $instance = array(
-        'field_name' => $field['field_name'],
-        'entity_type' => 'test_entity',
-        'bundle' => 'test_bundle',
-      );
-      field_create_instance($instance);
-    }
-
-    $entity_init = field_test_create_stub_entity();
-
-    // Create entity and insert random values.
-    $entity = clone($entity_init);
-    $values = array();
-    foreach ($fields as $field) {
-      $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
-      $entity->{$field['field_name']}[$langcode] = $values[$field['field_name']];
-    }
-    field_attach_insert($entity_type, $entity);
-
-    // Check that values are loaded as expected.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    foreach ($fields as $field) {
-      $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}[$langcode], t('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
-    }
-  }
-
-  /**
-   * Test storage details alteration.
-   *
-   * @see field_test_storage_details_alter()
-   */
-  function testFieldStorageDetailsAlter() {
-    $field_name = 'field_test_change_my_details';
-    $field = array(
-      'field_name' => $field_name,
-      'type' => 'test_field',
-      'cardinality' => 4,
-      'storage' => array('type' => 'field_test_storage'),
-    );
-    $field = field_create_field($field);
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-    );
-    field_create_instance($instance);
-
-    $field = field_info_field($instance['field_name']);
-    $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
-
-    // The storage details are indexed by a storage engine type.
-    $this->assertTrue(array_key_exists('drupal_variables', $field['storage']['details']), t('The storage type is Drupal variables.'));
-
-    $details = $field['storage']['details']['drupal_variables'];
-
-    // The field_test storage details are indexed by variable name. The details
-    // are altered, so moon and mars are correct for this test.
-    $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), t('Moon is available in the instance array.'));
-    $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), t('Mars is available in the instance array.'));
-
-    // Test current and revision storage details together because the columns
-    // are the same.
-    foreach ((array) $field['columns'] as $column_name => $attributes) {
-      $this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
-      $this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
-    }
-  }
-
-  /**
-   * Tests insert and update with missing or NULL fields.
-   */
-  function testFieldAttachSaveMissingData() {
-    $entity_type = 'test_entity';
-    $entity_init = field_test_create_stub_entity();
-    $langcode = LANGUAGE_NONE;
-
-    // Insert: Field is missing.
-    $entity = clone($entity_init);
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
-
-    // Insert: Field is NULL.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = NULL;
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
-
-    // Add some real data.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $values = $this->_generateTestFieldValues(1);
-    $entity->{$this->field_name}[$langcode] = $values;
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
-
-    // Update: Field is missing. Data should survive.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    field_attach_update($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Update: missing field leaves existing values in place'));
-
-    // Update: Field is NULL. Data should be wiped.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = NULL;
-    field_attach_update($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
-
-    // Re-add some data.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $values = $this->_generateTestFieldValues(1);
-    $entity->{$this->field_name}[$langcode] = $values;
-    field_attach_update($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
-
-    // Update: Field is empty array. Data should be wiped.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = array();
-    field_attach_update($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}), t('Update: empty array removes existing values'));
-  }
-
-  /**
-   * Test insert with missing or NULL fields, with default value.
-   */
-  function testFieldAttachSaveMissingDataDefaultValue() {
-    // Add a default value function.
-    $this->instance['default_value_function'] = 'field_test_default_value';
-    field_update_instance($this->instance);
-
-    $entity_type = 'test_entity';
-    $entity_init = field_test_create_stub_entity();
-    $langcode = LANGUAGE_NONE;
-
-    // Insert: Field is NULL.
-    $entity = clone($entity_init);
-    $entity->{$this->field_name}[$langcode] = NULL;
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertTrue(empty($entity->{$this->field_name}[$langcode]), t('Insert: NULL field results in no value saved'));
-
-    // Insert: Field is missing.
-    field_cache_clear();
-    $entity = clone($entity_init);
-    field_attach_insert($entity_type, $entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
-    $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Insert: missing field results in default value saved'));
-  }
-
-  /**
-   * Test field_attach_delete().
-   */
-  function testFieldAttachDelete() {
-    $entity_type = 'test_entity';
-    $langcode = LANGUAGE_NONE;
-    $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Create revision 0
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $rev[0]->{$this->field_name}[$langcode] = $values;
-    field_attach_insert($entity_type, $rev[0]);
-
-    // Create revision 1
-    $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
-    $rev[1]->{$this->field_name}[$langcode] = $values;
-    field_attach_update($entity_type, $rev[1]);
-
-    // Create revision 2
-    $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
-    $rev[2]->{$this->field_name}[$langcode] = $values;
-    field_attach_update($entity_type, $rev[2]);
-
-    // Confirm each revision loads
-    foreach (array_keys($rev) as $vid) {
-      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
-    }
-
-    // Delete revision 1, confirm the other two still load.
-    field_attach_delete_revision($entity_type, $rev[1]);
-    foreach (array(0, 2) as $vid) {
-      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
-    }
-
-    // Confirm the current revision still loads
-    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
-
-    // Delete all field data, confirm nothing loads
-    field_attach_delete($entity_type, $rev[2]);
-    foreach (array(0, 1, 2) as $vid) {
-      $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertIdentical($read->{$this->field_name}, array(), "The test entity revision $vid is deleted.");
-    }
-    $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertIdentical($read->{$this->field_name}, array(), t('The test entity current revision is deleted.'));
-  }
-
-  /**
-   * Test field_attach_create_bundle() and field_attach_rename_bundle().
-   */
-  function testFieldAttachCreateRenameBundle() {
-    // Create a new bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
-    field_test_create_bundle($new_bundle);
-
-    // Add an instance to that bundle.
-    $this->instance['bundle'] = $new_bundle;
-    field_create_instance($this->instance);
-
-    // Save an entity with data in the field.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    $langcode = LANGUAGE_NONE;
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity->{$this->field_name}[$langcode] = $values;
-    $entity_type = 'test_entity';
-    field_attach_insert($entity_type, $entity);
-
-    // Verify the field data is present on load.
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
-
-    // Rename the bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
-    field_test_rename_bundle($this->instance['bundle'], $new_bundle);
-
-    // Check that the instance definition has been updated.
-    $this->instance = field_info_instance($entity_type, $this->field_name, $new_bundle);
-    $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
-
-    // Verify the field data is present on load.
-    $entity = field_test_create_stub_entity(0, 0, $new_bundle);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage");
-  }
-
-  /**
-   * Test field_attach_delete_bundle().
-   */
-  function testFieldAttachDeleteBundle() {
-    // Create a new bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
-    field_test_create_bundle($new_bundle);
-
-    // Add an instance to that bundle.
-    $this->instance['bundle'] = $new_bundle;
-    field_create_instance($this->instance);
-
-    // Create a second field for the test bundle
-    $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
-    field_create_field($field);
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => 'test_entity',
-      'bundle' => $this->instance['bundle'],
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      // test_field has no instance settings
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'settings' => array(
-          'size' => mt_rand(0, 255))));
-    field_create_instance($instance);
-
-    // Save an entity with data for both fields
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    $langcode = LANGUAGE_NONE;
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity->{$this->field_name}[$langcode] = $values;
-    $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1);
-    field_attach_insert('test_entity', $entity);
-
-    // Verify the fields are present on load
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load('test_entity', array(0 => $entity));
-    $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded');
-    $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded');
-
-    // Delete the bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
-    field_test_delete_bundle($this->instance['bundle']);
-
-    // Verify no data gets loaded
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    field_attach_load('test_entity', array(0 => $entity));
-    $this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field');
-    $this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field');
-
-    // Verify that the instances are gone
-    $this->assertFalse(field_read_instance('test_entity', $this->field_name, $this->instance['bundle']), "First field is deleted");
-    $this->assertFalse(field_read_instance('test_entity', $field_name, $instance['bundle']), "Second field is deleted");
-  }
-}
-
-/**
- * Unit test class for non-storage related field_attach_* functions.
- */
-class FieldAttachOtherTestCase extends FieldAttachTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Field attach tests (other)',
-      'description' => 'Test other Field Attach API functions.',
-      'group' => 'Field API',
-    );
-  }
-
-  /**
-   * Test field_attach_view() and field_attach_prepare_view().
-   */
-  function testFieldAttachView() {
-    $entity_type = 'test_entity';
-    $entity_init = field_test_create_stub_entity();
-    $langcode = LANGUAGE_NONE;
-
-    // Populate values to be displayed.
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity_init->{$this->field_name}[$langcode] = $values;
-
-    // Simple formatter, label displayed.
-    $entity = clone($entity_init);
-    $formatter_setting = $this->randomName();
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'field_test_default',
-        'settings' => array(
-          'test_formatter_setting' => $formatter_setting,
-        )
-      ),
-    );
-    field_update_instance($this->instance);
-    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
-    $entity->content = field_attach_view($entity_type, $entity, 'full');
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    $this->assertRaw($this->instance['label'], "Label is displayed.");
-    foreach ($values as $delta => $value) {
-      $this->content = $output;
-      $this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
-    }
-
-    // Label hidden.
-    $entity = clone($entity_init);
-    $this->instance['display']['full']['label'] = 'hidden';
-    field_update_instance($this->instance);
-    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
-    $entity->content = field_attach_view($entity_type, $entity, 'full');
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
-
-    // Field hidden.
-    $entity = clone($entity_init);
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'hidden',
-      ),
-    );
-    field_update_instance($this->instance);
-    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
-    $entity->content = field_attach_view($entity_type, $entity, 'full');
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
-    foreach ($values as $delta => $value) {
-      $this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
-    }
-
-    // Multiple formatter.
-    $entity = clone($entity_init);
-    $formatter_setting = $this->randomName();
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'field_test_multiple',
-        'settings' => array(
-          'test_formatter_setting_multiple' => $formatter_setting,
-        )
-      ),
-    );
-    field_update_instance($this->instance);
-    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
-    $entity->content = field_attach_view($entity_type, $entity, 'full');
-    $output = drupal_render($entity->content);
-    $display = $formatter_setting;
-    foreach ($values as $delta => $value) {
-      $display .= "|$delta:{$value['value']}";
-    }
-    $this->content = $output;
-    $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
-
-    // Test a formatter that uses hook_field_formatter_prepare_view().
-    $entity = clone($entity_init);
-    $formatter_setting = $this->randomName();
-    $this->instance['display'] = array(
-      'full' => array(
-        'label' => 'above',
-        'type' => 'field_test_with_prepare_view',
-        'settings' => array(
-          'test_formatter_setting_additional' => $formatter_setting,
-        )
-      ),
-    );
-    field_update_instance($this->instance);
-    field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
-    $entity->content = field_attach_view($entity_type, $entity, 'full');
-    $output = drupal_render($entity->content);
-    $this->content = $output;
-    foreach ($values as $delta => $value) {
-      $this->content = $output;
-      $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
-      $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
-    }
-
-    // TODO:
-    // - check display order with several fields
-
-    // Preprocess template.
-    $variables = array();
-    field_attach_preprocess($entity_type, $entity, $entity->content, $variables);
-    $result = TRUE;
-    foreach ($values as $delta => $item) {
-      if ($variables[$this->field_name][$delta]['value'] !== $item['value']) {
-        $result = FALSE;
-        break;
-      }
-    }
-    $this->assertTrue($result, t('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name)));
-  }
-
-  /**
-   * Test field cache.
-   */
-  function testFieldAttachCache() {
-    // Initialize random values and a test entity.
-    $entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
-    $langcode = LANGUAGE_NONE;
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-
-    // Non-cacheable entity type.
-    $entity_type = 'test_entity';
-    $cid = "field:$entity_type:{$entity_init->ftid}";
-
-    // Check that no initial cache entry is present.
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
-
-    // Save, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    $entity->{$this->field_name}[$langcode] = $values;
-    field_attach_insert($entity_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert'));
-
-    // Load, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load'));
-
-
-    // Cacheable entity type.
-    $entity_type = 'test_cacheable_entity';
-    $cid = "field:$entity_type:{$entity_init->ftid}";
-    $instance = $this->instance;
-    $instance['entity_type'] = $entity_type;
-    field_create_instance($instance);
-
-    // Check that no initial cache entry is present.
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
-
-    // Save, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    $entity->{$this->field_name}[$langcode] = $values;
-    field_attach_insert($entity_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert'));
-
-    // Load a single field, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on loading a single field'));
-
-    // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
-
-    // Update with different values, and check that the cache entry is wiped.
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
-    $entity->{$this->field_name}[$langcode] = $values;
-    field_attach_update($entity_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update'));
-
-    // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
-
-    // Create a new revision, and check that the cache entry is wiped.
-    $entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
-    $entity->{$this->field_name}[$langcode] = $values;
-    field_attach_update($entity_type, $entity);
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation'));
-
-    // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->ftid => $entity));
-    $cache = cache_get($cid, 'cache_field');
-    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
-
-    // Delete, and check that the cache entry is wiped.
-    field_attach_delete($entity_type, $entity);
-    $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete'));
-  }
-
-  /**
-   * Test field_attach_validate().
-   *
-   * Verify that field_attach_validate() invokes the correct
-   * hook_field_validate.
-   */
-  function testFieldAttachValidate() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-    $langcode = LANGUAGE_NONE;
-
-    // Set up values to generate errors
-    $values = array();
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      $values[$delta]['value'] = -1;
-    }
-    // Arrange for item 1 not to generate an error
-    $values[1]['value'] = 1;
-    $entity->{$this->field_name}[$langcode] = $values;
-
-    try {
-      field_attach_validate($entity_type, $entity);
-    }
-    catch (FieldValidationException $e) {
-      $errors = $e->errors;
-    }
-
-    foreach ($values as $delta => $value) {
-      if ($value['value'] != 1) {
-        $this->assertIdentical($errors[$this->field_name][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
-        $this->assertEqual(count($errors[$this->field_name][$langcode][$delta]), 1, "Only one error set on value $delta");
-        unset($errors[$this->field_name][$langcode][$delta]);
-      }
-      else {
-        $this->assertFalse(isset($errors[$this->field_name][$langcode][$delta]), "No error set on value $delta");
-      }
-    }
-    $this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set');
-
-    // Check that cardinality is validated.
-    $entity->{$this->field_name}[$langcode] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
-    try {
-      field_attach_validate($entity_type, $entity);
-    }
-    catch (FieldValidationException $e) {
-      $errors = $e->errors;
-    }
-    $this->assertEqual($errors[$this->field_name][$langcode][0][0]['error'], 'field_cardinality', t('Cardinality validation failed.'));
-
-  }
-
-  /**
-   * Test field_attach_form().
-   *
-   * This could be much more thorough, but it does verify that the correct
-   * widgets show up.
-   */
-  function testFieldAttachForm() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    $form = array();
-    $form_state = form_state_defaults();
-    field_attach_form($entity_type, $entity, $form, $form_state);
-
-    $langcode = LANGUAGE_NONE;
-    $this->assertEqual($form[$this->field_name][$langcode]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      // field_test_widget uses 'textfield'
-      $this->assertEqual($form[$this->field_name][$langcode][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
-    }
-  }
-
-  /**
-   * Test field_attach_submit().
-   */
-  function testFieldAttachSubmit() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Build the form.
-    $form = array();
-    $form_state = form_state_defaults();
-    field_attach_form($entity_type, $entity, $form, $form_state);
-
-    // Simulate incoming values.
-    $values = array();
-    $weights = array();
-    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-      $values[$delta]['value'] = mt_rand(1, 127);
-      // Assign random weight.
-      do {
-        $weight = mt_rand(0, $this->field['cardinality']);
-      } while (in_array($weight, $weights));
-      $weights[$delta] = $weight;
-      $values[$delta]['_weight'] = $weight;
-    }
-    // Leave an empty value. 'field_test' fields are empty if empty().
-    $values[1]['value'] = 0;
-
-    $langcode = LANGUAGE_NONE;
-    // Pretend the form has been built.
-    drupal_prepare_form('field_test_entity_form', $form, $form_state);
-    drupal_process_form('field_test_entity_form', $form, $form_state);
-    $form_state['values'][$this->field_name][$langcode] = $values;
-    field_attach_submit($entity_type, $entity, $form, $form_state);
-
-    asort($weights);
-    $expected_values = array();
-    foreach ($weights as $key => $value) {
-      if ($key != 1) {
-        $expected_values[] = array('value' => $values[$key]['value']);
-      }
-    }
-    $this->assertIdentical($entity->{$this->field_name}[$langcode], $expected_values, 'Submit filters empty values');
-  }
-}
-
-class FieldInfoTestCase extends FieldTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Field info tests',
-      'description' => 'Get information about existing fields, instances and bundles.',
-      'group' => 'Field API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('field_test');
-  }
-
-  /**
-   * Test that field types and field definitions are correcly cached.
-   */
-  function testFieldInfo() {
-    // Test that field_test module's fields, widgets, and formatters show up.
-    $field_test_info = field_test_field_info();
-    $formatter_info = field_test_field_formatter_info();
-    $widget_info = field_test_field_widget_info();
-    $storage_info = field_test_field_storage_info();
-
-    $info = field_info_field_types();
-    foreach ($field_test_info as $t_key => $field_type) {
-      foreach ($field_type as $key => $val) {
-        $this->assertEqual($info[$t_key][$key], $val, t("Field type $t_key key $key is $val"));
-      }
-      $this->assertEqual($info[$t_key]['module'], 'field_test',  t("Field type field_test module appears"));
-    }
-
-    $info = field_info_formatter_types();
-    foreach ($formatter_info as $f_key => $formatter) {
-      foreach ($formatter as $key => $val) {
-        $this->assertEqual($info[$f_key][$key], $val, t("Formatter type $f_key key $key is $val"));
-      }
-      $this->assertEqual($info[$f_key]['module'], 'field_test',  t("Formatter type field_test module appears"));
-    }
-
-    $info = field_info_widget_types();
-    foreach ($widget_info as $w_key => $widget) {
-      foreach ($widget as $key => $val) {
-        $this->assertEqual($info[$w_key][$key], $val, t("Widget type $w_key key $key is $val"));
-      }
-      $this->assertEqual($info[$w_key]['module'], 'field_test',  t("Widget type field_test module appears"));
-    }
-
-    $info = field_info_storage_types();
-    foreach ($storage_info as $s_key => $storage) {
-      foreach ($storage as $key => $val) {
-        $this->assertEqual($info[$s_key][$key], $val, t("Storage type $s_key key $key is $val"));
-      }
-      $this->assertEqual($info[$s_key]['module'], 'field_test',  t("Storage type field_test module appears"));
-    }
-
-    // Verify that no unexpected instances exist.
-    $core_fields = field_info_fields();
-    $instances = field_info_instances('test_entity', 'test_bundle');
-    $this->assertTrue(empty($instances), t('With no instances, info bundles is empty.'));
-
-    // Create a field, verify it shows up.
-    $field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
-      'type' => 'test_field',
-    );
-    field_create_field($field);
-    $fields = field_info_fields();
-    $this->assertEqual(count($fields), count($core_fields) + 1, t('One new field exists'));
-    $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], t('info fields contains field name'));
-    $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], t('info fields contains field type'));
-    $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', t('info fields contains field module'));
-    $settings = array('test_field_setting' => 'dummy test string');
-    foreach ($settings as $key => $val) {
-      $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, t("Field setting $key has correct default value $val"));
-    }
-    $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, t('info fields contains cardinality 1'));
-    $this->assertEqual($fields[$field['field_name']]['active'], 1, t('info fields contains active 1'));
-
-    // Create an instance, verify that it shows up
-    $instance = array(
-      'field_name' => $field['field_name'],
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'label' => $this->randomName(),
-      'description' => $this->randomName(),
-      'weight' => mt_rand(0, 127),
-      // test_field has no instance settings
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'settings' => array(
-          'test_setting' => 999)));
-    field_create_instance($instance);
-
-    $instances = field_info_instances('test_entity', $instance['bundle']);
-    $this->assertEqual(count($instances), 1, t('One instance shows up in info when attached to a bundle.'));
-    $this->assertTrue($instance < $instances[$instance['field_name']], t('Instance appears in info correctly'));
-  }
-
-  /**
-   * Test that cached field definitions are ready for current runtime context.
-   */
-  function testFieldPrepare() {
-    $field_definition = array(
-      'field_name' => 'field',
-      'type' => 'test_field',
-    );
-    field_create_field($field_definition);
-
-    // Simulate a stored field definition missing a field setting (e.g. a
-    // third-party module adding a new field setting has been enabled, and
-    // existing fields do not know the setting yet).
-    $data = db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']))->fetchField();
-    $data = unserialize($data);
-    $data['settings'] = array();
-    db_update('field_config')
-      ->fields(array('data' => serialize($data)))
-      ->condition('field_name', $field_definition['field_name'])
-      ->execute();
-
-    field_cache_clear();
-
-    // Read the field back.
-    $field = field_info_field($field_definition['field_name']);
-
-    // Check that all expected settings are in place.
-    $field_type = field_info_field_types($field_definition['type']);
-    $this->assertIdentical($field['settings'], $field_type['settings'], t('All expected default field settings are present.'));
-  }
-
-  /**
-   * Test that cached instance definitions are ready for current runtime context.
-   */
-  function testInstancePrepare() {
-    $field_definition = array(
-      'field_name' => 'field',
-      'type' => 'test_field',
-    );
-    field_create_field($field_definition);
-    $instance_definition = array(
-      'field_name' => $field_definition['field_name'],
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-    );
-    field_create_instance($instance_definition);
-
-    // Simulate a stored instance definition missing various settings (e.g. a
-    // third-party module adding instance, widget or display settings has been
-    // enabled, but existing instances do not know the new settings).
-    $data = db_query('SELECT data FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $instance_definition['field_name'], ':bundle' => $instance_definition['bundle']))->fetchField();
-    $data = unserialize($data);
-    $data['settings'] = array();
-    $data['widget']['settings'] = 'unavailable_widget';
-    $data['widget']['settings'] = array();
-    $data['display']['default']['type'] = 'unavailable_formatter';
-    $data['display']['default']['settings'] = array();
-    db_update('field_config_instance')
-      ->fields(array('data' => serialize($data)))
-      ->condition('field_name', $instance_definition['field_name'])
-      ->condition('bundle', $instance_definition['bundle'])
-      ->execute();
-
-    field_cache_clear();
-
-    // Read the instance back.
-    $instance = field_info_instance($instance_definition['entity_type'], $instance_definition['field_name'], $instance_definition['bundle']);
-
-    // Check that all expected instance settings are in place.
-    $field_type = field_info_field_types($field_definition['type']);
-    $this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
-
-    // Check that the default widget is used and expected settings are in place.
-    $this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
-    $widget_type = field_info_widget_types($instance['widget']['type']);
-    $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
-
-    // Check that display settings are set for the 'default' mode.
-    $display = $instance['display']['default'];
-    $this->assertIdentical($display['type'], $field_type['default_formatter'], t("Formatter is set for the 'default' view mode"));
-    $formatter_type = field_info_formatter_types($display['type']);
-    $this->assertIdentical($display['settings'], $formatter_type['settings'] , t("Formatter settings are set for the 'default' view mode"));
-  }
-
-  /**
-   * Test that instances on disabled entity types are filtered out.
-   */
-  function testInstanceDisabledEntityType() {
-    // For this test the field type and the entity type must be exposed by
-    // different modules.
-    $field_definition = array(
-      'field_name' => 'field',
-      'type' => 'test_field',
-    );
-    field_create_field($field_definition);
-    $instance_definition = array(
-      'field_name' => 'field',
-      'entity_type' => 'comment',
-      'bundle' => 'comment_node_article',
-    );
-    field_create_instance($instance_definition);
-
-    // Disable coment module. This clears field_info cache.
-    module_disable(array('comment'));
-    $this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), t('No instances are returned on disabled entity types.'));
-  }
-
-  /**
-   * Test that the field_info settings convenience functions work.
-   */
-  function testSettingsInfo() {
-    $info = field_test_field_info();
-    foreach ($info as $type => $data) {
-      $this->assertIdentical(field_info_field_settings($type), $data['settings'], "field_info_field_settings returns {$type}'s field settings");
-      $this->assertIdentical(field_info_instance_settings($type), $data['instance_settings'], "field_info_field_settings returns {$type}'s field instance settings");
-    }
-
-    $info = field_test_field_widget_info();
-    foreach ($info as $type => $data) {
-      $this->assertIdentical(field_info_widget_settings($type), $data['settings'], "field_info_widget_settings returns {$type}'s widget settings");
-    }
-
-    $info = field_test_field_formatter_info();
-    foreach ($info as $type => $data) {
-      $this->assertIdentical(field_info_formatter_settings($type), $data['settings'], "field_info_formatter_settings returns {$type}'s formatter settings");
-    }
-  }
-}
-
-class FieldFormTestCase extends FieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Field form tests',
-      'description' => 'Test Field form handling.',
-      'group' => 'Field API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('field_test');
-
-    $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
-    $this->drupalLogin($web_user);
-
-    $this->field_single = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field');
-    $this->field_multiple = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field', 'cardinality' => 4);
-    $this->field_unlimited = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
-
-    $this->instance = array(
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      'settings' => array(
-        'test_instance_setting' => $this->randomName(),
-      ),
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'label' => 'Test Field',
-        'settings' => array(
-          'test_widget_setting' => $this->randomName(),
-        )
-      )
-    );
-  }
-
-  function testFieldFormSingle() {
-    $this->field = $this->field_single;
-    $this->field_name = $this->field['field_name'];
-    $this->instance['field_name'] = $this->field_name;
-    field_create_field($this->field);
-    field_create_instance($this->instance);
-    $langcode = LANGUAGE_NONE;
-
-    // Display creation form.
-    $this->drupalGet('test-entity/add/test-bundle');
-    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
-    $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
-    // TODO : check that the widget is populated with default value ?
-
-    // Submit with invalid value (field-level validation).
-    $edit = array("{$this->field_name}[$langcode][0][value]" => -1);
-    $this->drupalPost(NULL, $edit, t('Save'));
-    $this->assertRaw(t('%name does not accept the value -1.', array('%name' => $this->instance['label'])), 'Field validation fails with invalid input.');
-    // TODO : check that the correct field is flagged for error.
-
-    // Create an entity
-    $value = mt_rand(1, 127);
-    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
-    $this->drupalPost(NULL, $edit, t('Save'));
-    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
-    $id = $match[1];
-    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
-    $entity = field_test_entity_test_load($id);
-    $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved');
-
-    // Display edit form.
-    $this->drupalGet('test-entity/' . $id . '/edit');
-    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", $value, 'Widget is displayed with the correct default value');
-    $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
-
-    // Update the entity.
-    $value = mt_rand(1, 127);
-    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
-    $this->drupalPost(NULL, $edit, t('Save'));
-    $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
-    $entity = field_test_entity_test_load($id);
-    $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was updated');
-
-    // Empty the field.
-    $value = '';
-    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
-    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
-    $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
-    $entity = field_test_entity_test_load($id);
-    $this->assertIdentical($entity->{$this->field_name}, array(), 'Field was emptied');
-
-  }
-
-  function testFieldFormSingleRequired() {
-    $this->field = $this->field_single;
-    $this->field_name = $this->field['field_name'];
-    $this->instance['field_name'] = $this->field_name;
-    $this->instance['required'] = TRUE;
-    field_create_field($this->field);
-    field_create_instance($this->instance);
-    $langcode = LANGUAGE_NONE;
-
-    // Submit with missing required value.
-    $edit = array();
-    $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
-
-    // Create an entity
-    $value = mt_rand(1, 127);
-    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
-    $this->drupalPost(NULL, $edit, t('Save'));
-    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
-    $id = $match[1];
-    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
-    $entity = field_test_entity_test_load($id);
-    $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved');
-
-    // Edit with missing required value.
-    $value = '';
-    $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
-    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
-  }
-
-//  function testFieldFormMultiple() {
-//    $this->field = $this->field_multiple;
-//    $this->field_name = $this->field['field_name'];
-//    $this->instance['field_name'] = $this->field_name;
-//    field_create_field($this->field);
-//    field_create_instance($this->instance);
-//  }
-
-  function testFieldFormUnlimited() {
-    $this->field = $this->field_unlimited;
-    $this->field_name = $this->field['field_name'];
-    $this->instance['field_name'] = $this->field_name;
-    field_create_field($this->field);
-    field_create_instance($this->instance);
-    $langcode = LANGUAGE_NONE;
-
-    // Display creation form -> 1 widget.
-    $this->drupalGet('test-entity/add/test-bundle');
-    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
-    $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
-
-    // Press 'add more' button -> 2 widgets.
-    $this->drupalPost(NULL, array(), t('Add another item'));
-    $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
-    $this->assertFieldByName("{$this->field_name}[$langcode][1][value]", '', 'New widget is displayed');
-    $this->assertNoField("{$this->field_name}[$langcode][2][value]", 'No extraneous widget is displayed');
-    // TODO : check that non-field inpurs are preserved ('title')...
-
-    // Yet another time so that we can play with more values -> 3 widgets.
-    $this->drupalPost(NULL, array(), t('Add another item'));
-
-    // Prepare values and weights.
-    $count = 3;
-    $delta_range = $count - 1;
-    $values = $weights = $pattern = $expected_values = $edit = array();
-    for ($delta = 0; $delta <= $delta_range; $delta++) {
-      // Assign unique random weights.
-      do {
-        $weight = mt_rand(-$delta_range, $delta_range);
-      } while (in_array($weight, $weights));
-      $value = mt_rand(1, 127);
-      $edit["$this->field_name[$langcode][$delta][value]"] = $value;
-      $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
-      // We'll need three slightly different formats to check the values.
-      $values[$delta] = $value;
-      $weights[$delta] = $weight;
-      $field_values[$weight]['value'] = (string) $value;
-      $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
-    }
-
-    // Press 'add more' button -> 4 widgets
-    $this->drupalPost(NULL, $edit, t('Add another item'));
-    for ($delta = 0; $delta <= $delta_range; $delta++) {
-      $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
-      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
-    }
-    ksort($pattern);
-    $pattern = implode('.*', array_values($pattern));
-    $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
-    $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
-    $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
-    $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
-
-    // Submit the form and create the entity.
-    $this->drupalPost(NULL, $edit, t('Save'));
-    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
-    $id = $match[1];
-    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
-    $entity = field_test_entity_test_load($id);
-    ksort($field_values);
-    $field_values = array_values($field_values);
-    $this->assertIdentical($entity->{$this->field_name}[$langcode], $field_values, 'Field values were saved in the correct order');
-
-    // Display edit form: check that the expected number of widgets is
-    // displayed, with correct values change values, reorder, leave an empty
-    // value in the middle.
-    // Submit: check that the entity is updated with correct values
-    // Re-submit: check that the field can be emptied.
-
-    // Test with several multiple fields in a form
-  }
-
-  function testFieldFormJSAddMore() {
-    $this->field = $this->field_unlimited;
-    $this->field_name = $this->field['field_name'];
-    $this->instance['field_name'] = $this->field_name;
-    field_create_field($this->field);
-    field_create_instance($this->instance);
-    $langcode = LANGUAGE_NONE;
-
-    // Display creation form -> 1 widget.
-    $this->drupalGet('test-entity/add/test-bundle');
-
-    // Press 'add more' button a couple times -> 3 widgets.
-    // drupalPostAJAX() will not work iteratively, so we add those through
-    // non-JS submission.
-    $this->drupalPost(NULL, array(), t('Add another item'));
-    $this->drupalPost(NULL, array(), t('Add another item'));
-
-    // Prepare values and weights.
-    $count = 3;
-    $delta_range = $count - 1;
-    $values = $weights = $pattern = $expected_values = $edit = array();
-    for ($delta = 0; $delta <= $delta_range; $delta++) {
-      // Assign unique random weights.
-      do {
-        $weight = mt_rand(-$delta_range, $delta_range);
-      } while (in_array($weight, $weights));
-      $value = mt_rand(1, 127);
-      $edit["$this->field_name[$langcode][$delta][value]"] = $value;
-      $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
-      // We'll need three slightly different formats to check the values.
-      $values[$delta] = $value;
-      $weights[$delta] = $weight;
-      $field_values[$weight]['value'] = (string) $value;
-      $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
-    }
-    // Press 'add more' button through AJAX, and place the expected HTML result
-    // as the tested content.
-    $commands = $this->drupalPostAJAX(NULL, $edit, $this->field_name . '_add_more');
-    $this->content = $commands[1]['data'];
-
-    for ($delta = 0; $delta <= $delta_range; $delta++) {
-      $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
-      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
-    }
-    ksort($pattern);
-    $pattern = implode('.*', array_values($pattern));
-    $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
-    $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
-    $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
-    $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
-  }
-
-  /**
-   * Tests widgets handling multiple values.
-   */
-  function testFieldFormMultipleWidget() {
-    // Create a field with fixed cardinality and an instance using a multiple
-    // widget.
-    $this->field = $this->field_multiple;
-    $this->field_name = $this->field['field_name'];
-    $this->instance['field_name'] = $this->field_name;
-    $this->instance['widget']['type'] = 'test_field_widget_multiple';
-    field_create_field($this->field);
-    field_create_instance($this->instance);
-    $langcode = LANGUAGE_NONE;
-
-    // Display creation form.
-    $this->drupalGet('test-entity/add/test-bundle');
-    $this->assertFieldByName("{$this->field_name}[$langcode]", '', t('Widget is displayed.'));
-
-    // Create entity with three values.
-    $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3');
-    $this->drupalPost(NULL, $edit, t('Save'));
-    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
-    $id = $match[1];
-
-    // Check that the values were saved.
-    $entity_init = field_test_create_stub_entity($id);
-    $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3));
-
-    // Display the form, check that the values are correctly filled in.
-    $this->drupalGet('test-entity/' . $id . '/edit');
-    $this->assertFieldByName("{$this->field_name}[$langcode]", '1, 2, 3', t('Widget is displayed.'));
-
-    // Submit the form with more values than the field accepts.
-    $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3, 4, 5');
-    $this->drupalPost(NULL, $edit, t('Save'));
-    $this->assertRaw('this field cannot hold more than 4 values', t('Form validation failed.'));
-    // Check that the field values were not submitted.
-    $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3));
-  }
-
-  /**
-   * Tests fields with no 'edit' access.
-   */
-  function testFieldFormAccess() {
-    // Create a "regular" field.
-    $field = $this->field_single;
-    $field_name = $field['field_name'];
-    $instance = $this->instance;
-    $instance['field_name'] = $field_name;
-    field_create_field($field);
-    field_create_instance($instance);
-
-    // Create a field with no edit access - see field_test_field_access().
-    $field_no_access = array(
-      'field_name' => 'field_no_edit_access',
-      'type' => 'test_field',
-    );
-    $field_name_no_access = $field_no_access['field_name'];
-    $instance_no_access = array(
-      'field_name' => $field_name_no_access,
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'default_value' => array(0 => array('value' => 99)),
-    );
-    field_create_field($field_no_access);
-    field_create_instance($instance_no_access);
-
-    $langcode = LANGUAGE_NONE;
-
-    // Display creation form.
-    $this->drupalGet('test-entity/add/test-bundle');
-    $this->assertNoFieldByName("{$field_name_no_access}[$langcode][0][value]", '', t('Widget is not displayed if field access is denied.'));
-
-    // Create entity.
-    $edit = array("{$field_name}[$langcode][0][value]" => 1);
-    $this->drupalPost(NULL, $edit, t('Save'));
-    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
-    $id = $match[1];
-
-    // Check that the default value was saved.
-    $entity = field_test_entity_test_load($id);
-    $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('Default value was saved for the field with no edit access.'));
-    $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 1, t('Entered value vas saved for the field with edit access.'));
-
-    // Create a new revision.
-    $edit = array("{$field_name}[$langcode][0][value]" => 2, 'revision' => TRUE);
-    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
-
-    // Check that the new revision has the expected values.
-    $entity = field_test_entity_test_load($id);
-    $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.'));
-    $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
-
-    // Check that the revision is also saved in the revisions table.
-    $entity = field_test_entity_test_load($id, $entity->ftvid);
-    $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.'));
-    $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
-  }
-}
-
-class FieldDisplayAPITestCase extends FieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Field Display API tests',
-      'description' => 'Test the display API.',
-      'group' => 'Field API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('field_test');
-
-    // Create a field and instance.
-    $this->field_name = 'test_field';
-    $this->label = $this->randomName();
-    $this->cardinality = 4;
-
-    $this->field = array(
-      'field_name' => $this->field_name,
-      'type' => 'test_field',
-      'cardinality' => $this->cardinality,
-    );
-    $this->instance = array(
-      'field_name' => $this->field_name,
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'label' => $this->label,
-      'display' => array(
-        'default' => array(
-          'type' => 'field_test_default',
-          'settings' => array(
-            'test_formatter_setting' => $this->randomName(),
-          ),
-        ),
-        'teaser' => array(
-          'type' => 'field_test_default',
-          'settings' => array(
-            'test_formatter_setting' => $this->randomName(),
-          ),
-        ),
-      ),
-    );
-    field_create_field($this->field);
-    field_create_instance($this->instance);
-
-    // Create an entity with values.
-    $this->values = $this->_generateTestFieldValues($this->cardinality);
-    $this->entity = field_test_create_stub_entity();
-    $this->is_new = TRUE;
-    $this->entity->{$this->field_name}[LANGUAGE_NONE] = $this->values;
-    field_test_entity_save($this->entity);
-  }
-
-  /**
-   * Test the field_view_field() function.
-   */
-  function testFieldViewField() {
-    // No display settings: check that default display settings are used.
-    $output = field_view_field('test_entity', $this->entity, $this->field_name);
-    $this->drupalSetContent(drupal_render($output));
-    $settings = field_info_formatter_settings('field_test_default');
-    $setting = $settings['test_formatter_setting'];
-    $this->assertText($this->label, t('Label was displayed.'));
-    foreach($this->values as $delta => $value) {
-      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // Check that explicit display settings are used.
-    $display = array(
-      'label' => 'hidden',
-      'type' => 'field_test_multiple',
-      'settings' => array(
-        'test_formatter_setting_multiple' => $this->randomName(),
-      ),
-    );
-    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
-    $this->drupalSetContent(drupal_render($output));
-    $setting = $display['settings']['test_formatter_setting_multiple'];
-    $this->assertNoText($this->label, t('Label was not displayed.'));
-    $array = array();
-    foreach($this->values as $delta => $value) {
-      $array[] = $delta . ':' . $value['value'];
-    }
-    $this->assertText($setting . '|' . implode('|', $array), t('Values were displayed with expected setting.'));
-
-    // Check the prepare_view steps are invoked.
-    $display = array(
-      'label' => 'hidden',
-      'type' => 'field_test_with_prepare_view',
-      'settings' => array(
-        'test_formatter_setting_additional' => $this->randomName(),
-      ),
-    );
-    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
-    $view = drupal_render($output);
-    $this->drupalSetContent($view);
-    $setting = $display['settings']['test_formatter_setting_additional'];
-    $this->assertNoText($this->label, t('Label was not displayed.'));
-    foreach ($this->values as $delta => $value) {
-      $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // View mode: check that display settings specified in the instance are
-    // used.
-    $output = field_view_field('test_entity', $this->entity, $this->field_name, 'teaser');
-    $this->drupalSetContent(drupal_render($output));
-    $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting'];
-    $this->assertText($this->label, t('Label was displayed.'));
-    foreach($this->values as $delta => $value) {
-      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // Unknown view mode: check that display settings for 'default' view mode
-    // are used.
-    $output = field_view_field('test_entity', $this->entity, $this->field_name, 'unknown_view_mode');
-    $this->drupalSetContent(drupal_render($output));
-    $setting = $this->instance['display']['default']['settings']['test_formatter_setting'];
-    $this->assertText($this->label, t('Label was displayed.'));
-    foreach($this->values as $delta => $value) {
-      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-  }
-
-  /**
-   * Test the field_view_value() function.
-   */
-  function testFieldViewValue() {
-    // No display settings: check that default display settings are used.
-    $settings = field_info_formatter_settings('field_test_default');
-    $setting = $settings['test_formatter_setting'];
-    foreach ($this->values as $delta => $value) {
-      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
-      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item);
-      $this->drupalSetContent(drupal_render($output));
-      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // Check that explicit display settings are used.
-    $display = array(
-      'type' => 'field_test_multiple',
-      'settings' => array(
-        'test_formatter_setting_multiple' => $this->randomName(),
-      ),
-    );
-    $setting = $display['settings']['test_formatter_setting_multiple'];
-    $array = array();
-    foreach ($this->values as $delta => $value) {
-      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
-      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
-      $this->drupalSetContent(drupal_render($output));
-      $this->assertText($setting . '|0:' .  $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // Check that prepare_view steps are invoked.
-    $display = array(
-      'type' => 'field_test_with_prepare_view',
-      'settings' => array(
-        'test_formatter_setting_additional' => $this->randomName(),
-      ),
-    );
-    $setting = $display['settings']['test_formatter_setting_additional'];
-    $array = array();
-    foreach ($this->values as $delta => $value) {
-      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
-      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
-      $this->drupalSetContent(drupal_render($output));
-      $this->assertText($setting . '|' .  $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // View mode: check that display settings specified in the instance are
-    // used.
-    $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting'];
-    foreach ($this->values as $delta => $value) {
-      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
-      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'teaser');
-      $this->drupalSetContent(drupal_render($output));
-      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-
-    // Unknown view mode: check that display settings for 'default' view mode
-    // are used.
-    $setting = $this->instance['display']['default']['settings']['test_formatter_setting'];
-    foreach ($this->values as $delta => $value) {
-      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
-      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'unknown_view_mode');
-      $this->drupalSetContent(drupal_render($output));
-      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
-    }
-  }
-}
-
-class FieldCrudTestCase extends FieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Field CRUD tests',
-      'description' => 'Test field create, read, update, and delete.',
-      'group' => 'Field API',
-    );
-  }
-
-  function setUp() {
-    // field_update_field() tests use number.module
-    parent::setUp('field_test', 'number');
-  }
-
-  // TODO : test creation with
-  // - a full fledged $field structure, check that all the values are there
-  // - a minimal $field structure, check all default values are set
-  // defer actual $field comparison to a helper function, used for the two cases above
-
-  /**
-   * Test the creation of a field.
-   */
-  function testCreateField() {
-    $field_definition = array(
-      'field_name' => 'field_2',
-      'type' => 'test_field',
-    );
-    field_test_memorize();
-    $field_definition = field_create_field($field_definition);
-    $mem = field_test_memorize();
-    $this->assertIdentical($mem['field_test_field_create_field'][0][0], $field_definition, 'hook_field_create_field() called with correct arguments.');
-
-    // Read the raw record from the {field_config_instance} table.
-    $result = db_query('SELECT * FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']));
-    $record = $result->fetchAssoc();
-    $record['data'] = unserialize($record['data']);
-
-    // Ensure that basic properties are preserved.
-    $this->assertEqual($record['field_name'], $field_definition['field_name'], t('The field name is properly saved.'));
-    $this->assertEqual($record['type'], $field_definition['type'], t('The field type is properly saved.'));
-
-    // Ensure that cardinality defaults to 1.
-    $this->assertEqual($record['cardinality'], 1, t('Cardinality defaults to 1.'));
-
-    // Ensure that default settings are present.
-    $field_type = field_info_field_types($field_definition['type']);
-    $this->assertIdentical($record['data']['settings'], $field_type['settings'], t('Default field settings have been written.'));
-
-    // Ensure that default storage was set.
-    $this->assertEqual($record['storage_type'], variable_get('field_storage_default'), t('The field type is properly saved.'));
-
-    // Guarantee that the name is unique.
-    try {
-      field_create_field($field_definition);
-      $this->fail(t('Cannot create two fields with the same name.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create two fields with the same name.'));
-    }
-
-    // Check that field type is required.
-    try {
-      $field_definition = array(
-        'field_name' => 'field_1',
-      );
-      field_create_field($field_definition);
-      $this->fail(t('Cannot create a field with no type.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create a field with no type.'));
-    }
-
-    // Check that field name is required.
-    try {
-      $field_definition = array(
-        'type' => 'test_field'
-      );
-      field_create_field($field_definition);
-      $this->fail(t('Cannot create an unnamed field.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create an unnamed field.'));
-    }
-
-    // Check that field name must start with a letter or _.
-    try {
-      $field_definition = array(
-        'field_name' => '2field_2',
-        'type' => 'test_field',
-      );
-      field_create_field($field_definition);
-      $this->fail(t('Cannot create a field with a name starting with a digit.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create a field with a name starting with a digit.'));
-    }
-
-    // Check that field name must only contain lowercase alphanumeric or _.
-    try {
-      $field_definition = array(
-        'field_name' => 'field#_3',
-        'type' => 'test_field',
-      );
-      field_create_field($field_definition);
-      $this->fail(t('Cannot create a field with a name containing an illegal character.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create a field with a name containing an illegal character.'));
-    }
-
-    // Check that field name cannot be longer than 32 characters long.
-    try {
-      $field_definition = array(
-        'field_name' => '_12345678901234567890123456789012',
-        'type' => 'test_field',
-      );
-      field_create_field($field_definition);
-      $this->fail(t('Cannot create a field with a name longer than 32 characters.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create a field with a name longer than 32 characters.'));
-    }
-
-    // Check that field name can not be an entity key.
-    // "ftvid" is known as an entity key from the "test_entity" type.
-    try {
-      $field_definition = array(
-        'type' => 'test_field',
-        'field_name' => 'ftvid',
-      );
-      $field = field_create_field($field_definition);
-      $this->fail(t('Cannot create a field bearing the name of an entity key.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create a field bearing the name of an entity key.'));
-    }
-  }
-
-  /**
-   * Test failure to create a field.
-   */
-  function testCreateFieldFail() {
-    $field_name = 'duplicate';
-    $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
-    $query = db_select('field_config')->condition('field_name', $field_name)->countQuery();
-
-    // The field does not appear in field_config.
-    $count = $query->execute()->fetchField();
-    $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
-
-    // Try to create the field.
-    try {
-      $field = field_create_field($field_definition);
-      $this->assertTrue(FALSE, 'Field creation (correctly) fails.');
-    }
-    catch (Exception $e) {
-      $this->assertTrue(TRUE, 'Field creation (correctly) fails.');
-    }
-
-    // The field does not appear in field_config.
-    $count = $query->execute()->fetchField();
-    $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
-  }
-
-  /**
-   * Test reading back a field definition.
-   */
-  function testReadField() {
-    $field_definition = array(
-      'field_name' => 'field_1',
-      'type' => 'test_field',
-    );
-    field_create_field($field_definition);
-
-    // Read the field back.
-    $field = field_read_field($field_definition['field_name']);
-    $this->assertTrue($field_definition < $field, t('The field was properly read.'));
-  }
-
-  /**
-   * Test creation of indexes on data column.
-   */
-  function testFieldIndexes() {
-    // Check that indexes specified by the field type are used by default.
-    $field_definition = array(
-      'field_name' => 'field_1',
-      'type' => 'test_field',
-    );
-    field_create_field($field_definition);
-    $field = field_read_field($field_definition['field_name']);
-    $expected_indexes = array('value' => array('value'));
-    $this->assertEqual($field['indexes'], $expected_indexes, t('Field type indexes saved by default'));
-
-    // Check that indexes specified by the field definition override the field
-    // type indexes.
-    $field_definition = array(
-      'field_name' => 'field_2',
-      'type' => 'test_field',
-      'indexes' => array(
-        'value' => array(),
-      ),
-    );
-    field_create_field($field_definition);
-    $field = field_read_field($field_definition['field_name']);
-    $expected_indexes = array('value' => array());
-    $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes override field type indexes'));
-
-    // Check that indexes specified by the field definition add to the field
-    // type indexes.
-    $field_definition = array(
-      'field_name' => 'field_3',
-      'type' => 'test_field',
-      'indexes' => array(
-        'value_2' => array('value'),
-      ),
-    );
-    field_create_field($field_definition);
-    $field = field_read_field($field_definition['field_name']);
-    $expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
-    $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes are merged with field type indexes'));
-  }
-
-  /**
-   * Test the deletion of a field.
-   */
-  function testDeleteField() {
-    // TODO: Also test deletion of the data stored in the field ?
-
-    // Create two fields (so we can test that only one is deleted).
-    $this->field = array('field_name' => 'field_1', 'type' => 'test_field');
-    field_create_field($this->field);
-    $this->another_field = array('field_name' => 'field_2', 'type' => 'test_field');
-    field_create_field($this->another_field);
-
-    // Create instances for each.
-    $this->instance_definition = array(
-      'field_name' => $this->field['field_name'],
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'widget' => array(
-        'type' => 'test_field_widget',
-      ),
-    );
-    field_create_instance($this->instance_definition);
-    $this->another_instance_definition = $this->instance_definition;
-    $this->another_instance_definition['field_name'] = $this->another_field['field_name'];
-    field_create_instance($this->another_instance_definition);
-
-    // Test that the first field is not deleted, and then delete it.
-    $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
-    $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field is not marked for deletion.'));
-    field_delete_field($this->field['field_name']);
-
-    // Make sure that the field is marked as deleted when it is specifically
-    // loaded.
-    $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
-    $this->assertTrue(!empty($field['deleted']), t('A deleted field is marked for deletion.'));
-
-    // Make sure that this field's instance is marked as deleted when it is
-    // specifically loaded.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
-    $this->assertTrue(!empty($instance['deleted']), t('An instance for a deleted field is marked for deletion.'));
-
-    // Try to load the field normally and make sure it does not show up.
-    $field = field_read_field($this->field['field_name']);
-    $this->assertTrue(empty($field), t('A deleted field is not loaded by default.'));
-
-    // Try to load the instance normally and make sure it does not show up.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertTrue(empty($instance), t('An instance for a deleted field is not loaded by default.'));
-
-    // Make sure the other field (and its field instance) are not deleted.
-    $another_field = field_read_field($this->another_field['field_name']);
-    $this->assertTrue(!empty($another_field) && empty($another_field['deleted']), t('A non-deleted field is not marked for deletion.'));
-    $another_instance = field_read_instance('test_entity', $this->another_instance_definition['field_name'], $this->another_instance_definition['bundle']);
-    $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('An instance of a non-deleted field is not marked for deletion.'));
-
-    // Try to create a new field the same name as a deleted field and
-    // write data into it.
-    field_create_field($this->field);
-    field_create_instance($this->instance_definition);
-    $field = field_read_field($this->field['field_name']);
-    $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field with a previously used name is created.'));
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new instance for a previously used field name is created.'));
-
-    // Save an entity with data for the field
-    $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
-    $langcode = LANGUAGE_NONE;
-    $values[0]['value'] = mt_rand(1, 127);
-    $entity->{$field['field_name']}[$langcode] = $values;
-    $entity_type = 'test_entity';
-    field_attach_insert('test_entity', $entity);
-
-    // Verify the field is present on load
-    $entity = field_test_create_stub_entity(0, 0, $this->instance_definition['bundle']);
-    field_attach_load($entity_type, array(0 => $entity));
-    $this->assertIdentical(count($entity->{$field['field_name']}[$langcode]), count($values), "Data in previously deleted field saves and loads correctly");
-    foreach ($values as $delta => $value) {
-      $this->assertEqual($entity->{$field['field_name']}[$langcode][$delta]['value'], $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
-    }
-  }
-
-  function testUpdateNonExistentField() {
-    $test_field = array('field_name' => 'does_not_exist', 'type' => 'number_decimal');
-    try {
-      field_update_field($test_field);
-      $this->fail(t('Cannot update a field that does not exist.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot update a field that does not exist.'));
-    }
-  }
-
-  function testUpdateFieldType() {
-    $field = array('field_name' => 'field_type', 'type' => 'number_decimal');
-    $field = field_create_field($field);
-
-    $test_field = array('field_name' => 'field_type', 'type' => 'number_integer');
-    try {
-      field_update_field($test_field);
-      $this->fail(t('Cannot update a field to a different type.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot update a field to a different type.'));
-    }
-  }
-
-  /**
-   * Test updating a field.
-   */
-  function testUpdateField() {
-    // Create a decimal 5.2 field.
-    $field = array('field_name' => 'decimal53', 'type' => 'number_decimal', 'cardinality' => 3, 'settings' => array('precision' => 5, 'scale' => 2));
-    $field = field_create_field($field);
-    $instance = array('field_name' => 'decimal53', 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
-    $instance = field_create_instance($instance);
-
-    // Update it to a deciaml 5.3 field.
-    $field['settings']['scale'] = 3;
-    field_update_field($field);
-
-    // Save values with 2, 3, and 4 decimal places.
-    $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
-    $entity->decimal53[LANGUAGE_NONE][0]['value'] = '1.23';
-    $entity->decimal53[LANGUAGE_NONE][1]['value'] = '1.235';
-    $entity->decimal53[LANGUAGE_NONE][2]['value'] = '1.2355';
-    field_attach_insert('test_entity', $entity);
-    $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
-
-    // Verify that the updated 5.3 field rounds to 3 decimal places.
-    field_attach_load('test_entity', array(0 => $entity));
-    $this->assertEqual($entity->decimal53[LANGUAGE_NONE][0]['value'], '1.23', t('2 decimal places are left alone'));
-    $this->assertEqual($entity->decimal53[LANGUAGE_NONE][1]['value'], '1.235', t('3 decimal places are left alone'));
-    $this->assertEqual($entity->decimal53[LANGUAGE_NONE][2]['value'], '1.236', t('4 decimal places are rounded to 3'));
-  }
-
-  /**
-   * Test field type modules forbidding an update.
-   */
-  function testUpdateFieldForbid() {
-    $field = array('field_name' => 'forbidden', 'type' => 'test_field', 'settings' => array('changeable' => 0, 'unchangeable' => 0));
-    $field = field_create_field($field);
-    $field['settings']['changeable']++;
-    try {
-      field_update_field($field);
-      $this->pass(t("A changeable setting can be updated."));
-    }
-    catch (FieldException $e) {
-      $this->fail(t("An unchangeable setting cannot be updated."));
-    }
-    $field['settings']['unchangeable']++;
-    try {
-      field_update_field($field);
-      $this->fail(t("An unchangeable setting can be updated."));
-    }
-    catch (FieldException $e) {
-      $this->pass(t("An unchangeable setting cannot be updated."));
-    }
-  }
-
-  /**
-   * Test that fields are properly marked active or inactive.
-   */
-  function testActive() {
-    $field_definition = array(
-      'field_name' => 'field_1',
-      'type' => 'test_field',
-      // For this test, we need a storage backend provided by a different
-      // module than field_test.module.
-      'storage' => array(
-        'type' => 'field_sql_storage',
-      ),
-    );
-    field_create_field($field_definition);
-
-    // Test disabling and enabling:
-    // - the field type module,
-    // - the storage module,
-    // - both.
-    $this->_testActiveHelper($field_definition, array('field_test'));
-    $this->_testActiveHelper($field_definition, array('field_sql_storage'));
-    $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
-  }
-
-  /**
-   * Helper function for testActive().
-   *
-   * Test dependency between a field and a set of modules.
-   *
-   * @param $field_definition
-   *   A field definition.
-   * @param $modules
-   *   An aray of module names. The field will be tested to be inactive as long
-   *   as any of those modules is disabled.
-   */
-  function _testActiveHelper($field_definition, $modules) {
-    $field_name = $field_definition['field_name'];
-
-    // Read the field.
-    $field = field_read_field($field_name);
-    $this->assertTrue($field_definition <= $field, t('The field was properly read.'));
-
-    module_disable($modules, FALSE);
-
-    $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
-    $this->assertTrue(isset($fields[$field_name]) && $field_definition < $field, t('The field is properly read when explicitly fetching inactive fields.'));
-
-    // Re-enable modules one by one, and check that the field is still inactive
-    // while some modules remain disabled.
-    while ($modules) {
-      $field = field_read_field($field_name);
-      $this->assertTrue(empty($field), t('%modules disabled. The field is marked inactive.', array('%modules' => implode(', ', $modules))));
-
-      $module = array_shift($modules);
-      module_enable(array($module), FALSE);
-    }
-
-    // Check that the field is active again after all modules have been
-    // enabled.
-    $field = field_read_field($field_name);
-    $this->assertTrue($field_definition <= $field, t('The field was was marked active.'));
-  }
-}
-
-class FieldInstanceCrudTestCase extends FieldTestCase {
-  protected $field;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Field instance CRUD tests',
-      'description' => 'Create field entities by attaching fields to entities.',
-      'group' => 'Field API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('field_test');
-
-    $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
-      'type' => 'test_field',
-    );
-    field_create_field($this->field);
-    $this->instance_definition = array(
-      'field_name' => $this->field['field_name'],
-      'entity_type' => 'test_entity',
-      'bundle' => 'test_bundle',
-    );
-  }
-
-  // TODO : test creation with
-  // - a full fledged $instance structure, check that all the values are there
-  // - a minimal $instance structure, check all default values are set
-  // defer actual $instance comparison to a helper function, used for the two cases above,
-  // and for testUpdateFieldInstance
-
-  /**
-   * Test the creation of a field instance.
-   */
-  function testCreateFieldInstance() {
-    field_create_instance($this->instance_definition);
-
-    // Read the raw record from the {field_config_instance} table.
-    $result = db_query('SELECT * FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $this->instance_definition['field_name'], ':bundle' => $this->instance_definition['bundle']));
-    $record = $result->fetchAssoc();
-    $record['data'] = unserialize($record['data']);
-
-    $field_type = field_info_field_types($this->field['type']);
-    $widget_type = field_info_widget_types($field_type['default_widget']);
-    $formatter_type = field_info_formatter_types($field_type['default_formatter']);
-
-    // Check that default values are set.
-    $this->assertIdentical($record['data']['required'], FALSE, t('Required defaults to false.'));
-    $this->assertIdentical($record['data']['label'], $this->instance_definition['field_name'], t('Label defaults to field name.'));
-    $this->assertIdentical($record['data']['description'], '', t('Description defaults to empty string.'));
-    $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], t('Default widget has been written.'));
-    $this->assertTrue(isset($record['data']['display']['default']), t('Display for "full" view_mode has been written.'));
-    $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], t('Default formatter for "full" view_mode has been written.'));
-
-    // Check that default settings are set.
-    $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , t('Default instance settings have been written.'));
-    $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , t('Default widget settings have been written.'));
-    $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" view_mode have been written.'));
-
-    // Guarantee that the field/bundle combination is unique.
-    try {
-      field_create_instance($this->instance_definition);
-      $this->fail(t('Cannot create two instances with the same field / bundle combination.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create two instances with the same field / bundle combination.'));
-    }
-
-    // Check that the specified field exists.
-    try {
-      $this->instance_definition['field_name'] = $this->randomName();
-      field_create_instance($this->instance_definition);
-      $this->fail(t('Cannot create an instance of a non-existing field.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create an instance of a non-existing field.'));
-    }
-
-    // Create a field restricted to a specific entity type.
-    $field_restricted = array(
-      'field_name' => drupal_strtolower($this->randomName()),
-      'type' => 'test_field',
-      'entity_types' => array('test_cacheable_entity'),
-    );
-    field_create_field($field_restricted);
-
-    // Check that an instance can be added to an entity type allowed
-    // by the field.
-    try {
-      $instance = $this->instance_definition;
-      $instance['field_name'] = $field_restricted['field_name'];
-      $instance['entity_type'] = 'test_cacheable_entity';
-      field_create_instance($instance);
-      $this->pass(t('Can create an instance on an entity type allowed by the field.'));
-    }
-    catch (FieldException $e) {
-      $this->fail(t('Can create an instance on an entity type allowed by the field.'));
-    }
-
-    // Check that an instance cannot be added to an entity type
-    // forbidden by the field.
-    try {
-      $instance = $this->instance_definition;
-      $instance['field_name'] = $field_restricted['field_name'];
-      field_create_instance($instance);
-      $this->fail(t('Cannot create an instance on an entity type forbidden by the field.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create an instance on an entity type forbidden by the field.'));
-    }
-
-    // TODO: test other failures.
-  }
-
-  /**
-   * Test reading back an instance definition.
-   */
-  function testReadFieldInstance() {
-    field_create_instance($this->instance_definition);
-
-    // Read the instance back.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertTrue($this->instance_definition < $instance, t('The field was properly read.'));
-  }
-
-  /**
-   * Test the update of a field instance.
-   */
-  function testUpdateFieldInstance() {
-    field_create_instance($this->instance_definition);
-    $field_type = field_info_field_types($this->field['type']);
-
-    // Check that basic changes are saved.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $instance['required'] = !$instance['required'];
-    $instance['label'] = $this->randomName();
-    $instance['description'] = $this->randomName();
-    $instance['settings']['test_instance_setting'] = $this->randomName();
-    $instance['widget']['settings']['test_widget_setting'] =$this->randomName();
-    $instance['widget']['weight']++;
-    $instance['display']['default']['settings']['test_formatter_setting'] = $this->randomName();
-    $instance['display']['default']['weight']++;
-    field_update_instance($instance);
-
-    $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertEqual($instance['required'], $instance_new['required'], t('"required" change is saved'));
-    $this->assertEqual($instance['label'], $instance_new['label'], t('"label" change is saved'));
-    $this->assertEqual($instance['description'], $instance_new['description'], t('"description" change is saved'));
-    $this->assertEqual($instance['widget']['settings']['test_widget_setting'], $instance_new['widget']['settings']['test_widget_setting'], t('Widget setting change is saved'));
-    $this->assertEqual($instance['widget']['weight'], $instance_new['widget']['weight'], t('Widget weight change is saved'));
-    $this->assertEqual($instance['display']['default']['settings']['test_formatter_setting'], $instance_new['display']['default']['settings']['test_formatter_setting'], t('Formatter setting change is saved'));
-    $this->assertEqual($instance['display']['default']['weight'], $instance_new['display']['default']['weight'], t('Widget weight change is saved'));
-
-    // Check that changing widget and formatter types updates the default settings.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $instance['widget']['type'] = 'test_field_widget_multiple';
-    $instance['display']['default']['type'] = 'field_test_multiple';
-    field_update_instance($instance);
-
-    $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertEqual($instance['widget']['type'], $instance_new['widget']['type'] , t('Widget type change is saved.'));
-    $settings = field_info_widget_settings($instance_new['widget']['type']);
-    $this->assertIdentical($settings, array_intersect_key($instance_new['widget']['settings'], $settings) , t('Widget type change updates default settings.'));
-    $this->assertEqual($instance['display']['default']['type'], $instance_new['display']['default']['type'] , t('Formatter type change is saved.'));
-    $info = field_info_formatter_types($instance_new['display']['default']['type']);
-    $settings = $info['settings'];
-    $this->assertIdentical($settings, array_intersect_key($instance_new['display']['default']['settings'], $settings) , t('Changing formatter type updates default settings.'));
-
-    // Check that adding a new view mode is saved and gets default settings.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $instance['display']['teaser'] = array();
-    field_update_instance($instance);
-
-    $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertTrue(isset($instance_new['display']['teaser']), t('Display for the new view_mode has been written.'));
-    $this->assertIdentical($instance_new['display']['teaser']['type'], $field_type['default_formatter'], t('Default formatter for the new view_mode has been written.'));
-    $info = field_info_formatter_types($instance_new['display']['teaser']['type']);
-    $settings = $info['settings'];
-    $this->assertIdentical($settings, $instance_new['display']['teaser']['settings'] , t('Default formatter settings for the new view_mode have been written.'));
-
-    // TODO: test failures.
-  }
-
-  /**
-   * Test the deletion of a field instance.
-   */
-  function testDeleteFieldInstance() {
-    // TODO: Test deletion of the data stored in the field also.
-    // Need to check that data for a 'deleted' field / instance doesn't get loaded
-    // Need to check data marked deleted is cleaned on cron (not implemented yet...)
-
-    // Create two instances for the same field so we can test that only one
-    // is deleted.
-    field_create_instance($this->instance_definition);
-    $this->another_instance_definition = $this->instance_definition;
-    $this->another_instance_definition['bundle'] .= '_another_bundle';
-    $instance = field_create_instance($this->another_instance_definition);
-
-    // Test that the first instance is not deleted, and then delete it.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
-    $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new field instance is not marked for deletion.'));
-    field_delete_instance($instance);
-
-    // Make sure the instance is marked as deleted when the instance is
-    // specifically loaded.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
-    $this->assertTrue(!empty($instance['deleted']), t('A deleted field instance is marked for deletion.'));
-
-    // Try to load the instance normally and make sure it does not show up.
-    $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
-    $this->assertTrue(empty($instance), t('A deleted field instance is not loaded by default.'));
-
-    // Make sure the other field instance is not deleted.
-    $another_instance = field_read_instance('test_entity', $this->another_instance_definition['field_name'], $this->another_instance_definition['bundle']);
-    $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('A non-deleted field instance is not marked for deletion.'));
-  }
-}
-
-/**
- * Unit test class for the multilanguage fields logic.
- *
- * The following tests will check the multilanguage logic of _field_invoke() and
- * that only the correct values are returned by field_available_languages().
- */
-class FieldTranslationsTestCase extends FieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Field translations tests',
-      'description' => 'Test multilanguage fields logic.',
-      'group' => 'Field API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('locale', 'field_test');
-
-    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
-
-    $this->entity_type = 'test_entity';
-
-    $field = array(
-      'field_name' => $this->field_name,
-      'type' => 'test_field',
-      'cardinality' => 4,
-      'translatable' => TRUE,
-    );
-    field_create_field($field);
-    $this->field = field_read_field($this->field_name);
-
-    $instance = array(
-      'field_name' => $this->field_name,
-      'entity_type' => $this->entity_type,
-      'bundle' => 'test_bundle',
-    );
-    field_create_instance($instance);
-    $this->instance = field_read_instance('test_entity', $this->field_name, 'test_bundle');
-
-    require_once DRUPAL_ROOT . '/includes/locale.inc';
-    for ($i = 0; $i < 3; ++$i) {
-      locale_add_language('l' . $i, $this->randomString(), $this->randomString());
-    }
-  }
-
-  /**
-   * Ensures that only valid values are returned by field_available_languages().
-   */
-  function testFieldAvailableLanguages() {
-    // Test 'translatable' fieldable info.
-    field_test_entity_info_translatable('test_entity', FALSE);
-    $field = $this->field;
-    $field['field_name'] .= '_untranslatable';
-
-    // Enable field translations for the entity.
-    field_test_entity_info_translatable('test_entity', TRUE);
-
-    // Test hook_field_languages() invocation on a translatable field.
-    variable_set('field_test_field_available_languages_alter', TRUE);
-    $enabled_languages = field_content_languages();
-    $available_languages = field_available_languages($this->entity_type, $this->field);
-    foreach ($available_languages as $delta => $langcode) {
-      if ($langcode != 'xx' && $langcode != 'en') {
-        $this->assertTrue(in_array($langcode, $enabled_languages), t('%language is an enabled language.', array('%language' => $langcode)));
-      }
-    }
-    $this->assertTrue(in_array('xx', $available_languages), t('%language was made available.', array('%language' => 'xx')));
-    $this->assertFalse(in_array('en', $available_languages), t('%language was made unavailable.', array('%language' => 'en')));
-
-    // Test field_available_languages() behavior for untranslatable fields.
-    $this->field['translatable'] = FALSE;
-    $this->field_name = $this->field['field_name'] = $this->instance['field_name'] = drupal_strtolower($this->randomName() . '_field_name');
-    $available_languages = field_available_languages($this->entity_type, $this->field);
-    $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === LANGUAGE_NONE, t('For untranslatable fields only LANGUAGE_NONE is available.'));
-  }
-
-  /**
-   * Test the multilanguage logic of _field_invoke().
-   */
-  function testFieldInvoke() {
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
-
-    // Populate some extra languages to check if _field_invoke() correctly uses
-    // the result of field_available_languages().
-    $values = array();
-    $extra_languages = mt_rand(1, 4);
-    $languages = $available_languages = field_available_languages($this->entity_type, $this->field);
-    for ($i = 0; $i < $extra_languages; ++$i) {
-      $languages[] = $this->randomString(2);
-    }
-
-    // For each given language provide some random values.
-    foreach ($languages as $langcode) {
-      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-        $values[$langcode][$delta]['value'] = mt_rand(1, 127);
-      }
-    }
-    $entity->{$this->field_name} = $values;
-
-    $results = _field_invoke('test_op', $entity_type, $entity);
-    foreach ($results as $langcode => $result) {
-      $hash = hash('sha256', serialize(array($entity_type, $entity, $this->field_name, $langcode, $values[$langcode])));
-      // Check whether the parameters passed to _field_invoke() were correctly
-      // forwarded to the callback function.
-      $this->assertEqual($hash, $result, t('The result for %language is correctly stored.', array('%language' => $langcode)));
-    }
-    $this->assertEqual(count($results), count($available_languages), t('No unavailable language has been processed.'));
-  }
-
-  /**
-   * Test the multilanguage logic of _field_invoke_multiple().
-   */
-  function testFieldInvokeMultiple() {
-    $values = array();
-    $entities = array();
-    $entity_type = 'test_entity';
-    $entity_count = mt_rand(1, 5);
-    $available_languages = field_available_languages($this->entity_type, $this->field);
-
-    for ($id = 1; $id <= $entity_count; ++$id) {
-      $entity = field_test_create_stub_entity($id, $id, $this->instance['bundle']);
-      $languages = $available_languages;
-
-      // Populate some extra languages to check whether _field_invoke()
-      // correctly uses the result of field_available_languages().
-      $extra_languages = mt_rand(1, 4);
-      for ($i = 0; $i < $extra_languages; ++$i) {
-        $languages[] = $this->randomString(2);
-      }
-
-      // For each given language provide some random values.
-      foreach ($languages as $langcode) {
-        for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
-          $values[$id][$langcode][$delta]['value'] = mt_rand(1, 127);
-        }
-      }
-      $entity->{$this->field_name} = $values[$id];
-      $entities[$id] = $entity;
-    }
-
-    $grouped_results = _field_invoke_multiple('test_op_multiple', $entity_type, $entities);
-    foreach ($grouped_results as $id => $results) {
-      foreach ($results as $langcode => $result) {
-        $hash = hash('sha256', serialize(array($entity_type, $entities[$id], $this->field_name, $langcode, $values[$id][$langcode])));
-        // Check whether the parameters passed to _field_invoke() were correctly
-        // forwarded to the callback function.
-        $this->assertEqual($hash, $result, t('The result for entity %id/%language is correctly stored.', array('%id' => $id, '%language' => $langcode)));
-      }
-      $this->assertEqual(count($results), count($available_languages), t('No unavailable language has been processed for entity %id.', array('%id' => $id)));
-    }
-  }
-
-  /**
-   * Test translatable fields storage/retrieval.
-   */
-  function testTranslatableFieldSaveLoad() {
-    // Enable field translations for nodes.
-    field_test_entity_info_translatable('node', TRUE);
-    $entity_info = entity_get_info('node');
-    $this->assertTrue(count($entity_info['translation']), t('Nodes are translatable.'));
-
-    // Prepare the field translations.
-    field_test_entity_info_translatable('test_entity', TRUE);
-    $eid = $evid = 1;
-    $entity_type = 'test_entity';
-    $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
-    $field_translations = array();
-    $available_languages = field_available_languages($entity_type, $this->field);
-    $this->assertTrue(count($available_languages) > 1, t('Field is translatable.'));
-    foreach ($available_languages as $langcode) {
-      $field_translations[$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
-    }
-
-    // Save and reload the field translations.
-    $entity->{$this->field_name} = $field_translations;
-    field_attach_insert($entity_type, $entity);
-    unset($entity->{$this->field_name});
-    field_attach_load($entity_type, array($eid => $entity));
-
-    // Check if the correct values were saved/loaded.
-    foreach ($field_translations as $langcode => $items) {
-      $result = TRUE;
-      foreach ($items as $delta => $item) {
-        $result = $result && $item['value'] == $entity->{$this->field_name}[$langcode][$delta]['value'];
-      }
-      $this->assertTrue($result, t('%language translation correctly handled.', array('%language' => $langcode)));
-    }
-  }
-
-  /**
-   * Tests display language logic for translatable fields.
-   */
-  function testFieldDisplayLanguage() {
-    $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $entity_type = 'test_entity';
-
-    // We need an additional field here to properly test display language
-    // suggestions.
-    $field = array(
-      'field_name' => $field_name,
-      'type' => 'test_field',
-      'cardinality' => 2,
-      'translatable' => TRUE,
-    );
-    field_create_field($field);
-
-    $instance = array(
-      'field_name' => $field['field_name'],
-      'entity_type' => $entity_type,
-      'bundle' => 'test_bundle',
-    );
-    field_create_instance($instance);
-
-    $entity = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
-    list(, , $bundle) = entity_extract_ids($entity_type, $entity);
-    $instances = field_info_instances($entity_type, $bundle);
-
-    $enabled_languages = field_content_languages();
-    $languages = array();
-
-    // Generate field translations for languages different from the first
-    // enabled.
-    foreach ($instances as $instance) {
-      $field_name = $instance['field_name'];
-      $field = field_info_field($field_name);
-      do {
-        // Index 0 is reserved for the requested language, this way we ensure
-        // that no field is actually populated with it.
-        $langcode = $enabled_languages[mt_rand(1, count($enabled_languages) - 1)];
-      }
-      while (isset($languages[$langcode]));
-      $languages[$langcode] = TRUE;
-      $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues($field['cardinality']);
-    }
-
-    // Test multiple-fields display languages for untranslatable entities.
-    field_test_entity_info_translatable($entity_type, FALSE);
-    drupal_static_reset('field_language');
-    $requested_language = $enabled_languages[0];
-    $display_language = field_language($entity_type, $entity, NULL, $requested_language);
-    foreach ($instances as $instance) {
-      $field_name = $instance['field_name'];
-      $this->assertTrue($display_language[$field_name] == LANGUAGE_NONE, t('The display language for field %field_name is %language.', array('%field_name' => $field_name, '%language' => LANGUAGE_NONE)));
-    }
-
-    // Test multiple-fields display languages for translatable entities.
-    field_test_entity_info_translatable($entity_type, TRUE);
-    drupal_static_reset('field_language');
-    $display_language = field_language($entity_type, $entity, NULL, $requested_language);
-
-    foreach ($instances as $instance) {
-      $field_name = $instance['field_name'];
-      $langcode = $display_language[$field_name];
-      // As the requested language was not assinged to any field, if the
-      // returned language is defined for the current field, core fallback rules
-      // were successfully applied.
-      $this->assertTrue(isset($entity->{$field_name}[$langcode]) && $langcode != $requested_language, t('The display language for the field %field_name is %language.', array('%field_name' => $field_name, '%language' => $langcode)));
-    }
-
-    // Test single-field display language.
-    drupal_static_reset('field_language');
-    $langcode = field_language($entity_type, $entity, $this->field_name, $requested_language);
-    $this->assertTrue(isset($entity->{$this->field_name}[$langcode]) && $langcode != $requested_language, t('The display language for the (single) field %field_name is %language.', array('%field_name' => $field_name, '%language' => $langcode)));
-  }
-
-  /**
-   * Tests field translations when creating a new revision.
-   */
-  function testFieldFormTranslationRevisions() {
-    $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
-    $this->drupalLogin($web_user);
-
-    // Prepare the field translations.
-    field_test_entity_info_translatable($this->entity_type, TRUE);
-    $eid = 1;
-    $entity = field_test_create_stub_entity($eid, $eid, $this->instance['bundle']);
-    $available_languages = array_flip(field_available_languages($this->entity_type, $this->field));
-    unset($available_languages[LANGUAGE_NONE]);
-    $field_name = $this->field['field_name'];
-
-    // Store the field translations.
-    $entity->is_new = TRUE;
-    foreach ($available_languages as $langcode => $value) {
-      $entity->{$field_name}[$langcode][0]['value'] = $value + 1;
-    }
-    field_test_entity_save($entity);
-
-    // Create a new revision.
-    $langcode = field_valid_language(NULL);
-    $edit = array("{$field_name}[$langcode][0][value]" => $entity->{$field_name}[$langcode][0]['value'], 'revision' => TRUE);
-    $this->drupalPost('test-entity/' . $eid . '/edit', $edit, t('Save'));
-
-    // Check translation revisions.
-    $this->checkTranslationRevisions($eid, $eid, $available_languages);
-    $this->checkTranslationRevisions($eid, $eid + 1, $available_languages);
-  }
-
-  /**
-   * Check if the field translation attached to the entity revision identified
-   * by the passed arguments were correctly stored.
-   */
-  private function checkTranslationRevisions($eid, $evid, $available_languages) {
-    $field_name = $this->field['field_name'];
-    $entity = field_test_entity_test_load($eid, $evid);
-    foreach ($available_languages as $langcode => $value) {
-      $passed = isset($entity->{$field_name}[$langcode]) && $entity->{$field_name}[$langcode][0]['value'] == $value + 1;
-      $this->assertTrue($passed, t('The @language translation for revision @revision was correctly stored', array('@language' => $langcode, '@revision' => $entity->ftvid)));
-    }
-  }
-}
-
-/**
- * Unit test class for field bulk delete and batch purge functionality.
- */
-class FieldBulkDeleteTestCase extends FieldTestCase {
-  protected $field;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Field bulk delete tests',
-      'description'=> 'Bulk delete fields and instances, and clean up afterwards.',
-      'group' => 'Field API',
-    );
-  }
-
-  /**
-   * Convenience function for Field API tests.
-   *
-   * Given an array of potentially fully-populated entities and an
-   * optional field name, generate an array of stub entities of the
-   * same fieldable type which contains the data for the field name
-   * (if given).
-   *
-   * @param $entity_type
-   *   The entity type of $entities.
-   * @param $entities
-   *   An array of entities of type $entity_type.
-   * @param $field_name
-   *   Optional; a field name whose data should be copied from
-   *   $entities into the returned stub entities.
-   * @return
-   *   An array of stub entities corresponding to $entities.
-   */
-  function _generateStubEntities($entity_type, $entities, $field_name = NULL) {
-    $stubs = array();
-    foreach ($entities as $entity) {
-      $stub = entity_create_stub_entity($entity_type, entity_extract_ids($entity_type, $entity));
-      if (isset($field_name)) {
-        $stub->{$field_name} = $entity->{$field_name};
-      }
-      $stubs[] = $stub;
-    }
-    return $stubs;
-  }
-
-  function setUp() {
-    parent::setUp('field_test');
-
-    // Clean up data from previous test cases.
-    $this->fields = array();
-    $this->instances = array();
-
-    // Create two bundles.
-    $this->bundles = array('bb_1' => 'bb_1', 'bb_2' => 'bb_2');
-    foreach ($this->bundles as $name => $desc) {
-      field_test_create_bundle($name, $desc);
-    }
-
-    // Create two fields.
-    $field = array('field_name' => 'bf_1', 'type' => 'test_field', 'cardinality' => 1);
-    $this->fields[] = field_create_field($field);
-    $field = array('field_name' => 'bf_2', 'type' => 'test_field', 'cardinality' => 4);
-    $this->fields[] = field_create_field($field);
-
-    // For each bundle, create an instance of each field, and 10
-    // entities with values for each field.
-    $id = 0;
-    $this->entity_type = 'test_entity';
-    foreach ($this->bundles as $bundle) {
-      foreach ($this->fields as $field) {
-        $instance = array(
-          'field_name' => $field['field_name'],
-          'entity_type' => $this->entity_type,
-          'bundle' => $bundle,
-          'widget' => array(
-            'type' => 'test_field_widget',
-          )
-        );
-        $this->instances[] = field_create_instance($instance);
-      }
-
-      for ($i = 0; $i < 10; $i++) {
-        $entity = field_test_create_stub_entity($id, $id, $bundle);
-        foreach ($this->fields as $field) {
-          $entity->{$field['field_name']}[LANGUAGE_NONE] = $this->_generateTestFieldValues($field['cardinality']);
-        }
-        $this->entities[$id] = $entity;
-        field_attach_insert($this->entity_type, $entity);
-        $id++;
-      }
-    }
-  }
-
-  /**
-   * Verify that deleting an instance leaves the field data items in
-   * the database and that the appropriate Field API functions can
-   * operate on the deleted data and instance.
-   *
-   * This tests how EntityFieldQuery interacts with
-   * field_delete_instance() and could be moved to FieldCrudTestCase,
-   * but depends on this class's setUp().
-   */
-  function testDeleteFieldInstance() {
-    $bundle = reset($this->bundles);
-    $field = reset($this->fields);
-
-    // There are 10 entities of this bundle.
-    $query = new EntityFieldQuery();
-    $found = $query
-      ->fieldCondition($field)
-      ->entityCondition('bundle', $bundle)
-      ->execute();
-    $this->assertEqual(count($found['test_entity']), 10, 'Correct number of entities found before deleting');
-
-    // Delete the instance.
-    $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle);
-    field_delete_instance($instance);
-
-    // The instance still exists, deleted.
-    $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1));
-    $this->assertEqual(count($instances), 1, 'There is one deleted instance');
-    $this->assertEqual($instances[0]['bundle'], $bundle, 'The deleted instance is for the correct bundle');
-
-    // There are 0 entities of this bundle with non-deleted data.
-    $query = new EntityFieldQuery();
-    $found = $query
-      ->fieldCondition($field)
-      ->entityCondition('bundle', $bundle)
-      ->execute();
-    $this->assertTrue(!isset($found['test_entity']), 'No entities found after deleting');
-
-    // There are 10 entities of this bundle when deleted fields are allowed, and
-    // their values are correct.
-    $query = new EntityFieldQuery();
-    $found = $query
-      ->fieldCondition($field)
-      ->entityCondition('bundle', $bundle)
-      ->deleted(TRUE)
-      ->execute();
-    field_attach_load($this->entity_type, $found[$this->entity_type], FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1));
-    $this->assertEqual(count($found['test_entity']), 10, 'Correct number of entities found after deleting');
-    foreach ($found['test_entity'] as $id => $entity) {
-      $this->assertEqual($this->entities[$id]->{$field['field_name']}, $entity->{$field['field_name']}, "Entity $id with deleted data loaded correctly");
-    }
-  }
-
-  /**
-   * Verify that field data items and instances are purged when an
-   * instance is deleted.
-   */
-  function testPurgeInstance() {
-    field_test_memorize();
-
-    $bundle = reset($this->bundles);
-    $field = reset($this->fields);
-
-    // Delete the instance.
-    $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle);
-    field_delete_instance($instance);
-
-    // No field hooks were called.
-    $mem = field_test_memorize();
-    $this->assertEqual(count($mem), 0, 'No field hooks were called');
-
-    $batch_size = 2;
-    for ($count = 8; $count >= 0; $count -= 2) {
-      // Purge two entities.
-      field_purge_batch($batch_size);
-
-      // There are $count deleted entities left.
-      $query = new EntityFieldQuery();
-      $found = $query
-        ->fieldCondition($field)
-        ->entityCondition('bundle', $bundle)
-        ->deleted(TRUE)
-        ->execute();
-      $this->assertEqual($count ? count($found['test_entity']) : count($found), $count, 'Correct number of entities found after purging 2');
-    }
-
-    // hook_field_delete() was called on a pseudo-entity for each entity. Each
-    // pseudo entity has a $field property that matches the original entity,
-    // but no others.
-    $mem = field_test_memorize();
-    $this->assertEqual(count($mem['field_test_field_delete']), 10, 'hook_field_delete was called for the right number of entities');
-    $stubs = $this->_generateStubEntities($this->entity_type, $this->entities, $field['field_name']);
-    $count = count($stubs);
-    foreach ($mem['field_test_field_delete'] as $args) {
-      $entity = $args[1];
-      $this->assertEqual($stubs[$entity->ftid], $entity, 'hook_field_delete() called with the correct stub');
-      unset($stubs[$entity->ftid]);
-    }
-    $this->assertEqual(count($stubs), $count-10, 'hook_field_delete was called with each entity once');
-
-    // The instance still exists, deleted.
-    $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1));
-    $this->assertEqual(count($instances), 1, 'There is one deleted instance');
-
-    // Purge the instance.
-    field_purge_batch($batch_size);
-
-    // The instance is gone.
-    $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1));
-    $this->assertEqual(count($instances), 0, 'The instance is gone');
-
-    // The field still exists, not deleted, because it has a second instance.
-    $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
-    $this->assertTrue(isset($fields[$field['id']]), 'The field exists and is not deleted');
-  }
-
-  /**
-   * Verify that fields are preserved and purged correctly as multiple
-   * instances are deleted and purged.
-   */
-  function testPurgeField() {
-    $field = reset($this->fields);
-
-    foreach ($this->bundles as $bundle) {
-      // Delete the instance.
-      $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle);
-      field_delete_instance($instance);
-
-      // Purge the data.
-      field_purge_batch(10);
-
-      // Purge again to purge the instance.
-      field_purge_batch(0);
-
-      // The field still exists, not deleted, because it was never deleted.
-      $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
-      $this->assertTrue(isset($fields[$field['id']]), 'The field exists and is not deleted');
-    }
-
-    // Delete the field.
-    field_delete_field($field['field_name']);
-
-    // The field still exists, deleted.
-    $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
-    $this->assertEqual($fields[$field['id']]['deleted'], 1, 'The field exists and is deleted');
-
-    // Purge the field.
-    field_purge_batch(0);
-
-    // The field is gone.
-    $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1, 'include_inactive' => 1));
-    $this->assertEqual(count($fields), 0, 'The field is purged.');
-  }
-}
diff --git a/modules/file/file.test b/modules/file/file.test
new file mode 100644
index 0000000..51e3b2d
--- /dev/null
+++ b/modules/file/file.test
@@ -0,0 +1,759 @@
+<?php
+// $Id: file.test,v 1.18 2010/07/02 12:37:57 dries Exp $
+
+/**
+ * @file
+ * Tests file uploading through the File module.
+ */
+
+/**
+ * This class provides methods specifically for testing File's field handling.
+ */
+class FileFieldTestCase extends DrupalWebTestCase {
+  protected $admin_user;
+
+  function setUp() {
+    parent::setUp('file');
+    $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer content types', 'administer nodes', 'bypass node access'));
+    $this->drupalLogin($this->admin_user);
+  }
+
+  /**
+   * Get a sample file of the specified type.
+   */
+  function getTestFile($type_name, $size = NULL) {
+    // Get a file to upload.
+    $file = current($this->drupalGetTestFiles($type_name, $size));
+
+    // Add a filesize property to files as would be read by file_load().
+    $file->filesize = filesize($file->uri);
+
+    return $file;
+  }
+
+  /**
+   * Create a new file field.
+   *
+   * @param $name
+   *   The name of the new field (all lowercase), exclude the "field_" prefix.
+   * @param $type_name
+   *   The node type that this field will be added to.
+   * @param $field_settings
+   *   A list of field settings that will be added to the defaults.
+   * @param $instance_settings
+   *   A list of instance settings that will be added to the instance defaults.
+   * @param $widget_settings
+   *   A list of widget settings that will be added to the widget defaults.
+   */
+  function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
+    $field = array(
+      'field_name' => $name,
+      'type' => 'file',
+      'settings' => array(),
+      'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
+    );
+    $field['settings'] = array_merge($field['settings'], $field_settings);
+    field_create_field($field);
+
+    $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings);
+  }
+
+  /**
+   * Attach a file field to an entity.
+   *
+   * @param $name
+   *   The name of the new field (all lowercase), exclude the "field_" prefix.
+   * @param $entity_type
+   *   The entity type this field will be added to.
+   * @param $bundle
+   *   The bundle this field will be added to.
+   * @param $field_settings
+   *   A list of field settings that will be added to the defaults.
+   * @param $instance_settings
+   *   A list of instance settings that will be added to the instance defaults.
+   * @param $widget_settings
+   *   A list of widget settings that will be added to the widget defaults.
+   */
+  function attachFileField($name, $entity_type, $bundle, $instance_settings = array(), $widget_settings = array()) {
+    $instance = array(
+      'field_name' => $name,
+      'label' => $name,
+      'entity_type' => $entity_type,
+      'bundle' => $bundle,
+      'required' => !empty($instance_settings['required']),
+      'settings' => array(),
+      'widget' => array(
+        'type' => 'file_generic',
+        'settings' => array(),
+      ),
+    );
+    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
+    field_create_instance($instance);
+  }
+
+  /**
+   * Update an existing file field with new settings.
+   */
+  function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) {
+    $field = field_info_field($name);
+    $instance = field_info_instance('node', $name, $type_name);
+    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
+
+    field_update_instance($instance);
+  }
+
+  /**
+   * Upload a file to a node.
+   */
+  function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE) {
+    $langcode = LANGUAGE_NONE;
+    $edit = array(
+      "title" => $this->randomName(),
+      'revision' => (string) (int) $new_revision,
+    );
+
+    if (is_numeric($nid_or_type)) {
+      $node = node_load($nid_or_type);
+      $delta = isset($node->$field_name) ? count($node->$field_name) : 0;
+      $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']'] = realpath($file->uri);
+      $this->drupalPost('node/' . $nid_or_type . '/edit', $edit, t('Save'));
+    }
+    else {
+      $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = realpath($file->uri);
+      $type_name = str_replace('_', '-', $nid_or_type);
+      $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+    }
+
+    $matches = array();
+    preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
+    return isset($matches[1]) ? $matches[1] : FALSE;
+  }
+
+  /**
+   * Remove a file from a node.
+   *
+   * Note that if replacing a file, it must first be removed then added again.
+   */
+  function removeNodeFile($nid, $new_revision = TRUE) {
+    $edit = array(
+      'revision' => (string) (int) $new_revision,
+    );
+
+    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
+    $this->drupalPost(NULL, $edit, t('Save'));
+  }
+
+  /**
+   * Replace a file within a node.
+   */
+  function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) {
+    $edit = array(
+      'files[' . $field_name . '_' . LANGUAGE_NONE . '_0]' => realpath($file->uri),
+      'revision' => (string) (int) $new_revision,
+    );
+
+    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
+    $this->drupalPost(NULL, $edit, t('Save'));
+  }
+
+  /**
+   * Assert that a file exists physically on disk.
+   */
+  function assertFileExists($file, $message = NULL) {
+    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
+    $this->assertTrue(is_file($file->uri), $message);
+  }
+
+  /**
+   * Assert that a file exists in the database.
+   */
+  function assertFileEntryExists($file, $message = NULL) {
+    drupal_static_reset('file_load_multiple');
+    $db_file = file_load($file->fid);
+    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
+    $this->assertEqual($db_file->uri, $file->uri, $message);
+  }
+
+  /**
+   * Assert that a file does not exist on disk.
+   */
+  function assertFileNotExists($file, $message = NULL) {
+    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
+    $this->assertFalse(is_file($file->uri), $message);
+  }
+
+  /**
+   * Assert that a file does not exist in the database.
+   */
+  function assertFileEntryNotExists($file, $message) {
+    drupal_static_reset('file_load_multiple');
+    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
+    $this->assertFalse(file_load($file->fid), $message);
+  }
+
+  /**
+   * Assert that a file's status is set to permanent in the database.
+   */
+  function assertFileIsPermanent($file, $message = NULL) {
+    $message = isset($message) ? $message : t('File %file is permanent.', array('%file' => $file->uri));
+    $this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
+  }
+}
+
+
+/**
+ * Test class to test file field upload and remove buttons, with and without AJAX.
+ */
+class FileFieldWidgetTestCase extends FileFieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field widget test',
+      'description' => 'Test upload and remove buttons, with and without AJAX.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Tests upload and remove buttons, with and without AJAX.
+   *
+   * @todo This function currently only tests the "remove" button of a single-
+   *   valued field. Tests should be added for the "upload" button and for each
+   *   button of a multi-valued field. Tests involving multiple AJAX steps on
+   *   the same page will become easier after http://drupal.org/node/789186
+   *   lands. Testing the "upload" button in AJAX context requires more
+   *   investigation into how jQuery uploads files, so that drupalPostAJAX() can
+   *   emulate that correctly.
+   */
+  function testWidget() {
+    // Use 'page' instead of 'article', so that the 'article' image field does
+    // not conflict with this test. If in the future the 'page' type gets its
+    // own default file or image field, this test can be made more robust by
+    // using a custom node type.
+    $type_name = 'page';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+
+    foreach (array('nojs', 'js') as $type) {
+      // Create a new node with the uploaded file and ensure it got uploaded
+      // successfully.
+      $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+      $node = node_load($nid, NULL, TRUE);
+      $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+      $this->assertFileExists($node_file, t('New file saved to disk on node creation.'));
+
+      // Ensure the edit page has a remove button instead of an upload button.
+      $this->drupalGet("node/$nid/edit");
+      $this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), t('Node with file does not display the "Upload" button.'));
+      $this->assertFieldByXpath('//input[@type="submit"]', t('Remove'), t('Node with file displays the "Remove" button.'));
+
+      // "Click" the remove button (emulating either a nojs or js submission).
+      switch ($type) {
+        case 'nojs':
+          $this->drupalPost(NULL, array(), t('Remove'));
+          break;
+        case 'js':
+          // @todo This can be simplified after http://drupal.org/node/789186
+          //   lands.
+          preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
+          $settings = drupal_json_decode($matches[1]);
+          $button = $this->xpath('//input[@type="submit" and @value="' . t('Remove') . '"]');
+          $button_id = (string) $button[0]['id'];
+          $this->drupalPostAJAX(NULL, array(), array((string) $button[0]['name'] => (string) $button[0]['value']), $settings['ajax'][$button_id]['url'], array(), array(), NULL, $settings['ajax'][$button_id]);
+          break;
+      }
+
+      // Ensure the page now has an upload button instead of a remove button.
+      $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After clicking the "Remove" button, it is no longer displayed.'));
+      $this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), t('After clicking the "Remove" button, the "Upload" button is displayed.'));
+
+      // Save the node and ensure it does not have the file.
+      $this->drupalPost(NULL, array(), t('Save'));
+      $node = node_load($nid, NULL, TRUE);
+      $this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), t('File was successfully removed from the node.'));
+    }
+  }
+}
+
+/**
+ * Test class to test file handling with node revisions.
+ */
+class FileFieldRevisionTestCase extends FileFieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field revision test',
+      'description' => 'Test creating and deleting revisions with files attached.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Test creating multiple revisions of a node and managing the attached files.
+   *
+   * Expected behaviors:
+   *  - Adding a new revision will make another entry in the field table, but
+   *    the original file will not be duplicated.
+   *  - Deleting a revision should not delete the original file if the file
+   *    is in use by another revision.
+   *  - When the last revision that uses a file is deleted, the original file
+   *    should be deleted also.
+   */
+  function testRevisions() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    // Attach the same fields to users.
+    $this->attachFileField($field_name, 'user', 'user');
+
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file exists on disk and in the database.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r1 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $node_vid_r1 = $node->vid;
+    $this->assertFileExists($node_file_r1, t('New file saved to disk on node creation.'));
+    $this->assertFileEntryExists($node_file_r1, t('File entry exists in database on node creation.'));
+    $this->assertFileIsPermanent($node_file_r1, t('File is permanent.'));
+
+    // Upload another file to the same node in a new revision.
+    $this->replaceNodeFile($test_file, $field_name, $nid);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r2 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $node_vid_r2 = $node->vid;
+    $this->assertFileExists($node_file_r2, t('Replacement file exists on disk after creating new revision.'));
+    $this->assertFileEntryExists($node_file_r2, t('Replacement file entry exists in database after creating new revision.'));
+    $this->assertFileIsPermanent($node_file_r2, t('Replacement file is permanent.'));
+
+    // Check that the original file is still in place on the first revision.
+    $node = node_load($nid, $node_vid_r1, TRUE);
+    $this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NONE][0], t('Original file still in place after replacing file in new revision.'));
+    $this->assertFileExists($node_file_r1, t('Original file still in place after replacing file in new revision.'));
+    $this->assertFileEntryExists($node_file_r1, t('Original file entry still in place after replacing file in new revision'));
+    $this->assertFileIsPermanent($node_file_r1, t('Original file is still permanent.'));
+
+    // Save a new version of the node without any changes.
+    // Check that the file is still the same as the previous revision.
+    $this->drupalPost('node/' . $nid . '/edit', array('revision' => '1'), t('Save'));
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r3 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $node_vid_r3 = $node->vid;
+    $this->assertEqual($node_file_r2, $node_file_r3, t('Previous revision file still in place after creating a new revision without a new file.'));
+    $this->assertFileIsPermanent($node_file_r3, t('New revision file is permanent.'));
+
+    // Revert to the first revision and check that the original file is active.
+    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r4 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $node_vid_r4 = $node->vid;
+    $this->assertEqual($node_file_r1, $node_file_r4, t('Original revision file still in place after reverting to the original revision.'));
+    $this->assertFileIsPermanent($node_file_r4, t('Original revision file still permanent after reverting to the original revision.'));
+
+    // Delete the second revision and check that the file is kept (since it is
+    // still being used by the third revision).
+    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', array(), t('Delete'));
+    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting second revision, since it is being used by the third revision.'));
+    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting second revision, since it is being used by the third revision.'));
+    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting second revision, since it is being used by the third revision.'));
+
+    // Attach the second file to a user.
+    $user = $this->drupalCreateUser();
+    $edit = array();
+    $edit[$field_name][LANGUAGE_NONE][0] = (array) $node_file_r3;
+    user_save($user, $edit);
+    $this->drupalGet('user/' . $user->uid . '/edit');
+
+    // Delete the third revision and check that the file is not deleted yet.
+    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', array(), t('Delete'));
+    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting third revision, since it is being used by the user.'));
+    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting third revision, since it is being used by the user.'));
+    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting third revision, since it is being used by the user.'));
+
+    // Delete the user and check that the file is also deleted.
+    user_delete($user->uid);
+    // TODO: This seems like a bug in File API. Clearing the stat cache should
+    // not be necessary here. The file really is deleted, but stream wrappers
+    // doesn't seem to think so unless we clear the PHP file stat() cache.
+    clearstatcache();
+    $this->assertFileNotExists($node_file_r3, t('Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
+    $this->assertFileEntryNotExists($node_file_r3, t('Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
+
+    // Delete the entire node and check that the original file is deleted.
+    $this->drupalPost('node/' . $nid . '/delete', array(), t('Delete'));
+    $this->assertFileNotExists($node_file_r1, t('Original file is deleted after deleting the entire node with two revisions remaining.'));
+    $this->assertFileEntryNotExists($node_file_r1, t('Original file entry is deleted after deleting the entire node with two revisions remaining.'));
+  }
+}
+
+/**
+ * Test class to check that formatters are working properly.
+ */
+class FileFieldDisplayTestCase extends FileFieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field display tests',
+      'description' => 'Test the display of file fields in node and views.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Test normal formatter display on node display.
+   */
+  function testNodeDisplay() {
+    $field_name = strtolower($this->randomName());
+    $type_name = 'article';
+    $field_settings = array(
+      'display_field' => '1',
+      'display_default' => '1',
+    );
+    $instance_settings = array(
+      'description_field' => '1',
+    );
+    $widget_settings = array();
+    $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $this->drupalGet('node/' . $nid . '/edit');
+
+    // Check that the default formatter is displaying with the file name.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $default_output = theme('file_link', array('file' => $node_file));
+    $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.'));
+
+    // Turn the "display" option off and check that the file is no longer displayed.
+    $edit = array($field_name . '[' . LANGUAGE_NONE . '][0][display]' => FALSE);
+    $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
+
+    $this->assertNoRaw($default_output, t('Field is hidden when "display" option is unchecked.'));
+
+  }
+}
+
+/**
+ * Test class to check for various validations.
+ */
+class FileFieldValidateTestCase extends FileFieldTestCase {
+  protected $field;
+  protected $node_type;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'File field validation tests',
+      'description' => 'Tests validation functions such as file type, max file size, max size per node, and required.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Test required property on file fields.
+   */
+  function testRequired() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+
+    // Try to post a new node without uploading a file.
+    $langcode = LANGUAGE_NONE;
+    $edit = array("title" => $this->randomName());
+    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required file field was empty.'));
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertFileExists($node_file, t('File exists after uploading to the required field.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required field.'));
+
+    // Try again with a multiple value field.
+    field_delete_field($field_name);
+    $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
+
+    // Try to post a new node without uploading a file in the multivalue field.
+    $edit = array('title' => $this->randomName());
+    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required multiple value file field was empty.'));
+
+    // Create a new node with the uploaded file into the multivalue field.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertFileExists($node_file, t('File exists after uploading to the required multiple value field.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required multipel value field.'));
+
+    // Remove our file field.
+    field_delete_field($field_name);
+  }
+
+  /**
+   * Test the max file size validator.
+   */
+  function testFileMaxSize() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $small_file = $this->getTestFile('text', 131072); // 128KB.
+    $large_file = $this->getTestFile('text', 1310720); // 1.2MB
+
+    // Test uploading both a large and small file with different increments.
+    $sizes = array(
+      '1M' => 1048576,
+      '1024K' => 1048576,
+      '1048576' => 1048576,
+    );
+
+    foreach ($sizes as $max_filesize => $file_limit) {
+      // Set the max file upload size.
+      $this->updateFileField($field_name, $type_name, array('max_filesize' => $max_filesize));
+      $instance = field_info_instance('node', $field_name, $type_name);
+
+      // Create a new node with the small file, which should pass.
+      $nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
+      $node = node_load($nid, NULL, TRUE);
+      $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+      $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
+      $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
+
+      // Check that uploading the large file fails (1M limit).
+      $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
+      $error_message = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($large_file->filesize), '%maxsize' => format_size($file_limit)));
+      $this->assertRaw($error_message, t('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
+    }
+
+    // Turn off the max filesize.
+    $this->updateFileField($field_name, $type_name, array('max_filesize' => ''));
+
+    // Upload the big file successfully.
+    $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
+
+    // Remove our file field.
+    field_delete_field($field_name);
+  }
+
+  /**
+   * Test the file extension, do additional checks if mimedetect is installed.
+   */
+  function testFileExtension() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('image');
+    list(, $test_file_extension) = explode('.', $test_file->filename);
+
+    // Disable extension checking.
+    $this->updateFileField($field_name, $type_name, array('file_extensions' => ''));
+
+    // Check that the file can be uploaded with no extension checking.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file with no extension checking.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with no extension checking.'));
+
+    // Enable extension checking for text files.
+    $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
+
+    // Check that the file with the wrong extension cannot be uploaded.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
+    $this->assertRaw($error_message, t('Node save failed when file uploaded with the wrong extension.'));
+
+    // Enable extension checking for text and image files.
+    $this->updateFileField($field_name, $type_name, array('file_extensions' => "txt $test_file_extension"));
+
+    // Check that the file can be uploaded with extension checking.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file with extension checking.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with extension checking.'));
+
+    // Remove our file field.
+    field_delete_field($field_name);
+  }
+}
+
+/**
+ * Test class to check that files are uploaded to proper locations.
+ */
+class FileFieldPathTestCase extends FileFieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field file path tests',
+      'description' => 'Test that files are uploaded to the proper location with token support.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Test normal formatter display on node display.
+   */
+  function testUploadPath() {
+    $field_name = strtolower($this->randomName());
+    $type_name = 'article';
+    $field = $this->createFileField($field_name, $type_name);
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file was uploaded to the file root.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
+
+    // Change the path to contain multiple subdirectories.
+    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => 'foo/bar/baz'));
+
+    // Upload a new file into the subdirectories.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file was uploaded into the subdirectory.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
+
+    // Check the path when used with tokens.
+    // Change the path to contain multiple token directories.
+    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => '[user:uid]/[user:name]'));
+
+    // Upload a new file into the token subdirectories.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file was uploaded into the subdirectory.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $data = array('user' => $this->admin_user);
+    $subdirectory = token_replace('[user:uid]/[user:name]', $data);
+    $this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
+  }
+
+  /**
+   * A loose assertion to check that a file is uploaded to the right location.
+   *
+   * @param $expected_path
+   *   The location where the file is expected to be uploaded. Duplicate file
+   *   names to not need to be taken into account.
+   * @param $actual_path
+   *   Where the file was actually uploaded.
+   * @param $message
+   *   The message to display with this assertion.
+   */
+  function assertPathMatch($expected_path, $actual_path, $message) {
+    // Strip off the extension of the expected path to allow for _0, _1, etc.
+    // suffixes when the file hits a duplicate name.
+    $pos = strrpos($expected_path, '.');
+    $base_path = substr($expected_path, 0, $pos);
+    $extension = substr($expected_path, $pos + 1);
+
+    $result = preg_match('/' . preg_quote($base_path, '/') . '(_[0-9]+)?\.' . preg_quote($extension, '/') . '/', $actual_path);
+    $this->assertTrue($result, $message);
+  }
+}
+
+/**
+ * Test file token replacement in strings.
+ */
+class FileTokenReplaceTestCase extends FileFieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check file token replacement.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Creates a file, then tests the tokens generated from it.
+   */
+  function testFileTokenReplacement() {
+    global $language;
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language,
+    );
+
+    // Create file field.
+    $type_name = 'article';
+    $field_name = 'field_' . strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Load the node and the file.
+    $node = node_load($nid);
+    $file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $file->description = 'File description.';
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[file:fid]'] = $file->fid;
+    $tests['[file:name]'] = check_plain($file->filename);
+    $tests['[file:description]'] = filter_xss($file->description);
+    $tests['[file:path]'] = filter_xss($file->uri);
+    $tests['[file:mime]'] = filter_xss($file->filemime);
+    $tests['[file:size]'] = format_size($file->filesize);
+    $tests['[file:url]'] = url(file_create_url($file->uri), $url_options);
+    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language->language);
+    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language->language);
+    $tests['[file:owner]'] = $this->admin_user->name;
+    $tests['[file:owner:uid]'] = $file->uid;
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('file' => $file), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized file token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[file:name]'] = $file->filename;
+    $tests['[file:description]'] = $file->description;
+    $tests['[file:path]'] = $file->uri;
+    $tests['[file:mime]'] = $file->filemime;
+    $tests['[file:size]'] = format_size($file->filesize);
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('file' => $file), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized file token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test
deleted file mode 100644
index 51e3b2d..0000000
--- a/modules/file/tests/file.test
+++ /dev/null
@@ -1,759 +0,0 @@
-<?php
-// $Id: file.test,v 1.18 2010/07/02 12:37:57 dries Exp $
-
-/**
- * @file
- * Tests file uploading through the File module.
- */
-
-/**
- * This class provides methods specifically for testing File's field handling.
- */
-class FileFieldTestCase extends DrupalWebTestCase {
-  protected $admin_user;
-
-  function setUp() {
-    parent::setUp('file');
-    $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer content types', 'administer nodes', 'bypass node access'));
-    $this->drupalLogin($this->admin_user);
-  }
-
-  /**
-   * Get a sample file of the specified type.
-   */
-  function getTestFile($type_name, $size = NULL) {
-    // Get a file to upload.
-    $file = current($this->drupalGetTestFiles($type_name, $size));
-
-    // Add a filesize property to files as would be read by file_load().
-    $file->filesize = filesize($file->uri);
-
-    return $file;
-  }
-
-  /**
-   * Create a new file field.
-   *
-   * @param $name
-   *   The name of the new field (all lowercase), exclude the "field_" prefix.
-   * @param $type_name
-   *   The node type that this field will be added to.
-   * @param $field_settings
-   *   A list of field settings that will be added to the defaults.
-   * @param $instance_settings
-   *   A list of instance settings that will be added to the instance defaults.
-   * @param $widget_settings
-   *   A list of widget settings that will be added to the widget defaults.
-   */
-  function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
-    $field = array(
-      'field_name' => $name,
-      'type' => 'file',
-      'settings' => array(),
-      'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
-    );
-    $field['settings'] = array_merge($field['settings'], $field_settings);
-    field_create_field($field);
-
-    $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings);
-  }
-
-  /**
-   * Attach a file field to an entity.
-   *
-   * @param $name
-   *   The name of the new field (all lowercase), exclude the "field_" prefix.
-   * @param $entity_type
-   *   The entity type this field will be added to.
-   * @param $bundle
-   *   The bundle this field will be added to.
-   * @param $field_settings
-   *   A list of field settings that will be added to the defaults.
-   * @param $instance_settings
-   *   A list of instance settings that will be added to the instance defaults.
-   * @param $widget_settings
-   *   A list of widget settings that will be added to the widget defaults.
-   */
-  function attachFileField($name, $entity_type, $bundle, $instance_settings = array(), $widget_settings = array()) {
-    $instance = array(
-      'field_name' => $name,
-      'label' => $name,
-      'entity_type' => $entity_type,
-      'bundle' => $bundle,
-      'required' => !empty($instance_settings['required']),
-      'settings' => array(),
-      'widget' => array(
-        'type' => 'file_generic',
-        'settings' => array(),
-      ),
-    );
-    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
-    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
-    field_create_instance($instance);
-  }
-
-  /**
-   * Update an existing file field with new settings.
-   */
-  function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) {
-    $field = field_info_field($name);
-    $instance = field_info_instance('node', $name, $type_name);
-    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
-    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
-
-    field_update_instance($instance);
-  }
-
-  /**
-   * Upload a file to a node.
-   */
-  function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE) {
-    $langcode = LANGUAGE_NONE;
-    $edit = array(
-      "title" => $this->randomName(),
-      'revision' => (string) (int) $new_revision,
-    );
-
-    if (is_numeric($nid_or_type)) {
-      $node = node_load($nid_or_type);
-      $delta = isset($node->$field_name) ? count($node->$field_name) : 0;
-      $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']'] = realpath($file->uri);
-      $this->drupalPost('node/' . $nid_or_type . '/edit', $edit, t('Save'));
-    }
-    else {
-      $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = realpath($file->uri);
-      $type_name = str_replace('_', '-', $nid_or_type);
-      $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
-    }
-
-    $matches = array();
-    preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
-    return isset($matches[1]) ? $matches[1] : FALSE;
-  }
-
-  /**
-   * Remove a file from a node.
-   *
-   * Note that if replacing a file, it must first be removed then added again.
-   */
-  function removeNodeFile($nid, $new_revision = TRUE) {
-    $edit = array(
-      'revision' => (string) (int) $new_revision,
-    );
-
-    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
-    $this->drupalPost(NULL, $edit, t('Save'));
-  }
-
-  /**
-   * Replace a file within a node.
-   */
-  function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) {
-    $edit = array(
-      'files[' . $field_name . '_' . LANGUAGE_NONE . '_0]' => realpath($file->uri),
-      'revision' => (string) (int) $new_revision,
-    );
-
-    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
-    $this->drupalPost(NULL, $edit, t('Save'));
-  }
-
-  /**
-   * Assert that a file exists physically on disk.
-   */
-  function assertFileExists($file, $message = NULL) {
-    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
-    $this->assertTrue(is_file($file->uri), $message);
-  }
-
-  /**
-   * Assert that a file exists in the database.
-   */
-  function assertFileEntryExists($file, $message = NULL) {
-    drupal_static_reset('file_load_multiple');
-    $db_file = file_load($file->fid);
-    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
-    $this->assertEqual($db_file->uri, $file->uri, $message);
-  }
-
-  /**
-   * Assert that a file does not exist on disk.
-   */
-  function assertFileNotExists($file, $message = NULL) {
-    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
-    $this->assertFalse(is_file($file->uri), $message);
-  }
-
-  /**
-   * Assert that a file does not exist in the database.
-   */
-  function assertFileEntryNotExists($file, $message) {
-    drupal_static_reset('file_load_multiple');
-    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
-    $this->assertFalse(file_load($file->fid), $message);
-  }
-
-  /**
-   * Assert that a file's status is set to permanent in the database.
-   */
-  function assertFileIsPermanent($file, $message = NULL) {
-    $message = isset($message) ? $message : t('File %file is permanent.', array('%file' => $file->uri));
-    $this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
-  }
-}
-
-
-/**
- * Test class to test file field upload and remove buttons, with and without AJAX.
- */
-class FileFieldWidgetTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field widget test',
-      'description' => 'Test upload and remove buttons, with and without AJAX.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Tests upload and remove buttons, with and without AJAX.
-   *
-   * @todo This function currently only tests the "remove" button of a single-
-   *   valued field. Tests should be added for the "upload" button and for each
-   *   button of a multi-valued field. Tests involving multiple AJAX steps on
-   *   the same page will become easier after http://drupal.org/node/789186
-   *   lands. Testing the "upload" button in AJAX context requires more
-   *   investigation into how jQuery uploads files, so that drupalPostAJAX() can
-   *   emulate that correctly.
-   */
-  function testWidget() {
-    // Use 'page' instead of 'article', so that the 'article' image field does
-    // not conflict with this test. If in the future the 'page' type gets its
-    // own default file or image field, this test can be made more robust by
-    // using a custom node type.
-    $type_name = 'page';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('text');
-
-    foreach (array('nojs', 'js') as $type) {
-      // Create a new node with the uploaded file and ensure it got uploaded
-      // successfully.
-      $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-      $node = node_load($nid, NULL, TRUE);
-      $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-      $this->assertFileExists($node_file, t('New file saved to disk on node creation.'));
-
-      // Ensure the edit page has a remove button instead of an upload button.
-      $this->drupalGet("node/$nid/edit");
-      $this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), t('Node with file does not display the "Upload" button.'));
-      $this->assertFieldByXpath('//input[@type="submit"]', t('Remove'), t('Node with file displays the "Remove" button.'));
-
-      // "Click" the remove button (emulating either a nojs or js submission).
-      switch ($type) {
-        case 'nojs':
-          $this->drupalPost(NULL, array(), t('Remove'));
-          break;
-        case 'js':
-          // @todo This can be simplified after http://drupal.org/node/789186
-          //   lands.
-          preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
-          $settings = drupal_json_decode($matches[1]);
-          $button = $this->xpath('//input[@type="submit" and @value="' . t('Remove') . '"]');
-          $button_id = (string) $button[0]['id'];
-          $this->drupalPostAJAX(NULL, array(), array((string) $button[0]['name'] => (string) $button[0]['value']), $settings['ajax'][$button_id]['url'], array(), array(), NULL, $settings['ajax'][$button_id]);
-          break;
-      }
-
-      // Ensure the page now has an upload button instead of a remove button.
-      $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After clicking the "Remove" button, it is no longer displayed.'));
-      $this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), t('After clicking the "Remove" button, the "Upload" button is displayed.'));
-
-      // Save the node and ensure it does not have the file.
-      $this->drupalPost(NULL, array(), t('Save'));
-      $node = node_load($nid, NULL, TRUE);
-      $this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), t('File was successfully removed from the node.'));
-    }
-  }
-}
-
-/**
- * Test class to test file handling with node revisions.
- */
-class FileFieldRevisionTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field revision test',
-      'description' => 'Test creating and deleting revisions with files attached.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Test creating multiple revisions of a node and managing the attached files.
-   *
-   * Expected behaviors:
-   *  - Adding a new revision will make another entry in the field table, but
-   *    the original file will not be duplicated.
-   *  - Deleting a revision should not delete the original file if the file
-   *    is in use by another revision.
-   *  - When the last revision that uses a file is deleted, the original file
-   *    should be deleted also.
-   */
-  function testRevisions() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    // Attach the same fields to users.
-    $this->attachFileField($field_name, 'user', 'user');
-
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file exists on disk and in the database.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r1 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $node_vid_r1 = $node->vid;
-    $this->assertFileExists($node_file_r1, t('New file saved to disk on node creation.'));
-    $this->assertFileEntryExists($node_file_r1, t('File entry exists in database on node creation.'));
-    $this->assertFileIsPermanent($node_file_r1, t('File is permanent.'));
-
-    // Upload another file to the same node in a new revision.
-    $this->replaceNodeFile($test_file, $field_name, $nid);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r2 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $node_vid_r2 = $node->vid;
-    $this->assertFileExists($node_file_r2, t('Replacement file exists on disk after creating new revision.'));
-    $this->assertFileEntryExists($node_file_r2, t('Replacement file entry exists in database after creating new revision.'));
-    $this->assertFileIsPermanent($node_file_r2, t('Replacement file is permanent.'));
-
-    // Check that the original file is still in place on the first revision.
-    $node = node_load($nid, $node_vid_r1, TRUE);
-    $this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NONE][0], t('Original file still in place after replacing file in new revision.'));
-    $this->assertFileExists($node_file_r1, t('Original file still in place after replacing file in new revision.'));
-    $this->assertFileEntryExists($node_file_r1, t('Original file entry still in place after replacing file in new revision'));
-    $this->assertFileIsPermanent($node_file_r1, t('Original file is still permanent.'));
-
-    // Save a new version of the node without any changes.
-    // Check that the file is still the same as the previous revision.
-    $this->drupalPost('node/' . $nid . '/edit', array('revision' => '1'), t('Save'));
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r3 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $node_vid_r3 = $node->vid;
-    $this->assertEqual($node_file_r2, $node_file_r3, t('Previous revision file still in place after creating a new revision without a new file.'));
-    $this->assertFileIsPermanent($node_file_r3, t('New revision file is permanent.'));
-
-    // Revert to the first revision and check that the original file is active.
-    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r4 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $node_vid_r4 = $node->vid;
-    $this->assertEqual($node_file_r1, $node_file_r4, t('Original revision file still in place after reverting to the original revision.'));
-    $this->assertFileIsPermanent($node_file_r4, t('Original revision file still permanent after reverting to the original revision.'));
-
-    // Delete the second revision and check that the file is kept (since it is
-    // still being used by the third revision).
-    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', array(), t('Delete'));
-    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting second revision, since it is being used by the third revision.'));
-    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting second revision, since it is being used by the third revision.'));
-    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting second revision, since it is being used by the third revision.'));
-
-    // Attach the second file to a user.
-    $user = $this->drupalCreateUser();
-    $edit = array();
-    $edit[$field_name][LANGUAGE_NONE][0] = (array) $node_file_r3;
-    user_save($user, $edit);
-    $this->drupalGet('user/' . $user->uid . '/edit');
-
-    // Delete the third revision and check that the file is not deleted yet.
-    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', array(), t('Delete'));
-    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting third revision, since it is being used by the user.'));
-    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting third revision, since it is being used by the user.'));
-    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting third revision, since it is being used by the user.'));
-
-    // Delete the user and check that the file is also deleted.
-    user_delete($user->uid);
-    // TODO: This seems like a bug in File API. Clearing the stat cache should
-    // not be necessary here. The file really is deleted, but stream wrappers
-    // doesn't seem to think so unless we clear the PHP file stat() cache.
-    clearstatcache();
-    $this->assertFileNotExists($node_file_r3, t('Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
-    $this->assertFileEntryNotExists($node_file_r3, t('Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
-
-    // Delete the entire node and check that the original file is deleted.
-    $this->drupalPost('node/' . $nid . '/delete', array(), t('Delete'));
-    $this->assertFileNotExists($node_file_r1, t('Original file is deleted after deleting the entire node with two revisions remaining.'));
-    $this->assertFileEntryNotExists($node_file_r1, t('Original file entry is deleted after deleting the entire node with two revisions remaining.'));
-  }
-}
-
-/**
- * Test class to check that formatters are working properly.
- */
-class FileFieldDisplayTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field display tests',
-      'description' => 'Test the display of file fields in node and views.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Test normal formatter display on node display.
-   */
-  function testNodeDisplay() {
-    $field_name = strtolower($this->randomName());
-    $type_name = 'article';
-    $field_settings = array(
-      'display_field' => '1',
-      'display_default' => '1',
-    );
-    $instance_settings = array(
-      'description_field' => '1',
-    );
-    $widget_settings = array();
-    $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $this->drupalGet('node/' . $nid . '/edit');
-
-    // Check that the default formatter is displaying with the file name.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $default_output = theme('file_link', array('file' => $node_file));
-    $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.'));
-
-    // Turn the "display" option off and check that the file is no longer displayed.
-    $edit = array($field_name . '[' . LANGUAGE_NONE . '][0][display]' => FALSE);
-    $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
-
-    $this->assertNoRaw($default_output, t('Field is hidden when "display" option is unchecked.'));
-
-  }
-}
-
-/**
- * Test class to check for various validations.
- */
-class FileFieldValidateTestCase extends FileFieldTestCase {
-  protected $field;
-  protected $node_type;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'File field validation tests',
-      'description' => 'Tests validation functions such as file type, max file size, max size per node, and required.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Test required property on file fields.
-   */
-  function testRequired() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('text');
-
-    // Try to post a new node without uploading a file.
-    $langcode = LANGUAGE_NONE;
-    $edit = array("title" => $this->randomName());
-    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
-    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required file field was empty.'));
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertFileExists($node_file, t('File exists after uploading to the required field.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required field.'));
-
-    // Try again with a multiple value field.
-    field_delete_field($field_name);
-    $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
-
-    // Try to post a new node without uploading a file in the multivalue field.
-    $edit = array('title' => $this->randomName());
-    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
-    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required multiple value file field was empty.'));
-
-    // Create a new node with the uploaded file into the multivalue field.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertFileExists($node_file, t('File exists after uploading to the required multiple value field.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required multipel value field.'));
-
-    // Remove our file field.
-    field_delete_field($field_name);
-  }
-
-  /**
-   * Test the max file size validator.
-   */
-  function testFileMaxSize() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $small_file = $this->getTestFile('text', 131072); // 128KB.
-    $large_file = $this->getTestFile('text', 1310720); // 1.2MB
-
-    // Test uploading both a large and small file with different increments.
-    $sizes = array(
-      '1M' => 1048576,
-      '1024K' => 1048576,
-      '1048576' => 1048576,
-    );
-
-    foreach ($sizes as $max_filesize => $file_limit) {
-      // Set the max file upload size.
-      $this->updateFileField($field_name, $type_name, array('max_filesize' => $max_filesize));
-      $instance = field_info_instance('node', $field_name, $type_name);
-
-      // Create a new node with the small file, which should pass.
-      $nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
-      $node = node_load($nid, NULL, TRUE);
-      $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-      $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
-      $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
-
-      // Check that uploading the large file fails (1M limit).
-      $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
-      $error_message = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($large_file->filesize), '%maxsize' => format_size($file_limit)));
-      $this->assertRaw($error_message, t('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
-    }
-
-    // Turn off the max filesize.
-    $this->updateFileField($field_name, $type_name, array('max_filesize' => ''));
-
-    // Upload the big file successfully.
-    $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
-
-    // Remove our file field.
-    field_delete_field($field_name);
-  }
-
-  /**
-   * Test the file extension, do additional checks if mimedetect is installed.
-   */
-  function testFileExtension() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('image');
-    list(, $test_file_extension) = explode('.', $test_file->filename);
-
-    // Disable extension checking.
-    $this->updateFileField($field_name, $type_name, array('file_extensions' => ''));
-
-    // Check that the file can be uploaded with no extension checking.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertFileExists($node_file, t('File exists after uploading a file with no extension checking.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with no extension checking.'));
-
-    // Enable extension checking for text files.
-    $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
-
-    // Check that the file with the wrong extension cannot be uploaded.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
-    $this->assertRaw($error_message, t('Node save failed when file uploaded with the wrong extension.'));
-
-    // Enable extension checking for text and image files.
-    $this->updateFileField($field_name, $type_name, array('file_extensions' => "txt $test_file_extension"));
-
-    // Check that the file can be uploaded with extension checking.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertFileExists($node_file, t('File exists after uploading a file with extension checking.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with extension checking.'));
-
-    // Remove our file field.
-    field_delete_field($field_name);
-  }
-}
-
-/**
- * Test class to check that files are uploaded to proper locations.
- */
-class FileFieldPathTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field file path tests',
-      'description' => 'Test that files are uploaded to the proper location with token support.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Test normal formatter display on node display.
-   */
-  function testUploadPath() {
-    $field_name = strtolower($this->randomName());
-    $type_name = 'article';
-    $field = $this->createFileField($field_name, $type_name);
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file was uploaded to the file root.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
-
-    // Change the path to contain multiple subdirectories.
-    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => 'foo/bar/baz'));
-
-    // Upload a new file into the subdirectories.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file was uploaded into the subdirectory.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
-
-    // Check the path when used with tokens.
-    // Change the path to contain multiple token directories.
-    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => '[user:uid]/[user:name]'));
-
-    // Upload a new file into the token subdirectories.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file was uploaded into the subdirectory.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $data = array('user' => $this->admin_user);
-    $subdirectory = token_replace('[user:uid]/[user:name]', $data);
-    $this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
-  }
-
-  /**
-   * A loose assertion to check that a file is uploaded to the right location.
-   *
-   * @param $expected_path
-   *   The location where the file is expected to be uploaded. Duplicate file
-   *   names to not need to be taken into account.
-   * @param $actual_path
-   *   Where the file was actually uploaded.
-   * @param $message
-   *   The message to display with this assertion.
-   */
-  function assertPathMatch($expected_path, $actual_path, $message) {
-    // Strip off the extension of the expected path to allow for _0, _1, etc.
-    // suffixes when the file hits a duplicate name.
-    $pos = strrpos($expected_path, '.');
-    $base_path = substr($expected_path, 0, $pos);
-    $extension = substr($expected_path, $pos + 1);
-
-    $result = preg_match('/' . preg_quote($base_path, '/') . '(_[0-9]+)?\.' . preg_quote($extension, '/') . '/', $actual_path);
-    $this->assertTrue($result, $message);
-  }
-}
-
-/**
- * Test file token replacement in strings.
- */
-class FileTokenReplaceTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File token replacement',
-      'description' => 'Generates text using placeholders for dummy content to check file token replacement.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Creates a file, then tests the tokens generated from it.
-   */
-  function testFileTokenReplacement() {
-    global $language;
-    $url_options = array(
-      'absolute' => TRUE,
-      'language' => $language,
-    );
-
-    // Create file field.
-    $type_name = 'article';
-    $field_name = 'field_' . strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Load the node and the file.
-    $node = node_load($nid);
-    $file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
-    $file->description = 'File description.';
-
-    // Generate and test sanitized tokens.
-    $tests = array();
-    $tests['[file:fid]'] = $file->fid;
-    $tests['[file:name]'] = check_plain($file->filename);
-    $tests['[file:description]'] = filter_xss($file->description);
-    $tests['[file:path]'] = filter_xss($file->uri);
-    $tests['[file:mime]'] = filter_xss($file->filemime);
-    $tests['[file:size]'] = format_size($file->filesize);
-    $tests['[file:url]'] = url(file_create_url($file->uri), $url_options);
-    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language->language);
-    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language->language);
-    $tests['[file:owner]'] = $this->admin_user->name;
-    $tests['[file:owner:uid]'] = $file->uid;
-
-    // Test to make sure that we generated something for each token.
-    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('file' => $file), array('language' => $language));
-      $this->assertFalse(strcmp($output, $expected), t('Sanitized file token %token replaced.', array('%token' => $input)));
-    }
-
-    // Generate and test unsanitized tokens.
-    $tests['[file:name]'] = $file->filename;
-    $tests['[file:description]'] = $file->description;
-    $tests['[file:path]'] = $file->uri;
-    $tests['[file:mime]'] = $file->filemime;
-    $tests['[file:size]'] = format_size($file->filesize);
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('file' => $file), array('language' => $language, 'sanitize' => FALSE));
-      $this->assertFalse(strcmp($output, $expected), t('Unsanitized file token %token replaced.', array('%token' => $input)));
-    }
-  }
-}
diff --git a/modules/simpletest/tests/actions.test b/modules/simpletest/tests/actions.test
deleted file mode 100644
index 8e8f176..0000000
--- a/modules/simpletest/tests/actions.test
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-// $Id: actions.test,v 1.16 2010/05/26 11:26:49 dries Exp $
-
-class ActionsConfigurationTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Actions configuration',
-      'description' => 'Tests complex actions configuration by adding, editing, and deleting a complex action.',
-      'group' => 'Actions',
-    );
-  }
-
-  /**
-   * Test the configuration of advanced actions through the administration
-   * interface.
-   */
-  function testActionConfiguration() {
-    // Create a user with permission to view the actions administration pages.
-    $user = $this->drupalCreateUser(array('administer actions'));
-    $this->drupalLogin($user);
-
-    // Make a POST request to admin/config/system/actions/manage.
-    $edit = array();
-    $edit['action'] = drupal_hash_base64('system_goto_action');
-    $this->drupalPost('admin/config/system/actions/manage', $edit, t('Create'));
-
-    // Make a POST request to the individual action configuration page.
-    $edit = array();
-    $action_label = $this->randomName();
-    $edit['actions_label'] = $action_label;
-    $edit['url'] = 'admin';
-    $this->drupalPost('admin/config/system/actions/configure/' . drupal_hash_base64('system_goto_action'), $edit, t('Save'));
-
-    // Make sure that the new complex action was saved properly.
-    $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action."));
-    $this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action."));
-
-    // Make another POST request to the action edit page.
-    $this->clickLink(t('configure'));
-    preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches);
-    $aid = $matches[1];
-    $edit = array();
-    $new_action_label = $this->randomName();
-    $edit['actions_label'] = $new_action_label;
-    $edit['url'] = 'admin';
-    $this->drupalPost(NULL, $edit, t('Save'));
-
-    // Make sure that the action updated properly.
-    $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action."));
-    $this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."));
-    $this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action."));
-
-    // Make sure that deletions work properly.
-    $this->clickLink(t('delete'));
-    $edit = array();
-    $this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete'));
-
-    // Make sure that the action was actually deleted.
-    $this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.'));
-    $this->drupalGet('admin/config/system/actions/manage');
-    $this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action."));
-    $exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
-    $this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.'));
-  }
-}
-
-/**
- * Test actions executing in a potential loop, and make sure they abort properly.
- */
-class ActionLoopTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Actions executing in a potentially infinite loop',
-      'description' => 'Tests actions executing in a loop, and makes sure they abort properly.',
-      'group' => 'Actions',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('dblog', 'trigger', 'actions_loop_test');
-  }
-
-  /**
-   * Set up a loop with 3 - 12 recursions, and see if it aborts properly.
-   */
-  function testActionLoop() {
-    $user = $this->drupalCreateUser(array('administer actions'));
-    $this->drupalLogin($user);
-
-    $hash = drupal_hash_base64('actions_loop_test_log');
-    $edit = array('aid' => $hash);
-    $this->drupalPost('admin/structure/trigger/actions_loop_test', $edit, t('Assign'));
-
-    // Delete any existing watchdog messages to clear the plethora of
-    // "Action added" messages from when Drupal was installed.
-    db_delete('watchdog')->execute();
-    // To prevent this test from failing when xdebug is enabled, the maximum
-    // recursion level should be kept low enough to prevent the xdebug
-    // infinite recursion protection mechanism from aborting the request.
-    // See http://drupal.org/node/587634.
-    variable_set('actions_max_stack', mt_rand(3, 12));
-    $this->triggerActions();
-  }
-
-  /**
-   * Create an infinite loop by causing a watchdog message to be set,
-   * which causes the actions to be triggered again, up to actions_max_stack
-   * times.
-   */
-  protected function triggerActions() {
-    $this->drupalGet('<front>', array('query' => array('trigger_actions_on_watchdog' => TRUE)));
-    $expected = array();
-    $expected[] = 'Triggering action loop';
-    for ($i = 1; $i <= variable_get('actions_max_stack', 35); $i++) {
-      $expected[] = "Test log #$i";
-    }
-    $expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.';
-
-    $result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid");
-    $loop_started = FALSE;
-    foreach ($result as $row) {
-      $expected_message = array_shift($expected);
-      $this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message)));
-    }
-    $this->assertTrue(empty($expected), t('All expected messages found.'));
-  }
-}
diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test
deleted file mode 100644
index 2e10b80..0000000
--- a/modules/simpletest/tests/ajax.test
+++ /dev/null
@@ -1,329 +0,0 @@
-<?php
-// $Id: ajax.test,v 1.11 2010/04/30 08:07:55 dries Exp $
-
-class AJAXTestCase extends DrupalWebTestCase {
-  function setUp() {
-    $modules = func_get_args();
-    if (isset($modules[0]) && is_array($modules[0])) {
-      $modules = $modules[0];
-    }
-    parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
-  }
-
-  /**
-   * Returns the passed-in commands array without the initial settings command.
-   *
-   * Depending on factors that may be irrelevant to a particular test,
-   * ajax_render() may prepend a settings command. This function allows the test
-   * to only have to concern itself with the commands that were passed to
-   * ajax_render().
-   */
-  protected function discardSettings($commands) {
-    if ($commands[0]['command'] == 'settings') {
-      array_shift($commands);
-    }
-    return $commands;
-  }
-}
-
-/**
- * Tests primary AJAX framework functions.
- */
-class AJAXFrameworkTestCase extends AJAXTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'AJAX framework',
-      'description' => 'Performs tests on AJAX framework functions.',
-      'group' => 'AJAX',
-    );
-  }
-
-  /**
-   * Test proper passing of JavaScript settings via ajax_render().
-   */
-  function testAJAXRender() {
-    $result = $this->drupalGetAJAX('ajax-test/render');
-    // Verify that JavaScript settings are contained (always first).
-    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
-    // Verify that basePath is contained in JavaScript settings.
-    $this->assertEqual($result[0]['settings']['basePath'], base_path(), t('Base path is contained in JavaScript settings.'));
-  }
-
-  /**
-   * Test behavior of ajax_render_error().
-   */
-  function testAJAXRenderError() {
-    $result = $this->drupalGetAJAX('ajax-test/render-error');
-    // Verify default error message.
-    $this->assertEqual($result[0]['command'], 'alert', t('ajax_render_error() invokes alert command.'));
-    $this->assertEqual($result[0]['text'], t('An error occurred while handling the request: The server received invalid input.'), t('Default error message is output.'));
-    // Verify custom error message.
-    $edit = array(
-      'message' => 'Custom error message.',
-    );
-    $result = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
-    $this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.'));
-  }
-}
-
-/**
- * Tests AJAX framework commands.
- */
-class AJAXCommandsTestCase extends AJAXTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'AJAX commands',
-      'description' => 'Performs tests on AJAX framework commands.',
-      'group' => 'AJAX',
-    );
-  }
-
-  /**
-   * Test ajax_command_settings().
-   */
-  function testAJAXRender() {
-    $commands = array();
-    $commands[] = ajax_command_settings(array('foo' => 42));
-    $result = $this->drupalGetAJAX('ajax-test/render', array('query' => array('commands' => $commands)));
-    // Verify that JavaScript settings are contained (always first).
-    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
-    // Verify that the custom setting is contained.
-    $this->assertEqual($result[1]['settings']['foo'], 42, t('Custom setting is output.'));
-  }
-
-  /**
-   * Test the various AJAX Commands.
-   */
-  function testAJAXCommands() {
-    $form_path = 'ajax_forms_test_ajax_commands_form';
-    $web_user = $this->drupalCreateUser(array('access content'));
-    $this->drupalLogin($web_user);
-
-    $edit = array();
-
-    // Tests the 'after' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'after' && $command['data'] == 'This will be placed after', "'after' AJAX command issued with correct data");
-
-    // Tests the 'alert' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'alert' && $command['text'] == 'Alert', "'alert' AJAX Command issued with correct text");
-
-    // Tests the 'append' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'append' && $command['data'] == 'Appended text', "'append' AJAX command issued with correct data");
-
-    // Tests the 'before' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'before' && $command['data'] == 'Before text', "'before' AJAX command issued with correct data");
-
-    // Tests the 'changed' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div', "'changed' AJAX command issued with correct selector");
-
-    // Tests the 'changed' command using the second argument.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div' && $command['asterisk'] == '#changed_div_mark_this', "'changed' AJAX command (with asterisk) issued with correct selector");
-
-    // Tests the 'css' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'css' && $command['selector'] == '#css_div' && $command['argument']['background-color'] == 'blue', "'css' AJAX command issued with correct selector");
-
-    // Tests the 'data' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'data' && $command['name'] == 'testkey' && $command['value'] == 'testvalue', "'data' AJAX command issued with correct key and value");
-
-    // Tests the 'html' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'html' && $command['data'] == 'replacement text', "'html' AJAX command issued with correct data");
-
-    // Tests the 'insert' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method']."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == NULL && $command['data'] == 'insert replacement text', "'insert' AJAX command issued with correct data");
-
-    // Tests the 'prepend' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'prepend' && $command['data'] == 'prepended text', "'prepend' AJAX command issued with correct data");
-
-    // Tests the 'remove' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'remove' && $command['selector'] == '#remove_text', "'remove' AJAX command issued with correct command and selector");
-
-    // Tests the 'restripe' command.
-    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command"))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector");
-  }
-}
-
-/**
- * Test that $form_state['values'] is properly delivered to $ajax['callback'].
- */
-class AJAXFormValuesTestCase extends AJAXTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'AJAX command form values',
-      'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
-      'group' => 'AJAX',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-
-    $this->web_user = $this->drupalCreateUser(array('access content'));
-    $this->drupalLogin($this->web_user);
-  }
-
-  /**
-   * Create a simple form, then POST to system/ajax to change to it.
-   */
-  function testSimpleAJAXFormValue() {
-    // Verify form values of a select element.
-    foreach(array('red', 'green', 'blue') as $item) {
-      $edit = array(
-        'select' => $item,
-      );
-      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'));
-      $data_command = $commands[1];
-      $this->assertEqual($data_command['value'], $item);
-    }
-
-    // Verify form values of a checkbox element.
-    foreach(array(FALSE, TRUE) as $item) {
-      $edit = array(
-        'checkbox' => $item,
-      );
-      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox'));
-      $data_command = $commands[1];
-      $this->assertEqual((int) $data_command['value'], (int) $item);
-    }
-  }
-}
-
-/**
- * Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.
- */
-class AJAXMultiFormTestCase extends AJAXTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'AJAX multi form',
-      'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.',
-      'group' => 'AJAX',
-    );
-  }
-
-  function setUp() {
-    parent::setUp(array('form_test'));
-
-    // Create a multi-valued field for 'page' nodes to use for AJAX testing.
-    $field_name = 'field_ajax_test';
-    $field = array(
-      'field_name' => $field_name,
-      'type' => 'text',
-      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
-    );
-    field_create_field($field);
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => 'node',
-      'bundle' => 'page',
-    );
-    field_create_instance($instance);
-
-    // Login a user who can create 'page' nodes.
-    $this->web_user = $this->drupalCreateUser(array('create page content'));
-    $this->drupalLogin($this->web_user);
-  }
-
-  /**
-   * Test that a page with the 'page_node_form' included twice works correctly.
-   */
-  function testMultiForm() {
-    // HTML IDs for elements within the field are potentially modified with
-    // each AJAX submission, but these variables are stable and help target the
-    // desired elements.
-    $field_name = 'field_ajax_test';
-    $field_xpaths = array(
-      'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]',
-      'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]',
-    );
-    $button_name = $field_name . '_add_more';
-    $button_value = t('Add another item');
-    $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
-    $field_items_xpath_suffix = '//input[@type="text"]';
-
-    // Ensure the initial page contains both node forms and the correct number
-    // of field items and "add more" button for the multi-valued field within
-    // each form.
-    $this->drupalGet('form-test/two-instances-of-same-form');
-    foreach ($field_xpaths as $form_id => $field_xpath) {
-      $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
-      $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
-    }
-    $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
-
-    // Submit the "add more" button of each form twice. After each corresponding
-    // page update, ensure the same as above. To successfully implement
-    // consecutive AJAX submissions, we need to manage $settings as ajax.js
-    // does for Drupal.settings.
-    preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
-    $settings = drupal_json_decode($matches[1]);
-    foreach ($field_xpaths as $form_id => $field_xpath) {
-      for ($i=0; $i<2; $i++) {
-        $button = $this->xpath($field_xpath . $button_xpath_suffix);
-        $button_id = (string) $button[0]['id'];
-        $commands = $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_id, $settings['ajax'][$button_id]);
-        $settings = array_merge_recursive($settings, $commands[0]['settings']);
-        $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
-        $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
-        $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
-      }
-    }
-  }
-}
-
-/**
- * Miscellaneous AJAX tests using ajax_test module.
- */
-class AJAXElementValidation extends AJAXTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Miscellaneous AJAX tests',
-      'description' => 'Various tests of AJAX behavior',
-      'group' => 'AJAX',
-    );
-  }
-
-  /**
-   * Try to post an AJAX change to a form that has a validated element.
-   *
-   * The drivertext field is AJAX-enabled. An additional field is not, but
-   * is set to be a required field. In this test the required field is not
-   * filled in, and we want to see if the activation of the "drivertext"
-   * AJAX-enabled field fails due to the required field being empty.
-   */
-  function testAJAXElementValidation() {
-    $web_user = $this->drupalCreateUser();
-    $edit = array('drivertext' => t('some dumb text'));
-
-    // Post with 'drivertext' as the triggering element.
-    $post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext');
-    // Look for a validation failure in the resultant JSON.
-    $this->assertNoText(t('Error message'), t("No error message in resultant JSON"));
-    $this->assertText('ajax_forms_test_validation_form_callback invoked', t('The correct callback was invoked'));
-  }
-}
-
diff --git a/modules/simpletest/tests/batch.test b/modules/simpletest/tests/batch.test
deleted file mode 100644
index 5c11f72..0000000
--- a/modules/simpletest/tests/batch.test
+++ /dev/null
@@ -1,367 +0,0 @@
-<?php
-// $Id: batch.test,v 1.9 2010/05/06 05:59:31 webchick Exp $
-
-/**
- * @file
- * Tests for the Batch API.
- */
-
-/**
- * Tests for the Batch API.
- */
-class BatchProcessingTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Batch processing',
-      'description' => 'Test batch processing in form and non-form workflow.',
-      'group' => 'Batch API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('batch_test');
-  }
-
-  /**
-   * Test batches triggered outside of form submission.
-   */
-  function testBatchNoForm() {
-    // Displaying the page triggers batch 1.
-    $this->drupalGet('batch_test/no_form');
-    $this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-  }
-
-  /**
-   * Test batches defined in a form submit handler.
-   */
-  function testBatchForm() {
-    // Batch 0: no operation.
-    $edit = array('batch' => 'batch_0');
-    $this->drupalPost('batch_test/simple', $edit, 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-
-    // Batch 1: several simple operations.
-    $edit = array('batch' => 'batch_1');
-    $this->drupalPost('batch_test/simple', $edit, 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-
-    // Batch 2: one multistep operation.
-    $edit = array('batch' => 'batch_2');
-    $this->drupalPost('batch_test/simple', $edit, 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-
-    // Batch 3: simple + multistep combined.
-    $edit = array('batch' => 'batch_3');
-    $this->drupalPost('batch_test/simple', $edit, 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-
-    // Batch 4: nested batch.
-    $edit = array('batch' => 'batch_4');
-    $this->drupalPost('batch_test/simple', $edit, 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-  }
-
-  /**
-   * Test batches defined in a multistep form.
-   */
-  function testBatchFormMultistep() {
-    $this->drupalGet('batch_test/multistep');
-    $this->assertText('step 1', t('Form is displayed in step 1.'));
-
-    // First step triggers batch 1.
-    $this->drupalPost(NULL, array(), 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
-    $this->assertText('step 2', t('Form is displayed in step 2.'));
-
-    // Second step triggers batch 2.
-    $this->drupalPost(NULL, array(), 'Submit');
-    $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.'));
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-  }
-
-  /**
-   * Test batches defined in different submit handlers on the same form.
-   */
-  function testBatchFormMultipleBatches() {
-    // Batches 1, 2 and 3 are triggered in sequence by different submit
-    // handlers. Each submit handler modify the submitted 'value'.
-    $value = rand(0, 255);
-    $edit = array('value' => $value);
-    $this->drupalPost('batch_test/chained', $edit, 'Submit');
-    // Check that result messages are present and in the correct order.
-    $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
-    // The stack contains execution order of batch callbacks and submit
-    // hanlders and logging of corresponding $form_state[{values'].
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
-    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
-  }
-
-  /**
-   * Test batches defined in a programmatically submitted form.
-   *
-   * Same as above, but the form is submitted through drupal_form_execute().
-   */
-  function testBatchFormProgrammatic() {
-    // Batches 1, 2 and 3 are triggered in sequence by different submit
-    // handlers. Each submit handler modify the submitted 'value'.
-    $value = rand(0, 255);
-    $this->drupalGet('batch_test/programmatic/' . $value);
-    // Check that result messages are present and in the correct order.
-    $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
-    // The stack contains execution order of batch callbacks and submit
-    // hanlders and logging of corresponding $form_state[{values'].
-    $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
-    $this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.'));
-  }
-
-  /**
-   * Test that drupal_form_submit() can run within a batch operation.
-   */
-  function testDrupalFormSubmitInBatch() {
-    // Displaying the page triggers a batch that programmatically submits a
-    // form.
-    $value = rand(0, 255);
-    $this->drupalGet('batch_test/nested_programmatic/' . $value);
-    $this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.'));
-  }
-
-  /**
-   * Will trigger a pass if the texts were found in order in the raw content.
-   *
-   * @param $texts
-   *   Array of raw strings to look for .
-   * @param $message
-   *   Message to display.
-   * @return
-   *   TRUE on pass, FALSE on fail.
-   */
-  function assertBatchMessages($texts, $message) {
-    $pattern = '|' . implode('.*', $texts) .'|s';
-    return $this->assertPattern($pattern, $message);
-  }
-
-  /**
-   * Helper function: return expected execution stacks for the test batches.
-   */
-  function _resultStack($id, $value = 0) {
-    $stack = array();
-    switch ($id) {
-      case 'batch_1':
-        for ($i = 1; $i <= 10; $i++) {
-          $stack[] = "op 1 id $i";
-        }
-        break;
-
-      case 'batch_2':
-        for ($i = 1; $i <= 10; $i++) {
-          $stack[] = "op 2 id $i";
-        }
-        break;
-
-      case 'batch_3':
-        for ($i = 1; $i <= 5; $i++) {
-          $stack[] = "op 1 id $i";
-        }
-        for ($i = 1; $i <= 5; $i++) {
-          $stack[] = "op 2 id $i";
-        }
-        for ($i = 6; $i <= 10; $i++) {
-          $stack[] = "op 1 id $i";
-        }
-        for ($i = 6; $i <= 10; $i++) {
-          $stack[] = "op 2 id $i";
-        }
-        break;
-
-      case 'batch_4':
-        for ($i = 1; $i <= 5; $i++) {
-          $stack[] = "op 1 id $i";
-        }
-        $stack[] = 'setting up batch 2';
-        for ($i = 6; $i <= 10; $i++) {
-          $stack[] = "op 1 id $i";
-        }
-        $stack = array_merge($stack, $this->_resultStack('batch_2'));
-        break;
-
-      case 'chained':
-        $stack[] = 'submit handler 1';
-        $stack[] = 'value = ' . $value;
-        $stack = array_merge($stack, $this->_resultStack('batch_1'));
-        $stack[] = 'submit handler 2';
-        $stack[] = 'value = ' . ($value + 1);
-        $stack = array_merge($stack, $this->_resultStack('batch_2'));
-        $stack[] = 'submit handler 3';
-        $stack[] = 'value = ' . ($value + 2);
-        $stack[] = 'submit handler 4';
-        $stack[] = 'value = ' . ($value + 3);
-        $stack = array_merge($stack, $this->_resultStack('batch_3'));
-        break;
-    }
-    return $stack;
-  }
-
-  /**
-   * Helper function: return expected result messages for the test batches.
-   */
-  function _resultMessages($id) {
-    $messages = array();
-
-    switch ($id) {
-      case 'batch_0':
-        $messages[] = 'results for batch 0<br />none';
-        break;
-
-      case 'batch_1':
-        $messages[] = 'results for batch 1<br />op 1: processed 10 elements';
-        break;
-
-      case 'batch_2':
-        $messages[] = 'results for batch 2<br />op 2: processed 10 elements';
-        break;
-
-      case 'batch_3':
-        $messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements';
-        break;
-
-      case 'batch_4':
-        $messages[] = 'results for batch 4<br />op 1: processed 10 elements';
-        $messages = array_merge($messages, $this->_resultMessages('batch_2'));
-        break;
-
-      case 'chained':
-        $messages = array_merge($messages, $this->_resultMessages('batch_1'));
-        $messages = array_merge($messages, $this->_resultMessages('batch_2'));
-        $messages = array_merge($messages, $this->_resultMessages('batch_3'));
-        break;
-    }
-    return $messages;
-  }
-}
-
-/**
- * Tests for the Batch API Progress page.
- */
-class BatchPageTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Batch progress page',
-      'description' => 'Test the content of the progress page.',
-      'group' => 'Batch API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('batch_test');
-  }
-
-  /**
-   * Tests that the batch API progress page uses the correct theme.
-   */
-  function testBatchProgressPageTheme() {
-    // Make sure that the page which starts the batch (an administrative page)
-    // is using a different theme than would normally be used by the batch API.
-    variable_set('theme_default', 'garland');
-    variable_set('admin_theme', 'seven');
-    // Visit an administrative page that runs a test batch, and check that the
-    // theme that was used during batch execution (which the batch callback
-    // function saved as a variable) matches the theme used on the
-    // administrative page.
-    $this->drupalGet('admin/batch_test/test_theme');
-    // The stack should contain the name of the the used on the progress page.
-    $this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.'));
-  }
-}
-
-/**
- * Tests the function _batch_api_percentage() to make sure that the rounding
- * works properly in all cases.
- */
-class BatchPercentagesUnitTestCase extends DrupalUnitTestCase {
-  protected $testCases = array();
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Batch percentages',
-      'description' => 'Unit tests of progress percentage rounding.',
-      'group' => 'Batch API',
-    );
-  }
-
-  function setUp() {
-    // Set up an array of test cases, where the expected values are the keys,
-    // and the values are arrays with the keys 'total' and 'current',
-    // corresponding with the function parameters of _batch_api_percentage().
-    $this->testCases = array(
-      // 1/2 is 50%.
-      '50' => array('total' => 2, 'current' => 1),
-      // Though we should never encounter a case where the current set is set
-      // 0, if we did, we should get 0%.
-      '0' => array('total' => 3, 'current' => 0),
-      // 1/3 is closer to 33% than to 34%.
-      '33' => array('total' => 3, 'current' => 1),
-      // 2/3 is closer to 67% than to 66%.
-      '67' => array('total' => 3, 'current' => 2),
-      // A full 3/3 should equal 100%.
-      '100' => array('total' => 3, 'current' => 3),
-      // 1/199 should round up to 1%.
-      '1' => array('total' => 199, 'current' => 1),
-      // 198/199 should round down to 99%.
-      '99' => array('total' => 199, 'current' => 198),
-      // 199/200 would have rounded up to 100%, which would give the false
-      // impression of being finished, so we add another digit and should get
-      // 99.5%.
-      '99.5' => array('total' => 200, 'current' => 199),
-      // The same logic holds for 1/200: we should get 0.5%.
-      '0.5' => array('total' => 200, 'current' => 1),
-      // Numbers that come out evenly, such as 50/200, should be forced to have
-      // extra digits for consistancy.
-      '25.0' => array('total' => 200, 'current' => 50),
-      // Regardless of number of digits we're using, 100% should always just be
-      // 100%.
-      '100' => array('total' => 200, 'current' => 200),
-      // 1998/1999 should similarly round down to 99.9%.
-      '99.9' => array('total' => 1999, 'current' => 1998),
-      // 1999/2000 should add another digit and go to 99.95%.
-      '99.95' => array('total' => 2000, 'current' => 1999),
-      // 19999/20000 should add yet another digit and go to 99.995%.
-      '99.995' => array('total' => 20000, 'current' => 19999),
-    );
-
-    parent::setUp();
-  }
-
-  /**
-   * Test the _batch_api_percentage() function.
-   */
-  function testBatchPercentages() {
-    require_once DRUPAL_ROOT . '/includes/batch.inc';
-    foreach ($this->testCases as $expected_result => $arguments) {
-      // PHP sometimes casts numeric strings that are array keys to integers,
-      // cast them back here.
-      $expected_result = (string) $expected_result;
-      $total = $arguments['total'];
-      $current = $arguments['current'];
-      $actual_result = _batch_api_percentage($total, $current);
-      if ($actual_result === $expected_result) {
-        $this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
-      }
-      else {
-        $this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
-      }
-    }
-  }
-}
diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test
deleted file mode 100644
index 95d7244..0000000
--- a/modules/simpletest/tests/bootstrap.test
+++ /dev/null
@@ -1,428 +0,0 @@
-<?php
-// $Id: bootstrap.test,v 1.31 2010/06/14 13:24:32 dries Exp $
-
-class BootstrapIPAddressTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'IP address and HTTP_HOST test',
-      'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.',
-      'group' => 'Bootstrap'
-    );
-  }
-
-  function setUp() {
-    $this->oldserver = $_SERVER;
-
-    $this->remote_ip = '127.0.0.1';
-    $this->proxy_ip = '127.0.0.2';
-    $this->proxy2_ip = '127.0.0.3';
-    $this->forwarded_ip = '127.0.0.4';
-    $this->cluster_ip = '127.0.0.5';
-    $this->untrusted_ip = '0.0.0.0';
-
-    drupal_static_reset('ip_address');
-
-    $_SERVER['REMOTE_ADDR'] = $this->remote_ip;
-    unset($_SERVER['HTTP_X_FORWARDED_FOR']);
-    unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']);
-
-    parent::setUp();
-  }
-
-  function tearDown() {
-    $_SERVER = $this->oldserver;
-    drupal_static_reset('ip_address');
-    parent::tearDown();
-  }
-
-  /**
-   * test IP Address and hostname
-   */
-  function testIPAddressHost() {
-    // Test the normal IP address.
-    $this->assertTrue(
-      ip_address() == $this->remote_ip,
-      t('Got remote IP address.')
-    );
-
-    // Proxy forwarding on but no proxy addresses defined.
-    variable_set('reverse_proxy', 1);
-    $this->assertTrue(
-      ip_address() == $this->remote_ip,
-      t('Proxy forwarding without trusted proxies got remote IP address.')
-    );
-
-    // Proxy forwarding on and proxy address not trusted.
-    variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip));
-    drupal_static_reset('ip_address');
-    $_SERVER['REMOTE_ADDR'] = $this->untrusted_ip;
-    $this->assertTrue(
-      ip_address() == $this->untrusted_ip,
-      t('Proxy forwarding with untrusted proxy got remote IP address.')
-    );
-
-    // Proxy forwarding on and proxy address trusted.
-    $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
-    $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip;
-    drupal_static_reset('ip_address');
-    $this->assertTrue(
-      ip_address() == $this->forwarded_ip,
-      t('Proxy forwarding with trusted proxy got forwarded IP address.')
-    );
-
-    // Multi-tier architecture with comma separated values in header.
-    $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
-    $_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
-    drupal_static_reset('ip_address');
-    $this->assertTrue(
-      ip_address() == $this->forwarded_ip,
-      t('Proxy forwarding with trusted 2-tier proxy got forwarded IP address.')
-    );
-
-    // Custom client-IP header.
-    variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP');
-    $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip;
-    drupal_static_reset('ip_address');
-    $this->assertTrue(
-      ip_address() == $this->cluster_ip,
-      t('Cluster environment got cluster client IP.')
-    );
-
-    // Verifies that drupal_valid_http_host() prevents invalid characters.
-    $this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), t('HTTP_HOST with / is invalid'));
-    $this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), t('HTTP_HOST with \\ is invalid'));
-    $this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), t('HTTP_HOST with &lt; is invalid'));
-    $this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), t('HTTP_HOST with .. is invalid'));
-    // IPv6 loopback address
-    $this->assertTrue(drupal_valid_http_host('[::1]:80'), t('HTTP_HOST containing IPv6 loopback is valid'));
-  }
-}
-
-class BootstrapPageCacheTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Page cache test',
-      'description' => 'Enable the page cache and test it with various HTTP requests.',
-      'group' => 'Bootstrap'
-    );
-  }
-
-  function setUp() {
-    parent::setUp('system_test');
-  }
-
-  /**
-   * Test support for requests containing If-Modified-Since and If-None-Match headers.
-   */
-  function testConditionalRequests() {
-    variable_set('cache', 1);
-
-    // Fill the cache.
-    $this->drupalGet('');
-
-    $this->drupalHead('');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-    $etag = $this->drupalGetHeader('ETag');
-    $last_modified = $this->drupalGetHeader('Last-Modified');
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
-    $this->assertResponse(304, t('Conditional request returned 304 Not Modified.'));
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
-    $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.'));
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
-    $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.'));
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
-    $this->assertResponse(200, t('Conditional request without If-None-Match returned 200 OK.'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC1123, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
-    $this->assertResponse(200, t('Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-
-    $user = $this->drupalCreateUser();
-    $this->drupalLogin($user);
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
-    $this->assertResponse(200, t('Conditional request returned 200 OK for authenticated user.'));
-    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Absense of Page was not cached.'));
-  }
-
-  /**
-   * Test cache headers.
-   */
-  function testPageCache() {
-    variable_set('cache', 1);
-
-    // Fill the cache.
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
-    $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));
-
-    // Check cache.
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-    $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary: Cookie header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));
-
-    // Check replacing default headers.
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', t('Default header was replaced.'));
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
-    $this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', t('Default header was replaced.'));
-
-    // Check that authenticated users bypass the cache.
-    $user = $this->drupalCreateUser();
-    $this->drupalLogin($user);
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
-    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
-    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, t('Vary: Cookie header was not sent.'));
-    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
-    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));
-
-  }
-
-  /**
-   * Test page compression.
-   *
-   * The test should pass even if zlib.output_compression is enabled in php.ini,
-   * .htaccess or similar, or if compression is done outside PHP, e.g. by the
-   * mod_deflate Apache module.
-   */
-  function testPageCompression() {
-    variable_set('cache', 1);
-
-    // Fill the cache and verify that output is compressed.
-    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
-    $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
-    $this->assertRaw('</html>', t('Page was gzip compressed.'));
-
-    // Verify that cached output is compressed.
-    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-    $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', t('A Content-Encoding header was sent.'));
-    $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
-    $this->assertRaw('</html>', t('Page was gzip compressed.'));
-
-    // Verify that a client without compression support gets an uncompressed page.
-    $this->drupalGet('');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-    $this->assertFalse($this->drupalGetHeader('Content-Encoding'), t('A Content-Encoding header was not sent.'));
-    $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
-    $this->assertRaw('</html>', t('Page was not compressed.'));
-  }
-}
-
-class BootstrapVariableTestCase extends DrupalWebTestCase {
-
-  function setUp() {
-    parent::setUp('system_test');
-  }
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Variable test',
-      'description' => 'Make sure the variable system functions correctly.',
-      'group' => 'Bootstrap'
-    );
-  }
-
-  /**
-   * testVariable
-   */
-  function testVariable() {
-    // Setting and retrieving values.
-    $variable = $this->randomName();
-    variable_set('simpletest_bootstrap_variable_test', $variable);
-    $this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), t('Setting and retrieving values'));
-
-    // Make sure the variable persists across multiple requests.
-    $this->drupalGet('system-test/variable-get');
-    $this->assertText($variable, t('Variable persists across multiple requests'));
-
-    // Deleting variables.
-    $default_value = $this->randomName();
-    variable_del('simpletest_bootstrap_variable_test');
-    $variable = variable_get('simpletest_bootstrap_variable_test', $default_value);
-    $this->assertIdentical($variable, $default_value, t('Deleting variables'));
-  }
-
-  /**
-   * Makes sure that the default variable parameter is passed through okay.
-   */
-  function testVariableDefaults() {
-    // Tests passing nothing through to the default.
-    $this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), t('Variables are correctly defaulting to NULL.'));
-
-    // Tests passing 5 to the default parameter.
-    $this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), t('The default variable parameter is passed through correctly.'));
-  }
-
-}
-
-/**
- * Test hook_boot() and hook_exit().
- */
-class HookBootExitTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Boot and exit hook invocation',
-      'description' => 'Test that hook_boot() and hook_exit() are called correctly.',
-      'group' => 'Bootstrap',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('system_test', 'dblog');
-  }
-
-  /**
-   * Test calling of hook_boot() and hook_exit().
-   */
-  function testHookBootExit() {
-    // Test with cache disabled. Boot and exit should always fire.
-    variable_set('cache', 0);
-    $this->drupalGet('');
-    $calls = 1;
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));
-
-    // Test with normal cache. Boot and exit should be called.
-    variable_set('cache', 1);
-    $this->drupalGet('');
-    $calls++;
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.'));
-
-    // Boot and exit should not fire since the page is cached.
-    variable_set('page_cache_invoke_hooks', FALSE);
-    $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
-    $this->drupalGet('');
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with agressive cache and a cached page.'));
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with agressive cache and a cached page.'));
-
-    // Test with page cache cleared, boot and exit should be called.
-    $this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
-    $this->drupalGet('');
-    $calls++;
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with agressive cache and no cached page.'));
-    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with agressive cache and no cached page.'));
-  }
-}
-
-/**
- * Test drupal_get_filename()'s availability.
- */
-class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Get filename test',
-      'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.',
-      'group' => 'Bootstrap',
-    );
-  }
-
-  /**
-   * Test that drupal_get_filename() works correctly when the file is not found in the database.
-   */
-  function testDrupalGetFilename() {
-    // Reset the static cache so we can test the "db is not active" code of
-    // drupal_get_filename().
-    drupal_static_reset('drupal_get_filename');
-
-    // Retrieving the location of a module.
-    $this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.'));
-
-    // Retrieving the location of a theme.
-    $this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.'));
-
-    // Retrieving the location of a theme engine.
-    $this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.'));
-
-    // Retrieving a file that is definitely not stored in the database.
-    $this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.'));
-  }
-}
-
-class BootstrapTimerTestCase extends DrupalUnitTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Timer test',
-      'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.',
-      'group' => 'Bootstrap',
-    );
-  }
-
-  /**
-   * Test timer_read() to ensure it properly accumulates time when the timer
-   * started and stopped multiple times.
-   * @return
-   */
-  function testTimer() {
-    timer_start('test');
-    sleep(1);
-    $this->assertTrue(timer_read('test') >= 1000, t('Timer measured 1 second of sleeping while running.'));
-    sleep(1);
-    timer_stop('test');
-    $this->assertTrue(timer_read('test') >= 2000, t('Timer measured 2 seconds of sleeping after being stopped.'));
-    timer_start('test');
-    sleep(1);
-    $this->assertTrue(timer_read('test') >= 3000, t('Timer measured 3 seconds of sleeping after being restarted.'));
-    sleep(1);
-    $timer = timer_stop('test');
-    $this->assertTrue(timer_read('test') >= 4000, t('Timer measured 4 seconds of sleeping after being stopped for a second time.'));
-    $this->assertEqual($timer['count'], 2, t('Timer counted 2 instances of being started.'));
-  }
-}
-
-/**
- * Test that resetting static variables works.
- */
-class BootstrapResettableStaticTestCase extends DrupalUnitTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Resettable static variables test',
-      'description' => 'Test that drupal_static() and drupal_static_reset() work.',
-      'group' => 'Bootstrap',
-    );
-  }
-
-  /**
-   * Test that a variable reference returned by drupal_static() gets reset when
-   * drupal_static_reset() is called.
-   */
-  function testDrupalStatic() {
-    $name = __CLASS__ . '_' . __METHOD__;
-    $var = &drupal_static($name, 'foo');
-    $this->assertEqual($var, 'foo', t('Variable returned by drupal_static() was set to its default.'));
-
-    // Call the specific reset and the global reset each twice to ensure that
-    // multiple resets can be issued without odd side effects.
-    $var = 'bar';
-    drupal_static_reset($name);
-    $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of name-specific reset.'));
-    $var = 'bar';
-    drupal_static_reset($name);
-    $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of name-specific reset.'));
-    $var = 'bar';
-    drupal_static_reset();
-    $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of global reset.'));
-    $var = 'bar';
-    drupal_static_reset();
-    $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of global reset.'));
-  }
-}
diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test
deleted file mode 100644
index d5a9c5f..0000000
--- a/modules/simpletest/tests/cache.test
+++ /dev/null
@@ -1,351 +0,0 @@
-<?php
-// $Id: cache.test,v 1.10 2009/09/13 17:49:51 dries Exp $
-
-class CacheTestCase extends DrupalWebTestCase {
-  protected $default_bin = 'cache';
-  protected $default_cid = 'test_temporary';
-  protected $default_value = 'CacheTest';
-
-  /**
-   * Check whether or not a cache entry exists.
-   *
-   * @param $cid
-   *   The cache id.
-   * @param $var
-   *   The variable the cache should contain.
-   * @param $bin
-   *   The bin the cache item was stored in.
-   * @return
-   *   TRUE on pass, FALSE on fail.
-   */
-  protected function checkCacheExists($cid, $var, $bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-
-    $cache = cache_get($cid, $bin);
-
-    return isset($cache->data) && $cache->data == $var;
-  }
-
-  /**
-   * Assert or a cache entry exists.
-   *
-   * @param $message
-   *   Message to display.
-   * @param $var
-   *   The variable the cache should contain.
-   * @param $cid
-   *   The cache id.
-   * @param $bin
-   *   The bin the cache item was stored in.
-   */
-  protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-    if ($cid == NULL) {
-      $cid = $this->default_cid;
-    }
-    if ($var == NULL) {
-      $var = $this->default_value;
-    }
-
-    $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
-  }
-
-  /**
-   * Assert or a cache entry has been removed.
-   *
-   * @param $message
-   *   Message to display.
-   * @param $cid
-   *   The cache id.
-   * @param $bin
-   *   The bin the cache item was stored in.
-   */
-  function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-    if ($cid == NULL) {
-      $cid = $this->default_cid;
-    }
-
-    $cache = cache_get($cid, $bin);
-    $this->assertFalse($cache, $message);
-  }
-
-  /**
-   * Perform the general wipe.
-   * @param $bin
-   *   The bin to perform the wipe on.
-   */
-  protected function generalWipe($bin = NULL) {
-    if ($bin == NULL) {
-      $bin = $this->default_bin;
-    }
-
-    cache_clear_all(NULL, $bin);
-  }
-
-  /**
-   * Setup the lifetime settings for caching.
-   *
-   * @param $time
-   *   The time in seconds the cache should minimal live.
-   */
-  protected function setupLifetime($time) {
-    variable_set('cache_lifetime', $time);
-    variable_set('cache_flush', 0);
-  }
-}
-
-class CacheSavingCase extends CacheTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Cache saving test',
-      'description' => 'Check our variables are saved and restored the right way.',
-      'group' => 'Cache'
-    );
-  }
-
-  /**
-   * Test the saving and restoring of a string.
-   */
-  function testString() {
-    $this->checkVariable($this->randomName(100));
-  }
-
-  /**
-   * Test the saving and restoring of an integer.
-   */
-  function testInteger() {
-    $this->checkVariable(100);
-  }
-
-  /**
-   * Test the saving and restoring of a double.
-   */
-  function testDouble() {
-    $this->checkVariable(1.29);
-  }
-
-  /**
-   * Test the saving and restoring of an array.
-   */
-  function testArray() {
-    $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
-  }
-
-  /**
-   * Test the saving and restoring of an object.
-   */
-  function testObject() {
-    $test_object = new stdClass();
-    $test_object->test1 = $this->randomName(100);
-    $test_object->test2 = 100;
-    $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
-
-    cache_set('test_object', $test_object, 'cache');
-    $cache = cache_get('test_object', 'cache');
-    $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
-  }
-
-  /*
-   * Check or a variable is stored and restored properly.
-   **/
-  function checkVariable($var) {
-    cache_set('test_var', $var, 'cache');
-    $cache = cache_get('test_var', 'cache');
-    $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
-  }
-}
-
-/**
- * Test cache_get_multiple().
- */
-class CacheGetMultipleUnitTest extends CacheTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Fetching multiple cache items',
-      'description' => 'Confirm that multiple records are fetched correctly.',
-      'group' => 'Cache',
-    );
-  }
-
-  function setUp() {
-    $this->default_bin = 'cache_page';
-    parent::setUp();
-  }
-
-  /**
-   * Test cache_get_multiple().
-   */
-  function testCacheMultiple() {
-    $item1 = $this->randomName(10);
-    $item2 = $this->randomName(10);
-    cache_set('item1', $item1, $this->default_bin);
-    cache_set('item2', $item2, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.'));
-    $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.'));
-
-    // Fetch both records from the database with cache_get_multiple().
-    $item_ids = array('item1', 'item2');
-    $items = cache_get_multiple($item_ids, $this->default_bin);
-    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
-    $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.'));
-
-    // Remove one item from the cache.
-    cache_clear_all('item2', $this->default_bin);
-
-    // Confirm that only one item is returned by cache_get_multiple().
-    $item_ids = array('item1', 'item2');
-    $items = cache_get_multiple($item_ids, $this->default_bin);
-    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
-    $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.'));
-    $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
-  }
-}
-
-/**
- * Test cache clearing methods.
- */
-class CacheClearCase extends CacheTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Cache clear test',
-      'description' => 'Check our clearing is done the proper way.',
-      'group' => 'Cache'
-    );
-  }
-
-  function setUp() {
-    $this->default_bin = 'cache_page';
-    $this->default_value = $this->randomName(10);
-
-    parent::setUp();
-  }
-
-  /**
-   * Test clearing using a cid.
-   */
-  function testClearCid() {
-    cache_set('test_cid_clear', $this->default_value, $this->default_bin);
-
-    $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
-    cache_clear_all('test_cid_clear', $this->default_bin);
-
-    $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
-
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches were created for checking cid "*" with wildcard false.'));
-    cache_clear_all('*', $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches still exists after clearing cid "*" with wildcard false.'));
-  }
-
-  /**
-   * Test clearing using wildcard.
-   */
-  function testClearWildcard() {
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches were created for checking cid "*" with wildcard true.'));
-    cache_clear_all('*', $this->default_bin, TRUE);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches removed after clearing cid "*" with wildcard true.'));
-
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches were created for checking cid substring with wildcard true.'));
-    cache_clear_all('test_', $this->default_bin, TRUE);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two caches removed after clearing cid substring with wildcard true.'));
-  }
-
-  /**
-   * Test clearing using an array.
-   */
-  function testClearArray() {
-    // Create three cache entries.
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear3', $this->default_value),
-                      t('Three cache entries were created.'));
-
-    // Clear two entries using an array.
-    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                       t('Two cache entries removed after clearing with an array.'));
-
-    $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
-                      t('Entry was not cleared from the cache'));
-
-    // Set the cache clear threshold to 2 to confirm that the full bin is cleared
-    // when the threshold is exceeded.
-    variable_set('cache_clear_threshold', 2);
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
-                      t('Two cache entries were created.'));
-    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear2', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear3', $this->default_value),
-                       t('All cache entries removed when the array exceeded the cache clear threshold.'));
-  }
-}
-
-/**
- * Test cache_is_empty() function.
- */
-class CacheIsEmptyCase extends CacheTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Cache emptiness test',
-      'description' => 'Check if a cache bin is empty after performing clear operations.',
-      'group' => 'Cache'
-    );
-  }
-
-  function setUp() {
-    $this->default_bin = 'cache_page';
-    $this->default_value = $this->randomName(10);
-
-    parent::setUp();
-  }
-
-  /**
-   * Test clearing using a cid.
-   */
-  function testIsEmpty() {
-    // Clear the cache bin.
-    cache_clear_all('*', $this->default_bin);
-    $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
-    // Add some data to the cache bin.
-    cache_set($this->default_cid, $this->default_value, $this->default_bin);
-    $this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid);
-    $this->assertFalse(cache_is_empty($this->default_bin), t('The cache bin is not empty'));
-    // Remove the cached data.
-    cache_clear_all($this->default_cid, $this->default_bin);
-    $this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid);
-    $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
-  }
-}
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
deleted file mode 100644
index e81a950..0000000
--- a/modules/simpletest/tests/common.test
+++ /dev/null
@@ -1,1990 +0,0 @@
-<?php
-// $Id: common.test,v 1.116 2010/07/07 17:00:43 webchick Exp $
-
-/**
- * @file
- * Tests for common.inc functionality.
- */
-
-/**
- * Tests for URL generation functions.
- */
-class DrupalAlterTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'drupal_alter() tests',
-      'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('common_test');
-  }
-
-  function testDrupalAlter() {
-    // This test depends on Garland, so make sure that it is always the current
-    // active theme.
-    global $theme, $base_theme_info;
-    $theme = 'garland';
-    $base_theme_info = array();
-
-    $array = array('foo' => 'bar');
-    $entity = new stdClass();
-    $entity->foo = 'bar';
-
-    // Verify alteration of a single argument.
-    $array_copy = $array;
-    $array_expected = array('foo' => 'Drupal theme');
-    drupal_alter('drupal_alter', $array_copy);
-    $this->assertEqual($array_copy, $array_expected, t('Single array was altered.'));
-
-    $entity_copy = clone $entity;
-    $entity_expected = clone $entity;
-    $entity_expected->foo = 'Drupal theme';
-    drupal_alter('drupal_alter', $entity_copy);
-    $this->assertEqual($entity_copy, $entity_expected, t('Single object was altered.'));
-
-    // Verify alteration of multiple arguments.
-    $array_copy = $array;
-    $array_expected = array('foo' => 'Drupal theme');
-    $entity_copy = clone $entity;
-    $entity_expected = clone $entity;
-    $entity_expected->foo = 'Drupal theme';
-    $array2_copy = $array;
-    $array2_expected = array('foo' => 'Drupal theme');
-    drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
-    $this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.'));
-    $this->assertEqual($entity_copy, $entity_expected, t('Second argument to drupal_alter() was altered.'));
-    $this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.'));
-  }
-}
-
-/**
- * Tests for URL generation functions.
- *
- * url() calls module_implements(), which may issue a db query, which requires
- * inheriting from a web test case rather than a unit test case.
- */
-class CommonURLUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'URL generation tests',
-      'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Confirm that invalid text given as $path is filtered.
-   */
-  function testLXSS() {
-    $text = $this->randomName();
-    $path = "<SCRIPT>alert('XSS')</SCRIPT>";
-    $link = l($text, $path);
-    $sanitized_path = check_url(url($path));
-    $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path)));
-  }
-
-  /*
-   * Tests for active class in l() function.
-   */
-  function testLActiveClass() {
-    $link = l($this->randomName(), $_GET['q']);
-    $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
-  }
-
-  /**
-   * Tests for custom class in l() function.
-   */
-  function testLCustomClass() {
-    $class = $this->randomName();
-    $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class))));
-    $this->assertTrue($this->hasClass($link, $class), t('Custom class @class is present on link when requested', array('@class' => $class)));
-    $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
-  }
-
-  private function hasClass($link, $class) {
-    return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link);
-  }
-
-  /**
-   * Test drupal_get_query_parameters().
-   */
-  function testDrupalGetQueryParameters() {
-    $original = array(
-      'a' => 1,
-      'b' => array(
-        'd' => 4,
-        'e' => array(
-          'f' => 5,
-        ),
-      ),
-      'c' => 3,
-      'q' => 'foo/bar',
-    );
-
-    // Default arguments.
-    $result = $_GET;
-    unset($result['q']);
-    $this->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed."));
-
-    // Default exclusion.
-    $result = $original;
-    unset($result['q']);
-    $this->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed."));
-
-    // First-level exclusion.
-    $result = $original;
-    unset($result['b']);
-    $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, t("'b' was removed."));
-
-    // Second-level exclusion.
-    $result = $original;
-    unset($result['b']['d']);
-    $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, t("'b[d]' was removed."));
-
-    // Third-level exclusion.
-    $result = $original;
-    unset($result['b']['e']['f']);
-    $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, t("'b[e][f]' was removed."));
-
-    // Multiple exclusions.
-    $result = $original;
-    unset($result['a'], $result['b']['e'], $result['c']);
-    $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, t("'a', 'b[e]', 'c' were removed."));
-  }
-
-  /**
-   * Test drupal_http_build_query().
-   */
-  function testDrupalHttpBuildQuery() {
-    $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', t('Value was properly encoded.'));
-    $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', t('Key was properly encoded.'));
-    $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', t('Multiple values were properly concatenated.'));
-    $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', t('Nested array was properly encoded.'));
-  }
-
-  /**
-   * Test drupal_parse_url().
-   */
-  function testDrupalParseUrl() {
-    // Relative URL.
-    $url = 'foo/bar?foo=bar&bar=baz&baz#foo';
-    $result = array(
-      'path' => 'foo/bar',
-      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
-      'fragment' => 'foo',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
-
-    // Relative URL that is known to confuse parse_url().
-    $url = 'foo/bar:1';
-    $result = array(
-      'path' => 'foo/bar:1',
-      'query' => array(),
-      'fragment' => '',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
-
-    // Absolute URL.
-    $url = '/foo/bar?foo=bar&bar=baz&baz#foo';
-    $result = array(
-      'path' => '/foo/bar',
-      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
-      'fragment' => 'foo',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.'));
-
-    // External URL testing.
-    $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
-
-    // Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
-    $this->assertTrue(url_is_external($url), t('Correctly identified an external URL.'));
-
-    // Test the parsing of absolute URLs.
-    $result = array(
-      'path' => 'http://drupal.org/foo/bar',
-      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
-      'fragment' => 'foo',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.'));
-
-    // Verify proper parsing of URLs when clean URLs are disabled.
-    $result = array(
-      'path' => 'foo/bar',
-      'query' => array('bar' => 'baz'),
-      'fragment' => 'foo',
-    );
-    // Non-clean URLs #1: Absolute URL generated by url().
-    $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
-    $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.'));
-
-    // Non-clean URLs #2: Relative URL generated by url().
-    $url = '?q=foo/bar&bar=baz#foo';
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.'));
-
-    // Non-clean URLs #3: URL generated by url() on non-Apache webserver.
-    $url = 'index.php?q=foo/bar&bar=baz#foo';
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'));
-
-    // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect.
-    $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html');
-    $this->assertFalse(valid_url($parts['path'], TRUE), t('drupal_parse_url() correctly parsed a forged URL.'));
-  }
-
-  /**
-   * Test url() with/without query, with/without fragment, absolute on/off and
-   * assert all that works when clean URLs are on and off.
-   */
-  function testUrl() {
-    global $base_url;
-
-    foreach (array(FALSE, TRUE) as $absolute) {
-      // Get the expected start of the path string.
-      $base = $absolute ? $base_url . '/' : base_path();
-      $absolute_string = $absolute ? 'absolute' : NULL;
-
-      // Disable Clean URLs.
-      $GLOBALS['conf']['clean_url'] = 0;
-
-      $url = $base . '?q=node/123';
-      $result = url('node/123', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123#foo';
-      $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo=bar&bar=baz';
-      $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo#bar';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base;
-      $result = url('<front>', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      // Enable Clean URLs.
-      $GLOBALS['conf']['clean_url'] = 1;
-
-      $url = $base . 'node/123';
-      $result = url('node/123', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123#foo';
-      $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123?foo';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123?foo=bar&bar=baz';
-      $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123?foo#bar';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base;
-      $result = url('<front>', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-    }
-  }
-
-  /**
-   * Test external URL handling.
-   */
-  function testExternalUrls() {
-    $test_url = 'http://drupal.org/';
-
-    // Verify external URL can contain a fragment.
-    $url = $test_url . '#drupal';
-    $result = url($url);
-    $this->assertEqual($url, $result, t('External URL with fragment works without a fragment in $options.'));
-
-    // Verify fragment can be overidden in an external URL.
-    $url = $test_url . '#drupal';
-    $fragment = $this->randomName(10);
-    $result = url($url, array('fragment' => $fragment));
-    $this->assertEqual($test_url . '#' . $fragment, $result, t('External URL fragment is overidden with a custom fragment in $options.'));
-
-    // Verify external URL can contain a query string.
-    $url = $test_url . '?drupal=awesome';
-    $result = url($url);
-    $this->assertEqual($url, $result, t('External URL with query string works without a query string in $options.'));
-
-    // Verify external URL can be extended with a query string.
-    $url = $test_url;
-    $query = array($this->randomName(5) => $this->randomName(5));
-    $result = url($url, array('query' => $query));
-    $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, t('External URL can be extended with a query string in $options.'));
-
-    // Verify query string can be extended in an external URL.
-    $url = $test_url . '?drupal=awesome';
-    $query = array($this->randomName(5) => $this->randomName(5));
-    $result = url($url, array('query' => $query));
-    $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, t('External URL query string can be extended with a custom query string in $options.'));
-  }
-}
-
-/**
- * Tests for the check_plain() and filter_xss() functions.
- */
-class CommonXssUnitTest extends DrupalUnitTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'String filtering tests',
-      'description' => 'Confirm that check_plain(), filter_xss(), and check_url() work correctly, including invalid multi-byte sequences.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Check that invalid multi-byte sequences are rejected.
-   */
-  function testInvalidMultiByte() {
-     // Ignore PHP 5.3+ invalid multibyte sequence warning.
-     $text = @check_plain("Foo\xC0barbaz");
-     $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"');
-     $text = check_plain("Fooÿñ");
-     $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"');
-     $text = filter_xss("Foo\xC0barbaz");
-     $this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"');
-     $text = filter_xss("Fooÿñ");
-     $this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ');
-  }
-
-  /**
-   * Check that special characters are escaped.
-   */
-  function testEscaping() {
-     $text = check_plain("<script>");
-     $this->assertEqual($text, '&lt;script&gt;', 'check_plain() escapes &lt;script&gt;');
-  }
-
-  /**
-   * Check that harmful protocols are stripped.
-   */
-  function testBadProtocolStripping() {
-    // Ensure that check_url() strips out harmful protocols, and encodes for
-    // HTML. Ensure drupal_strip_dangerous_protocols() can be used to return a
-    // plain-text string stripped of harmful protocols.
-    $url = 'javascript:http://www.example.com/?x=1&y=2';
-    $expected_plain = 'http://www.example.com/?x=1&y=2';
-    $expected_html = 'http://www.example.com/?x=1&amp;y=2';
-    $this->assertIdentical(check_url($url), $expected_html, t('check_url() filters a URL and encodes it for HTML.'));
-    $this->assertIdentical(drupal_strip_dangerous_protocols($url), $expected_plain, t('drupal_strip_dangerous_protocols() filters a URL and returns plain text.'));
-  }
-}
-
-class CommonSizeTestCase extends DrupalUnitTestCase {
-  protected $exact_test_cases;
-  protected $rounded_test_cases;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Size parsing test',
-      'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.',
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    $kb = DRUPAL_KILOBYTE;
-    $this->exact_test_cases = array(
-      '1 byte' => 1,
-      '1 KB'   => $kb,
-      '1 MB'   => $kb * $kb,
-      '1 GB'   => $kb * $kb * $kb,
-      '1 TB'   => $kb * $kb * $kb * $kb,
-      '1 PB'   => $kb * $kb * $kb * $kb * $kb,
-      '1 EB'   => $kb * $kb * $kb * $kb * $kb * $kb,
-      '1 ZB'   => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
-      '1 YB'   => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
-    );
-    $this->rounded_test_cases = array(
-      '2 bytes' => 2,
-      '1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
-      round(3623651 / ($this->exact_test_cases['1 MB']), 2) . ' MB' => 3623651, // megabytes
-      round(67234178751368124 / ($this->exact_test_cases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
-      round(235346823821125814962843827 / ($this->exact_test_cases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
-    );
-    parent::setUp();
-  }
-
-  /**
-   * Check that format_size() returns the expected string.
-   */
-  function testCommonFormatSize() {
-    foreach (array($this->exact_test_cases, $this->rounded_test_cases) as $test_cases) {
-      foreach ($test_cases as $expected => $input) {
-        $this->assertEqual(
-          ($result = format_size($input, NULL)),
-          $expected,
-          $expected . ' == ' . $result . ' (' . $input . ' bytes)'
-        );
-      }
-    }
-  }
-
-  /**
-   * Check that parse_size() returns the proper byte sizes.
-   */
-  function testCommonParseSize() {
-    foreach ($this->exact_test_cases as $string => $size) {
-      $this->assertEqual(
-        $parsed_size = parse_size($string),
-        $size,
-        $size . ' == ' . $parsed_size . ' (' . $string . ')'
-      );
-    }
-
-    // Some custom parsing tests
-    $string = '23476892 bytes';
-    $this->assertEqual(
-      ($parsed_size = parse_size($string)),
-      $size = 23476892,
-      $string . ' == ' . $parsed_size . ' bytes'
-    );
-    $string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB
-    $this->assertEqual(
-      $parsed_size = parse_size($string),
-      $size = 79691776,
-      $string . ' == ' . $parsed_size . ' bytes'
-    );
-    $string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB
-    $this->assertEqual(
-      $parsed_size = parse_size($string),
-      $size = 81862076662,
-      $string . ' == ' . $parsed_size . ' bytes'
-    );
-  }
-
-  /**
-   * Cross-test parse_size() and format_size().
-   */
-  function testCommonParseSizeFormatSize() {
-    foreach ($this->exact_test_cases as $size) {
-      $this->assertEqual(
-        $size,
-        ($parsed_size = parse_size($string = format_size($size, NULL))),
-        $size . ' == ' . $parsed_size . ' (' . $string . ')'
-      );
-    }
-  }
-}
-
-/**
- * Test drupal_explode_tags() and drupal_implode_tags().
- */
-class DrupalTagsHandlingTestCase extends DrupalWebTestCase {
-  var $validTags = array(
-    'Drupal' => 'Drupal',
-    'Drupal with some spaces' => 'Drupal with some spaces',
-    '"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
-    '"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
-  );
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal tags handling',
-      'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.",
-      'group' => 'System'
-    );
-  }
-
-  /**
-   * Explode a series of tags.
-   */
-  function testDrupalExplodeTags() {
-    $string = implode(', ', array_keys($this->validTags));
-    $tags = drupal_explode_tags($string);
-    $this->assertTags($tags);
-  }
-
-  /**
-   * Implode a series of tags.
-   */
-  function testDrupalImplodeTags() {
-    $tags = array_values($this->validTags);
-    // Let's explode and implode to our heart's content.
-    for ($i = 0; $i < 10; $i++) {
-      $string = drupal_implode_tags($tags);
-      $tags = drupal_explode_tags($string);
-    }
-    $this->assertTags($tags);
-  }
-
-  /**
-   * Helper function: asserts that the ending array of tags is what we wanted.
-   */
-  function assertTags($tags) {
-    $original = $this->validTags;
-    foreach ($tags as $tag) {
-      $key = array_search($tag, $original);
-      $this->assertTrue($key, t('Make sure tag %tag shows up in the final tags array (originally %original)', array('%tag' => $tag, '%original' => $key)));
-      unset($original[$key]);
-    }
-    foreach ($original as $leftover) {
-      $this->fail(t('Leftover tag %leftover was left over.', array('%leftover' => $leftover)));
-    }
-  }
-}
-
-/**
- * Test the Drupal CSS system.
- */
-class CascadingStylesheetsTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Cascading stylesheets',
-      'description' => 'Tests adding various cascading stylesheets to the page.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('php', 'locale', 'common_test');
-    // Reset drupal_add_css() before each test.
-    drupal_static_reset('drupal_add_css');
-  }
-
-  /**
-   * Check default stylesheets as empty.
-   */
-  function testDefault() {
-    $this->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.'));
-  }
-
-  /**
-   * Tests adding a file stylesheet.
-   */
-  function testAddFile() {
-    $path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
-    $css = drupal_add_css($path);
-    $this->assertEqual($css[$path]['data'], $path, t('Adding a CSS file caches it properly.'));
-  }
-
-  /**
-   * Tests adding an external stylesheet.
-   */
-  function testAddExternal() {
-    $path = 'http://example.com/style.css';
-    $css = drupal_add_css($path, 'external');
-    $this->assertEqual($css[$path]['type'], 'external', t('Adding an external CSS file caches it properly.'));
-  }
-
-  /**
-   * Makes sure that reseting the CSS empties the cache.
-   */
-  function testReset() {
-    drupal_static_reset('drupal_add_css');
-    $this->assertEqual(array(), drupal_add_css(), t('Resetting the CSS empties the cache.'));
-  }
-
-  /**
-   * Tests rendering the stylesheets.
-   */
-  function testRenderFile() {
-    $css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
-    drupal_add_css($css);
-    $styles = drupal_get_css();
-    $this->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.'));
-  }
-
-  /**
-   * Tests rendering an external stylesheet.
-   */
-  function testRenderExternal() {
-    $css = 'http://example.com/style.css';
-    drupal_add_css($css, 'external');
-    $styles = drupal_get_css();
-    // Stylesheet URL may be the href of a LINK tag or in an @import statement
-    // of a STYLE tag.
-    $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, t('Rendering an external CSS file.'));
-  }
-
-  /**
-   * Tests rendering inline stylesheets with preprocessing on.
-   */
-  function testRenderInlinePreprocess() {
-    $css = 'body { padding: 0px; }';
-    $css_preprocessed = '<style type="text/css" media="all">' . drupal_load_stylesheet_content($css, TRUE) . '</style>';
-    drupal_add_css($css, 'inline');
-    $styles = drupal_get_css();
-    $this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
-  }
-
-  /**
-   * Tests rendering inline stylesheets with preprocessing off.
-   */
-  function testRenderInlineNoPreprocess() {
-    $css = 'body { padding: 0px; }';
-    drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
-    $styles = drupal_get_css();
-    $this->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.'));
-  }
-
-  /**
-   * Tests rendering inline stylesheets through a full page request.
-   */
-  function testRenderInlineFullPage() {
-    $css = 'body { font-size: 254px; }';
-    $expected = 'font-size:254px;';
-
-    // Create a node, using the PHP filter that tests drupal_add_css().
-    $php_format_id = db_query_range('SELECT format FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'PHP code'))->fetchField();
-    $settings = array(
-      'type' => 'page',
-      'body' => array(
-        LANGUAGE_NONE => array(
-          array(
-            'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('$css', 'inline'); ?>",
-            'format' => $php_format_id,
-          ),
-        ),
-      ),
-      'promote' => 1,
-    );
-    $node = $this->drupalCreateNode($settings);
-
-    // Fetch the page.
-    $this->drupalGet('node/' . $node->nid);
-    $this->assertRaw($expected, t('Inline stylesheets appear in the full page rendering.'));
-  }
-
-  /**
-   * Test CSS ordering.
-   */
-  function testRenderOrder() {
-    // A module CSS file.
-    drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
-    // A few system CSS files, ordered in a strange way.
-    $system_path = drupal_get_path('module', 'system');
-    drupal_add_css($system_path . '/system-behavior.css', array('weight' => CSS_SYSTEM));
-    drupal_add_css($system_path . '/system.css', array('weight' => CSS_SYSTEM - 10));
-    drupal_add_css($system_path . '/system-menus.css', array('weight' => CSS_SYSTEM));
-
-    $expected = array(
-      $system_path . '/system.css',
-      $system_path . '/system-behavior.css',
-      $system_path . '/system-menus.css',
-      drupal_get_path('module', 'simpletest') . '/simpletest.css',
-    );
-
-
-    $styles = drupal_get_css();
-    // Stylesheet URL may be the href of a LINK tag or in an @import statement
-    // of a STYLE tag.
-    if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
-      $result = $matches[2];
-    }
-    else {
-      $result = array();
-    }
-
-    $this->assertIdentical($result, $expected, t('The CSS files are in the expected order.'));
-  }
-
-  /**
-   * Test CSS override.
-   */
-  function testRenderOverride() {
-    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
-    drupal_add_css(drupal_get_path('module', 'simpletest') . '/tests/system.css');
-
-    // The dummy stylesheet should be the only one included.
-    $styles = drupal_get_css();
-    $this->assert(strpos($styles, drupal_get_path('module', 'simpletest') . '/tests/system.css') !== FALSE, t('The overriding CSS file is output.'));
-    $this->assert(strpos($styles, drupal_get_path('module', 'system') . '/system.css') === FALSE, t('The overriden CSS file is not output.'));
-
-    drupal_add_css(drupal_get_path('module', 'simpletest') . '/tests/system.css');
-    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
-
-    // The standard stylesheet should be the only one included.
-    $styles = drupal_get_css();
-    $this->assert(strpos($styles, drupal_get_path('module', 'system') . '/system.css') !== FALSE, t('The overriding CSS file is output.'));
-    $this->assert(strpos($styles, drupal_get_path('module', 'simpletest') . '/tests/system.css') === FALSE, t('The overriden CSS file is not output.'));
-  }
-
-  /**
-   * Tests Locale module's CSS Alter to include RTL overrides.
-   */
-  function testAlter() {
-    // Switch the language to a right to left language and add system.css.
-    global $language;
-    $language->direction = LANGUAGE_RTL;
-    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
-
-    // Check to see if system-rtl.css was also added.
-    $styles = drupal_get_css();
-    $this->assert(strpos($styles, drupal_get_path('module', 'system') . '/system-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.'));
-
-    // Change the language back to left to right.
-    $language->direction = LANGUAGE_LTR;
-  }
-
-  /**
-   * Tests that the query string remains intact when adding CSS files that have
-   * query string parameters.
-   */
-  function testAddCssFileWithQueryString() {
-    $this->drupalGet('common-test/query-string');
-    $query_string = substr(variable_get('css_js_query_string', '0'), 0, 1);
-    $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?arg1=value1&amp;arg2=value2&amp;' . $query_string, t('Query string was appended correctly to css.'));
-  }
-}
-
-/**
- * Test for cleaning HTML identifiers.
- */
-class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'HTML identifiers',
-      'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Tests that drupal_clean_css_identifier() cleans the identifier properly.
-   */
-  function testDrupalCleanCSSIdentifier() {
-    // Verify that no valid ASCII characters are stripped from the identifier.
-    $identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
-    $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid ASCII characters pass through.'));
-
-    // Verify that valid UTF-8 characters are not stripped from the identifier.
-    $identifier = '¡¢£¤¥';
-    $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid UTF-8 characters pass through.'));
-
-    // Verify that invalid characters (including non-breaking space) are stripped from the identifier.
-    $this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', t('Strip invalid characters.'));
-  }
-
-  /**
-   * Tests that drupal_html_class() cleans the class name properly.
-   */
-  function testDrupalHTMLClass() {
-    // Verify Drupal coding standards are enforced.
-    $this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', t('Enforce Drupal coding standards.'));
-  }
-
-  /**
-   * Tests that drupal_html_id() cleans the ID properly.
-   */
-  function testDrupalHTMLId() {
-    // Verify that letters, digits, and hyphens are not stripped from the ID.
-    $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
-    $this->assertIdentical(drupal_html_id($id), $id, t('Verify valid characters pass through.'));
-
-    // Verify that invalid characters are stripped from the ID.
-    $this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', t('Strip invalid characters.'));
-
-    // Verify Drupal coding standards are enforced.
-    $this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', t('Enforce Drupal coding standards.'));
-
-    // Reset the static cache so we can ensure the unique id count is at zero.
-    drupal_static_reset('drupal_html_id');
-
-    // Clean up IDs with invalid starting characters.
-    $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', t('Test the uniqueness of IDs #1.'));
-    $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', t('Test the uniqueness of IDs #2.'));
-    $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', t('Test the uniqueness of IDs #3.'));
-  }
-}
-
-/**
- * CSS Unit Tests.
- */
-class CascadingStylesheetsUnitTest extends DrupalUnitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'CSS Unit Tests',
-      'description' => 'Unit tests on CSS functions like aggregation.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
-   *
-   * This can be enhanced by adding additional CSS files with variant test cases.
-   * Currently, this is specifically testing to make sure that whitespace
-   * is treated with adequate respect (see http://drupal.org/node/472820) and
-   * that image paths in imported files are preserved (see
-   * http://drupal.org/node/265719).
-   */
-  function testLoadCssBasic() {
-    // Array of files to test living in 'simpletest/files/css_test_files/'.
-    // - Original: name.css
-    // - Unoptimized expected content: name.css.unoptimized.css
-    // - Optimized expected content: name.css.optimized.css
-    $testfiles = array(
-      'css_input_without_import.css',
-      'css_input_with_import.css',
-      'comment_hacks.css'
-    );
-    $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
-    foreach ($testfiles as $file) {
-      $expected = file_get_contents("$path/$file.unoptimized.css");
-      $unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE);
-      $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array('@file' => $file)));
-
-      $expected = file_get_contents("$path/$file.optimized.css");
-      $optimized_output = drupal_load_stylesheet("$path/$file", TRUE);
-      $this->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array('@file' => $file)));
-    }
-  }
-}
-
-/**
- * Test drupal_http_request().
- */
-class DrupalHTTPRequestTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal HTTP request',
-      'description' => "Performs tests on Drupal's HTTP request mechanism.",
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    parent::setUp('system_test');
-  }
-
-  function testDrupalHTTPRequest() {
-    global $is_https;
-
-    // Parse URL schema.
-    $missing_scheme = drupal_http_request('example.com/path');
-    $this->assertEqual($missing_scheme->code, -1002, t('Returned with "-1002" error code.'));
-    $this->assertEqual($missing_scheme->error, 'missing schema', t('Returned with "missing schema" error message.'));
-
-    $unable_to_parse = drupal_http_request('http:///path');
-    $this->assertEqual($unable_to_parse->code, -1001, t('Returned with "-1001" error code.'));
-    $this->assertEqual($unable_to_parse->error, 'unable to parse URL', t('Returned with "unable to parse URL" error message.'));
-
-    // Fetch page.
-    $result = drupal_http_request(url('node', array('absolute' => TRUE)));
-    $this->assertEqual($result->code, 200, t('Fetched page successfully.'));
-    $this->drupalSetContent($result->data);
-    $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
-
-    // Test that code and status message is returned.
-    $result = drupal_http_request(url('pagedoesnotexist', array('absolute' => TRUE)));
-    $this->assertTrue(!empty($result->protocol),  t('Result protocol is returned.'));
-    $this->assertEqual($result->code, '404', t('Result code is 404'));
-    $this->assertEqual($result->status_message, 'Not Found', t('Result status message is "Not Found"'));
-
-    // Skip the timeout tests when the testing environment is HTTPS because
-    // stream_set_timeout() does not work for SSL connections.
-    // @link http://bugs.php.net/bug.php?id=47929
-    if (!$is_https) {
-      // Test that timeout is respected. The test machine is expected to be able
-      // to make the connection (i.e. complete the fsockopen()) in 2 seconds and
-      // return within a total of 5 seconds. If the test machine is extremely
-      // slow, the test will fail. fsockopen() has been seen to time out in
-      // slightly less than the specified timeout, so allow a little slack on
-      // the minimum expected time (i.e. 1.8 instead of 2).
-      timer_start(__METHOD__);
-      $result = drupal_http_request(url('system-test/sleep/10', array('absolute' => TRUE)), array('timeout' => 2));
-      $time = timer_read(__METHOD__) / 1000;
-      $this->assertTrue(1.8 < $time && $time < 5, t('Request timed out (%time seconds).', array('%time' => $time)));
-      $this->assertTrue($result->error, t('An error message was returned.'));
-      $this->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, t('Proper error code was returned.'));
-    }
-  }
-
-  function testDrupalHTTPRequestBasicAuth() {
-    $username = $this->randomName();
-    $password = $this->randomName();
-    $url = url('system-test/auth', array('absolute' => TRUE));
-
-    $auth = str_replace('://', '://' . $username . ':' . $password . '@', $url);
-    $result = drupal_http_request($auth);
-
-    $this->drupalSetContent($result->data);
-    $this->assertRaw($username, t('$_SERVER["PHP_AUTH_USER"] is passed correctly.'));
-    $this->assertRaw($password, t('$_SERVER["PHP_AUTH_PW"] is passed correctly.'));
-  }
-
-  function testDrupalHTTPRequestRedirect() {
-    $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1));
-    $this->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.'));
-
-    $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0));
-    $this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.'));
-
-    $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array('max_redirects' => 1));
-    $this->assertEqual($redirect_invalid->code, -1002, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
-    $this->assertEqual($redirect_invalid->error, 'missing schema', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
-
-    $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array('max_redirects' => 1));
-    $this->assertEqual($redirect_invalid->code, -1001, t('301 redirect to invalid URL returned with error message code "!error".', array('!error' => $redirect_invalid->error)));
-    $this->assertEqual($redirect_invalid->error, 'unable to parse URL', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
-
-    $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array('max_redirects' => 1));
-    $this->assertEqual($redirect_invalid->code, -1003, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
-    $this->assertEqual($redirect_invalid->error, 'invalid schema ftp', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
-
-    $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 1));
-    $this->assertEqual($redirect_302->redirect_code, 302, t('drupal_http_request follows the 302 redirect.'));
-
-    $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 0));
-    $this->assertFalse(isset($redirect_302->redirect_code), t('drupal_http_request does not follow 302 redirect if $retry = 0.'));
-
-    $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 1));
-    $this->assertEqual($redirect_307->redirect_code, 307, t('drupal_http_request follows the 307 redirect.'));
-
-    $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 0));
-    $this->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if max_redirects = 0.'));
-  }
-}
-
-/**
- * Testing drupal_add_region_content and drupal_get_region_content.
- */
-class DrupalSetContentTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal set/get regions',
-      'description' => 'Performs tests on setting and retrieiving content from theme regions.',
-      'group' => 'System'
-    );
-  }
-
-
-  /**
-   * Test setting and retrieving content for theme regions.
-   */
-  function testRegions() {
-    global $theme_key;
-
-    $block_regions = array_keys(system_region_list($theme_key));
-    $delimiter = $this->randomName(32);
-    $values = array();
-    // Set some random content for each region available.
-    foreach ($block_regions as $region) {
-      $first_chunk = $this->randomName(32);
-      drupal_add_region_content($region, $first_chunk);
-      $second_chunk = $this->randomName(32);
-      drupal_add_region_content($region, $second_chunk);
-      // Store the expected result for a drupal_get_region_content call for this region.
-      $values[$region] = $first_chunk . $delimiter . $second_chunk;
-    }
-
-    // Ensure drupal_get_region_content returns expected results when fetching all regions.
-    $content = drupal_get_region_content(NULL, $delimiter);
-    foreach ($content as $region => $region_content) {
-      $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching all regions', array('@region' => $region)));
-    }
-
-    // Ensure drupal_get_region_content returns expected results when fetching a single region.
-    foreach ($block_regions as $region) {
-      $region_content = drupal_get_region_content($region, $delimiter);
-      $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching single region.', array('@region' => $region)));
-    }
-  }
-}
-
-/**
- * Testing drupal_goto and hook_drupal_goto_alter().
- */
-class DrupalGotoTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal goto',
-      'description' => 'Performs tests on the drupal_goto function and hook_drupal_goto_alter',
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    parent::setUp('common_test');
-  }
-
-  /**
-   * Test drupal_goto().
-   */
-  function testDrupalGoto() {
-    $this->drupalGet('common-test/drupal_goto/redirect');
-    $headers = $this->drupalGetHeaders(TRUE);
-    list(, $status) = explode(' ', $headers[0][':status'], 3);
-    $this->assertEqual($status, 302, t('Expected response code was sent.'));
-    $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.'));
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('absolute' => TRUE)), t('Drupal goto redirected to expected URL.'));
-
-    $this->drupalGet('common-test/drupal_goto/redirect_advanced');
-    $headers = $this->drupalGetHeaders(TRUE);
-    list(, $status) = explode(' ', $headers[0][':status'], 3);
-    $this->assertEqual($status, 301, t('Expected response code was sent.'));
-    $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.'));
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to expected URL.'));
-
-    // Test that drupal_goto() respects ?destination=xxx. Use an complicated URL
-    // to test that the path is encoded and decoded properly.
-    $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123';
-    $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
-    $this->assertText('drupal_goto', t('Drupal goto redirect with destination succeeded.'));
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/destination', array('query' => array('foo' => '%25', 'bar' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to given query string destination. '));
-  }
-
-  /**
-   * Test hook_drupal_goto_alter().
-   */
-  function testDrupalGotoAlter() {
-    $this->drupalGet('common-test/drupal_goto/redirect_fail');
-
-    $this->assertNoText(t("Drupal goto failed to stop program"), t("Drupal goto stopped program."));
-    $this->assertNoText('drupal_goto_fail', t("Drupal goto redirect failed."));
-  }
-
-  /**
-   * Test drupal_get_destination().
-   */
-  function testDrupalGetDestination() {
-    $query = $this->randomName(10);
-
-    // Verify that a 'destination' query string is used as destination.
-    $this->drupalGet('common-test/destination', array('query' => array('destination' => $query)));
-    $this->assertText('The destination: ' . $query, t('The given query string destination is determined as destination.'));
-
-    // Verify that the current path is used as destination.
-    $this->drupalGet('common-test/destination', array('query' => array($query => NULL)));
-    $url = 'common-test/destination?' . $query;
-    $this->assertText('The destination: ' . $url, t('The current path is determined as destination.'));
-  }
-}
-
-/**
- * Tests for the JavaScript system.
- */
-class JavaScriptTestCase extends DrupalWebTestCase {
-  /**
-   * Store configured value for JavaScript preprocessing.
-   */
-  protected $preprocess_js = NULL;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'JavaScript',
-      'description' => 'Tests the JavaScript system.',
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    // Enable Locale and SimpleTest in the test environment.
-    parent::setUp('locale', 'simpletest', 'common_test');
-
-    // Disable preprocessing
-    $this->preprocess_js = variable_get('preprocess_js', 0);
-    variable_set('preprocess_js', 0);
-
-    // Reset drupal_add_js() and drupal_add_library() statics before each test.
-    drupal_static_reset('drupal_add_js');
-    drupal_static_reset('drupal_add_library');
-  }
-
-  function tearDown() {
-    // Restore configured value for JavaScript preprocessing.
-    variable_set('preprocess_js', $this->preprocess_js);
-    parent::tearDown();
-  }
-
-  /**
-   * Test default JavaScript is empty.
-   */
-  function testDefault() {
-    $this->assertEqual(array(), drupal_add_js(), t('Default JavaScript is empty.'));
-  }
-
-  /**
-   * Test adding a JavaScript file.
-   */
-  function testAddFile() {
-    $javascript = drupal_add_js('misc/collapse.js');
-    $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when a file is added.'));
-    $this->assertTrue(array_key_exists('misc/drupal.js', $javascript), t('Drupal.js is added when file is added.'));
-    $this->assertTrue(array_key_exists('misc/collapse.js', $javascript), t('JavaScript files are correctly added.'));
-    $this->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], t('Base path JavaScript setting is correctly set.'));
-  }
-
-  /**
-   * Test adding settings.
-   */
-  function testAddSetting() {
-    $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
-    $this->assertEqual(280342800, $javascript['settings']['data'][1]['dries'], t('JavaScript setting is set correctly.'));
-    $this->assertEqual('rocks', $javascript['settings']['data'][1]['drupal'], t('The other JavaScript setting is set correctly.'));
-  }
-
-  /**
-   * Tests adding an external JavaScript File.
-   */
-  function testAddExternal() {
-    $path = 'http://example.com/script.js';
-    $javascript = drupal_add_js($path, 'external');
-    $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), t('Added an external JavaScript file.'));
-  }
-
-  /**
-   * Test drupal_get_js() for JavaScript settings.
-   */
-  function testHeaderSetting() {
-    drupal_add_js(array('testSetting' => 'testValue'), 'setting');
-    $javascript = drupal_get_js('header');
-    $this->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.'));
-    $this->assertTrue(strpos($javascript, 'testSetting') > 0, t('Rendered JavaScript header returns custom setting.'));
-    $this->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.'));
-  }
-
-  /**
-   * Test to see if resetting the JavaScript empties the cache.
-   */
-  function testReset() {
-    drupal_add_js('misc/collapse.js');
-    drupal_static_reset('drupal_add_js');
-    $this->assertEqual(array(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.'));
-  }
-
-  /**
-   * Test adding inline scripts.
-   */
-  function testAddInline() {
-    $inline = 'jQuery(function () { });';
-    $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
-    $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when inline scripts are added.'));
-    $data = end($javascript);
-    $this->assertEqual($inline, $data['data'], t('Inline JavaScript is correctly added to the footer.'));
-  }
-
-  /**
-   * Test rendering an external JavaScript file.
-   */
-  function testRenderExternal() {
-    $external = 'http://example.com/example.js';
-    drupal_add_js($external, 'external');
-    $javascript = drupal_get_js();
-    // Local files have a base_path() prefix, external files should not.
-    $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, t('Rendering an external JavaScript file.'));
-  }
-
-  /**
-   * Test drupal_get_js() with a footer scope.
-   */
-  function testFooterHTML() {
-    $inline = 'jQuery(function () { });';
-    drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
-    $javascript = drupal_get_js('footer');
-    $this->assertTrue(strpos($javascript, $inline) > 0, t('Rendered JavaScript footer returns the inline code.'));
-  }
-
-  /**
-   * Test drupal_add_js() sets preproccess to false when cache is set to false.
-   */
-  function testNoCache() {
-    $javascript = drupal_add_js('misc/collapse.js', array('cache' => FALSE));
-    $this->assertFalse($javascript['misc/collapse.js']['preprocess'], t('Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.'));
-  }
-
-  /**
-   * Test adding a JavaScript file with a different weight.
-   */
-  function testDifferentWeight() {
-    $javascript = drupal_add_js('misc/collapse.js', array('weight' => JS_THEME));
-    $this->assertEqual($javascript['misc/collapse.js']['weight'], JS_THEME, t('Adding a JavaScript file with a different weight caches the given weight.'));
-  }
-
-  /**
-   * Test JavaScript ordering.
-   */
-  function testRenderOrder() {
-    // Add a bunch of JavaScript in strange ordering.
-    drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
-    drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
-    drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
-    drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
-    drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
-    drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
-    drupal_add_js('http://example.com/example.js?Weight -5 #1', array('type' => 'external', 'scope' => 'footer', 'weight' => -5));
-    drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
-    drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
-    drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
-
-    // Construct the expected result from the regex.
-    $expected = array(
-      "-8 #1",
-      "-8 #2",
-      "-8 #3",
-      "-8 #4",
-      "-5 #1", // The external script.
-      "0 #1",
-      "0 #2",
-      "0 #3",
-      "5 #1",
-      "5 #2",
-    );
-
-    // Retrieve the rendered JavaScript and test against the regex.
-    $js = drupal_get_js('footer');
-    $matches = array();
-    if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) {
-      $result = $matches[1];
-    }
-    else {
-      $result = array();
-    }
-    $this->assertIdentical($result, $expected, t('JavaScript is added in the expected weight order.'));
-  }
-
-  /**
-   * Test rendering the JavaScript with a file's weight above jQuery's.
-   */
-  function testRenderDifferentWeight() {
-    drupal_add_js('misc/collapse.js', array('weight' => JS_LIBRARY - 21));
-    $javascript = drupal_get_js();
-    $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), t('Rendering a JavaScript file above jQuery.'));
-  }
-
-  /**
-   * Test altering a JavaScript's weight via hook_js_alter().
-   *
-   * @see simpletest_js_alter()
-   */
-  function testAlter() {
-    // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
-    drupal_add_js('misc/tableselect.js');
-    drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array('weight' => JS_THEME));
-
-    // Render the JavaScript, testing if simpletest.js was altered to be before
-    // tableselect.js. See simpletest_js_alter() to see where this alteration
-    // takes place.
-    $javascript = drupal_get_js();
-    $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), t('Altering JavaScript weight through the alter hook.'));
-  }
-
-  /**
-   * Adds a library to the page and tests for both its JavaScript and its CSS.
-   */
-  function testLibraryRender() {
-    $result = drupal_add_library('system', 'farbtastic');
-    $this->assertTrue($result !== FALSE, t('Library was added without errors.'));
-    $scripts = drupal_get_js();
-    $styles = drupal_get_css();
-    $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('JavaScript of library was added to the page.'));
-    $this->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), t('Stylesheet of library was added to the page.'));
-  }
-
-  /**
-   * Adds a JavaScript library to the page and alters it.
-   *
-   * @see common_test_library_alter()
-   */
-  function testLibraryAlter() {
-    // Verify that common_test altered the title of Farbtastic.
-    $library = drupal_get_library('system', 'farbtastic');
-    $this->assertEqual($library['title'], 'Farbtastic: Altered Library', t('Registered libraries were altered.'));
-
-    // common_test_library_alter() also added a dependency on jQuery Form.
-    drupal_add_library('system', 'farbtastic');
-    $scripts = drupal_get_js();
-    $this->assertTrue(strpos($scripts, 'misc/jquery.form.js'), t('Altered library dependencies are added to the page.'));
-  }
-
-  /**
-   * Tests that multiple modules can implement the same library.
-   *
-   * @see common_test_library()
-   */
-  function testLibraryNameConflicts() {
-    $farbtastic = drupal_get_library('common_test', 'farbtastic');
-    $this->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', t('Alternative libraries can be added to the page.'));
-  }
-
-  /**
-   * Tests non-existing libraries.
-   */
-  function testLibraryUnknown() {
-    $result = drupal_get_library('unknown', 'unknown');
-    $this->assertFalse($result, t('Unknown library returned FALSE.'));
-    drupal_static_reset('drupal_get_library');
-
-    $result = drupal_add_library('unknown', 'unknown');
-    $this->assertFalse($result, t('Unknown library returned FALSE.'));
-    $scripts = drupal_get_js();
-    $this->assertTrue(strpos($scripts, 'unknown') === FALSE, t('Unknown library was not added to the page.'));
-  }
-
-  /**
-   * Tests the addition of libraries through the #attached['library'] property.
-   */
-  function testAttachedLibrary() {
-    $element['#attached']['library'][] = array('system', 'farbtastic');
-    drupal_render($element);
-    $scripts = drupal_get_js();
-    $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('The attached_library property adds the additional libraries.'));
-  }
-
-  /**
-   * Tests that the query string remains intact when adding JavaScript files
-   *  that have query string parameters.
-   */
-  function testAddJsFileWithQueryString() {
-    $this->drupalGet('common-test/query-string');
-    $query_string = substr(variable_get('css_js_query_string', '0'), 0, 1);
-    $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?arg1=value1&amp;arg2=value2&amp;' . $query_string, t('Query string was appended correctly to js.'));
-  }
-}
-
-/**
- * Tests for drupal_render().
- */
-class DrupalRenderUnitTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal render',
-      'description' => 'Performs unit tests on drupal_render().',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('common_test');
-  }
-
-  /**
-   * Test sorting by weight.
-   */
-  function testDrupalRenderSorting() {
-    $first = $this->randomName();
-    $second = $this->randomName();
-    // Build an array with '#weight' set for each element.
-    $elements = array(
-      'second' => array(
-        '#weight' => 10,
-        '#markup' => $second,
-      ),
-      'first' => array(
-        '#weight' => 0,
-        '#markup' => $first,
-      ),
-    );
-    $output = drupal_render($elements);
-
-    // The lowest weight element should appear last in $output.
-    $this->assertTrue(strpos($output, $second) > strpos($output, $first), t('Elements were sorted correctly by weight.'));
-
-    // Confirm that the $elements array has '#sorted' set to TRUE.
-    $this->assertTrue($elements['#sorted'], t("'#sorted' => TRUE was added to the array"));
-
-    // Pass $elements through element_children() and ensure it remains
-    // sorted in the correct order. drupal_render() will return an empty string
-    // if used on the same array in the same request.
-    $children = element_children($elements);
-    $this->assertTrue(array_shift($children) == 'first', t('Child found in the correct order.'));
-    $this->assertTrue(array_shift($children) == 'second', t('Child found in the correct order.'));
-
-
-    // The same array structure again, but with #sorted set to TRUE.
-    $elements = array(
-      'second' => array(
-        '#weight' => 10,
-        '#markup' => $second,
-      ),
-      'first' => array(
-        '#weight' => 0,
-        '#markup' => $first,
-      ),
-      '#sorted' => TRUE,
-    );
-    $output = drupal_render($elements);
-
-    // The elements should appear in output in the same order as the array.
-    $this->assertTrue(strpos($output, $second) < strpos($output, $first), t('Elements were not sorted.'));
-  }
-
-  /**
-   * Test passing arguments to the theme function.
-   */
-  function testDrupalRenderThemeArguments() {
-    $element = array(
-      '#theme' => 'common_test_foo',
-    );
-    // Test that defaults work.
-    $this->assertEqual(drupal_render($element), 'foobar', 'Defaults work');
-    $element = array(
-      '#theme' => 'common_test_foo',
-      '#foo' => $this->randomName(),
-      '#bar' => $this->randomName(),
-    );
-    // Test that passing arguments to the theme function works.
-    $this->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
-  }
-}
-
-/**
- * Test for valid_url().
- */
-class ValidUrlTestCase extends DrupalUnitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Valid Url',
-      'description' => "Performs tests on Drupal's valid url function.",
-      'group' => 'System'
-    );
-  }
-
-  /**
-   * Test valid absolute urls.
-   */
-  function testValidAbsolute() {
-    $url_schemes = array('http', 'https', 'ftp');
-    $valid_absolute_urls = array(
-      'example.com',
-      'www.example.com',
-      'ex-ample.com',
-      '3xampl3.com',
-      'example.com/paren(the)sis',
-      'example.com/index.html#pagetop',
-      'example.com:8080',
-      'subdomain.example.com',
-      'example.com/index.php?q=node',
-      'example.com/index.php?q=node&param=false',
-      'user@www.example.com',
-      'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop',
-      '127.0.0.1',
-      'example.org?',
-      'john%20doe:secret:foo@example.org/',
-      'example.org/~,$\'*;',
-      'caf%C3%A9.example.org',
-      '[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
-    );
-
-    foreach ($url_schemes as $scheme) {
-      foreach ($valid_absolute_urls as $url) {
-        $test_url = $scheme . '://' . $url;
-        $valid_url = valid_url($test_url, TRUE);
-        $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url)));
-      }
-    }
-  }
-
-  /**
-   * Test invalid absolute urls.
-   */
-  function testInvalidAbsolute() {
-    $url_schemes = array('http', 'https', 'ftp');
-    $invalid_ablosule_urls = array(
-      '',
-      'ex!ample.com',
-      'ex%ample.com',
-    );
-
-    foreach ($url_schemes as $scheme) {
-      foreach ($invalid_ablosule_urls as $url) {
-        $test_url = $scheme . '://' . $url;
-        $valid_url = valid_url($test_url, TRUE);
-        $this->assertFalse($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url)));
-      }
-    }
-  }
-
-  /**
-   * Test valid relative urls.
-   */
-  function testValidRelative() {
-    $valid_relative_urls = array(
-      'paren(the)sis',
-      'index.html#pagetop',
-      'index.php?q=node',
-      'index.php?q=node&param=false',
-      'login.php?do=login&style=%23#pagetop',
-    );
-
-    foreach (array('', '/') as $front) {
-      foreach ($valid_relative_urls as $url) {
-        $test_url = $front . $url;
-        $valid_url = valid_url($test_url);
-        $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url)));
-      }
-    }
-  }
-
-  /**
-   * Test invalid relative urls.
-   */
-  function testInvalidRelative() {
-    $invalid_relative_urls = array(
-      'ex^mple',
-      'example<>',
-      'ex%ample',
-    );
-
-    foreach (array('', '/') as $front) {
-      foreach ($invalid_relative_urls as $url) {
-        $test_url = $front . $url;
-        $valid_url = valid_url($test_url);
-        $this->assertFALSE($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url)));
-      }
-    }
-  }
-}
-
-/**
- * Tests for CRUD API functions.
- */
-class DrupalDataApiTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Data API functions',
-      'description' => 'Tests the performance of CRUD APIs.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('database_test');
-  }
-
-  /**
-   * Test the drupal_write_record() API function.
-   */
-  function testDrupalWriteRecord() {
-    // Insert a record - no columns allow NULL values.
-    $person = new stdClass();
-    $person->name = 'John';
-    $person->unknown_column = 123;
-    $insert_result = drupal_write_record('test', $person);
-    $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.'));
-    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
-    $this->assertIdentical($person->age, 0, t('Age field set to default value.'));
-    $this->assertIdentical($person->job, 'Undefined', t('Job field set to default value.'));
-
-    // Verify that the record was inserted.
-    $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'John', t('Name field set.'));
-    $this->assertIdentical($result->age, '0', t('Age field set to default value.'));
-    $this->assertIdentical($result->job, 'Undefined', t('Job field set to default value.'));
-    $this->assertFalse(isset($result->unknown_column), t('Unknown column was ignored.'));
-
-    // Update the newly created record.
-    $person->name = 'Peter';
-    $person->age = 27;
-    $person->job = NULL;
-    $update_result = drupal_write_record('test', $person, array('id'));
-    $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.'));
-
-    // Verify that the record was updated.
-    $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'Peter', t('Name field set.'));
-    $this->assertIdentical($result->age, '27', t('Age field set.'));
-    $this->assertIdentical($result->job, '', t('Job field set and cast to string.'));
-
-    // Try to insert NULL in columns that does not allow this.
-    $person = new stdClass();
-    $person->name = 'Ringo';
-    $person->age = NULL;
-    $person->job = NULL;
-    $insert_result = drupal_write_record('test', $person);
-    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
-    $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'Ringo', t('Name field set.'));
-    $this->assertIdentical($result->age, '0', t('Age field set.'));
-    $this->assertIdentical($result->job, '', t('Job field set.'));
-
-    // Insert a record - the "age" column allows NULL.
-    $person = new stdClass();
-    $person->name = 'Paul';
-    $person->age = NULL;
-    $insert_result = drupal_write_record('test_null', $person);
-    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
-    $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'Paul', t('Name field set.'));
-    $this->assertIdentical($result->age, NULL, t('Age field set.'));
-
-    // Insert a record - do not specify the value of a column that allows NULL.
-    $person = new stdClass();
-    $person->name = 'Meredith';
-    $insert_result = drupal_write_record('test_null', $person);
-    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
-    $this->assertIdentical($person->age, 0, t('Age field set to default value.'));
-    $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'Meredith', t('Name field set.'));
-    $this->assertIdentical($result->age, '0', t('Age field set to default value.'));
-
-    // Update the newly created record.
-    $person->name = 'Mary';
-    $person->age = NULL;
-    $update_result = drupal_write_record('test_null', $person, array('id'));
-    $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'Mary', t('Name field set.'));
-    $this->assertIdentical($result->age, NULL, t('Age field set.'));
-
-    // Insert a record - the "data" column should be serialized.
-    $person = new stdClass();
-    $person->name = 'Dave';
-    $update_result = drupal_write_record('test_serialized', $person);
-    $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical($result->name, 'Dave', t('Name field set.'));
-    $this->assertIdentical($result->info, NULL, t('Info field set.'));
-
-    $person->info = array();
-    $update_result = drupal_write_record('test_serialized', $person, array('id'));
-    $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical(unserialize($result->info), array(), t('Info field updated.'));
-
-    // Update the serialized record.
-    $data = array('foo' => 'bar', 1 => 2, 'empty' => '', 'null' => NULL);
-    $person->info = $data;
-    $update_result = drupal_write_record('test_serialized', $person, array('id'));
-    $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
-    $this->assertIdentical(unserialize($result->info), $data, t('Info field updated.'));
-
-    // Run an update query where no field values are changed. The database
-    // layer should return zero for number of affected rows, but
-    // db_write_record() should still return SAVED_UPDATED.
-    $update_result = drupal_write_record('test_null', $person, array('id'));
-    $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a valid update is run without changing any values.'));
-
-    // Insert an object record for a table with a multi-field primary key.
-    $node_access = new stdClass();
-    $node_access->nid = mt_rand();
-    $node_access->gid = mt_rand();
-    $node_access->realm = $this->randomName();
-    $insert_result = drupal_write_record('node_access', $node_access);
-    $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.'));
-
-    // Update the record.
-    $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm'));
-    $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.'));
-  }
-
-}
-
-/**
- * Tests Simpletest error and exception collector.
- */
-class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {
-
-  /**
-   * Errors triggered during the test.
-   *
-   * Errors are intercepted by the overriden implementation
-   * of DrupalWebTestCase::error below.
-   *
-   * @var Array
-   */
-  protected $collectedErrors = array();
-
-  public static function getInfo() {
-    return array(
-      'name' => 'SimpleTest error collector',
-      'description' => 'Performs tests on the Simpletest error and exception collector.',
-      'group' => 'SimpleTest',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('system_test', 'error_test');
-  }
-
-  /**
-   * Test that simpletest collects errors from the tested site.
-   */
-  function testErrorCollect() {
-    $this->collectedErrors = array();
-    $this->drupalGet('error-test/generate-warnings-with-report');
-    $this->assertEqual(count($this->collectedErrors), 3, t('Three errors were collected'));
-
-    if (count($this->collectedErrors) == 3) {
-      $this->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas');
-      $this->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero');
-      $this->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
-    }
-    else {
-      // Give back the errors to the log report.
-      foreach ($this->collectedErrors as $error) {
-        parent::error($error['message'], $error['group'], $error['caller']);
-      }
-    }
-  }
-
-  protected function error($message = '', $group = 'Other', array $caller = NULL) {
-    // This function overiddes DrupalWebTestCase::error(). We collect an error...
-    $this->collectedErrors[] = array(
-      'message' => $message,
-      'group' => $group,
-      'caller' => $caller
-    );
-    // ... and ignore it.
-  }
-
-  /**
-   * Assert that a collected error matches what we are expecting.
-   */
-  function assertError($error, $group, $function, $file, $message = NULL) {
-    $this->assertEqual($error['group'], $group, t("Group was %group", array('%group' => $group)));
-    $this->assertEqual($error['caller']['function'], $function, t("Function was %function", array('%function' => $function)));
-    $this->assertEqual(basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file)));
-    if (isset($message)) {
-      $this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message)));
-    }
-  }
-}
-
-/**
- * Test the drupal_parse_info_file() API function.
- */
-class ParseInfoFilesTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Parsing .info files',
-      'description' => 'Tests parsing .info files.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Parse an example .info file an verify the results.
-   */
-  function testParseInfoFile() {
-    $info_values = drupal_parse_info_file(drupal_get_path('module', 'simpletest') . '/tests/common_test_info.txt');
-    $this->assertEqual($info_values['simple_string'], 'A simple string', t('Simple string value was parsed correctly.'), t('System'));
-    $this->assertEqual($info_values['simple_constant'], WATCHDOG_INFO, t('Constant value was parsed correctly.'), t('System'));
-    $this->assertEqual($info_values['double_colon'], 'dummyClassName::', t('Value containing double-colon was parsed correctly.'), t('System'));
-  }
-}
-
-/**
- * Tests for the format_date() function.
- */
-class FormatDateUnitTest extends DrupalWebTestCase {
-
-  /**
-   * Arbitrary langcode for a custom language.
-   */
-  const LANGCODE = 'xx';
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Format date',
-      'description' => 'Test the format_date() function.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('locale');
-    variable_set('configurable_timezones', 1);
-    variable_set('date_format_long', 'l, j. F Y - G:i');
-    variable_set('date_format_medium', 'j. F Y - G:i');
-    variable_set('date_format_short', 'Y M j - g:ia');
-    variable_set('locale_custom_strings_' . self::LANGCODE, array(
-      '' => array('Sunday' => 'domingo'),
-      'Long month name' => array('March' => 'marzo'),
-    ));
-    $this->refreshVariables();
-  }
-
-  /**
-   * Tests for the format_date() function.
-   */
-  function testFormatDate() {
-    global $user, $language;
-
-    $timestamp = strtotime('2007-03-26T00:00:00+00:00');
-    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test all parameters.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test translated format.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'l, 25-Mar-07 17:00:00 PDT', t('Test an escaped format string.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\domingo, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash character.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\l, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash followed by escaped format string.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.'));
-
-    // Create an admin user and add Spanish language.
-    $admin_user = $this->drupalCreateUser(array('administer languages'));
-    $this->drupalLogin($admin_user);
-    $edit = array(
-      'langcode' => self::LANGCODE,
-      'name' => self::LANGCODE,
-      'native' => self::LANGCODE,
-      'direction' => LANGUAGE_LTR,
-      'prefix' => self::LANGCODE,
-    );
-    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
-
-    // Create a test user to carry out the tests.
-    $test_user = $this->drupalCreateUser();
-    $this->drupalLogin($test_user);
-    $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles');
-    $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save'));
-
-    // Disable session saving as we are about to modify the global $user.
-    drupal_save_session(FALSE);
-    // Save the original user and language and then replace it with the test user and language.
-    $real_user = $user;
-    $user = user_load($test_user->uid, TRUE);
-    $real_language = $language->language;
-    $language->language = $user->language;
-    // Simulate a Drupal bootstrap with the logged-in user.
-    date_default_timezone_set(drupal_get_user_timezone());
-
-    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test a different language.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.'));
-    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test custom date format.'));
-    $this->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', t('Test long date format.'));
-    $this->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', t('Test medium date format.'));
-    $this->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', t('Test short date format.'));
-    $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', t('Test default date format.'));
-
-    // Restore the original user and language, and enable session saving.
-    $user = $real_user;
-    $language->language = $real_language;
-    // Restore default time zone.
-    date_default_timezone_set(drupal_get_user_timezone());
-    drupal_save_session(TRUE);
-  }
-}
-
-/**
- * Tests for the format_date() function.
- */
-class DrupalAttributesUnitTest extends DrupalUnitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'HTML Attributes',
-      'description' => 'Perform unit tests on the drupal_attributes() function.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Tests that drupal_html_class() cleans the class name properly.
-   */
-  function testDrupalAttributes() {
-    // Verify that special characters are HTML encoded.
-    $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&amp;&quot;&#039;&lt;&gt;"', t('HTML encode attribute values.'));
-
-    // Verify multi-value attributes are concatenated with spaces.
-    $attributes = array('class' => array('first', 'last'));
-    $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.'));
-
-    // Verify empty attribute values are rendered.
-    $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.'));
-    $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.'));
-
-    // Verify multiple attributes are rendered.
-    $attributes = array(
-      'id' => 'id-test',
-      'class' => array('first', 'last'),
-      'alt' => 'Alternate',
-    );
-    $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.'));
-
-    // Verify empty attributes array is rendered.
-    $this->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.'));
-  }
-}
-
-/**
- * Tests converting PHP variables to JSON strings and back.
- */
-class DrupalJSONTest extends DrupalUnitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'JSON',
-      'description' => 'Perform unit tests on the drupal_json_encode() and drupal_json_decode() functions.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Tests converting PHP variables to JSON strings and back.
-   */
-  function testJSON() {
-    // Setup a string with the full ASCII table.
-    // @todo: Add tests for non-ASCII characters and Unicode.
-    $str = '';
-    for ($i=0; $i < 128; $i++) {
-      $str .= chr($i);
-    }
-    // Characters that must be escaped.
-    $html_unsafe = array('<', '>', '&');
-    $html_unsafe_escaped = array('\u003c', '\u003e', '\u0026');
-
-    // Verify there aren't character encoding problems with the source string.
-    $this->assertIdentical(strlen($str), 128, t('A string with the full ASCII table has the correct length.'));
-    foreach ($html_unsafe as $char) {
-      $this->assertTrue(strpos($str, $char) > 0, t('A string with the full ASCII table includes @s.', array('@s' => $char)));
-    }
-
-    // Verify that JSON encoding produces a string with all of the characters.
-    $json = drupal_json_encode($str);
-    $this->assertTrue(strlen($json) > strlen($str), t('A JSON encoded string is larger than the source string.'));
-
-    // Verify that encoding/decoding is reversible.
-    $json_decoded = drupal_json_decode($json);
-    $this->assertIdentical($str, $json_decoded, t('Encoding a string to JSON and decoding back results in the original string.'));
-
-    // Verify reversibility for structured data. Also verify that necessary
-    // characters are escaped.
-    $source = array(TRUE, FALSE, 0, 1, '0', '1', $str, array('key1' => $str, 'key2' => array('nested' => TRUE)));
-    $json = drupal_json_encode($source);
-    foreach ($html_unsafe as $char) {
-      $this->assertTrue(strpos($json, $char) === FALSE, t('A JSON encoded string does not contain @s.', array('@s' => $char)));
-    }
-    // Verify that JSON encoding escapes the HTML unsafe characters
-    foreach ($html_unsafe_escaped as $char) {
-      $this->assertTrue(strpos($json, $char) > 0, t('A JSON encoded string contains @s.', array('@s' => $char)));
-    }
-    $json_decoded = drupal_json_decode($json);
-    $this->assertNotIdentical($source, $json, t('An array encoded in JSON is not identical to the source.'));
-    $this->assertIdentical($source, $json_decoded, t('Encoding structured data to JSON and decoding back results in the original data.'));
-  }
-}
-
-/**
- * Tests for RDF namespaces XML serialization.
- */
-class DrupalGetRdfNamespacesTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'RDF namespaces XML serialization tests',
-      'description' => 'Confirm that the serialization of RDF namespaces via drupal_get_rdf_namespaces() is output and parsed correctly in the XHTML document.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('rdf', 'rdf_test');
-  }
-
-  /**
-   * Test RDF namespaces.
-   */
-  function testGetRdfNamespaces() {
-    // Fetches the front page and extracts XML namespaces.
-    $this->drupalGet('');
-    $xml = new SimpleXMLElement($this->content);
-    $ns = $xml->getDocNamespaces();
-
-    $this->assertEqual($ns['owl'], 'http://www.w3.org/2002/07/owl#', t('A prefix declared once is displayed.'));
-    $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', t('The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.'));
-    $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', t('Two prefixes can be assigned the same namespace.'));
-    $this->assertTrue(!isset($ns['dc']), t('A prefix with conflicting namespaces is discarded.'));
-  }
-}
diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test
deleted file mode 100644
index faffcf5..0000000
--- a/modules/simpletest/tests/database_test.test
+++ /dev/null
@@ -1,3180 +0,0 @@
-<?php
-// $Id: database_test.test,v 1.95 2010/07/28 10:52:12 dries Exp $
-
-/**
- * Dummy class for fetching into a class.
- *
- * PDO supports using a new instance of an arbitrary class for records
- * rather than just a stdClass or array. This class is for testing that
- * functionality. (See testQueryFetchClass() below)
- */
-class FakeRecord { }
-
-/**
- * Base test class for databases.
- *
- * Because all database tests share the same test data, we can centralize that
- * here.
- */
-class DatabaseTestCase extends DrupalWebTestCase {
-
-  function setUp() {
-    parent::setUp('database_test');
-
-    $schema['test'] = drupal_get_schema('test');
-    $schema['test_people'] = drupal_get_schema('test_people');
-    $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
-    $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
-    $schema['test_task'] = drupal_get_schema('test_task');
-
-    $this->installTables($schema);
-
-    $this->addSampleData();
-  }
-
-  /**
-   * Set up several tables needed by a certain test.
-   *
-   * @param $schema
-   *   An array of table definitions to install.
-   */
-  function installTables($schema) {
-    // This ends up being a test for table drop and create, too, which is nice.
-    foreach ($schema as $name => $data) {
-      if (db_table_exists($name)) {
-        db_drop_table($name);
-      }
-      db_create_table($name, $data);
-    }
-
-    foreach ($schema as $name => $data) {
-      $this->assertTrue(db_table_exists($name), t('Table @name created successfully.', array('@name' => $name)));
-    }
-  }
-
-  /**
-   * Set up tables for NULL handling.
-   */
-  function ensureSampleDataNull() {
-    $schema['test_null'] = drupal_get_schema('test_null');
-    $this->installTables($schema);
-
-    db_insert('test_null')
-    ->fields(array('name', 'age'))
-    ->values(array(
-      'name' => 'Kermit',
-      'age' => 25,
-    ))
-    ->values(array(
-      'name' => 'Fozzie',
-      'age' => NULL,
-    ))
-    ->values(array(
-      'name' => 'Gonzo',
-      'age' => 27,
-    ))
-    ->execute();
-  }
-
-  /**
-   * Setup our sample data.
-   *
-   * These are added using db_query(), since we're not trying to test the
-   * INSERT operations here, just populate.
-   */
-  function addSampleData() {
-    // We need the IDs, so we can't use a multi-insert here.
-    $john = db_insert('test')
-      ->fields(array(
-        'name' => 'John',
-        'age' => 25,
-        'job' => 'Singer',
-      ))
-      ->execute();
-
-    $george = db_insert('test')
-      ->fields(array(
-        'name' => 'George',
-        'age' => 27,
-        'job' => 'Singer',
-      ))
-      ->execute();
-
-    $ringo = db_insert('test')
-      ->fields(array(
-        'name' => 'Ringo',
-        'age' => 28,
-        'job' => 'Drummer',
-      ))
-      ->execute();
-
-    $paul = db_insert('test')
-      ->fields(array(
-        'name' => 'Paul',
-        'age' => 26,
-        'job' => 'Songwriter',
-      ))
-      ->execute();
-
-    db_insert('test_people')
-      ->fields(array(
-        'name' => 'Meredith',
-        'age' => 30,
-        'job' => 'Speaker',
-      ))
-      ->execute();
-
-    db_insert('test_task')
-      ->fields(array('pid', 'task', 'priority'))
-      ->values(array(
-        'pid' => $john,
-        'task' => 'eat',
-        'priority' => 3,
-      ))
-      ->values(array(
-        'pid' => $john,
-        'task' => 'sleep',
-        'priority' => 4,
-      ))
-      ->values(array(
-        'pid' => $john,
-        'task' => 'code',
-        'priority' => 1,
-      ))
-      ->values(array(
-        'pid' => $george,
-        'task' => 'sing',
-        'priority' => 2,
-      ))
-      ->values(array(
-        'pid' => $george,
-        'task' => 'sleep',
-        'priority' => 2,
-      ))
-      ->values(array(
-        'pid' => $paul,
-        'task' => 'found new band',
-        'priority' => 1,
-      ))
-      ->values(array(
-        'pid' => $paul,
-        'task' => 'perform at superbowl',
-        'priority' => 3,
-      ))
-      ->execute();
-  }
-}
-
-/**
- * Test connection management.
- */
-class DatabaseConnectionTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Connection tests',
-      'description' => 'Tests of the core database system.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test that connections return appropriate connection objects.
-   */
-  function testConnectionRouting() {
-    // Clone the master credentials to a slave connection.
-    // Note this will result in two independent connection objects that happen
-    // to point to the same place.
-    $connection_info = Database::getConnectionInfo('default');
-    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
-
-    $db1 = Database::getConnection('default', 'default');
-    $db2 = Database::getConnection('slave', 'default');
-
-    $this->assertNotNull($db1, t('default connection is a real connection object.'));
-    $this->assertNotNull($db2, t('slave connection is a real connection object.'));
-    $this->assertNotIdentical($db1, $db2, t('Each target refers to a different connection.'));
-
-    // Try to open those targets another time, that should return the same objects.
-    $db1b = Database::getConnection('default', 'default');
-    $db2b = Database::getConnection('slave', 'default');
-    $this->assertIdentical($db1, $db1b, t('A second call to getConnection() returns the same object.'));
-    $this->assertIdentical($db2, $db2b, t('A second call to getConnection() returns the same object.'));
-
-    // Try to open an unknown target.
-    $unknown_target = $this->randomName();
-    $db3 = Database::getConnection($unknown_target, 'default');
-    $this->assertNotNull($db3, t('Opening an unknown target returns a real connection object.'));
-    $this->assertIdentical($db1, $db3, t('An unknown target opens the default connection.'));
-
-    // Try to open that unknown target another time, that should return the same object.
-    $db3b = Database::getConnection($unknown_target, 'default');
-    $this->assertIdentical($db3, $db3b, t('A second call to getConnection() returns the same object.'));
-  }
-
-  /**
-   * Test that connections return appropriate connection objects.
-   */
-  function testConnectionRoutingOverride() {
-    // Clone the master credentials to a slave connection.
-    // Note this will result in two independent connection objects that happen
-    // to point to the same place.
-    $connection_info = Database::getConnectionInfo('default');
-    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
-
-    Database::ignoreTarget('default', 'slave');
-
-    $db1 = Database::getConnection('default', 'default');
-    $db2 = Database::getConnection('slave', 'default');
-
-    $this->assertIdentical($db1, $db2, t('Both targets refer to the same connection.'));
-  }
-
-  /**
-   * Tests the closing of a database connection.
-   */
-  function testConnectionClosing() {
-    // Open the default target so we have an object to compare.
-    $db1 = Database::getConnection('default', 'default');
-
-    // Try to close the the default connection, then open a new one.
-    Database::closeConnection('default', 'default');
-    $db2 = Database::getConnection('default', 'default');
-
-    // Opening a connection after closing it should yield an object different than the original.
-    $this->assertNotIdentical($db1, $db2, t('Opening the default connection after it is closed returns a new object.'));
-  }
-
-  /**
-   * Tests the connection options of the active database.
-   */
-  function testConnectionOptions() {
-    $connection_info = Database::getConnectionInfo('default');
-
-    // Be sure we're connected to the default database.
-    $db = Database::getConnection('default', 'default');
-    $connectionOptions = $db->getConnectionOptions();
-
-    // In the MySQL driver, the port can be different, so check individual
-    // options.
-    $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], t('The default connection info driver matches the current connection options driver.'));
-    $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], t('The default connection info database matches the current connection options database.'));
-
-    // Set up identical slave and confirm connection options are identical.
-    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
-    $db2 = Database::getConnection('slave', 'default');
-    $connectionOptions2 = $db2->getConnectionOptions();
-
-    // Get a fresh copy of the default connection options.
-    $connectionOptions = $db->getConnectionOptions();
-    $this->assertIdentical($connectionOptions, $connectionOptions2, t('The default and slave connection options are identical.'));
-
-    // Set up a new connection with different connection info.
-    $test = $connection_info['default'];
-    $test['database'] .= 'test';
-    Database::addConnectionInfo('test', 'default', $test);
-    $connection_info = Database::getConnectionInfo('test');
-
-    // Get a fresh copy of the default connection options.
-    $connectionOptions = $db->getConnectionOptions();
-    $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], t('The test connection info database does not match the current connection options database.'));
-  }
-}
-
-/**
- * Test fetch actions, part 1.
- *
- * We get timeout errors if we try to run too many tests at once.
- */
-class DatabaseFetchTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Fetch tests',
-      'description' => 'Test the Database system\'s various fetch capabilities.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that we can fetch a record properly in default object mode.
-   */
-  function testQueryFetchDefault() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
-    $this->assertTrue($result instanceof DatabaseStatementInterface, t('Result set is a Drupal statement object.'));
-    foreach ($result as $record) {
-      $records[] = $record;
-      $this->assertTrue(is_object($record), t('Record is an object.'));
-      $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
-    }
-
-    $this->assertIdentical(count($records), 1, t('There is only one record.'));
-  }
-
-  /**
-   * Confirm that we can fetch a record to an object explicitly.
-   */
-  function testQueryFetchObject() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ));
-    foreach ($result as $record) {
-      $records[] = $record;
-      $this->assertTrue(is_object($record), t('Record is an object.'));
-      $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
-    }
-
-    $this->assertIdentical(count($records), 1, t('There is only one record.'));
-  }
-
-  /**
-   * Confirm that we can fetch a record to an array associative explicitly.
-   */
-  function testQueryFetchArray() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC));
-    foreach ($result as $record) {
-      $records[] = $record;
-      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
-        $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.'));
-      }
-    }
-
-    $this->assertIdentical(count($records), 1, t('There is only one record.'));
-  }
-
-  /**
-   * Confirm that we can fetch a record into a new instance of a custom class.
-   *
-   * @see FakeRecord
-   */
-  function testQueryFetchClass() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord'));
-    foreach ($result as $record) {
-      $records[] = $record;
-      if ($this->assertTrue($record instanceof FakeRecord, t('Record is an object of class FakeRecord.'))) {
-        $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
-      }
-    }
-
-    $this->assertIdentical(count($records), 1, t('There is only one record.'));
-  }
-}
-
-/**
- * Test fetch actions, part 2.
- *
- * We get timeout errors if we try to run too many tests at once.
- */
-class DatabaseFetch2TestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Fetch tests, part 2',
-      'description' => 'Test the Database system\'s various fetch capabilities.',
-      'group' => 'Database',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-  }
-
-  // Confirm that we can fetch a record into an indexed array explicitly.
-  function testQueryFetchNum() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM));
-    foreach ($result as $record) {
-      $records[] = $record;
-      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
-        $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.'));
-      }
-    }
-
-    $this->assertIdentical(count($records), 1, 'There is only one record');
-  }
-
-  /**
-   * Confirm that we can fetch a record into a doubly-keyed array explicitly.
-   */
-  function testQueryFetchBoth() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH));
-    foreach ($result as $record) {
-      $records[] = $record;
-      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
-        $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.'));
-        $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.'));
-      }
-    }
-
-    $this->assertIdentical(count($records), 1, t('There is only one record.'));
-  }
-
-  /**
-   * Confirm that we can fetch an entire column of a result set at once.
-   */
-  function testQueryFetchCol() {
-    $records = array();
-    $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
-    $column = $result->fetchCol();
-    $this->assertIdentical(count($column), 3, t('fetchCol() returns the right number of records.'));
-
-    $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
-    $i = 0;
-    foreach ($result as $record) {
-      $this->assertIdentical($record->name, $column[$i++], t('Column matches direct accesss.'));
-    }
-  }
-}
-
-/**
- * Test the insert builder.
- */
-class DatabaseInsertTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Insert tests',
-      'description' => 'Test the Insert query builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test the very basic insert functionality.
-   */
-  function testSimpleInsert() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-
-    $query = db_insert('test');
-    $query->fields(array(
-      'name' => 'Yoko',
-      'age' => '29',
-    ));
-    $query->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-    $this->assertIdentical($num_records_before + 1, (int) $num_records_after, t('Record inserts correctly.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
-    $this->assertIdentical($saved_age, '29', t('Can retrieve after inserting.'));
-  }
-
-  /**
-   * Test that we can insert multiple records in one query object.
-   */
-  function testMultiInsert() {
-    $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-
-    $query = db_insert('test');
-    $query->fields(array(
-      'name' => 'Larry',
-      'age' => '30',
-    ));
-
-    // We should be able to specify values in any order if named.
-    $query->values(array(
-      'age' => '31',
-      'name' => 'Curly',
-    ));
-
-    // We should be able to say "use the field order".
-    // This is not the recommended mechanism for most cases, but it should work.
-    $query->values(array('Moe', '32'));
-    $query->execute();
-
-    $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-    $this->assertIdentical($num_records_before + 3, $num_records_after, t('Record inserts correctly.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
-    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
-    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
-    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
-  }
-
-  /**
-   * Test that an insert object can be reused with new data after it executes.
-   */
-  function testRepeatedInsert() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-
-    $query = db_insert('test');
-
-    $query->fields(array(
-      'name' => 'Larry',
-      'age' => '30',
-    ));
-    $query->execute();  // This should run the insert, but leave the fields intact.
-
-    // We should be able to specify values in any order if named.
-    $query->values(array(
-      'age' => '31',
-      'name' => 'Curly',
-    ));
-    $query->execute();
-
-    // We should be able to say "use the field order".
-    $query->values(array('Moe', '32'));
-    $query->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-    $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, t('Record inserts correctly.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
-    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
-    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
-    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
-  }
-
-  /**
-   * Test that we can specify fields without values and specify values later.
-   */
-  function testInsertFieldOnlyDefinintion() {
-    // This is useful for importers, when we want to create a query and define
-    // its fields once, then loop over a multi-insert execution.
-    db_insert('test')
-      ->fields(array('name', 'age'))
-      ->values(array('Larry', '30'))
-      ->values(array('Curly', '31'))
-      ->values(array('Moe', '32'))
-      ->execute();
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
-    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
-    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
-    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
-  }
-
-  /**
-   * Test that inserts return the proper auto-increment ID.
-   */
-  function testInsertLastInsertID() {
-    $id = db_insert('test')
-      ->fields(array(
-        'name' => 'Larry',
-        'age' => '30',
-      ))
-      ->execute();
-
-    $this->assertIdentical($id, '5', t('Auto-increment ID returned successfully.'));
-  }
-
-  /**
-   * Test that the INSERT INTO ... SELECT ... syntax works.
-   */
-  function testInsertSelect() {
-    $query = db_select('test_people', 'tp');
-    // The query builder will always append expressions after fields.
-    // Add the expression first to test that the insert fields are correctly
-    // re-ordered.
-    $query->addExpression('tp.age', 'age');
-    $query
-      ->fields('tp', array('name','job'))
-      ->condition('tp.name', 'Meredith');
-
-    // The resulting query should be equivalent to:
-    // INSERT INTO test (age, name, job)
-    // SELECT tp.age AS age, tp.name AS name, tp.job AS job
-    // FROM test_people tp
-    // WHERE tp.name = 'Meredith'
-    db_insert('test')
-      ->from($query)
-      ->execute();
-
-    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
-    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
-  }
-}
-
-/**
- * Insert tests using LOB fields, which are weird on some databases.
- */
-class DatabaseInsertLOBTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Insert tests, LOB fields',
-      'description' => 'Test the Insert query builder with LOB fields.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test that we can insert a single blob field successfully.
-   */
-  function testInsertOneBlob() {
-    $data = "This is\000a test.";
-    $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.'));
-    $id = db_insert('test_one_blob')
-      ->fields(array('blob1' => $data))
-      ->execute();
-    $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
-    $this->assertTrue($r['blob1'] === $data, t('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
-  }
-
-  /**
-   * Test that we can insert multiple blob fields in the same query.
-   */
-  function testInsertMultipleBlob() {
-    $id = db_insert('test_two_blobs')
-      ->fields(array(
-        'blob1' => 'This is',
-        'blob2' => 'a test',
-      ))
-      ->execute();
-    $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
-    $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', t('Can insert multiple blobs per row.'));
-  }
-}
-
-/**
- * Insert tests for "database default" values.
- */
-class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Insert tests, default fields',
-      'description' => 'Test the Insert query builder with default values.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test that we can run a query that is "default values for everything".
-   */
-  function testDefaultInsert() {
-    $query = db_insert('test')->useDefaults(array('job'));
-    $id = $query->execute();
-
-    $schema = drupal_get_schema('test');
-
-    $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
-    $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.'));
-  }
-
-  /**
-   * Test that no action will be preformed if no fields are specified.
-   */
-  function testDefaultEmptyInsert() {
-    $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-
-    try {
-      $result = db_insert('test')->execute();
-      // This is only executed if no exception has been thrown.
-      $this->fail(t('Expected exception NoFieldsException has not been thrown.'));
-    } catch (NoFieldsException $e) {
-      $this->pass(t('Expected exception NoFieldsException has been thrown.'));
-    }
-
-    $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-    $this->assertIdentical($num_records_before, $num_records_after, t('Do nothing as no fields are specified.'));
-  }
-
-  /**
-   * Test that we can insert fields with values and defaults in the same query.
-   */
-  function testDefaultInsertWithFields() {
-    $query = db_insert('test')
-      ->fields(array('name' => 'Bob'))
-      ->useDefaults(array('job'));
-    $id = $query->execute();
-
-    $schema = drupal_get_schema('test');
-
-    $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
-    $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.'));
-  }
-}
-
-/**
- * Update builder tests.
- */
-class DatabaseUpdateTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Update tests',
-      'description' => 'Test the Update query builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that we can update a single record successfully.
-   */
-  function testSimpleUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('name' => 'Tiffany'))
-      ->condition('id', 1)
-      ->execute();
-    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
-
-    $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
-    $this->assertIdentical($saved_name, 'Tiffany', t('Updated name successfully.'));
-  }
-
-  /**
-   * Confirm that we can update a multiple records successfully.
-   */
-  function testMultiUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition('job', 'Singer')
-      ->execute();
-    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Confirm that we can update a multiple records with a non-equality condition.
-   */
-  function testMultiGTUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition('age', 26, '>')
-      ->execute();
-    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Confirm that we can update a multiple records with a where call.
-   */
-  function testWhereUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->where('age > :age', array(':age' => 26))
-      ->execute();
-    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Confirm that we can stack condition and where calls.
-   */
-  function testWhereAndConditionUpdate() {
-    $update = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->where('age > :age', array(':age' => 26))
-      ->condition('name', 'Ringo');
-    $num_updated = $update->execute();
-    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
-  }
-
-}
-
-/**
- * Tests for more complex update statements.
- */
-class DatabaseUpdateComplexTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Update tests, Complex',
-      'description' => 'Test the Update query builder, complex queries.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test updates with OR conditionals.
-   */
-  function testOrConditionUpdate() {
-    $update = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition(db_or()
-        ->condition('name', 'John')
-        ->condition('name', 'Paul')
-      );
-    $num_updated = $update->execute();
-    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Test WHERE IN clauses.
-   */
-  function testInConditionUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition('name', array('John', 'Paul'), 'IN')
-      ->execute();
-    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Test WHERE NOT IN clauses.
-   */
-  function testNotInConditionUpdate() {
-    // The o is lowercase in the 'NoT IN' operator, to make sure the operators
-    // work in mixed case.
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
-      ->execute();
-    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Test BETWEEN conditional clauses.
-   */
-  function testBetweenConditionUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition('age', array(25, 26), 'BETWEEN')
-      ->execute();
-    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Test LIKE conditionals.
-   */
-  function testLikeConditionUpdate() {
-    $num_updated = db_update('test')
-      ->fields(array('job' => 'Musician'))
-      ->condition('name', '%ge%', 'LIKE')
-      ->execute();
-    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
-  }
-
-  /**
-   * Test update with expression values.
-   */
-  function testUpdateExpression() {
-    $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
-    $GLOBALS['larry_test'] = 1;
-    $num_updated = db_update('test')
-      ->condition('name', 'Ringo')
-      ->fields(array('job' => 'Musician'))
-      ->expression('age', 'age + :age', array(':age' => 4))
-      ->execute();
-    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
-
-    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
-    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
-
-    $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
-    $this->assertEqual($person->name, 'Ringo', t('Name set correctly.'));
-    $this->assertEqual($person->age, $before_age + 4, t('Age set correctly.'));
-    $this->assertEqual($person->job, 'Musician', t('Job set correctly.'));
-    $GLOBALS['larry_test'] = 0;
-  }
-
-  /**
-   * Test update with only expression values.
-   */
-  function testUpdateOnlyExpression() {
-    $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
-    $num_updated = db_update('test')
-      ->condition('name', 'Ringo')
-      ->expression('age', 'age + :age', array(':age' => 4))
-      ->execute();
-    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
-
-    $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
-    $this->assertEqual($before_age + 4, $after_age, t('Age updated correctly'));
-  }
-}
-
-/**
- * Test update queries involving LOB values.
- */
-class DatabaseUpdateLOBTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Update tests, LOB',
-      'description' => 'Test the Update query builder with LOB fields.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that we can update a blob column.
-   */
-  function testUpdateOneBlob() {
-    $data = "This is\000a test.";
-    $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.'));
-    $id = db_insert('test_one_blob')
-      ->fields(array('blob1' => $data))
-      ->execute();
-
-    $data .= $data;
-    db_update('test_one_blob')
-      ->condition('id', $id)
-      ->fields(array('blob1' => $data))
-      ->execute();
-
-    $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
-    $this->assertTrue($r['blob1'] === $data, t('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
-  }
-
-  /**
-   * Confirm that we can update two blob columns in the same table.
-   */
-  function testUpdateMultipleBlob() {
-    $id = db_insert('test_two_blobs')
-      ->fields(array(
-        'blob1' => 'This is',
-        'blob2' => 'a test',
-      ))
-      ->execute();
-
-    db_update('test_two_blobs')
-      ->condition('id', $id)
-      ->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
-      ->execute();
-
-    $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
-    $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', t('Can update multiple blobs per row.'));
-  }
-}
-
-/**
- * Delete/Truncate tests.
- *
- * The DELETE tests are not as extensive, as all of the interesting code for
- * DELETE queries is in the conditional which is identical to the UPDATE and
- * SELECT conditional handling.
- *
- * The TRUNCATE tests are not extensive either, because the behavior of
- * TRUNCATE queries is not consistent across database engines. We only test
- * that a TRUNCATE query actually deletes all rows from the target table.
- */
-class DatabaseDeleteTruncateTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Delete/Truncate tests',
-      'description' => 'Test the Delete and Truncate query builders.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that we can use a subselect in a delete successfully.
-   */
-  function testSubselectDelete() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
-    $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();
-
-    $subquery = db_select('test', 't')
-      ->fields('t', array('id'))
-      ->condition('t.id', array($pid_to_delete), 'IN');
-    $delete = db_delete('test_task')
-      ->condition('task', 'sleep')
-      ->condition('pid', $subquery, 'IN');
-
-    $num_deleted = $delete->execute();
-    $this->assertEqual($num_deleted, 1, t("Deleted 1 record."));
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.'));
-  }
-
-  /**
-   * Confirm that we can delete a single record successfully.
-   */
-  function testSimpleDelete() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-
-    $num_deleted = db_delete('test')
-      ->condition('id', 1)
-      ->execute();
-    $this->assertIdentical($num_deleted, 1, t('Deleted 1 record.'));
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.'));
-  }
-
-  /**
-   * Confirm that we can truncate a whole table successfully.
-   */
-  function testTruncate() {
-    $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
-
-    db_truncate('test')->execute();
-
-    $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
-    $this->assertEqual(0, $num_records_after, t('Truncate really deletes everything.'));
-  }
-}
-
-/**
- * Test the MERGE query builder.
- */
-class DatabaseMergeTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Merge tests',
-      'description' => 'Test the Merge query builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that we can merge-insert a record successfully.
-   */
-  function testMergeInsert() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    $result = db_merge('test_people')
-      ->key(array('job' => 'Presenter'))
-      ->fields(array(
-        'age' => 31,
-        'name' => 'Tiffany',
-      ))
-      ->execute();
-
-    $this->assertEqual($result, MergeQuery::STATUS_INSERT, t('Insert status returned.'));
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
-    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
-    $this->assertEqual($person->age, 31, t('Age set correctly.'));
-    $this->assertEqual($person->job, 'Presenter', t('Job set correctly.'));
-  }
-
-  /**
-   * Confirm that we can merge-update a record successfully.
-   */
-  function testMergeUpdate() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    $result = db_merge('test_people')
-      ->key(array('job' => 'Speaker'))
-      ->fields(array(
-        'age' => 31,
-        'name' => 'Tiffany',
-      ))
-      ->execute();
-
-    $this->assertEqual($result, MergeQuery::STATUS_UPDATE, t('Update status returned.'));
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
-    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
-    $this->assertEqual($person->age, 31, t('Age set correctly.'));
-    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
-  }
-
-  /**
-   * Confirm that we can merge-update a record successfully, with exclusion.
-   */
-  function testMergeUpdateExcept() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    db_merge('test_people')
-      ->key(array('job' => 'Speaker'))
-      ->fields(array(
-        'age' => 31,
-        'name' => 'Tiffany',
-      ))
-      ->updateExcept('age')
-      ->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
-    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
-    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
-    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
-  }
-
-  /**
-   * Confirm that we can merge-update a record successfully, with alternate replacement.
-   */
-  function testMergeUpdateExplicit() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    db_merge('test_people')
-      ->key(array('job' => 'Speaker'))
-      ->fields(array(
-        'age' => 31,
-        'name' => 'Tiffany',
-      ))
-      ->update(array('name' => 'Joe'))
-      ->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
-    $this->assertEqual($person->name, 'Joe', t('Name set correctly.'));
-    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
-    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
-  }
-
-  /**
-   * Confirm that we can merge-update a record successfully, with expressions.
-   */
-  function testMergeUpdateExpression() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();
-
-    // This is a very contrived example, as I have no idea why you'd want to
-    // change age this way, but that's beside the point.
-    // Note that we are also double-setting age here, once as a literal and
-    // once as an expression. This test will only pass if the expression wins,
-    // which is what is supposed to happen.
-    db_merge('test_people')
-      ->key(array('job' => 'Speaker'))
-      ->fields(array(
-        'age' => 31,
-        'name' => 'Tiffany',
-      ))
-      ->expression('age', 'age + :age', array(':age' => 4))
-      ->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
-    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
-    $this->assertEqual($person->age, $age_before + 4, t('Age updated correctly.'));
-    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
-  }
-
-  /**
-   * Test that we can merge-insert without any update fields.
-   */
-  function testMergeInsertWithoutUpdate() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    db_merge('test_people')
-      ->key(array('job' => 'Presenter'))
-      ->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
-    $this->assertEqual($person->name, '', t('Name set correctly.'));
-    $this->assertEqual($person->age, 0, t('Age set correctly.'));
-    $this->assertEqual($person->job, 'Presenter', t('Job set correctly.'));
-  }
-
-  /**
-   * Confirm that we can merge-update without any update fields.
-   */
-  function testMergeUpdateWithoutUpdate() {
-    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-
-    db_merge('test_people')
-      ->key(array('job' => 'Speaker'))
-      ->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
-    $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.'));
-    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
-    $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.'));
-
-    db_merge('test_people')
-      ->key(array('job' => 'Speaker'))
-      ->fields(array('age' => 31))
-      ->updateExcept(array('age'))
-      ->execute();
-
-    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
-    $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.'));
-
-    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
-    $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.'));
-    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
-    $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.'));
-  }
-
-  /**
-   * Test that an invalid merge query throws an exception like it is supposed to.
-   */
-  function testInvalidMerge() {
-    try {
-      // This query should die because there is no key field specified.
-      db_merge('test_people')
-        ->fields(array(
-          'age' => 31,
-          'name' => 'Tiffany',
-        ))
-        ->execute();
-    }
-    catch (InvalidMergeQueryException $e) {
-      $this->pass(t('InvalidMergeQueryException thrown for invalid query.'));
-      return;
-    }
-    $this->fail(t('No InvalidMergeQueryException thrown'));
-  }
-}
-
-/**
- * Test the SELECT builder.
- */
-class DatabaseSelectTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Select tests',
-      'description' => 'Test the Select query builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test rudimentary SELECT statements.
-   */
-  function testSimpleSelect() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $result = $query->execute();
-
-    $num_records = 0;
-    foreach ($result as $record) {
-      $num_records++;
-    }
-
-    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test rudimentary SELECT statement with a COMMENT.
-   */
-  function testSimpleComment() {
-    $query = db_select('test')->comment('Testing query comments');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $result = $query->execute();
-
-    $num_records = 0;
-    foreach ($result as $record) {
-      $num_records++;
-    }
-
-    $query = (string)$query;
-    $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
-
-    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
-    $this->assertEqual($query, $expected, t('The flattened query contains the comment string.'));
-  }
-
-  /**
-   * Test basic conditionals on SELECT statements.
-   */
-  function testSimpleSelectConditional() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $query->condition('age', 27);
-    $result = $query->execute();
-
-    // Check that the aliases are being created the way we want.
-    $this->assertEqual($name_field, 'name', t('Name field alias is correct.'));
-    $this->assertEqual($age_field, 'age', t('Age field alias is correct.'));
-
-    // Ensure that we got the right record.
-    $record = $result->fetch();
-    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
-    $this->assertEqual($record->$age_field, 27, t('Fetched age is correct.'));
-  }
-
-  /**
-   * Test SELECT statements with expressions.
-   */
-  function testSimpleSelectExpression() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addExpression("age*2", 'double_age');
-    $query->condition('age', 27);
-    $result = $query->execute();
-
-    // Check that the aliases are being created the way we want.
-    $this->assertEqual($name_field, 'name', t('Name field alias is correct.'));
-    $this->assertEqual($age_field, 'double_age', t('Age field alias is correct.'));
-
-    // Ensure that we got the right record.
-    $record = $result->fetch();
-    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
-    $this->assertEqual($record->$age_field, 27*2, t('Fetched age expression is correct.'));
-  }
-
-  /**
-   * Test SELECT statements with multiple expressions.
-   */
-  function testSimpleSelectExpressionMultiple() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_double_field = $query->addExpression("age*2");
-    $age_triple_field = $query->addExpression("age*3");
-    $query->condition('age', 27);
-    $result = $query->execute();
-
-    // Check that the aliases are being created the way we want.
-    $this->assertEqual($age_double_field, 'expression', t('Double age field alias is correct.'));
-    $this->assertEqual($age_triple_field, 'expression_2', t('Triple age field alias is correct.'));
-
-    // Ensure that we got the right record.
-    $record = $result->fetch();
-    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
-    $this->assertEqual($record->$age_double_field, 27*2, t('Fetched double age expression is correct.'));
-    $this->assertEqual($record->$age_triple_field, 27*3, t('Fetched triple age expression is correct.'));
-  }
-
-  /**
-   * Test adding multiple fields to a select statement at the same time.
-   */
-  function testSimpleSelectMultipleFields() {
-    $record = db_select('test')
-      ->fields('test', array('id', 'name', 'age', 'job'))
-      ->condition('age', 27)
-      ->execute()->fetchObject();
-
-    // Check that all fields we asked for are present.
-    $this->assertNotNull($record->id, t('ID field is present.'));
-    $this->assertNotNull($record->name, t('Name field is present.'));
-    $this->assertNotNull($record->age, t('Age field is present.'));
-    $this->assertNotNull($record->job, t('Job field is present.'));
-
-    // Ensure that we got the right record.
-    // Check that all fields we asked for are present.
-    $this->assertEqual($record->id, 2, t('ID field has the correct value.'));
-    $this->assertEqual($record->name, 'George', t('Name field has the correct value.'));
-    $this->assertEqual($record->age, 27, t('Age field has the correct value.'));
-    $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.'));
-  }
-
-  /**
-   * Test adding all fields from a given table to a select statement.
-   */
-  function testSimpleSelectAllFields() {
-    $record = db_select('test')
-      ->fields('test')
-      ->condition('age', 27)
-      ->execute()->fetchObject();
-
-    // Check that all fields we asked for are present.
-    $this->assertNotNull($record->id, t('ID field is present.'));
-    $this->assertNotNull($record->name, t('Name field is present.'));
-    $this->assertNotNull($record->age, t('Age field is present.'));
-    $this->assertNotNull($record->job, t('Job field is present.'));
-
-    // Ensure that we got the right record.
-    // Check that all fields we asked for are present.
-    $this->assertEqual($record->id, 2, t('ID field has the correct value.'));
-    $this->assertEqual($record->name, 'George', t('Name field has the correct value.'));
-    $this->assertEqual($record->age, 27, t('Age field has the correct value.'));
-    $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.'));
-  }
-
-  /**
-   * Test that we can find a record with a NULL value.
-   */
-  function testNullCondition() {
-    $this->ensureSampleDataNull();
-
-    $names = db_select('test_null', 'tn')
-      ->fields('tn', array('name'))
-      ->isNull('age')
-      ->execute()->fetchCol();
-
-    $this->assertEqual(count($names), 1, t('Correct number of records found with NULL age.'));
-    $this->assertEqual($names[0], 'Fozzie', t('Correct record returned for NULL age.'));
-  }
-
-  /**
-   * Test that we can find a record without a NULL value.
-   */
-  function testNotNullCondition() {
-    $this->ensureSampleDataNull();
-
-    $names = db_select('test_null', 'tn')
-      ->fields('tn', array('name'))
-      ->isNotNull('tn.age')
-      ->orderBy('name')
-      ->execute()->fetchCol();
-
-    $this->assertEqual(count($names), 2, t('Correct number of records found withNOT NULL age.'));
-    $this->assertEqual($names[0], 'Gonzo', t('Correct record returned for NOT NULL age.'));
-    $this->assertEqual($names[1], 'Kermit', t('Correct record returned for NOT NULL age.'));
-  }
-
-  /**
-   * Test that we can UNION multiple Select queries together. This is
-   * semantically equal to UNION DISTINCT, so we don't explicity test that.
-   */
-  function testUnion() {
-    $query_1 = db_select('test', 't')
-      ->fields('t', array('name'))
-      ->condition('age', array(27, 28), 'IN');
-
-    $query_2 = db_select('test', 't')
-      ->fields('t', array('name'))
-      ->condition('age', 28);
-
-    $query_1->union($query_2);
-
-    $names = $query_1->execute()->fetchCol();
-
-    // Ensure we only get 2 records.
-    $this->assertEqual(count($names), 2, t('UNION correctly discarded duplicates.'));
-
-    $this->assertEqual($names[0], 'George', t('First query returned correct name.'));
-    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct name.'));
-  }
-
-  /**
-   * Test that we can UNION ALL multiple Select queries together.
-   */
-  function testUnionAll() {
-    $query_1 = db_select('test', 't')
-      ->fields('t', array('name'))
-      ->condition('age', array(27, 28), 'IN');
-
-    $query_2 = db_select('test', 't')
-      ->fields('t', array('name'))
-      ->condition('age', 28);
-
-    $query_1->union($query_2, 'ALL');
-
-    $names = $query_1->execute()->fetchCol();
-
-    // Ensure we get all 3 records.
-    $this->assertEqual(count($names), 3, t('UNION ALL correctly preserved duplicates.'));
-
-    $this->assertEqual($names[0], 'George', t('First query returned correct first name.'));
-    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct second name.'));
-    $this->assertEqual($names[2], 'Ringo', t('Third query returned correct name.'));
-  }
-
-  /**
-   * Test that random ordering of queries works.
-   *
-   * We take the approach of testing the Drupal layer only, rather than trying
-   * to test that the database's random number generator actually produces
-   * random queries (which is very difficult to do without an unacceptable risk
-   * of the test failing by accident).
-   *
-   * Therefore, in this test we simply run the same query twice and assert that
-   * the two results are reordered versions of each other (as well as of the
-   * same query without the random ordering). It is reasonable to assume that
-   * if we run the same select query twice and the results are in a different
-   * order each time, the only way this could happen is if we have successfully
-   * triggered the database's random ordering functionality.
-   */
-  function testRandomOrder() {
-    // Use 52 items, so the chance that this test fails by accident will be the
-    // same as the chance that a deck of cards will come out in the same order
-    // after shuffling it (in other words, nearly impossible).
-    $number_of_items = 52;
-    while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
-      db_insert('test')->fields(array('name' => $this->randomName()))->execute();
-    }
-
-    // First select the items in order and make sure we get an ordered list.
-    $expected_ids = range(1, $number_of_items);
-    $ordered_ids = db_select('test', 't')
-      ->fields('t', array('id'))
-      ->range(0, $number_of_items)
-      ->orderBy('id')
-      ->execute()
-      ->fetchCol();
-    $this->assertEqual($ordered_ids, $expected_ids, t('A query without random ordering returns IDs in the correct order.'));
-
-    // Now perform the same query, but instead choose a random ordering. We
-    // expect this to contain a differently ordered version of the original
-    // result.
-    $randomized_ids = db_select('test', 't')
-      ->fields('t', array('id'))
-      ->range(0, $number_of_items)
-      ->orderRandom()
-      ->execute()
-      ->fetchCol();
-    $this->assertNotEqual($randomized_ids, $ordered_ids, t('A query with random ordering returns an unordered set of IDs.'));
-    $sorted_ids = $randomized_ids;
-    sort($sorted_ids);
-    $this->assertEqual($sorted_ids, $ordered_ids, t('After sorting the random list, the result matches the original query.'));
-
-    // Now perform the exact same query again, and make sure the order is
-    // different.
-    $randomized_ids_second_set = db_select('test', 't')
-      ->fields('t', array('id'))
-      ->range(0, $number_of_items)
-      ->orderRandom()
-      ->execute()
-      ->fetchCol();
-    $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, t('Performing the query with random ordering a second time returns IDs in a different order.'));
-    $sorted_ids_second_set = $randomized_ids_second_set;
-    sort($sorted_ids_second_set);
-    $this->assertEqual($sorted_ids_second_set, $sorted_ids, t('After sorting the second random list, the result matches the sorted version of the first random list.'));
-  }
-
-  /**
-   * Test that aliases are renamed when duplicates.
-   */
-  function testSelectDuplicateAlias() {
-    $query = db_select('test', 't');
-    $alias1 = $query->addField('t', 'name', 'the_alias');
-    $alias2 = $query->addField('t', 'age', 'the_alias');
-    $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
-  }
-}
-
-/**
- * Test case for subselects in a dynamic SELECT query.
- */
-class DatabaseSelectSubqueryTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Select tests, subqueries',
-      'description' => 'Test the Select query builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test that we can use a subquery in a FROM clause.
-   */
-  function testFromSubquerySelect() {
-    // Create a subquery, which is just a normal query object.
-    $subquery = db_select('test_task', 'tt');
-    $subquery->addField('tt', 'pid', 'pid');
-    $subquery->addField('tt', 'task', 'task');
-    $subquery->condition('priority', 1);
-
-    // Create another query that joins against the virtual table resulting
-    // from the subquery.
-    $select = db_select($subquery, 'tt2');
-    $select->join('test', 't', 't.id=tt2.pid');
-    $select->addField('t', 'name');
-
-    $select->condition('task', 'code');
-
-    // The resulting query should be equivalent to:
-    // SELECT t.name
-    // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
-    //   INNER JOIN test t ON t.id=tt.pid
-    // WHERE tt.task = 'code'
-    $people = $select->execute()->fetchCol();
-
-    $this->assertEqual(count($people), 1, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test that we can use a subquery in a FROM clause with a limit.
-   */
-  function testFromSubquerySelectWithLimit() {
-    // Create a subquery, which is just a normal query object.
-    $subquery = db_select('test_task', 'tt');
-    $subquery->addField('tt', 'pid', 'pid');
-    $subquery->addField('tt', 'task', 'task');
-    $subquery->orderBy('priority', 'DESC');
-    $subquery->range(0, 1);
-
-    // Create another query that joins against the virtual table resulting
-    // from the subquery.
-    $select = db_select($subquery, 'tt2');
-    $select->join('test', 't', 't.id=tt2.pid');
-    $select->addField('t', 'name');
-
-    // The resulting query should be equivalent to:
-    // SELECT t.name
-    // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
-    //   INNER JOIN test t ON t.id=tt.pid
-    $people = $select->execute()->fetchCol();
-
-    $this->assertEqual(count($people), 1, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test that we can use a subquery in a WHERE clause.
-   */
-  function testConditionSubquerySelect() {
-    // Create a subquery, which is just a normal query object.
-    $subquery = db_select('test_task', 'tt');
-    $subquery->addField('tt', 'pid', 'pid');
-    $subquery->condition('tt.priority', 1);
-
-    // Create another query that joins against the virtual table resulting
-    // from the subquery.
-    $select = db_select('test_task', 'tt2');
-    $select->addField('tt2', 'task');
-    $select->condition('tt2.pid', $subquery, 'IN');
-
-    // The resulting query should be equivalent to:
-    // SELECT tt2.name
-    // FROM test tt2
-    // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
-    $people = $select->execute()->fetchCol();
-    $this->assertEqual(count($people), 5, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test that we can use a subquery in a JOIN clause.
-   */
-  function testJoinSubquerySelect() {
-    // Create a subquery, which is just a normal query object.
-    $subquery = db_select('test_task', 'tt');
-    $subquery->addField('tt', 'pid', 'pid');
-    $subquery->condition('priority', 1);
-
-    // Create another query that joins against the virtual table resulting
-    // from the subquery.
-    $select = db_select('test', 't');
-    $select->join($subquery, 'tt', 't.id=tt.pid');
-    $select->addField('t', 'name');
-
-    // The resulting query should be equivalent to:
-    // SELECT t.name
-    // FROM test t
-    //   INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
-    $people = $select->execute()->fetchCol();
-
-    $this->assertEqual(count($people), 2, t('Returned the correct number of rows.'));
-  }
-}
-
-/**
- * Test select with order by clauses.
- */
-class DatabaseSelectOrderedTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Select tests, ordered',
-      'description' => 'Test the Select query builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test basic order by.
-   */
-  function testSimpleSelectOrdered() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $query->orderBy($age_field);
-    $result = $query->execute();
-
-    $num_records = 0;
-    $last_age = 0;
-    foreach ($result as $record) {
-      $num_records++;
-      $this->assertTrue($record->age >= $last_age, t('Results returned in correct order.'));
-      $last_age = $record->age;
-    }
-
-    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test multiple order by.
-   */
-  function testSimpleSelectMultiOrdered() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $job_field = $query->addField('test', 'job');
-    $query->orderBy($job_field);
-    $query->orderBy($age_field);
-    $result = $query->execute();
-
-    $num_records = 0;
-    $expected = array(
-      array('Ringo', 28, 'Drummer'),
-      array('John', 25, 'Singer'),
-      array('George', 27, 'Singer'),
-      array('Paul', 26, 'Songwriter'),
-    );
-    $results = $result->fetchAll(PDO::FETCH_NUM);
-    foreach ($expected as $k => $record) {
-      $num_records++;
-      foreach ($record as $kk => $col) {
-        if ($expected[$k][$kk] != $results[$k][$kk]) {
-          $this->assertTrue(FALSE, t('Results returned in correct order.'));
-        }
-      }
-    }
-    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test order by descending.
-   */
-  function testSimpleSelectOrderedDesc() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $query->orderBy($age_field, 'DESC');
-    $result = $query->execute();
-
-    $num_records = 0;
-    $last_age = 100000000;
-    foreach ($result as $record) {
-      $num_records++;
-      $this->assertTrue($record->age <= $last_age, t('Results returned in correct order.'));
-      $last_age = $record->age;
-    }
-
-    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
-  }
-}
-
-/**
- * Test more complex select statements.
- */
-class DatabaseSelectComplexTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Select tests, complex',
-      'description' => 'Test the Select query builder with more complex queries.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test simple JOIN statements.
-   */
-  function testDefaultJoin() {
-    $query = db_select('test_task', 't');
-    $people_alias = $query->join('test', 'p', 't.pid = p.id');
-    $name_field = $query->addField($people_alias, 'name', 'name');
-    $task_field = $query->addField('t', 'task', 'task');
-    $priority_field = $query->addField('t', 'priority', 'priority');
-
-    $query->orderBy($priority_field);
-    $result = $query->execute();
-
-    $num_records = 0;
-    $last_priority = 0;
-    foreach ($result as $record) {
-      $num_records++;
-      $this->assertTrue($record->$priority_field >= $last_priority, t('Results returned in correct order.'));
-      $this->assertNotEqual($record->$name_field, 'Ringo', t('Taskless person not selected.'));
-      $last_priority = $record->$priority_field;
-    }
-
-    $this->assertEqual($num_records, 7, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test LEFT OUTER joins.
-   */
-  function testLeftOuterJoin() {
-    $query = db_select('test', 'p');
-    $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
-    $name_field = $query->addField('p', 'name', 'name');
-    $task_field = $query->addField($people_alias, 'task', 'task');
-    $priority_field = $query->addField($people_alias, 'priority', 'priority');
-
-    $query->orderBy($name_field);
-    $result = $query->execute();
-
-    $num_records = 0;
-    $last_name = 0;
-
-    foreach ($result as $record) {
-      $num_records++;
-      $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, t('Results returned in correct order.'));
-      $last_priority = $record->$name_field;
-    }
-
-    $this->assertEqual($num_records, 8, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test GROUP BY clauses.
-   */
-  function testGroupBy() {
-    $query = db_select('test_task', 't');
-    $count_field = $query->addExpression('COUNT(task)', 'num');
-    $task_field = $query->addField('t', 'task');
-    $query->orderBy($count_field);
-    $query->groupBy($task_field);
-    $result = $query->execute();
-
-    $num_records = 0;
-    $last_count = 0;
-    $records = array();
-    foreach ($result as $record) {
-      $num_records++;
-      $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
-      $last_count = $record->$count_field;
-      $records[$record->$task_field] = $record->$count_field;
-    }
-
-    $correct_results = array(
-      'eat' => 1,
-      'sleep' => 2,
-      'code' => 1,
-      'found new band' => 1,
-      'perform at superbowl' => 1,
-    );
-
-    foreach ($correct_results as $task => $count) {
-      $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
-    }
-
-    $this->assertEqual($num_records, 6, t('Returned the correct number of total rows.'));
-  }
-
-  /**
-   * Test GROUP BY and HAVING clauses together.
-   */
-  function testGroupByAndHaving() {
-    $query = db_select('test_task', 't');
-    $count_field = $query->addExpression('COUNT(task)', 'num');
-    $task_field = $query->addField('t', 'task');
-    $query->orderBy($count_field);
-    $query->groupBy($task_field);
-    $query->having('COUNT(task) >= 2');
-    $result = $query->execute();
-
-    $num_records = 0;
-    $last_count = 0;
-    $records = array();
-    foreach ($result as $record) {
-      $num_records++;
-      $this->assertTrue($record->$count_field >= 2, t('Record has the minimum count.'));
-      $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
-      $last_count = $record->$count_field;
-      $records[$record->$task_field] = $record->$count_field;
-    }
-
-    $correct_results = array(
-      'sleep' => 2,
-    );
-
-    foreach ($correct_results as $task => $count) {
-      $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
-    }
-
-    $this->assertEqual($num_records, 1, t('Returned the correct number of total rows.'));
-  }
-
-  /**
-   * Test range queries. The SQL clause varies with the database.
-   */
-  function testRange() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $query->range(0, 2);
-    $result = $query->execute();
-
-    $num_records = 0;
-    foreach ($result as $record) {
-      $num_records++;
-    }
-
-    $this->assertEqual($num_records, 2, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test distinct queries.
-   */
-  function testDistinct() {
-    $query = db_select('test_task');
-    $task_field = $query->addField('test_task', 'task');
-    $query->distinct();
-    $result = $query->execute();
-
-    $num_records = 0;
-    foreach ($result as $record) {
-      $num_records++;
-    }
-
-    $this->assertEqual($num_records, 6, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test that we can generate a count query from a built query.
-   */
-  function testCountQuery() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $query->orderBy('name');
-
-    $count = $query->countQuery()->execute()->fetchField();
-
-    $this->assertEqual($count, 4, t('Counted the correct number of records.'));
-
-    // Now make sure we didn't break the original query!  We should still have
-    // all of the fields we asked for.
-    $record = $query->execute()->fetch();
-    $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.'));
-    $this->assertEqual($record->$age_field, 27, t('Correct data retrieved.'));
-  }
-
-  /**
-   * Test that countQuery properly removes 'all_fields' statements and
-   * ordering clauses.
-   */
-  function testCountQueryRemovals() {
-    $query = db_select('test');
-    $query->fields('test');
-    $query->orderBy('name');
-    $count = $query->countQuery();
-
-    // Check that the 'all_fields' statement is handled properly.
-    $tables = $query->getTables();
-    $this->assertEqual($tables['test']['all_fields'], 1, t('Query correctly sets \'all_fields\' statement.'));
-    $tables = $count->getTables();
-    $this->assertFalse(isset($tables['test']['all_fields']), t('Count query correctly unsets \'all_fields\' statement.'));
-
-    // Check that the ordering clause is handled properly.
-    $orderby = $query->getOrderBy();
-    $this->assertEqual($orderby['name'], 'ASC', t('Query correctly sets ordering clause.'));
-    $orderby = $count->getOrderBy();
-    $this->assertFalse(isset($orderby['name']), t('Count query correctly unsets ordering caluse.'));
-
-    // Make sure that the count query works.
-    $count = $count->execute()->fetchField();
-
-    $this->assertEqual($count, 4, t('Counted the correct number of records.'));
-  }
-
-  /**
-   * Test that we can generate a count query from a query with distinct.
-   */
-  function testCountQueryDistinct() {
-    $query = db_select('test_task');
-    $task_field = $query->addField('test_task', 'task');
-    $query->distinct();
-
-    $count = $query->countQuery()->execute()->fetchField();
-
-    $this->assertEqual($count, 6, t('Counted the correct number of records.'));
-  }
-
-  /**
-   * Confirm that we can properly nest conditional clauses.
-   */
-  function testNestedConditions() {
-    // This query should translate to:
-    // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
-    // That should find only one record. Yes it's a non-optimal way of writing
-    // that query but that's not the point!
-    $query = db_select('test');
-    $query->addField('test', 'job');
-    $query->condition('name', 'Paul');
-    $query->condition(db_or()->condition('age', 26)->condition('age', 27));
-
-    $job = $query->execute()->fetchField();
-    $this->assertEqual($job, 'Songwriter', t('Correct data retrieved.'));
-  }
-
-  /**
-   * Confirm we can join on a single table twice with a dynamic alias.
-   */
-  function testJoinTwice() {
-    $query = db_select('test')->fields('test');
-    $alias = $query->join('test', 'test', 'test.job = %alias.job');
-    $query->addField($alias, 'name', 'othername');
-    $query->addField($alias, 'job', 'otherjob');
-    $query->where("$alias.name <> test.name");
-    $crowded_job = $query->execute()->fetch();
-    $this->assertEqual($crowded_job->job, $crowded_job->otherjob, t('Correctly joined same table twice.'));
-    $this->assertNotEqual($crowded_job->name, $crowded_job->othername, t('Correctly joined same table twice.'));
-  }
-}
-
-class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Pager query tests',
-      'description' => 'Test the pager query extender.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that a pager query returns the correct results.
-   *
-   * Note that we have to make an HTTP request to a test page handler
-   * because the pager depends on GET parameters.
-   */
-  function testEvenPagerQuery() {
-    // To keep the test from being too brittle, we determine up front
-    // what the page count should be dynamically, and pass the control
-    // information forward to the actual query on the other side of the
-    // HTTP request.
-    $limit = 2;
-    $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
-
-    $correct_number = $limit;
-    $num_pages = floor($count / $limit);
-
-    // If there is no remainder from rounding, subtract 1 since we index from 0.
-    if (!($num_pages * $limit < $count)) {
-      $num_pages--;
-    }
-
-    for ($page = 0; $page <= $num_pages; ++$page) {
-      $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
-      $data = json_decode($this->drupalGetContent());
-
-      if ($page == $num_pages) {
-        $correct_number = $count - ($limit * $page);
-      }
-
-      $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
-    }
-  }
-
-  /**
-   * Confirm that a pager query returns the correct results.
-   *
-   * Note that we have to make an HTTP request to a test page handler
-   * because the pager depends on GET parameters.
-   */
-  function testOddPagerQuery() {
-    // To keep the test from being too brittle, we determine up front
-    // what the page count should be dynamically, and pass the control
-    // information forward to the actual query on the other side of the
-    // HTTP request.
-    $limit = 2;
-    $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
-
-    $correct_number = $limit;
-    $num_pages = floor($count / $limit);
-
-    // If there is no remainder from rounding, subtract 1 since we index from 0.
-    if (!($num_pages * $limit < $count)) {
-      $num_pages--;
-    }
-
-    for ($page = 0; $page <= $num_pages; ++$page) {
-      $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
-      $data = json_decode($this->drupalGetContent());
-
-      if ($page == $num_pages) {
-        $correct_number = $count - ($limit * $page);
-      }
-
-      $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
-    }
-  }
-
-  /**
-   * Confirm that a pager query with inner pager query returns valid results.
-   *
-   * This is a regression test for #467984.
-   */
-  function testInnerPagerQuery() {
-    $query = db_select('test', 't')->extend('PagerDefault');
-    $query
-      ->fields('t', array('age'))
-      ->orderBy('age')
-      ->limit(5);
-
-    $outer_query = db_select($query);
-    $outer_query->addField('subquery', 'age');
-
-    $ages = $outer_query
-      ->execute()
-      ->fetchCol();
-    $this->assertEqual($ages, array(25, 26, 27, 28), t('Inner pager query returned the correct ages.'));
-  }
-
-  /**
-   * Confirm that a paging query with a having expression returns valid results.
-   *
-   * This is a regression test for #467984.
-   */
-  function testHavingPagerQuery() {
-    $query = db_select('test', 't')->extend('PagerDefault');
-    $query
-      ->fields('t', array('name'))
-      ->orderBy('name')
-      ->groupBy('name')
-      ->having('MAX(age) > :count', array(':count' => 26))
-      ->limit(5);
-
-    $ages = $query
-      ->execute()
-      ->fetchCol();
-    $this->assertEqual($ages, array('George', 'Ringo'), t('Pager query with having expression returned the correct ages.'));
-  }
-}
-
-
-class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Tablesort query tests',
-      'description' => 'Test the tablesort query extender.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that a tablesort query returns the correct results.
-   *
-   * Note that we have to make an HTTP request to a test page handler
-   * because the pager depends on GET parameters.
-   */
-  function testTableSortQuery() {
-    $sorts = array(
-      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
-      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
-      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
-      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
-      // more elements here
-
-    );
-
-    foreach ($sorts as $sort) {
-      $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
-      $data = json_decode($this->drupalGetContent());
-
-      $first = array_shift($data->tasks);
-      $last = array_pop($data->tasks);
-
-      $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order.'));
-      $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order.'));
-    }
-  }
-
-  /**
-   * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first.
-   *
-   */
-  function testTableSortQueryFirst() {
-    $sorts = array(
-      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
-      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
-      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
-      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
-      // more elements here
-
-    );
-
-    foreach ($sorts as $sort) {
-      $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
-      $data = json_decode($this->drupalGetContent());
-
-      $first = array_shift($data->tasks);
-      $last = array_pop($data->tasks);
-
-      $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
-      $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
-    }
-  }
-}
-
-/**
- * Select tagging tests.
- *
- * Tags are a way to flag queries for alter hooks so they know
- * what type of query it is, such as "node_access".
- */
-class DatabaseTaggingTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Query tagging tests',
-      'description' => 'Test the tagging capabilities of the Select builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Confirm that a query has a "tag" added to it.
-   */
-  function testHasTag() {
-    $query = db_select('test');
-    $query->addField('test', 'name');
-    $query->addField('test', 'age', 'age');
-
-    $query->addTag('test');
-
-    $this->assertTrue($query->hasTag('test'), t('hasTag() returned true.'));
-    $this->assertFalse($query->hasTag('other'), t('hasTag() returned false.'));
-  }
-
-  /**
-   * Test query tagging "has all of these tags" functionality.
-   */
-  function testHasAllTags() {
-    $query = db_select('test');
-    $query->addField('test', 'name');
-    $query->addField('test', 'age', 'age');
-
-    $query->addTag('test');
-    $query->addTag('other');
-
-    $this->assertTrue($query->hasAllTags('test', 'other'), t('hasAllTags() returned true.'));
-    $this->assertFalse($query->hasAllTags('test', 'stuff'), t('hasAllTags() returned false.'));
-  }
-
-  /**
-   * Test query tagging "has at least one of these tags" functionality.
-   */
-  function testHasAnyTag() {
-    $query = db_select('test');
-    $query->addField('test', 'name');
-    $query->addField('test', 'age', 'age');
-
-    $query->addTag('test');
-
-    $this->assertTrue($query->hasAnyTag('test', 'other'), t('hasAnyTag() returned true.'));
-    $this->assertFalse($query->hasAnyTag('other', 'stuff'), t('hasAnyTag() returned false.'));
-  }
-
-  /**
-   * Test that we can attach meta data to a query object.
-   *
-   * This is how we pass additional context to alter hooks.
-   */
-  function testMetaData() {
-    $query = db_select('test');
-    $query->addField('test', 'name');
-    $query->addField('test', 'age', 'age');
-
-    $data = array(
-      'a' => 'A',
-      'b' => 'B',
-    );
-
-    $query->addMetaData('test', $data);
-
-    $return = $query->getMetaData('test');
-    $this->assertEqual($data, $return, t('Corect metadata returned.'));
-
-    $return = $query->getMetaData('nothere');
-    $this->assertNull($return, t('Non-existent key returned NULL.'));
-  }
-}
-
-/**
- * Select alter tests.
- *
- * @see database_test_query_alter()
- */
-class DatabaseAlterTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Query altering tests',
-      'description' => 'Test the hook_query_alter capabilities of the Select builder.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test that we can do basic alters.
-   */
-  function testSimpleAlter() {
-    $query = db_select('test');
-    $query->addField('test', 'name');
-    $query->addField('test', 'age', 'age');
-    $query->addTag('database_test_alter_add_range');
-
-    $result = $query->execute();
-
-    $num_records = 0;
-    foreach ($result as $record) {
-      $num_records++;
-    }
-
-    $this->assertEqual($num_records, 2, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test that we can alter the joins on a query.
-   */
-  function testAlterWithJoin() {
-    $query = db_select('test_task');
-    $tid_field = $query->addField('test_task', 'tid');
-    $task_field = $query->addField('test_task', 'task');
-    $query->orderBy($task_field);
-    $query->addTag('database_test_alter_add_join');
-
-    $result = $query->execute();
-
-    $records = $result->fetchAll();
-
-    $this->assertEqual(count($records), 2, t('Returned the correct number of rows.'));
-
-    $this->assertEqual($records[0]->name, 'George', t('Correct data retrieved.'));
-    $this->assertEqual($records[0]->$tid_field, 4, t('Correct data retrieved.'));
-    $this->assertEqual($records[0]->$task_field, 'sing', t('Correct data retrieved.'));
-    $this->assertEqual($records[1]->name, 'George', t('Correct data retrieved.'));
-    $this->assertEqual($records[1]->$tid_field, 5, t('Correct data retrieved.'));
-    $this->assertEqual($records[1]->$task_field, 'sleep', t('Correct data retrieved.'));
-  }
-
-  /**
-   * Test that we can alter a query's conditionals.
-   */
-  function testAlterChangeConditional() {
-    $query = db_select('test_task');
-    $tid_field = $query->addField('test_task', 'tid');
-    $pid_field = $query->addField('test_task', 'pid');
-    $task_field = $query->addField('test_task', 'task');
-    $people_alias = $query->join('test', 'people', "test_task.pid = people.id");
-    $name_field = $query->addField($people_alias, 'name', 'name');
-    $query->condition('test_task.tid', '1');
-    $query->orderBy($tid_field);
-    $query->addTag('database_test_alter_change_conditional');
-
-    $result = $query->execute();
-
-    $records = $result->fetchAll();
-
-    $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
-    $this->assertEqual($records[0]->$name_field, 'John', t('Correct data retrieved.'));
-    $this->assertEqual($records[0]->$tid_field, 2, t('Correct data retrieved.'));
-    $this->assertEqual($records[0]->$pid_field, 1, t('Correct data retrieved.'));
-    $this->assertEqual($records[0]->$task_field, 'sleep', t('Correct data retrieved.'));
-  }
-
-  /**
-   * Test that we can alter the fields of a query.
-   */
-  function testAlterChangeFields() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addField('test', 'age', 'age');
-    $query->orderBy('name');
-    $query->addTag('database_test_alter_change_fields');
-
-    $record = $query->execute()->fetch();
-    $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.'));
-    $this->assertFalse(isset($record->$age_field), t('Age field not found, as intended.'));
-  }
-
-  /**
-   * Test that we can alter expressions in the query.
-   */
-  function testAlterExpression() {
-    $query = db_select('test');
-    $name_field = $query->addField('test', 'name');
-    $age_field = $query->addExpression("age*2", 'double_age');
-    $query->condition('age', 27);
-    $query->addTag('database_test_alter_change_expressions');
-    $result = $query->execute();
-
-    // Ensure that we got the right record.
-    $record = $result->fetch();
-
-    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
-    $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.'));
-  }
-
-  /**
-   * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter().
-   */
-  function testAlterRemoveRange() {
-    $query = db_select('test');
-    $query->addField('test', 'name');
-    $query->addField('test', 'age', 'age');
-    $query->range(0, 2);
-    $query->addTag('database_test_alter_remove_range');
-
-    $num_records = count($query->execute()->fetchAll());
-
-    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
-  }
-
-  /**
-   * Test that we can do basic alters on subqueries.
-   */
-  function testSimpleAlterSubquery() {
-    // Create a sub-query with an alter tag.
-    $subquery = db_select('test', 'p');
-    $subquery->addField('p', 'name');
-    $subquery->addField('p', 'id');
-    // Pick out George.
-    $subquery->condition('age', 27);
-    $subquery->addExpression("age*2", 'double_age');
-    // This query alter should change it to age * 3.
-    $subquery->addTag('database_test_alter_change_expressions');
-
-    // Create a main query and join to sub-query.
-    $query = db_select('test_task', 'tt');
-    $query->join($subquery, 'pq', 'pq.id = tt.pid');
-    $age_field = $query->addField('pq', 'double_age');
-    $name_field = $query->addField('pq', 'name');
-
-    $record = $query->execute()->fetch();
-    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
-    $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.'));
-  }
-}
-
-/**
- * Regression tests.
- */
-class DatabaseRegressionTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Regression tests',
-      'description' => 'Regression tests cases for the database layer.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Regression test for #310447.
-   *
-   * Tries to insert non-ascii UTF-8 data in a database column and checks
-   * if its stored properly.
-   */
-  function testRegression_310447() {
-    // That's a 255 character UTF-8 string.
-    $name = str_repeat("é", 255);
-    db_insert('test')
-      ->fields(array(
-        'name' => $name,
-        'age' => 20,
-        'job' => 'Dancer',
-      ))->execute();
-
-    $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
-    $this->assertIdentical($name, $from_database, t("The database handles UTF-8 characters cleanly."));
-  }
-
-  /**
-   * Test the db_table_exists() function.
-   */
-  function testDBTableExists() {
-    $this->assertIdentical(TRUE, db_table_exists('node'), t('Returns true for existent table.'));
-    $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), t('Returns false for nonexistent table.'));
-  }
-
-  /**
-   * Test the db_field_exists() function.
-   */
-  function testDBFieldExists() {
-    $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), t('Returns true for existent column.'));
-    $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), t('Returns false for nonexistent column.'));
-  }
-
-  /**
-   * Test the db_index_exists() function.
-   */
-  function testDBIndexExists() {
-    $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), t('Returns true for existent index.'));
-    $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), t('Returns false for nonexistent index.'));
-  }
-}
-
-/**
- * Query logging tests.
- */
-class DatabaseLoggingTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Query logging',
-      'description' => 'Test the query logging facility.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Test that we can log the existence of a query.
-   */
-  function testEnableLogging() {
-    Database::startLog('testing');
-
-    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
-    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
-
-    $queries = Database::getLog('testing', 'default');
-
-    $this->assertEqual(count($queries), 2, t('Correct number of queries recorded.'));
-
-    foreach ($queries as $query) {
-      $this->assertEqual($query['caller']['function'], __FUNCTION__, t('Correct function in query log.'));
-    }
-  }
-
-  /**
-   * Test that we can run two logs in parallel.
-   */
-  function testEnableMultiLogging() {
-    Database::startLog('testing1');
-
-    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
-
-    Database::startLog('testing2');
-
-    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
-
-    $queries1 = Database::getLog('testing1');
-    $queries2 = Database::getLog('testing2');
-
-    $this->assertEqual(count($queries1), 2, t('Correct number of queries recorded for log 1.'));
-    $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for log 2.'));
-  }
-
-  /**
-   * Test that we can log queries against multiple targets on the same connection.
-   */
-  function testEnableTargetLogging() {
-    // Clone the master credentials to a slave connection and to another fake
-    // connection.
-    $connection_info = Database::getConnectionInfo('default');
-    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
-
-    Database::startLog('testing1');
-
-    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
-
-    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol();
-
-    $queries1 = Database::getLog('testing1');
-
-    $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.'));
-    $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.'));
-    $this->assertEqual($queries1[1]['target'], 'slave', t('Second query used slave target.'));
-  }
-
-  /**
-   * Test that logs to separate targets collapse to the same connection properly.
-   *
-   * This test is identical to the one above, except that it doesn't create
-   * a fake target so the query should fall back to running on the default
-   * target.
-   */
-  function testEnableTargetLoggingNoTarget() {
-    Database::startLog('testing1');
-
-    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
-
-    // We use "fake" here as a target because any non-existent target will do.
-    // However, because all of the tests in this class share a single page
-    // request there is likely to be a target of "slave" from one of the other
-    // unit tests, so we use a target here that we know with absolute certainty
-    // does not exist.
-    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();
-
-    $queries1 = Database::getLog('testing1');
-
-    $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.'));
-    $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.'));
-    $this->assertEqual($queries1[1]['target'], 'default', t('Second query used default target as fallback.'));
-  }
-
-  /**
-   * Test that we can log queries separately on different connections.
-   */
-  function testEnableMultiConnectionLogging() {
-    // Clone the master credentials to a fake connection.
-    // That both connections point to the same physical database is irrelevant.
-    $connection_info = Database::getConnectionInfo('default');
-    Database::addConnectionInfo('test2', 'default', $connection_info['default']);
-
-    Database::startLog('testing1');
-    Database::startLog('testing1', 'test2');
-
-    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
-
-    $old_key = db_set_active('test2');
-
-    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol();
-
-    db_set_active($old_key);
-
-    $queries1 = Database::getLog('testing1');
-    $queries2 = Database::getLog('testing1', 'test2');
-
-    $this->assertEqual(count($queries1), 1, t('Correct number of queries recorded for first connection.'));
-    $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for second connection.'));
-  }
-}
-
-/**
- * Range query tests.
- */
-class DatabaseRangeQueryTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Range query test',
-      'description' => 'Test the Range query functionality.',
-      'group' => 'Database',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('database_test');
-  }
-
-  /**
-   * Confirm that range query work and return correct result.
-   */
-  function testRangeQuery() {
-    // Test if return correct number of rows.
-    $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll();
-    $this->assertEqual(count($range_rows), 3, t('Range query work and return correct number of rows.'));
-
-    // Test if return target data.
-    $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll();
-    $raw_rows = array_slice($raw_rows, 2, 3);
-    $this->assertEqual($range_rows, $raw_rows, t('Range query work and return target data.'));
-  }
-}
-
-/**
- * Temporary query tests.
- */
-class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Temporary query test',
-      'description' => 'Test the temporary query functionality.',
-      'group' => 'Database',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('database_test');
-  }
-
-  /**
-   * Return the number of rows of a table.
-   */
-  function countTableRows($table_name) {
-    return db_select($table_name)->countQuery()->execute()->fetchField();
-  }
-
-  /**
-   * Confirm that temporary tables work and are limited to one request.
-   */
-  function testTemporaryQuery() {
-    $this->drupalGet('database_test/db_query_temporary');
-    $data = json_decode($this->drupalGetContent());
-    if ($data) {
-      $this->assertEqual($this->countTableRows("system"), $data->row_count, t('The temporary table contains the correct amount of rows.'));
-      $this->assertFalse(db_table_exists($data->table_name), t('The temporary table is, indeed, temporary.'));
-    }
-    else {
-      $this->fail(t("The creation of the temporary table failed."));
-    }
-
-    // Now try to run two db_query_temporary() in the same request.
-    $table_name_system = db_query_temporary('SELECT status FROM {system}', array());
-    $table_name_users = db_query_temporary('SELECT uid FROM {users}', array());
-
-    $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), t('A temporary table was created successfully in this request.'));
-    $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), t('A second temporary table was created successfully in this request.'));
-  }
-}
-
-/**
- * Test how the current database driver interprets the SQL syntax.
- *
- * In order to ensure consistent SQL handling throughout Drupal
- * across multiple kinds of database systems, we test that the
- * database system interprets SQL syntax in an expected fashion.
- */
-class DatabaseBasicSyntaxTestCase extends DatabaseTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Basic SQL syntax tests',
-      'description' => 'Test SQL syntax interpretation.',
-      'group' => 'Database',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('database_test');
-  }
-
-  /**
-   * Test for string concatenation.
-   */
-  function testBasicConcat() {
-    $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
-      ':a1' => 'This',
-      ':a2' => ' ',
-      ':a3' => 'is',
-      ':a4' => ' a ',
-      ':a5' => 'test.',
-    ));
-    $this->assertIdentical($result->fetchField(), 'This is a test.', t('Basic CONCAT works.'));
-  }
-
-  /**
-   * Test for string concatenation with field values.
-   */
-  function testFieldConcat() {
-    $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
-      ':a1' => 'The age of ',
-      ':a2' => ' is ',
-      ':a3' => '.',
-      ':age' => 25,
-    ));
-    $this->assertIdentical($result->fetchField(), 'The age of John is 25.', t('Field CONCAT works.'));
-  }
-
-  /**
-   * Test escaping of LIKE wildcards.
-   */
-  function testLikeEscape() {
-    db_insert('test')
-      ->fields(array(
-        'name' => 'Ring_',
-      ))
-      ->execute();
-
-    // Match both "Ringo" and "Ring_".
-    $num_matches = db_select('test', 't')
-      ->condition('name', 'Ring_', 'LIKE')
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Found 2 records.'));
-    // Match only "Ring_" using a LIKE expression with no wildcards.
-    $num_matches = db_select('test', 't')
-      ->condition('name', db_like('Ring_'), 'LIKE')
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertIdentical($num_matches, '1', t('Found 1 record.'));
-  }
-
-  /**
-   * Test LIKE query containing a backslash.
-   */
-  function testLikeBackslash() {
-    db_insert('test')
-      ->fields(array('name'))
-      ->values(array(
-        'name' => 'abcde\f',
-      ))
-      ->values(array(
-        'name' => 'abc%\_',
-      ))
-      ->execute();
-
-    // Match both rows using a LIKE expression with two wildcards and a verbatim
-    // backslash.
-    $num_matches = db_select('test', 't')
-      ->condition('name', 'abc%\\\\_', 'LIKE')
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertIdentical($num_matches, '2', t('Found 2 records.'));
-    // Match only the former using a LIKE expression with no wildcards.
-    $num_matches = db_select('test', 't')
-      ->condition('name', db_like('abc%\_'), 'LIKE')
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertIdentical($num_matches, '1', t('Found 1 record.'));
-  }
-}
-
-/**
- * Test invalid data handling.
- */
-class DatabaseInvalidDataTestCase extends DatabaseTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Invalid data',
-      'description' => 'Test handling of some invalid data.',
-      'group' => 'Database',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('database_test');
-  }
-
-  /**
-   * Traditional SQL database systems abort inserts when invalid data is encountered.
-   */
-  function testInsertDuplicateData() {
-    // Try to insert multiple records where at least one has bad data.
-    try {
-      db_insert('test')
-        ->fields(array('name', 'age', 'job'))
-        ->values(array(
-          'name' => 'Elvis',
-          'age' => 63,
-          'job' => 'Singer',
-        ))->values(array(
-          'name' => 'John', // <-- Duplicate value on unique field.
-          'age' => 17,
-          'job' => 'Consultant',
-        ))
-        ->values(array(
-          'name' => 'Frank',
-          'age' => 75,
-          'job' => 'Singer',
-        ))
-        ->execute();
-      $this->fail(t('Insert succeedded when it should not have.'));
-    }
-    catch (Exception $e) {
-      // Check if the first record was inserted.
-      $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
-
-      if ($name == 'Elvis') {
-        if (!Database::getConnection()->supportsTransactions()) {
-          // This is an expected fail.
-          // Database engines that don't support transactions can leave partial
-          // inserts in place when an error occurs. This is the case for MySQL
-          // when running on a MyISAM table.
-          $this->pass(t("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions"));
-        }
-        else {
-          $this->fail(t('The whole transaction is rolled back when a duplicate key insert occurs.'));
-        }
-      }
-      else {
-        $this->pass(t('The whole transaction is rolled back when a duplicate key insert occurs.'));
-      }
-
-      // Ensure the other values were not inserted.
-      $record = db_select('test')
-        ->fields('test', array('name', 'age'))
-        ->condition('age', array(17, 75), 'IN')
-        ->execute()->fetchObject();
-
-      $this->assertFalse($record, t('The rest of the insert aborted as expected.'));
-    }
-  }
-
-}
-
-/**
- * Drupal-specific SQL syntax tests.
- */
-class DatabaseQueryTestCase extends DatabaseTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom query syntax tests',
-      'description' => 'Test Drupal\'s extended prepared statement syntax..',
-      'group' => 'Database',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('database_test');
-  }
-
-  /**
-   * Test that we can specify an array of values in the query by simply passing in an array.
-   */
-  function testArraySubstitution() {
-    $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll();
-
-    $this->assertEqual(count($names), 3, t('Correct number of names returned'));
-  }
-}
-
-/**
- * Test transaction support, particularly nesting.
- *
- * We test nesting by having two transaction layers, an outer and inner. The
- * outer layer encapsulates the inner layer. Our transaction nesting abstraction
- * should allow the outer layer function to call any function it wants,
- * especially the inner layer that starts its own transaction, and be
- * confident that, when the function it calls returns, its own transaction
- * is still "alive."
- *
- * Call structure:
- *   transactionOuterLayer()
- *     Start transaction
- *     transactionInnerLayer()
- *       Start transaction (does nothing in database)
- *       [Maybe decide to roll back]
- *     Do more stuff
- *     Should still be in transaction A
- *
- */
-class DatabaseTransactionTestCase extends DatabaseTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Transaction tests',
-      'description' => 'Test the transaction abstraction system.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   * Helper method for transaction unit test. This "outer layer" transaction
-   * starts and then encapsulates the "inner layer" transaction. This nesting
-   * is used to evaluate whether the the database transaction API properly
-   * supports nesting. By "properly supports," we mean the outer transaction
-   * continues to exist regardless of what functions are called and whether
-   * those functions start their own transactions.
-   *
-   * In contrast, a typical database would commit the outer transaction, start
-   * a new transaction for the inner layer, commit the inner layer transaction,
-   * and then be confused when the outer layer transaction tries to commit its
-   * transaction (which was already committed when the inner transaction
-   * started).
-   *
-   * @param $suffix
-   *   Suffix to add to field values to differentiate tests.
-   * @param $rollback
-   *   Whether or not to try rolling back the transaction when we're done.
-   */
-  protected function transactionOuterLayer($suffix, $rollback = FALSE) {
-    $connection = Database::getConnection();
-    $depth = $connection->transactionDepth();
-    $txn = db_transaction();
-
-    // Insert a single row into the testing table.
-    db_insert('test')
-      ->fields(array(
-        'name' => 'David' . $suffix,
-        'age' => '24',
-      ))
-      ->execute();
-
-    $this->assertTrue($connection->inTransaction(), t('In transaction before calling nested transaction.'));
-
-    // We're already in a transaction, but we call ->transactionInnerLayer
-    // to nest another transaction inside the current one.
-    $this->transactionInnerLayer($suffix, $rollback);
-
-    $this->assertTrue($connection->inTransaction(), t('In transaction after calling nested transaction.'));
-
-    if ($rollback) {
-      // Roll back the transaction, if requested.
-      // This rollback should propagate to the last savepoint.
-      $txn->rollback();
-      $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().'));
-    }
-  }
-
-  /**
-   * Helper method for transaction unit tests. This "inner layer" transaction
-   * is either used alone or nested inside of the "outer layer" transaction.
-   *
-   * @param $suffix
-   *   Suffix to add to field values to differentiate tests.
-   * @param $rollback
-   *   Whether or not to try rolling back the transaction when we're done.
-   */
-  protected function transactionInnerLayer($suffix, $rollback = FALSE) {
-    $connection = Database::getConnection();
-
-    $this->assertTrue($connection->inTransaction(), t('In transaction in nested transaction.'));
-
-    $depth = $connection->transactionDepth();
-    // Start a transaction. If we're being called from ->transactionOuterLayer,
-    // then we're already in a transaction. Normally, that would make starting
-    // a transaction here dangerous, but the database API handles this problem
-    // for us by tracking the nesting and avoiding the danger.
-    $txn = db_transaction();
-
-    $depth2 = $connection->transactionDepth();
-    $this->assertTrue($depth < $depth2, t('Transaction depth is has increased with new transaction.'));
-
-    // Insert a single row into the testing table.
-    db_insert('test')
-      ->fields(array(
-        'name' => 'Daniel' . $suffix,
-        'age' => '19',
-      ))
-      ->execute();
-
-    $this->assertTrue($connection->inTransaction(), t('In transaction inside nested transaction.'));
-
-    if ($rollback) {
-      // Roll back the transaction, if requested.
-      // This rollback should propagate to the last savepoint.
-      $txn->rollback();
-      $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().'));
-    }
-  }
-
-  /**
-   * Test transaction rollback on a database that supports transactions.
-   *
-   * If the active connection does not support transactions, this test does nothing.
-   */
-  function testTransactionRollBackSupported() {
-    // This test won't work right if transactions are not supported.
-    if (!Database::getConnection()->supportsTransactions()) {
-      return;
-    }
-    try {
-      // Create two nested transactions. Roll back from the inner one.
-      $this->transactionOuterLayer('B', TRUE);
-
-      // Neither of the rows we inserted in the two transaction layers
-      // should be present in the tables post-rollback.
-      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
-      $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidB row after commit.'));
-      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
-      $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielB row after commit.'));
-    }
-    catch (Exception $e) {
-      $this->fail($e->getMessage());
-    }
-  }
-
-  /**
-   * Test transaction rollback on a database that does not support transactions.
-   *
-   * If the active driver supports transactions, this test does nothing.
-   */
-  function testTransactionRollBackNotSupported() {
-    // This test won't work right if transactions are supported.
-    if (Database::getConnection()->supportsTransactions()) {
-      return;
-    }
-    try {
-      // Create two nested transactions. Attempt to roll back from the inner one.
-      $this->transactionOuterLayer('B', TRUE);
-
-      // Because our current database claims to not support transactions,
-      // the inserted rows should be present despite the attempt to roll back.
-      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
-      $this->assertIdentical($saved_age, '24', t('DavidB not rolled back, since transactions are not supported.'));
-      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
-      $this->assertIdentical($saved_age, '19', t('DanielB not rolled back, since transactions are not supported.'));
-    }
-    catch (Exception $e) {
-      $this->fail($e->getMessage());
-    }
-  }
-
-  /**
-   * Test committed transaction.
-   *
-   * The behavior of this test should be identical for connections that support
-   * transactions and those that do not.
-   */
-  function testCommittedTransaction() {
-    try {
-      // Create two nested transactions. The changes should be committed.
-      $this->transactionOuterLayer('A');
-
-      // Because we committed, both of the inserted rows should be present.
-      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
-      $this->assertIdentical($saved_age, '24', t('Can retrieve DavidA row after commit.'));
-      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
-      $this->assertIdentical($saved_age, '19', t('Can retrieve DanielA row after commit.'));
-    }
-    catch (Exception $e) {
-      $this->fail($e->getMessage());
-    }
-  }
-}
-
-
-/**
- * Check the sequences API.
- */
-class DatabaseNextIdCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => t('Sequences API'),
-      'description' => t('Test the secondary sequences API.'),
-      'group' => t('Database'),
-    );
-  }
-
-  /**
-   * Test that the sequences API work.
-   */
-  function testDbNextId() {
-    $first = db_next_id();
-    $second = db_next_id();
-    // We can test for exact increase in here because we know there is no
-    // other process operating on these tables -- normally we could only
-    // expect $second > $first.
-    $this->assertEqual($first + 1, $second, t('The second call from a sequence provides a number increased by one.'));
-    $result = db_next_id(1000);
-    $this->assertEqual($result, 1001, t('Sequence provides a larger number than the existing ID.'));
-  }
-}
-
-/**
- * Tests the empty pseudo-statement class.
- */
-class DatabaseEmptyStatementTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => t('Empty statement'),
-      'description' => t('Test the empty pseudo-statement class.'),
-      'group' => t('Database'),
-    );
-  }
-
-  /**
-   * Test that the empty result set behaves as empty.
-   */
-  function testEmpty() {
-    $result = new DatabaseStatementEmpty();
-
-    $this->assertTrue($result instanceof DatabaseStatementInterface, t('Class implements expected interface'));
-    $this->assertNull($result->fetchObject(), t('Null result returned.'));
-  }
-
-  /**
-   * Test that the empty result set iterates safely.
-   */
-  function testEmptyIteration() {
-    $result = new DatabaseStatementEmpty();
-
-    foreach ($result as $record) {
-      $this->fail(t('Iterating empty result set should not iterate.'));
-      return;
-    }
-
-    $this->pass(t('Iterating empty result set skipped iteration.'));
-  }
-
-  /**
-   * Test that the empty result set mass-fetches in an expected way.
-   */
-  function testEmptyFetchAll() {
-    $result = new DatabaseStatementEmpty();
-
-    $this->assertEqual($result->fetchAll(), array(), t('Empty array returned from empty result set.'));
-  }
-}
diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test
deleted file mode 100644
index 39a176f..0000000
--- a/modules/simpletest/tests/entity_query.test
+++ /dev/null
@@ -1,930 +0,0 @@
-<?php
-
-// $Id: entity_query.test,v 1.5 2010/06/26 02:16:23 dries Exp $
-
-/**
- * @file
- * Unit test file for the entity API.
- */
-
-/**
- * Tests EntityFieldQuery.
- */
-class EntityFieldQueryTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Entity query',
-      'description' => 'Test the EntityFieldQuery class.',
-      'group' => 'Entity API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp(array('field_test'));
-
-    field_attach_create_bundle('bundle1', 'test_entity_bundle_key');
-    field_attach_create_bundle('bundle2', 'test_entity_bundle_key');
-    field_attach_create_bundle('test_bundle', 'test_entity');
-    field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle');
-
-    $instances = array();
-    $this->fields = array();
-    $this->field_names[0] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 4);
-    $field = field_create_field($field);
-    $this->fields[0] = $field;
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => '',
-      'bundle' => '',
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      'settings' => array(
-        'test_instance_setting' => $this->randomName(),
-      ),
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'label' => 'Test Field',
-        'settings' => array(
-          'test_widget_setting' => $this->randomName(),
-        )
-      )
-    );
-
-    $instances[0] = $instance;
-
-    // Add an instance to that bundle.
-    $instances[0]['bundle'] = 'bundle1';
-    $instances[0]['entity_type'] = 'test_entity_bundle_key';
-    field_create_instance($instances[0]);
-    $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle';
-    field_create_instance($instances[0]);
-
-    $this->field_names[1] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
-    $field = field_create_field($field);
-    $this->fields[1] = $field;
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => '',
-      'bundle' => '',
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      'settings' => array(
-        'test_instance_setting' => $this->randomName(),
-      ),
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'label' => 'Test Field',
-        'settings' => array(
-          'test_widget_setting' => $this->randomName(),
-        )
-      )
-    );
-
-    $instances[1] = $instance;
-
-    // Add an instance to that bundle.
-    $instances[1]['bundle'] = 'bundle1';
-    $instances[1]['entity_type'] = 'test_entity_bundle_key';
-    field_create_instance($instances[1]);
-    $instances[1]['bundle'] = $instances[1]['entity_type'] = 'test_entity_bundle';
-    field_create_instance($instances[1]);
-
-    $this->instances = $instances;
-    // Write entity base table if there is one.
-    $entities = array();
-
-    // Create entities which have a 'bundle key' defined.
-    for ($i = 1; $i < 7; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2';
-
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
-      drupal_write_record('test_entity_bundle_key', $entity);
-      field_attach_insert('test_entity_bundle_key', $entity);
-    }
-
-    $entity = new stdClass();
-    $entity->ftid = 5;
-    $entity->fttype = 'bundle2';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['shape'] = 'square';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['color'] = 'red';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['shape'] = 'circle';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['color'] = 'blue';
-    drupal_write_record('test_entity_bundle', $entity);
-    field_attach_insert('test_entity_bundle', $entity);
-
-    $instances[2] = $instance;
-    $instances[2]['bundle'] = 'test_bundle';
-    $instances[2]['field_name'] = $this->field_names[0];
-    $instances[2]['entity_type'] = 'test_entity';
-    field_create_instance($instances[2]);
-
-    // Create entities with support for revisions.
-    for ($i = 1; $i < 5; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->ftvid = $i;
-      $entity->fttype = 'test_bundle';
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
-
-      drupal_write_record('test_entity', $entity);
-      field_attach_insert('test_entity', $entity);
-      drupal_write_record('test_entity_revision', $entity);
-    }
-
-    // Add two revisions to an entity.
-    for ($i = 100; $i < 102; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = 4;
-      $entity->ftvid = $i;
-      $entity->fttype = 'test_bundle';
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
-
-      drupal_write_record('test_entity', $entity, 'ftid');
-      drupal_write_record('test_entity_revision', $entity);
-
-      db_update('test_entity')
-       ->fields(array('ftvid' => $entity->ftvid))
-       ->condition('ftid', $entity->ftid)
-       ->execute();
-
-      field_attach_update('test_entity', $entity);
-    }
-  }
-
-  /**
-   * Tests EntityFieldQuery.
-   */
-  function testEntityFieldQuery() {
-    // Test entity_type condition.
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test entity entity_type condition.'));
-
-    // Test entity_id condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('entity_id', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-    ), t('Test entity entity_id condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-    ), t('Test entity entity_id condition and entity_id property condition.'));
-
-    // Test bundle condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('bundle', 'bundle1');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test entity bundle condition: bundle1.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('bundle', 'bundle2');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test entity bundle condition: bundle2.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('fttype', 'bundle2');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test entity bundle condition and bundle property condition.'));
-
-    // Test revision_id condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->entityCondition('revision_id', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 3),
-    ), t('Test entity revision_id condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->propertyCondition('ftvid', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 3),
-    ), t('Test entity revision_id condition and revision_id property condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->fieldCondition($this->fields[0], 'value', 100, '>=')
-      ->age(FIELD_LOAD_REVISION);
-    $this->assertEntityFieldQuery($query, array(
-        array('test_entity', 100),
-        array('test_entity', 101),
-    ), t('Test revision age.'));
-
-    // Test that fields attached to the non-revision supporting entity
-    // 'test_entity_bundle_key' are reachable in FIELD_LOAD_REVISION.
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[0], 'value', 100, '<')
-      ->age(FIELD_LOAD_REVISION);
-    $this->assertEntityFieldQuery($query, array(
-        array('test_entity_bundle_key', 1),
-        array('test_entity_bundle_key', 2),
-        array('test_entity_bundle_key', 3),
-        array('test_entity_bundle_key', 4),
-        array('test_entity', 1),
-        array('test_entity', 2),
-        array('test_entity', 3),
-        array('test_entity', 4),
-    ), t('Test that fields are reachable from FIELD_LOAD_REVISION even for non-revision entities.'));
-
-    // Test entity sort by entity_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('entity_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity entity_id in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('entity_id', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity entity_id in descending order.'), TRUE);
-
-    // Test property sort by entity id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity entity_id property in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity entity_id property in descending order.'), TRUE);
-
-    // Test entity sort by bundle.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('bundle', 'ASC')
-      ->propertyOrderBy('ftid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity bundle in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('bundle', 'DESC')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity bundle in descending order.'), TRUE);
-
-    // Test entity sort by revision_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->entityOrderBy('revision_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test sort entity revision_id in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->entityOrderBy('revision_id', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 4),
-      array('test_entity', 3),
-      array('test_entity', 2),
-      array('test_entity', 1),
-    ), t('Test sort entity revision_id in descending order.'), TRUE);
-
-    // Test property sort by revision_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->propertyOrderBy('ftvid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test sort entity revision_id property in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->propertyOrderBy('ftvid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 4),
-      array('test_entity', 3),
-      array('test_entity', 2),
-      array('test_entity', 1),
-    ), t('Test sort entity revision_id property in descending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test sort field in ascending order without field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort field in descending order without field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->fieldOrderBy($this->fields[0], 'value', 'asc');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test sort field in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->fieldOrderBy($this->fields[0], 'value', 'desc');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort field in descending order.'), TRUE);
-
-    // Test "in" operation with entity entity_type condition and entity_id
-    // property condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(1, 3, 4), 'IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test "in" operation with entity entity_type condition and entity_id property condition.'));
-
-    // Test "in" operation with entity entity_type condition and entity_id
-    // property condition. Sort in descending order by entity_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(1, 3, 4), 'IN')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 1),
-    ), t('Test "in" operation with entity entity_type condition and entity_id property condition. Sort entity_id in descending order.'), TRUE);
-
-    // Test query count
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 6, t('Test query count on entity condition.'));
-
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', '1')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 1, t('Test query count on entity and property condition.'));
-
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', '4', '>')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 2, t('Test query count on entity and property condition with operator.'));
-
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 3, '=')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 1, t('Test query count on field condition.'));
-
-    // First, test without options.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 1, 'CONTAINS');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-    ), t('Test the "contains" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, 'CONTAINS');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 3),
-    ), t('Test the "contains" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 1, '=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-    ), t('Test the "equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, '=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 3),
-    ), t('Test the "equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 3, '!=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "not equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, '!=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 4),
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 4),
-    ), t('Test the "not equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 2, '<');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-    ), t('Test the "less than" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '<');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity', 1),
-    ), t('Test the "less than" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 2, '<=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test the "less than or equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '<=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity', 1),
-      array('test_entity', 2),
-    ), t('Test the "less than or equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 4, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "greater than" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test the "greater than" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 4, '>=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "greater than or equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, '>=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test the "greater than or equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(3, 4), 'NOT IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "not in" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(3, 4, 100, 101), 'NOT IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity', 1),
-      array('test_entity', 2),
-    ), t('Test the "not in" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(3, 4), 'IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test the "in" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(2, 3), 'IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 2),
-      array('test_entity', 3),
-    ), t('Test the "in" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(1, 3), 'BETWEEN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-    ), t('Test the "between" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(1, 3), 'BETWEEN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-    ), t('Test the "between" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('fttype', 'bun', 'STARTS_WITH');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "starts_with" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test the "starts_with" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 3),
-    ), t('Test omission of an operator with a single item.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(2, 3));
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 2),
-      array('test_entity', 3),
-    ), t('Test omission of an operator with multiple items.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 1, '>')
-      ->fieldCondition($this->fields[0], 'value', 4, '<');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-    ), t('Test entity, property and field conditions.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('bundle', 'bundle', 'STARTS_WITH')
-      ->propertyCondition('ftid', 4)
-      ->fieldCondition($this->fields[0], 'value', 4);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-    ), t('Test entity condition with "starts_with" operation, and property and field conditions.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'asc')
-      ->range(0, 2);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test limit on a property.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>=')
-      ->fieldOrderBy($this->fields[0], 'value', 'asc')
-      ->range(0, 2);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test limit on a field.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'asc')
-      ->range(4, 6);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test offset on a property.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->fieldOrderBy($this->fields[0], 'value', 'asc')
-      ->range(2, 4);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test offset on a field.'), TRUE);
-
-    for ($i = 6; $i < 10; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i - 5;
-      drupal_write_record('test_entity_bundle', $entity);
-      field_attach_insert('test_entity_bundle', $entity);
-    }
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity', 3),
-      array('test_entity', 4),
-      array('test_entity_bundle', 8),
-      array('test_entity_bundle', 9),
-    ), t('Select a field across multiple entities.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[1], 'shape', 'square')
-      ->fieldCondition($this->fields[1], 'color', 'blue');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test without a delta group.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[1], 'shape', 'square', '=', 'group')
-      ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'group');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a delta group.'));
-
-    // Test query on a deleted field.
-    field_attach_delete_bundle('test_entity_bundle_key', 'bundle1');
-    field_attach_delete_bundle('test_entity', 'test_bundle');
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 8),
-    ), t('Test query on a field after deleting field from some entities.'));
-
-    field_attach_delete_bundle('test_entity_bundle', 'test_entity_bundle');
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertEntityFieldQuery($query, array(), t('Test query on a field after deleting field from all entities.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[0], 'value', '3')
-      ->deleted(TRUE);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle', 8),
-      array('test_entity', 3),
-    ), t('Test query on a deleted field with deleted option set to TRUE.'));
-
-    $pass = FALSE;
-    $query = new EntityFieldQuery();
-    try {
-      $query->execute();
-    }
-    catch (EntityFieldQueryException $exception) {
-      $pass = ($exception->getMessage() == t('For this query an entity type must be specified.'));
-    }
-    $this->assertTrue($pass, t("Can't query the universe."));
-  }
-
-  /**
-   * Tests the routing feature of EntityFieldQuery.
-   */
-  function testEntityFieldQueryRouting() {
-    // Entity-only query.
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    $this->assertIdentical($query->queryCallback(), array($query, 'propertyQuery'), t('Entity-only queries are handled by the propertyQuery handler.'));
-
-    // Field-only query.
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Pure field queries are handled by the Field storage handler.'));
-
-    // Mixed entity and field query.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Mixed queries are handled by the Field storage handler.'));
-
-    // Overriding with $query->executeCallback.
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    $query->executeCallback = 'field_test_dummy_field_storage_query';
-    $this->assertEntityFieldQuery($query, array(
-      array('user', 1),
-    ), t('executeCallback can override the query handler.'));
-
-    // Overriding with $query->executeCallback via hook_entity_query_alter().
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    // Add a flag that will be caught by field_test_entity_query_alter().
-    $query->alterMyExecuteCallbackPlease = TRUE;
-    $this->assertEntityFieldQuery($query, array(
-      array('user', 1),
-    ), t('executeCallback can override the query handler when set in a hook_entity_query_alter().'));
-
-    // Mixed-storage queries.
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[0], 'value', '3')
-      ->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
-    // Alter the storage of the field.
-    $query->fields[1]['storage']['module'] = 'dummy_storage';
-    try {
-      $query->queryCallback();
-    }
-    catch (EntityFieldQueryException $exception) {
-      $pass = ($exception->getMessage() == t("Can't handle more than one field storage engine"));
-    }
-    $this->assertTrue($pass, t('Cannot query across field storage engines.'));
-  }
-
-  /**
-   * Fetches the results of an EntityFieldQuery and compares.
-   *
-   * @param $query
-   *   An EntityFieldQuery to run.
-   * @param $intended_results
-   *   A list of results, every entry is again a list, first being the entity
-   *   type, the second being the entity_id.
-   * @param $message
-   *   The message to be displayed as the result of this test.
-   * @param $ordered
-   *   If FALSE then the result of EntityFieldQuery will match
-   *   $intended_results even if the order is not the same. If TRUE then order
-   *   should match too.
-   */
-  function assertEntityFieldQuery($query, $intended_results, $message, $ordered = FALSE) {
-    $results = array();
-    foreach ($query->execute() as $entity_type => $entity_ids) {
-      foreach ($entity_ids as $entity_id => $stub_entity) {
-        $results[] = array($entity_type, $entity_id);
-      }
-    }
-    if (!isset($ordered) || !$ordered) {
-      sort($results);
-      sort($intended_results);
-    }
-    $this->assertEqual($results, $intended_results, $message);
-  }
-}
diff --git a/modules/simpletest/tests/error.test b/modules/simpletest/tests/error.test
deleted file mode 100644
index b0d1991..0000000
--- a/modules/simpletest/tests/error.test
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-// $Id: error.test,v 1.6 2009/08/17 19:14:41 webchick Exp $
-
-/**
- * Tests Drupal error and exception handlers.
- */
-class DrupalErrorHandlerUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal error handlers',
-      'description' => 'Performs tests on the Drupal error and exception handler.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('error_test');
-  }
-
-  /**
-   * Test the error handler.
-   */
-  function testErrorHandler() {
-    $error_notice = array(
-      '%type' => 'Notice',
-      '%message' => 'Undefined variable: bananas',
-      '%function' => 'error_test_generate_warnings()',
-      '%line' => 44,
-      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
-    );
-    $error_warning = array(
-      '%type' => 'Warning',
-      '%message' => 'Division by zero',
-      '%function' => 'error_test_generate_warnings()',
-      '%line' => 46,
-      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
-    );
-    $error_user_notice = array(
-      '%type' => 'User warning',
-      '%message' => 'Drupal is awesome',
-      '%function' => 'error_test_generate_warnings()',
-      '%line' => 48,
-      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
-    );
-
-    // Set error reporting to collect notices.
-    variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL);
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, t('Received expected HTTP status code.'));
-    $this->assertErrorMessage($error_notice);
-    $this->assertErrorMessage($error_warning);
-    $this->assertErrorMessage($error_user_notice);
-
-    // Set error reporting to not collect notices.
-    variable_set('error_level', ERROR_REPORTING_DISPLAY_SOME);
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, t('Received expected HTTP status code.'));
-    $this->assertNoErrorMessage($error_notice);
-    $this->assertErrorMessage($error_warning);
-    $this->assertErrorMessage($error_user_notice);
-
-    // Set error reporting to not show any errors.
-    variable_set('error_level', ERROR_REPORTING_HIDE);
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, t('Received expected HTTP status code.'));
-    $this->assertNoErrorMessage($error_notice);
-    $this->assertNoErrorMessage($error_warning);
-    $this->assertNoErrorMessage($error_user_notice);
-  }
-
-  /**
-   * Test the exception handler.
-   */
-  function testExceptionHandler() {
-    $error_exception = array(
-      '%type' => 'Exception',
-      '%message' => 'Drupal is awesome',
-      '%function' => 'error_test_trigger_exception()',
-      '%line' => 57,
-      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
-    );
-    $error_pdo_exception = array(
-      '%type' => 'PDOException',
-      '%message' => 'SELECT * FROM bananas_are_awesome',
-      '%function' => 'error_test_trigger_pdo_exception()',
-      '%line' => 65,
-      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
-    );
-
-    $this->drupalGet('error-test/trigger-exception');
-    $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), t('Received expected HTTP status line.'));
-    $this->assertErrorMessage($error_exception);
-
-    $this->drupalGet('error-test/trigger-pdo-exception');
-    $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), t('Received expected HTTP status line.'));
-    // We cannot use assertErrorMessage() since the extact error reported
-    // varies from database to database. Check that the SQL string is displayed.
-    $this->assertText($error_pdo_exception['%type'], t('Found %type in error page.', $error_pdo_exception));
-    $this->assertText($error_pdo_exception['%message'], t('Found %message in error page.', $error_pdo_exception));
-    $error_details = t('in %function (line %line of %file)', $error_pdo_exception);
-    $this->assertRaw($error_details, t("Found '!message' in error page.", array('!message' => $error_details)));
-  }
-
-  /**
-   * Helper function: assert that the error message is found.
-   */
-  function assertErrorMessage(array $error) {
-    $message = t('%type: %message in %function (line %line of %file).', $error);
-    $this->assertRaw($message, t('Error !message found.', array('!message' => $message)));
-  }
-
-  /**
-   * Helper function: assert that the error message is not found.
-   */
-  function assertNoErrorMessage(array $error) {
-    $message = t('%type: %message in %function (line %line of %file).', $error);
-    $this->assertNoRaw($message, t('Error !message not found.', array('!message' => $message)));
-  }
-}
-
diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test
deleted file mode 100644
index e1648b1..0000000
--- a/modules/simpletest/tests/file.test
+++ /dev/null
@@ -1,2464 +0,0 @@
-<?php
-// $Id: file.test,v 1.59 2010/07/26 03:04:29 dries Exp $
-
-/**
- *  @file
- *  This provides SimpleTests for the core file handling functionality.
- *  These include FileValidateTest and FileSaveTest.
- */
-
-/**
- * Helper validator that returns the $errors parameter.
- */
-function file_test_validator($file, $errors) {
-  return $errors;
-}
-
-/**
- * Helper function for testing file_scan_directory().
- *
- * Each time the function is called the file is stored in a static variable.
- * When the function is called with no $filepath parameter, the results are
- * returned.
- *
- * @param $filepath
- *   File path
- * @return
- *   If $filepath is NULL, an array of all previous $filepath parameters
- */
-function file_test_file_scan_callback($filepath = NULL) {
-  $files = &drupal_static(__FUNCTION__, array());
-  if (isset($filepath)) {
-    $files[] = $filepath;
-  }
-  else {
-    return $files;
-  }
-}
-
-/**
- * Reset static variables used by file_test_file_scan_callback().
- */
-function file_test_file_scan_callback_reset() {
-  drupal_static_reset('file_test_file_scan_callback');
-}
-
-/**
- * Base class for file tests that adds some additional file specific
- * assertions and helper functions.
- */
-class FileTestCase extends DrupalWebTestCase {
-  /**
-   * Check that two files have the same values for all fields other than the
-   * timestamp.
-   *
-   * @param $before
-   *   File object to compare.
-   * @param $after
-   *   File object to compare.
-   */
-  function assertFileUnchanged($before, $after) {
-    $this->assertEqual($before->fid, $after->fid, t('File id is the same: %file1 == %file2.', array('%file1' => $before->fid, '%file2' => $after->fid)), 'File unchanged');
-    $this->assertEqual($before->uid, $after->uid, t('File owner is the same: %file1 == %file2.', array('%file1' => $before->uid, '%file2' => $after->uid)), 'File unchanged');
-    $this->assertEqual($before->filename, $after->filename, t('File name is the same: %file1 == %file2.', array('%file1' => $before->filename, '%file2' => $after->filename)), 'File unchanged');
-    $this->assertEqual($before->uri, $after->uri, t('File path is the same: %file1 == %file2.', array('%file1' => $before->uri, '%file2' => $after->uri)), 'File unchanged');
-    $this->assertEqual($before->filemime, $after->filemime, t('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->filemime, '%file2' => $after->filemime)), 'File unchanged');
-    $this->assertEqual($before->filesize, $after->filesize, t('File size is the same: %file1 == %file2.', array('%file1' => $before->filesize, '%file2' => $after->filesize)), 'File unchanged');
-    $this->assertEqual($before->status, $after->status, t('File status is the same: %file1 == %file2.', array('%file1' => $before->status, '%file2' => $after->status)), 'File unchanged');
-  }
-
-  /**
-   * Check that two files are not the same by comparing the fid and filepath.
-   *
-   * @param $file1
-   *   File object to compare.
-   * @param $file2
-   *   File object to compare.
-   */
-  function assertDifferentFile($file1, $file2) {
-    $this->assertNotEqual($file1->fid, $file2->fid, t('Files have different ids: %file1 != %file2.', array('%file1' => $file1->fid, '%file2' => $file2->fid)), 'Different file');
-    $this->assertNotEqual($file1->uri, $file2->uri, t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Different file');
-  }
-
-  /**
-   * Check that two files are the same by comparing the fid and filepath.
-   *
-   * @param $file1
-   *   File object to compare.
-   * @param $file2
-   *   File object to compare.
-   */
-  function assertSameFile($file1, $file2) {
-    $this->assertEqual($file1->fid, $file2->fid, t('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->fid, '%file2-fid' => $file2->fid)), 'Same file');
-    $this->assertEqual($file1->uri, $file2->uri, t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Same file');
-  }
-
-  /**
-   * Helper function to test the permissions of a file.
-   *
-   * @param $filepath
-   *   String file path.
-   * @param $expected_mode
-   *   Octal integer like 0664 or 0777.
-   * @param $message
-   *   Optional message.
-   */
-  function assertFilePermissions($filepath, $expected_mode, $message = NULL) {
-    // Clear out PHP's file stat cache to be sure we see the current value.
-    clearstatcache();
-
-    // Mask out all but the last three octets.
-    $actual_mode = fileperms($filepath) & 511;
-    if (!isset($message)) {
-      $message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
-    }
-    $this->assertEqual($actual_mode, $expected_mode, $message);
-  }
-
-  /**
-   * Helper function to test the permissions of a directory.
-   *
-   * @param $directory
-   *   String directory path.
-   * @param $expected_mode
-   *   Octal integer like 0664 or 0777.
-   * @param $message
-   *   Optional message.
-   */
-  function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) {
-    // Clear out PHP's file stat cache to be sure we see the current value.
-    clearstatcache();
-
-    // Mask out all but the last three octets.
-    $actual_mode = fileperms($directory) & 511;
-    if (!isset($message)) {
-      $message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
-    }
-    $this->assertEqual($actual_mode, $expected_mode, $message);
-  }
-
-  /**
-   * Create a directory and assert it exists.
-   *
-   * @param $path
-   *   Optional string with a directory path. If none is provided, a random
-   *   name in the site's files directory will be used.
-   * @return
-   *   The path to the directory.
-   */
-  function createDirectory($path = NULL) {
-    // A directory to operate on.
-    if (is_null($path)) {
-      $path = file_directory_path() . '/' . $this->randomName();
-    }
-    $this->assertTrue(drupal_mkdir($path) && is_dir($path), t('Directory was created successfully.'));
-    return $path;
-  }
-
-  /**
-   * Create a file and save it to the files table and assert that it occurs
-   * correctly.
-   *
-   * @param $filepath
-   *   Optional string specifying the file path. If none is provided then a
-   *   randomly named file will be created in the site's files directory.
-   * @param $contents
-   *   Optional contents to save into the file. If a NULL value is provided an
-   *   arbitrary string will be used.
-   * @param $scheme
-   *   Optional string indicating the stream scheme to use. Drupal core includes
-   *   public, private, and temporary. The public wrapper is the default.
-   * @return
-   *   File object.
-   */
-  function createFile($filepath = NULL, $contents = NULL, $scheme = 'public') {
-    if (is_null($filepath)) {
-      $filepath = $this->randomName();
-    }
-    $filepath = $scheme . '://' . $filepath;
-
-    if (is_null($contents)) {
-      $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";
-    }
-
-    file_put_contents($filepath, $contents);
-    $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file');
-
-    $file = new stdClass();
-    $file->uri = $filepath;
-    $file->filename = basename($file->uri);
-    $file->filemime = 'text/plain';
-    $file->uid = 1;
-    $file->timestamp = REQUEST_TIME;
-    $file->filesize = filesize($file->uri);
-    $file->status = 0;
-    // Write the record directly rather than calling file_save() so we don't
-    // invoke the hooks.
-    $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, t('The file was added to the database.'), 'Create test file');
-
-    return $file;
-  }
-}
-
-/**
- * Base class for file tests that use the file_test module to test uploads and
- * hooks.
- */
-class FileHookTestCase extends FileTestCase {
-  function setUp() {
-    // Install file_test module
-    parent::setUp('file_test');
-    // Clear out any hook calls.
-    file_test_reset();
-  }
-
-  /**
-   * Assert that all of the specified hook_file_* hooks were called once, other
-   * values result in failure.
-   *
-   * @param $expected
-   *   Array with string containing with the hook name, e.g. 'load', 'save',
-   *   'insert', etc.
-   */
-  function assertFileHooksCalled($expected) {
-    // Determine which hooks were called.
-    $actual = array_keys(array_filter(file_test_get_all_calls()));
-
-    // Determine if there were any expected that were not called.
-    $uncalled = array_diff($expected, $actual);
-    if (count($uncalled)) {
-      $this->assertTrue(FALSE, t('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled))));
-    }
-    else {
-      $this->assertTrue(TRUE, t('All the expected hooks were called: %expected', array('%expected' => implode(', ', $expected))));
-    }
-
-    // Determine if there were any unexpected calls.
-    $unexpected = array_diff($actual, $expected);
-    if (count($unexpected)) {
-      $this->assertTrue(FALSE, t('Unexpected hooks were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected))));
-    }
-    else {
-      $this->assertTrue(TRUE, t('No unexpected hooks were called.'));
-    }
-  }
-
-  /**
-   * Assert that a hook_file_* hook was called a certain number of times.
-   *
-   * @param $hook
-   *   String with the hook name, e.g. 'load', 'save', 'insert', etc.
-   * @param $expected_count
-   *   Optional integer count.
-   * @param $message
-   *   Optional translated string message.
-   */
-  function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) {
-    $actual_count = count(file_test_get_calls($hook));
-
-    if (is_null($message)) {
-      if ($actual_count == $expected_count) {
-        $message = t('hook_file_@name was called correctly.', array('@name' => $hook));
-      }
-      elseif ($expected_count == 0) {
-        $message = format_plural($actual_count, 'hook_file_@name was not expected to be called but was actually called once.', 'hook_file_@name was not expected to be called but was actually called @count times.', array('@name' => $hook, '@count' => $actual_count));
-      }
-      else {
-        $message = t('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count));
-      }
-    }
-    $this->assertEqual($actual_count, $expected_count, $message);
-  }
-}
-
-
-/**
- *  This will run tests against the file_space_used() function.
- */
-class FileSpaceUsedTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File space used tests',
-      'description' => 'Tests the file_space_used() function.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-
-    // Create records for a couple of users with different sizes.
-    $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT);
-    drupal_write_record('file_managed', $file);
-    $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT);
-    drupal_write_record('file_managed', $file);
-    $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT);
-    drupal_write_record('file_managed', $file);
-    $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT);
-    drupal_write_record('file_managed', $file);
-
-    // Now create some non-permanent files.
-    $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0);
-    drupal_write_record('file_managed', $file);
-    $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0);
-    drupal_write_record('file_managed', $file);
-  }
-
-  /**
-   * Test different users with the default status.
-   */
-  function testFileSpaceUsed() {
-    // Test different users with default status.
-    $this->assertEqual(file_space_used(2), 70);
-    $this->assertEqual(file_space_used(3), 300);
-    $this->assertEqual(file_space_used(), 370);
-
-    // Test the status fields
-    $this->assertEqual(file_space_used(NULL, 0), 4);
-    $this->assertEqual(file_space_used(NULL, FILE_STATUS_PERMANENT), 370);
-
-    // Test both the user and status.
-    $this->assertEqual(file_space_used(1, 0), 0);
-    $this->assertEqual(file_space_used(1, FILE_STATUS_PERMANENT), 0);
-    $this->assertEqual(file_space_used(2, 0), 1);
-    $this->assertEqual(file_space_used(2, FILE_STATUS_PERMANENT), 70);
-    $this->assertEqual(file_space_used(3, 0), 3);
-    $this->assertEqual(file_space_used(3, FILE_STATUS_PERMANENT), 300);
-  }
-}
-
-/**
- *  This will run tests against the file validation functions (file_validate_*).
- */
-class FileValidatorTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File validator tests',
-      'description' => 'Tests the functions used to validate uploaded files.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-
-    $this->image = new stdClass();
-    $this->image->uri = 'misc/druplicon.png';
-    $this->image->filename = basename($this->image->uri);
-
-    $this->non_image = new stdClass();
-    $this->non_image->uri = 'misc/jquery.js';
-    $this->non_image->filename = basename($this->non_image->uri);
-  }
-
-  /**
-   * Test the file_validate_extensions() function.
-   */
-  function testFileValidateExtensions() {
-    $file = new stdClass();
-    $file->filename = 'asdf.txt';
-    $errors = file_validate_extensions($file, 'asdf txt pork');
-    $this->assertEqual(count($errors), 0, t('Valid extension accepted.'), 'File');
-
-    $file->filename = 'asdf.txt';
-    $errors = file_validate_extensions($file, 'exe png');
-    $this->assertEqual(count($errors), 1, t('Invalid extension blocked.'), 'File');
-  }
-
-  /**
-   *  This ensures a specific file is actually an image.
-   */
-  function testFileValidateIsImage() {
-    $this->assertTrue(file_exists($this->image->uri), t('The image being tested exists.'), 'File');
-    $errors = file_validate_is_image($this->image);
-    $this->assertEqual(count($errors), 0, t('No error reported for our image file.'), 'File');
-
-    $this->assertTrue(file_exists($this->non_image->uri), t('The non-image being tested exists.'), 'File');
-    $errors = file_validate_is_image($this->non_image);
-    $this->assertEqual(count($errors), 1, t('An error reported for our non-image file.'), 'File');
-  }
-
-  /**
-   *  This ensures the resolution of a specific file is within bounds.
-   *  The image will be resized if it's too large.
-   */
-  function testFileValidateImageResolution() {
-    // Non-images.
-    $errors = file_validate_image_resolution($this->non_image);
-    $this->assertEqual(count($errors), 0, t("Shouldn't get any errors for a non-image file."), 'File');
-    $errors = file_validate_image_resolution($this->non_image, '50x50', '100x100');
-    $this->assertEqual(count($errors), 0, t("Don't check the resolution on non files."), 'File');
-
-    // Minimum size.
-    $errors = file_validate_image_resolution($this->image);
-    $this->assertEqual(count($errors), 0, t('No errors for an image when there is no minimum or maximum resolution.'), 'File');
-    $errors = file_validate_image_resolution($this->image, 0, '200x1');
-    $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't wide enough."), 'File');
-    $errors = file_validate_image_resolution($this->image, 0, '1x200');
-    $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't tall enough."), 'File');
-    $errors = file_validate_image_resolution($this->image, 0, '200x200');
-    $this->assertEqual(count($errors), 1, t('Small images report an error.'), 'File');
-
-    // Maximum size.
-    if (image_get_toolkit()) {
-      // Copy the image so that the original doesn't get resized.
-      $temp_dir = file_directory_path('temporary');
-      copy(drupal_realpath('misc/druplicon.png'), drupal_realpath($temp_dir) . '/druplicon.png');
-      $this->image->uri = $temp_dir . '/druplicon.png';
-
-      $errors = file_validate_image_resolution($this->image, '10x5');
-      $this->assertEqual(count($errors), 0, t('No errors should be reported when an oversized image can be scaled down.'), 'File');
-
-      $info = image_get_info($this->image->uri);
-      $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File');
-      $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File');
-
-      unlink(drupal_realpath($temp_dir . '/druplicon.png'));
-    }
-    else {
-      // TODO: should check that the error is returned if no toolkit is available.
-      $errors = file_validate_image_resolution($this->image, '5x10');
-      $this->assertEqual(count($errors), 1, t("Oversize images that can't be scaled get an error."), 'File');
-    }
-  }
-
-  /**
-   *  This will ensure the filename length is valid.
-   */
-  function testFileValidateNameLength() {
-    // Create a new file object.
-    $file = new stdClass();
-
-    // Add a filename with an allowed length and test it.
-    $file->filename = str_repeat('x', 240);
-    $this->assertEqual(strlen($file->filename), 240);
-    $errors = file_validate_name_length($file);
-    $this->assertEqual(count($errors), 0, t('No errors reported for 240 length filename.'), 'File');
-
-    // Add a filename with a length too long and test it.
-    $file->filename = str_repeat('x', 241);
-    $errors = file_validate_name_length($file);
-    $this->assertEqual(count($errors), 1, t('An error reported for 241 length filename.'), 'File');
-
-    // Add a filename with an empty string and test it.
-    $file->filename = '';
-    $errors = file_validate_name_length($file);
-    $this->assertEqual(count($errors), 1, t('An error reported for 0 length filename.'), 'File');
-  }
-
-
-  /**
-   * Test file_validate_size().
-   */
-  function testFileValidateSize() {
-    global $user;
-    $original_user = $user;
-    drupal_save_session(FALSE);
-
-    // Run these test as uid = 1.
-    $user = user_load(1);
-
-    $file = new stdClass();
-    $file->filesize = 999999;
-    $errors = file_validate_size($file, 1, 1);
-    $this->assertEqual(count($errors), 0, t('No size limits enforced on uid=1.'), 'File');
-
-    // Run these tests as a regular user.
-    $user = $this->drupalCreateUser();
-
-    // Create a file with a size of 1000 bytes, and quotas of only 1 byte.
-    $file = new stdClass();
-    $file->filesize = 1000;
-    $errors = file_validate_size($file, 0, 0);
-    $this->assertEqual(count($errors), 0, t('No limits means no errors.'), 'File');
-    $errors = file_validate_size($file, 1, 0);
-    $this->assertEqual(count($errors), 1, t('Error for the file being over the limit.'), 'File');
-    $errors = file_validate_size($file, 0, 1);
-    $this->assertEqual(count($errors), 1, t('Error for the user being over their limit.'), 'File');
-    $errors = file_validate_size($file, 1, 1);
-    $this->assertEqual(count($errors), 2, t('Errors for both the file and their limit.'), 'File');
-
-    $user = $original_user;
-    drupal_save_session(TRUE);
-  }
-}
-
-
-
-/**
- *  Tests the file_unmanaged_save_data() function.
- */
-class FileUnmanagedSaveDataTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Unmanaged file save data',
-      'description' => 'Tests the unmanaged file save data function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Test the file_unmanaged_save_data() function.
-   */
-  function testFileSaveData() {
-    $contents = $this->randomName(8);
-
-    // No filename.
-    $filepath = file_unmanaged_save_data($contents);
-    $this->assertTrue($filepath, t('Unnamed file saved correctly.'));
-    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($filepath)), t("File was placed in Drupal's files directory."));
-    $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.'));
-
-    // Provide a filename.
-    $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE);
-    $this->assertTrue($filepath, t('Unnamed file saved correctly.'));
-    $this->assertEqual(file_directory_path('public'), file_directory_path(file_uri_scheme($filepath)), t("File was placed in Drupal's files directory."));
-    $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.'));
-    $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.'));
-    $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664));
-  }
-}
-
-/**
- * Test the file_save_upload() function.
- */
-class FileSaveUploadTest extends FileHookTestCase {
-  /**
-   * An image file path for uploading.
-   */
-  protected $image;
-
-  /**
-   * A PHP file path for upload security testing.
-   */
-  protected $phpfile;
-
-  /**
-   * The largest file id when the test starts.
-   */
-  protected $maxFidBefore;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'File uploading',
-      'description' => 'Tests the file uploading functions.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    $account = $this->drupalCreateUser(array('access content'));
-    $this->drupalLogin($account);
-
-    $this->image = current($this->drupalGetTestFiles('image'));
-    list(, $this->image_extension) = explode('.', $this->image->filename);
-    $this->assertTrue(is_file($this->image->uri), t("The image file we're going to upload exists."));
-
-    $this->phpfile = current($this->drupalGetTestFiles('php'));
-    $this->assertTrue(is_file($this->phpfile->uri), t("The PHP file we're going to upload exists."));
-
-    $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
-
-    // Upload with replace to guarantee there's something there.
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
-    );
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called then clean out the hook
-    // counters.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-    file_test_reset();
-  }
-
-  /**
-   * Test the file_save_upload() function.
-   */
-  function testNormal() {
-    $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
-    $this->assertTrue($max_fid_after > $this->maxFidBefore, t('A new file was created.'));
-    $file1 = file_load($max_fid_after);
-    $this->assertTrue($file1, t('Loaded the file.'));
-    // MIME type of the uploaded image may be either image/jpeg or image/png.
-    $this->assertEqual(substr($file1->filemime, 0, 5), 'image', 'A MIME type was set.');
-
-    // Reset the hook counters to get rid of the 'load' we just called.
-    file_test_reset();
-
-    // Upload a second file.
-    $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
-    $image2 = current($this->drupalGetTestFiles('image'));
-    $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri));
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('You WIN!'));
-    $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-
-    $file2 = file_load($max_fid_after);
-    $this->assertTrue($file2);
-    // MIME type of the uploaded image may be either image/jpeg or image/png.
-    $this->assertEqual(substr($file2->filemime, 0, 5), 'image', 'A MIME type was set.');
-
-    // Load both files using file_load_multiple().
-    $files = file_load_multiple(array($file1->fid, $file2->fid));
-    $this->assertTrue(isset($files[$file1->fid]), t('File was loaded successfully'));
-    $this->assertTrue(isset($files[$file2->fid]), t('File was loaded successfully'));
-
-    // Upload a third file to a subdirectory.
-    $image3 = current($this->drupalGetTestFiles('image'));
-    $image3_realpath = drupal_realpath($image3->uri);
-    $dir = $this->randomName();
-    $edit = array(
-      'files[file_test_upload]' => $image3_realpath,
-      'file_subdir' => $dir,
-    );
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('You WIN!'));
-    $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(basename($image3_realpath))));
-
-    // Check that file_load_multiple() with no arguments returns FALSE.
-    $this->assertFalse(file_load_multiple(), t('No files were loaded.'));
-  }
-
-  /**
-   * Test extension handling.
-   */
-  function testHandleExtension() {
-    // The file being tested is a .gif which is in the default safe list
-    // of extensions to allow when the extension validator isn't used. This is
-    // implicitly tested at the testNormal() test. Here we tell
-    // file_save_upload() to only allow ".foo".
-    $extensions = 'foo';
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
-      'extensions' => $extensions,
-    );
-
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $message = t('Only files with the following extensions are allowed: ') . '<em class="placeholder">' . $extensions . '</em>';
-    $this->assertRaw($message, t('Can\'t upload a disallowed extension'));
-    $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate'));
-
-    // Reset the hook counters.
-    file_test_reset();
-
-    $extensions = 'foo ' . $this->image_extension;
-    // Now tell file_save_upload() to allow the extension of our test image.
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
-      'extensions' => $extensions,
-    );
-
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload an allowed extension.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'load', 'update'));
-
-    // Reset the hook counters.
-    file_test_reset();
-
-    // Now tell file_save_upload() to allow any extension.
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
-      'allow_all_extensions' => TRUE,
-    );
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload any extension.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'load', 'update'));
-  }
-
-  /**
-   * Test dangerous file handling.
-   */
-  function testHandleDangerousFile() {
-    // Allow the .php extension and make sure it gets renamed to .txt for
-    // safety. Also check to make sure its MIME type was changed.
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->phpfile->uri),
-      'is_image_file' => FALSE,
-      'extensions' => 'php',
-    );
-
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $message = t('For security reasons, your upload has been renamed to ') . '<em class="placeholder">' . $this->phpfile->filename . '.txt' . '</em>';
-    $this->assertRaw($message, t('Dangerous file was renamed.'));
-    $this->assertRaw(t('File MIME type is text/plain.'), t('Dangerous file\'s MIME type was changed.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-
-    // Ensure dangerous files are not renamed when insecure uploads is TRUE.
-    // Turn on insecure uploads.
-    variable_set('allow_insecure_uploads', 1);
-    // Reset the hook counters.
-    file_test_reset();
-
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.'));
-    $this->assertRaw(t('File name is !filename', array('!filename' => $this->phpfile->filename)), t('Dangerous file was not renamed when insecure uploads is TRUE.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-
-    // Turn off insecure uploads.
-    variable_set('allow_insecure_uploads', 0);
-  }
-
-  /**
-   * Test file munge handling.
-   */
-  function testHandleFileMunge() {
-    // Ensure insecure uploads are disabled for this test.
-    variable_set('allow_insecure_uploads', 0);
-    $this->image = file_move($this->image, $this->image->uri . '.foo.' . $this->image_extension);
-
-    // Reset the hook counters to get rid of the 'move' we just called.
-    file_test_reset();
-
-    $extensions = $this->image_extension;
-    $edit = array(
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
-      'extensions' => $extensions,
-    );
-
-    $munged_filename = $this->image->filename;
-    $munged_filename = substr($munged_filename, 0, strrpos($munged_filename, '.'));
-    $munged_filename .= '_.' . $this->image_extension;
-
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('For security reasons, your upload has been renamed'), t('Found security message.'));
-    $this->assertRaw(t('File name is !filename', array('!filename' => $munged_filename)), t('File was successfully munged.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-
-    // Ensure we don't munge files if we're allowing any extension.
-    // Reset the hook counters.
-    file_test_reset();
-
-    $edit = array(
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
-      'allow_all_extensions' => TRUE,
-    );
-
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.'));
-    $this->assertRaw(t('File name is !filename', array('!filename' => $this->image->filename)), t('File was not munged when allowing any extension.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-  }
-
-  /**
-   * Test renaming when uploading over a file that already exists.
-   */
-  function testExistingRename() {
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_RENAME,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri)
-    );
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'insert'));
-  }
-
-  /**
-   * Test replacement when uploading over a file that already exists.
-   */
-  function testExistingReplace() {
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri)
-    );
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('validate', 'load', 'update'));
-  }
-
-  /**
-   * Test for failure when uploading over a file that already exists.
-   */
-  function testExistingError() {
-    $edit = array(
-      'file_test_replace' => FILE_EXISTS_ERROR,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri)
-    );
-    $this->drupalPost('file-test/upload', $edit, t('Submit'));
-    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
-    $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.'));
-
-    // Check that the no hooks were called while failing.
-    $this->assertFileHooksCalled(array());
-  }
-
-  /**
-   * Test for no failures when not uploading a file.
-   */
-  function testNoUpload() {
-    $this->drupalPost('file-test/upload', array(), t('Submit'));
-    $this->assertNoRaw(t('Epic upload FAIL!'), t('Failure message not found.'));
-  }
-}
-
-/**
- * Directory related tests.
- */
-class FileDirectoryTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File paths and directories',
-      'description' => 'Tests operations dealing with directories.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Test the file_directory_path() function.
-   */
-  function testFileCheckDirectory() {
-    // A directory to operate on.
-    $directory = file_directory_path() . '/' . $this->randomName() . '/' . $this->randomName();
-    $this->assertFalse(is_dir($directory), t('Directory does not exist prior to testing.'));
-
-    // Non-existent directory.
-    $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for non-existing directory.'), 'File');
-
-    // Make a directory.
-    $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), t('No error reported when creating a new directory.'), 'File');
-
-    // Make sure directory actually exists.
-    $this->assertTrue(is_dir($directory), t('Directory actually exists.'), 'File');
-
-    // Make directory read only.
-    @chmod($directory, 0444);
-    $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File');
-
-    // Test directory permission modification.
-    $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File');
-
-    // Test directory permission modification actually set correct permissions.
-    $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775));
-
-    // Remove .htaccess file to then test that it gets re-created.
-    $directory = file_directory_path();
-    @unlink($directory . '/.htaccess');
-    $this->assertFalse(is_file($directory . '/.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File');
-    file_ensure_htaccess();
-    $this->assertTrue(is_file($directory . '/.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File');
-    // Verify contents of .htaccess file.
-    $file = file_get_contents(file_directory_path() . '/.htaccess');
-    $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", t('The .htaccess file contains the proper content.'), 'File');
-  }
-
-  /**
-   * Check file_directory_path('public').
-   */
-  function testFileDirectoryPath() {
-    // Directory path.
-    $path = variable_get('file_public_path', '');
-    $this->assertEqual($path, file_directory_path('public'), t('Properly returns the stored file directory path.'), 'File');
-  }
-
-  /**
-   * Check file_directory_path() and file_directory_path('temporary').
-   */
-  function testFileDirectoryTemp() {
-    // Temporary directory handling.
-    variable_set('file_directory_temp', NULL);
-    $temp = file_directory_temp();
-    $this->assertTrue(!is_null($temp), t('Properly set and retrieved temp directory %directory.', array('%directory' => $temp)), 'File');
-  }
-
-  /**
-   * This will take a directory and path, and find a valid filepath that is not
-   * taken by another file.
-   */
-  function testFileCreateNewFilepath() {
-    // First we test against an imaginary file that does not exist in a
-    // directory.
-    $basename = 'xyz.txt';
-    $directory = 'misc';
-    $original = $directory . '/' . $basename;
-    $path = file_create_filename($basename, $directory);
-    $this->assertEqual($path, $original, t('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File');
-
-    // Then we test against a file that already exists within that directory.
-    $basename = 'druplicon.png';
-    $original = $directory . '/' . $basename;
-    $expected = $directory . '/druplicon_0.png';
-    $path = file_create_filename($basename, $directory);
-    $this->assertEqual($path, $expected, t('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File');
-
-    // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
-  }
-
-  /**
-   * This will test the filepath for a destination based on passed flags and
-   * whether or not the file exists.
-   *
-   * If a file exists, file_destination($destination, $replace) will either
-   * return:
-   * - the existing filepath, if $replace is FILE_EXISTS_REPLACE
-   * - a new filepath if FILE_EXISTS_RENAME
-   * - an error (returning FALSE) if FILE_EXISTS_ERROR.
-   * If the file doesn't currently exist, then it will simply return the
-   * filepath.
-   */
-  function testFileDestination() {
-    // First test for non-existent file.
-    $destination = 'misc/xyz.txt';
-    $path = file_destination($destination, FILE_EXISTS_REPLACE);
-    $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.'), 'File');
-    $path = file_destination($destination, FILE_EXISTS_RENAME);
-    $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_RENAME.'), 'File');
-    $path = file_destination($destination, FILE_EXISTS_ERROR);
-    $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_ERROR.'), 'File');
-
-    $destination = 'misc/druplicon.png';
-    $path = file_destination($destination, FILE_EXISTS_REPLACE);
-    $this->assertEqual($path, $destination, t('Existing filepath destination remains the same with FILE_EXISTS_REPLACE.'), 'File');
-    $path = file_destination($destination, FILE_EXISTS_RENAME);
-    $this->assertNotEqual($path, $destination, t('A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.'), 'File');
-    $path = file_destination($destination, FILE_EXISTS_ERROR);
-    $this->assertEqual($path, FALSE, t('An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.'), 'File');
-  }
-}
-
-
-/**
- * Tests the file_scan_directory() function.
- */
-class FileScanDirectoryTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File scan directory',
-      'description' => 'Tests the file_scan_directory() function.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    $this->path = drupal_get_path('module', 'simpletest') . '/files';
-  }
-
-  /**
-   * Check the format of the returned values.
-   */
-  function testReturn() {
-    // Grab a listing of all the JavaSscript files and check that they're
-    // passed to the callback.
-    $all_files = file_scan_directory($this->path, '/^javascript-/');
-    ksort($all_files);
-    $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
-
-    // Check the first file.
-    $file = reset($all_files);
-    $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the first returned file.'));
-    $this->assertEqual($file->uri, $this->path . '/javascript-1.txt', t('First file name was set correctly.'));
-    $this->assertEqual($file->filename, 'javascript-1.txt', t('First basename was set correctly'));
-    $this->assertEqual($file->name, 'javascript-1', t('First name was set correctly.'));
-
-    // Check the second file.
-    $file = next($all_files);
-    $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the second returned file.'));
-    $this->assertEqual($file->uri, $this->path . '/javascript-2.script', t('Second file name was set correctly.'));
-    $this->assertEqual($file->filename, 'javascript-2.script', t('Second basename was set correctly'));
-    $this->assertEqual($file->name, 'javascript-2', t('Second name was set correctly.'));
-  }
-
-  /**
-   * Check that the callback function is called correctly.
-   */
-  function testOptionCallback() {
-    // When nothing is matched nothing should be passed to the callback.
-    $all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback'));
-    $this->assertEqual(0, count($all_files), t('No files were found.'));
-    $results = file_test_file_scan_callback();
-    file_test_file_scan_callback_reset();
-    $this->assertEqual(0, count($results), t('No files were passed to the callback.'));
-
-    // Grab a listing of all the JavaSscript files and check that they're
-    // passed to the callback.
-    $all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback'));
-    $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
-    $results = file_test_file_scan_callback();
-    file_test_file_scan_callback_reset();
-    $this->assertEqual(2, count($results), t('Files were passed to the callback.'));
-  }
-
-  /**
-   * Check that the no-mask parameter is honored.
-   */
-  function testOptionNoMask() {
-    // Grab a listing of all the JavaSscript files.
-    $all_files = file_scan_directory($this->path, '/^javascript-/');
-    $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
-
-    // Now use the nomast parameter to filter out the .script file.
-    $filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/'));
-    $this->assertEqual(1, count($filtered_files), t('Filtered correctly.'));
-  }
-
-  /**
-   * Check that key parameter sets the return value's key.
-   */
-  function testOptionKey() {
-    // "filename", for the path starting with $dir.
-    $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
-    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath')));
-    sort($actual);
-    $this->assertEqual($expected, $actual, t('Returned the correct values for the filename key.'));
-
-    // "basename", for the basename of the file.
-    $expected = array('javascript-1.txt', 'javascript-2.script');
-    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename')));
-    sort($actual);
-    $this->assertEqual($expected, $actual, t('Returned the correct values for the basename key.'));
-
-    // "name" for the name of the file without an extension.
-    $expected = array('javascript-1', 'javascript-2');
-    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name')));
-    sort($actual);
-    $this->assertEqual($expected, $actual, t('Returned the correct values for the name key.'));
-
-    // Invalid option that should default back to "filename".
-    $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
-    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID')));
-    sort($actual);
-    $this->assertEqual($expected, $actual, t('An invalid key defaulted back to the default.'));
-  }
-
-  /**
-   * Check that the recurse option decends into subdirectories.
-   */
-  function testOptionRecurse() {
-    $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => FALSE));
-    $this->assertTrue(empty($files), t("Without recursion couldn't find javascript files."));
-
-    $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => TRUE));
-    $this->assertEqual(2, count($files), t('With recursion we found the expected javascript files.'));
-  }
-
-
-  /**
-   * Check that the min_depth options lets us ignore files in the starting
-   * directory.
-   */
-  function testOptionMinDepth() {
-    $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0));
-    $this->assertEqual(2, count($files), t('No minimum-depth gets files in current directory.'));
-
-    $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1));
-    $this->assertTrue(empty($files), t("Minimum-depth of 1 successfully excludes files from current directory."));
-  }
-}
-
-
-/**
- * Deletion related tests.
- */
-class FileUnmanagedDeleteTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Unmanaged file delete',
-      'description' => 'Tests the unmanaged file delete function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Delete a normal file.
-   */
-  function testNormal() {
-    // Create a file for testing
-    $file = $this->createFile();
-
-    // Delete a regular file
-    $this->assertTrue(file_unmanaged_delete($file->uri), t('Deleted worked.'));
-    $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.'));
-  }
-
-  /**
-   * Try deleting a missing file.
-   */
-  function testMissing() {
-    // Try to delete a non-existing file
-    $this->assertTrue(file_unmanaged_delete(file_directory_path() . '/' . $this->randomName()), t('Returns true when deleting a non-existent file.'));
-  }
-
-  /**
-   * Try deleting a directory.
-   */
-  function testDirectory() {
-    // A directory to operate on.
-    $directory = $this->createDirectory();
-
-    // Try to delete a directory
-    $this->assertFalse(file_unmanaged_delete($directory), t('Could not delete the delete directory.'));
-    $this->assertTrue(file_exists($directory), t('Directory has not been deleted.'));
-  }
-}
-
-
-/**
- * Deletion related tests.
- */
-class FileUnmanagedDeleteRecursiveTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Unmanaged recursive file delete',
-      'description' => 'Tests the unmanaged file delete recursive function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Delete a normal file.
-   */
-  function testSingleFile() {
-    // Create a file for testing
-    $filepath = file_directory_path() . '/' . $this->randomName();
-    file_put_contents($filepath, '');
-
-    // Delete the file.
-    $this->assertTrue(file_unmanaged_delete_recursive($filepath), t('Function reported success.'));
-    $this->assertFalse(file_exists($filepath), t('Test file has been deleted.'));
-  }
-
-  /**
-   * Try deleting an empty directory.
-   */
-  function testEmptyDirectory() {
-    // A directory to operate on.
-    $directory = $this->createDirectory();
-
-    // Delete the directory.
-    $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.'));
-    $this->assertFalse(file_exists($directory), t('Directory has been deleted.'));
-  }
-
-  /**
-   * Try deleting a directory with some files.
-   */
-  function testDirectory() {
-    // A directory to operate on.
-    $directory = $this->createDirectory();
-    $filepathA = $directory . '/A';
-    $filepathB = $directory . '/B';
-    file_put_contents($filepathA, '');
-    file_put_contents($filepathB, '');
-
-    // Delete the directory.
-    $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.'));
-    $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.'));
-    $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.'));
-    $this->assertFalse(file_exists($directory), t('Directory has been deleted.'));
-  }
-
-  /**
-   * Try deleting subdirectories with some files.
-   */
-  function testSubDirectory() {
-    // A directory to operate on.
-    $directory = $this->createDirectory();
-    $subdirectory = $this->createDirectory($directory . '/sub');
-    $filepathA = $directory . '/A';
-    $filepathB = $subdirectory . '/B';
-    file_put_contents($filepathA, '');
-    file_put_contents($filepathB, '');
-
-    // Delete the directory.
-    $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.'));
-    $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.'));
-    $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.'));
-    $this->assertFalse(file_exists($subdirectory), t('Subdirectory has been deleted.'));
-    $this->assertFalse(file_exists($directory), t('Directory has been deleted.'));
-  }
-}
-
-
-/**
- * Unmanaged move related tests.
- */
-class FileUnmanagedMoveTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Unmanaged file moving',
-      'description' => 'Tests the unmanaged file move function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Move a normal file.
-   */
-  function testNormal() {
-    // Create a file for testing
-    $file = $this->createFile();
-
-    // Moving to a new name.
-    $desired_filepath = 'public://' . $this->randomName();
-    $new_filepath = file_unmanaged_move($file->uri, $desired_filepath, FILE_EXISTS_ERROR);
-    $this->assertTrue($new_filepath, t('Move was successful.'));
-    $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.'));
-    $this->assertTrue(file_exists($new_filepath), t('File exists at the new location.'));
-    $this->assertFalse(file_exists($file->uri), t('No file remains at the old location.'));
-    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
-
-    // Moving with rename.
-    $desired_filepath = 'public://' . $this->randomName();
-    $this->assertTrue(file_exists($new_filepath), t('File exists before moving.'));
-    $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.'));
-    $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
-    $this->assertTrue($newer_filepath, t('Move was successful.'));
-    $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.'));
-    $this->assertTrue(file_exists($newer_filepath), t('File exists at the new location.'));
-    $this->assertFalse(file_exists($new_filepath), t('No file remains at the old location.'));
-    $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664));
-
-    // TODO: test moving to a directory (rather than full directory/file path)
-    // TODO: test creating and moving normal files (rather than streams)
-  }
-
-  /**
-   * Try to move a missing file.
-   */
-  function testMissing() {
-    // Move non-existent file.
-    $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName());
-    $this->assertFalse($new_filepath, t('Moving a missing file fails.'));
-  }
-
-  /**
-   * Try to move a file onto itself.
-   */
-  function testOverwriteSelf() {
-    // Create a file for testing.
-    $file = $this->createFile();
-
-    // Move the file onto itself without renaming shouldn't make changes.
-    $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_REPLACE);
-    $this->assertFalse($new_filepath, t('Moving onto itself without renaming fails.'));
-    $this->assertTrue(file_exists($file->uri), t('File exists after moving onto itself.'));
-
-    // Move the file onto itself with renaming will result in a new filename.
-    $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_RENAME);
-    $this->assertTrue($new_filepath, t('Moving onto itself with renaming works.'));
-    $this->assertFalse(file_exists($file->uri), t('Original file has been removed.'));
-    $this->assertTrue(file_exists($new_filepath), t('File exists after moving onto itself.'));
-  }
-}
-
-
-/**
- * Unmanaged copy related tests.
- */
-class FileUnmanagedCopyTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Unmanaged file copying',
-      'description' => 'Tests the unmanaged file copy function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Copy a normal file.
-   */
-  function testNormal() {
-    // Create a file for testing
-    $file = $this->createFile();
-
-    // Copying to a new name.
-    $desired_filepath = 'public://' . $this->randomName();
-    $new_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_ERROR);
-    $this->assertTrue($new_filepath, t('Copy was successful.'));
-    $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.'));
-    $this->assertTrue(file_exists($file->uri), t('Original file remains.'));
-    $this->assertTrue(file_exists($new_filepath), t('New file exists.'));
-    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
-
-    // Copying with rename.
-    $desired_filepath = 'public://' . $this->randomName();
-    $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.'));
-    $newer_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_RENAME);
-    $this->assertTrue($newer_filepath, t('Copy was successful.'));
-    $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.'));
-    $this->assertTrue(file_exists($file->uri), t('Original file remains.'));
-    $this->assertTrue(file_exists($newer_filepath), t('New file exists.'));
-    $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664));
-
-    // TODO: test copying to a directory (rather than full directory/file path)
-    // TODO: test copying normal files using normal paths (rather than only streams)
-  }
-
-  /**
-   * Copy a non-existent file.
-   */
-  function testNonExistent() {
-    // Copy non-existent file
-    $desired_filepath = $this->randomName();
-    $this->assertFalse(file_exists($desired_filepath), t("Randomly named file doesn't exists."));
-    $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomName());
-    $this->assertFalse($new_filepath, t('Copying a missing file fails.'));
-  }
-
-  /**
-   * Copy a file onto itself.
-   */
-  function testOverwriteSelf() {
-    // Create a file for testing
-    $file = $this->createFile();
-
-    // Copy the file onto itself with renaming works.
-    $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_RENAME);
-    $this->assertTrue($new_filepath, t('Copying onto itself with renaming works.'));
-    $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.'));
-    $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.'));
-    $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.'));
-    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
-
-    // Copy the file onto itself without renaming fails.
-    $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_ERROR);
-    $this->assertFalse($new_filepath, t('Copying onto itself without renaming fails.'));
-    $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.'));
-
-    // Copy the file into same directory without renaming fails.
-    $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_ERROR);
-    $this->assertFalse($new_filepath, t('Copying onto itself fails.'));
-    $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.'));
-
-    // Copy the file into same directory with renaming works.
-    $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_RENAME);
-    $this->assertTrue($new_filepath, t('Copying into same directory works.'));
-    $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.'));
-    $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.'));
-    $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.'));
-    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
-  }
-}
-
-/**
- * Deletion related tests.
- */
-class FileDeleteTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File delete',
-      'description' => 'Tests the file delete function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Try deleting a normal file (as opposed to a directory, symlink, etc).
-   */
-  function testNormal() {
-    $file = $this->createFile();
-
-    // Check that deletion removes the file and database record.
-    $this->assertTrue(is_file($file->uri), t("File exists."));
-    $this->assertIdentical(file_delete($file), TRUE, t("Delete worked."));
-    $this->assertFileHooksCalled(array('references', 'delete'));
-    $this->assertFalse(file_exists($file->uri), t("Test file has actually been deleted."));
-    $this->assertFalse(file_load($file->fid), t('File was removed from the database.'));
-
-    // TODO: implement hook_file_references() in file_test.module and report a
-    // file in use and test the $force parameter.
-  }
-}
-
-
-/**
- * Move related tests
- */
-class FileMoveTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File moving',
-      'description' => 'Tests the file move function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Move a normal file.
-   */
-  function testNormal() {
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-    $desired_filepath = 'public://' . $this->randomName();
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_move(clone $source, $desired_filepath, FILE_EXISTS_ERROR);
-
-    // Check the return status and that the contents changed.
-    $this->assertTrue($result, t('File moved sucessfully.'));
-    $this->assertFalse(file_exists($source->uri));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('move', 'update'));
-
-    // Make sure we got the same file back.
-    $this->assertEqual($source->fid, $result->fid, t("Source file id's' %fid is unchanged after move.", array('%fid' => $source->fid)));
-
-    // Reload the file from the database and check that the changes were
-    // actually saved.
-    $loaded_file = file_load($result->fid, TRUE);
-    $this->assertTrue($loaded_file, t('File can be loaded from the database.'));
-    $this->assertFileUnchanged($result, $loaded_file);
-  }
-
-  /**
-   * Test renaming when moving onto a file that already exists.
-   */
-  function testExistingRename() {
-    // Setup a file to overwrite.
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-    $target = $this->createFile();
-    $this->assertDifferentFile($source, $target);
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_move(clone $source, $target->uri, FILE_EXISTS_RENAME);
-
-    // Check the return status and that the contents changed.
-    $this->assertTrue($result, t('File moved sucessfully.'));
-    $this->assertFalse(file_exists($source->uri));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('move', 'update'));
-
-    // Compare the returned value to what made it into the database.
-    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
-    // The target file should not have been altered.
-    $this->assertFileUnchanged($target, file_load($target->fid, TRUE));
-    // Make sure we end up with two distinct files afterwards.
-    $this->assertDifferentFile($target, $result);
-
-    // Compare the source and results.
-    $loaded_source = file_load($source->fid, TRUE);
-    $this->assertEqual($loaded_source->fid, $result->fid, t("Returned file's id matches the source."));
-    $this->assertNotEqual($loaded_source->uri, $source->uri, t("Returned file path has changed from the original."));
-  }
-
-  /**
-   * Test replacement when moving onto a file that already exists.
-   */
-  function testExistingReplace() {
-    // Setup a file to overwrite.
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-    $target = $this->createFile();
-    $this->assertDifferentFile($source, $target);
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_move(clone $source, $target->uri, FILE_EXISTS_REPLACE);
-
-    // Look at the results.
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.'));
-    $this->assertFalse(file_exists($source->uri));
-    $this->assertTrue($result, t('File moved sucessfully.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('move', 'update', 'delete', 'references', 'load'));
-
-    // Reload the file from the database and check that the changes were
-    // actually saved.
-    $loaded_result = file_load($result->fid, TRUE);
-    $this->assertFileUnchanged($result, $loaded_result);
-    // Check that target was re-used.
-    $this->assertSameFile($target, $loaded_result);
-    // Source and result should be totally different.
-    $this->assertDifferentFile($source, $loaded_result);
-  }
-
-  /**
-   * Test replacement when moving onto itself.
-   */
-  function testExistingReplaceSelf() {
-    // Setup a file to overwrite.
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-
-    // Copy the file over itself. Clone the object so we don't have to worry
-    // about the function changing our reference copy.
-    $result = file_move(clone $source, $source->uri, FILE_EXISTS_REPLACE);
-    $this->assertFalse($result, t('File move failed.'));
-    $this->assertEqual($contents, file_get_contents($source->uri), t('Contents of file were not altered.'));
-
-    // Check that no hooks were called while failing.
-    $this->assertFileHooksCalled(array());
-
-    // Load the file from the database and make sure it is identical to what
-    // was returned.
-    $this->assertFileUnchanged($source, file_load($source->fid, TRUE));
-  }
-
-  /**
-   * Test that moving onto an existing file fails when FILE_EXISTS_ERROR is
-   * specified.
-   */
-  function testExistingError() {
-    $contents = $this->randomName(10);
-    $source = $this->createFile();
-    $target = $this->createFile(NULL, $contents);
-    $this->assertDifferentFile($source, $target);
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_move(clone $source, $target->uri, FILE_EXISTS_ERROR);
-
-    // Check the return status and that the contents did not change.
-    $this->assertFalse($result, t('File move failed.'));
-    $this->assertTrue(file_exists($source->uri));
-    $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.'));
-
-    // Check that no hooks were called while failing.
-    $this->assertFileHooksCalled(array());
-
-    // Load the file from the database and make sure it is identical to what
-    // was returned.
-    $this->assertFileUnchanged($source, file_load($source->fid, TRUE));
-    $this->assertFileUnchanged($target, file_load($target->fid, TRUE));
-  }
-}
-
-
-/**
- * Copy related tests.
- */
-class FileCopyTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File copying',
-      'description' => 'Tests the file copy function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Test file copying in the normal, base case.
-   */
-  function testNormal() {
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-    $desired_uri = 'public://' . $this->randomName();
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_copy(clone $source, $desired_uri, FILE_EXISTS_ERROR);
-
-    // Check the return status and that the contents changed.
-    $this->assertTrue($result, t('File copied sucessfully.'));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('copy', 'insert'));
-
-    $this->assertDifferentFile($source, $result);
-    $this->assertEqual($result->uri, $desired_uri, t('The copied file object has the desired filepath.'));
-    $this->assertTrue(file_exists($source->uri), t('The original file still exists.'));
-    $this->assertTrue(file_exists($result->uri), t('The copied file exists.'));
-
-    // Reload the file from the database and check that the changes were
-    // actually saved.
-    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
-  }
-
-  /**
-   * Test renaming when copying over a file that already exists.
-   */
-  function testExistingRename() {
-    // Setup a file to overwrite.
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-    $target = $this->createFile();
-    $this->assertDifferentFile($source, $target);
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_copy(clone $source, $target->uri, FILE_EXISTS_RENAME);
-
-    // Check the return status and that the contents changed.
-    $this->assertTrue($result, t('File copied sucessfully.'));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.'));
-    $this->assertNotEqual($result->uri, $source->uri, t('Returned file path has changed from the original.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('copy', 'insert'));
-
-    // Load all the affected files to check the changes that actually made it
-    // to the database.
-    $loaded_source = file_load($source->fid, TRUE);
-    $loaded_target = file_load($target->fid, TRUE);
-    $loaded_result = file_load($result->fid, TRUE);
-
-    // Verify that the source file wasn't changed.
-    $this->assertFileUnchanged($source, $loaded_source);
-
-    // Verify that what was returned is what's in the database.
-    $this->assertFileUnchanged($result, $loaded_result);
-
-    // Make sure we end up with three distinct files afterwards.
-    $this->assertDifferentFile($loaded_source, $loaded_target);
-    $this->assertDifferentFile($loaded_target, $loaded_result);
-    $this->assertDifferentFile($loaded_source, $loaded_result);
-  }
-
-  /**
-   * Test replacement when copying over a file that already exists.
-   */
-  function testExistingReplace() {
-    // Setup a file to overwrite.
-    $contents = $this->randomName(10);
-    $source = $this->createFile(NULL, $contents);
-    $target = $this->createFile();
-    $this->assertDifferentFile($source, $target);
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_copy(clone $source, $target->uri, FILE_EXISTS_REPLACE);
-
-    // Check the return status and that the contents changed.
-    $this->assertTrue($result, t('File copied sucessfully.'));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.'));
-    $this->assertDifferentFile($source, $result);
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('load', 'copy', 'update'));
-
-    // Load all the affected files to check the changes that actually made it
-    // to the database.
-    $loaded_source = file_load($source->fid, TRUE);
-    $loaded_target = file_load($target->fid, TRUE);
-    $loaded_result = file_load($result->fid, TRUE);
-
-    // Verify that the source file wasn't changed.
-    $this->assertFileUnchanged($source, $loaded_source);
-
-    // Verify that what was returned is what's in the database.
-    $this->assertFileUnchanged($result, $loaded_result);
-
-    // Target file was reused for the result.
-    $this->assertFileUnchanged($loaded_target, $loaded_result);
-  }
-
-  /**
-   * Test that copying over an existing file fails when FILE_EXISTS_ERROR is
-   * specified.
-   */
-  function testExistingError() {
-    $contents = $this->randomName(10);
-    $source = $this->createFile();
-    $target = $this->createFile(NULL, $contents);
-    $this->assertDifferentFile($source, $target);
-
-    // Clone the object so we don't have to worry about the function changing
-    // our reference copy.
-    $result = file_copy(clone $source, $target->uri, FILE_EXISTS_ERROR);
-
-    // Check the return status and that the contents were not changed.
-    $this->assertFalse($result, t('File copy failed.'));
-    $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.'));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array());
-
-    $this->assertFileUnchanged($source, file_load($source->fid, TRUE));
-    $this->assertFileUnchanged($target, file_load($target->fid, TRUE));
-  }
-}
-
-
-/**
- * Tests the file_load() function.
- */
-class FileLoadTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File loading',
-      'description' => 'Tests the file_load() function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Try to load a non-existent file by fid.
-   */
-  function testLoadMissingFid() {
-    $this->assertFalse(file_load(-1), t("Try to load an invalid fid fails."));
-    $this->assertFileHooksCalled(array());
-  }
-
-  /**
-   * Try to load a non-existent file by URI.
-   */
-  function testLoadMissingFilepath() {
-    $files = file_load_multiple(array(), array('uri' => 'foobar://misc/druplicon.png'));
-    $this->assertFalse(reset($files), t("Try to load a file that doesn't exist in the database fails."));
-    $this->assertFileHooksCalled(array());
-  }
-
-  /**
-   * Try to load a non-existent file by status.
-   */
-  function testLoadInvalidStatus() {
-    $files = file_load_multiple(array(), array('status' => -99));
-    $this->assertFalse(reset($files), t("Trying to load a file with an invalid status fails."));
-    $this->assertFileHooksCalled(array());
-  }
-
-  /**
-   * Load a single file and ensure that the correct values are returned.
-   */
-  function testSingleValues() {
-    // Create a new file object from scratch so we know the values.
-    $file = $this->createFile('druplicon.txt', NULL, 'public');
-
-    $by_fid_file = file_load($file->fid);
-    $this->assertFileHookCalled('load');
-    $this->assertTrue(is_object($by_fid_file), t('file_load() returned an object.'));
-    $this->assertEqual($by_fid_file->fid, $file->fid, t("Loading by fid got the same fid."), 'File');
-    $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File');
-    $this->assertEqual($by_fid_file->filename, $file->filename, t("Loading by fid got the correct filename."), 'File');
-    $this->assertEqual($by_fid_file->filemime, $file->filemime, t("Loading by fid got the correct MIME type."), 'File');
-    $this->assertEqual($by_fid_file->status, $file->status, t("Loading by fid got the correct status."), 'File');
-    $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
-  }
-
-  /**
-   * This will test loading file data from the database.
-   */
-  function testMultiple() {
-    // Create a new file object.
-    $file = $this->createFile('druplicon.txt', NULL, 'public');
-
-    // Load by path.
-    file_test_reset();
-    $by_path_files = file_load_multiple(array(), array('uri' => $file->uri));
-    $this->assertFileHookCalled('load');
-    $this->assertEqual(1, count($by_path_files), t('file_load_multiple() returned an array of the correct size.'));
-    $by_path_file = reset($by_path_files);
-    $this->assertTrue($by_path_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
-    $this->assertEqual($by_path_file->fid, $file->fid, t("Loading by filepath got the correct fid."), 'File');
-
-    // Load by fid.
-    file_test_reset();
-    $by_fid_files = file_load_multiple(array($file->fid), array());
-    $this->assertFileHookCalled('load');
-    $this->assertEqual(1, count($by_fid_files), t('file_load_multiple() returned an array of the correct size.'));
-    $by_fid_file = reset($by_fid_files);
-    $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
-    $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File');
-  }
-}
-
-/**
- * Tests the file_save() function.
- */
-class FileSaveTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File saving',
-      'description' => 'Tests the file_save() function.',
-      'group' => 'File API',
-    );
-  }
-
-  function testFileSave() {
-    // Create a new file object.
-    $file = array(
-      'uid' => 1,
-      'filename' => 'druplicon.txt',
-      'uri' => 'public://druplicon.txt',
-      'filemime' => 'text/plain',
-      'timestamp' => 1,
-      'status' => FILE_STATUS_PERMANENT,
-    );
-    $file = (object) $file;
-    file_put_contents($file->uri, 'hello world');
-
-    // Save it, inserting a new record.
-    $saved_file = file_save($file);
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('insert'));
-
-    $this->assertNotNull($saved_file, t("Saving the file should give us back a file object."), 'File');
-    $this->assertTrue($saved_file->fid > 0, t("A new file ID is set when saving a new file to the database."), 'File');
-    $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ);
-    $this->assertNotNull($loaded_file, t("Record exists in the database."));
-    $this->assertEqual($loaded_file->status, $file->status, t("Status was saved correctly."));
-    $this->assertEqual($saved_file->filesize, filesize($file->uri), t("File size was set correctly."), 'File');
-    $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File');
-
-
-    // Resave the file, updating the existing record.
-    file_test_reset();
-    $saved_file->status = 7;
-    $resaved_file = file_save($saved_file);
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('update'));
-
-    $this->assertEqual($resaved_file->fid, $saved_file->fid, t("The file ID of an existing file is not changed when updating the database."), 'File');
-    $this->assertTrue($resaved_file->timestamp >= $saved_file->timestamp, t("Timestamp didn't go backwards."), 'File');
-    $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ);
-    $this->assertNotNull($loaded_file, t("Record still exists in the database."), 'File');
-    $this->assertEqual($loaded_file->status, $saved_file->status, t("Status was saved correctly."));
-  }
-}
-
-
-/**
- * Tests the file_validate() function..
- */
-class FileValidateTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File validate',
-      'description' => 'Tests the file_validate() function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Test that the validators passed into are checked.
-   */
-  function testCallerValidation() {
-    $file = $this->createFile();
-
-    // Empty validators.
-    $this->assertEqual(file_validate($file, array()), array(), t('Validating an empty array works succesfully.'));
-    $this->assertFileHooksCalled(array('validate'));
-
-    // Use the file_test.module's test validator to ensure that passing tests
-    // return correctly.
-    file_test_reset();
-    file_test_set_return('validate', array());
-    $passing = array('file_test_validator' => array(array()));
-    $this->assertEqual(file_validate($file, $passing), array(), t('Validating passes.'));
-    $this->assertFileHooksCalled(array('validate'));
-
-    // Now test for failures in validators passed in and by hook_validate.
-    file_test_reset();
-    file_test_set_return('validate', array('Epic fail'));
-    $failing = array('file_test_validator' => array(array('Failed', 'Badly')));
-    $this->assertEqual(file_validate($file, $failing), array('Failed', 'Badly', 'Epic fail'), t('Validating returns errors.'));
-    $this->assertFileHooksCalled(array('validate'));
-  }
-}
-
-/**
- *  Tests the file_save_data() function.
- */
-class FileSaveDataTest extends FileHookTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File save data',
-      'description' => 'Tests the file save data function.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Test the file_save_data() function when no filename is provided.
-   */
-  function testWithoutFilename() {
-    $contents = $this->randomName(8);
-
-    $result = file_save_data($contents);
-    $this->assertTrue($result, t('Unnamed file saved correctly.'));
-
-    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
-    $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename."));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
-    $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.'));
-    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('insert'));
-
-    // Verify that what was returned is what's in the database.
-    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
-  }
-
-  /**
-   * Test the file_save_data() function when a filename is provided.
-   */
-  function testWithFilename() {
-    $contents = $this->randomName(8);
-
-    $result = file_save_data($contents, 'public://' . 'asdf.txt');
-    $this->assertTrue($result, t('Unnamed file saved correctly.'));
-
-    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
-    $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.'));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
-    $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.'));
-    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('insert'));
-
-    // Verify that what was returned is what's in the database.
-    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
-  }
-
-  /**
-   * Test file_save_data() when renaming around an existing file.
-   */
-  function testExistingRename() {
-    // Setup a file to overwrite.
-    $existing = $this->createFile();
-    $contents = $this->randomName(8);
-
-    $result = file_save_data($contents, $existing->uri, FILE_EXISTS_RENAME);
-    $this->assertTrue($result, t("File saved sucessfully."));
-
-    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
-    $this->assertEqual($result->filename, $existing->filename, t("Filename was set to the basename of the source, rather than that of the renamed file."));
-    $this->assertEqual($contents, file_get_contents($result->uri), t("Contents of the file are correct."));
-    $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set."));
-    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('insert'));
-
-    // Ensure that the existing file wasn't overwritten.
-    $this->assertDifferentFile($existing, $result);
-    $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE));
-
-    // Verify that was returned is what's in the database.
-    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
-  }
-
-  /**
-   * Test file_save_data() when replacing an existing file.
-   */
-  function testExistingReplace() {
-    // Setup a file to overwrite.
-    $existing = $this->createFile();
-    $contents = $this->randomName(8);
-
-    $result = file_save_data($contents, $existing->uri, FILE_EXISTS_REPLACE);
-    $this->assertTrue($result, t('File saved sucessfully.'));
-
-    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
-    $this->assertEqual($result->filename, $existing->filename, t('Filename was set to the basename of the existing file, rather than preserving the original name.'));
-    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
-    $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.'));
-    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
-
-    // Check that the correct hooks were called.
-    $this->assertFileHooksCalled(array('load', 'update'));
-
-    // Verify that the existing file was re-used.
-    $this->assertSameFile($existing, $result);
-
-    // Verify that what was returned is what's in the database.
-    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
-  }
-
-  /**
-   * Test that file_save_data() fails overwriting an existing file.
-   */
-  function testExistingError() {
-    $contents = $this->randomName(8);
-    $existing = $this->createFile(NULL, $contents);
-
-    // Check the overwrite error.
-    $result = file_save_data('asdf', $existing->uri, FILE_EXISTS_ERROR);
-    $this->assertFalse($result, t('Overwriting a file fails when FILE_EXISTS_ERROR is specified.'));
-    $this->assertEqual($contents, file_get_contents($existing->uri), t('Contents of existing file were unchanged.'));
-
-    // Check that no hooks were called while failing.
-    $this->assertFileHooksCalled(array());
-
-    // Ensure that the existing file wasn't overwritten.
-    $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE));
-  }
-}
-
-/**
- * Tests for download/file transfer functions.
- */
-class FileDownloadTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File download',
-      'description' => 'Tests for file download/transfer functions.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('file_test');
-    // Clear out any hook calls.
-    file_test_reset();
-  }
-
-  /**
-   * Test the public file transfer system.
-   */
-  function testPublicFileTransfer() {
-    // Test generating an URL to a created file.
-    $file = $this->createFile();
-    $url = file_create_url($file->uri);
-    $this->assertEqual($GLOBALS['base_url'] . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.'));
-    $this->drupalHead($url);
-    $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.'));
-
-    // Test generating an URL to a shipped file (i.e. a file that is part of
-    // Drupal core, a module or a theme, for example a JavaScript file).
-    $filepath = 'misc/jquery.js';
-    $url = file_create_url($filepath);
-    $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath, $url, t('Correctly generated a URL for a shipped file.'));
-    $this->drupalHead($url);
-    $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
-  }
-
-  /**
-   * Test the private file transfer system.
-   */
-  function testPrivateFileTransfer() {
-    // Set file downloads to private so handler functions get called.
-
-    // Create a file.
-    $file = $this->createFile(NULL, NULL, 'private');
-    $url  = file_create_url($file->uri);
-
-    // Set file_test access header to allow the download.
-    file_test_set_return('download', array('x-foo' => 'Bar'));
-    $this->drupalHead($url);
-    $headers = $this->drupalGetHeaders();
-    $this->assertEqual($headers['x-foo'] , 'Bar', t('Found header set by file_test module on private download.'));
-    $this->assertResponse(200, t('Correctly allowed access to a file when file_test provides headers.'));
-
-    // Deny access to all downloads via a -1 header.
-    file_test_set_return('download', -1);
-    $this->drupalHead($url);
-    $this->assertResponse(403, t('Correctly denied access to a file when file_test sets the header to -1.'));
-
-    // Try non-existent file.
-    $url = file_create_url('private://' . $this->randomName());
-    $this->drupalHead($url);
-    $this->assertResponse(404, t('Correctly returned 404 response for a non-existent file.'));
-  }
-
-  /**
-   * Test file_create_url().
-   */
-  function testFileCreateUrl() {
-    global $base_url;
-
-    // Tilde (~) is excluded from this test because it is encoded by
-    // rawurlencode() in PHP 5.2 but not in PHP 5.3, as per RFC 3986.
-    // @see http://www.php.net/manual/en/function.rawurlencode.php#86506
-    $basename = " -._!$'\"()*@[]?&+%#,;=:\n\x00" . // "Special" ASCII characters.
-      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
-      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
-    $basename_encoded = '%20-._%21%24%27%22%28%29%2A%40%5B%5D%3F%26%2B%25%23%2C%3B%3D%3A__' .
-      '%2523%2525%2526%252B%252F%253F' .
-      '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E';
-
-    $this->checkUrl('public', '', $basename, $base_url . '/' . file_directory_path() . '/' . $basename_encoded);
-    $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded);
-    $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0');
-  }
-
-  /**
-   * Download a file from the URL generated by file_create_url().
-   *
-   * Create a file with the specified scheme, directory and filename; check that
-   * the URL generated by file_create_url() for the specified file equals the
-   * specified URL; fetch the URL and then compare the contents to the file.
-   *
-   * @param $scheme
-   *   A scheme, e.g. "public"
-   * @param $directory
-   *   A directory, possibly ""
-   * @param $filename
-   *   A filename
-   * @param $expected_url
-   *   The expected URL
-   * @param $clean_url
-   *   The value of the clean_url setting
-   */
-  private function checkUrl($scheme, $directory, $filename, $expected_url, $clean_url = '1') {
-    variable_set('clean_url', $clean_url);
-
-    // Convert $filename to a valid filename, i.e. strip characters not
-    // supported by the filesystem, and create the file in the specified
-    // directory.
-    $filepath = file_create_filename($filename, $directory);
-    $directory_uri = $scheme . '://' . dirname($filepath);
-    file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY);
-    $file = $this->createFile($filepath, NULL, $scheme);
-
-    $url = file_create_url($file->uri);
-    $this->assertEqual($url, $expected_url, t('Generated URL matches expected URL.'));
-
-    if ($scheme == 'private') {
-      // Tell the implementation of hook_file_download() in file_test.module
-      // that this file may be downloaded.
-      file_test_set_return('download', array('x-foo' => 'Bar'));
-    }
-
-    $this->drupalGet($url);
-    if ($this->assertResponse(200) == 'pass') {
-      $this->assertRaw(file_get_contents($file->uri), t('Contents of the file are correct.'));
-    }
-
-    file_delete($file);
-  }
-}
-
-/**
- * Tests for file URL rewriting.
- */
-class FileURLRewritingTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File URL rewriting',
-      'description' => 'Tests for file URL rewriting.',
-      'group' => 'File',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('file_test');
-  }
-
-  /**
-   * Test the generating of rewritten shipped file URLs.
-   */
-  function testShippedFileURL()  {
-    // Test generating an URL to a shipped file (i.e. a file that is part of
-    // Drupal core, a module or a theme, for example a JavaScript file).
-
-    // Test alteration of file URLs to use a CDN.
-    variable_set('file_test_hook_file_url_alter', 'cdn');
-    $filepath = 'misc/jquery.js';
-    $url = file_create_url($filepath);
-    $this->assertEqual(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.'));
-    $filepath = 'misc/favicon.ico';
-    $url = file_create_url($filepath);
-    $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.'));
-
-    // Test alteration of file URLs to use root-relative URLs.
-    variable_set('file_test_hook_file_url_alter', 'root-relative');
-    $filepath = 'misc/jquery.js';
-    $url = file_create_url($filepath);
-    $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.'));
-    $filepath = 'misc/favicon.ico';
-    $url = file_create_url($filepath);
-    $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.'));
-
-    // Test alteration of file URLs to use protocol-relative URLs.
-    variable_set('file_test_hook_file_url_alter', 'protocol-relative');
-    $filepath = 'misc/jquery.js';
-    $url = file_create_url($filepath);
-    $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.'));
-    $filepath = 'misc/favicon.ico';
-    $url = file_create_url($filepath);
-    $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.'));
-  }
-
-  /**
-   * Test the generating of rewritten public created file URLs.
-   */
-  function testPublicCreatedFileURL() {
-    // Test generating an URL to a created file.
-
-    // Test alteration of file URLs to use a CDN.
-    variable_set('file_test_hook_file_url_alter', 'cdn');
-    $file = $this->createFile();
-    $url = file_create_url($file->uri);
-    $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a CDN URL for a created file.'));
-
-    // Test alteration of file URLs to use root-relative URLs.
-    variable_set('file_test_hook_file_url_alter', 'root-relative');
-    $file = $this->createFile();
-    $url = file_create_url($file->uri);
-    $this->assertEqual(base_path() . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a root-relative URL for a created file.'));
-
-    // Test alteration of file URLs to use a protocol-relative URLs.
-    variable_set('file_test_hook_file_url_alter', 'protocol-relative');
-    $file = $this->createFile();
-    $url = file_create_url($file->uri);
-    $this->assertEqual('/' . base_path() . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a protocol-relative URL for a created file.'));
-  }
-}
-
-/**
- * Tests for file_munge_filename() and file_unmunge_filename().
- */
-class FileNameMungingTest extends FileTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File naming',
-      'description' => 'Test filename munging and unmunging.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    $this->bad_extension = 'php';
-    $this->name = $this->randomName() . '.' . $this->bad_extension . '.txt';
-  }
-
-  /**
-   * Create a file and munge/unmunge the name.
-   */
-  function testMunging() {
-    // Disable insecure uploads.
-    variable_set('allow_insecure_uploads', 0);
-    $munged_name = file_munge_filename($this->name, '', TRUE);
-    $messages = drupal_get_messages();
-    $this->assertTrue(in_array(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $munged_name)), $messages['status']), t('Alert properly set when a file is renamed.'));
-    $this->assertNotEqual($munged_name, $this->name, t('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name)));
-  }
-
-  /**
-   * If the allow_insecure_uploads variable evaluates to true, the file should
-   * come out untouched, no matter how evil the filename.
-   */
-  function testMungeIgnoreInsecure() {
-    variable_set('allow_insecure_uploads', 1);
-    $munged_name = file_munge_filename($this->name, '');
-    $this->assertIdentical($munged_name, $this->name, t('The original filename (%original) matches the munged filename (%munged) when insecure uploads are enabled.', array('%munged' => $munged_name, '%original' => $this->name)));
-  }
-
-  /**
-   * White listed extensions are ignored by file_munge_filename().
-   */
-  function testMungeIgnoreWhitelisted() {
-    // Declare our extension as whitelisted.
-    $munged_name = file_munge_filename($this->name, $this->bad_extension);
-    $this->assertIdentical($munged_name, $this->name, t('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->name)));
-  }
-
-  /**
-   * Ensure that unmunge gets your name back.
-   */
-  function testUnMunge() {
-    $munged_name = file_munge_filename($this->name, '', FALSE);
-    $unmunged_name = file_unmunge_filename($munged_name);
-    // @TODO uncomment when this tests passes reliably, see
-    // http://drupal.org/node/368502
-    // $this->assertIdentical($unmunged_name, $this->name, t('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name)));
-  }
-}
-
-/**
- * Tests for file_get_mimetype().
- */
-class FileMimeTypeTest extends DrupalWebTestCase {
-  function setUp() {
-    parent::setUp('file_test');
-  }
-
-  public static function getInfo() {
-    return array(
-      'name' => 'File mimetypes',
-      'description' => 'Test filename mimetype detection.',
-      'group' => 'File API',
-    );
-  }
-
-  /**
-   * Test mapping of mimetypes from filenames.
-   */
-  public function testFileMimeTypeDetection() {
-    $prefix = 'simpletest://';
-
-    $test_case = array(
-      'test.jar' => 'application/java-archive',
-      'test.jpeg' => 'image/jpeg',
-      'test.JPEG' => 'image/jpeg',
-      'test.jpg' => 'image/jpeg',
-      'test.jar.jpg' => 'image/jpeg',
-      'test.jpg.jar' => 'application/java-archive',
-      'test.pcf.Z' => 'application/x-font',
-      'pcf.z' => 'application/octet-stream',
-      'jar' => 'application/octet-stream',
-      'some.junk' => 'application/octet-stream',
-      'foo.file_test_1' => 'madeup/file_test_1',
-      'foo.file_test_2' => 'madeup/file_test_2',
-      'foo.doc' => 'madeup/doc',
-    );
-
-    // Test using default mappings.
-    foreach ($test_case as $input => $expected) {
-      // Test stream [URI].
-      $output = file_get_mimetype($prefix . $input);
-      $this->assertIdentical($output, $expected, t('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
-
-      // Test normal path equivalent
-      $output = file_get_mimetype($input);
-      $this->assertIdentical($output, $expected, t('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
-    }
-
-    // Now test passing in the map.
-    $mapping = array(
-      'mimetypes' => array(
-        0 => 'application/java-archive',
-        1 => 'image/jpeg',
-      ),
-      'extensions' => array(
-         'jar' => 0,
-         'jpg' => 1,
-      )
-    );
-
-    $test_case = array(
-      'test.jar' => 'application/java-archive',
-      'test.jpeg' => 'application/octet-stream',
-      'test.jpg' => 'image/jpeg',
-      'test.jar.jpg' => 'image/jpeg',
-      'test.jpg.jar' => 'application/java-archive',
-      'test.pcf.z' => 'application/octet-stream',
-      'pcf.z' => 'application/octet-stream',
-      'jar' => 'application/octet-stream',
-      'some.junk' => 'application/octet-stream',
-      'foo.file_test_1' => 'application/octet-stream',
-      'foo.file_test_2' => 'application/octet-stream',
-      'foo.doc' => 'application/octet-stream',
-    );
-
-    foreach ($test_case as $input => $expected) {
-      $output = file_get_mimetype($input, $mapping);
-      $this->assertIdentical($output, $expected, t('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
-    }
-  }
-}
-
-/**
- * Tests stream wrapper functions.
- */
-class StreamWrapperTest extends DrupalWebTestCase {
-
-  protected $scheme = 'dummy';
-  protected $classname = 'DrupalDummyStreamWrapper';
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Stream wrappers',
-      'description' => 'Tests stream wrapper functions.',
-      'group' => 'File API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('file_test');
-    drupal_static_reset('file_get_stream_wrappers');
-  }
-
-  function tearDown() {
-    parent::tearDown();
-    stream_wrapper_unregister($this->scheme);
-  }
-
-  /**
-   * Test the getClassName() function.
-   */
-  function testGetClassName() {
-    // Check the dummy scheme.
-    $this->assertEqual($this->classname, file_stream_wrapper_get_class($this->scheme), t('Got correct class name for dummy scheme.'));
-    // Check core's scheme.
-    $this->assertEqual('DrupalPublicStreamWrapper', file_stream_wrapper_get_class('public'), t('Got correct class name for public scheme.'));
-  }
-
-  /**
-   * Test the file_stream_wrapper_get_instance_by_scheme() function.
-   */
-  function testGetInstanceByScheme() {
-    $instance = file_stream_wrapper_get_instance_by_scheme($this->scheme);
-    $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy scheme.'));
-
-    $instance = file_stream_wrapper_get_instance_by_scheme('public');
-    $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public scheme.'));
-  }
-
-  /**
-   * Test the URI and target functions.
-   */
-  function testUriFunctions() {
-    $instance = file_stream_wrapper_get_instance_by_uri($this->scheme . '://foo');
-    $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy URI.'));
-
-    $instance = file_stream_wrapper_get_instance_by_uri('public://foo');
-    $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.'));
-
-    // Test file_uri_target().
-    $this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', t('Got a valid stream target from public://foo/bar.txt.'));
-    $this->assertFalse(file_uri_target('foo/bar.txt'), t('foo/bar.txt is not a valid stream.'));
-
-    // Test file_build_uri() and file_directory_path().
-    $this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', t('Expected scheme was added.'));
-    $this->assertEqual(file_directory_path(), variable_get('file_public_path'), t('Expected default directory path was returned.'));
-    $this->assertEqual(file_directory_path('public'), variable_get('file_public_path'), t('Expected public directory path was returned.'));
-    $this->assertEqual(file_directory_path('temporary'), variable_get('file_temporary_path'), t('Expected temporary directory path was returned.'));
-    $this->assertEqual(file_directory_path($this->scheme), variable_get('stream_public_path', 'sites/default/files'), t('Expected dummy directory path was returned.'));
-    $this->assertFalse(file_directory_path('non-existent'), t('No directory path returned for invalid scheme.'));
-    variable_set('file_default_scheme', 'private');
-    $this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', t('Got a valid URI from foo/bar.txt.'));
-    $this->assertEqual(file_directory_path(), variable_get('file_private_path'), t('Expected default directory path was returned.'));
-    $this->assertEqual(file_directory_path('private'), variable_get('file_private_path'), t('Expected private directory path was returned.'));
-  }
-
-  /**
-   * Test the scheme functions.
-   */
-  function testGetValidStreamScheme() {
-    $this->assertEqual('foo', file_uri_scheme('foo://pork//chops'), t('Got the correct scheme from foo://asdf'));
-    $this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), t('Got a valid stream scheme from public://asdf'));
-    $this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), t('Did not get a valid stream scheme from foo://asdf'));
-  }
-}
diff --git a/modules/simpletest/tests/filetransfer.test b/modules/simpletest/tests/filetransfer.test
deleted file mode 100644
index b9a79f7..0000000
--- a/modules/simpletest/tests/filetransfer.test
+++ /dev/null
@@ -1,169 +0,0 @@
-<?php
-// $Id: filetransfer.test,v 1.6 2009/11/10 17:27:53 webchick Exp $
-
-
-class FileTranferTest extends DrupalWebTestCase {
-  protected $hostname = 'localhost';
-  protected $username = 'drupal';
-  protected $password = 'password';
-  protected $port = '42';
-
-  public static function getInfo() {
-    return array(
-      'name' => 'FileTransfer unit tests',
-      'description' => 'Test that the jail is respected and that protocols using recursive file move operations work.',
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    $this->testConnection = TestFileTransfer::factory(DRUPAL_ROOT, array('hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port));
-  }
-
-  function _getFakeModuleFiles() {
-    $files = array(
-      'fake.module',
-      'fake.info',
-      'theme' => array(
-        'fake.tpl.php'
-      ),
-      'inc' => array(
-        'fake.inc'
-      )
-    );
-    return $files;
-  }
-
-  function _buildFakeModule() {
-    $location = file_directory_path('temporary') . '/fake';
-    if (is_dir($location)) {
-      $ret = 0;
-      $output = array();
-      exec('rm -Rf ' . escapeshellarg($location), $output, $ret);
-      if ($ret != 0) {
-        throw new Exception('Error removing fake module directory.');
-      }
-    }
-
-    $files = $this->_getFakeModuleFiles();
-    $this->_writeDirectory($location, $files);
-    return $location;
-  }
-
-  function _writeDirectory($base, $files = array()) {
-    mkdir($base);
-    foreach ($files as $key => $file) {
-      if (is_array($file)) {
-        $this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file);
-      }
-      else {
-        //just write the filename into the file
-        file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file);
-      }
-    }
-  }
-
-  function testJail() {
-    $source = $this->_buildFakeModule();
-
-    // This convoluted piece of code is here because our testing framework does
-    // not support expecting exceptions.
-    $gotit = FALSE;
-    try {
-      $this->testConnection->copyDirectory($source, '/tmp');
-    }
-    catch (FileTransferException $e) {
-      $gotit = TRUE;
-    }
-    $this->assertTrue($gotit, 'Was not able to copy a directory outside of the jailed area.');
-
-    $gotit = TRUE;
-    try {
-      $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. file_directory_path());
-    }
-    catch (FileTransferException $e) {
-      $gotit = FALSE;
-    }
-    $this->assertTrue($gotit, 'Was able to copy a directory inside of the jailed area');
-  }
-}
-
-/**
- * Mock FileTransfer object for test case.
- */
-class TestFileTransfer extends FileTransfer {
-  protected $host = NULL;
-  protected $username = NULL;
-  protected $password = NULL;
-  protected $port = NULL;
-
-  /**
-   * This is for testing the CopyRecursive logic.
-   */
-  public $shouldIsDirectoryReturnTrue = FALSE;
-
-  function __construct($jail, $username, $password, $hostname = 'localhost', $port = 9999) {
-    parent::__construct($jail, $username, $password, $hostname, $port);
-  }
-
-  static function factory($jail, $settings) {
-    return new TestFileTransfer($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']);
-  }
-
-  function connect() {
-    $parts = explode(':', $this->hostname);
-    $port = (count($parts) == 2) ? $parts[1] : $this->port;
-    $this->connection = new MockTestConnection();
-    $this->connection->connectionString = 'test://' . urlencode($this->username) . ':' . urlencode($this->password) . "@$this->host:$this->port/";
-  }
-
-  function copyFileJailed($source, $destination) {
-    $this->connection->run("copyFile $source $destination");
-  }
-
-  protected function removeDirectoryJailed($directory) {
-    $this->connection->run("rmdir $directory");
-  }
-
-  function createDirectoryJailed($directory) {
-    $this->connection->run("mkdir $directory");
-  }
-
-  function removeFileJailed($destination) {
-    if (!ftp_delete($this->connection, $item)) {
-      throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
-    }
-  }
-
-  function isDirectory($path) {
-    return $this->shouldIsDirectoryReturnTrue;
-  }
-
-  function isFile($path) {
-    return FALSE;
-  }
-
-  function chmodJailed($path, $mode, $recursive) {
-    return;
-  }
-}
-
-/**
- * Mock connection object for test case.
- */
-class MockTestConnection {
-
-  var $commandsRun = array();
-  var $connectionString;
-
-  function run($cmd) {
-    $this->commandsRun[] = $cmd;
-  }
-
-  function flushCommands() {
-    $out = $this->commandsRun;
-    $this->commandsRun = array();
-    return $out;
-  }
-}
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
deleted file mode 100644
index 1dc8c7b..0000000
--- a/modules/simpletest/tests/form.test
+++ /dev/null
@@ -1,1226 +0,0 @@
-<?php
-// $Id: form.test,v 1.56 2010/07/26 13:07:59 dries Exp $
-
-/**
- * @file
- * Unit tests for the Drupal Form API.
- */
-
-class FormsTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Form element validation',
-      'description' => 'Tests various form element validation mechanisms.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Check several empty values for required forms elements.
-   *
-   * Carriage returns, tabs, spaces, and unchecked checkbox elements are not
-   * valid content for a required field.
-   *
-   * If the form field is found in form_get_errors() then the test pass.
-   */
-  function testRequiredFields() {
-    // Originates from http://drupal.org/node/117748
-    // Sets of empty strings and arrays.
-    $empty_strings = array('""' => "", '"\n"' => "\n", '" "' => " ", '"\t"' => "\t", '" \n\t "' => " \n\t ", '"\n\n\n\n\n"' => "\n\n\n\n\n");
-    $empty_arrays = array('array()' => array());
-    $empty_checkbox = array(NULL);
-
-    $elements['textfield']['element'] = array('#title' => $this->randomName(), '#type' => 'textfield');
-    $elements['textfield']['empty_values'] = $empty_strings;
-
-    $elements['password']['element'] = array('#title' => $this->randomName(), '#type' => 'password');
-    $elements['password']['empty_values'] = $empty_strings;
-
-    $elements['password_confirm']['element'] = array('#title' => $this->randomName(), '#type' => 'password_confirm');
-    $elements['password_confirm']['empty_values'] = $empty_strings;
-
-    $elements['textarea']['element'] = array('#title' => $this->randomName(), '#type' => 'textarea');
-    $elements['textarea']['empty_values'] = $empty_strings;
-
-    $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array($this->randomName(), $this->randomName(), $this->randomName()));
-    $elements['radios']['empty_values'] = $empty_arrays;
-
-    $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName());
-    $elements['checkbox']['empty_values'] = $empty_checkbox;
-
-    $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName()));
-    $elements['checkboxes']['empty_values'] = $empty_arrays;
-
-    $elements['select']['element'] = array('#title' => $this->randomName(), '#type' => 'select', '#options' => array($this->randomName(), $this->randomName(), $this->randomName()));
-    $elements['select']['empty_values'] = $empty_strings;
-
-    $elements['file']['element'] = array('#title' => $this->randomName(), '#type' => 'file');
-    $elements['file']['empty_values'] = $empty_strings;
-
-    // Regular expression to find the expected marker on required elements.
-    $required_marker_preg = '@<label.*<span class="form-required" title="This field is required\.">\*</span></label>@';
-
-    // Go through all the elements and all the empty values for them.
-    foreach ($elements as $type => $data) {
-      foreach ($data['empty_values'] as $key => $empty) {
-        foreach (array(TRUE, FALSE) as $required) {
-          $form_id = $this->randomName();
-          $form = array();
-          $form_state = form_state_defaults();
-          form_clear_error();
-          $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
-          $element = $data['element']['#title'];
-          $form[$element] = $data['element'];
-          $form[$element]['#required'] = $required;
-          $form_state['values'][$element] = $empty;
-          $form_state['input'] = $form_state['values'];
-          $form_state['input']['form_id'] = $form_id;
-          $form_state['method'] = 'post';
-          drupal_prepare_form($form_id, $form, $form_state);
-          drupal_process_form($form_id, $form, $form_state);
-          $errors = form_get_errors();
-          // Form elements of type 'radios' throw all sorts of PHP notices
-          // when you try to render them like this, so we ignore those for
-          // testing the required marker.
-          // @todo Fix this work-around (http://drupal.org/node/588438).
-          $form_output = ($type == 'radios') ? '' : drupal_render($form);
-          if ($required) {
-            // Make sure we have a form error for this element.
-            $this->assertTrue(isset($errors[$element]), "Check empty($key) '$type' field '$element'");
-            if (!empty($form_output)) {
-              // Make sure the form element is marked as required.
-              $this->assertTrue(preg_match($required_marker_preg, $form_output), "Required '$type' field is marked as required");
-            }
-          }
-          else {
-            if (!empty($form_output)) {
-              // Make sure the form element is *not* marked as required.
-              $this->assertFalse(preg_match($required_marker_preg, $form_output), "Optional '$type' field is not marked as required");
-            }
-            if ($type == 'select') {
-              // Select elements are going to have validation errors with empty
-              // input, since those are illegal choices. Just make sure the
-              // error is not "field is required".
-              $this->assertTrue((empty($errors[$element]) || strpos('field is required', $errors[$element]) === FALSE), "Optional '$type' field '$element' is not treated as a required element");
-            }
-            else {
-              // Make sure there is *no* form error for this element.
-              $this->assertTrue(empty($errors[$element]), "Optional '$type' field '$element' has no errors with empty input");
-            }
-          }
-        }
-      }
-    }
-    // Clear the expected form error messages so they don't appear as exceptions.
-    drupal_get_messages();
-  }
-
-  /**
-   * Test default value handling for checkboxes.
-   *
-   * @see _form_test_checkbox()
-   */
-  function testCheckboxProcessing() {
-    // First, try to submit without the required checkbox.
-    $edit = array();
-    $this->drupalPost('form-test/checkbox', $edit, t('Submit'));
-    $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
-
-    // Now try to submit the form correctly.
-    $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit')));
-    $expected_values = array(
-      'disabled_checkbox_on' => 'disabled_checkbox_on',
-      'disabled_checkbox_off' => '',
-      'checkbox_on' => 'checkbox_on',
-      'checkbox_off' => '',
-      'zero_checkbox_on' => '0',
-      'zero_checkbox_off' => '',
-    );
-    foreach ($expected_values as $widget => $expected_value) {
-      $this->assertEqual($values[$widget], $expected_value, t('Checkbox %widget returns expected value (expected: %expected, got: %value)', array(
-        '%widget' => var_export($widget, TRUE),
-        '%expected' => var_export($expected_value, TRUE),
-        '%value' => var_export($values[$widget], TRUE),
-      )));
-    }
-  }
-
-  /**
-   * Test handling of disabled elements.
-   *
-   * @see _form_test_disabled_elements()
-   */
-  function testDisabledElements() {
-    // Get the raw form in its original state.
-    $form_state = array();
-    $form = _form_test_disabled_elements(array(), $form_state);
-
-    // Build a submission that tries to hijack the form by submitting input for
-    // elements that are disabled.
-    $edit = array();
-    foreach (element_children($form) as $key) {
-      if (isset($form[$key]['#test_hijack_value'])) {
-        if (is_array($form[$key]['#test_hijack_value'])) {
-          foreach ($form[$key]['#test_hijack_value'] as $subkey => $value) {
-            $edit[$key . '[' . $subkey . ']'] = $value;
-          }
-        }
-        else {
-          $edit[$key] = $form[$key]['#test_hijack_value'];
-        }
-      }
-    }
-
-    // Submit the form with no input, as the browser does for disabled elements,
-    // and fetch the $form_state['values'] that is passed to the submit handler.
-    $this->drupalPost('form-test/disabled-elements', array(), t('Submit'));
-    $returned_values['normal'] = drupal_json_decode($this->content);
-
-    // Do the same with input, as could happen if JavaScript un-disables an
-    // element. drupalPost() emulates a browser by not submitting input for
-    // disabled elements, so we need to un-disable those elements first.
-    $this->drupalGet('form-test/disabled-elements');
-    $disabled_elements = array();
-    foreach ($this->xpath('//*[@disabled]') as $element) {
-      $disabled_elements[] = (string) $element['name'];
-      unset($element['disabled']);
-    }
-
-    // All the elements should be marked as disabled, including the ones below
-    // the disabled container.
-    $this->assertEqual(count($disabled_elements), 20, t('The correct elements have the disabled property in the HTML code.'));
-
-    $this->drupalPost(NULL, $edit, t('Submit'));
-    $returned_values['hijacked'] = drupal_json_decode($this->content);
-
-    // Ensure that the returned values match the form's default values in both
-    // cases.
-    foreach ($returned_values as $type => $values) {
-      $this->assertFormValuesDefault($values, $form);
-    }
-  }
-
-  /**
-   * Assert that the values submitted to a form matches the default values of the elements.
-   */
-  function assertFormValuesDefault($values, $form) {
-    foreach (element_children($form) as $key) {
-      if (isset($form[$key]['#default_value'])) {
-        $expected_value = $form[$key]['#default_value'];
-
-        if ($key == 'checkboxes_multiple') {
-          // Checkboxes values are not filtered out.
-          $values[$key] = array_filter($values[$key]);
-        }
-        $this->assertIdentical($expected_value, $values[$key], t('Default value for %type: expected %expected, returned %returned.', array('%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE))));
-      }
-
-      // Recurse children.
-      $this->assertFormValuesDefault($values, $form[$key]);
-    }
-  }
-
-  /**
-   * Test Form API protections against input forgery.
-   *
-   * @see _form_test_input_forgery()
-   */
-  function testInputForgery() {
-    $this->drupalGet('form-test/input-forgery');
-    $checkbox = $this->xpath('//input[@name="checkboxes[two]"]');
-    $checkbox[0]['value'] = 'FORGERY';
-    $this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit'));
-    $this->assertText('An illegal choice has been detected.', t('Input forgery was detected.'));
-  }
-}
-
-/**
- * Test form alter hooks.
- */
-class FormAlterTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Form alter hooks',
-      'description' => 'Tests hook_form_alter() and hook_form_FORM_ID_alter().',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter().
-   */
-  function testExecutionOrder() {
-    $this->drupalGet('form-test/alter');
-    // Ensure that the order is first by module, then for a given module, the
-    // id-specific one after the generic one.
-    $expected = array(
-      'block_form_form_test_alter_form_alter() executed.',
-      'form_test_form_alter() executed.',
-      'form_test_form_form_test_alter_form_alter() executed.',
-      'system_form_form_test_alter_form_alter() executed.',
-    );
-    $content = preg_replace('/\s+/', ' ', filter_xss($this->content, array()));
-    $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, t('Form alter hooks executed in the expected order.'));
-  }
-}
-
-/**
- * Test form validation handlers.
- */
-class FormValidationTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Form validation handlers',
-      'description' => 'Tests form processing and alteration via form validation handlers.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Tests form alterations by #element_validate, #validate, and form_set_value().
-   */
-  function testValidate() {
-    $this->drupalGet('form-test/validate');
-    // Verify that #element_validate handlers can alter the form and submitted
-    // form values.
-    $edit = array(
-      'name' => 'element_validate',
-    );
-    $this->drupalPost(NULL, $edit, 'Save');
-    $this->assertFieldByName('name', '#value changed by #element_validate', t('Form element #value was altered.'));
-    $this->assertText('Name value: value changed by form_set_value() in #element_validate', t('Form element value in $form_state was altered.'));
-
-    // Verify that #validate handlers can alter the form and submitted
-    // form values.
-    $edit = array(
-      'name' => 'validate',
-    );
-    $this->drupalPost(NULL, $edit, 'Save');
-    $this->assertFieldByName('name', '#value changed by #validate', t('Form element #value was altered.'));
-    $this->assertText('Name value: value changed by form_set_value() in #validate', t('Form element value in $form_state was altered.'));
-
-    // Verify that #element_validate handlers can make form elements
-    // inaccessible, but values persist.
-    $edit = array(
-      'name' => 'element_validate_access',
-    );
-    $this->drupalPost(NULL, $edit, 'Save');
-    $this->assertNoFieldByName('name', t('Form element was hidden.'));
-    $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.'));
-
-    // Verify that value for inaccessible form element persists.
-    $this->drupalPost(NULL, array(), 'Save');
-    $this->assertNoFieldByName('name', t('Form element was hidden.'));
-    $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.'));
-  }
-
-  /**
-   * Tests partial form validation through #limit_validation_errors.
-   */
-  function testValidateLimitErrors() {
-    $edit = array('test' => 'invalid');
-    $path = 'form-test/limit-validation-errors';
-
-    // Submit the form by pressing the button with #limit_validation_errors and
-    // ensure that the title field is not validated, but the #element_validate
-    // handler for the 'test' field is triggered.
-    $this->drupalPost($path, $edit, t('Partial validate'));
-    $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
-    $this->assertText('Test element is invalid');
-
-    // Now test full form validation and ensure that the #element_validate
-    // handler is still triggered.
-    $this->drupalPost($path, $edit, t('Full validate'));
-    $this->assertText(t('!name field is required.', array('!name' => 'Title')));
-    $this->assertText('Test element is invalid');
-  }
-}
-
-/**
- * Test form element labels, required markers and associated output.
- */
-class FormsElementsLabelsTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Form element and label output test',
-      'description' => 'Test form element labels, required markers and associated output.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Test form elements, labels, title attibutes and required marks output
-   * correctly and have the correct label option class if needed.
-   */
-  function testFormLabels() {
-    $this->drupalGet('form_test/form-labels');
-
-    // Check that the checkbox/radio processing is not interfering with
-    // basic placement.
-    $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular checkboxes."));
-
-    $elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular radios."));
-
-    // Exercise various defaults for checkboxes and modifications to ensure
-    // appropriate override and correct behaviour.
-    $elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for a checkbox by default."));
-
-    // Exercise various defaults for textboxes and modifications to ensure
-    // appropriate override and correct behaviour.
-    $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required"]/child::span[@class="form-required"]/parent::*/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
-    $this->assertTrue(isset($elements[0]), t("Label preceeds textfield, with required marker inside label."));
-
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required"]/span[@class="form-required"]');
-    $this->assertTrue(isset($elements[0]), t("Label tag with required marker preceeds required textfield with no title."));
-
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="form-required"]');
-    $this->assertFalse(isset($elements[0]), t("No required marker on non-required field."));
-
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), t("Label after field and label option class correct for text field."));
-
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/following-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="element-invisible"]');
-    $this->assertTrue(isset($elements[0]), t("Label after field and label class is element-invisible."));
-
-    $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]');
-    $this->assertFalse(isset($elements[0]), t("No label tag when title set not to display."));
-
-    // Check #field_prefix and #field_suffix placement.
-    $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]');
-    $this->assertTrue(isset($elements[0]), t("Properly placed the #field_prefix element after the label and before the field."));
-
-    $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]');
-    $this->assertTrue(isset($elements[0]), t("Properly places the #field_suffix element immediately after the form field."));
-
-    // Check #prefix and #suffix placement.
-    $elements = $this->xpath('//div[@id="form-test-textfield-title-prefix"]/following-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]');
-    $this->assertTrue(isset($elements[0]), t("Properly places the #prefix element before the form item."));
-
-    $elements = $this->xpath('//div[@id="form-test-textfield-title-suffix"]/preceding-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]');
-    $this->assertTrue(isset($elements[0]), t("Properly places the #suffix element before the form item."));
-  }
-}
-
-/**
- * Test the tableselect form element for expected behavior.
- */
-class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Tableselect form element type test',
-      'description' => 'Test the tableselect element for expected behavior',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-
-  /**
-   * Test the display of checkboxes when #multiple is TRUE.
-   */
-  function testMultipleTrue() {
-
-    $this->drupalGet('form_test/tableselect/multiple-true');
-
-    $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.'));
-
-    // Test for the presence of the Select all rows tableheader.
-    $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Presence of the "Select all" checkbox.'));
-
-    $rows = array('row1', 'row2', 'row3');
-    foreach ($rows as $row) {
-      $this->assertFieldByXPath('//input[@type="checkbox"]', $row, t('Checkbox for value @row.', array('@row' => $row)));
-    }
-  }
-
-  /**
-   * Test the display of radios when #multiple is FALSE.
-   */
-  function testMultipleFalse() {
-    $this->drupalGet('form_test/tableselect/multiple-false');
-
-    $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.'));
-
-    // Test for the absence of the Select all rows tableheader.
-    $this->assertNoFieldByXPath('//th[@class="select-all"]', '', t('Absence of the "Select all" checkbox.'));
-
-    $rows = array('row1', 'row2', 'row3');
-    foreach ($rows as $row) {
-      $this->assertFieldByXPath('//input[@type="radio"]', $row, t('Radio button for value @row.', array('@row' => $row)));
-    }
-  }
-
-  /**
-   * Test the display of the #empty text when #options is an empty array.
-   */
-  function testEmptyText() {
-    $this->drupalGet('form_test/tableselect/empty-text');
-    $this->assertText(t('Empty text.'), t('Empty text should be displayed.'));
-  }
-
-  /**
-   * Test the submission of single and multiple values when #multiple is TRUE.
-   */
-  function testMultipleTrueSubmit() {
-
-    // Test a submission with one checkbox checked.
-    $edit = array();
-    $edit['tableselect[row1]'] = TRUE;
-    $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit');
-
-    $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1'));
-    $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.'));
-    $this->assertText(t('Submitted: row3 = 0'), t('Unchecked checkbox row3.'));
-
-    // Test a submission with multiple checkboxes checked.
-    $edit['tableselect[row1]'] = TRUE;
-    $edit['tableselect[row3]'] = TRUE;
-    $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit');
-
-    $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1.'));
-    $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.'));
-    $this->assertText(t('Submitted: row3 = row3'), t('Checked checkbox row3.'));
-
-  }
-
-  /**
-   * Test submission of values when #multiple is FALSE.
-   */
-  function testMultipleFalseSubmit() {
-    $edit['tableselect'] = 'row1';
-    $this->drupalPost('form_test/tableselect/multiple-false', $edit, 'Submit');
-    $this->assertText(t('Submitted: row1'), t('Selected radio button'));
-  }
-
-  /**
-   * Test the #js_select property.
-   */
-  function testAdvancedSelect() {
-    // When #multiple = TRUE a Select all checkbox should be displayed by default.
-    $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-default');
-    $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Display a "Select all" checkbox by default when #multiple is TRUE.'));
-
-    // When #js_select is set to FALSE, a "Select all" checkbox should not be displayed.
-    $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-no-advanced-select');
-    $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #js_select is FALSE.'));
-
-    // A "Select all" checkbox never makes sense when #multiple = FALSE, regardless of the value of #js_select.
-    $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-default');
-    $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE.'));
-
-    $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-advanced-select');
-    $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE, even when #js_select is TRUE.'));
-  }
-
-
-  /**
-   * Test the whether the option checker gives an error on invalid tableselect values for checkboxes.
-   */
-  function testMultipleTrueOptionchecker() {
-
-    list($header, $options) = _form_test_tableselect_get_data();
-
-    $form['tableselect'] = array(
-      '#type' => 'tableselect',
-      '#header' => $header,
-      '#options' => $options,
-    );
-
-    // Test with a valid value.
-    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1'));
-    $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for checkboxes.'));
-
-    // Test with an invalid value.
-    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value'));
-    $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for checkboxes.'));
-
-  }
-
-
-  /**
-   * Test the whether the option checker gives an error on invalid tableselect values for radios.
-   */
-  function testMultipleFalseOptionchecker() {
-
-    list($header, $options) = _form_test_tableselect_get_data();
-
-    $form['tableselect'] = array(
-      '#type' => 'tableselect',
-      '#header' => $header,
-      '#options' => $options,
-      '#multiple' => FALSE,
-    );
-
-    // Test with a valid value.
-    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1'));
-    $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for radio buttons.'));
-
-    // Test with an invalid value.
-    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value'));
-    $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for radio buttons.'));
-  }
-
-
-  /**
-   * Helper function for the option check test to submit a form while collecting errors.
-   *
-   * @param $form_element
-   *   A form element to test.
-   * @param $edit
-   *   An array containing post data.
-   *
-   * @return
-   *   An array containing the processed form, the form_state and any errors.
-   */
-  private function formSubmitHelper($form, $edit) {
-    $form_id = $this->randomName();
-    $form_state = form_state_defaults();
-
-    $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
-
-    $form_state['input'] = $edit;
-    $form_state['input']['form_id'] = $form_id;
-
-    drupal_prepare_form($form_id, $form, $form_state);
-
-    drupal_process_form($form_id, $form, $form_state);
-
-    $errors = form_get_errors();
-
-    // Clear errors and messages.
-    drupal_get_messages();
-    form_clear_error();
-
-    // Return the processed form together with form_state and errors
-    // to allow the caller lowlevel access to the form.
-    return array($form, $form_state, $errors);
-  }
-
-}
-
-/**
- * Test the form storage on a multistep form.
- *
- * The tested form puts data into the storage during the initial form
- * construction. These tests verify that there are no duplicate form
- * constructions, with and without manual form caching activiated. Furthermore
- * when a validation error occurs, it makes sure that changed form element
- * values aren't lost due to a wrong form rebuild.
- */
-class FormsFormStorageTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name'  => 'Multistep form using form storage',
-      'description'  => 'Tests a multistep form using form storage and makes sure validation and caching works right.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-
-    $this->web_user = $this->drupalCreateUser(array('access content'));
-    $this->drupalLogin($this->web_user);
-  }
-
-  /**
-   * Tests using the form in a usual way.
-   */
-  function testForm() {
-    $this->drupalGet('form_test/form-storage');
-    $this->assertText('Form constructions: 1');
-
-    $edit = array('title' => 'new', 'value' => 'value_is_set');
-    // Reload the form, but don't rebuild.
-    $this->drupalPost(NULL, $edit, 'Reload');
-    $this->assertText('Form constructions: 2');
-
-    // Now use form rebuilding triggered by a submit button.
-    $this->drupalPost(NULL, $edit, 'Continue submit');
-    $this->assertText('Form constructions: 3');
-    $this->assertText('Form constructions: 4');
-
-    // Reset the form to the values of the storage, using a form rebuild
-    // triggered by button of type button.
-    $this->drupalPost(NULL, array('title' => 'changed'), 'Reset');
-    $this->assertFieldByName('title', 'new', 'Values have been resetted.');
-    // After rebuilding, the form has been cached.
-    $this->assertText('Form constructions: 5');
-
-    $this->drupalPost(NULL, $edit, 'Save');
-    $this->assertText('Form constructions: 5');
-    $this->assertText('Title: new', t('The form storage has stored the values.'));
-  }
-
-  /**
-   * Tests using the form with an activated $form_state['cache'] property.
-   */
-  function testFormCached() {
-    $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1)));
-    $this->assertText('Form constructions: 1');
-
-    $edit = array('title' => 'new', 'value' => 'value_is_set');
-    // Reload the form, but don't rebuild.
-    $this->drupalPost(NULL, $edit, 'Reload');
-    $this->assertNoText('Form constructions');
-
-    // Now use form rebuilding triggered by a submit button.
-    $this->drupalPost(NULL, $edit, 'Continue submit');
-    $this->assertText('Form constructions: 2');
-
-    // Reset the form to the values of the storage, using a form rebuild
-    // triggered by button of type button.
-    $this->drupalPost(NULL, array('title' => 'changed'), 'Reset');
-    $this->assertFieldByName('title', 'new', 'Values have been resetted.');
-    $this->assertText('Form constructions: 3');
-
-    $this->drupalPost(NULL, $edit, 'Save');
-    $this->assertText('Form constructions: 3');
-    $this->assertText('Title: new', t('The form storage has stored the values.'));
-  }
-
-  /**
-   * Tests validation when form storage is used.
-   */
-  function testValidation() {
-    $this->drupalPost('form_test/form-storage', array('title' => '', 'value' => 'value_is_set'), 'Continue submit');
-    $this->assertPattern('/value_is_set/', t('The input values have been kept.'));
-  }
-
-  /**
-   * Tests updating cached form storage during form validation.
-   *
-   * If form caching is enabled and a form stores data in the form storage, then
-   * the form storage also has to be updated in case of a validation error in
-   * the form. This test re-uses the existing form for multi-step tests, but
-   * triggers a special #element_validate handler to update the form storage
-   * during form validation, while another, required element in the form
-   * triggers a form validation error.
-   */
-  function testCachedFormStorageValidation() {
-    // Request the form with 'cache' query parameter to enable form caching.
-    $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1)));
-
-    // Skip step 1 of the multi-step form, since the first step copies over
-    // 'title' into form storage, but we want to verify that changes in the form
-    // storage are updated in the cache during form validation.
-    $edit = array('title' => 'foo');
-    $this->drupalPost(NULL, $edit, 'Continue submit');
-
-    // In step 2, trigger a validation error for the required 'title' field, and
-    // post the special 'change_title' value for the 'value' field, which
-    // conditionally invokes the #element_validate handler to update the form
-    // storage.
-    $edit = array('title' => '', 'value' => 'change_title');
-    $this->drupalPost(NULL, $edit, 'Save');
-
-    // At this point, the form storage should contain updated values, but we do
-    // not see them, because the form has not been rebuilt yet due to the
-    // validation error. Post again and verify that the rebuilt form contains
-    // the values of the updated form storage.
-    $this->drupalPost(NULL, array('title' => 'foo', 'value' => 'bar'), 'Save');
-    $this->assertText("The thing has been changed.", 'The altered form storage value was updated in cache and taken over.');
-  }
-
-  /**
-   * Tests a form using form state without using 'storage' to pass data from the
-   * constructor to a submit handler. The data has to persist even when caching
-   * gets activated, what may happen when a modules alter the form and adds
-   * #ajax properties.
-   */
-  function testFormStatePersist() {
-    // Test the form one time with caching activated and one time without.
-    $run_options = array(
-      array(),
-      array('query' => array('cache' => 1)),
-    );
-    foreach($run_options as $options) {
-      $this->drupalPost('form-test/state-persist', array(), t('Submit'), $options);
-      // The submit handler outputs the value in $form_state, assert it's there.
-      $this->assertText('State persisted.');
-
-      // Test it again, but first trigger a validation error, then test.
-      $this->drupalPost('form-test/state-persist', array('title' => ''), t('Submit'), $options);
-      $this->assertText(t('!name field is required.', array('!name' => 'title')));
-      // Submit the form again triggering no validation error.
-      $this->drupalPost(NULL, array('title' => 'foo'), t('Submit'), $options);
-      $this->assertText('State persisted.');
-
-      // Now post to the rebuilt form and verify it's still there afterwards.
-      $this->drupalPost(NULL, array('title' => 'bar'), t('Submit'), $options);
-      $this->assertText('State persisted.');
-    }
-  }
-}
-
-/**
- * Test wrapper form callbacks.
- */
-class FormsFormWrapperTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Form wrapper callback',
-      'description' => 'Tests form wrapper callbacks to pass a prebuilt form to form builder functions.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Tests using the form in a usual way.
-   */
-  function testWrapperCallback() {
-    $this->drupalGet('form_test/wrapper-callback');
-    $this->assertText('Form wrapper callback element output.', t('The form contains form wrapper elements.'));
-    $this->assertText('Form builder element output.', t('The form contains form builder elements.'));
-  }
-}
-
-/**
- * Test $form_state clearance.
- */
-class FormStateValuesCleanTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Form state values clearance',
-      'description' => 'Test proper removal of submitted form values using form_state_values_clean().',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Tests form_state_values_clean().
-   */
-  function testFormStateValuesClean() {
-    $values = drupal_json_decode($this->drupalPost('form_test/form-state-values-clean', array(), t('Submit')));
-
-    // Setup the expected result.
-    $result = array(
-      'beer' => 1000,
-      'baz' => array('beer' => 2000),
-    );
-
-    // Verify that all internal Form API elements were removed.
-    $this->assertFalse(isset($values['form_id']), t('%element was removed.', array('%element' => 'form_id')));
-    $this->assertFalse(isset($values['form_token']), t('%element was removed.', array('%element' => 'form_token')));
-    $this->assertFalse(isset($values['form_build_id']), t('%element was removed.', array('%element' => 'form_build_id')));
-    $this->assertFalse(isset($values['op']), t('%element was removed.', array('%element' => 'op')));
-
-    // Verify that all buttons were removed.
-    $this->assertFalse(isset($values['foo']), t('%element was removed.', array('%element' => 'foo')));
-    $this->assertFalse(isset($values['bar']), t('%element was removed.', array('%element' => 'bar')));
-    $this->assertFalse(isset($values['baz']['foo']), t('%element was removed.', array('%element' => 'foo')));
-    $this->assertFalse(isset($values['baz']['baz']), t('%element was removed.', array('%element' => 'baz')));
-
-    // Verify that nested form value still exists.
-    $this->assertTrue(isset($values['baz']['beer']), t('Nested form value still exists.'));
-
-    // Verify that actual form values equal resulting form values.
-    $this->assertEqual($values, $result, t('Expected form values equal actual form values.'));
-  }
-}
-
-/**
- * Tests form rebuilding.
- *
- * @todo Add tests for other aspects of form rebuilding.
- */
-class FormsRebuildTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Form rebuilding',
-      'description' => 'Tests functionality of drupal_rebuild_form().',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-
-    $this->web_user = $this->drupalCreateUser(array('access content'));
-    $this->drupalLogin($this->web_user);
-  }
-
-  /**
-   * Tests preservation of values.
-   */
-  function testRebuildPreservesValues() {
-    $edit = array(
-      'checkbox_1_default_off' => TRUE,
-      'checkbox_1_default_on' => FALSE,
-      'text_1' => 'foo',
-    );
-    $this->drupalPost('form-test/form-rebuild-preserve-values', $edit, 'Add more');
-
-    // Verify that initial elements retained their submitted values.
-    $this->assertFieldChecked('edit-checkbox-1-default-off', t('A submitted checked checkbox retained its checked state during a rebuild.'));
-    $this->assertNoFieldChecked('edit-checkbox-1-default-on', t('A submitted unchecked checkbox retained its unchecked state during a rebuild.'));
-    $this->assertFieldById('edit-text-1', 'foo', t('A textfield retained its submitted value during a rebuild.'));
-
-    // Verify that newly added elements were initialized with their default values.
-    $this->assertFieldChecked('edit-checkbox-2-default-on', t('A newly added checkbox was initialized with a default checked state.'));
-    $this->assertNoFieldChecked('edit-checkbox-2-default-off', t('A newly added checkbox was initialized with a default unchecked state.'));
-    $this->assertFieldById('edit-text-2', 'DEFAULT 2', t('A newly added textfield was initialized with its default value.'));
-  }
-
-  /**
-   * Tests that a form's action is retained after an AJAX submission.
-   *
-   * The 'action' attribute of a form should not change after an AJAX submission
-   * followed by a non-AJAX submission, which triggers a validation error.
-   */
-  function testPreserveFormActionAfterAJAX() {
-    // Create a multi-valued field for 'page' nodes to use for AJAX testing.
-    $field_name = 'field_ajax_test';
-    $field = array(
-      'field_name' => $field_name,
-      'type' => 'text',
-      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
-    );
-    field_create_field($field);
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => 'node',
-      'bundle' => 'page',
-    );
-    field_create_instance($instance);
-
-    // Log in a user who can create 'page' nodes.
-    $this->web_user = $this->drupalCreateUser(array('create page content'));
-    $this->drupalLogin($this->web_user);
-
-    // Get the form for adding a 'page' node. Submit an "add another item" AJAX
-    // submission and verify it worked by ensuring the updated page has two text
-    // field items in the field for which we just added an item.
-    $this->drupalGet('node/add/page');
-    preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
-    $settings = drupal_json_decode($matches[1]);
-    $button = $this->xpath('//input[@name="field_ajax_test_add_more"]');
-    $button_id = (string) $button[0]['id'];
-    $this->drupalPostAJAX(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), 'system/ajax', array(), array(), 'page-node-form', $settings['ajax'][$button_id]);
-    $this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, t('AJAX submission succeeded.'));
-
-    // Submit the form with the non-AJAX "Save" button, leaving the title field
-    // blank to trigger a validation error, and ensure that a validation error
-    // occurred, because this test is for testing what happens when a form is
-    // re-rendered without being re-built, which is what happens when there's
-    // a validation error.
-    $this->drupalPost(NULL, array(), t('Save'));
-    $this->assertText('Title field is required.', t('Non-AJAX submission correctly triggered a validation error.'));
-
-    // Ensure that the form's action is correct.
-    $forms = $this->xpath('//form[contains(@class, "node-page-form")]');
-    $this->assert(count($forms) == 1 && $forms[0]['action'] == url('node/add/page'), t('Re-rendered form contains the correct action value.'));
-  }
-}
-
-/**
- * Test the programmatic form submission behavior.
- */
-class FormsProgrammaticTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Programmatic form submissions',
-      'description' => 'Test the programmatic form submission behavior.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Test the programmatic form submission workflow.
-   */
-  function testSubmissionWorkflow() {
-    // Backup the current batch status and reset it to avoid conflicts while
-    // processing the dummy form submit handler.
-    $current_batch = $batch =& batch_get();
-    $batch = array();
-
-    // Test that a programmatic form submission is rejected when a required
-    // textfield is omitted and correctly processed when it is provided.
-    $this->submitForm(array(), FALSE);
-    $this->submitForm(array('textfield' => 'test 1'), TRUE);
-    $this->submitForm(array(), FALSE);
-    $this->submitForm(array('textfield' => 'test 2'), TRUE);
-
-    // Test that a programmatic form submission can turn on and off checkboxes
-    // which are, by default, checked.
-    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => 2)), TRUE);
-    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => NULL)), TRUE);
-    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => 2)), TRUE);
-    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => NULL)), TRUE);
-
-    // Restore the current batch status.
-    $batch = $current_batch;
-  }
-
-  /**
-   * Helper function used to programmatically submit the form defined in
-   * form_test.module with the given values.
-   *
-   * @param $values
-   *   An array of field values to be submitted.
-   * @param $valid_input
-   *   A boolean indicating whether or not the form submission is expected to
-   *   be valid.
-   */
-  private function submitForm($values, $valid_input) {
-    // Programmatically submit the given values.
-    $form_state = array('values' => $values);
-    drupal_form_submit('form_test_programmatic_form', $form_state);
-
-    // Check that the form returns an error when expected, and vice versa.
-    $errors = form_get_errors();
-    $valid_form = empty($errors);
-    $args = array(
-      '%values' => print_r($values, TRUE),
-      '%errors' => $valid_form ? t('None') : implode(' ', $errors),
-    );
-    $this->assertTrue($valid_input == $valid_form, t('Input values: %values<br/>Validation handler errors: %errors', $args));
-
-    // We check submitted values only if we have a valid input.
-    if ($valid_input) {
-      // By fetching the values from $form_state['storage'] we ensure that the
-      // submission handler was properly executed.
-      $stored_values = $form_state['storage']['programmatic_form_submit'];
-      foreach ($values as $key => $value) {
-        $this->assertTrue(isset($stored_values[$key]) && $stored_values[$key] == $value, t('Submission handler correctly executed: %stored_key is %stored_value', array('%stored_key' => $key, '%stored_value' => print_r($value, TRUE))));
-      }
-    }
-  }
-}
-
-/**
- * Test that FAPI correctly determines $form_state['triggering_element'].
- */
-class FormsTriggeringElementTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Form triggering element determination',
-      'description' => 'Test the determination of $form_state[\'triggering_element\'].',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Test the determination of $form_state['triggering_element'] when no button
-   * information is included in the POST data, as is sometimes the case when
-   * the ENTER key is pressed in a textfield in Internet Explorer.
-   */
-  function testNoButtonInfoInPost() {
-    $path = 'form-test/clicked-button';
-    $edit = array();
-    $form_html_id = 'form-test-clicked-button';
-
-    // Ensure submitting a form with no buttons results in no
-    // $form_state['triggering_element'] and the form submit handler not
-    // running.
-    $this->drupalPost($path, $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('There is no clicked button.', t('$form_state[\'triggering_element\'] set to NULL.'));
-    $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.'));
-
-    // Ensure submitting a form with one or more submit buttons results in
-    // $form_state['triggering_element'] being set to the first one the user has
-    // access to. An argument with 'r' in it indicates a restricted
-    // (#access=FALSE) button.
-    $this->drupalPost($path . '/s', $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to only button.'));
-    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
-
-    $this->drupalPost($path . '/s/s', $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
-    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
-
-    $this->drupalPost($path . '/rs/s', $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] set to first available button.'));
-    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
-
-    // Ensure submitting a form with buttons of different types results in
-    // $form_state['triggering_element'] being set to the first button,
-    // regardless of type. For the FAPI 'button' type, this should result in the
-    // submit handler not executing. The types are 's'(ubmit), 'b'(utton), and
-    // 'i'(mage_button).
-    $this->drupalPost($path . '/s/b/i', $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
-    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
-
-    $this->drupalPost($path . '/b/s/i', $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
-    $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.'));
-
-    $this->drupalPost($path . '/i/s/b', $edit, NULL, array(), array(), $form_html_id);
-    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
-    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
-  }
-
-  /**
-   * Test that $form_state['triggering_element'] does not get set to a button
-   * with #access=FALSE.
-   */
-  function testAttemptAccessControlBypass() {
-    $path = 'form-test/clicked-button';
-    $form_html_id = 'form-test-clicked-button';
-
-    // Retrieve a form where 'button1' has #access=FALSE and 'button2' doesn't.
-    $this->drupalGet($path . '/rs/s');
-
-    // Submit the form with 'button1=button1' in the POST data, which someone
-    // trying to get around security safeguards could easily do. We have to do
-    // a little trickery here, to work around the safeguards in drupalPost(): by
-    // renaming the text field that is in the form to 'button1', we can get the
-    // data we want into $_POST.
-    $elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="text"]');
-    $elements[0]['name'] = 'button1';
-    $this->drupalPost(NULL, array('button1' => 'button1'), NULL, array(), array(), $form_html_id);
-
-    // Ensure that $form_state['triggering_element'] was not set to the
-    // restricted button. Do this with both a negative and positive assertion,
-    // because negative assertions alone can be brittle. See
-    // testNoButtonInfoInPost() for why the triggering element gets set to
-    // 'button2'.
-    $this->assertNoText('The clicked button is button1.', t('$form_state[\'triggering_element\'] not set to a restricted button.'));
-    $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] not set to a restricted button.'));
-  }
-}
-
-/**
- * Tests rebuilding of arbitrary forms by altering them.
- */
-class FormsArbitraryRebuildTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Rebuild arbitrary forms',
-      'description' => 'Tests altering forms to be rebuilt so there are multiple steps.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-    // Auto-create a field for testing.
-    $field = array(
-      'field_name' => 'test_multiple',
-      'type' => 'text',
-      'cardinality' => -1,
-      'translatable' => FALSE,
-    );
-    field_create_field($field);
-
-    $instance = array(
-      'entity_type' => 'node',
-      'field_name' => 'test_multiple',
-      'bundle' => 'page',
-      'label' => 'Test a multiple valued field',
-      'widget' => array(
-        'type' => 'text_textfield',
-        'weight' => 0,
-      ),
-    );
-    field_create_instance($instance);
-    variable_set('user_register', USER_REGISTER_VISITORS);
-  }
-
-  /**
-   * Tests a basic rebuild with the user registration form.
-   */
-  function testUserRegistrationRebuild() {
-    $edit = array(
-      'name' => 'foo',
-      'mail' => 'bar@example.com',
-    );
-    $this->drupalPost('user/register', $edit, 'Rebuild');
-    $this->assertText('Form rebuilt.');
-    $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.');
-    $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.');
-  }
-
-  /**
-   * Tests a rebuild caused by a multiple value field.
-   */
-  function testUserRegistrationMultipleField() {
-    $edit = array(
-      'name' => 'foo',
-      'mail' => 'bar@example.com',
-    );
-    $this->drupalPost('user/register', $edit, t('Add another item'), array('query' => array('field' => TRUE)));
-    $this->assertText('Test a multiple valued field', 'Form has been rebuilt.');
-    $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.');
-    $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.');
-  }
-}
-
-/**
- * Tests form API file inclusion.
- */
-class FormsFileInclusionTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Form API file inclusion',
-      'description' => 'Tests form API file inclusion.',
-      'group' => 'Form API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('form_test');
-  }
-
-  /**
-   * Tests loading an include specified in hook_menu().
-   */
-  function testLoadMenuInclude() {
-    $this->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), 'system/ajax', array(), array(), 'form-test-load-include-menu');
-    $this->assertText('Submit callback called.');
-  }
-
-  /**
-   * Tests loading a custom specified inlcude.
-   */
-  function testLoadCustomInclude() {
-    $this->drupalPost('form-test/load-include-custom', array(), t('Save'));
-    $this->assertText('Submit callback called.');
-  }
-}
diff --git a/modules/simpletest/tests/graph.test b/modules/simpletest/tests/graph.test
deleted file mode 100644
index 1751fe2..0000000
--- a/modules/simpletest/tests/graph.test
+++ /dev/null
@@ -1,194 +0,0 @@
-<?php
-// $Id: graph.test,v 1.7 2010/05/18 18:11:13 dries Exp $
-
-/**
- * @file
- * Provides unit tests for graph.inc.
- */
-
-/**
- * Unit tests for the graph handling features.
- */
-class GraphUnitTest extends DrupalUnitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Graph',
-      'description' => 'Graph handling unit tests.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Test depth-first-search features.
-   */
-  function testDepthFirstSearch() {
-    // Provoke the inclusion of graph.inc.
-    require_once DRUPAL_ROOT . '/includes/graph.inc';
-
-    // The sample graph used is:
-    // 1 --> 2 --> 3     5 ---> 6
-    //       |     ^     ^
-    //       |     |     |
-    //       |     |     |
-    //       +---> 4 <-- 7      8 ---> 9
-    $graph = $this->normalizeGraph(array(
-      1 => array(2),
-      2 => array(3, 4),
-      3 => array(),
-      4 => array(3),
-      5 => array(6),
-      7 => array(4, 5),
-      8 => array(9),
-      9 => array(),
-    ));
-    drupal_depth_first_search($graph);
-
-    $expected_paths = array(
-      1 => array(2, 3, 4),
-      2 => array(3, 4),
-      3 => array(),
-      4 => array(3),
-      5 => array(6),
-      7 => array(4, 3, 5, 6),
-      8 => array(9),
-      9 => array(),
-    );
-    $this->assertPaths($graph, $expected_paths);
-
-    $expected_reverse_paths = array(
-      1 => array(),
-      2 => array(1),
-      3 => array(2, 1, 4, 7),
-      4 => array(2, 1, 7),
-      5 => array(7),
-      7 => array(),
-      8 => array(),
-      9 => array(8),
-    );
-    $this->assertReversePaths($graph, $expected_reverse_paths);
-
-    // Assert that DFS didn't created "missing" vertexes automatically.
-    $this->assertFALSE(isset($graph[6]), t('Vertex 6 has not been created'));
-
-    $expected_components = array(
-      array(1, 2, 3, 4, 5, 7),
-      array(8, 9),
-    );
-    $this->assertComponents($graph, $expected_components);
-
-    $expected_weights = array(
-      array(1, 2, 3),
-      array(2, 4, 3),
-      array(7, 4, 3),
-      array(7, 5),
-      array(8, 9),
-    );
-    $this->assertWeights($graph, $expected_weights);
-  }
-
-  /**
-   * Return a normalized version of a graph.
-   */
-  function normalizeGraph($graph) {
-    $normalized_graph = array();
-    foreach ($graph as $vertex => $edges) {
-      // Create vertex even if it hasn't any edges.
-      $normalized_graph[$vertex] = array();
-      foreach ($edges as $edge) {
-        $normalized_graph[$vertex]['edges'][$edge] = TRUE;
-      }
-    }
-    return $normalized_graph;
-  }
-
-  /**
-   * Verify expected paths in a graph.
-   *
-   * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
-   * @param $expected_paths
-   *   An associative array containing vertices with their expected paths.
-   */
-  function assertPaths($graph, $expected_paths) {
-    foreach ($expected_paths as $vertex => $paths) {
-      // Build an array with keys = $paths and values = TRUE.
-      $expected = array_fill_keys($paths, TRUE);
-      $result = isset($graph[$vertex]['paths']) ? $graph[$vertex]['paths'] : array();
-      $this->assertEqual($expected, $result, t('Expected paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE))));
-    }
-  }
-
-  /**
-   * Verify expected reverse paths in a graph.
-   *
-   * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
-   * @param $expected_reverse_paths
-   *   An associative array containing vertices with their expected reverse
-   *   paths.
-   */
-  function assertReversePaths($graph, $expected_reverse_paths) {
-    foreach ($expected_reverse_paths as $vertex => $paths) {
-      // Build an array with keys = $paths and values = TRUE.
-      $expected = array_fill_keys($paths, TRUE);
-      $result = isset($graph[$vertex]['reverse_paths']) ? $graph[$vertex]['reverse_paths'] : array();
-      $this->assertEqual($expected, $result, t('Expected reverse paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE))));
-    }
-  }
-
-  /**
-   * Verify expected components in a graph.
-   *
-   * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
-   * @param $expected_components
-   *   An array containing of components defined as a list of their vertices.
-   */
-  function assertComponents($graph, $expected_components) {
-    $unassigned_vertices = array_fill_keys(array_keys($graph), TRUE);
-    foreach ($expected_components as $component) {
-      $result_components = array();
-      foreach ($component as $vertex) {
-        $result_components[] = $graph[$vertex]['component'];
-        unset($unassigned_vertices[$vertex]);
-      }
-      $this->assertEqual(1, count(array_unique($result_components)), t('Expected one unique component for vertices @vertices, got @components', array('@vertices' => $this->displayArray($component), '@components' => $this->displayArray($result_components))));
-    }
-    $this->assertEqual(array(), $unassigned_vertices, t('Vertices not assigned to a component: @vertices', array('@vertices' => $this->displayArray($unassigned_vertices, TRUE))));
-  }
-
-  /**
-   * Verify expected order in a graph.
-   *
-   * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
-   * @param $expected_orders
-   *   An array containing lists of vertices in their expected order.
-   */
-  function assertWeights($graph, $expected_orders) {
-    foreach ($expected_orders as $order) {
-      $previous_vertex = array_shift($order);
-      foreach ($order as $vertex) {
-        $this->assertTrue($graph[$previous_vertex]['weight'] < $graph[$vertex]['weight'], t('Weights of @previous-vertex and @vertex are correct relative to each other', array('@previous-vertex' => $previous_vertex, '@vertex' => $vertex)));
-      }
-    }
-  }
-
-  /**
-   * Helper function to output vertices as comma-separated list.
-   *
-   * @param $paths
-   *   An array containing a list of vertices.
-   * @param $keys
-   *   (optional) Whether to output the keys of $paths instead of the values.
-   */
-  function displayArray($paths, $keys = FALSE) {
-    if (!empty($paths)) {
-      return implode(', ', $keys ? array_keys($paths) : $paths);
-    }
-    else {
-      return '(empty)';
-    }
-  }
-}
-
diff --git a/modules/simpletest/tests/image.test b/modules/simpletest/tests/image.test
deleted file mode 100644
index d1b7fc7..0000000
--- a/modules/simpletest/tests/image.test
+++ /dev/null
@@ -1,461 +0,0 @@
-<?php
-// $Id: image.test,v 1.13 2010/04/23 05:10:35 webchick Exp $
-
-/**
- * @file
- * Tests for core image handling API.
- */
-
-/**
- * Base class for image manipulation testing.
- */
-class ImageToolkitTestCase extends DrupalWebTestCase {
-  protected $toolkit;
-  protected $file;
-  protected $image;
-
-  function setUp() {
-    parent::setUp('image_test');
-
-    // Use the image_test.module's test toolkit.
-    $this->toolkit = 'test';
-
-    // Pick a file for testing.
-    $file = current($this->drupalGetTestFiles('image'));
-    $this->file = $file->uri;
-
-    // Setup a dummy image to work with, this replicate image_load() so we
-    // can avoid calling it.
-    $this->image = new stdClass();
-    $this->image->source = $this->file;
-    $this->image->info = image_get_info($this->file);
-    $this->image->toolkit = $this->toolkit;
-
-    // Clear out any hook calls.
-    image_test_reset();
-  }
-
-  /**
-   * Assert that all of the specified image toolkit operations were called
-   * exactly once once, other values result in failure.
-   *
-   * @param $expected
-   *   Array with string containing with the operation name, e.g. 'load',
-   *   'save', 'crop', etc.
-   */
-  function assertToolkitOperationsCalled(array $expected) {
-    // Determine which operations were called.
-    $actual = array_keys(array_filter(image_test_get_all_calls()));
-
-    // Determine if there were any expected that were not called.
-    $uncalled = array_diff($expected, $actual);
-    if (count($uncalled)) {
-      $this->assertTrue(FALSE, t('Expected operations %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled))));
-    }
-    else {
-      $this->assertTrue(TRUE, t('All the expected operations were called: %expected', array('%expected' => implode(', ', $expected))));
-    }
-
-    // Determine if there were any unexpected calls.
-    $unexpected = array_diff($actual, $expected);
-    if (count($unexpected)) {
-      $this->assertTrue(FALSE, t('Unexpected operations were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected))));
-    }
-    else {
-      $this->assertTrue(TRUE, t('No unexpected operations were called.'));
-    }
-  }
-}
-
-/**
- * Test that the functions in image.inc correctly pass data to the toolkit.
- */
-class ImageToolkitUnitTest extends ImageToolkitTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Image toolkit tests',
-      'description' => 'Check image toolkit functions.',
-      'group' => 'Image',
-    );
-  }
-
-  /**
-   * Check that hook_image_toolkits() is called and only available toolkits are
-   * returned.
-   */
-  function testGetAvailableToolkits() {
-    $toolkits = image_get_available_toolkits();
-    $this->assertTrue(isset($toolkits['test']), t('The working toolkit was returned.'));
-    $this->assertFalse(isset($toolkits['broken']), t('The toolkit marked unavailable was not returned'));
-    $this->assertToolkitOperationsCalled(array());
-  }
-
-  /**
-   * Test the image_load() function.
-   */
-  function testLoad() {
-    $image = image_load($this->file, $this->toolkit);
-    $this->assertTrue(is_object($image), t('Returned an object.'));
-    $this->assertEqual($this->toolkit, $image->toolkit, t('Image had toolkit set.'));
-    $this->assertToolkitOperationsCalled(array('load', 'get_info'));
-  }
-
-  /**
-   * Test the image_save() function.
-   */
-  function testSave() {
-    $this->assertFalse(image_save($this->image), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('save'));
-  }
-
-  /**
-   * Test the image_resize() function.
-   */
-  function testResize() {
-    $this->assertTrue(image_resize($this->image, 1, 2), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('resize'));
-
-    // Check the parameters.
-    $calls = image_test_get_all_calls();
-    $this->assertEqual($calls['resize'][0][1], 1, t('Width was passed correctly'));
-    $this->assertEqual($calls['resize'][0][2], 2, t('Height was passed correctly'));
-  }
-
-  /**
-   * Test the image_scale() function.
-   */
-  function testScale() {
-// TODO: need to test upscaling
-    $this->assertTrue(image_scale($this->image, 10, 10), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('resize'));
-
-    // Check the parameters.
-    $calls = image_test_get_all_calls();
-    $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly'));
-    $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly'));
-  }
-
-  /**
-   * Test the image_scale_and_crop() function.
-   */
-  function testScaleAndCrop() {
-    $this->assertTrue(image_scale_and_crop($this->image, 5, 10), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('resize', 'crop'));
-
-    // Check the parameters.
-    $calls = image_test_get_all_calls();
-
-    $this->assertEqual($calls['crop'][0][1], 7.5, t('X was computed and passed correctly'));
-    $this->assertEqual($calls['crop'][0][2], 0, t('Y was computed and passed correctly'));
-    $this->assertEqual($calls['crop'][0][3], 5, t('Width was computed and passed correctly'));
-    $this->assertEqual($calls['crop'][0][4], 10, t('Height was computed and passed correctly'));
-  }
-
-  /**
-   * Test the image_rotate() function.
-   */
-  function testRotate() {
-    $this->assertTrue(image_rotate($this->image, 90, 1), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('rotate'));
-
-    // Check the parameters.
-    $calls = image_test_get_all_calls();
-    $this->assertEqual($calls['rotate'][0][1], 90, t('Degrees were passed correctly'));
-    $this->assertEqual($calls['rotate'][0][2], 1, t('Background color was passed correctly'));
-  }
-
-  /**
-   * Test the image_crop() function.
-   */
-  function testCrop() {
-    $this->assertTrue(image_crop($this->image, 1, 2, 3, 4), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('crop'));
-
-    // Check the parameters.
-    $calls = image_test_get_all_calls();
-    $this->assertEqual($calls['crop'][0][1], 1, t('X was passed correctly'));
-    $this->assertEqual($calls['crop'][0][2], 2, t('Y was passed correctly'));
-    $this->assertEqual($calls['crop'][0][3], 3, t('Width was passed correctly'));
-    $this->assertEqual($calls['crop'][0][4], 4, t('Height was passed correctly'));
-  }
-
-  /**
-   * Test the image_desaturate() function.
-   */
-  function testDesaturate() {
-    $this->assertTrue(image_desaturate($this->image), t('Function returned the expected value.'));
-    $this->assertToolkitOperationsCalled(array('desaturate'));
-
-    // Check the parameters.
-    $calls = image_test_get_all_calls();
-    $this->assertEqual(count($calls['desaturate'][0]), 1, t('Only the image was passed.'));
-  }
-}
-
-/**
- * Test the core GD image manipulation functions.
- */
-class ImageToolkitGdTestCase extends DrupalWebTestCase {
-  // Colors that are used in testing.
-  protected $black       = array(0, 0, 0, 0);
-  protected $red         = array(255, 0, 0, 0);
-  protected $green       = array(0, 255, 0, 0);
-  protected $blue        = array(0, 0, 255, 0);
-  protected $yellow      = array(255, 255, 0, 0);
-  protected $fuchsia     = array(255, 0, 255, 0); // Used as background colors.
-  protected $transparent = array(0, 0, 0, 127);
-  protected $white       = array(255, 255, 255, 0);
-
-  protected $width = 40;
-  protected $height = 20;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Image GD manipulation tests',
-      'description' => 'Check that core image manipulations work properly: scale, resize, rotate, crop, scale and crop, and desaturate.',
-      'group' => 'Image',
-    );
-  }
-
-  /**
-   * Function to compare two colors by RGBa.
-   */
-  function colorsAreEqual($color_a, $color_b) {
-    // Fully transparent pixels are equal, regardless of RGB.
-    if ($color_a[3] == 127 && $color_b[3] == 127) {
-      return TRUE;
-    }
-
-    foreach ($color_a as $key => $value) {
-      if ($color_b[$key] != $value) {
-        return FALSE;
-      }
-    }
-
-    return TRUE;
-  }
-
-  /**
-   * Function for finding a pixel's RGBa values.
-   */
-  function getPixelColor($image, $x, $y) {
-    $color_index = imagecolorat($image->resource, $x, $y);
-
-    $transparent_index = imagecolortransparent($image->resource);
-    if ($color_index == $transparent_index) {
-      return array(0, 0, 0, 127);
-    }
-
-    return array_values(imagecolorsforindex($image->resource, $color_index));
-  }
-
-  /**
-   * Since PHP can't visually check that our images have been manipulated
-   * properly, build a list of expected color values for each of the corners and
-   * the expected height and widths for the final images.
-   */
-  function testManipulations() {
-    // If GD isn't available don't bother testing this.
-    if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
-      $this->pass(t('Image manipulations for the GD toolkit were skipped because the GD toolkit is not available.'));
-      return;
-    }
-
-    // Typically the corner colors will be unchanged. These colors are in the
-    // order of top-left, top-right, bottom-right, bottom-left.
-    $default_corners = array($this->red, $this->green, $this->blue, $this->transparent);
-
-    // A list of files that will be tested.
-    $files = array(
-      'image-test.png',
-      'image-test.gif',
-      'image-test.jpg',
-    );
-
-    // Setup a list of tests to perform on each type.
-    $operations = array(
-      'resize' => array(
-        'function' => 'resize',
-        'arguments' => array(20, 10),
-        'width' => 20,
-        'height' => 10,
-        'corners' => $default_corners,
-      ),
-      'scale_x' => array(
-        'function' => 'scale',
-        'arguments' => array(20, NULL),
-        'width' => 20,
-        'height' => 10,
-        'corners' => $default_corners,
-      ),
-      'scale_y' => array(
-        'function' => 'scale',
-        'arguments' => array(NULL, 10),
-        'width' => 20,
-        'height' => 10,
-        'corners' => $default_corners,
-      ),
-      'upscale_x' => array(
-        'function' => 'scale',
-        'arguments' => array(80, NULL, TRUE),
-        'width' => 80,
-        'height' => 40,
-        'corners' => $default_corners,
-      ),
-      'upscale_y' => array(
-        'function' => 'scale',
-        'arguments' => array(NULL, 40, TRUE),
-        'width' => 80,
-        'height' => 40,
-        'corners' => $default_corners,
-      ),
-      'crop' => array(
-        'function' => 'crop',
-        'arguments' => array(12, 4, 16, 12),
-        'width' => 16,
-        'height' => 12,
-        'corners' => array_fill(0, 4, $this->white),
-      ),
-      'scale_and_crop' => array(
-        'function' => 'scale_and_crop',
-        'arguments' => array(10, 8),
-        'width' => 10,
-        'height' => 8,
-        'corners' => array_fill(0, 4, $this->black),
-      ),
-    );
-
-    // Systems using non-bundled GD2 don't have imagerotate. Test if available.
-    if (function_exists('imagerotate')) {
-      $operations += array(
-        'rotate_5' => array(
-          'function' => 'rotate',
-          'arguments' => array(5, 0xFF00FF), // Fuchsia background.
-          'width' => 42,
-          'height' => 24,
-          'corners' => array_fill(0, 4, $this->fuchsia),
-        ),
-        'rotate_90' => array(
-          'function' => 'rotate',
-          'arguments' => array(90, 0xFF00FF), // Fuchsia background.
-          'width' => 20,
-          'height' => 40,
-          'corners' => array($this->fuchsia, $this->red, $this->green, $this->blue),
-        ),
-        'rotate_transparent_5' => array(
-          'function' => 'rotate',
-          'arguments' => array(5),
-          'width' => 42,
-          'height' => 24,
-          'corners' => array_fill(0, 4, $this->transparent),
-        ),
-        'rotate_transparent_90' => array(
-          'function' => 'rotate',
-          'arguments' => array(90),
-          'width' => 20,
-          'height' => 40,
-          'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
-        ),
-      );
-    }
-
-    // Systems using non-bundled GD2 don't have imagefilter. Test if available.
-    if (function_exists('imagefilter')) {
-      $operations += array(
-        'desaturate' => array(
-          'function' => 'desaturate',
-          'arguments' => array(),
-          'height' => 20,
-          'width' => 40,
-          // Grayscale corners are a bit funky. Each of the corners are a shade of
-          // gray. The values of these were determined simply by looking at the
-          // final image to see what desaturated colors end up being.
-          'corners' => array(
-            array_fill(0, 3, 76) + array(3 => 0),
-            array_fill(0, 3, 149) + array(3 => 0),
-            array_fill(0, 3, 29) + array(3 => 0),
-            array_fill(0, 3, 0) + array(3 => 127)
-          ),
-        ),
-      );
-    }
-
-    foreach ($files as $file) {
-      foreach ($operations as $op => $values) {
-        // Load up a fresh image.
-        $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd');
-        if (!$image) {
-          $this->fail(t('Could not load image %file.', array('%file' => $file)));
-          continue 2;
-        }
-
-        // Transparent GIFs and the imagefilter function don't work together.
-        // There is a todo in image.gd.inc to correct this.
-        if ($image->info['extension'] == 'gif') {
-          if ($op == 'desaturate') {
-            $values['corners'][3] = $this->white;
-          }
-        }
-
-        // Perform our operation.
-        $function = 'image_' . $values['function'];
-        $arguments = array();
-        $arguments[] = &$image;
-        $arguments = array_merge($arguments, $values['arguments']);
-        call_user_func_array($function, $arguments);
-
-        // To keep from flooding the test with assert values, make a general
-        // value for whether each group of values fail.
-        $correct_dimensions_real = TRUE;
-        $correct_dimensions_object = TRUE;
-        $correct_colors = TRUE;
-
-        // Check the real dimensions of the image first.
-        if (imagesy($image->resource) != $values['height'] || imagesx($image->resource) != $values['width']) {
-          $correct_dimensions_real = FALSE;
-        }
-
-        // Check that the image object has an accurate record of the dimensions.
-        if ($image->info['width'] != $values['width'] || $image->info['height'] != $values['height']) {
-          $correct_dimensions_object = FALSE;
-        }
-        // Now check each of the corners to ensure color correctness.
-        foreach ($values['corners'] as $key => $corner) {
-          // Get the location of the corner.
-          switch ($key) {
-            case 0:
-              $x = 0;
-              $y = 0;
-              break;
-            case 1:
-              $x = $values['width'] - 1;
-              $y = 0;
-              break;
-            case 2:
-              $x = $values['width'] - 1;
-              $y = $values['height'] - 1;
-              break;
-            case 3:
-              $x = 0;
-              $y = $values['height'] - 1;
-              break;
-          }
-          $color = $this->getPixelColor($image, $x, $y);
-          $correct_colors = $this->colorsAreEqual($color, $corner);
-        }
-
-        $directory = file_directory_path() . '/imagetests';
-        file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
-        image_save($image, $directory . '/' . $op . '.' . $image->info['extension']);
-
-        $this->assertTrue($correct_dimensions_real, t('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
-        $this->assertTrue($correct_dimensions_object, t('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
-        // JPEG colors will always be messed up due to compression.
-        if ($image->info['extension'] != 'jpg') {
-          $this->assertTrue($correct_colors, t('Image %file object after %action action has the correct color placement.', array('%file' => $file, '%action' => $op)));
-        }
-      }
-    }
-
-  }
-}
diff --git a/modules/simpletest/tests/lock.test b/modules/simpletest/tests/lock.test
deleted file mode 100644
index 5df032b..0000000
--- a/modules/simpletest/tests/lock.test
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-// $Id: lock.test,v 1.1 2009/08/17 20:32:30 dries Exp $
-
-/**
- * Tests for the lock system.
- */
-class LockFunctionalTest extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Locking framework tests',
-      'description' => 'Confirm locking works between two separate requests.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('system_test');
-  }
-
-  /**
-   * Confirm that we can acquire and release locks in two parallel requests.
-   */
-  function testLockAcquire() {
-    $lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
-    $lock_not_acquired = 'FALSE: Lock not acquired in system_test_lock_acquire()';
-    $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
-    $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock extended by this request.'), t('Lock'));
-    lock_release('system_test_lock_acquire');
-
-    // Cause another request to acquire the lock.
-    $this->drupalGet('system-test/lock-acquire');
-    $this->assertText($lock_acquired, t('Lock acquired by the other request.'), t('Lock'));
-    // The other request has finished, thus it should have released its lock.
-    $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
-    // This request holds the lock, so the other request cannot acquire it.
-    $this->drupalGet('system-test/lock-acquire');
-    $this->assertText($lock_not_acquired, t('Lock not acquired by the other request.'), t('Lock'));
-    lock_release('system_test_lock_acquire');
-
-    // Try a very short timeout and lock breaking.
-    $this->assertTrue(lock_acquire('system_test_lock_acquire', 0.5), t('Lock acquired by this request.'), t('Lock'));
-    sleep(1);
-    // The other request should break our lock.
-    $this->drupalGet('system-test/lock-acquire');
-    $this->assertText($lock_acquired, t('Lock acquired by the other request, breaking our lock.'), t('Lock'));
-    // We cannot renew it, since the other thread took it.
-    $this->assertFalse(lock_acquire('system_test_lock_acquire'), t('Lock cannot be extended by this request.'), t('Lock'));
-
-    // Check the shut-down function.
-    $lock_acquired_exit = 'TRUE: Lock successfully acquired in system_test_lock_exit()';
-    $lock_not_acquired_exit = 'FALSE: Lock not acquired in system_test_lock_exit()';
-    $this->drupalGet('system-test/lock-exit');
-    $this->assertText($lock_acquired_exit, t('Lock acquired by the other request before exit.'), t('Lock'));
-    $this->assertTrue(lock_acquire('system_test_lock_exit'), t('Lock acquired by this request after the other request exits.'), t('Lock'));
-  }
-}
-
diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test
deleted file mode 100644
index f5cb76a..0000000
--- a/modules/simpletest/tests/mail.test
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-// $Id: mail.test,v 1.3 2010/04/11 18:33:44 dries Exp $
-
-/**
- * Test the Drupal mailing system.
- */
-class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
-  /**
-   * The most recent message that was sent through the test case.
-   *
-   * We take advantage here of the fact that static variables are shared among
-   * all instance of the same class.
-   */
-  private static $sent_message;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Mail system',
-      'description' => 'Performs tests on the pluggable mailing framework.',
-      'group' => 'System',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-
-    // Set MailTestCase (i.e. this class) as the SMTP library
-    variable_set('mail_system', array('default-system' => 'MailTestCase'));
-  }
-
-  /**
-   * Assert that the pluggable mail system is functional.
-   */
-  function testPluggableFramework() {
-    global $language;
-
-    // Use MailTestCase for sending a message.
-    $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language);
-
-    // Assert whether the message was sent through the send function.
-    $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.'));
-  }
-
-  /**
-   * Concatenate and wrap the e-mail body for plain-text mails.
-   *
-   * @see DefaultMailSystem
-   */
-  public function format(array $message) {
-    // Join the body array into one string.
-    $message['body'] = implode("\n\n", $message['body']);
-    // Convert any HTML to plain-text.
-    $message['body'] = drupal_html_to_text($message['body']);
-    // Wrap the mail body for sending.
-    $message['body'] = drupal_wrap_mail($message['body']);
-    return $message;
-  }
-
-  /**
-   * Send function that is called through the mail system.
-   */
-  public function mail(array $message) {
-    self::$sent_message = $message;
-  }
-}
-
diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test
deleted file mode 100644
index b108d06..0000000
--- a/modules/simpletest/tests/menu.test
+++ /dev/null
@@ -1,527 +0,0 @@
-<?php
-// $Id: menu.test,v 1.31 2010/07/08 03:41:27 webchick Exp $
-
-/**
- * @file
- * Provides SimpleTests for menu.inc.
- */
-
-class MenuRouterTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Menu router',
-      'description' => 'Tests menu router and hook_menu() functionality.',
-      'group' => 'Menu',
-    );
-  }
-
-  function setUp() {
-    // Enable dummy module that implements hook_menu.
-    parent::setUp('menu_test');
-    // Make the tests below more robust by explicitly setting the default theme
-    // and administrative theme that they expect.
-    theme_enable(array('garland'));
-    variable_set('theme_default', 'garland');
-    variable_set('admin_theme', 'seven');
-  }
-
-  /**
-   * Test title callback set to FALSE.
-   */
-  function testTitleCallbackFalse() {
-    $this->drupalGet('node');
-    $this->assertText('A title with @placeholder', t('Raw text found on the page'));
-    $this->assertNoText(t('A title with @placeholder', array('@placeholder' => 'some other text')), t('Text with placeholder substitutions not found.'));
-  }
-
-  /**
-   * Test the theme callback when it is set to use an administrative theme.
-   */
-  function testThemeCallbackAdministrative() {
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: seven. Actual theme: seven.', t('The administrative theme can be correctly set in a theme callback.'));
-    $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
-  }
-
-  /**
-   * Test that the theme callback is properly inherited.
-   */
-  function testThemeCallbackInheritance() {
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance');
-    $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', t('Theme callback inheritance correctly uses the administrative theme.'));
-    $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
-  }
-
-  /**
-   * Test that 'page callback', 'file' and 'file path' keys are properly
-   * inherited from parent menu paths.
-   */
-  function testFileInheritance() {
-    $this->drupalGet('admin/config/development/file-inheritance');
-    $this->assertText('File inheritance test description', t('File inheritance works.'));
-  }
-
-  /**
-   * Test path containing "exotic" characters.
-   */
-  function testExoticPath() {
-    $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
-      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
-      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
-    $this->drupalGet($path);
-    $this->assertRaw('This is menu_test_callback().');
-  }
-
-  /**
-   * Test the theme callback when the site is in maintenance mode.
-   */
-  function testThemeCallbackMaintenanceMode() {
-    variable_set('maintenance_mode', TRUE);
-
-    // For a regular user, the fact that the site is in maintenance mode means
-    // we expect the theme callback system to be bypassed entirely.
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertRaw('garland/style.css', t("The maintenance theme's CSS appears on the page."));
-
-    // An administrator, however, should continue to see the requested theme.
-    $admin_user = $this->drupalCreateUser(array('access site in maintenance mode'));
-    $this->drupalLogin($admin_user);
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: seven. Actual theme: seven.', t('The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.'));
-    $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
-  }
-
-  /**
-   * Make sure the maintenance mode can be bypassed using hook_menu_site_status_alter().
-   *
-   * @see hook_menu_site_status_alter().
-   */
-  function testMaintenanceModeLoginPaths() {
-    variable_set('maintenance_mode', TRUE);
-
-    $offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')));
-    $this->drupalLogout();
-    $this->drupalGet('node');
-    $this->assertText($offline_message);
-    $this->drupalGet('menu_login_callback');
-    $this->assertText('This is menu_login_callback().', t('Maintenance mode can be bypassed through hook_login_paths().'));
-  }
-
-  /**
-   * Test that an authenticated user hitting 'user/login' gets redirected to
-   * 'user' and 'user/register' gets redirected to the user edit page.
-   */
-  function testAuthUserUserLogin() {
-    $loggedInUser = $this->drupalCreateUser(array());
-    $this->drupalLogin($loggedInUser);
-
-    $this->DrupalGet('user/login');
-    // Check that we got to 'user'.
-    $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to q=user on accessing q=user/login"));
-
-    // user/register should redirect to user/UID/edit.
-    $this->DrupalGet('user/register');
-    $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to q=user/UID/edit on accessing q=user/register"));
-  }
-
-  /**
-   * Test the theme callback when it is set to use an optional theme.
-   */
-  function testThemeCallbackOptionalTheme() {
-    // Request a theme that is not enabled.
-    $this->drupalGet('menu-test/theme-callback/use-stark-theme');
-    $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when a theme that is not enabled is requested.'));
-    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page."));
-
-    // Now enable the theme and request it again.
-    theme_enable(array('stark'));
-    $this->drupalGet('menu-test/theme-callback/use-stark-theme');
-    $this->assertText('Custom theme: stark. Actual theme: stark.', t('The theme callback system uses an optional theme once it has been enabled.'));
-    $this->assertRaw('stark/layout.css', t("The optional theme's CSS appears on the page."));
-  }
-
-  /**
-   * Test the theme callback when it is set to use a theme that does not exist.
-   */
-  function testThemeCallbackFakeTheme() {
-    $this->drupalGet('menu-test/theme-callback/use-fake-theme');
-    $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when a theme that does not exist is requested.'));
-    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page."));
-  }
-
-  /**
-   * Test the theme callback when no theme is requested.
-   */
-  function testThemeCallbackNoThemeRequested() {
-    $this->drupalGet('menu-test/theme-callback/no-theme-requested');
-    $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when no theme is requested.'));
-    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page."));
-  }
-
-  /**
-   * Test that the result of hook_custom_theme() overrides the theme callback.
-   */
-  function testHookCustomTheme() {
-    // Trigger hook_custom_theme() to dynamically request the Stark theme for
-    // the requested page.
-    variable_set('menu_test_hook_custom_theme_name', 'stark');
-
-    // Request a page whose theme callback returns the Seven theme. Since Stark
-    // is not a currently enabled theme, our above request should be ignored,
-    // and Seven should still be used.
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: seven. Actual theme: seven.', t('The result of hook_custom_theme() does not override a theme callback when it returns a theme that is not enabled.'));
-    $this->assertRaw('seven/style.css', t("The Seven theme's CSS appears on the page."));
-
-    // Now enable the Stark theme and request the same page as above. This
-    // time, we expect hook_custom_theme() to prevail.
-    theme_enable(array('stark'));
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: stark. Actual theme: stark.', t('The result of hook_custom_theme() overrides what was set in a theme callback.'));
-    $this->assertRaw('stark/layout.css', t("The Stark theme's CSS appears on the page."));
-  }
-
-  /**
-   * Tests for menu_link_maintain().
-   */
-  function testMenuLinkMaintain() {
-    $admin_user = $this->drupalCreateUser(array('administer site configuration'));
-    $this->drupalLogin($admin_user);
-
-    // Create three menu items.
-    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1');
-    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1-1');
-    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/2', 'Menu link #2');
-
-    // Move second link to the main-menu, to test caching later on.
-    db_update('menu_links')
-      ->fields(array('menu_name' => 'main-menu'))
-      ->condition('link_title', 'Menu link #1-1')
-      ->condition('customized', 0)
-      ->condition('module', 'menu_test')
-      ->execute();
-    menu_cache_clear('main-menu');
-
-    // Load front page.
-    $this->drupalGet('node');
-    $this->assertLink(t('Menu link #1'), 0, 'Found menu link #1');
-    $this->assertLink(t('Menu link #1-1'), 0, 'Found menu link #1-1');
-    $this->assertLink(t('Menu link #2'), 0, 'Found menu link #2');
-
-    // Rename all links for the given path.
-    menu_link_maintain('menu_test', 'update', 'menu_test_maintain/1', 'Menu link updated');
-    // Load a different page to be sure that we have up to date information.
-    $this->drupalGet('menu_test_maintain/1');
-    $this->assertLink(t('Menu link updated'), 0, t('Found updated menu link'));
-    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1'));
-    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1'));
-    $this->assertLink(t('Menu link #2'), 0, t('Found menu link #2'));
-
-    // Delete all links for the given path.
-    menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/1', '');
-    // Load a different page to be sure that we have up to date information.
-    $this->drupalGet('menu_test_maintain/2');
-    $this->assertNoLink(t('Menu link updated'), 0, t('Not found deleted menu link'));
-    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1'));
-    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1'));
-    $this->assertLink(t('Menu link #2'), 0, t('Found menu link #2'));
-  }
-
-  /**
-   * Test menu_get_names().
-   */
-  function testMenuGetNames() {
-    // Create three menu items.
-    for ($i = 0; $i < 3; $i++) {
-      $menu_link = array(
-        'link_title' => 'Menu link #' . $i,
-        'link_path' => 'menu_test/' . $i,
-        'module' => 'menu_test',
-        'menu_name' => 'menu_test_' . $i,
-      );
-      menu_link_save($menu_link);
-    }
-
-    drupal_static_reset('menu_get_names');
-
-    // Verify that the menu names are correctly reported by menu_get_names().
-    $menu_names = menu_get_names();
-    $this->pass(implode(' | ', $menu_names));
-    for ($i = 0; $i < 3; $i++) {
-      $this->assertTrue(in_array('menu_test_' . $i, $menu_names), t('Expected menu name %expected is returned.', array('%expected' => 'menu_test_' . $i)));
-    }
-  }
-
-  /**
-   * Tests for menu_name parameter for hook_menu().
-   */
-  function testMenuName() {
-    $admin_user = $this->drupalCreateUser(array('administer site configuration'));
-    $this->drupalLogin($admin_user);
-
-    $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
-    $name = db_query($sql)->fetchField();
-    $this->assertEqual($name, 'original', t('Menu name is "original".'));
-
-    // Change the menu_name parameter in menu_test.module, then force a menu
-    // rebuild.
-    menu_test_menu_name('changed');
-    menu_rebuild();
-
-    $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
-    $name = db_query($sql)->fetchField();
-    $this->assertEqual($name, 'changed', t('Menu name was successfully changed after rebuild.'));
-  }
-
-  /**
-   * Tests for menu hierarchy.
-   */
-  function testMenuHierarchy() {
-    $parent_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent'))->fetchAssoc();
-    $child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child'))->fetchAssoc();
-    $unattached_child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child2/child'))->fetchAssoc();
-
-    $this->assertEqual($child_link['plid'], $parent_link['mlid'], t('The parent of a directly attached child is correct.'));
-    $this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], t('The parent of a non-directly attached child is correct.'));
-  }
-
-  /**
-   * Tests menu link depth and parents of local tasks and menu callbacks.
-   */
-  function testMenuHidden() {
-    // Verify links for one dynamic argument.
-    $links = db_select('menu_links', 'ml')
-      ->fields('ml')
-      ->condition('ml.router_path', 'menu-test/hidden/menu%', 'LIKE')
-      ->orderBy('ml.router_path')
-      ->execute()
-      ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
-
-    $parent = $links['menu-test/hidden/menu'];
-    $depth = $parent['depth'] + 1;
-    $plid = $parent['mlid'];
-
-    $link = $links['menu-test/hidden/menu/list'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/menu/add'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/menu/settings'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/menu/manage/%'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $parent = $links['menu-test/hidden/menu/manage/%'];
-    $depth = $parent['depth'] + 1;
-    $plid = $parent['mlid'];
-
-    $link = $links['menu-test/hidden/menu/manage/%/list'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/menu/manage/%/add'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/menu/manage/%/edit'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/menu/manage/%/delete'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    // Verify links for two dynamic arguments.
-    $links = db_select('menu_links', 'ml')
-      ->fields('ml')
-      ->condition('ml.router_path', 'menu-test/hidden/block%', 'LIKE')
-      ->orderBy('ml.router_path')
-      ->execute()
-      ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
-
-    $parent = $links['menu-test/hidden/block'];
-    $depth = $parent['depth'] + 1;
-    $plid = $parent['mlid'];
-
-    $link = $links['menu-test/hidden/block/list'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/block/add'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/block/manage/%/%'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $parent = $links['menu-test/hidden/block/manage/%/%'];
-    $depth = $parent['depth'] + 1;
-    $plid = $parent['mlid'];
-
-    $link = $links['menu-test/hidden/block/manage/%/%/configure'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
-    $link = $links['menu-test/hidden/block/manage/%/%/delete'];
-    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-  }
-
-  /**
-   * Test menu_set_item().
-   */
-  function testMenuSetItem() {
-    $item = menu_get_item('node');
-
-    $this->assertEqual($item['path'], 'node', t("Path from menu_get_item('node') is equal to 'node'"), 'menu');
-
-    // Modify the path for the item then save it.
-    $item['path'] = 'node_test';
-    $item['href'] = 'node_test';
-
-    menu_set_item('node', $item);
-    $compare_item = menu_get_item('node');
-    $this->assertEqual($compare_item, $item, t('Modified menu item is equal to newly retrieved menu item.'), 'menu');
-  }
-
-  /**
-   * Test menu maintainance hooks.
-   */
-  function testMenuItemHooks() {
-    // Create an item.
-    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/4', 'Menu link #4');
-    $this->assertEqual(menu_test_static_variable(), 'insert', t('hook_menu_link_insert() fired correctly'));
-    // Update the item.
-    menu_link_maintain('menu_test', 'update', 'menu_test_maintain/4', 'Menu link updated');
-    $this->assertEqual(menu_test_static_variable(), 'update', t('hook_menu_link_update() fired correctly'));
-    // Delete the item.
-    menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/4', '');
-    $this->assertEqual(menu_test_static_variable(), 'delete', t('hook_menu_link_delete() fired correctly'));
-  }
-
-  /**
-   * Test menu link 'options' storage and rendering.
-   */
-  function testMenuLinkOptions() {
-    // Create a menu link with options.
-    $menu_link = array(
-      'link_title' => 'Menu link options test',
-      'link_path' => 'node',
-      'module' => 'menu_test',
-      'options' => array(
-        'attributes' => array(
-          'title' => 'Test title attribute',
-        ),
-        'query' => array(
-          'testparam' => 'testvalue',
-        ),
-      ),
-    );
-    menu_link_save($menu_link);
-
-    // Load front page.
-    $this->drupalGet('node');
-    $this->assertRaw('title="Test title attribute"', t('Title attribute of a menu link renders.'));
-    $this->assertRaw('testparam=testvalue', t('Query parameter added to menu link.'));
-  }
-}
-
-/**
- * Tests rebuilding the menu by setting 'menu_rebuild_needed.'
- */
-class MenuRebuildTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Menu rebuild test',
-      'description' => 'Test rebuilding of menu.',
-      'group' => 'Menu',
-    );
-  }
-
-  /**
-   * Test if the 'menu_rebuild_needed' variable triggers a menu_rebuild() call.
-   */
-  function testMenuRebuildByVariable() {
-    // Check if 'admin' path exists.
-    $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
-    $this->assertEqual($admin_exists, 'admin', t("The path 'admin/' exists prior to deleting."));
-
-    // Delete the path item 'admin', and test that the path doesn't exist in the database.
-    $delete = db_delete('menu_router')
-      ->condition('path', 'admin')
-      ->execute();
-    $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
-    $this->assertFalse($admin_exists, t("The path 'admin/' has been deleted and doesn't exist in the database."));
-
-    // Now we enable the rebuild variable and trigger menu_execute_active_handler()
-    // to rebuild the menu item. Now 'admin' should exist.
-    variable_set('menu_rebuild_needed', TRUE);
-    // menu_execute_active_handler() should trigger the rebuild.
-    $this->drupalGet('<front>');
-    $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
-    $this->assertEqual($admin_exists, 'admin', t("The menu has been rebuilt, the path 'admin' now exists again."));
-  }
-
-}
-
-/**
- * Menu tree data related tests.
- */
-class MenuTreeDataTestCase extends DrupalUnitTestCase {
-  /**
-   * Dummy link structure acceptable for menu_tree_data().
-   */
-  var $links = array(
-    1 => array('mlid' => 1, 'depth' => 1),
-    2 => array('mlid' => 2, 'depth' => 1),
-    3 => array('mlid' => 3, 'depth' => 2),
-    4 => array('mlid' => 4, 'depth' => 3),
-    5 => array('mlid' => 5, 'depth' => 1),
-  );
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Menu tree generation',
-      'description' => 'Tests recursive menu tree generation functions.',
-      'group' => 'Menu',
-    );
-  }
-
-  /**
-   * Validate the generation of a proper menu tree hierarchy.
-   */
-  function testMenuTreeData() {
-    $tree = menu_tree_data($this->links);
-
-    // Validate that parent items #1, #2, and #5 exist on the root level.
-    $this->assertSameLink($this->links[1], $tree[1]['link'], t('Parent item #1 exists.'));
-    $this->assertSameLink($this->links[2], $tree[2]['link'], t('Parent item #2 exists.'));
-    $this->assertSameLink($this->links[5], $tree[5]['link'], t('Parent item #5 exists.'));
-
-    // Validate that child item #4 exists at the correct location in the hierarchy.
-    $this->assertSameLink($this->links[4], $tree[2]['below'][3]['below'][4]['link'], t('Child item #4 exists in the hierarchy.'));
-  }
-
-  /**
-   * Check that two menu links are the same by comparing the mlid.
-   *
-   * @param $link1
-   *   A menu link item.
-   * @param $link2
-   *   A menu link item.
-   * @param $message
-   *   The message to display along with the assertion.
-   * @return
-   *   TRUE if the assertion succeeded, FALSE otherwise.
-   */
-  protected function assertSameLink($link1, $link2, $message = '') {
-    return $this->assert($link1['mlid'] == $link2['mlid'], $message ? $message : t('First link is identical to second link'));
-  }
-}
diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test
deleted file mode 100644
index a9b16c4..0000000
--- a/modules/simpletest/tests/module.test
+++ /dev/null
@@ -1,205 +0,0 @@
-<?php
-// $Id: module.test,v 1.21 2010/04/22 18:56:31 dries Exp $
-
-/**
- * @file
- * Tests for the module API.
- */
-
-/**
- * Unit tests for the module API.
- */
-class ModuleUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Module API',
-      'description' => 'Test low-level module functions.',
-      'group' => 'Module',
-    );
-  }
-
-  /**
-   * The basic functionality of module_list().
-   */
-  function testModuleList() {
-    // Build a list of modules, sorted alphabetically.
-    $profile_info = install_profile_info('standard', 'en');
-    $module_list = $profile_info['dependencies'];
-
-    // Install profile is a module that is expected to be loaded.
-    $module_list[] = 'standard';
-
-    sort($module_list);
-    // Compare this list to the one returned by module_list(). We expect them
-    // to match, since all default profile modules have a weight equal to 0
-    // (except for block.module, which has a lower weight but comes first in
-    // the alphabet anyway).
-    $this->assertModuleList($module_list, t('Standard profile'));
-
-    // Try to install a new module.
-    module_enable(array('contact'));
-    $module_list[] = 'contact';
-    sort($module_list);
-    $this->assertModuleList($module_list, t('After adding a module'));
-
-    // Try to mess with the module weights.
-    db_update('system')
-      ->fields(array('weight' => 20))
-      ->condition('name', 'contact')
-      ->condition('type', 'module')
-      ->execute();
-    // Reset the module list.
-    module_list(TRUE);
-    // Move contact to the end of the array.
-    unset($module_list[array_search('contact', $module_list)]);
-    $module_list[] = 'contact';
-    $this->assertModuleList($module_list, t('After changing weights'));
-
-    // Test the fixed list feature.
-    $fixed_list = array(
-      'system' => array('filename' => drupal_get_path('module', 'system')),
-      'menu' => array('filename' => drupal_get_path('module', 'menu')),
-    );
-    module_list(FALSE, FALSE, FALSE, $fixed_list);
-    $new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list));
-    $this->assertModuleList($new_module_list, t('When using a fixed list'));
-
-    // Reset the module list.
-    module_list(TRUE);
-    $this->assertModuleList($module_list, t('After reset'));
-  }
-
-  /**
-   * Assert that module_list() return the expected values.
-   *
-   * @param $expected_values
-   *   The expected values, sorted by weight and module name.
-   */
-  protected function assertModuleList(Array $expected_values, $condition) {
-    $expected_values = array_combine($expected_values, $expected_values);
-    $this->assertEqual($expected_values, module_list(), t('@condition: module_list() returns correct results', array('@condition' => $condition)));
-    ksort($expected_values);
-    $this->assertIdentical($expected_values, module_list(FALSE, FALSE, TRUE), t('@condition: module_list() returns correctly sorted results', array('@condition' => $condition)));
-  }
-
-  /**
-   * Test module_implements() caching.
-   */
-  function testModuleImplements() {
-    // Clear the cache.
-    cache_clear_all('module_implements', 'cache_bootstrap');
-    $this->assertFalse(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is empty.'));
-    $this->drupalGet('');
-    $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.'));
-
-    // Test again with an authenticated user.
-    $this->user = $this->drupalCreateUser();
-    $this->drupalLogin($this->user);
-    cache_clear_all('module_implements', 'cache_bootstrap');
-    $this->drupalGet('');
-    $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.'));
-
-    // Make sure group include files are detected properly even when the file is
-    // already loaded when the cache is rebuilt.
-    // For that activate the module_test which provides the file to load.
-    module_enable(array('module_test'));
-
-    module_load_include('inc', 'module_test', 'module_test.file');
-    $modules = module_implements('test_hook');
-    $static = drupal_static('module_implements');
-    $this->assertTrue(in_array('module_test', $modules), 'Hook found.');
-    $this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.');
-  }
-
-  /**
-   * Test dependency resolution.
-   */
-  function testDependencyResolution() {
-    module_enable(array('module_test'), FALSE);
-    $this->assertTrue(module_exists('module_test'), t('Test module is enabled.'));
-
-    // First, create a fake missing dependency. Forum depends on poll, which
-    // depends on a made-up module, foo. Nothing should be installed.
-    variable_set('dependency_test', 'missing dependency');
-    $result = module_enable(array('forum'));
-    $this->assertFalse($result, t('module_enable() returns FALSE if dependencies are missing.'));
-    $this->assertFalse(module_exists('forum'), t('module_enable() aborts if dependencies are missing.'));
-
-    // Now, fix the missing dependency. module_enable() should work.
-    variable_set('dependency_test', 'dependency');
-    $result = module_enable(array('forum'));
-    $this->assertTrue($result, t('module_enable() returns the correct value.'));
-    // Verify that the fake dependency chain was installed.
-    $this->assertTrue(module_exists('poll') && module_exists('php'), t('Dependency chain was installed by module_enable().'));
-    // Finally, verify that the original module was installed.
-    $this->assertTrue(module_exists('forum'), t('Module installation with unlisted dependencies succeeded.'));
-  }
-}
-
-/**
- * Unit tests for module installation.
- */
-class ModuleInstallTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Module installation',
-      'description' => 'Tests the installation of modules.',
-      'group' => 'Module',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('module_test');
-  }
-
-  /**
-   * Test that calls to drupal_write_record() work during module installation.
-   *
-   * This is a useful function to test because modules often use it to insert
-   * initial data in their database tables when they are being installed or
-   * enabled. Furthermore, drupal_write_record() relies on the module schema
-   * information being available, so this also checks that the data from one of
-   * the module's hook implementations, in particular hook_schema(), is
-   * properly available during this time. Therefore, this test helps ensure
-   * that modules are fully functional while Drupal is installing and enabling
-   * them.
-   */
-  function testDrupalWriteRecord() {
-    // Check for data that was inserted using drupal_write_record() while the
-    // 'module_test' module was being installed and enabled.
-    $data = db_query("SELECT data FROM {module_test}")->fetchCol();
-    $this->assertTrue(in_array('Data inserted in hook_install()', $data), t('Data inserted using drupal_write_record() in hook_install() is correctly saved.'));
-    $this->assertTrue(in_array('Data inserted in hook_enable()', $data), t('Data inserted using drupal_write_record() in hook_enable() is correctly saved.'));
-  }
-}
-
-/**
- * Unit tests for module uninstallation and related hooks.
- */
-class ModuleUninstallTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Module uninstallation',
-      'description' => 'Tests the uninstallation of modules.',
-      'group' => 'Module',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('module_test', 'user');
-  }
-
-  /**
-   * Tests the hook_modules_uninstalled() of the user module.
-   */
-  function testUserPermsUninstalled() {
-    // Uninstalls the module_test module, so hook_modules_uninstalled()
-    // is executed.
-    module_disable(array('module_test'));
-    drupal_uninstall_modules(array('module_test'));
-
-    // Are the perms defined by module_test removed from {role_permission}.
-    $count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField();
-    $this->assertEqual(0, $count, t('Permissions were all removed.'));
-  }
-}
diff --git a/modules/simpletest/tests/path.test b/modules/simpletest/tests/path.test
deleted file mode 100644
index 5fb8ced..0000000
--- a/modules/simpletest/tests/path.test
+++ /dev/null
@@ -1,238 +0,0 @@
-<?php
-// $Id: path.test,v 1.5 2010/04/06 19:49:03 dries Exp $
-
-/**
- * @file
- * Tests for path.inc.
- */
-
-/**
- * Unit tests for the drupal_match_path() function in path.inc.
- *
- * @see drupal_match_path().
- */
-class DrupalMatchPathTestCase extends DrupalWebTestCase {
-  protected $front;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Drupal match path',
-      'description' => 'Tests the drupal_match_path() function to make sure it works properly.',
-      'group' => 'Path API',
-    );
-  }
-
-  function setUp() {
-    // Set up the database and testing environment.
-    parent::setUp();
-
-    // Set up a random site front page to test the '<front>' placeholder.
-    $this->front = $this->randomName();
-    variable_set('site_frontpage', $this->front);
-    // Refresh our static variables from the database.
-    $this->refreshVariables();
-  }
-
-  /**
-   * Run through our test cases, making sure each one works as expected.
-   */
-  function testDrupalMatchPath() {
-    // Set up our test cases.
-    $tests = $this->drupalMatchPathTests();
-    foreach ($tests as $patterns => $cases) {
-      foreach ($cases as $path => $expected_result) {
-        $actual_result = drupal_match_path($path, $patterns);
-        $this->assertIdentical($actual_result, $expected_result, t('Tried matching the path <code>@path</code> to the pattern <pre>@patterns</pre> - expected @expected, got @actual.', array('@path' => $path, '@patterns' => $patterns, '@expected' => var_export($expected_result, TRUE), '@actual' => var_export($actual_result, TRUE))));
-      }
-    }
-  }
-
-  /**
-   * Helper function for testDrupalMatchPath(): set up an array of test cases.
-   *
-   * @return
-   *   An array of test cases to cycle through.
-   */
-  private function drupalMatchPathTests() {
-    return array(
-      // Single absolute paths.
-      'blog/1' => array(
-        'blog/1' => TRUE,
-        'blog/2' => FALSE,
-        'test' => FALSE,
-      ),
-      // Single paths with wildcards.
-      'blog/*' => array(
-        'blog/1' => TRUE,
-        'blog/2' => TRUE,
-        'blog/3/edit' => TRUE,
-        'blog/' => TRUE,
-        'blog' => FALSE,
-        'test' => FALSE,
-      ),
-      // Single paths with multiple wildcards.
-      'node/*/revisions/*' => array(
-        'node/1/revisions/3' => TRUE,
-        'node/345/revisions/test' => TRUE,
-        'node/23/edit' => FALSE,
-        'test' => FALSE,
-      ),
-      // Single paths with '<front>'.
-      '<front>' => array(
-        $this->front => TRUE,
-        "$this->front/" => FALSE,
-        "$this->front/edit" => FALSE,
-        'node' => FALSE,
-        '' => FALSE,
-      ),
-      // Paths with both '<front>' and wildcards (should not work).
-      '<front>/*' => array(
-        $this->front => FALSE,
-        "$this->front/" => FALSE,
-        "$this->front/edit" => FALSE,
-        'node/12' => FALSE,
-        '' => FALSE,
-      ),
-      // Multiple paths with the \n delimiter.
-      "node/*\nnode/*/edit" => array(
-        'node/1' => TRUE,
-        'node/view' => TRUE,
-        'node/32/edit' => TRUE,
-        'node/delete/edit' => TRUE,
-        'node/50/delete' => TRUE,
-        'test/example' => FALSE,
-      ),
-      // Multiple paths with the \r delimiter.
-      "user/*\rblog/*" => array(
-        'user/1' => TRUE,
-        'blog/1' => TRUE,
-        'user/1/blog/1' => TRUE,
-        'user/blog' => TRUE,
-        'test/example' => FALSE,
-        'user' => FALSE,
-        'blog' => FALSE,
-      ),
-      // Multiple paths with the \r\n delimiter.
-      "test\r\n<front>" => array(
-        'test' => TRUE,
-        $this->front => TRUE,
-        'example' => FALSE,
-      ),
-      // Test existing regular expressions (should be escaped).
-      '[^/]+?/[0-9]' => array(
-        'test/1' => FALSE,
-        '[^/]+?/[0-9]' => TRUE,
-      ),
-    );
-  }
-}
-
-/**
- * Tests hook_url_alter functions.
- */
-class UrlAlterFunctionalTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => t('URL altering'),
-      'description' => t('Tests hook_url_inbound_alter() and hook_url_outbound_alter().'),
-      'group' => t('Path API'),
-    );
-  }
-
-  function setUp() {
-    parent::setUp('path', 'forum', 'url_alter_test');
-  }
-
-  /**
-   * Test that URL altering works and that it occurs in the correct order.
-   */
-  function testUrlAlter() {
-    $account = $this->drupalCreateUser(array('administer url aliases'));
-    $this->drupalLogin($account);
-
-    $uid = $account->uid;
-    $name = $account->name;
-
-    // Test a single altered path.
-    $this->assertUrlInboundAlter("user/$name", "user/$uid");
-    $this->assertUrlOutboundAlter("user/$uid", "user/$name");
-
-    // Test that a path always uses its alias.
-    $path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1');
-    path_save($path);
-    $this->assertUrlInboundAlter('alias/test1', "user/$uid/test1");
-    $this->assertUrlOutboundAlter("user/$uid/test1", 'alias/test1');
-
-    // Test that alias source paths are normalized in the interface.
-    $edit = array('source' => "user/$name/edit", 'alias' => 'alias/test2');
-    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
-    $this->assertText(t('The alias has been saved.'));
-
-    // Test that a path always uses its alias.
-    $this->assertUrlInboundAlter('alias/test2', "user/$uid/edit");
-    $this->assertUrlOutboundAlter("user/$uid/edit", 'alias/test2');
-
-    // Test a non-existant user is not altered.
-    $uid++;
-    $this->assertUrlInboundAlter("user/$uid", "user/$uid");
-    $this->assertUrlOutboundAlter("user/$uid", "user/$uid");
-
-    // Test that 'forum' is altered to 'community' correctly, both at the root
-    // level and for a specific existing forum.
-    $this->assertUrlInboundAlter('community', 'forum');
-    $this->assertUrlOutboundAlter('forum', 'community');
-    $forum_vid = db_query("SELECT vid FROM {taxonomy_vocabulary} WHERE module = 'forum'")->fetchField();
-    $tid = db_insert('taxonomy_term_data')
-      ->fields(array(
-        'name' => $this->randomName(),
-        'vid' => $forum_vid,
-      ))
-      ->execute();
-    $this->assertUrlInboundAlter("community/$tid", "forum/$tid");
-    $this->assertUrlOutboundAlter("forum/$tid", "community/$tid");
-  }
-
-  /**
-   * Test current_path() and request_path().
-   */
-  function testCurrentUrlRequestedPath() {
-    $this->drupalGet('url-alter-test/bar');
-    $this->assertRaw('request_path=url-alter-test/bar', t('request_path() returns the requested path.'));
-    $this->assertRaw('current_path=url-alter-test/foo', t('current_path() returns the internal path.'));
-  }
-
-  /**
-   * Assert that an outbound path is altered to an expected value.
-   *
-   * @param $original
-   *   A string with the original path that is run through url().
-   * @param $final
-   *   A string with the expected result after url().
-   * @return
-   *   TRUE if $original was correctly altered to $final, FALSE otherwise.
-   */
-  protected function assertUrlOutboundAlter($original, $final) {
-    // Test outbound altering.
-    $result = url($original);
-    $base_path = base_path() . (variable_get('clean_url', '0') ? '' : '?q=');
-    $result = substr($result, strlen($base_path));
-    $this->assertIdentical($result, $final, t('Altered outbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
-  }
-
-  /**
-   * Assert that a inbound path is altered to an expected value.
-   *
-   * @param $original
-   *   A string with the aliased or un-normal path that is run through
-   *   drupal_get_normal_path().
-   * @param $final
-   *   A string with the expected result after url().
-   * @return
-   *   TRUE if $original was correctly altered to $final, FALSE otherwise.
-   */
-  protected function assertUrlInboundAlter($original, $final) {
-    // Test inbound altering.
-    $result = drupal_get_normal_path($original);
-    $this->assertIdentical($result, $final, t('Altered inbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
-  }
-}
diff --git a/modules/simpletest/tests/registry.test b/modules/simpletest/tests/registry.test
deleted file mode 100644
index ab05468..0000000
--- a/modules/simpletest/tests/registry.test
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-// $Id: registry.test,v 1.18 2010/07/10 01:57:32 dries Exp $
-
-class RegistryParseFileTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Registry parse file test',
-      'description' => 'Parse a simple file and check that its resources are saved to the database.',
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    $chrs = hash('sha256', microtime() . mt_rand());
-    $this->fileName = 'registry_test_' . substr($chrs, 0, 16);
-    $this->className = 'registry_test_class' . substr($chrs, 16, 16);
-    $this->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16);
-    parent::setUp();
-  }
-
-  /**
-   * testRegistryParseFile
-   */
-  function testRegistryParseFile() {
-    _registry_parse_file($this->fileName, $this->getFileContents());
-    foreach (array('className', 'interfaceName') as $resource) {
-      $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$resource))->fetchField();
-      $this->assertTrue($this->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$resource)));
-    }
-  }
-
-  /**
-   * getFileContents
-   */
-  function getFileContents() {
-    $file_contents = <<<CONTENTS
-<?php
-
-class {$this->className} {}
-
-interface {$this->interfaceName} {}
-
-CONTENTS;
-    return $file_contents;
-  }
-
-}
-
-class RegistryParseFilesTestCase extends DrupalWebTestCase {
-  protected $fileTypes = array('new', 'existing_changed');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Registry parse files test',
-      'description' => 'Read two a simple files from disc, and check that their resources are saved to the database.',
-      'group' => 'System'
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    // Create files with some php to parse - one 'new', one 'existing' so
-    // we test all the important code paths in _registry_parse_files.
-    foreach ($this->fileTypes as $fileType) {
-      $chrs = hash('sha256', microtime() . mt_rand());
-      $this->$fileType = new stdClass();
-      $this->$fileType->fileName = file_directory_path() . '/registry_test_' . substr($chrs, 0, 16);
-      $this->$fileType->className = 'registry_test_class' . substr($chrs, 16, 16);
-      $this->$fileType->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16);
-      $this->$fileType->contents = $this->getFileContents($fileType);
-      file_save_data($this->$fileType->contents, $this->$fileType->fileName);
-
-      if ($fileType == 'existing_changed') {
-        // Add a record with an incorrect hash.
-        $this->$fileType->fakeHash = hash('sha256', mt_rand());
-        db_insert('registry_file')
-          ->fields(array(
-            'hash' => $this->$fileType->fakeHash,
-            'filename' => $this->$fileType->fileName,
-          ))
-          ->execute();
-
-        // Insert some fake resource records.
-        foreach (array('class', 'interface') as $type) {
-          db_insert('registry')
-            ->fields(array(
-              'name' => $type . hash('sha256', microtime() . mt_rand()),
-              'type' => $type,
-              'filename' => $this->$fileType->fileName,
-            ))
-            ->execute();
-        }
-      }
-    }
-  }
-
-  /**
-   * testRegistryParseFiles
-   */
-  function testRegistryParseFiles() {
-    _registry_parse_files($this->getFiles());
-    foreach ($this->fileTypes as $fileType) {
-      // Test that we have all the right resources.
-      foreach (array('className', 'interfaceName') as $resource) {
-        $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$fileType->$resource))->fetchField();
-        $this->assertTrue($this->$fileType->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$fileType->$resource)));
-      }
-      // Test that we have the right hash.
-      $hash = db_query('SELECT hash FROM {registry_file} WHERE filename = :filename', array(':filename' => $this->$fileType->fileName))->fetchField();
-      $this->assertTrue(hash('sha256', $this->$fileType->contents) == $hash, t('sha-256 for "@filename" matched.' . $fileType . $hash, array('@filename' => $this->$fileType->fileName)));
-    }
-  }
-
-  /**
-   * getFiles
-   */
-  function getFiles() {
-    $files = array();
-    foreach ($this->fileTypes as $fileType) {
-      $files[$this->$fileType->fileName] = array('module' => '', 'weight' => 0);
-      if ($fileType == 'existing_changed') {
-        $files[$this->$fileType->fileName]['hash'] = $this->$fileType->fakeHash;
-      }
-    }
-    return $files;
-  }
-
-  /**
-   * getFileContents
-   */
-  function getFileContents($fileType) {
-    $file_contents = <<<CONTENTS
-<?php
-
-class {$this->$fileType->className} {}
-
-interface {$this->$fileType->interfaceName} {}
-
-CONTENTS;
-    return $file_contents;
-  }
-
-}
diff --git a/modules/simpletest/tests/schema.test b/modules/simpletest/tests/schema.test
deleted file mode 100644
index c8c297e..0000000
--- a/modules/simpletest/tests/schema.test
+++ /dev/null
@@ -1,199 +0,0 @@
-<?php
-// $Id: schema.test,v 1.19 2010/03/28 11:45:11 dries Exp $
-
-/**
- * @file
- * Tests for the Database Schema API.
- */
-
-/**
- * Unit tests for the Schema API.
- */
-class SchemaTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Schema API',
-      'description' => 'Tests table creation and modification via the schema API.',
-      'group' => 'Database',
-    );
-  }
-
-  /**
-   *
-   */
-  function testSchema() {
-    // Try creating a table.
-    $table_specification = array(
-      'description' => 'Schema table description.',
-      'fields' => array(
-        'id'  => array(
-          'type' => 'int',
-          'default' => NULL,
-        ),
-        'test_field'  => array(
-          'type' => 'int',
-          'not null' => TRUE,
-          'description' => 'Schema column description.',
-        ),
-      ),
-    );
-    db_create_table('test_table', $table_specification);
-
-    // Assert that the table exists.
-    $this->assertTrue(db_table_exists('test_table'), t('The table exists.'));
-
-    // Assert that the table comment has been set.
-    $this->checkSchemaComment($table_specification['description'], 'test_table');
-
-    // Assert that the column comment has been set.
-    $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
-
-    // An insert without a value for the column 'test_table' should fail.
-    $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
-
-    // Add a default value to the column.
-    db_field_set_default('test_table', 'test_field', 0);
-    // The insert should now succeed.
-    $this->assertTrue($this->tryInsert(), t('Insert with a default succeeded.'));
-
-    // Remove the default.
-    db_field_set_no_default('test_table', 'test_field');
-    // The insert should fail again.
-    $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
-
-    // Test for fake index and test for the boolean result of indexExists().
-    $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
-    $this->assertIdentical($index_exists, FALSE, t('Fake index does not exists'));
-    // Add index.
-    db_add_index('test_table', 'test_field', array('test_field'));
-    // Test for created index and test for the boolean result of indexExists().
-    $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
-    $this->assertIdentical($index_exists, TRUE, t('Index created.'));
-
-    // Rename the table.
-    db_rename_table('test_table', 'test_table2');
-
-    // Index should be renamed.
-    $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
-    $this->assertTrue($index_exists, t('Index was renamed.'));
-
-    // We need the default so that we can insert after the rename.
-    db_field_set_default('test_table2', 'test_field', 0);
-    $this->assertFalse($this->tryInsert(), t('Insert into the old table failed.'));
-    $this->assertTrue($this->tryInsert('test_table2'), t('Insert into the new table succeeded.'));
-
-    // We should have successfully inserted exactly two rows.
-    $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
-    $this->assertEqual($count, 2, t('Two fields were successfully inserted.'));
-
-    // Try to drop the table.
-    db_drop_table('test_table2');
-    $this->assertFalse(db_table_exists('test_table2'), t('The dropped table does not exist.'));
-
-    // Recreate the table.
-    db_create_table('test_table', $table_specification);
-    db_field_set_default('test_table', 'test_field', 0);
-    db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.'));
-
-    // Assert that the column comment has been set.
-    $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
-
-    // Change the new field to a serial column.
-    db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial')));
-
-    // Assert that the column comment has been set.
-    $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
-
-    $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
-    $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
-    $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
-    $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
-    $this->assertTrue($max2 > $max1, t('The serial is monotone.'));
-
-    $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
-    $this->assertEqual($count, 2, t('There were two rows.'));
-  }
-
-  function tryInsert($table = 'test_table') {
-    try {
-       db_insert($table)
-         ->fields(array('id' => mt_rand(10, 20)))
-         ->execute();
-      return TRUE;
-    }
-    catch (Exception $e) {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Checks that a table or column comment matches a given description.
-   *
-   * @param $description
-   *   The asserted description.
-   * @param $table
-   *   The table to test.
-   * @param $column
-   *   Optional column to test.
-   */
-  function checkSchemaComment($description, $table, $column = NULL) {
-    if (method_exists(Database::getConnection()->schema(), 'getComment')) {
-      $comment = Database::getConnection()->schema()->getComment($table, $column);
-      $this->assertEqual($comment, $description, t('The comment matches the schema description.'));
-    }
-  }
-
-  /**
-   * Tests creating unsigned columns and data integrity thereof.
-   */
-  function testUnsignedColumns() {
-    // First create the table with just a serial column.
-    $table_name = 'unsigned_table';
-    $table_spec = array(
-      'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)),
-      'primary key' => array('serial_column'),
-    );
-    $ret = array();
-    db_create_table($table_name, $table_spec);
-
-    // Now set up columns for the other types.
-    $types = array('int', 'float', 'numeric');
-    foreach ($types as $type) {
-      $column_spec = array('type' => $type, 'unsigned'=> TRUE);
-      if ($type == 'numeric') {
-        $column_spec += array('precision' => 10, 'scale' => 0);
-      }
-      $column_name = $type . '_column';
-      $table_spec['fields'][$column_name] = $column_spec;
-      db_add_field($table_name, $column_name, $column_spec);
-    }
-
-    // Finally, check each column and try to insert invalid values into them.
-    foreach($table_spec['fields'] as $column_name => $column_spec) {
-      $this->assertTrue(db_field_exists($table_name, $column_name), t('Unsigned @type column was created.', array('@type' => $column_spec['type'])));
-      $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), t('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type'])));
-    }
-  }
-
-  /**
-   * Tries to insert a negative value into columns defined as unsigned.
-   *
-   * @param $table_name
-   *   The table to insert
-   * @param $column_name
-   *   The column to insert
-   * @return
-   *   TRUE if the insert succeeded, FALSE otherwise
-   */
-  function tryUnsignedInsert($table_name, $column_name) {
-    try {
-      db_insert($table_name)
-         ->fields(array($column_name => -1))
-         ->execute();
-      return TRUE;
-    }
-    catch (Exception $e) {
-      return FALSE;
-    }
-  }
-}
diff --git a/modules/simpletest/tests/session.test b/modules/simpletest/tests/session.test
deleted file mode 100644
index 552f8a8..0000000
--- a/modules/simpletest/tests/session.test
+++ /dev/null
@@ -1,424 +0,0 @@
-<?php
-// $Id: session.test,v 1.29 2010/06/14 12:31:46 dries Exp $
-
-/**
- * @file
- * Provides SimpleTests for core session handling functionality.
- */
-
-class SessionTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Session tests',
-      'description' => 'Drupal session handling tests.',
-      'group' => 'Session'
-    );
-  }
-
-  function setUp() {
-    parent::setUp('session_test');
-  }
-
-  /**
-   * Tests for drupal_save_session() and drupal_session_regenerate().
-   */
-  function testSessionSaveRegenerate() {
-    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session'));
-    $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
-    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
-    $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
-    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
-
-    // Test session hardening code from SA-2008-044.
-    $user = $this->drupalCreateUser(array('access content'));
-
-    // Enable sessions.
-    $this->sessionReset($user->uid);
-
-    // Make sure the session cookie is set as HttpOnly.
-    $this->drupalLogin($user);
-    $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.'));
-    $this->drupalLogout();
-
-    // Verify that the session is regenerated if a module calls exit
-    // in hook_user_login().
-    user_save($user, array('name' => 'session_test_user'));
-    $user->name = 'session_test_user';
-    $this->drupalGet('session-test/id');
-    $matches = array();
-    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
-    $this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
-    $original_session = $matches[1];
-
-    // We cannot use $this->drupalLogin($user); because we exit in
-    // session_test_user_login() which breaks a normal assertion.
-    $edit = array(
-      'name' => $user->name,
-      'pass' => $user->pass_raw
-    );
-    $this->drupalPost('user', $edit, t('Log in'));
-    $this->drupalGet('user');
-    $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
-    $this->_logged_in = $pass;
-
-    $this->drupalGet('session-test/id');
-    $matches = array();
-    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
-    $this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
-    $this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));
-  }
-
-  /**
-   * Test data persistence via the session_test module callbacks. Also tests
-   * drupal_session_count() since session data is already generated here.
-   */
-  function testDataPersistence() {
-    $user = $this->drupalCreateUser(array('access content'));
-    // Enable sessions.
-    $this->sessionReset($user->uid);
-
-    $this->drupalLogin($user);
-
-    $value_1 = $this->randomName();
-    $this->drupalGet('session-test/set/' . $value_1);
-    $this->assertText($value_1, t('The session value was stored.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));
-
-    // Attempt to write over val_1. If drupal_save_session(FALSE) is working.
-    // properly, val_1 will still be set.
-    $value_2 = $this->randomName();
-    $this->drupalGet('session-test/no-set/' . $value_2);
-    $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
-
-    // Switch browser cookie to anonymous user, then back to user 1.
-    $this->sessionReset();
-    $this->sessionReset($user->uid);
-    $this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
-
-    // Logout the user and make sure the stored value no longer persists.
-    $this->drupalLogout();
-    $this->sessionReset();
-    $this->drupalGet('session-test/get');
-    $this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
-
-    // Now try to store some data as an anonymous user.
-    $value_3 = $this->randomName();
-    $this->drupalGet('session-test/set/' . $value_3);
-    $this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
-
-    // Try to store data when drupal_save_session(FALSE).
-    $value_4 = $this->randomName();
-    $this->drupalGet('session-test/no-set/' . $value_4);
-    $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
-    $this->drupalGet('session-test/get');
-    $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
-
-    // Login, the data should persist.
-    $this->drupalLogin($user);
-    $this->sessionReset($user->uid);
-    $this->drupalGet('session-test/get');
-    $this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
-
-    // Change session and create another user.
-    $user2 = $this->drupalCreateUser(array('access content'));
-    $this->sessionReset($user2->uid);
-    $this->drupalLogin($user2);
-  }
-
-  /**
-   * Test that empty anonymous sessions are destroyed.
-   */
-  function testEmptyAnonymousSession() {
-    // Verify that no session is automatically created for anonymous user.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-
-    // The same behavior is expected when caching is enabled.
-    variable_set('cache', 1);
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
-
-    // Start a new session by setting a message.
-    $this->drupalGet('session-test/set-message');
-    $this->assertSessionCookie(TRUE);
-    $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
-
-    // Display the message, during the same request the session is destroyed
-    // and the session cookie is unset.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(FALSE);
-    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
-    $this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
-    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
-
-    // Verify that session was destroyed.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
-    $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
-
-    // Verify that no session is created if drupal_save_session(FALSE) is called.
-    $this->drupalGet('session-test/set-message-but-dont-save');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-
-    // Verify that no message is displayed.
-    $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.'));
-  }
-
-  /**
-   * Reset the cookie file so that it refers to the specified user.
-   *
-   * @param $uid User id to set as the active session.
-   */
-  function sessionReset($uid = 0) {
-    // Close the internal browser.
-    $this->curlClose();
-    $this->loggedInUser = FALSE;
-
-    // Change cookie file for user.
-    $this->cookieFile = file_directory_path('temporary') . '/cookie.' . $uid . '.txt';
-    $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile;
-    $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE;
-    $this->drupalGet('session-test/get');
-    $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
-  }
-
-  /**
-   * Assert whether the SimpleTest browser sent a session cookie.
-   */
-  function assertSessionCookie($sent) {
-    if ($sent) {
-      $this->assertNotNull($this->session_id, t('Session cookie was sent.'));
-    }
-    else {
-      $this->assertNull($this->session_id, t('Session cookie was not sent.'));
-    }
-  }
-
-  /**
-   * Assert whether $_SESSION is empty at the beginning of the request.
-   */
-  function assertSessionEmpty($empty) {
-    if ($empty) {
-      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
-    }
-    else {
-      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
-    }
-  }
-}
-
-/**
- * Ensure that when running under https two session cookies are generated.
- */
-class SessionHttpsTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Session https handling',
-      'description' => 'Ensure that when running under https two session cookies are generated.',
-      'group' => 'Session'
-    );
-  }
-
-  public function setUp() {
-    parent::setUp('session_test');
-  }
-
-  protected function testHttpsSession() {
-    global $is_https;
-
-    if ($is_https) {
-      $secure_session_name = session_name();
-      $insecure_session_name = substr(session_name(), 1);
-    }
-    else {
-      $secure_session_name = 'S' . session_name();
-      $insecure_session_name = session_name();
-    }
-
-    $user = $this->drupalCreateUser(array('access administration pages'));
-
-    // Test HTTPS session handling by altering the form action to submit the
-    // login form through https.php, which creates a mock HTTPS request.
-    $this->drupalGet('user');
-    $form = $this->xpath('//form[@id="user-login"]');
-    $form[0]['action'] = $this->httpsUrl('user');
-    $edit = array('name' => $user->name, 'pass' => $user->pass_raw);
-    $this->drupalPost(NULL, $edit, t('Log in'));
-
-    // Test a second concurrent session.
-    $this->curlClose();
-    $this->drupalGet('user');
-    $form = $this->xpath('//form[@id="user-login"]');
-    $form[0]['action'] = $this->httpsUrl('user');
-    $this->drupalPost(NULL, $edit, t('Log in'));
-
-    // Check secure cookie on secure page.
-    $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute');
-    // Check insecure cookie is not set.
-    $this->assertFalse(isset($this->cookies[$insecure_session_name]));
-    $ssid = $this->cookies[$secure_session_name]['value'];
-    $this->assertSessionIds('', $ssid, 'Session has NULL for SID and a correct secure SID.');
-    $cookie = $secure_session_name . '=' . $ssid;
-
-    // Verify that user is logged in on secure URL.
-    $this->curlClose();
-    $this->drupalGet($this->httpsUrl('admin/config'), array(), array('Cookie: ' . $cookie));
-    $this->assertText(t('Configuration'));
-    $this->assertResponse(200);
-
-    // Verify that user is not logged in on non-secure URL.
-    if (!$is_https) {
-      $this->curlClose();
-      $this->drupalGet('admin/config', array(), array('Cookie: ' . $cookie));
-      $this->assertNoText(t('Configuration'));
-      $this->assertResponse(403);
-    }
-
-    // Clear browser cookie jar.
-    $this->cookies = array();
-
-    if ($is_https) {
-      // The functionality does not make sense when running on https.
-      return;
-    }
-
-    // Enable secure pages.
-    variable_set('https', TRUE);
-
-    $this->curlClose();
-    // Start an anonymous session on the insecure site.
-    $session_data = $this->randomName();
-    $this->drupalGet('session-test/set/' . $session_data);
-    // Check secure cookie on insecure page.
-    $this->assertFalse(isset($this->cookies[$secure_session_name]), 'The secure cookie is not sent on insecure pages.');
-    // Check insecure cookie on insecure page.
-    $this->assertFalse($this->cookies[$insecure_session_name]['secure'], 'The insecure cookie does not have the secure attribute');
-
-    // Store the anonymous cookie so we can validate that its session is killed
-    // after login.
-    $anonymous_cookie = $insecure_session_name . '=' . $this->cookies[$insecure_session_name]['value'];
-
-    // Check that password request form action is not secure.
-    $this->drupalGet('user/password');
-    $form = $this->xpath('//form[@id="user-pass"]');
-    $this->assertNotEqual(substr($form[0]['action'], 0, 6), 'https:', 'Password request form action is not secure');
-    $form[0]['action'] = $this->httpsUrl('user');
-
-    // Check that user login form action is secure.
-    $this->drupalGet('user');
-    $form = $this->xpath('//form[@id="user-login"]');
-    $this->assertEqual(substr($form[0]['action'], 0, 6), 'https:', 'Login form action is secure');
-    $form[0]['action'] = $this->httpsUrl('user');
-
-    $edit = array(
-      'name' => $user->name,
-      'pass' => $user->pass_raw,
-    );
-    $this->drupalPost(NULL, $edit, t('Log in'));
-    // Check secure cookie on secure page.
-    $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute');
-    // Check insecure cookie on secure page.
-    $this->assertFalse($this->cookies[$insecure_session_name]['secure'], 'The insecure cookie does not have the secure attribute');
-
-    $sid = $this->cookies[$insecure_session_name]['value'];
-    $ssid = $this->cookies[$secure_session_name]['value'];
-    $this->assertSessionIds($sid, $ssid, 'Session has both secure and insecure SIDs');
-    $cookies = array(
-      $insecure_session_name . '=' . $sid,
-      $secure_session_name . '=' . $ssid,
-    );
-
-    // Test that session data saved before login is still available on the
-    // authenticated session.
-    $this->drupalGet('session-test/get');
-    $this->assertText($session_data, 'Session correctly returned the stored data set by the anonymous session.');
-
-    foreach ($cookies as $cookie_key => $cookie) {
-      foreach (array('admin/config', $this->httpsUrl('admin/config')) as $url_key => $url) {
-        $this->curlClose();
-
-        $this->drupalGet($url, array(), array('Cookie: ' . $cookie));
-        if ($cookie_key == $url_key) {
-          $this->assertText(t('Configuration'));
-          $this->assertResponse(200);
-        }
-        else {
-          $this->assertNoText(t('Configuration'));
-          $this->assertResponse(403);
-        }
-      }
-    }
-
-    // Test that session data saved before login is not available using the 
-    // pre-login anonymous cookie.
-    $this->cookies = array();
-    $this->drupalGet('session-test/get', array('Cookie: ' . $anonymous_cookie));
-    $this->assertNoText($session_data, 'Initial anonymous session is inactive after login.');
-
-    // Clear browser cookie jar.
-    $this->cookies = array();
-
-    // Start an anonymous session on the secure site.
-    $this->drupalGet($this->httpsUrl('session-test/set/1'));
-
-    // Mock a login to the secure site using the secure session cookie.
-    $this->drupalGet('user');
-    $form = $this->xpath('//form[@id="user-login"]');
-    $form[0]['action'] = $this->httpsUrl('user');
-    $this->drupalPost(NULL, $edit, t('Log in'), array(), array('Cookie: ' . $secure_session_name . '=' . $this->cookies[$secure_session_name]['value']));
-
-    // Get the insecure session cookie set by the secure login POST request.
-    $headers = $this->drupalGetHeaders(TRUE);
-    strtok($headers[0]['set-cookie'], ';=');
-    $session_id = strtok(';=');
-
-    // Test that the user is also authenticated on the insecure site.
-    $this->drupalGet("user/{$user->uid}/edit", array(), array('Cookie: ' . $insecure_session_name . '=' . $session_id));
-    $this->assertResponse(200);
-  }
-
-  /**
-   * Test that there exists a session with two specific session IDs.
-   *
-   * @param $sid
-   *   The insecure session ID to search for.
-   * @param $ssid
-   *   The secure session ID to search for.
-   * @param $assertion_text
-   *   The text to display when we perform the assertion.
-   *
-   * @return
-   *   The result of assertTrue() that there's a session in the system that
-   *   has the given insecure and secure session IDs.
-   */
-  protected function assertSessionIds($sid, $ssid, $assertion_text) {
-    $args = array(
-      ':sid' => $sid,
-      ':ssid' => $ssid,
-    );
-    return $this->assertTrue(db_query('SELECT timestamp FROM {sessions} WHERE sid = :sid AND ssid = :ssid', $args)->fetchField(), $assertion_text);
-  }
-
-  protected function httpsUrl($url) {
-    global $base_url;
-    return $base_url . '/modules/simpletest/tests/https.php?q=' . $url;
-  }
-}
-
diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test
deleted file mode 100644
index 2d27c26..0000000
--- a/modules/simpletest/tests/theme.test
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-// $Id: theme.test,v 1.16 2010/04/29 05:22:06 webchick Exp $
-
-/**
- * @file
- * Tests for the theme API.
- */
-
-/**
- * Unit tests for the Theme API.
- */
-class ThemeUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Theme API',
-      'description' => 'Test low-level theme functions.',
-      'group' => 'Theme',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('theme_test');
-    theme_enable(array('test_theme'));
-  }
-
-  /**
-   * Test function theme_get_suggestions() for SA-CORE-2009-003.
-   */
-  function testThemeSuggestions() {
-    // Set the front page as something random otherwise the CLI
-    // test runner fails.
-    variable_set('site_frontpage', 'nobody-home');
-    $args = array('node', '1', 'edit');
-    $suggestions = theme_get_suggestions($args, 'page');
-    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1', 'page__node__edit'), t('Found expected node edit page suggestions'));
-    // Check attack vectors.
-    $args = array('node', '\\1');
-    $suggestions = theme_get_suggestions($args, 'page');
-    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\ from suggestions'));
-    $args = array('node', '1/');
-    $suggestions = theme_get_suggestions($args, 'page');
-    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid / from suggestions'));
-    $args = array('node', "1\0");
-    $suggestions = theme_get_suggestions($args, 'page');
-    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\0 from suggestions'));
-  }
-
-  /**
-  * Preprocess functions for the base hook should run even for suggestion implementations.
-  */
-  function testPreprocessForSuggestions() {
-    $this->drupalGet('theme-test/suggestion');
-    $this->assertText('test_theme_breadcrumb__suggestion: 1', t('Theme hook suggestion ran with data available from a preprocess function for the base hook.'));
-  }
-
-  /**
-   * Ensure page-front template suggestion is added when on front page.
-   */
-  function testFrontPageThemeSuggestion() {
-    $q = $_GET['q'];
-    // Set $_GET['q'] to node because theme_get_suggestions() will query it to
-    // see if we are on the front page.
-    $_GET['q'] = variable_get('site_frontpage', 'node');
-    $suggestions = theme_get_suggestions(explode('/', $_GET['q']), 'page');
-    // Set it back to not annoy the batch runner.
-    $_GET['q'] = $q;
-    $this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.'));
-  }
-}
-
-/**
- * Unit tests for theme_table().
- */
-class ThemeTableUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Theme Table',
-      'description' => 'Tests built-in theme functions.',
-      'group' => 'Theme',
-    );
-  }
-
-  /**
-   * Tableheader.js provides 'sticky' table headers, and is included by default.
-   */
-  function testThemeTableStickyHeaders() {
-    $header = array('one', 'two', 'three');
-    $rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
-    $this->content = theme('table', array('header' => $header, 'rows' => $rows));
-    $js = drupal_add_js();
-    $this->assertTrue(isset($js['misc/tableheader.js']), t('tableheader.js was included when $sticky = TRUE.'));
-    $this->assertRaw('sticky-enabled',  t('Table has a class of sticky-enabled when $sticky = TRUE.'));
-    drupal_static_reset('drupal_add_js');
-  }
-
-  /**
-   * If $sticky is FALSE, no tableheader.js should be included.
-   */
-  function testThemeTableNoStickyHeaders() {
-    $header = array('one', 'two', 'three');
-    $rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
-    $attributes = array();
-    $caption = NULL;
-    $colgroups = array();
-    $this->content = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes, 'caption' => $caption, 'colgroups' => $colgroups, 'sticky' => FALSE));
-    $js = drupal_add_js();
-    $this->assertFalse(isset($js['misc/tableheader.js']), t('tableheader.js was not included because $sticky = FALSE.'));
-    $this->assertNoRaw('sticky-enabled',  t('Table does not have a class of sticky-enabled because $sticky = FALSE.'));
-    drupal_static_reset('drupal_add_js');
-  }
-
-  /**
-   * Tests that the table header is printed correctly even if there are no rows,
-   * and that the empty text is displayed correctly.
-   */
-  function testThemeTableWithEmptyMessage() {
-    $header = array(
-      t('Header 1'),
-      array(
-        'data' => t('Header 2'),
-        'colspan' => 2,
-      ),
-    );
-    $this->content = theme('table', array('header' => $header, 'rows' => array(), 'empty' => t('No strings available.')));
-    $this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">No strings available.</td>', t('Correct colspan was set on empty message.'));
-    $this->assertRaw('<thead><tr><th>Header 1</th>', t('Table header was printed.'));
-  }
-
-}
-
-/**
- * Unit tests for theme_item_list().
- */
-class ThemeItemListUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Theme item list',
-      'description' => 'Test the theme_item_list() function.',
-      'group' => 'Theme',
-    );
-  }
-
-  /**
-   * Test nested list rendering.
-   */
-  function testNestedList() {
-    $items = array('a', array('data' => 'b', 'children' => array('c', 'd')), 'e');
-    $expected = '<div class="item-list"><ul><li class="first">a</li>
-<li>b<div class="item-list"><ul><li class="first">c</li>
-<li class="last">d</li>
-</ul></div></li>
-<li class="last">e</li>
-</ul></div>';
-    $output = theme('item_list', array('items' => $items, 'type' => 'ul', 'title' => NULL, 'attributes' => array()));
-    $this->assertIdentical($expected, $output, 'Nested list is rendered correctly.');
-  }
-}
-
-/**
- * Functional test for initialization of the theme system in hook_init().
- */
-class ThemeHookInitUnitTest extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Theme initialization in hook_init()',
-      'description' => 'Tests that the theme system can be correctly initialized in hook_init().',
-      'group' => 'Theme',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('theme_test');
-    variable_set('theme_default', 'garland');
-  }
-
-  /**
-   * Test that the theme system can generate output when called by hook_init().
-   */
-  function testThemeInitializationHookInit() {
-    $this->drupalGet('theme-test/hook-init');
-    $this->assertRaw('Themed output generated in hook_init()', t('Themed output generated in hook_init() correctly appears on the page.'));
-    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init()."));
-  }
-}
diff --git a/modules/simpletest/tests/unicode.test b/modules/simpletest/tests/unicode.test
deleted file mode 100644
index 42cb987..0000000
--- a/modules/simpletest/tests/unicode.test
+++ /dev/null
@@ -1,336 +0,0 @@
-<?php
-// $Id: unicode.test,v 1.6 2010/06/10 15:20:48 dries Exp $
-
-/**
- * @file
- * Various unicode handling tests.
- */
-
-/**
- * Test unicode handling features implemented in unicode.inc.
- */
-class UnicodeUnitTest extends DrupalWebTestCase {
-
-  /**
-   * Whether to run the extended version of the tests (including non latin1 characters).
-   *
-   * @var boolean
-   */
-  protected $extendedMode = FALSE;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Unicode handling',
-      'description' => 'Tests Drupal Unicode handling.',
-      'group' => 'System',
-    );
-  }
-
-  /**
-   * Test full unicode features implemented using the mbstring extension.
-   */
-  function testMbStringUnicode() {
-    global $multibyte;
-
-    // mbstring was not detected on this installation, there is no way to test
-    // multibyte features. Treat that as an exception.
-    if ($multibyte == UNICODE_SINGLEBYTE) {
-      $this->error(t('Unable to test Multibyte features: mbstring extension was not detected.'));
-    }
-
-    $multibyte = UNICODE_MULTIBYTE;
-
-    $this->extendedMode = TRUE;
-    $this->pass(t('Testing in mbstring mode'));
-
-    $this->helperTestStrToLower();
-    $this->helperTestStrToUpper();
-    $this->helperTestUcFirst();
-    $this->helperTestStrLen();
-    $this->helperTestSubStr();
-    $this->helperTestTruncate();
-  }
-
-  /**
-   * Test emulated unicode features.
-   */
-  function testEmulatedUnicode() {
-    global $multibyte;
-
-    $multibyte = UNICODE_SINGLEBYTE;
-
-    $this->extendedMode = FALSE;
-
-    $this->pass(t('Testing in emulated (best-effort) mode'));
-
-    $this->helperTestStrToLower();
-    $this->helperTestStrToUpper();
-    $this->helperTestUcFirst();
-    $this->helperTestStrLen();
-    $this->helperTestSubStr();
-    $this->helperTestTruncate();
-  }
-
-  function helperTestStrToLower() {
-    $testcase = array(
-      'tHe QUIcK bRoWn' => 'the quick brown',
-      'FrançAIS is ÜBER-åwesome' => 'français is über-åwesome',
-    );
-    if ($this->extendedMode) {
-      $testcase['ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ'] = 'αβγδεζηθικλμνξοσὠ';
-    }
-
-    foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_strtolower($input), $output, t('%input is lowercased as %output', array('%input' => $input, '%output' => $output)));
-    }
-  }
-
-  function helperTestStrToUpper() {
-    $testcase = array(
-      'tHe QUIcK bRoWn' => 'THE QUICK BROWN',
-      'FrançAIS is ÜBER-åwesome' => 'FRANÇAIS IS ÜBER-ÅWESOME',
-    );
-    if ($this->extendedMode) {
-      $testcase['αβγδεζηθικλμνξοσὠ'] = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ';
-    }
-
-    foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_strtoupper($input), $output, t('%input is uppercased as %output', array('%input' => $input, '%output' => $output)));
-    }
-  }
-
-  function helperTestUcFirst() {
-    $testcase = array(
-      'tHe QUIcK bRoWn' => 'THe QUIcK bRoWn',
-      'françAIS' => 'FrançAIS',
-      'über' => 'Über',
-      'åwesome' => 'Åwesome'
-    );
-    if ($this->extendedMode) {
-      $testcase['σion'] = 'Σion';
-    }
-
-    foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_ucfirst($input), $output, t('%input is ucfirst-ed as %output', array('%input' => $input, '%output' => $output)));
-    }
-  }
-
-  function helperTestStrLen() {
-    $testcase = array(
-      'tHe QUIcK bRoWn' => 15,
-      'ÜBER-åwesome' => 12,
-    );
-
-    foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_strlen($input), $output, t('%input length is %output', array('%input' => $input, '%output' => $output)));
-    }
-  }
-
-  function helperTestSubStr() {
-    $testcase = array(
-      //     012345678901234567890123
-      array('frànçAIS is über-åwesome', 0, 0,
-            ''),
-      array('frànçAIS is über-åwesome', 0, 1,
-            'f'),
-      array('frànçAIS is über-åwesome', 0, 8,
-            'frànçAIS'),
-      array('frànçAIS is über-åwesome', 0, 23,
-            'frànçAIS is über-åwesom'),
-      array('frànçAIS is über-åwesome', 0, 24,
-            'frànçAIS is über-åwesome'),
-      array('frànçAIS is über-åwesome', 0, 25,
-            'frànçAIS is über-åwesome'),
-      array('frànçAIS is über-åwesome', 0, 100,
-            'frànçAIS is über-åwesome'),
-      array('frànçAIS is über-åwesome', 4, 4,
-                'çAIS'),
-      array('frànçAIS is über-åwesome', 1, 0,
-            ''),
-      array('frànçAIS is über-åwesome', 100, 0,
-            ''),
-      array('frànçAIS is über-åwesome', -4, 2,
-                                'so'),
-      array('frànçAIS is über-åwesome', -4, 3,
-                                'som'),
-      array('frànçAIS is über-åwesome', -4, 4,
-                                'some'),
-      array('frànçAIS is über-åwesome', -4, 5,
-                                'some'),
-      array('frànçAIS is über-åwesome', -7, 10,
-                             'åwesome'),
-      array('frànçAIS is über-åwesome', 5, -10,
-                 'AIS is üb'),
-      array('frànçAIS is über-åwesome', 0, -10,
-            'frànçAIS is üb'),
-      array('frànçAIS is über-åwesome', 0, -1,
-        'frànçAIS is über-åwesom'),
-      array('frànçAIS is über-åwesome', -7, -2,
-                             'åweso'),
-      array('frànçAIS is über-åwesome', -7, -6,
-                             'å'),
-      array('frànçAIS is über-åwesome', -7, -7,
-                             ''),
-      array('frànçAIS is über-åwesome', -7, -8,
-                             ''),
-      array('...', 0, 2, '..'),
-      array('以呂波耳・ほへとち。リヌルヲ。', 1, 3,
-              '呂波耳'),
-
-    );
-
-    foreach ($testcase as $test) {
-      list($input, $start, $length, $output) = $test;
-      $result = drupal_substr($input, $start, $length);
-      $this->assertEqual($result, $output, t('%input substring at offset %offset for %length characters is %output (got %result)', array('%input' => $input, '%offset' => $start, '%length' => $length, '%output' => $output, '%result' => $result)));
-    }
-  }
-
-  /**
-   * Test decode_entities().
-   */
-  function testDecodeEntities() {
-    $testcase = array(
-      'Drupal' => 'Drupal',
-      '<script>' => '<script>',
-      '&lt;script&gt;' => '<script>',
-      '&#60;script&#62;' => '<script>',
-      '&amp;lt;script&amp;gt;' => '&lt;script&gt;',
-      '"' => '"',
-      '&#34;' => '"',
-      '&amp;#34;' => '&#34;',
-      '&quot;' => '"',
-      '&amp;quot;' => '&quot;',
-      "'" => "'",
-      '&#39;' => "'",
-      '&amp;#39;' => '&#39;',
-      '©' => '©',
-      '&copy;' => '©',
-      '&#169;' => '©',
-      '→' => '→',
-      '&#8594;' => '→',
-      '➼' => '➼',
-      '&#10172;' => '➼',
-      '&euro;' => '€',
-    );
-    foreach ($testcase as $input => $output) {
-      $this->assertEqual(decode_entities($input), $output, t('Make sure the decoded entity of @input is @output', array('@input' => $input, '@output' => $output)));
-    }
-  }
-
-  function testDecodeEntitiesExclusion() {
-    $testcase = array(
-      'Drupal' => 'Drupal',
-      '<script>' => '<script>',
-      '&lt;script&gt;' => '&lt;script>',
-      '&#60;script&#62;' => '&#60;script>',
-      '&amp;lt;script&amp;gt;' => '&amp;lt;script&amp;gt;',
-      '"' => '"',
-      '&#34;' => '&#34;',
-      '&amp;#34;' => '&amp;#34;',
-      '&quot;' => '&quot;',
-      '&amp;quot;' => '&amp;quot;',
-      "'" => "'",
-      '&#39;' => "'",
-      '&amp;#39;' => '&amp;#39;',
-      '©' => '©',
-      '&copy;' => '©',
-      '&#169;' => '©',
-      '→' => '→',
-      '&#8594;' => '→',
-      '➼' => '➼',
-      '&#10172;' => '➼',
-      '&euro;' => '€',
-    );
-    $exclude = array('<', '&', '"');
-    foreach ($testcase as $input => $output) {
-      $this->assertIdentical(decode_entities($input, $exclude), $output, t('Make sure the decoded entity of %input, excluding %excludes, is %output', array('%input' => $input, '%excludes' => implode(',', $exclude), '%output' => $output)));
-    }
-  }
-
-  /**
-   * Tests truncate_utf8().
-   */
-  function helperTestTruncate() {
-    // Each case is an array with input string, length to truncate to, and
-    // expected return value.
-
-    // Test non-wordsafe, non-ellipsis cases.
-    $non_wordsafe_non_ellipsis_cases = array(
-      array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
-      array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwesom'),
-      array('frànçAIS is über-åwesome', 17, 'frànçAIS is über-'),
-      array('以呂波耳・ほへとち。リヌルヲ。', 6, '以呂波耳・ほ'),
-    );
-    $this->runTruncateTests($non_wordsafe_non_ellipsis_cases, FALSE, FALSE);
-
-    // Test non-wordsafe, ellipsis cases.
-    $non_wordsafe_ellipsis_cases = array(
-      array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
-      array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwe...'),
-      array('frànçAIS is über-åwesome', 17, 'frànçAIS is üb...'),
-    );
-    $this->runTruncateTests($non_wordsafe_ellipsis_cases, FALSE, TRUE);
-
-    // Test wordsafe, ellipsis cases.
-    $wordsafe_ellipsis_cases = array(
-      array('123', 1, '.'),
-      array('123', 2, '..'),
-      array('123', 3, '123'),
-      array('1234', 3, '...'),
-      array('1234567890', 10, '1234567890'),
-      array('12345678901', 10, '1234567...'),
-      array('12345678901', 11, '12345678901'),
-      array('123456789012', 11, '12345678...'),
-      array('12345 7890', 10, '12345 7890'),
-      array('12345 7890', 9, '12345...'),
-      array('123 567 90', 10, '123 567 90'),
-      array('123 567 901', 10, '123 567...'),
-      array('Stop. Hammertime.', 17, 'Stop. Hammertime.'),
-      array('Stop. Hammertime.', 16, 'Stop....'),
-      array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
-      array('frànçAIS is über-åwesome', 23, 'frànçAIS is über...'),
-      array('frànçAIS is über-åwesome', 17, 'frànçAIS is...'),
-      array('¿Dónde está el niño?', 20, '¿Dónde está el niño?'),
-      array('¿Dónde está el niño?', 19, '¿Dónde está el...'),
-      array('¿Dónde está el niño?', 15, '¿Dónde está...'),
-      array('¿Dónde está el niño?', 10, '¿Dónde...'),
-      array('Help! Help! Help!', 17, 'Help! Help! Help!'),
-      array('Help! Help! Help!', 16, 'Help! Help!...'),
-      array('Help! Help! Help!', 15, 'Help! Help!...'),
-      array('Help! Help! Help!', 14, 'Help! Help!...'),
-      array('Help! Help! Help!', 13, 'Help! Help...'),
-      array('Help! Help! Help!', 12, 'Help!...'),
-      array('Help! Help! Help!', 11, 'Help!...'),
-      array('Help! Help! Help!', 10, 'Help!...'),
-      array('Help! Help! Help!', 9, 'Help!...'),
-      array('Help! Help! Help!', 8, 'Help!...'),
-      array('Help! Help! Help!', 7, 'Help...'),
-      array('Help! Help! Help!', 6, 'Hel...'),
-      array('Help! Help! Help!', 5, 'He...'),
-    );
-    $this->runTruncateTests($wordsafe_ellipsis_cases, TRUE, TRUE);
-  }
-
-  /**
-   * Runs test cases for helperTestTruncate().
-   *
-   * Runs each test case through truncate_utf8() and compares the output
-   * to the expected output.
-   *
-   * @param $cases
-   *   Cases array. Each case is an array with the input string, length to
-   *   truncate to, and expected output.
-   * @param $wordsafe
-   *   TRUE to use word-safe truncation, FALSE to not use word-safe truncation.
-   * @param $ellipsis
-   *   TRUE to append ... if the input is truncated, FALSE to not append ....
-   */
-  function runTruncateTests($cases, $wordsafe, $ellipsis) {
-    foreach ($cases as $case) {
-      list($input, $max_length, $expected) = $case;
-      $output = truncate_utf8($input, $max_length, $wordsafe, $ellipsis);
-      $this->assertEqual($output, $expected, t('%input truncate to %length characters with %wordsafe, %ellipsis is %expected (got %output)', array('%input' => $input, '%length' => $max_length, '%output' => $output, '%expected' => $expected, '%wordsafe' => ($wordsafe ? 'word-safe' : 'not word-safe'), '%ellipsis' => ($ellipsis ? 'ellipsis' : 'not ellipsis'))));
-    }
-  }
-}
diff --git a/modules/simpletest/tests/update.test b/modules/simpletest/tests/update.test
deleted file mode 100644
index 944dba7..0000000
--- a/modules/simpletest/tests/update.test
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-// $Id: update.test,v 1.2 2010/04/28 05:28:22 webchick Exp $
-
-/**
- * @file
- * Tests for the update system.
- */
-
-/**
- * Tests for the update dependency ordering system.
- */
-class UpdateDependencyOrderingTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Update dependency ordering',
-      'description' => 'Test that update functions are run in the proper order.',
-      'group' => 'Update API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('update_test_1', 'update_test_2', 'update_test_3');
-    require_once DRUPAL_ROOT . '/includes/update.inc';
-  }
-
-  /**
-   * Test that updates within a single module run in the correct order.
-   */
-  function testUpdateOrderingSingleModule() {
-    $starting_updates = array(
-      'update_test_1' => 7000,
-    );
-    $expected_updates = array(
-      'update_test_1_update_7000',
-      'update_test_1_update_7001',
-      'update_test_1_update_7002',
-    );
-    $actual_updates = array_keys(update_resolve_dependencies($starting_updates));
-    $this->assertEqual($expected_updates, $actual_updates, t('Updates within a single module run in the correct order.'));
-  }
-
-  /**
-   * Test that dependencies between modules are resolved correctly.
-   */
-  function testUpdateOrderingModuleInterdependency() {
-    $starting_updates = array(
-      'update_test_2' => 7000,
-      'update_test_3' => 7000,
-    );
-    $update_order = array_keys(update_resolve_dependencies($starting_updates));
-    // Make sure that each dependency is satisfied.
-    $first_dependency_satisfied = array_search('update_test_2_update_7000', $update_order) < array_search('update_test_3_update_7000', $update_order);
-    $this->assertTrue($first_dependency_satisfied, t('The dependency of the second module on the first module is respected by the update function order.'));
-    $second_dependency_satisfied = array_search('update_test_3_update_7000', $update_order) < array_search('update_test_2_update_7001', $update_order);
-    $this->assertTrue($second_dependency_satisfied, t('The dependency of the first module on the second module is respected by the update function order.'));
-  }
-}
-
-/**
- * Tests for missing update dependencies.
- */
-class UpdateDependencyMissingTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Missing update dependencies',
-      'description' => 'Test that missing update dependencies are correctly flagged.',
-      'group' => 'Update API',
-    );
-  }
-
-  function setUp() {
-    // Only install update_test_2.module, even though its updates have a
-    // dependency on update_test_3.module.
-    parent::setUp('update_test_2');
-    require_once DRUPAL_ROOT . '/includes/update.inc';
-  }
-
-  function testMissingUpdate() {
-    $starting_updates = array(
-      'update_test_2' => 7000,
-    );
-    $update_graph = update_resolve_dependencies($starting_updates);
-    $this->assertTrue($update_graph['update_test_2_update_7000']['allowed'], t("The module's first update function is allowed to run, since it does not have any missing dependencies."));
-    $this->assertFalse($update_graph['update_test_2_update_7001']['allowed'], t("The module's second update function is not allowed to run, since it has a direct dependency on a missing update."));
-    $this->assertFalse($update_graph['update_test_2_update_7002']['allowed'], t("The module's third update function is not allowed to run, since it has an indirect dependency on a missing update."));
-  }
-}
-
-/**
- * Tests for the invocation of hook_update_dependencies().
- */
-class UpdateDependencyHookInvocationTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Update dependency hook invocation',
-      'description' => 'Test that the hook invocation for determining update dependencies works correctly.',
-      'group' => 'Update API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('update_test_1', 'update_test_2');
-    require_once DRUPAL_ROOT . '/includes/update.inc';
-  }
-
-  /**
-   * Test the structure of the array returned by hook_update_dependencies().
-   */
-  function testHookUpdateDependencies() {
-    $update_dependencies = update_retrieve_dependencies();
-    $this->assertTrue($update_dependencies['system'][7000]['update_test_1'] == 7000, t('An update function that has a dependency on two separate modules has the first dependency recorded correctly.'));
-    $this->assertTrue($update_dependencies['system'][7000]['update_test_2'] == 7001, t('An update function that has a dependency on two separate modules has the second dependency recorded correctly.'));
-    $this->assertTrue($update_dependencies['system'][7001]['update_test_1'] == 7002, t('An update function that depends on more than one update from the same module only has the dependency on the higher-numbered update function recorded.'));
-  }
-}
-
diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test
deleted file mode 100644
index 7f2ddd2..0000000
--- a/modules/simpletest/tests/upgrade/upgrade.test
+++ /dev/null
@@ -1,351 +0,0 @@
-<?php
-// $Id: upgrade.test,v 1.2 2010/06/29 00:21:00 webchick Exp $
-
-/**
- * Perform end-to-end tests of the upgrade path.
- */
-abstract class UpgradePathTestCase extends DrupalWebTestCase {
-
-  /**
-   * The file path to the dumped database to load into the child site.
-   */
-  var $databaseDumpFile = NULL;
-
-  /**
-   * Flag that indicates whether the child site has been upgraded.
-   */
-  var $upgradedSite = FALSE;
-
-  /**
-   * Array of errors triggered during the upgrade process.
-   */
-  var $upgradeErrors = array();
-
-  /**
-   * Override of DrupalWebTestCase::setUp() specialized for upgrade testing.
-   */
-  protected function setUp() {
-    global $user, $language, $conf;
-
-    // Reset flags.
-    $this->upgradedSite = FALSE;
-    $this->upgradeErrors = array();
-
-    // Generate a temporary prefixed database to ensure that tests have a clean starting point.
-    $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
-    db_update('simpletest_test_id')
-      ->fields(array('last_prefix' => $this->databasePrefix))
-      ->condition('test_id', $this->testId)
-      ->execute();
-
-    // Clone the current connection and replace the current prefix.
-    $connection_info = Database::getConnectionInfo('default');
-    Database::renameConnection('default', 'simpletest_original_default');
-    foreach ($connection_info as $target => $value) {
-      $connection_info[$target]['prefix'] = array(
-        'default' => $value['prefix']['default'] . $this->databasePrefix,
-      );
-    }
-    Database::addConnectionInfo('default', 'default', $connection_info['default']);
-
-    // Store necessary current values before switching to prefixed database.
-    $this->originalLanguage = $language;
-    $this->originalLanguageDefault = variable_get('language_default');
-    $this->originalFileDirectory = file_directory_path();
-    $this->originalProfile = drupal_get_profile();
-    $clean_url_original = variable_get('clean_url', 0);
-
-    // Unregister the registry.
-    // This is required to make sure that the database layer works properly.
-    spl_autoload_unregister('drupal_autoload_class');
-    spl_autoload_unregister('drupal_autoload_interface');
-
-    // Create test directories ahead of installation so fatal errors and debug
-    // information can be logged during installation process.
-    // Use mock files directories with the same prefix as the database.
-    $public_files_directory  = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10);
-    $private_files_directory = $public_files_directory . '/private';
-    $temp_files_directory    = $private_files_directory . '/temp';
-
-    // Create the directories.
-    file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
-    file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY);
-    file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY);
-    $this->generatedTestFiles = FALSE;
-
-    // Log fatal errors.
-    ini_set('log_errors', 1);
-    ini_set('error_log', $public_files_directory . '/error.log');
-
-    // Reset all statics and variables to perform tests in a clean environment.
-    $conf = array();
-
-    // Load the database from the portable PHP dump.
-    require $this->databaseDumpFile;
-
-    // Set path variables.
-    $this->variable_set('file_public_path', $public_files_directory);
-    $this->variable_set('file_private_path', $private_files_directory);
-    $this->variable_set('file_temporary_path', $temp_files_directory);
-
-    $this->pass('Finished loading the dump.');
-
-    // Load user 1.
-    $this->originalUser = $user;
-    drupal_save_session(FALSE);
-    $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject();
-
-    // Generate and set a session cookie.
-    $this->curlInitialize();
-    $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
-    curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($this->session_name) . '=' . rawurlencode($sid));
-
-    // Force our way into the session of the child site.
-    drupal_save_session(TRUE);
-    _drupal_session_write($sid, '');
-    drupal_save_session(FALSE);
-
-    // Restore necessary variables.
-    $this->variable_set('clean_url', $clean_url_original);
-    $this->variable_set('site_mail', 'simpletest@example.com');
-
-    drupal_set_time_limit($this->timeLimit);
-  }
-
-  /**
-   * Override of DrupalWebTestCase::tearDown() specialized for upgrade testing.
-   */
-  protected function tearDown() {
-    global $user, $language;
-
-    // In case a fatal error occured that was not in the test process read the
-    // log to pick up any fatal errors.
-    simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
-
-    // Delete temporary files directory.
-    file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
-
-    // Get back to the original connection.
-    Database::removeConnection('default');
-    Database::renameConnection('simpletest_original_default', 'default');
-
-    // Remove all prefixed tables.
-    $tables = db_find_tables($this->databasePrefix . '%');
-    foreach ($tables as $table) {
-      db_drop_table($table);
-    }
-
-    // Return the user to the original one.
-    $user = $this->originalUser;
-    drupal_save_session(TRUE);
-
-    // Ensure that internal logged in variable and cURL options are reset.
-    $this->loggedInUser = FALSE;
-    $this->additionalCurlOptions = array();
-
-    // Reload module list and implementations to ensure that test module hooks
-    // aren't called after tests.
-    module_list(TRUE);
-    module_implements('', FALSE, TRUE);
-
-    // Reset the Field API.
-    field_cache_clear();
-
-    // Rebuild caches.
-    parent::refreshVariables();
-
-    // Reset language.
-    $language = $this->originalLanguage;
-    if ($this->originalLanguageDefault) {
-      $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
-    }
-
-    // Close the CURL handler.
-    $this->curlClose();
-  }
-
-  /**
-   * Specialized variable_set() that works even if the child site is not upgraded.
-   *
-   * @param $name
-   *   The name of the variable to set.
-   * @param $value
-   *   The value to set. This can be any PHP data type; these functions take care
-   *   of serialization as necessary.
-   */
-  protected function variable_set($name, $value) {
-    db_delete('variable')
-      ->condition('name', $name)
-      ->execute();
-    db_insert('variable')
-      ->fields(array(
-        'name' => $name,
-        'value' => serialize($value),
-      ))
-      ->execute();
-
-    try {
-      cache_clear_all('variables', 'cache');
-      cache_clear_all('variables', 'cache_bootstrap');
-    }
-    // Since cache_bootstrap won't exist in a Drupal 6 site, ignore the
-    // exception if the above fails.
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Specialized refreshVariables().
-   */
-  protected function refreshVariables() {
-    // No operation if the child has not been upgraded yet.
-    if (!$this->upgradedSite) {
-      return parent::refreshVariables();
-    }
-  }
-
-  /**
-   * Perform the upgrade.
-   *
-   * @param $register_errors
-   *   Register the errors during the upgrade process as failures.
-   * @return
-   *   TRUE if the upgrade succeeded, FALSE otherwise.
-   */
-  protected function performUpgrade($register_errors = TRUE) {
-    $update_url = $GLOBALS['base_url'] . '/update.php';
-
-    // Load the first update screen.
-    $this->drupalGet($update_url, array('external' => TRUE));
-    if (!$this->assertResponse(200)) {
-      return FALSE;
-    }
-
-    // Continue.
-    $this->drupalPost(NULL, array(), t('Continue'));
-    if (!$this->assertResponse(200)) {
-      return FALSE;
-    }
-
-    // Go!
-    $this->drupalPost(NULL, array(), t('Apply pending updates'));
-    if (!$this->assertResponse(200)) {
-      return FALSE;
-    }
-
-    // Check for errors during the update process.
-    foreach ($this->xpath('//li[@class=:class]', array(':class' => 'failure')) as $element) {
-      $message = strip_tags($element->asXML());
-      $this->upgradeErrors[] = $message;
-      if ($register_errors) {
-        $this->fail($message);
-      }
-    }
-
-    if (!empty($this->upgradeErrors)) {
-      // Upgrade failed, the installation might be in an inconsistent state,
-      // don't process.
-      return FALSE;
-    }
-
-    // Check if there still are pending updates.
-    $this->drupalGet($update_url, array('external' => TRUE));
-    $this->drupalPost(NULL, array(), t('Continue'));
-    if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) {
-      return FALSE;
-    }
-
-    // Upgrade succeed, rebuild the environment so that we can call the API
-    // of the child site directly from this request.
-    $this->upgradedSite = TRUE;
-
-    // Reload module list and implementations.
-    module_list(TRUE);
-    module_implements('', FALSE, TRUE);
-
-    // Rebuild caches.
-    drupal_static_reset();
-    drupal_flush_all_caches();
-
-    // Register actions declared by any modules.
-    actions_synchronize();
-
-    // Reload global $conf array and permissions.
-    $this->refreshVariables();
-    $this->checkPermissions(array(), TRUE);
-
-    return TRUE;
-  }
-
-}
-
-/**
- * Perform basic upgrade tests.
- *
- * Load a bare installation of Drupal 6 and run the upgrade process on it.
- *
- * The install only contains dblog (although it's optional, it's only so that
- * another hook_watchdog module can take its place, the site is not functional
- * without watchdog) and update.
- */
-class BasicUpgradePath extends UpgradePathTestCase {
-  public static function getInfo() {
-    return array(
-      'name'  => 'Basic upgrade path',
-      'description'  => 'Basic upgrade path tests.',
-      'group' => 'Upgrade path',
-    );
-  }
-
-  public function setUp() {
-    // Path to the database dump.
-    $this->databaseDumpFile = drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.bare.database.php';
-    parent::setUp();
-  }
-
-  /**
-   * Test a failed upgrade, and verify that the failure is reported.
-   */
-  public function testFailedUpgrade() {
-    // Destroy a table that the upgrade process needs.
-    db_drop_table('access');
-    // Assert that the upgrade fails.
-    $this->assertFalse($this->performUpgrade(FALSE), t('A failed upgrade should return messages.'));
-  }
-
-  /**
-   * Test a successful upgrade.
-   */
-  public function testBasicUpgrade() {
-    $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
-
-    // Hit the frontpage.
-    $this->drupalGet('');
-    $this->assertResponse(200);
-
-    // Verify that we are still logged in.
-    $this->drupalGet('user');
-    $this->clickLink(t('Edit'));
-    $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.'));
-
-    // Logout and verify that we can login back in with our initial password.
-    $this->drupalLogout();
-    $this->drupalLogin((object) array(
-      'uid' => 1,
-      'name' => 'admin',
-      'pass_raw' => 'admin',
-    ));
-
-    // Test that the site name is correctly displayed.
-    $this->assertText('Drupal 6', t('The site name is correctly displayed.'));
-
-    // Verify that the main admin sections are available.
-    $this->drupalGet('admin');
-    $this->assertText(t('Content'));
-    $this->assertText(t('Appearance'));
-    $this->assertText(t('People'));
-    $this->assertText(t('Configuration'));
-    $this->assertText(t('Reports'));
-    $this->assertText(t('Structure'));
-    $this->assertText(t('Modules'));
-  }
-}
diff --git a/modules/simpletest/tests/xmlrpc.test b/modules/simpletest/tests/xmlrpc.test
deleted file mode 100644
index 7625a8f..0000000
--- a/modules/simpletest/tests/xmlrpc.test
+++ /dev/null
@@ -1,220 +0,0 @@
-<?php
-// $Id: xmlrpc.test,v 1.17 2010/05/06 05:59:31 webchick Exp $
-
-/**
- * Perform basic XML-RPC tests that do not require addition callbacks.
- */
-class XMLRPCBasicTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name'  => 'XML-RPC basic',
-      'description'  => 'Perform basic XML-RPC tests that do not require additional callbacks.',
-      'group' => 'XML-RPC',
-    );
-  }
-
-  /**
-   * Ensure that a basic XML-RPC call with no parameters works.
-   */
-  protected function testListMethods() {
-    // Minimum list of methods that should be included.
-    $minimum = array(
-      'system.multicall',
-      'system.methodSignature',
-      'system.getCapabilities',
-      'system.listMethods',
-      'system.methodHelp',
-    );
-
-    // Invoke XML-RPC call to get list of methods.
-    $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
-    $methods = xmlrpc($url, 'system.listMethods');
-
-    // Ensure that the minimum methods were found.
-    $count = 0;
-    foreach ($methods as $method) {
-      if (in_array($method, $minimum)) {
-        $count++;
-      }
-    }
-
-    $this->assertEqual($count, count($minimum), 'system.listMethods returned at least the minimum listing');
-  }
-
-  /**
-   * Ensure that XML-RPC correctly handles invalid messages when parsing.
-   */
-  protected function testInvalidMessageParsing() {
-    $invalid_messages = array(
-      array(
-        'message' => xmlrpc_message(''),
-        'assertion' => t('Empty message correctly rejected during parsing.'),
-      ),
-      array(
-        'message' => xmlrpc_message('<?xml version="1.0" encoding="ISO-8859-1"?>'),
-        'assertion' => t('Empty message with XML declaration correctly rejected during parsing.'),
-      ),
-      array(
-        'message' => xmlrpc_message('<?xml version="1.0"?><params><param><value><string>value</string></value></param></params>'),
-        'assertion' => t('Non-empty message without a valid message type is rejected during parsing.'),
-      ),
-      array(
-        'message' => xmlrpc_message('<methodResponse><params><param><value><string>value</string></value></param></methodResponse>'),
-        'assertion' => t('Non-empty malformed message is rejected during parsing.'),
-      ),
-    );
-
-    foreach ($invalid_messages as $assertion) {
-      $this->assertFalse(xmlrpc_message_parse($assertion['message']), $assertion['assertion']);
-    }
-  }
-}
-
-class XMLRPCValidator1IncTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name'  => 'XML-RPC validator',
-      'description'  => 'See !validator-link.', array('!validator-link' => l('the xmlrpc validator1 specification', 'http://www.xmlrpc.com/validator1Docs')),
-      'group' => 'XML-RPC',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('xmlrpc_test');
-  }
-
-  /**
-   * Run validator1 tests.
-   */
-  function testValidator1() {
-    $xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
-    srand();
-    mt_srand();
-
-
-    $array_1 = array(array('curly' => mt_rand(-100, 100)),
-                  array('curly' => mt_rand(-100, 100)),
-                  array('larry' => mt_rand(-100, 100)),
-                  array('larry' => mt_rand(-100, 100)),
-                  array('moe' => mt_rand(-100, 100)),
-                  array('moe' => mt_rand(-100, 100)),
-                  array('larry' => mt_rand(-100, 100)));
-    shuffle($array_1);
-    $l_res_1 = xmlrpc_test_arrayOfStructsTest($array_1);
-    $r_res_1 = xmlrpc($xml_url, 'validator1.arrayOfStructsTest', $array_1);
-    $this->assertIdentical($l_res_1, $r_res_1, 'array of structs test: %s');
-
-
-    $string_2 = 't\'&>>zf"md>yr>xlcev<h<"k&j<og"w&&>">>uai"np&s>>q\'&b<>"&&&';
-    $l_res_2 = xmlrpc_test_countTheEntities($string_2);
-    $r_res_2 = xmlrpc($xml_url, 'validator1.countTheEntities', $string_2);
-    $this->assertIdentical($l_res_2, $r_res_2, 'count the entities test: %s');
-
-
-    $struct_3 = array('moe' => mt_rand(-100, 100), 'larry' => mt_rand(-100, 100), 'curly' => mt_rand(-100, 100), 'homer' => mt_rand(-100, 100));
-    $l_res_3 = xmlrpc_test_easyStructTest($struct_3);
-    $r_res_3 = xmlrpc($xml_url, 'validator1.easyStructTest', $struct_3);
-    $this->assertIdentical($l_res_3, $r_res_3, 'easy struct test: %s');
-
-
-    $struct_4 = array('sub1' => array('bar' => 13),
-                    'sub2' => 14,
-                    'sub3' => array('foo' => 1, 'baz' => 2),
-                    'sub4' => array('ss' => array('sss' => array('ssss' => 'sssss'))));
-    $l_res_4 = xmlrpc_test_echoStructTest($struct_4);
-    $r_res_4 = xmlrpc($xml_url, 'validator1.echoStructTest', $struct_4);
-    $this->assertIdentical($l_res_4, $r_res_4, 'echo struct test: %s');
-
-    $int_5     = mt_rand(-100, 100);
-    $bool_5    = (($int_5 % 2) == 0);
-    $string_5  = $this->randomName();
-    $double_5  = (double)(mt_rand(-1000, 1000) / 100);
-    $time_5    = REQUEST_TIME;
-    $base64_5  = $this->randomName(100);
-    $l_res_5 = xmlrpc_test_manyTypesTest($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), $base64_5);
-    $l_res_5[5] = $l_res_5[5]->data; /* override warpping */
-    $r_res_5 = xmlrpc($xml_url, 'validator1.manyTypesTest', $int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5));
-    /* Contains objects, objects are not equal */
-    // See http://drupal.org/node/37766 why this currently fails
-    $this->assertEqual($l_res_5, $r_res_5, 'many types test: %s');
-
-
-    $size = mt_rand(100, 200);
-    $array_6 = array();
-    for ($i = 0; $i < $size; $i++) {
-      $array_6[] = $this->randomName(mt_rand(8, 12));
-    }
-
-    $l_res_6 = xmlrpc_test_moderateSizeArrayCheck($array_6);
-    $r_res_6 = xmlrpc($xml_url, 'validator1.moderateSizeArrayCheck', $array_6);
-    $this->assertIdentical($l_res_6, $r_res_6, 'moderate size array check: %s');
-
-
-    $struct_7 = array();
-    for ($y = 2000; $y < 2002; $y++) {
-      for ($m = 3; $m < 5; $m++) {
-        for ($d = 1; $d < 6; $d++) {
-          $ys = (string) $y;
-          $ms = sprintf('%02d', $m);
-          $ds = sprintf('%02d', $d);
-          $struct_7[$ys][$ms][$ds]['moe']   = mt_rand(-100, 100);
-          $struct_7[$ys][$ms][$ds]['larry'] = mt_rand(-100, 100);
-          $struct_7[$ys][$ms][$ds]['curly'] = mt_rand(-100, 100);
-        }
-      }
-    }
-    $l_res_7 = xmlrpc_test_nestedStructTest($struct_7);
-    $r_res_7 = xmlrpc($xml_url, 'validator1.nestedStructTest', $struct_7);
-    $this->assertIdentical($l_res_7, $r_res_7, 'nested struct test: %s');
-
-
-    $int_8 = mt_rand(-100, 100);
-    $l_res_8 = xmlrpc_test_simpleStructReturnTest($int_8);
-    $r_res_8 = xmlrpc($xml_url, 'validator1.simpleStructReturnTest', $int_8);
-    $this->assertIdentical($l_res_8, $r_res_8, 'simple struct test: %s');
-
-    /* Now test multicall */
-    $x = array();
-    $x[] = array('validator1.arrayOfStructsTest',     $array_1);
-    $x[] = array('validator1.countTheEntities',       $string_2);
-    $x[] = array('validator1.easyStructTest',         $struct_3);
-    $x[] = array('validator1.echoStructTest',         $struct_4);
-    $x[] = array('validator1.manyTypesTest',          $int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5));
-    $x[] = array('validator1.moderateSizeArrayCheck', $array_6);
-    $x[] = array('validator1.nestedStructTest',       $struct_7);
-    $x[] = array('validator1.simpleStructReturnTest', $int_8);
-
-    $a_l_res = array($l_res_1, $l_res_2, $l_res_3, $l_res_4, $l_res_5, $l_res_6, $l_res_7, $l_res_8);
-    $a_r_res = xmlrpc($xml_url, $x);
-    $this->assertEqual($a_l_res, $a_r_res, 'multicall equals result');
-  }
-}
-
-class XMLRPCMessagesTestCase extends DrupalWebTestCase {
-  public static function getInfo() {
-    return array(
-      'name'  => 'XML-RPC message',
-      'description' => 'Test large messages.',
-      'group' => 'XML-RPC',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('xmlrpc_test');
-  }
-
-  /**
-   * Make sure that XML-RPC can transfer large messages.
-   */
-  function testSizedMessages() {
-    $xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
-    $sizes = array(8, 80, 160);
-    foreach ($sizes as $size) {
-      $xml_message_l = xmlrpc_test_message_sized_in_kb($size);
-      $xml_message_r = xmlrpc($xml_url, 'messages.messageSizedInKB', $size);
-
-      $this->assertEqual($xml_message_l, $xml_message_r, t('XML-RPC messages.messageSizedInKB of %s Kb size received', array('%s' => $size)));
-    }
-  }
-}
diff --git a/modules/system/tests/actions.test b/modules/system/tests/actions.test
new file mode 100644
index 0000000..8e8f176
--- /dev/null
+++ b/modules/system/tests/actions.test
@@ -0,0 +1,127 @@
+<?php
+// $Id: actions.test,v 1.16 2010/05/26 11:26:49 dries Exp $
+
+class ActionsConfigurationTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Actions configuration',
+      'description' => 'Tests complex actions configuration by adding, editing, and deleting a complex action.',
+      'group' => 'Actions',
+    );
+  }
+
+  /**
+   * Test the configuration of advanced actions through the administration
+   * interface.
+   */
+  function testActionConfiguration() {
+    // Create a user with permission to view the actions administration pages.
+    $user = $this->drupalCreateUser(array('administer actions'));
+    $this->drupalLogin($user);
+
+    // Make a POST request to admin/config/system/actions/manage.
+    $edit = array();
+    $edit['action'] = drupal_hash_base64('system_goto_action');
+    $this->drupalPost('admin/config/system/actions/manage', $edit, t('Create'));
+
+    // Make a POST request to the individual action configuration page.
+    $edit = array();
+    $action_label = $this->randomName();
+    $edit['actions_label'] = $action_label;
+    $edit['url'] = 'admin';
+    $this->drupalPost('admin/config/system/actions/configure/' . drupal_hash_base64('system_goto_action'), $edit, t('Save'));
+
+    // Make sure that the new complex action was saved properly.
+    $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action."));
+    $this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action."));
+
+    // Make another POST request to the action edit page.
+    $this->clickLink(t('configure'));
+    preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches);
+    $aid = $matches[1];
+    $edit = array();
+    $new_action_label = $this->randomName();
+    $edit['actions_label'] = $new_action_label;
+    $edit['url'] = 'admin';
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Make sure that the action updated properly.
+    $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action."));
+    $this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."));
+    $this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action."));
+
+    // Make sure that deletions work properly.
+    $this->clickLink(t('delete'));
+    $edit = array();
+    $this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete'));
+
+    // Make sure that the action was actually deleted.
+    $this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.'));
+    $this->drupalGet('admin/config/system/actions/manage');
+    $this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action."));
+    $exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
+    $this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.'));
+  }
+}
+
+/**
+ * Test actions executing in a potential loop, and make sure they abort properly.
+ */
+class ActionLoopTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Actions executing in a potentially infinite loop',
+      'description' => 'Tests actions executing in a loop, and makes sure they abort properly.',
+      'group' => 'Actions',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('dblog', 'trigger', 'actions_loop_test');
+  }
+
+  /**
+   * Set up a loop with 3 - 12 recursions, and see if it aborts properly.
+   */
+  function testActionLoop() {
+    $user = $this->drupalCreateUser(array('administer actions'));
+    $this->drupalLogin($user);
+
+    $hash = drupal_hash_base64('actions_loop_test_log');
+    $edit = array('aid' => $hash);
+    $this->drupalPost('admin/structure/trigger/actions_loop_test', $edit, t('Assign'));
+
+    // Delete any existing watchdog messages to clear the plethora of
+    // "Action added" messages from when Drupal was installed.
+    db_delete('watchdog')->execute();
+    // To prevent this test from failing when xdebug is enabled, the maximum
+    // recursion level should be kept low enough to prevent the xdebug
+    // infinite recursion protection mechanism from aborting the request.
+    // See http://drupal.org/node/587634.
+    variable_set('actions_max_stack', mt_rand(3, 12));
+    $this->triggerActions();
+  }
+
+  /**
+   * Create an infinite loop by causing a watchdog message to be set,
+   * which causes the actions to be triggered again, up to actions_max_stack
+   * times.
+   */
+  protected function triggerActions() {
+    $this->drupalGet('<front>', array('query' => array('trigger_actions_on_watchdog' => TRUE)));
+    $expected = array();
+    $expected[] = 'Triggering action loop';
+    for ($i = 1; $i <= variable_get('actions_max_stack', 35); $i++) {
+      $expected[] = "Test log #$i";
+    }
+    $expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.';
+
+    $result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid");
+    $loop_started = FALSE;
+    foreach ($result as $row) {
+      $expected_message = array_shift($expected);
+      $this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message)));
+    }
+    $this->assertTrue(empty($expected), t('All expected messages found.'));
+  }
+}
diff --git a/modules/system/tests/ajax.test b/modules/system/tests/ajax.test
new file mode 100644
index 0000000..2e10b80
--- /dev/null
+++ b/modules/system/tests/ajax.test
@@ -0,0 +1,329 @@
+<?php
+// $Id: ajax.test,v 1.11 2010/04/30 08:07:55 dries Exp $
+
+class AJAXTestCase extends DrupalWebTestCase {
+  function setUp() {
+    $modules = func_get_args();
+    if (isset($modules[0]) && is_array($modules[0])) {
+      $modules = $modules[0];
+    }
+    parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
+  }
+
+  /**
+   * Returns the passed-in commands array without the initial settings command.
+   *
+   * Depending on factors that may be irrelevant to a particular test,
+   * ajax_render() may prepend a settings command. This function allows the test
+   * to only have to concern itself with the commands that were passed to
+   * ajax_render().
+   */
+  protected function discardSettings($commands) {
+    if ($commands[0]['command'] == 'settings') {
+      array_shift($commands);
+    }
+    return $commands;
+  }
+}
+
+/**
+ * Tests primary AJAX framework functions.
+ */
+class AJAXFrameworkTestCase extends AJAXTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'AJAX framework',
+      'description' => 'Performs tests on AJAX framework functions.',
+      'group' => 'AJAX',
+    );
+  }
+
+  /**
+   * Test proper passing of JavaScript settings via ajax_render().
+   */
+  function testAJAXRender() {
+    $result = $this->drupalGetAJAX('ajax-test/render');
+    // Verify that JavaScript settings are contained (always first).
+    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
+    // Verify that basePath is contained in JavaScript settings.
+    $this->assertEqual($result[0]['settings']['basePath'], base_path(), t('Base path is contained in JavaScript settings.'));
+  }
+
+  /**
+   * Test behavior of ajax_render_error().
+   */
+  function testAJAXRenderError() {
+    $result = $this->drupalGetAJAX('ajax-test/render-error');
+    // Verify default error message.
+    $this->assertEqual($result[0]['command'], 'alert', t('ajax_render_error() invokes alert command.'));
+    $this->assertEqual($result[0]['text'], t('An error occurred while handling the request: The server received invalid input.'), t('Default error message is output.'));
+    // Verify custom error message.
+    $edit = array(
+      'message' => 'Custom error message.',
+    );
+    $result = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
+    $this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.'));
+  }
+}
+
+/**
+ * Tests AJAX framework commands.
+ */
+class AJAXCommandsTestCase extends AJAXTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'AJAX commands',
+      'description' => 'Performs tests on AJAX framework commands.',
+      'group' => 'AJAX',
+    );
+  }
+
+  /**
+   * Test ajax_command_settings().
+   */
+  function testAJAXRender() {
+    $commands = array();
+    $commands[] = ajax_command_settings(array('foo' => 42));
+    $result = $this->drupalGetAJAX('ajax-test/render', array('query' => array('commands' => $commands)));
+    // Verify that JavaScript settings are contained (always first).
+    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
+    // Verify that the custom setting is contained.
+    $this->assertEqual($result[1]['settings']['foo'], 42, t('Custom setting is output.'));
+  }
+
+  /**
+   * Test the various AJAX Commands.
+   */
+  function testAJAXCommands() {
+    $form_path = 'ajax_forms_test_ajax_commands_form';
+    $web_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($web_user);
+
+    $edit = array();
+
+    // Tests the 'after' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'after' && $command['data'] == 'This will be placed after', "'after' AJAX command issued with correct data");
+
+    // Tests the 'alert' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'alert' && $command['text'] == 'Alert', "'alert' AJAX Command issued with correct text");
+
+    // Tests the 'append' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'append' && $command['data'] == 'Appended text', "'append' AJAX command issued with correct data");
+
+    // Tests the 'before' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'before' && $command['data'] == 'Before text', "'before' AJAX command issued with correct data");
+
+    // Tests the 'changed' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed."))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div', "'changed' AJAX command issued with correct selector");
+
+    // Tests the 'changed' command using the second argument.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk."))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div' && $command['asterisk'] == '#changed_div_mark_this', "'changed' AJAX command (with asterisk) issued with correct selector");
+
+    // Tests the 'css' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue."))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'css' && $command['selector'] == '#css_div' && $command['argument']['background-color'] == 'blue', "'css' AJAX command issued with correct selector");
+
+    // Tests the 'data' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command."))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'data' && $command['name'] == 'testkey' && $command['value'] == 'testvalue', "'data' AJAX command issued with correct key and value");
+
+    // Tests the 'html' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector."))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'html' && $command['data'] == 'replacement text', "'html' AJAX command issued with correct data");
+
+    // Tests the 'insert' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method']."))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'insert' && $command['method'] == NULL && $command['data'] == 'insert replacement text', "'insert' AJAX command issued with correct data");
+
+    // Tests the 'prepend' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'prepend' && $command['data'] == 'prepended text', "'prepend' AJAX command issued with correct data");
+
+    // Tests the 'remove' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'remove' && $command['selector'] == '#remove_text', "'remove' AJAX command issued with correct command and selector");
+
+    // Tests the 'restripe' command.
+    $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command"))));
+    $command = $commands[0];
+    $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector");
+  }
+}
+
+/**
+ * Test that $form_state['values'] is properly delivered to $ajax['callback'].
+ */
+class AJAXFormValuesTestCase extends AJAXTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'AJAX command form values',
+      'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
+      'group' => 'AJAX',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->web_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Create a simple form, then POST to system/ajax to change to it.
+   */
+  function testSimpleAJAXFormValue() {
+    // Verify form values of a select element.
+    foreach(array('red', 'green', 'blue') as $item) {
+      $edit = array(
+        'select' => $item,
+      );
+      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'));
+      $data_command = $commands[1];
+      $this->assertEqual($data_command['value'], $item);
+    }
+
+    // Verify form values of a checkbox element.
+    foreach(array(FALSE, TRUE) as $item) {
+      $edit = array(
+        'checkbox' => $item,
+      );
+      $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox'));
+      $data_command = $commands[1];
+      $this->assertEqual((int) $data_command['value'], (int) $item);
+    }
+  }
+}
+
+/**
+ * Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.
+ */
+class AJAXMultiFormTestCase extends AJAXTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'AJAX multi form',
+      'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.',
+      'group' => 'AJAX',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('form_test'));
+
+    // Create a multi-valued field for 'page' nodes to use for AJAX testing.
+    $field_name = 'field_ajax_test';
+    $field = array(
+      'field_name' => $field_name,
+      'type' => 'text',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    );
+    field_create_field($field);
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'node',
+      'bundle' => 'page',
+    );
+    field_create_instance($instance);
+
+    // Login a user who can create 'page' nodes.
+    $this->web_user = $this->drupalCreateUser(array('create page content'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Test that a page with the 'page_node_form' included twice works correctly.
+   */
+  function testMultiForm() {
+    // HTML IDs for elements within the field are potentially modified with
+    // each AJAX submission, but these variables are stable and help target the
+    // desired elements.
+    $field_name = 'field_ajax_test';
+    $field_xpaths = array(
+      'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]',
+      'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]',
+    );
+    $button_name = $field_name . '_add_more';
+    $button_value = t('Add another item');
+    $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
+    $field_items_xpath_suffix = '//input[@type="text"]';
+
+    // Ensure the initial page contains both node forms and the correct number
+    // of field items and "add more" button for the multi-valued field within
+    // each form.
+    $this->drupalGet('form-test/two-instances-of-same-form');
+    foreach ($field_xpaths as $form_id => $field_xpath) {
+      $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
+      $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
+    }
+    $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
+
+    // Submit the "add more" button of each form twice. After each corresponding
+    // page update, ensure the same as above. To successfully implement
+    // consecutive AJAX submissions, we need to manage $settings as ajax.js
+    // does for Drupal.settings.
+    preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
+    $settings = drupal_json_decode($matches[1]);
+    foreach ($field_xpaths as $form_id => $field_xpath) {
+      for ($i=0; $i<2; $i++) {
+        $button = $this->xpath($field_xpath . $button_xpath_suffix);
+        $button_id = (string) $button[0]['id'];
+        $commands = $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_id, $settings['ajax'][$button_id]);
+        $settings = array_merge_recursive($settings, $commands[0]['settings']);
+        $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
+        $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
+        $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
+      }
+    }
+  }
+}
+
+/**
+ * Miscellaneous AJAX tests using ajax_test module.
+ */
+class AJAXElementValidation extends AJAXTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Miscellaneous AJAX tests',
+      'description' => 'Various tests of AJAX behavior',
+      'group' => 'AJAX',
+    );
+  }
+
+  /**
+   * Try to post an AJAX change to a form that has a validated element.
+   *
+   * The drivertext field is AJAX-enabled. An additional field is not, but
+   * is set to be a required field. In this test the required field is not
+   * filled in, and we want to see if the activation of the "drivertext"
+   * AJAX-enabled field fails due to the required field being empty.
+   */
+  function testAJAXElementValidation() {
+    $web_user = $this->drupalCreateUser();
+    $edit = array('drivertext' => t('some dumb text'));
+
+    // Post with 'drivertext' as the triggering element.
+    $post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext');
+    // Look for a validation failure in the resultant JSON.
+    $this->assertNoText(t('Error message'), t("No error message in resultant JSON"));
+    $this->assertText('ajax_forms_test_validation_form_callback invoked', t('The correct callback was invoked'));
+  }
+}
+
diff --git a/modules/system/tests/batch.test b/modules/system/tests/batch.test
new file mode 100644
index 0000000..5c11f72
--- /dev/null
+++ b/modules/system/tests/batch.test
@@ -0,0 +1,367 @@
+<?php
+// $Id: batch.test,v 1.9 2010/05/06 05:59:31 webchick Exp $
+
+/**
+ * @file
+ * Tests for the Batch API.
+ */
+
+/**
+ * Tests for the Batch API.
+ */
+class BatchProcessingTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Batch processing',
+      'description' => 'Test batch processing in form and non-form workflow.',
+      'group' => 'Batch API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('batch_test');
+  }
+
+  /**
+   * Test batches triggered outside of form submission.
+   */
+  function testBatchNoForm() {
+    // Displaying the page triggers batch 1.
+    $this->drupalGet('batch_test/no_form');
+    $this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+  }
+
+  /**
+   * Test batches defined in a form submit handler.
+   */
+  function testBatchForm() {
+    // Batch 0: no operation.
+    $edit = array('batch' => 'batch_0');
+    $this->drupalPost('batch_test/simple', $edit, 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+    // Batch 1: several simple operations.
+    $edit = array('batch' => 'batch_1');
+    $this->drupalPost('batch_test/simple', $edit, 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+    // Batch 2: one multistep operation.
+    $edit = array('batch' => 'batch_2');
+    $this->drupalPost('batch_test/simple', $edit, 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+    // Batch 3: simple + multistep combined.
+    $edit = array('batch' => 'batch_3');
+    $this->drupalPost('batch_test/simple', $edit, 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+    // Batch 4: nested batch.
+    $edit = array('batch' => 'batch_4');
+    $this->drupalPost('batch_test/simple', $edit, 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+  }
+
+  /**
+   * Test batches defined in a multistep form.
+   */
+  function testBatchFormMultistep() {
+    $this->drupalGet('batch_test/multistep');
+    $this->assertText('step 1', t('Form is displayed in step 1.'));
+
+    // First step triggers batch 1.
+    $this->drupalPost(NULL, array(), 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
+    $this->assertText('step 2', t('Form is displayed in step 2.'));
+
+    // Second step triggers batch 2.
+    $this->drupalPost(NULL, array(), 'Submit');
+    $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.'));
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+  }
+
+  /**
+   * Test batches defined in different submit handlers on the same form.
+   */
+  function testBatchFormMultipleBatches() {
+    // Batches 1, 2 and 3 are triggered in sequence by different submit
+    // handlers. Each submit handler modify the submitted 'value'.
+    $value = rand(0, 255);
+    $edit = array('value' => $value);
+    $this->drupalPost('batch_test/chained', $edit, 'Submit');
+    // Check that result messages are present and in the correct order.
+    $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
+    // The stack contains execution order of batch callbacks and submit
+    // hanlders and logging of corresponding $form_state[{values'].
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
+    $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+  }
+
+  /**
+   * Test batches defined in a programmatically submitted form.
+   *
+   * Same as above, but the form is submitted through drupal_form_execute().
+   */
+  function testBatchFormProgrammatic() {
+    // Batches 1, 2 and 3 are triggered in sequence by different submit
+    // handlers. Each submit handler modify the submitted 'value'.
+    $value = rand(0, 255);
+    $this->drupalGet('batch_test/programmatic/' . $value);
+    // Check that result messages are present and in the correct order.
+    $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
+    // The stack contains execution order of batch callbacks and submit
+    // hanlders and logging of corresponding $form_state[{values'].
+    $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
+    $this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.'));
+  }
+
+  /**
+   * Test that drupal_form_submit() can run within a batch operation.
+   */
+  function testDrupalFormSubmitInBatch() {
+    // Displaying the page triggers a batch that programmatically submits a
+    // form.
+    $value = rand(0, 255);
+    $this->drupalGet('batch_test/nested_programmatic/' . $value);
+    $this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.'));
+  }
+
+  /**
+   * Will trigger a pass if the texts were found in order in the raw content.
+   *
+   * @param $texts
+   *   Array of raw strings to look for .
+   * @param $message
+   *   Message to display.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  function assertBatchMessages($texts, $message) {
+    $pattern = '|' . implode('.*', $texts) .'|s';
+    return $this->assertPattern($pattern, $message);
+  }
+
+  /**
+   * Helper function: return expected execution stacks for the test batches.
+   */
+  function _resultStack($id, $value = 0) {
+    $stack = array();
+    switch ($id) {
+      case 'batch_1':
+        for ($i = 1; $i <= 10; $i++) {
+          $stack[] = "op 1 id $i";
+        }
+        break;
+
+      case 'batch_2':
+        for ($i = 1; $i <= 10; $i++) {
+          $stack[] = "op 2 id $i";
+        }
+        break;
+
+      case 'batch_3':
+        for ($i = 1; $i <= 5; $i++) {
+          $stack[] = "op 1 id $i";
+        }
+        for ($i = 1; $i <= 5; $i++) {
+          $stack[] = "op 2 id $i";
+        }
+        for ($i = 6; $i <= 10; $i++) {
+          $stack[] = "op 1 id $i";
+        }
+        for ($i = 6; $i <= 10; $i++) {
+          $stack[] = "op 2 id $i";
+        }
+        break;
+
+      case 'batch_4':
+        for ($i = 1; $i <= 5; $i++) {
+          $stack[] = "op 1 id $i";
+        }
+        $stack[] = 'setting up batch 2';
+        for ($i = 6; $i <= 10; $i++) {
+          $stack[] = "op 1 id $i";
+        }
+        $stack = array_merge($stack, $this->_resultStack('batch_2'));
+        break;
+
+      case 'chained':
+        $stack[] = 'submit handler 1';
+        $stack[] = 'value = ' . $value;
+        $stack = array_merge($stack, $this->_resultStack('batch_1'));
+        $stack[] = 'submit handler 2';
+        $stack[] = 'value = ' . ($value + 1);
+        $stack = array_merge($stack, $this->_resultStack('batch_2'));
+        $stack[] = 'submit handler 3';
+        $stack[] = 'value = ' . ($value + 2);
+        $stack[] = 'submit handler 4';
+        $stack[] = 'value = ' . ($value + 3);
+        $stack = array_merge($stack, $this->_resultStack('batch_3'));
+        break;
+    }
+    return $stack;
+  }
+
+  /**
+   * Helper function: return expected result messages for the test batches.
+   */
+  function _resultMessages($id) {
+    $messages = array();
+
+    switch ($id) {
+      case 'batch_0':
+        $messages[] = 'results for batch 0<br />none';
+        break;
+
+      case 'batch_1':
+        $messages[] = 'results for batch 1<br />op 1: processed 10 elements';
+        break;
+
+      case 'batch_2':
+        $messages[] = 'results for batch 2<br />op 2: processed 10 elements';
+        break;
+
+      case 'batch_3':
+        $messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements';
+        break;
+
+      case 'batch_4':
+        $messages[] = 'results for batch 4<br />op 1: processed 10 elements';
+        $messages = array_merge($messages, $this->_resultMessages('batch_2'));
+        break;
+
+      case 'chained':
+        $messages = array_merge($messages, $this->_resultMessages('batch_1'));
+        $messages = array_merge($messages, $this->_resultMessages('batch_2'));
+        $messages = array_merge($messages, $this->_resultMessages('batch_3'));
+        break;
+    }
+    return $messages;
+  }
+}
+
+/**
+ * Tests for the Batch API Progress page.
+ */
+class BatchPageTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Batch progress page',
+      'description' => 'Test the content of the progress page.',
+      'group' => 'Batch API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('batch_test');
+  }
+
+  /**
+   * Tests that the batch API progress page uses the correct theme.
+   */
+  function testBatchProgressPageTheme() {
+    // Make sure that the page which starts the batch (an administrative page)
+    // is using a different theme than would normally be used by the batch API.
+    variable_set('theme_default', 'garland');
+    variable_set('admin_theme', 'seven');
+    // Visit an administrative page that runs a test batch, and check that the
+    // theme that was used during batch execution (which the batch callback
+    // function saved as a variable) matches the theme used on the
+    // administrative page.
+    $this->drupalGet('admin/batch_test/test_theme');
+    // The stack should contain the name of the the used on the progress page.
+    $this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.'));
+  }
+}
+
+/**
+ * Tests the function _batch_api_percentage() to make sure that the rounding
+ * works properly in all cases.
+ */
+class BatchPercentagesUnitTestCase extends DrupalUnitTestCase {
+  protected $testCases = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Batch percentages',
+      'description' => 'Unit tests of progress percentage rounding.',
+      'group' => 'Batch API',
+    );
+  }
+
+  function setUp() {
+    // Set up an array of test cases, where the expected values are the keys,
+    // and the values are arrays with the keys 'total' and 'current',
+    // corresponding with the function parameters of _batch_api_percentage().
+    $this->testCases = array(
+      // 1/2 is 50%.
+      '50' => array('total' => 2, 'current' => 1),
+      // Though we should never encounter a case where the current set is set
+      // 0, if we did, we should get 0%.
+      '0' => array('total' => 3, 'current' => 0),
+      // 1/3 is closer to 33% than to 34%.
+      '33' => array('total' => 3, 'current' => 1),
+      // 2/3 is closer to 67% than to 66%.
+      '67' => array('total' => 3, 'current' => 2),
+      // A full 3/3 should equal 100%.
+      '100' => array('total' => 3, 'current' => 3),
+      // 1/199 should round up to 1%.
+      '1' => array('total' => 199, 'current' => 1),
+      // 198/199 should round down to 99%.
+      '99' => array('total' => 199, 'current' => 198),
+      // 199/200 would have rounded up to 100%, which would give the false
+      // impression of being finished, so we add another digit and should get
+      // 99.5%.
+      '99.5' => array('total' => 200, 'current' => 199),
+      // The same logic holds for 1/200: we should get 0.5%.
+      '0.5' => array('total' => 200, 'current' => 1),
+      // Numbers that come out evenly, such as 50/200, should be forced to have
+      // extra digits for consistancy.
+      '25.0' => array('total' => 200, 'current' => 50),
+      // Regardless of number of digits we're using, 100% should always just be
+      // 100%.
+      '100' => array('total' => 200, 'current' => 200),
+      // 1998/1999 should similarly round down to 99.9%.
+      '99.9' => array('total' => 1999, 'current' => 1998),
+      // 1999/2000 should add another digit and go to 99.95%.
+      '99.95' => array('total' => 2000, 'current' => 1999),
+      // 19999/20000 should add yet another digit and go to 99.995%.
+      '99.995' => array('total' => 20000, 'current' => 19999),
+    );
+
+    parent::setUp();
+  }
+
+  /**
+   * Test the _batch_api_percentage() function.
+   */
+  function testBatchPercentages() {
+    require_once DRUPAL_ROOT . '/includes/batch.inc';
+    foreach ($this->testCases as $expected_result => $arguments) {
+      // PHP sometimes casts numeric strings that are array keys to integers,
+      // cast them back here.
+      $expected_result = (string) $expected_result;
+      $total = $arguments['total'];
+      $current = $arguments['current'];
+      $actual_result = _batch_api_percentage($total, $current);
+      if ($actual_result === $expected_result) {
+        $this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
+      }
+      else {
+        $this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
+      }
+    }
+  }
+}
diff --git a/modules/system/tests/bootstrap.test b/modules/system/tests/bootstrap.test
new file mode 100644
index 0000000..95d7244
--- /dev/null
+++ b/modules/system/tests/bootstrap.test
@@ -0,0 +1,428 @@
+<?php
+// $Id: bootstrap.test,v 1.31 2010/06/14 13:24:32 dries Exp $
+
+class BootstrapIPAddressTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'IP address and HTTP_HOST test',
+      'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.',
+      'group' => 'Bootstrap'
+    );
+  }
+
+  function setUp() {
+    $this->oldserver = $_SERVER;
+
+    $this->remote_ip = '127.0.0.1';
+    $this->proxy_ip = '127.0.0.2';
+    $this->proxy2_ip = '127.0.0.3';
+    $this->forwarded_ip = '127.0.0.4';
+    $this->cluster_ip = '127.0.0.5';
+    $this->untrusted_ip = '0.0.0.0';
+
+    drupal_static_reset('ip_address');
+
+    $_SERVER['REMOTE_ADDR'] = $this->remote_ip;
+    unset($_SERVER['HTTP_X_FORWARDED_FOR']);
+    unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']);
+
+    parent::setUp();
+  }
+
+  function tearDown() {
+    $_SERVER = $this->oldserver;
+    drupal_static_reset('ip_address');
+    parent::tearDown();
+  }
+
+  /**
+   * test IP Address and hostname
+   */
+  function testIPAddressHost() {
+    // Test the normal IP address.
+    $this->assertTrue(
+      ip_address() == $this->remote_ip,
+      t('Got remote IP address.')
+    );
+
+    // Proxy forwarding on but no proxy addresses defined.
+    variable_set('reverse_proxy', 1);
+    $this->assertTrue(
+      ip_address() == $this->remote_ip,
+      t('Proxy forwarding without trusted proxies got remote IP address.')
+    );
+
+    // Proxy forwarding on and proxy address not trusted.
+    variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip));
+    drupal_static_reset('ip_address');
+    $_SERVER['REMOTE_ADDR'] = $this->untrusted_ip;
+    $this->assertTrue(
+      ip_address() == $this->untrusted_ip,
+      t('Proxy forwarding with untrusted proxy got remote IP address.')
+    );
+
+    // Proxy forwarding on and proxy address trusted.
+    $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
+    $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip;
+    drupal_static_reset('ip_address');
+    $this->assertTrue(
+      ip_address() == $this->forwarded_ip,
+      t('Proxy forwarding with trusted proxy got forwarded IP address.')
+    );
+
+    // Multi-tier architecture with comma separated values in header.
+    $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
+    $_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
+    drupal_static_reset('ip_address');
+    $this->assertTrue(
+      ip_address() == $this->forwarded_ip,
+      t('Proxy forwarding with trusted 2-tier proxy got forwarded IP address.')
+    );
+
+    // Custom client-IP header.
+    variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP');
+    $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip;
+    drupal_static_reset('ip_address');
+    $this->assertTrue(
+      ip_address() == $this->cluster_ip,
+      t('Cluster environment got cluster client IP.')
+    );
+
+    // Verifies that drupal_valid_http_host() prevents invalid characters.
+    $this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), t('HTTP_HOST with / is invalid'));
+    $this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), t('HTTP_HOST with \\ is invalid'));
+    $this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), t('HTTP_HOST with &lt; is invalid'));
+    $this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), t('HTTP_HOST with .. is invalid'));
+    // IPv6 loopback address
+    $this->assertTrue(drupal_valid_http_host('[::1]:80'), t('HTTP_HOST containing IPv6 loopback is valid'));
+  }
+}
+
+class BootstrapPageCacheTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Page cache test',
+      'description' => 'Enable the page cache and test it with various HTTP requests.',
+      'group' => 'Bootstrap'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('system_test');
+  }
+
+  /**
+   * Test support for requests containing If-Modified-Since and If-None-Match headers.
+   */
+  function testConditionalRequests() {
+    variable_set('cache', 1);
+
+    // Fill the cache.
+    $this->drupalGet('');
+
+    $this->drupalHead('');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $etag = $this->drupalGetHeader('ETag');
+    $last_modified = $this->drupalGetHeader('Last-Modified');
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
+    $this->assertResponse(304, t('Conditional request returned 304 Not Modified.'));
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
+    $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.'));
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
+    $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.'));
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
+    $this->assertResponse(200, t('Conditional request without If-None-Match returned 200 OK.'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC1123, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
+    $this->assertResponse(200, t('Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
+    $this->assertResponse(200, t('Conditional request returned 200 OK for authenticated user.'));
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Absense of Page was not cached.'));
+  }
+
+  /**
+   * Test cache headers.
+   */
+  function testPageCache() {
+    variable_set('cache', 1);
+
+    // Fill the cache.
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
+    $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));
+
+    // Check cache.
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary: Cookie header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));
+
+    // Check replacing default headers.
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', t('Default header was replaced.'));
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
+    $this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', t('Default header was replaced.'));
+
+    // Check that authenticated users bypass the cache.
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
+    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, t('Vary: Cookie header was not sent.'));
+    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
+    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));
+
+  }
+
+  /**
+   * Test page compression.
+   *
+   * The test should pass even if zlib.output_compression is enabled in php.ini,
+   * .htaccess or similar, or if compression is done outside PHP, e.g. by the
+   * mod_deflate Apache module.
+   */
+  function testPageCompression() {
+    variable_set('cache', 1);
+
+    // Fill the cache and verify that output is compressed.
+    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
+    $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
+    $this->assertRaw('</html>', t('Page was gzip compressed.'));
+
+    // Verify that cached output is compressed.
+    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', t('A Content-Encoding header was sent.'));
+    $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
+    $this->assertRaw('</html>', t('Page was gzip compressed.'));
+
+    // Verify that a client without compression support gets an uncompressed page.
+    $this->drupalGet('');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $this->assertFalse($this->drupalGetHeader('Content-Encoding'), t('A Content-Encoding header was not sent.'));
+    $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
+    $this->assertRaw('</html>', t('Page was not compressed.'));
+  }
+}
+
+class BootstrapVariableTestCase extends DrupalWebTestCase {
+
+  function setUp() {
+    parent::setUp('system_test');
+  }
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Variable test',
+      'description' => 'Make sure the variable system functions correctly.',
+      'group' => 'Bootstrap'
+    );
+  }
+
+  /**
+   * testVariable
+   */
+  function testVariable() {
+    // Setting and retrieving values.
+    $variable = $this->randomName();
+    variable_set('simpletest_bootstrap_variable_test', $variable);
+    $this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), t('Setting and retrieving values'));
+
+    // Make sure the variable persists across multiple requests.
+    $this->drupalGet('system-test/variable-get');
+    $this->assertText($variable, t('Variable persists across multiple requests'));
+
+    // Deleting variables.
+    $default_value = $this->randomName();
+    variable_del('simpletest_bootstrap_variable_test');
+    $variable = variable_get('simpletest_bootstrap_variable_test', $default_value);
+    $this->assertIdentical($variable, $default_value, t('Deleting variables'));
+  }
+
+  /**
+   * Makes sure that the default variable parameter is passed through okay.
+   */
+  function testVariableDefaults() {
+    // Tests passing nothing through to the default.
+    $this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), t('Variables are correctly defaulting to NULL.'));
+
+    // Tests passing 5 to the default parameter.
+    $this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), t('The default variable parameter is passed through correctly.'));
+  }
+
+}
+
+/**
+ * Test hook_boot() and hook_exit().
+ */
+class HookBootExitTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Boot and exit hook invocation',
+      'description' => 'Test that hook_boot() and hook_exit() are called correctly.',
+      'group' => 'Bootstrap',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('system_test', 'dblog');
+  }
+
+  /**
+   * Test calling of hook_boot() and hook_exit().
+   */
+  function testHookBootExit() {
+    // Test with cache disabled. Boot and exit should always fire.
+    variable_set('cache', 0);
+    $this->drupalGet('');
+    $calls = 1;
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));
+
+    // Test with normal cache. Boot and exit should be called.
+    variable_set('cache', 1);
+    $this->drupalGet('');
+    $calls++;
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.'));
+
+    // Boot and exit should not fire since the page is cached.
+    variable_set('page_cache_invoke_hooks', FALSE);
+    $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
+    $this->drupalGet('');
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with agressive cache and a cached page.'));
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with agressive cache and a cached page.'));
+
+    // Test with page cache cleared, boot and exit should be called.
+    $this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
+    $this->drupalGet('');
+    $calls++;
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with agressive cache and no cached page.'));
+    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with agressive cache and no cached page.'));
+  }
+}
+
+/**
+ * Test drupal_get_filename()'s availability.
+ */
+class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Get filename test',
+      'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.',
+      'group' => 'Bootstrap',
+    );
+  }
+
+  /**
+   * Test that drupal_get_filename() works correctly when the file is not found in the database.
+   */
+  function testDrupalGetFilename() {
+    // Reset the static cache so we can test the "db is not active" code of
+    // drupal_get_filename().
+    drupal_static_reset('drupal_get_filename');
+
+    // Retrieving the location of a module.
+    $this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.'));
+
+    // Retrieving the location of a theme.
+    $this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.'));
+
+    // Retrieving the location of a theme engine.
+    $this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.'));
+
+    // Retrieving a file that is definitely not stored in the database.
+    $this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.'));
+  }
+}
+
+class BootstrapTimerTestCase extends DrupalUnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Timer test',
+      'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.',
+      'group' => 'Bootstrap',
+    );
+  }
+
+  /**
+   * Test timer_read() to ensure it properly accumulates time when the timer
+   * started and stopped multiple times.
+   * @return
+   */
+  function testTimer() {
+    timer_start('test');
+    sleep(1);
+    $this->assertTrue(timer_read('test') >= 1000, t('Timer measured 1 second of sleeping while running.'));
+    sleep(1);
+    timer_stop('test');
+    $this->assertTrue(timer_read('test') >= 2000, t('Timer measured 2 seconds of sleeping after being stopped.'));
+    timer_start('test');
+    sleep(1);
+    $this->assertTrue(timer_read('test') >= 3000, t('Timer measured 3 seconds of sleeping after being restarted.'));
+    sleep(1);
+    $timer = timer_stop('test');
+    $this->assertTrue(timer_read('test') >= 4000, t('Timer measured 4 seconds of sleeping after being stopped for a second time.'));
+    $this->assertEqual($timer['count'], 2, t('Timer counted 2 instances of being started.'));
+  }
+}
+
+/**
+ * Test that resetting static variables works.
+ */
+class BootstrapResettableStaticTestCase extends DrupalUnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Resettable static variables test',
+      'description' => 'Test that drupal_static() and drupal_static_reset() work.',
+      'group' => 'Bootstrap',
+    );
+  }
+
+  /**
+   * Test that a variable reference returned by drupal_static() gets reset when
+   * drupal_static_reset() is called.
+   */
+  function testDrupalStatic() {
+    $name = __CLASS__ . '_' . __METHOD__;
+    $var = &drupal_static($name, 'foo');
+    $this->assertEqual($var, 'foo', t('Variable returned by drupal_static() was set to its default.'));
+
+    // Call the specific reset and the global reset each twice to ensure that
+    // multiple resets can be issued without odd side effects.
+    $var = 'bar';
+    drupal_static_reset($name);
+    $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of name-specific reset.'));
+    $var = 'bar';
+    drupal_static_reset($name);
+    $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of name-specific reset.'));
+    $var = 'bar';
+    drupal_static_reset();
+    $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of global reset.'));
+    $var = 'bar';
+    drupal_static_reset();
+    $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of global reset.'));
+  }
+}
diff --git a/modules/system/tests/cache.test b/modules/system/tests/cache.test
new file mode 100644
index 0000000..d5a9c5f
--- /dev/null
+++ b/modules/system/tests/cache.test
@@ -0,0 +1,351 @@
+<?php
+// $Id: cache.test,v 1.10 2009/09/13 17:49:51 dries Exp $
+
+class CacheTestCase extends DrupalWebTestCase {
+  protected $default_bin = 'cache';
+  protected $default_cid = 'test_temporary';
+  protected $default_value = 'CacheTest';
+
+  /**
+   * Check whether or not a cache entry exists.
+   *
+   * @param $cid
+   *   The cache id.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function checkCacheExists($cid, $var, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    $cache = cache_get($cid, $bin);
+
+    return isset($cache->data) && $cache->data == $var;
+  }
+
+  /**
+   * Assert or a cache entry exists.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $var
+   *   The variable the cache should contain.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+    if ($var == NULL) {
+      $var = $this->default_value;
+    }
+
+    $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
+  }
+
+  /**
+   * Assert or a cache entry has been removed.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $cid
+   *   The cache id.
+   * @param $bin
+   *   The bin the cache item was stored in.
+   */
+  function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+    if ($cid == NULL) {
+      $cid = $this->default_cid;
+    }
+
+    $cache = cache_get($cid, $bin);
+    $this->assertFalse($cache, $message);
+  }
+
+  /**
+   * Perform the general wipe.
+   * @param $bin
+   *   The bin to perform the wipe on.
+   */
+  protected function generalWipe($bin = NULL) {
+    if ($bin == NULL) {
+      $bin = $this->default_bin;
+    }
+
+    cache_clear_all(NULL, $bin);
+  }
+
+  /**
+   * Setup the lifetime settings for caching.
+   *
+   * @param $time
+   *   The time in seconds the cache should minimal live.
+   */
+  protected function setupLifetime($time) {
+    variable_set('cache_lifetime', $time);
+    variable_set('cache_flush', 0);
+  }
+}
+
+class CacheSavingCase extends CacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache saving test',
+      'description' => 'Check our variables are saved and restored the right way.',
+      'group' => 'Cache'
+    );
+  }
+
+  /**
+   * Test the saving and restoring of a string.
+   */
+  function testString() {
+    $this->checkVariable($this->randomName(100));
+  }
+
+  /**
+   * Test the saving and restoring of an integer.
+   */
+  function testInteger() {
+    $this->checkVariable(100);
+  }
+
+  /**
+   * Test the saving and restoring of a double.
+   */
+  function testDouble() {
+    $this->checkVariable(1.29);
+  }
+
+  /**
+   * Test the saving and restoring of an array.
+   */
+  function testArray() {
+    $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
+  }
+
+  /**
+   * Test the saving and restoring of an object.
+   */
+  function testObject() {
+    $test_object = new stdClass();
+    $test_object->test1 = $this->randomName(100);
+    $test_object->test2 = 100;
+    $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
+
+    cache_set('test_object', $test_object, 'cache');
+    $cache = cache_get('test_object', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
+  }
+
+  /*
+   * Check or a variable is stored and restored properly.
+   **/
+  function checkVariable($var) {
+    cache_set('test_var', $var, 'cache');
+    $cache = cache_get('test_var', 'cache');
+    $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
+  }
+}
+
+/**
+ * Test cache_get_multiple().
+ */
+class CacheGetMultipleUnitTest extends CacheTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Fetching multiple cache items',
+      'description' => 'Confirm that multiple records are fetched correctly.',
+      'group' => 'Cache',
+    );
+  }
+
+  function setUp() {
+    $this->default_bin = 'cache_page';
+    parent::setUp();
+  }
+
+  /**
+   * Test cache_get_multiple().
+   */
+  function testCacheMultiple() {
+    $item1 = $this->randomName(10);
+    $item2 = $this->randomName(10);
+    cache_set('item1', $item1, $this->default_bin);
+    cache_set('item2', $item2, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.'));
+    $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.'));
+
+    // Fetch both records from the database with cache_get_multiple().
+    $item_ids = array('item1', 'item2');
+    $items = cache_get_multiple($item_ids, $this->default_bin);
+    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
+    $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.'));
+
+    // Remove one item from the cache.
+    cache_clear_all('item2', $this->default_bin);
+
+    // Confirm that only one item is returned by cache_get_multiple().
+    $item_ids = array('item1', 'item2');
+    $items = cache_get_multiple($item_ids, $this->default_bin);
+    $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
+    $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.'));
+    $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
+  }
+}
+
+/**
+ * Test cache clearing methods.
+ */
+class CacheClearCase extends CacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache clear test',
+      'description' => 'Check our clearing is done the proper way.',
+      'group' => 'Cache'
+    );
+  }
+
+  function setUp() {
+    $this->default_bin = 'cache_page';
+    $this->default_value = $this->randomName(10);
+
+    parent::setUp();
+  }
+
+  /**
+   * Test clearing using a cid.
+   */
+  function testClearCid() {
+    cache_set('test_cid_clear', $this->default_value, $this->default_bin);
+
+    $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
+    cache_clear_all('test_cid_clear', $this->default_bin);
+
+    $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
+
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard false.'));
+    cache_clear_all('*', $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches still exists after clearing cid "*" with wildcard false.'));
+  }
+
+  /**
+   * Test clearing using wildcard.
+   */
+  function testClearWildcard() {
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid "*" with wildcard true.'));
+    cache_clear_all('*', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid "*" with wildcard true.'));
+
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches were created for checking cid substring with wildcard true.'));
+    cache_clear_all('test_', $this->default_bin, TRUE);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two caches removed after clearing cid substring with wildcard true.'));
+  }
+
+  /**
+   * Test clearing using an array.
+   */
+  function testClearArray() {
+    // Create three cache entries.
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear3', $this->default_value),
+                      t('Three cache entries were created.'));
+
+    // Clear two entries using an array.
+    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                       t('Two cache entries removed after clearing with an array.'));
+
+    $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
+                      t('Entry was not cleared from the cache'));
+
+    // Set the cache clear threshold to 2 to confirm that the full bin is cleared
+    // when the threshold is exceeded.
+    variable_set('cache_clear_threshold', 2);
+    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+                      t('Two cache entries were created.'));
+    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear2', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear3', $this->default_value),
+                       t('All cache entries removed when the array exceeded the cache clear threshold.'));
+  }
+}
+
+/**
+ * Test cache_is_empty() function.
+ */
+class CacheIsEmptyCase extends CacheTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache emptiness test',
+      'description' => 'Check if a cache bin is empty after performing clear operations.',
+      'group' => 'Cache'
+    );
+  }
+
+  function setUp() {
+    $this->default_bin = 'cache_page';
+    $this->default_value = $this->randomName(10);
+
+    parent::setUp();
+  }
+
+  /**
+   * Test clearing using a cid.
+   */
+  function testIsEmpty() {
+    // Clear the cache bin.
+    cache_clear_all('*', $this->default_bin);
+    $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
+    // Add some data to the cache bin.
+    cache_set($this->default_cid, $this->default_value, $this->default_bin);
+    $this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid);
+    $this->assertFalse(cache_is_empty($this->default_bin), t('The cache bin is not empty'));
+    // Remove the cached data.
+    cache_clear_all($this->default_cid, $this->default_bin);
+    $this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid);
+    $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
+  }
+}
diff --git a/modules/system/tests/common.test b/modules/system/tests/common.test
new file mode 100644
index 0000000..e81a950
--- /dev/null
+++ b/modules/system/tests/common.test
@@ -0,0 +1,1990 @@
+<?php
+// $Id: common.test,v 1.116 2010/07/07 17:00:43 webchick Exp $
+
+/**
+ * @file
+ * Tests for common.inc functionality.
+ */
+
+/**
+ * Tests for URL generation functions.
+ */
+class DrupalAlterTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'drupal_alter() tests',
+      'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('common_test');
+  }
+
+  function testDrupalAlter() {
+    // This test depends on Garland, so make sure that it is always the current
+    // active theme.
+    global $theme, $base_theme_info;
+    $theme = 'garland';
+    $base_theme_info = array();
+
+    $array = array('foo' => 'bar');
+    $entity = new stdClass();
+    $entity->foo = 'bar';
+
+    // Verify alteration of a single argument.
+    $array_copy = $array;
+    $array_expected = array('foo' => 'Drupal theme');
+    drupal_alter('drupal_alter', $array_copy);
+    $this->assertEqual($array_copy, $array_expected, t('Single array was altered.'));
+
+    $entity_copy = clone $entity;
+    $entity_expected = clone $entity;
+    $entity_expected->foo = 'Drupal theme';
+    drupal_alter('drupal_alter', $entity_copy);
+    $this->assertEqual($entity_copy, $entity_expected, t('Single object was altered.'));
+
+    // Verify alteration of multiple arguments.
+    $array_copy = $array;
+    $array_expected = array('foo' => 'Drupal theme');
+    $entity_copy = clone $entity;
+    $entity_expected = clone $entity;
+    $entity_expected->foo = 'Drupal theme';
+    $array2_copy = $array;
+    $array2_expected = array('foo' => 'Drupal theme');
+    drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
+    $this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.'));
+    $this->assertEqual($entity_copy, $entity_expected, t('Second argument to drupal_alter() was altered.'));
+    $this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.'));
+  }
+}
+
+/**
+ * Tests for URL generation functions.
+ *
+ * url() calls module_implements(), which may issue a db query, which requires
+ * inheriting from a web test case rather than a unit test case.
+ */
+class CommonURLUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'URL generation tests',
+      'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Confirm that invalid text given as $path is filtered.
+   */
+  function testLXSS() {
+    $text = $this->randomName();
+    $path = "<SCRIPT>alert('XSS')</SCRIPT>";
+    $link = l($text, $path);
+    $sanitized_path = check_url(url($path));
+    $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path)));
+  }
+
+  /*
+   * Tests for active class in l() function.
+   */
+  function testLActiveClass() {
+    $link = l($this->randomName(), $_GET['q']);
+    $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
+  }
+
+  /**
+   * Tests for custom class in l() function.
+   */
+  function testLCustomClass() {
+    $class = $this->randomName();
+    $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class))));
+    $this->assertTrue($this->hasClass($link, $class), t('Custom class @class is present on link when requested', array('@class' => $class)));
+    $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
+  }
+
+  private function hasClass($link, $class) {
+    return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link);
+  }
+
+  /**
+   * Test drupal_get_query_parameters().
+   */
+  function testDrupalGetQueryParameters() {
+    $original = array(
+      'a' => 1,
+      'b' => array(
+        'd' => 4,
+        'e' => array(
+          'f' => 5,
+        ),
+      ),
+      'c' => 3,
+      'q' => 'foo/bar',
+    );
+
+    // Default arguments.
+    $result = $_GET;
+    unset($result['q']);
+    $this->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed."));
+
+    // Default exclusion.
+    $result = $original;
+    unset($result['q']);
+    $this->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed."));
+
+    // First-level exclusion.
+    $result = $original;
+    unset($result['b']);
+    $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, t("'b' was removed."));
+
+    // Second-level exclusion.
+    $result = $original;
+    unset($result['b']['d']);
+    $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, t("'b[d]' was removed."));
+
+    // Third-level exclusion.
+    $result = $original;
+    unset($result['b']['e']['f']);
+    $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, t("'b[e][f]' was removed."));
+
+    // Multiple exclusions.
+    $result = $original;
+    unset($result['a'], $result['b']['e'], $result['c']);
+    $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, t("'a', 'b[e]', 'c' were removed."));
+  }
+
+  /**
+   * Test drupal_http_build_query().
+   */
+  function testDrupalHttpBuildQuery() {
+    $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', t('Value was properly encoded.'));
+    $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', t('Key was properly encoded.'));
+    $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', t('Multiple values were properly concatenated.'));
+    $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', t('Nested array was properly encoded.'));
+  }
+
+  /**
+   * Test drupal_parse_url().
+   */
+  function testDrupalParseUrl() {
+    // Relative URL.
+    $url = 'foo/bar?foo=bar&bar=baz&baz#foo';
+    $result = array(
+      'path' => 'foo/bar',
+      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+      'fragment' => 'foo',
+    );
+    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
+
+    // Relative URL that is known to confuse parse_url().
+    $url = 'foo/bar:1';
+    $result = array(
+      'path' => 'foo/bar:1',
+      'query' => array(),
+      'fragment' => '',
+    );
+    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
+
+    // Absolute URL.
+    $url = '/foo/bar?foo=bar&bar=baz&baz#foo';
+    $result = array(
+      'path' => '/foo/bar',
+      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+      'fragment' => 'foo',
+    );
+    $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.'));
+
+    // External URL testing.
+    $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
+
+    // Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
+    $this->assertTrue(url_is_external($url), t('Correctly identified an external URL.'));
+
+    // Test the parsing of absolute URLs.
+    $result = array(
+      'path' => 'http://drupal.org/foo/bar',
+      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+      'fragment' => 'foo',
+    );
+    $this->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.'));
+
+    // Verify proper parsing of URLs when clean URLs are disabled.
+    $result = array(
+      'path' => 'foo/bar',
+      'query' => array('bar' => 'baz'),
+      'fragment' => 'foo',
+    );
+    // Non-clean URLs #1: Absolute URL generated by url().
+    $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
+    $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.'));
+
+    // Non-clean URLs #2: Relative URL generated by url().
+    $url = '?q=foo/bar&bar=baz#foo';
+    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.'));
+
+    // Non-clean URLs #3: URL generated by url() on non-Apache webserver.
+    $url = 'index.php?q=foo/bar&bar=baz#foo';
+    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'));
+
+    // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect.
+    $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html');
+    $this->assertFalse(valid_url($parts['path'], TRUE), t('drupal_parse_url() correctly parsed a forged URL.'));
+  }
+
+  /**
+   * Test url() with/without query, with/without fragment, absolute on/off and
+   * assert all that works when clean URLs are on and off.
+   */
+  function testUrl() {
+    global $base_url;
+
+    foreach (array(FALSE, TRUE) as $absolute) {
+      // Get the expected start of the path string.
+      $base = $absolute ? $base_url . '/' : base_path();
+      $absolute_string = $absolute ? 'absolute' : NULL;
+
+      // Disable Clean URLs.
+      $GLOBALS['conf']['clean_url'] = 0;
+
+      $url = $base . '?q=node/123';
+      $result = url('node/123', array('absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . '?q=node/123#foo';
+      $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . '?q=node/123&foo';
+      $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . '?q=node/123&foo=bar&bar=baz';
+      $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . '?q=node/123&foo#bar';
+      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base;
+      $result = url('<front>', array('absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      // Enable Clean URLs.
+      $GLOBALS['conf']['clean_url'] = 1;
+
+      $url = $base . 'node/123';
+      $result = url('node/123', array('absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . 'node/123#foo';
+      $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . 'node/123?foo';
+      $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . 'node/123?foo=bar&bar=baz';
+      $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base . 'node/123?foo#bar';
+      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+
+      $url = $base;
+      $result = url('<front>', array('absolute' => $absolute));
+      $this->assertEqual($url, $result, "$url == $result");
+    }
+  }
+
+  /**
+   * Test external URL handling.
+   */
+  function testExternalUrls() {
+    $test_url = 'http://drupal.org/';
+
+    // Verify external URL can contain a fragment.
+    $url = $test_url . '#drupal';
+    $result = url($url);
+    $this->assertEqual($url, $result, t('External URL with fragment works without a fragment in $options.'));
+
+    // Verify fragment can be overidden in an external URL.
+    $url = $test_url . '#drupal';
+    $fragment = $this->randomName(10);
+    $result = url($url, array('fragment' => $fragment));
+    $this->assertEqual($test_url . '#' . $fragment, $result, t('External URL fragment is overidden with a custom fragment in $options.'));
+
+    // Verify external URL can contain a query string.
+    $url = $test_url . '?drupal=awesome';
+    $result = url($url);
+    $this->assertEqual($url, $result, t('External URL with query string works without a query string in $options.'));
+
+    // Verify external URL can be extended with a query string.
+    $url = $test_url;
+    $query = array($this->randomName(5) => $this->randomName(5));
+    $result = url($url, array('query' => $query));
+    $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, t('External URL can be extended with a query string in $options.'));
+
+    // Verify query string can be extended in an external URL.
+    $url = $test_url . '?drupal=awesome';
+    $query = array($this->randomName(5) => $this->randomName(5));
+    $result = url($url, array('query' => $query));
+    $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, t('External URL query string can be extended with a custom query string in $options.'));
+  }
+}
+
+/**
+ * Tests for the check_plain() and filter_xss() functions.
+ */
+class CommonXssUnitTest extends DrupalUnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'String filtering tests',
+      'description' => 'Confirm that check_plain(), filter_xss(), and check_url() work correctly, including invalid multi-byte sequences.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Check that invalid multi-byte sequences are rejected.
+   */
+  function testInvalidMultiByte() {
+     // Ignore PHP 5.3+ invalid multibyte sequence warning.
+     $text = @check_plain("Foo\xC0barbaz");
+     $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"');
+     $text = check_plain("Fooÿñ");
+     $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"');
+     $text = filter_xss("Foo\xC0barbaz");
+     $this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"');
+     $text = filter_xss("Fooÿñ");
+     $this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ');
+  }
+
+  /**
+   * Check that special characters are escaped.
+   */
+  function testEscaping() {
+     $text = check_plain("<script>");
+     $this->assertEqual($text, '&lt;script&gt;', 'check_plain() escapes &lt;script&gt;');
+  }
+
+  /**
+   * Check that harmful protocols are stripped.
+   */
+  function testBadProtocolStripping() {
+    // Ensure that check_url() strips out harmful protocols, and encodes for
+    // HTML. Ensure drupal_strip_dangerous_protocols() can be used to return a
+    // plain-text string stripped of harmful protocols.
+    $url = 'javascript:http://www.example.com/?x=1&y=2';
+    $expected_plain = 'http://www.example.com/?x=1&y=2';
+    $expected_html = 'http://www.example.com/?x=1&amp;y=2';
+    $this->assertIdentical(check_url($url), $expected_html, t('check_url() filters a URL and encodes it for HTML.'));
+    $this->assertIdentical(drupal_strip_dangerous_protocols($url), $expected_plain, t('drupal_strip_dangerous_protocols() filters a URL and returns plain text.'));
+  }
+}
+
+class CommonSizeTestCase extends DrupalUnitTestCase {
+  protected $exact_test_cases;
+  protected $rounded_test_cases;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Size parsing test',
+      'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.',
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    $kb = DRUPAL_KILOBYTE;
+    $this->exact_test_cases = array(
+      '1 byte' => 1,
+      '1 KB'   => $kb,
+      '1 MB'   => $kb * $kb,
+      '1 GB'   => $kb * $kb * $kb,
+      '1 TB'   => $kb * $kb * $kb * $kb,
+      '1 PB'   => $kb * $kb * $kb * $kb * $kb,
+      '1 EB'   => $kb * $kb * $kb * $kb * $kb * $kb,
+      '1 ZB'   => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
+      '1 YB'   => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
+    );
+    $this->rounded_test_cases = array(
+      '2 bytes' => 2,
+      '1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
+      round(3623651 / ($this->exact_test_cases['1 MB']), 2) . ' MB' => 3623651, // megabytes
+      round(67234178751368124 / ($this->exact_test_cases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
+      round(235346823821125814962843827 / ($this->exact_test_cases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
+    );
+    parent::setUp();
+  }
+
+  /**
+   * Check that format_size() returns the expected string.
+   */
+  function testCommonFormatSize() {
+    foreach (array($this->exact_test_cases, $this->rounded_test_cases) as $test_cases) {
+      foreach ($test_cases as $expected => $input) {
+        $this->assertEqual(
+          ($result = format_size($input, NULL)),
+          $expected,
+          $expected . ' == ' . $result . ' (' . $input . ' bytes)'
+        );
+      }
+    }
+  }
+
+  /**
+   * Check that parse_size() returns the proper byte sizes.
+   */
+  function testCommonParseSize() {
+    foreach ($this->exact_test_cases as $string => $size) {
+      $this->assertEqual(
+        $parsed_size = parse_size($string),
+        $size,
+        $size . ' == ' . $parsed_size . ' (' . $string . ')'
+      );
+    }
+
+    // Some custom parsing tests
+    $string = '23476892 bytes';
+    $this->assertEqual(
+      ($parsed_size = parse_size($string)),
+      $size = 23476892,
+      $string . ' == ' . $parsed_size . ' bytes'
+    );
+    $string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB
+    $this->assertEqual(
+      $parsed_size = parse_size($string),
+      $size = 79691776,
+      $string . ' == ' . $parsed_size . ' bytes'
+    );
+    $string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB
+    $this->assertEqual(
+      $parsed_size = parse_size($string),
+      $size = 81862076662,
+      $string . ' == ' . $parsed_size . ' bytes'
+    );
+  }
+
+  /**
+   * Cross-test parse_size() and format_size().
+   */
+  function testCommonParseSizeFormatSize() {
+    foreach ($this->exact_test_cases as $size) {
+      $this->assertEqual(
+        $size,
+        ($parsed_size = parse_size($string = format_size($size, NULL))),
+        $size . ' == ' . $parsed_size . ' (' . $string . ')'
+      );
+    }
+  }
+}
+
+/**
+ * Test drupal_explode_tags() and drupal_implode_tags().
+ */
+class DrupalTagsHandlingTestCase extends DrupalWebTestCase {
+  var $validTags = array(
+    'Drupal' => 'Drupal',
+    'Drupal with some spaces' => 'Drupal with some spaces',
+    '"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
+    '"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
+  );
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal tags handling',
+      'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.",
+      'group' => 'System'
+    );
+  }
+
+  /**
+   * Explode a series of tags.
+   */
+  function testDrupalExplodeTags() {
+    $string = implode(', ', array_keys($this->validTags));
+    $tags = drupal_explode_tags($string);
+    $this->assertTags($tags);
+  }
+
+  /**
+   * Implode a series of tags.
+   */
+  function testDrupalImplodeTags() {
+    $tags = array_values($this->validTags);
+    // Let's explode and implode to our heart's content.
+    for ($i = 0; $i < 10; $i++) {
+      $string = drupal_implode_tags($tags);
+      $tags = drupal_explode_tags($string);
+    }
+    $this->assertTags($tags);
+  }
+
+  /**
+   * Helper function: asserts that the ending array of tags is what we wanted.
+   */
+  function assertTags($tags) {
+    $original = $this->validTags;
+    foreach ($tags as $tag) {
+      $key = array_search($tag, $original);
+      $this->assertTrue($key, t('Make sure tag %tag shows up in the final tags array (originally %original)', array('%tag' => $tag, '%original' => $key)));
+      unset($original[$key]);
+    }
+    foreach ($original as $leftover) {
+      $this->fail(t('Leftover tag %leftover was left over.', array('%leftover' => $leftover)));
+    }
+  }
+}
+
+/**
+ * Test the Drupal CSS system.
+ */
+class CascadingStylesheetsTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Cascading stylesheets',
+      'description' => 'Tests adding various cascading stylesheets to the page.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('php', 'locale', 'common_test');
+    // Reset drupal_add_css() before each test.
+    drupal_static_reset('drupal_add_css');
+  }
+
+  /**
+   * Check default stylesheets as empty.
+   */
+  function testDefault() {
+    $this->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.'));
+  }
+
+  /**
+   * Tests adding a file stylesheet.
+   */
+  function testAddFile() {
+    $path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
+    $css = drupal_add_css($path);
+    $this->assertEqual($css[$path]['data'], $path, t('Adding a CSS file caches it properly.'));
+  }
+
+  /**
+   * Tests adding an external stylesheet.
+   */
+  function testAddExternal() {
+    $path = 'http://example.com/style.css';
+    $css = drupal_add_css($path, 'external');
+    $this->assertEqual($css[$path]['type'], 'external', t('Adding an external CSS file caches it properly.'));
+  }
+
+  /**
+   * Makes sure that reseting the CSS empties the cache.
+   */
+  function testReset() {
+    drupal_static_reset('drupal_add_css');
+    $this->assertEqual(array(), drupal_add_css(), t('Resetting the CSS empties the cache.'));
+  }
+
+  /**
+   * Tests rendering the stylesheets.
+   */
+  function testRenderFile() {
+    $css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
+    drupal_add_css($css);
+    $styles = drupal_get_css();
+    $this->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.'));
+  }
+
+  /**
+   * Tests rendering an external stylesheet.
+   */
+  function testRenderExternal() {
+    $css = 'http://example.com/style.css';
+    drupal_add_css($css, 'external');
+    $styles = drupal_get_css();
+    // Stylesheet URL may be the href of a LINK tag or in an @import statement
+    // of a STYLE tag.
+    $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, t('Rendering an external CSS file.'));
+  }
+
+  /**
+   * Tests rendering inline stylesheets with preprocessing on.
+   */
+  function testRenderInlinePreprocess() {
+    $css = 'body { padding: 0px; }';
+    $css_preprocessed = '<style type="text/css" media="all">' . drupal_load_stylesheet_content($css, TRUE) . '</style>';
+    drupal_add_css($css, 'inline');
+    $styles = drupal_get_css();
+    $this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
+  }
+
+  /**
+   * Tests rendering inline stylesheets with preprocessing off.
+   */
+  function testRenderInlineNoPreprocess() {
+    $css = 'body { padding: 0px; }';
+    drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
+    $styles = drupal_get_css();
+    $this->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.'));
+  }
+
+  /**
+   * Tests rendering inline stylesheets through a full page request.
+   */
+  function testRenderInlineFullPage() {
+    $css = 'body { font-size: 254px; }';
+    $expected = 'font-size:254px;';
+
+    // Create a node, using the PHP filter that tests drupal_add_css().
+    $php_format_id = db_query_range('SELECT format FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'PHP code'))->fetchField();
+    $settings = array(
+      'type' => 'page',
+      'body' => array(
+        LANGUAGE_NONE => array(
+          array(
+            'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('$css', 'inline'); ?>",
+            'format' => $php_format_id,
+          ),
+        ),
+      ),
+      'promote' => 1,
+    );
+    $node = $this->drupalCreateNode($settings);
+
+    // Fetch the page.
+    $this->drupalGet('node/' . $node->nid);
+    $this->assertRaw($expected, t('Inline stylesheets appear in the full page rendering.'));
+  }
+
+  /**
+   * Test CSS ordering.
+   */
+  function testRenderOrder() {
+    // A module CSS file.
+    drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
+    // A few system CSS files, ordered in a strange way.
+    $system_path = drupal_get_path('module', 'system');
+    drupal_add_css($system_path . '/system-behavior.css', array('weight' => CSS_SYSTEM));
+    drupal_add_css($system_path . '/system.css', array('weight' => CSS_SYSTEM - 10));
+    drupal_add_css($system_path . '/system-menus.css', array('weight' => CSS_SYSTEM));
+
+    $expected = array(
+      $system_path . '/system.css',
+      $system_path . '/system-behavior.css',
+      $system_path . '/system-menus.css',
+      drupal_get_path('module', 'simpletest') . '/simpletest.css',
+    );
+
+
+    $styles = drupal_get_css();
+    // Stylesheet URL may be the href of a LINK tag or in an @import statement
+    // of a STYLE tag.
+    if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
+      $result = $matches[2];
+    }
+    else {
+      $result = array();
+    }
+
+    $this->assertIdentical($result, $expected, t('The CSS files are in the expected order.'));
+  }
+
+  /**
+   * Test CSS override.
+   */
+  function testRenderOverride() {
+    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
+    drupal_add_css(drupal_get_path('module', 'simpletest') . '/tests/system.css');
+
+    // The dummy stylesheet should be the only one included.
+    $styles = drupal_get_css();
+    $this->assert(strpos($styles, drupal_get_path('module', 'simpletest') . '/tests/system.css') !== FALSE, t('The overriding CSS file is output.'));
+    $this->assert(strpos($styles, drupal_get_path('module', 'system') . '/system.css') === FALSE, t('The overriden CSS file is not output.'));
+
+    drupal_add_css(drupal_get_path('module', 'simpletest') . '/tests/system.css');
+    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
+
+    // The standard stylesheet should be the only one included.
+    $styles = drupal_get_css();
+    $this->assert(strpos($styles, drupal_get_path('module', 'system') . '/system.css') !== FALSE, t('The overriding CSS file is output.'));
+    $this->assert(strpos($styles, drupal_get_path('module', 'simpletest') . '/tests/system.css') === FALSE, t('The overriden CSS file is not output.'));
+  }
+
+  /**
+   * Tests Locale module's CSS Alter to include RTL overrides.
+   */
+  function testAlter() {
+    // Switch the language to a right to left language and add system.css.
+    global $language;
+    $language->direction = LANGUAGE_RTL;
+    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
+
+    // Check to see if system-rtl.css was also added.
+    $styles = drupal_get_css();
+    $this->assert(strpos($styles, drupal_get_path('module', 'system') . '/system-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.'));
+
+    // Change the language back to left to right.
+    $language->direction = LANGUAGE_LTR;
+  }
+
+  /**
+   * Tests that the query string remains intact when adding CSS files that have
+   * query string parameters.
+   */
+  function testAddCssFileWithQueryString() {
+    $this->drupalGet('common-test/query-string');
+    $query_string = substr(variable_get('css_js_query_string', '0'), 0, 1);
+    $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?arg1=value1&amp;arg2=value2&amp;' . $query_string, t('Query string was appended correctly to css.'));
+  }
+}
+
+/**
+ * Test for cleaning HTML identifiers.
+ */
+class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'HTML identifiers',
+      'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Tests that drupal_clean_css_identifier() cleans the identifier properly.
+   */
+  function testDrupalCleanCSSIdentifier() {
+    // Verify that no valid ASCII characters are stripped from the identifier.
+    $identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
+    $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid ASCII characters pass through.'));
+
+    // Verify that valid UTF-8 characters are not stripped from the identifier.
+    $identifier = '¡¢£¤¥';
+    $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid UTF-8 characters pass through.'));
+
+    // Verify that invalid characters (including non-breaking space) are stripped from the identifier.
+    $this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', t('Strip invalid characters.'));
+  }
+
+  /**
+   * Tests that drupal_html_class() cleans the class name properly.
+   */
+  function testDrupalHTMLClass() {
+    // Verify Drupal coding standards are enforced.
+    $this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', t('Enforce Drupal coding standards.'));
+  }
+
+  /**
+   * Tests that drupal_html_id() cleans the ID properly.
+   */
+  function testDrupalHTMLId() {
+    // Verify that letters, digits, and hyphens are not stripped from the ID.
+    $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
+    $this->assertIdentical(drupal_html_id($id), $id, t('Verify valid characters pass through.'));
+
+    // Verify that invalid characters are stripped from the ID.
+    $this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', t('Strip invalid characters.'));
+
+    // Verify Drupal coding standards are enforced.
+    $this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', t('Enforce Drupal coding standards.'));
+
+    // Reset the static cache so we can ensure the unique id count is at zero.
+    drupal_static_reset('drupal_html_id');
+
+    // Clean up IDs with invalid starting characters.
+    $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', t('Test the uniqueness of IDs #1.'));
+    $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', t('Test the uniqueness of IDs #2.'));
+    $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', t('Test the uniqueness of IDs #3.'));
+  }
+}
+
+/**
+ * CSS Unit Tests.
+ */
+class CascadingStylesheetsUnitTest extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'CSS Unit Tests',
+      'description' => 'Unit tests on CSS functions like aggregation.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
+   *
+   * This can be enhanced by adding additional CSS files with variant test cases.
+   * Currently, this is specifically testing to make sure that whitespace
+   * is treated with adequate respect (see http://drupal.org/node/472820) and
+   * that image paths in imported files are preserved (see
+   * http://drupal.org/node/265719).
+   */
+  function testLoadCssBasic() {
+    // Array of files to test living in 'simpletest/files/css_test_files/'.
+    // - Original: name.css
+    // - Unoptimized expected content: name.css.unoptimized.css
+    // - Optimized expected content: name.css.optimized.css
+    $testfiles = array(
+      'css_input_without_import.css',
+      'css_input_with_import.css',
+      'comment_hacks.css'
+    );
+    $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
+    foreach ($testfiles as $file) {
+      $expected = file_get_contents("$path/$file.unoptimized.css");
+      $unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE);
+      $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array('@file' => $file)));
+
+      $expected = file_get_contents("$path/$file.optimized.css");
+      $optimized_output = drupal_load_stylesheet("$path/$file", TRUE);
+      $this->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array('@file' => $file)));
+    }
+  }
+}
+
+/**
+ * Test drupal_http_request().
+ */
+class DrupalHTTPRequestTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal HTTP request',
+      'description' => "Performs tests on Drupal's HTTP request mechanism.",
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('system_test');
+  }
+
+  function testDrupalHTTPRequest() {
+    global $is_https;
+
+    // Parse URL schema.
+    $missing_scheme = drupal_http_request('example.com/path');
+    $this->assertEqual($missing_scheme->code, -1002, t('Returned with "-1002" error code.'));
+    $this->assertEqual($missing_scheme->error, 'missing schema', t('Returned with "missing schema" error message.'));
+
+    $unable_to_parse = drupal_http_request('http:///path');
+    $this->assertEqual($unable_to_parse->code, -1001, t('Returned with "-1001" error code.'));
+    $this->assertEqual($unable_to_parse->error, 'unable to parse URL', t('Returned with "unable to parse URL" error message.'));
+
+    // Fetch page.
+    $result = drupal_http_request(url('node', array('absolute' => TRUE)));
+    $this->assertEqual($result->code, 200, t('Fetched page successfully.'));
+    $this->drupalSetContent($result->data);
+    $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
+
+    // Test that code and status message is returned.
+    $result = drupal_http_request(url('pagedoesnotexist', array('absolute' => TRUE)));
+    $this->assertTrue(!empty($result->protocol),  t('Result protocol is returned.'));
+    $this->assertEqual($result->code, '404', t('Result code is 404'));
+    $this->assertEqual($result->status_message, 'Not Found', t('Result status message is "Not Found"'));
+
+    // Skip the timeout tests when the testing environment is HTTPS because
+    // stream_set_timeout() does not work for SSL connections.
+    // @link http://bugs.php.net/bug.php?id=47929
+    if (!$is_https) {
+      // Test that timeout is respected. The test machine is expected to be able
+      // to make the connection (i.e. complete the fsockopen()) in 2 seconds and
+      // return within a total of 5 seconds. If the test machine is extremely
+      // slow, the test will fail. fsockopen() has been seen to time out in
+      // slightly less than the specified timeout, so allow a little slack on
+      // the minimum expected time (i.e. 1.8 instead of 2).
+      timer_start(__METHOD__);
+      $result = drupal_http_request(url('system-test/sleep/10', array('absolute' => TRUE)), array('timeout' => 2));
+      $time = timer_read(__METHOD__) / 1000;
+      $this->assertTrue(1.8 < $time && $time < 5, t('Request timed out (%time seconds).', array('%time' => $time)));
+      $this->assertTrue($result->error, t('An error message was returned.'));
+      $this->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, t('Proper error code was returned.'));
+    }
+  }
+
+  function testDrupalHTTPRequestBasicAuth() {
+    $username = $this->randomName();
+    $password = $this->randomName();
+    $url = url('system-test/auth', array('absolute' => TRUE));
+
+    $auth = str_replace('://', '://' . $username . ':' . $password . '@', $url);
+    $result = drupal_http_request($auth);
+
+    $this->drupalSetContent($result->data);
+    $this->assertRaw($username, t('$_SERVER["PHP_AUTH_USER"] is passed correctly.'));
+    $this->assertRaw($password, t('$_SERVER["PHP_AUTH_PW"] is passed correctly.'));
+  }
+
+  function testDrupalHTTPRequestRedirect() {
+    $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1));
+    $this->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.'));
+
+    $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0));
+    $this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.'));
+
+    $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array('max_redirects' => 1));
+    $this->assertEqual($redirect_invalid->code, -1002, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
+    $this->assertEqual($redirect_invalid->error, 'missing schema', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
+
+    $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array('max_redirects' => 1));
+    $this->assertEqual($redirect_invalid->code, -1001, t('301 redirect to invalid URL returned with error message code "!error".', array('!error' => $redirect_invalid->error)));
+    $this->assertEqual($redirect_invalid->error, 'unable to parse URL', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
+
+    $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array('max_redirects' => 1));
+    $this->assertEqual($redirect_invalid->code, -1003, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
+    $this->assertEqual($redirect_invalid->error, 'invalid schema ftp', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
+
+    $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 1));
+    $this->assertEqual($redirect_302->redirect_code, 302, t('drupal_http_request follows the 302 redirect.'));
+
+    $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 0));
+    $this->assertFalse(isset($redirect_302->redirect_code), t('drupal_http_request does not follow 302 redirect if $retry = 0.'));
+
+    $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 1));
+    $this->assertEqual($redirect_307->redirect_code, 307, t('drupal_http_request follows the 307 redirect.'));
+
+    $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 0));
+    $this->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if max_redirects = 0.'));
+  }
+}
+
+/**
+ * Testing drupal_add_region_content and drupal_get_region_content.
+ */
+class DrupalSetContentTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal set/get regions',
+      'description' => 'Performs tests on setting and retrieiving content from theme regions.',
+      'group' => 'System'
+    );
+  }
+
+
+  /**
+   * Test setting and retrieving content for theme regions.
+   */
+  function testRegions() {
+    global $theme_key;
+
+    $block_regions = array_keys(system_region_list($theme_key));
+    $delimiter = $this->randomName(32);
+    $values = array();
+    // Set some random content for each region available.
+    foreach ($block_regions as $region) {
+      $first_chunk = $this->randomName(32);
+      drupal_add_region_content($region, $first_chunk);
+      $second_chunk = $this->randomName(32);
+      drupal_add_region_content($region, $second_chunk);
+      // Store the expected result for a drupal_get_region_content call for this region.
+      $values[$region] = $first_chunk . $delimiter . $second_chunk;
+    }
+
+    // Ensure drupal_get_region_content returns expected results when fetching all regions.
+    $content = drupal_get_region_content(NULL, $delimiter);
+    foreach ($content as $region => $region_content) {
+      $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching all regions', array('@region' => $region)));
+    }
+
+    // Ensure drupal_get_region_content returns expected results when fetching a single region.
+    foreach ($block_regions as $region) {
+      $region_content = drupal_get_region_content($region, $delimiter);
+      $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching single region.', array('@region' => $region)));
+    }
+  }
+}
+
+/**
+ * Testing drupal_goto and hook_drupal_goto_alter().
+ */
+class DrupalGotoTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal goto',
+      'description' => 'Performs tests on the drupal_goto function and hook_drupal_goto_alter',
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('common_test');
+  }
+
+  /**
+   * Test drupal_goto().
+   */
+  function testDrupalGoto() {
+    $this->drupalGet('common-test/drupal_goto/redirect');
+    $headers = $this->drupalGetHeaders(TRUE);
+    list(, $status) = explode(' ', $headers[0][':status'], 3);
+    $this->assertEqual($status, 302, t('Expected response code was sent.'));
+    $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.'));
+    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('absolute' => TRUE)), t('Drupal goto redirected to expected URL.'));
+
+    $this->drupalGet('common-test/drupal_goto/redirect_advanced');
+    $headers = $this->drupalGetHeaders(TRUE);
+    list(, $status) = explode(' ', $headers[0][':status'], 3);
+    $this->assertEqual($status, 301, t('Expected response code was sent.'));
+    $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.'));
+    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to expected URL.'));
+
+    // Test that drupal_goto() respects ?destination=xxx. Use an complicated URL
+    // to test that the path is encoded and decoded properly.
+    $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123';
+    $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
+    $this->assertText('drupal_goto', t('Drupal goto redirect with destination succeeded.'));
+    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/destination', array('query' => array('foo' => '%25', 'bar' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to given query string destination. '));
+  }
+
+  /**
+   * Test hook_drupal_goto_alter().
+   */
+  function testDrupalGotoAlter() {
+    $this->drupalGet('common-test/drupal_goto/redirect_fail');
+
+    $this->assertNoText(t("Drupal goto failed to stop program"), t("Drupal goto stopped program."));
+    $this->assertNoText('drupal_goto_fail', t("Drupal goto redirect failed."));
+  }
+
+  /**
+   * Test drupal_get_destination().
+   */
+  function testDrupalGetDestination() {
+    $query = $this->randomName(10);
+
+    // Verify that a 'destination' query string is used as destination.
+    $this->drupalGet('common-test/destination', array('query' => array('destination' => $query)));
+    $this->assertText('The destination: ' . $query, t('The given query string destination is determined as destination.'));
+
+    // Verify that the current path is used as destination.
+    $this->drupalGet('common-test/destination', array('query' => array($query => NULL)));
+    $url = 'common-test/destination?' . $query;
+    $this->assertText('The destination: ' . $url, t('The current path is determined as destination.'));
+  }
+}
+
+/**
+ * Tests for the JavaScript system.
+ */
+class JavaScriptTestCase extends DrupalWebTestCase {
+  /**
+   * Store configured value for JavaScript preprocessing.
+   */
+  protected $preprocess_js = NULL;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'JavaScript',
+      'description' => 'Tests the JavaScript system.',
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    // Enable Locale and SimpleTest in the test environment.
+    parent::setUp('locale', 'simpletest', 'common_test');
+
+    // Disable preprocessing
+    $this->preprocess_js = variable_get('preprocess_js', 0);
+    variable_set('preprocess_js', 0);
+
+    // Reset drupal_add_js() and drupal_add_library() statics before each test.
+    drupal_static_reset('drupal_add_js');
+    drupal_static_reset('drupal_add_library');
+  }
+
+  function tearDown() {
+    // Restore configured value for JavaScript preprocessing.
+    variable_set('preprocess_js', $this->preprocess_js);
+    parent::tearDown();
+  }
+
+  /**
+   * Test default JavaScript is empty.
+   */
+  function testDefault() {
+    $this->assertEqual(array(), drupal_add_js(), t('Default JavaScript is empty.'));
+  }
+
+  /**
+   * Test adding a JavaScript file.
+   */
+  function testAddFile() {
+    $javascript = drupal_add_js('misc/collapse.js');
+    $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when a file is added.'));
+    $this->assertTrue(array_key_exists('misc/drupal.js', $javascript), t('Drupal.js is added when file is added.'));
+    $this->assertTrue(array_key_exists('misc/collapse.js', $javascript), t('JavaScript files are correctly added.'));
+    $this->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], t('Base path JavaScript setting is correctly set.'));
+  }
+
+  /**
+   * Test adding settings.
+   */
+  function testAddSetting() {
+    $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
+    $this->assertEqual(280342800, $javascript['settings']['data'][1]['dries'], t('JavaScript setting is set correctly.'));
+    $this->assertEqual('rocks', $javascript['settings']['data'][1]['drupal'], t('The other JavaScript setting is set correctly.'));
+  }
+
+  /**
+   * Tests adding an external JavaScript File.
+   */
+  function testAddExternal() {
+    $path = 'http://example.com/script.js';
+    $javascript = drupal_add_js($path, 'external');
+    $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), t('Added an external JavaScript file.'));
+  }
+
+  /**
+   * Test drupal_get_js() for JavaScript settings.
+   */
+  function testHeaderSetting() {
+    drupal_add_js(array('testSetting' => 'testValue'), 'setting');
+    $javascript = drupal_get_js('header');
+    $this->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.'));
+    $this->assertTrue(strpos($javascript, 'testSetting') > 0, t('Rendered JavaScript header returns custom setting.'));
+    $this->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.'));
+  }
+
+  /**
+   * Test to see if resetting the JavaScript empties the cache.
+   */
+  function testReset() {
+    drupal_add_js('misc/collapse.js');
+    drupal_static_reset('drupal_add_js');
+    $this->assertEqual(array(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.'));
+  }
+
+  /**
+   * Test adding inline scripts.
+   */
+  function testAddInline() {
+    $inline = 'jQuery(function () { });';
+    $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
+    $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when inline scripts are added.'));
+    $data = end($javascript);
+    $this->assertEqual($inline, $data['data'], t('Inline JavaScript is correctly added to the footer.'));
+  }
+
+  /**
+   * Test rendering an external JavaScript file.
+   */
+  function testRenderExternal() {
+    $external = 'http://example.com/example.js';
+    drupal_add_js($external, 'external');
+    $javascript = drupal_get_js();
+    // Local files have a base_path() prefix, external files should not.
+    $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, t('Rendering an external JavaScript file.'));
+  }
+
+  /**
+   * Test drupal_get_js() with a footer scope.
+   */
+  function testFooterHTML() {
+    $inline = 'jQuery(function () { });';
+    drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
+    $javascript = drupal_get_js('footer');
+    $this->assertTrue(strpos($javascript, $inline) > 0, t('Rendered JavaScript footer returns the inline code.'));
+  }
+
+  /**
+   * Test drupal_add_js() sets preproccess to false when cache is set to false.
+   */
+  function testNoCache() {
+    $javascript = drupal_add_js('misc/collapse.js', array('cache' => FALSE));
+    $this->assertFalse($javascript['misc/collapse.js']['preprocess'], t('Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.'));
+  }
+
+  /**
+   * Test adding a JavaScript file with a different weight.
+   */
+  function testDifferentWeight() {
+    $javascript = drupal_add_js('misc/collapse.js', array('weight' => JS_THEME));
+    $this->assertEqual($javascript['misc/collapse.js']['weight'], JS_THEME, t('Adding a JavaScript file with a different weight caches the given weight.'));
+  }
+
+  /**
+   * Test JavaScript ordering.
+   */
+  function testRenderOrder() {
+    // Add a bunch of JavaScript in strange ordering.
+    drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
+    drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
+    drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
+    drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+    drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+    drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+    drupal_add_js('http://example.com/example.js?Weight -5 #1', array('type' => 'external', 'scope' => 'footer', 'weight' => -5));
+    drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+    drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
+    drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
+
+    // Construct the expected result from the regex.
+    $expected = array(
+      "-8 #1",
+      "-8 #2",
+      "-8 #3",
+      "-8 #4",
+      "-5 #1", // The external script.
+      "0 #1",
+      "0 #2",
+      "0 #3",
+      "5 #1",
+      "5 #2",
+    );
+
+    // Retrieve the rendered JavaScript and test against the regex.
+    $js = drupal_get_js('footer');
+    $matches = array();
+    if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) {
+      $result = $matches[1];
+    }
+    else {
+      $result = array();
+    }
+    $this->assertIdentical($result, $expected, t('JavaScript is added in the expected weight order.'));
+  }
+
+  /**
+   * Test rendering the JavaScript with a file's weight above jQuery's.
+   */
+  function testRenderDifferentWeight() {
+    drupal_add_js('misc/collapse.js', array('weight' => JS_LIBRARY - 21));
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), t('Rendering a JavaScript file above jQuery.'));
+  }
+
+  /**
+   * Test altering a JavaScript's weight via hook_js_alter().
+   *
+   * @see simpletest_js_alter()
+   */
+  function testAlter() {
+    // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
+    drupal_add_js('misc/tableselect.js');
+    drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array('weight' => JS_THEME));
+
+    // Render the JavaScript, testing if simpletest.js was altered to be before
+    // tableselect.js. See simpletest_js_alter() to see where this alteration
+    // takes place.
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), t('Altering JavaScript weight through the alter hook.'));
+  }
+
+  /**
+   * Adds a library to the page and tests for both its JavaScript and its CSS.
+   */
+  function testLibraryRender() {
+    $result = drupal_add_library('system', 'farbtastic');
+    $this->assertTrue($result !== FALSE, t('Library was added without errors.'));
+    $scripts = drupal_get_js();
+    $styles = drupal_get_css();
+    $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('JavaScript of library was added to the page.'));
+    $this->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), t('Stylesheet of library was added to the page.'));
+  }
+
+  /**
+   * Adds a JavaScript library to the page and alters it.
+   *
+   * @see common_test_library_alter()
+   */
+  function testLibraryAlter() {
+    // Verify that common_test altered the title of Farbtastic.
+    $library = drupal_get_library('system', 'farbtastic');
+    $this->assertEqual($library['title'], 'Farbtastic: Altered Library', t('Registered libraries were altered.'));
+
+    // common_test_library_alter() also added a dependency on jQuery Form.
+    drupal_add_library('system', 'farbtastic');
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, 'misc/jquery.form.js'), t('Altered library dependencies are added to the page.'));
+  }
+
+  /**
+   * Tests that multiple modules can implement the same library.
+   *
+   * @see common_test_library()
+   */
+  function testLibraryNameConflicts() {
+    $farbtastic = drupal_get_library('common_test', 'farbtastic');
+    $this->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', t('Alternative libraries can be added to the page.'));
+  }
+
+  /**
+   * Tests non-existing libraries.
+   */
+  function testLibraryUnknown() {
+    $result = drupal_get_library('unknown', 'unknown');
+    $this->assertFalse($result, t('Unknown library returned FALSE.'));
+    drupal_static_reset('drupal_get_library');
+
+    $result = drupal_add_library('unknown', 'unknown');
+    $this->assertFalse($result, t('Unknown library returned FALSE.'));
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, 'unknown') === FALSE, t('Unknown library was not added to the page.'));
+  }
+
+  /**
+   * Tests the addition of libraries through the #attached['library'] property.
+   */
+  function testAttachedLibrary() {
+    $element['#attached']['library'][] = array('system', 'farbtastic');
+    drupal_render($element);
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('The attached_library property adds the additional libraries.'));
+  }
+
+  /**
+   * Tests that the query string remains intact when adding JavaScript files
+   *  that have query string parameters.
+   */
+  function testAddJsFileWithQueryString() {
+    $this->drupalGet('common-test/query-string');
+    $query_string = substr(variable_get('css_js_query_string', '0'), 0, 1);
+    $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?arg1=value1&amp;arg2=value2&amp;' . $query_string, t('Query string was appended correctly to js.'));
+  }
+}
+
+/**
+ * Tests for drupal_render().
+ */
+class DrupalRenderUnitTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal render',
+      'description' => 'Performs unit tests on drupal_render().',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('common_test');
+  }
+
+  /**
+   * Test sorting by weight.
+   */
+  function testDrupalRenderSorting() {
+    $first = $this->randomName();
+    $second = $this->randomName();
+    // Build an array with '#weight' set for each element.
+    $elements = array(
+      'second' => array(
+        '#weight' => 10,
+        '#markup' => $second,
+      ),
+      'first' => array(
+        '#weight' => 0,
+        '#markup' => $first,
+      ),
+    );
+    $output = drupal_render($elements);
+
+    // The lowest weight element should appear last in $output.
+    $this->assertTrue(strpos($output, $second) > strpos($output, $first), t('Elements were sorted correctly by weight.'));
+
+    // Confirm that the $elements array has '#sorted' set to TRUE.
+    $this->assertTrue($elements['#sorted'], t("'#sorted' => TRUE was added to the array"));
+
+    // Pass $elements through element_children() and ensure it remains
+    // sorted in the correct order. drupal_render() will return an empty string
+    // if used on the same array in the same request.
+    $children = element_children($elements);
+    $this->assertTrue(array_shift($children) == 'first', t('Child found in the correct order.'));
+    $this->assertTrue(array_shift($children) == 'second', t('Child found in the correct order.'));
+
+
+    // The same array structure again, but with #sorted set to TRUE.
+    $elements = array(
+      'second' => array(
+        '#weight' => 10,
+        '#markup' => $second,
+      ),
+      'first' => array(
+        '#weight' => 0,
+        '#markup' => $first,
+      ),
+      '#sorted' => TRUE,
+    );
+    $output = drupal_render($elements);
+
+    // The elements should appear in output in the same order as the array.
+    $this->assertTrue(strpos($output, $second) < strpos($output, $first), t('Elements were not sorted.'));
+  }
+
+  /**
+   * Test passing arguments to the theme function.
+   */
+  function testDrupalRenderThemeArguments() {
+    $element = array(
+      '#theme' => 'common_test_foo',
+    );
+    // Test that defaults work.
+    $this->assertEqual(drupal_render($element), 'foobar', 'Defaults work');
+    $element = array(
+      '#theme' => 'common_test_foo',
+      '#foo' => $this->randomName(),
+      '#bar' => $this->randomName(),
+    );
+    // Test that passing arguments to the theme function works.
+    $this->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
+  }
+}
+
+/**
+ * Test for valid_url().
+ */
+class ValidUrlTestCase extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Valid Url',
+      'description' => "Performs tests on Drupal's valid url function.",
+      'group' => 'System'
+    );
+  }
+
+  /**
+   * Test valid absolute urls.
+   */
+  function testValidAbsolute() {
+    $url_schemes = array('http', 'https', 'ftp');
+    $valid_absolute_urls = array(
+      'example.com',
+      'www.example.com',
+      'ex-ample.com',
+      '3xampl3.com',
+      'example.com/paren(the)sis',
+      'example.com/index.html#pagetop',
+      'example.com:8080',
+      'subdomain.example.com',
+      'example.com/index.php?q=node',
+      'example.com/index.php?q=node&param=false',
+      'user@www.example.com',
+      'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop',
+      '127.0.0.1',
+      'example.org?',
+      'john%20doe:secret:foo@example.org/',
+      'example.org/~,$\'*;',
+      'caf%C3%A9.example.org',
+      '[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
+    );
+
+    foreach ($url_schemes as $scheme) {
+      foreach ($valid_absolute_urls as $url) {
+        $test_url = $scheme . '://' . $url;
+        $valid_url = valid_url($test_url, TRUE);
+        $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url)));
+      }
+    }
+  }
+
+  /**
+   * Test invalid absolute urls.
+   */
+  function testInvalidAbsolute() {
+    $url_schemes = array('http', 'https', 'ftp');
+    $invalid_ablosule_urls = array(
+      '',
+      'ex!ample.com',
+      'ex%ample.com',
+    );
+
+    foreach ($url_schemes as $scheme) {
+      foreach ($invalid_ablosule_urls as $url) {
+        $test_url = $scheme . '://' . $url;
+        $valid_url = valid_url($test_url, TRUE);
+        $this->assertFalse($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url)));
+      }
+    }
+  }
+
+  /**
+   * Test valid relative urls.
+   */
+  function testValidRelative() {
+    $valid_relative_urls = array(
+      'paren(the)sis',
+      'index.html#pagetop',
+      'index.php?q=node',
+      'index.php?q=node&param=false',
+      'login.php?do=login&style=%23#pagetop',
+    );
+
+    foreach (array('', '/') as $front) {
+      foreach ($valid_relative_urls as $url) {
+        $test_url = $front . $url;
+        $valid_url = valid_url($test_url);
+        $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url)));
+      }
+    }
+  }
+
+  /**
+   * Test invalid relative urls.
+   */
+  function testInvalidRelative() {
+    $invalid_relative_urls = array(
+      'ex^mple',
+      'example<>',
+      'ex%ample',
+    );
+
+    foreach (array('', '/') as $front) {
+      foreach ($invalid_relative_urls as $url) {
+        $test_url = $front . $url;
+        $valid_url = valid_url($test_url);
+        $this->assertFALSE($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url)));
+      }
+    }
+  }
+}
+
+/**
+ * Tests for CRUD API functions.
+ */
+class DrupalDataApiTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Data API functions',
+      'description' => 'Tests the performance of CRUD APIs.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('database_test');
+  }
+
+  /**
+   * Test the drupal_write_record() API function.
+   */
+  function testDrupalWriteRecord() {
+    // Insert a record - no columns allow NULL values.
+    $person = new stdClass();
+    $person->name = 'John';
+    $person->unknown_column = 123;
+    $insert_result = drupal_write_record('test', $person);
+    $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.'));
+    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
+    $this->assertIdentical($person->age, 0, t('Age field set to default value.'));
+    $this->assertIdentical($person->job, 'Undefined', t('Job field set to default value.'));
+
+    // Verify that the record was inserted.
+    $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'John', t('Name field set.'));
+    $this->assertIdentical($result->age, '0', t('Age field set to default value.'));
+    $this->assertIdentical($result->job, 'Undefined', t('Job field set to default value.'));
+    $this->assertFalse(isset($result->unknown_column), t('Unknown column was ignored.'));
+
+    // Update the newly created record.
+    $person->name = 'Peter';
+    $person->age = 27;
+    $person->job = NULL;
+    $update_result = drupal_write_record('test', $person, array('id'));
+    $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.'));
+
+    // Verify that the record was updated.
+    $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'Peter', t('Name field set.'));
+    $this->assertIdentical($result->age, '27', t('Age field set.'));
+    $this->assertIdentical($result->job, '', t('Job field set and cast to string.'));
+
+    // Try to insert NULL in columns that does not allow this.
+    $person = new stdClass();
+    $person->name = 'Ringo';
+    $person->age = NULL;
+    $person->job = NULL;
+    $insert_result = drupal_write_record('test', $person);
+    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
+    $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'Ringo', t('Name field set.'));
+    $this->assertIdentical($result->age, '0', t('Age field set.'));
+    $this->assertIdentical($result->job, '', t('Job field set.'));
+
+    // Insert a record - the "age" column allows NULL.
+    $person = new stdClass();
+    $person->name = 'Paul';
+    $person->age = NULL;
+    $insert_result = drupal_write_record('test_null', $person);
+    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
+    $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'Paul', t('Name field set.'));
+    $this->assertIdentical($result->age, NULL, t('Age field set.'));
+
+    // Insert a record - do not specify the value of a column that allows NULL.
+    $person = new stdClass();
+    $person->name = 'Meredith';
+    $insert_result = drupal_write_record('test_null', $person);
+    $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
+    $this->assertIdentical($person->age, 0, t('Age field set to default value.'));
+    $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'Meredith', t('Name field set.'));
+    $this->assertIdentical($result->age, '0', t('Age field set to default value.'));
+
+    // Update the newly created record.
+    $person->name = 'Mary';
+    $person->age = NULL;
+    $update_result = drupal_write_record('test_null', $person, array('id'));
+    $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'Mary', t('Name field set.'));
+    $this->assertIdentical($result->age, NULL, t('Age field set.'));
+
+    // Insert a record - the "data" column should be serialized.
+    $person = new stdClass();
+    $person->name = 'Dave';
+    $update_result = drupal_write_record('test_serialized', $person);
+    $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical($result->name, 'Dave', t('Name field set.'));
+    $this->assertIdentical($result->info, NULL, t('Info field set.'));
+
+    $person->info = array();
+    $update_result = drupal_write_record('test_serialized', $person, array('id'));
+    $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical(unserialize($result->info), array(), t('Info field updated.'));
+
+    // Update the serialized record.
+    $data = array('foo' => 'bar', 1 => 2, 'empty' => '', 'null' => NULL);
+    $person->info = $data;
+    $update_result = drupal_write_record('test_serialized', $person, array('id'));
+    $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+    $this->assertIdentical(unserialize($result->info), $data, t('Info field updated.'));
+
+    // Run an update query where no field values are changed. The database
+    // layer should return zero for number of affected rows, but
+    // db_write_record() should still return SAVED_UPDATED.
+    $update_result = drupal_write_record('test_null', $person, array('id'));
+    $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a valid update is run without changing any values.'));
+
+    // Insert an object record for a table with a multi-field primary key.
+    $node_access = new stdClass();
+    $node_access->nid = mt_rand();
+    $node_access->gid = mt_rand();
+    $node_access->realm = $this->randomName();
+    $insert_result = drupal_write_record('node_access', $node_access);
+    $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.'));
+
+    // Update the record.
+    $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm'));
+    $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.'));
+  }
+
+}
+
+/**
+ * Tests Simpletest error and exception collector.
+ */
+class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {
+
+  /**
+   * Errors triggered during the test.
+   *
+   * Errors are intercepted by the overriden implementation
+   * of DrupalWebTestCase::error below.
+   *
+   * @var Array
+   */
+  protected $collectedErrors = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'SimpleTest error collector',
+      'description' => 'Performs tests on the Simpletest error and exception collector.',
+      'group' => 'SimpleTest',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('system_test', 'error_test');
+  }
+
+  /**
+   * Test that simpletest collects errors from the tested site.
+   */
+  function testErrorCollect() {
+    $this->collectedErrors = array();
+    $this->drupalGet('error-test/generate-warnings-with-report');
+    $this->assertEqual(count($this->collectedErrors), 3, t('Three errors were collected'));
+
+    if (count($this->collectedErrors) == 3) {
+      $this->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas');
+      $this->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero');
+      $this->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
+    }
+    else {
+      // Give back the errors to the log report.
+      foreach ($this->collectedErrors as $error) {
+        parent::error($error['message'], $error['group'], $error['caller']);
+      }
+    }
+  }
+
+  protected function error($message = '', $group = 'Other', array $caller = NULL) {
+    // This function overiddes DrupalWebTestCase::error(). We collect an error...
+    $this->collectedErrors[] = array(
+      'message' => $message,
+      'group' => $group,
+      'caller' => $caller
+    );
+    // ... and ignore it.
+  }
+
+  /**
+   * Assert that a collected error matches what we are expecting.
+   */
+  function assertError($error, $group, $function, $file, $message = NULL) {
+    $this->assertEqual($error['group'], $group, t("Group was %group", array('%group' => $group)));
+    $this->assertEqual($error['caller']['function'], $function, t("Function was %function", array('%function' => $function)));
+    $this->assertEqual(basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file)));
+    if (isset($message)) {
+      $this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message)));
+    }
+  }
+}
+
+/**
+ * Test the drupal_parse_info_file() API function.
+ */
+class ParseInfoFilesTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Parsing .info files',
+      'description' => 'Tests parsing .info files.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Parse an example .info file an verify the results.
+   */
+  function testParseInfoFile() {
+    $info_values = drupal_parse_info_file(drupal_get_path('module', 'simpletest') . '/tests/common_test_info.txt');
+    $this->assertEqual($info_values['simple_string'], 'A simple string', t('Simple string value was parsed correctly.'), t('System'));
+    $this->assertEqual($info_values['simple_constant'], WATCHDOG_INFO, t('Constant value was parsed correctly.'), t('System'));
+    $this->assertEqual($info_values['double_colon'], 'dummyClassName::', t('Value containing double-colon was parsed correctly.'), t('System'));
+  }
+}
+
+/**
+ * Tests for the format_date() function.
+ */
+class FormatDateUnitTest extends DrupalWebTestCase {
+
+  /**
+   * Arbitrary langcode for a custom language.
+   */
+  const LANGCODE = 'xx';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Format date',
+      'description' => 'Test the format_date() function.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('locale');
+    variable_set('configurable_timezones', 1);
+    variable_set('date_format_long', 'l, j. F Y - G:i');
+    variable_set('date_format_medium', 'j. F Y - G:i');
+    variable_set('date_format_short', 'Y M j - g:ia');
+    variable_set('locale_custom_strings_' . self::LANGCODE, array(
+      '' => array('Sunday' => 'domingo'),
+      'Long month name' => array('March' => 'marzo'),
+    ));
+    $this->refreshVariables();
+  }
+
+  /**
+   * Tests for the format_date() function.
+   */
+  function testFormatDate() {
+    global $user, $language;
+
+    $timestamp = strtotime('2007-03-26T00:00:00+00:00');
+    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test all parameters.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test translated format.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'l, 25-Mar-07 17:00:00 PDT', t('Test an escaped format string.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\domingo, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash character.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\l, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash followed by escaped format string.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.'));
+
+    // Create an admin user and add Spanish language.
+    $admin_user = $this->drupalCreateUser(array('administer languages'));
+    $this->drupalLogin($admin_user);
+    $edit = array(
+      'langcode' => self::LANGCODE,
+      'name' => self::LANGCODE,
+      'native' => self::LANGCODE,
+      'direction' => LANGUAGE_LTR,
+      'prefix' => self::LANGCODE,
+    );
+    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+    // Create a test user to carry out the tests.
+    $test_user = $this->drupalCreateUser();
+    $this->drupalLogin($test_user);
+    $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles');
+    $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save'));
+
+    // Disable session saving as we are about to modify the global $user.
+    drupal_save_session(FALSE);
+    // Save the original user and language and then replace it with the test user and language.
+    $real_user = $user;
+    $user = user_load($test_user->uid, TRUE);
+    $real_language = $language->language;
+    $language->language = $user->language;
+    // Simulate a Drupal bootstrap with the logged-in user.
+    date_default_timezone_set(drupal_get_user_timezone());
+
+    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test a different language.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.'));
+    $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test custom date format.'));
+    $this->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', t('Test long date format.'));
+    $this->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', t('Test medium date format.'));
+    $this->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', t('Test short date format.'));
+    $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', t('Test default date format.'));
+
+    // Restore the original user and language, and enable session saving.
+    $user = $real_user;
+    $language->language = $real_language;
+    // Restore default time zone.
+    date_default_timezone_set(drupal_get_user_timezone());
+    drupal_save_session(TRUE);
+  }
+}
+
+/**
+ * Tests for the format_date() function.
+ */
+class DrupalAttributesUnitTest extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'HTML Attributes',
+      'description' => 'Perform unit tests on the drupal_attributes() function.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Tests that drupal_html_class() cleans the class name properly.
+   */
+  function testDrupalAttributes() {
+    // Verify that special characters are HTML encoded.
+    $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&amp;&quot;&#039;&lt;&gt;"', t('HTML encode attribute values.'));
+
+    // Verify multi-value attributes are concatenated with spaces.
+    $attributes = array('class' => array('first', 'last'));
+    $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.'));
+
+    // Verify empty attribute values are rendered.
+    $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.'));
+    $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.'));
+
+    // Verify multiple attributes are rendered.
+    $attributes = array(
+      'id' => 'id-test',
+      'class' => array('first', 'last'),
+      'alt' => 'Alternate',
+    );
+    $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.'));
+
+    // Verify empty attributes array is rendered.
+    $this->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.'));
+  }
+}
+
+/**
+ * Tests converting PHP variables to JSON strings and back.
+ */
+class DrupalJSONTest extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'JSON',
+      'description' => 'Perform unit tests on the drupal_json_encode() and drupal_json_decode() functions.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Tests converting PHP variables to JSON strings and back.
+   */
+  function testJSON() {
+    // Setup a string with the full ASCII table.
+    // @todo: Add tests for non-ASCII characters and Unicode.
+    $str = '';
+    for ($i=0; $i < 128; $i++) {
+      $str .= chr($i);
+    }
+    // Characters that must be escaped.
+    $html_unsafe = array('<', '>', '&');
+    $html_unsafe_escaped = array('\u003c', '\u003e', '\u0026');
+
+    // Verify there aren't character encoding problems with the source string.
+    $this->assertIdentical(strlen($str), 128, t('A string with the full ASCII table has the correct length.'));
+    foreach ($html_unsafe as $char) {
+      $this->assertTrue(strpos($str, $char) > 0, t('A string with the full ASCII table includes @s.', array('@s' => $char)));
+    }
+
+    // Verify that JSON encoding produces a string with all of the characters.
+    $json = drupal_json_encode($str);
+    $this->assertTrue(strlen($json) > strlen($str), t('A JSON encoded string is larger than the source string.'));
+
+    // Verify that encoding/decoding is reversible.
+    $json_decoded = drupal_json_decode($json);
+    $this->assertIdentical($str, $json_decoded, t('Encoding a string to JSON and decoding back results in the original string.'));
+
+    // Verify reversibility for structured data. Also verify that necessary
+    // characters are escaped.
+    $source = array(TRUE, FALSE, 0, 1, '0', '1', $str, array('key1' => $str, 'key2' => array('nested' => TRUE)));
+    $json = drupal_json_encode($source);
+    foreach ($html_unsafe as $char) {
+      $this->assertTrue(strpos($json, $char) === FALSE, t('A JSON encoded string does not contain @s.', array('@s' => $char)));
+    }
+    // Verify that JSON encoding escapes the HTML unsafe characters
+    foreach ($html_unsafe_escaped as $char) {
+      $this->assertTrue(strpos($json, $char) > 0, t('A JSON encoded string contains @s.', array('@s' => $char)));
+    }
+    $json_decoded = drupal_json_decode($json);
+    $this->assertNotIdentical($source, $json, t('An array encoded in JSON is not identical to the source.'));
+    $this->assertIdentical($source, $json_decoded, t('Encoding structured data to JSON and decoding back results in the original data.'));
+  }
+}
+
+/**
+ * Tests for RDF namespaces XML serialization.
+ */
+class DrupalGetRdfNamespacesTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'RDF namespaces XML serialization tests',
+      'description' => 'Confirm that the serialization of RDF namespaces via drupal_get_rdf_namespaces() is output and parsed correctly in the XHTML document.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('rdf', 'rdf_test');
+  }
+
+  /**
+   * Test RDF namespaces.
+   */
+  function testGetRdfNamespaces() {
+    // Fetches the front page and extracts XML namespaces.
+    $this->drupalGet('');
+    $xml = new SimpleXMLElement($this->content);
+    $ns = $xml->getDocNamespaces();
+
+    $this->assertEqual($ns['owl'], 'http://www.w3.org/2002/07/owl#', t('A prefix declared once is displayed.'));
+    $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', t('The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.'));
+    $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', t('Two prefixes can be assigned the same namespace.'));
+    $this->assertTrue(!isset($ns['dc']), t('A prefix with conflicting namespaces is discarded.'));
+  }
+}
diff --git a/modules/system/tests/database_test.test b/modules/system/tests/database_test.test
new file mode 100644
index 0000000..faffcf5
--- /dev/null
+++ b/modules/system/tests/database_test.test
@@ -0,0 +1,3180 @@
+<?php
+// $Id: database_test.test,v 1.95 2010/07/28 10:52:12 dries Exp $
+
+/**
+ * Dummy class for fetching into a class.
+ *
+ * PDO supports using a new instance of an arbitrary class for records
+ * rather than just a stdClass or array. This class is for testing that
+ * functionality. (See testQueryFetchClass() below)
+ */
+class FakeRecord { }
+
+/**
+ * Base test class for databases.
+ *
+ * Because all database tests share the same test data, we can centralize that
+ * here.
+ */
+class DatabaseTestCase extends DrupalWebTestCase {
+
+  function setUp() {
+    parent::setUp('database_test');
+
+    $schema['test'] = drupal_get_schema('test');
+    $schema['test_people'] = drupal_get_schema('test_people');
+    $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
+    $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
+    $schema['test_task'] = drupal_get_schema('test_task');
+
+    $this->installTables($schema);
+
+    $this->addSampleData();
+  }
+
+  /**
+   * Set up several tables needed by a certain test.
+   *
+   * @param $schema
+   *   An array of table definitions to install.
+   */
+  function installTables($schema) {
+    // This ends up being a test for table drop and create, too, which is nice.
+    foreach ($schema as $name => $data) {
+      if (db_table_exists($name)) {
+        db_drop_table($name);
+      }
+      db_create_table($name, $data);
+    }
+
+    foreach ($schema as $name => $data) {
+      $this->assertTrue(db_table_exists($name), t('Table @name created successfully.', array('@name' => $name)));
+    }
+  }
+
+  /**
+   * Set up tables for NULL handling.
+   */
+  function ensureSampleDataNull() {
+    $schema['test_null'] = drupal_get_schema('test_null');
+    $this->installTables($schema);
+
+    db_insert('test_null')
+    ->fields(array('name', 'age'))
+    ->values(array(
+      'name' => 'Kermit',
+      'age' => 25,
+    ))
+    ->values(array(
+      'name' => 'Fozzie',
+      'age' => NULL,
+    ))
+    ->values(array(
+      'name' => 'Gonzo',
+      'age' => 27,
+    ))
+    ->execute();
+  }
+
+  /**
+   * Setup our sample data.
+   *
+   * These are added using db_query(), since we're not trying to test the
+   * INSERT operations here, just populate.
+   */
+  function addSampleData() {
+    // We need the IDs, so we can't use a multi-insert here.
+    $john = db_insert('test')
+      ->fields(array(
+        'name' => 'John',
+        'age' => 25,
+        'job' => 'Singer',
+      ))
+      ->execute();
+
+    $george = db_insert('test')
+      ->fields(array(
+        'name' => 'George',
+        'age' => 27,
+        'job' => 'Singer',
+      ))
+      ->execute();
+
+    $ringo = db_insert('test')
+      ->fields(array(
+        'name' => 'Ringo',
+        'age' => 28,
+        'job' => 'Drummer',
+      ))
+      ->execute();
+
+    $paul = db_insert('test')
+      ->fields(array(
+        'name' => 'Paul',
+        'age' => 26,
+        'job' => 'Songwriter',
+      ))
+      ->execute();
+
+    db_insert('test_people')
+      ->fields(array(
+        'name' => 'Meredith',
+        'age' => 30,
+        'job' => 'Speaker',
+      ))
+      ->execute();
+
+    db_insert('test_task')
+      ->fields(array('pid', 'task', 'priority'))
+      ->values(array(
+        'pid' => $john,
+        'task' => 'eat',
+        'priority' => 3,
+      ))
+      ->values(array(
+        'pid' => $john,
+        'task' => 'sleep',
+        'priority' => 4,
+      ))
+      ->values(array(
+        'pid' => $john,
+        'task' => 'code',
+        'priority' => 1,
+      ))
+      ->values(array(
+        'pid' => $george,
+        'task' => 'sing',
+        'priority' => 2,
+      ))
+      ->values(array(
+        'pid' => $george,
+        'task' => 'sleep',
+        'priority' => 2,
+      ))
+      ->values(array(
+        'pid' => $paul,
+        'task' => 'found new band',
+        'priority' => 1,
+      ))
+      ->values(array(
+        'pid' => $paul,
+        'task' => 'perform at superbowl',
+        'priority' => 3,
+      ))
+      ->execute();
+  }
+}
+
+/**
+ * Test connection management.
+ */
+class DatabaseConnectionTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Connection tests',
+      'description' => 'Tests of the core database system.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test that connections return appropriate connection objects.
+   */
+  function testConnectionRouting() {
+    // Clone the master credentials to a slave connection.
+    // Note this will result in two independent connection objects that happen
+    // to point to the same place.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+    $db1 = Database::getConnection('default', 'default');
+    $db2 = Database::getConnection('slave', 'default');
+
+    $this->assertNotNull($db1, t('default connection is a real connection object.'));
+    $this->assertNotNull($db2, t('slave connection is a real connection object.'));
+    $this->assertNotIdentical($db1, $db2, t('Each target refers to a different connection.'));
+
+    // Try to open those targets another time, that should return the same objects.
+    $db1b = Database::getConnection('default', 'default');
+    $db2b = Database::getConnection('slave', 'default');
+    $this->assertIdentical($db1, $db1b, t('A second call to getConnection() returns the same object.'));
+    $this->assertIdentical($db2, $db2b, t('A second call to getConnection() returns the same object.'));
+
+    // Try to open an unknown target.
+    $unknown_target = $this->randomName();
+    $db3 = Database::getConnection($unknown_target, 'default');
+    $this->assertNotNull($db3, t('Opening an unknown target returns a real connection object.'));
+    $this->assertIdentical($db1, $db3, t('An unknown target opens the default connection.'));
+
+    // Try to open that unknown target another time, that should return the same object.
+    $db3b = Database::getConnection($unknown_target, 'default');
+    $this->assertIdentical($db3, $db3b, t('A second call to getConnection() returns the same object.'));
+  }
+
+  /**
+   * Test that connections return appropriate connection objects.
+   */
+  function testConnectionRoutingOverride() {
+    // Clone the master credentials to a slave connection.
+    // Note this will result in two independent connection objects that happen
+    // to point to the same place.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+    Database::ignoreTarget('default', 'slave');
+
+    $db1 = Database::getConnection('default', 'default');
+    $db2 = Database::getConnection('slave', 'default');
+
+    $this->assertIdentical($db1, $db2, t('Both targets refer to the same connection.'));
+  }
+
+  /**
+   * Tests the closing of a database connection.
+   */
+  function testConnectionClosing() {
+    // Open the default target so we have an object to compare.
+    $db1 = Database::getConnection('default', 'default');
+
+    // Try to close the the default connection, then open a new one.
+    Database::closeConnection('default', 'default');
+    $db2 = Database::getConnection('default', 'default');
+
+    // Opening a connection after closing it should yield an object different than the original.
+    $this->assertNotIdentical($db1, $db2, t('Opening the default connection after it is closed returns a new object.'));
+  }
+
+  /**
+   * Tests the connection options of the active database.
+   */
+  function testConnectionOptions() {
+    $connection_info = Database::getConnectionInfo('default');
+
+    // Be sure we're connected to the default database.
+    $db = Database::getConnection('default', 'default');
+    $connectionOptions = $db->getConnectionOptions();
+
+    // In the MySQL driver, the port can be different, so check individual
+    // options.
+    $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], t('The default connection info driver matches the current connection options driver.'));
+    $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], t('The default connection info database matches the current connection options database.'));
+
+    // Set up identical slave and confirm connection options are identical.
+    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+    $db2 = Database::getConnection('slave', 'default');
+    $connectionOptions2 = $db2->getConnectionOptions();
+
+    // Get a fresh copy of the default connection options.
+    $connectionOptions = $db->getConnectionOptions();
+    $this->assertIdentical($connectionOptions, $connectionOptions2, t('The default and slave connection options are identical.'));
+
+    // Set up a new connection with different connection info.
+    $test = $connection_info['default'];
+    $test['database'] .= 'test';
+    Database::addConnectionInfo('test', 'default', $test);
+    $connection_info = Database::getConnectionInfo('test');
+
+    // Get a fresh copy of the default connection options.
+    $connectionOptions = $db->getConnectionOptions();
+    $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], t('The test connection info database does not match the current connection options database.'));
+  }
+}
+
+/**
+ * Test fetch actions, part 1.
+ *
+ * We get timeout errors if we try to run too many tests at once.
+ */
+class DatabaseFetchTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Fetch tests',
+      'description' => 'Test the Database system\'s various fetch capabilities.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that we can fetch a record properly in default object mode.
+   */
+  function testQueryFetchDefault() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
+    $this->assertTrue($result instanceof DatabaseStatementInterface, t('Result set is a Drupal statement object.'));
+    foreach ($result as $record) {
+      $records[] = $record;
+      $this->assertTrue(is_object($record), t('Record is an object.'));
+      $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
+    }
+
+    $this->assertIdentical(count($records), 1, t('There is only one record.'));
+  }
+
+  /**
+   * Confirm that we can fetch a record to an object explicitly.
+   */
+  function testQueryFetchObject() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ));
+    foreach ($result as $record) {
+      $records[] = $record;
+      $this->assertTrue(is_object($record), t('Record is an object.'));
+      $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
+    }
+
+    $this->assertIdentical(count($records), 1, t('There is only one record.'));
+  }
+
+  /**
+   * Confirm that we can fetch a record to an array associative explicitly.
+   */
+  function testQueryFetchArray() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC));
+    foreach ($result as $record) {
+      $records[] = $record;
+      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
+        $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.'));
+      }
+    }
+
+    $this->assertIdentical(count($records), 1, t('There is only one record.'));
+  }
+
+  /**
+   * Confirm that we can fetch a record into a new instance of a custom class.
+   *
+   * @see FakeRecord
+   */
+  function testQueryFetchClass() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord'));
+    foreach ($result as $record) {
+      $records[] = $record;
+      if ($this->assertTrue($record instanceof FakeRecord, t('Record is an object of class FakeRecord.'))) {
+        $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
+      }
+    }
+
+    $this->assertIdentical(count($records), 1, t('There is only one record.'));
+  }
+}
+
+/**
+ * Test fetch actions, part 2.
+ *
+ * We get timeout errors if we try to run too many tests at once.
+ */
+class DatabaseFetch2TestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Fetch tests, part 2',
+      'description' => 'Test the Database system\'s various fetch capabilities.',
+      'group' => 'Database',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  // Confirm that we can fetch a record into an indexed array explicitly.
+  function testQueryFetchNum() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM));
+    foreach ($result as $record) {
+      $records[] = $record;
+      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
+        $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.'));
+      }
+    }
+
+    $this->assertIdentical(count($records), 1, 'There is only one record');
+  }
+
+  /**
+   * Confirm that we can fetch a record into a doubly-keyed array explicitly.
+   */
+  function testQueryFetchBoth() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH));
+    foreach ($result as $record) {
+      $records[] = $record;
+      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
+        $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.'));
+        $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.'));
+      }
+    }
+
+    $this->assertIdentical(count($records), 1, t('There is only one record.'));
+  }
+
+  /**
+   * Confirm that we can fetch an entire column of a result set at once.
+   */
+  function testQueryFetchCol() {
+    $records = array();
+    $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
+    $column = $result->fetchCol();
+    $this->assertIdentical(count($column), 3, t('fetchCol() returns the right number of records.'));
+
+    $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
+    $i = 0;
+    foreach ($result as $record) {
+      $this->assertIdentical($record->name, $column[$i++], t('Column matches direct accesss.'));
+    }
+  }
+}
+
+/**
+ * Test the insert builder.
+ */
+class DatabaseInsertTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Insert tests',
+      'description' => 'Test the Insert query builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test the very basic insert functionality.
+   */
+  function testSimpleInsert() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    $query = db_insert('test');
+    $query->fields(array(
+      'name' => 'Yoko',
+      'age' => '29',
+    ));
+    $query->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+    $this->assertIdentical($num_records_before + 1, (int) $num_records_after, t('Record inserts correctly.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
+    $this->assertIdentical($saved_age, '29', t('Can retrieve after inserting.'));
+  }
+
+  /**
+   * Test that we can insert multiple records in one query object.
+   */
+  function testMultiInsert() {
+    $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    $query = db_insert('test');
+    $query->fields(array(
+      'name' => 'Larry',
+      'age' => '30',
+    ));
+
+    // We should be able to specify values in any order if named.
+    $query->values(array(
+      'age' => '31',
+      'name' => 'Curly',
+    ));
+
+    // We should be able to say "use the field order".
+    // This is not the recommended mechanism for most cases, but it should work.
+    $query->values(array('Moe', '32'));
+    $query->execute();
+
+    $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+    $this->assertIdentical($num_records_before + 3, $num_records_after, t('Record inserts correctly.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
+    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
+    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
+    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
+  }
+
+  /**
+   * Test that an insert object can be reused with new data after it executes.
+   */
+  function testRepeatedInsert() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    $query = db_insert('test');
+
+    $query->fields(array(
+      'name' => 'Larry',
+      'age' => '30',
+    ));
+    $query->execute();  // This should run the insert, but leave the fields intact.
+
+    // We should be able to specify values in any order if named.
+    $query->values(array(
+      'age' => '31',
+      'name' => 'Curly',
+    ));
+    $query->execute();
+
+    // We should be able to say "use the field order".
+    $query->values(array('Moe', '32'));
+    $query->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+    $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, t('Record inserts correctly.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
+    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
+    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
+    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
+  }
+
+  /**
+   * Test that we can specify fields without values and specify values later.
+   */
+  function testInsertFieldOnlyDefinintion() {
+    // This is useful for importers, when we want to create a query and define
+    // its fields once, then loop over a multi-insert execution.
+    db_insert('test')
+      ->fields(array('name', 'age'))
+      ->values(array('Larry', '30'))
+      ->values(array('Curly', '31'))
+      ->values(array('Moe', '32'))
+      ->execute();
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
+    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
+    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
+    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
+  }
+
+  /**
+   * Test that inserts return the proper auto-increment ID.
+   */
+  function testInsertLastInsertID() {
+    $id = db_insert('test')
+      ->fields(array(
+        'name' => 'Larry',
+        'age' => '30',
+      ))
+      ->execute();
+
+    $this->assertIdentical($id, '5', t('Auto-increment ID returned successfully.'));
+  }
+
+  /**
+   * Test that the INSERT INTO ... SELECT ... syntax works.
+   */
+  function testInsertSelect() {
+    $query = db_select('test_people', 'tp');
+    // The query builder will always append expressions after fields.
+    // Add the expression first to test that the insert fields are correctly
+    // re-ordered.
+    $query->addExpression('tp.age', 'age');
+    $query
+      ->fields('tp', array('name','job'))
+      ->condition('tp.name', 'Meredith');
+
+    // The resulting query should be equivalent to:
+    // INSERT INTO test (age, name, job)
+    // SELECT tp.age AS age, tp.name AS name, tp.job AS job
+    // FROM test_people tp
+    // WHERE tp.name = 'Meredith'
+    db_insert('test')
+      ->from($query)
+      ->execute();
+
+    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
+    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
+  }
+}
+
+/**
+ * Insert tests using LOB fields, which are weird on some databases.
+ */
+class DatabaseInsertLOBTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Insert tests, LOB fields',
+      'description' => 'Test the Insert query builder with LOB fields.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test that we can insert a single blob field successfully.
+   */
+  function testInsertOneBlob() {
+    $data = "This is\000a test.";
+    $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.'));
+    $id = db_insert('test_one_blob')
+      ->fields(array('blob1' => $data))
+      ->execute();
+    $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+    $this->assertTrue($r['blob1'] === $data, t('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
+  }
+
+  /**
+   * Test that we can insert multiple blob fields in the same query.
+   */
+  function testInsertMultipleBlob() {
+    $id = db_insert('test_two_blobs')
+      ->fields(array(
+        'blob1' => 'This is',
+        'blob2' => 'a test',
+      ))
+      ->execute();
+    $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+    $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', t('Can insert multiple blobs per row.'));
+  }
+}
+
+/**
+ * Insert tests for "database default" values.
+ */
+class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Insert tests, default fields',
+      'description' => 'Test the Insert query builder with default values.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test that we can run a query that is "default values for everything".
+   */
+  function testDefaultInsert() {
+    $query = db_insert('test')->useDefaults(array('job'));
+    $id = $query->execute();
+
+    $schema = drupal_get_schema('test');
+
+    $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
+    $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.'));
+  }
+
+  /**
+   * Test that no action will be preformed if no fields are specified.
+   */
+  function testDefaultEmptyInsert() {
+    $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    try {
+      $result = db_insert('test')->execute();
+      // This is only executed if no exception has been thrown.
+      $this->fail(t('Expected exception NoFieldsException has not been thrown.'));
+    } catch (NoFieldsException $e) {
+      $this->pass(t('Expected exception NoFieldsException has been thrown.'));
+    }
+
+    $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+    $this->assertIdentical($num_records_before, $num_records_after, t('Do nothing as no fields are specified.'));
+  }
+
+  /**
+   * Test that we can insert fields with values and defaults in the same query.
+   */
+  function testDefaultInsertWithFields() {
+    $query = db_insert('test')
+      ->fields(array('name' => 'Bob'))
+      ->useDefaults(array('job'));
+    $id = $query->execute();
+
+    $schema = drupal_get_schema('test');
+
+    $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
+    $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.'));
+  }
+}
+
+/**
+ * Update builder tests.
+ */
+class DatabaseUpdateTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Update tests',
+      'description' => 'Test the Update query builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that we can update a single record successfully.
+   */
+  function testSimpleUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('name' => 'Tiffany'))
+      ->condition('id', 1)
+      ->execute();
+    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
+
+    $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
+    $this->assertIdentical($saved_name, 'Tiffany', t('Updated name successfully.'));
+  }
+
+  /**
+   * Confirm that we can update a multiple records successfully.
+   */
+  function testMultiUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition('job', 'Singer')
+      ->execute();
+    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Confirm that we can update a multiple records with a non-equality condition.
+   */
+  function testMultiGTUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition('age', 26, '>')
+      ->execute();
+    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Confirm that we can update a multiple records with a where call.
+   */
+  function testWhereUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->where('age > :age', array(':age' => 26))
+      ->execute();
+    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Confirm that we can stack condition and where calls.
+   */
+  function testWhereAndConditionUpdate() {
+    $update = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->where('age > :age', array(':age' => 26))
+      ->condition('name', 'Ringo');
+    $num_updated = $update->execute();
+    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
+  }
+
+}
+
+/**
+ * Tests for more complex update statements.
+ */
+class DatabaseUpdateComplexTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Update tests, Complex',
+      'description' => 'Test the Update query builder, complex queries.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test updates with OR conditionals.
+   */
+  function testOrConditionUpdate() {
+    $update = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition(db_or()
+        ->condition('name', 'John')
+        ->condition('name', 'Paul')
+      );
+    $num_updated = $update->execute();
+    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Test WHERE IN clauses.
+   */
+  function testInConditionUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition('name', array('John', 'Paul'), 'IN')
+      ->execute();
+    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Test WHERE NOT IN clauses.
+   */
+  function testNotInConditionUpdate() {
+    // The o is lowercase in the 'NoT IN' operator, to make sure the operators
+    // work in mixed case.
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
+      ->execute();
+    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Test BETWEEN conditional clauses.
+   */
+  function testBetweenConditionUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition('age', array(25, 26), 'BETWEEN')
+      ->execute();
+    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Test LIKE conditionals.
+   */
+  function testLikeConditionUpdate() {
+    $num_updated = db_update('test')
+      ->fields(array('job' => 'Musician'))
+      ->condition('name', '%ge%', 'LIKE')
+      ->execute();
+    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
+  }
+
+  /**
+   * Test update with expression values.
+   */
+  function testUpdateExpression() {
+    $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
+    $GLOBALS['larry_test'] = 1;
+    $num_updated = db_update('test')
+      ->condition('name', 'Ringo')
+      ->fields(array('job' => 'Musician'))
+      ->expression('age', 'age + :age', array(':age' => 4))
+      ->execute();
+    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
+
+    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
+
+    $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
+    $this->assertEqual($person->name, 'Ringo', t('Name set correctly.'));
+    $this->assertEqual($person->age, $before_age + 4, t('Age set correctly.'));
+    $this->assertEqual($person->job, 'Musician', t('Job set correctly.'));
+    $GLOBALS['larry_test'] = 0;
+  }
+
+  /**
+   * Test update with only expression values.
+   */
+  function testUpdateOnlyExpression() {
+    $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
+    $num_updated = db_update('test')
+      ->condition('name', 'Ringo')
+      ->expression('age', 'age + :age', array(':age' => 4))
+      ->execute();
+    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));
+
+    $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
+    $this->assertEqual($before_age + 4, $after_age, t('Age updated correctly'));
+  }
+}
+
+/**
+ * Test update queries involving LOB values.
+ */
+class DatabaseUpdateLOBTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Update tests, LOB',
+      'description' => 'Test the Update query builder with LOB fields.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that we can update a blob column.
+   */
+  function testUpdateOneBlob() {
+    $data = "This is\000a test.";
+    $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.'));
+    $id = db_insert('test_one_blob')
+      ->fields(array('blob1' => $data))
+      ->execute();
+
+    $data .= $data;
+    db_update('test_one_blob')
+      ->condition('id', $id)
+      ->fields(array('blob1' => $data))
+      ->execute();
+
+    $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+    $this->assertTrue($r['blob1'] === $data, t('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
+  }
+
+  /**
+   * Confirm that we can update two blob columns in the same table.
+   */
+  function testUpdateMultipleBlob() {
+    $id = db_insert('test_two_blobs')
+      ->fields(array(
+        'blob1' => 'This is',
+        'blob2' => 'a test',
+      ))
+      ->execute();
+
+    db_update('test_two_blobs')
+      ->condition('id', $id)
+      ->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
+      ->execute();
+
+    $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+    $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', t('Can update multiple blobs per row.'));
+  }
+}
+
+/**
+ * Delete/Truncate tests.
+ *
+ * The DELETE tests are not as extensive, as all of the interesting code for
+ * DELETE queries is in the conditional which is identical to the UPDATE and
+ * SELECT conditional handling.
+ *
+ * The TRUNCATE tests are not extensive either, because the behavior of
+ * TRUNCATE queries is not consistent across database engines. We only test
+ * that a TRUNCATE query actually deletes all rows from the target table.
+ */
+class DatabaseDeleteTruncateTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Delete/Truncate tests',
+      'description' => 'Test the Delete and Truncate query builders.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that we can use a subselect in a delete successfully.
+   */
+  function testSubselectDelete() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
+    $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();
+
+    $subquery = db_select('test', 't')
+      ->fields('t', array('id'))
+      ->condition('t.id', array($pid_to_delete), 'IN');
+    $delete = db_delete('test_task')
+      ->condition('task', 'sleep')
+      ->condition('pid', $subquery, 'IN');
+
+    $num_deleted = $delete->execute();
+    $this->assertEqual($num_deleted, 1, t("Deleted 1 record."));
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.'));
+  }
+
+  /**
+   * Confirm that we can delete a single record successfully.
+   */
+  function testSimpleDelete() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    $num_deleted = db_delete('test')
+      ->condition('id', 1)
+      ->execute();
+    $this->assertIdentical($num_deleted, 1, t('Deleted 1 record.'));
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.'));
+  }
+
+  /**
+   * Confirm that we can truncate a whole table successfully.
+   */
+  function testTruncate() {
+    $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
+
+    db_truncate('test')->execute();
+
+    $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
+    $this->assertEqual(0, $num_records_after, t('Truncate really deletes everything.'));
+  }
+}
+
+/**
+ * Test the MERGE query builder.
+ */
+class DatabaseMergeTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Merge tests',
+      'description' => 'Test the Merge query builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that we can merge-insert a record successfully.
+   */
+  function testMergeInsert() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    $result = db_merge('test_people')
+      ->key(array('job' => 'Presenter'))
+      ->fields(array(
+        'age' => 31,
+        'name' => 'Tiffany',
+      ))
+      ->execute();
+
+    $this->assertEqual($result, MergeQuery::STATUS_INSERT, t('Insert status returned.'));
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
+    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
+    $this->assertEqual($person->age, 31, t('Age set correctly.'));
+    $this->assertEqual($person->job, 'Presenter', t('Job set correctly.'));
+  }
+
+  /**
+   * Confirm that we can merge-update a record successfully.
+   */
+  function testMergeUpdate() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    $result = db_merge('test_people')
+      ->key(array('job' => 'Speaker'))
+      ->fields(array(
+        'age' => 31,
+        'name' => 'Tiffany',
+      ))
+      ->execute();
+
+    $this->assertEqual($result, MergeQuery::STATUS_UPDATE, t('Update status returned.'));
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
+    $this->assertEqual($person->age, 31, t('Age set correctly.'));
+    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
+  }
+
+  /**
+   * Confirm that we can merge-update a record successfully, with exclusion.
+   */
+  function testMergeUpdateExcept() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    db_merge('test_people')
+      ->key(array('job' => 'Speaker'))
+      ->fields(array(
+        'age' => 31,
+        'name' => 'Tiffany',
+      ))
+      ->updateExcept('age')
+      ->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
+    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
+    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
+  }
+
+  /**
+   * Confirm that we can merge-update a record successfully, with alternate replacement.
+   */
+  function testMergeUpdateExplicit() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    db_merge('test_people')
+      ->key(array('job' => 'Speaker'))
+      ->fields(array(
+        'age' => 31,
+        'name' => 'Tiffany',
+      ))
+      ->update(array('name' => 'Joe'))
+      ->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+    $this->assertEqual($person->name, 'Joe', t('Name set correctly.'));
+    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
+    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
+  }
+
+  /**
+   * Confirm that we can merge-update a record successfully, with expressions.
+   */
+  function testMergeUpdateExpression() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();
+
+    // This is a very contrived example, as I have no idea why you'd want to
+    // change age this way, but that's beside the point.
+    // Note that we are also double-setting age here, once as a literal and
+    // once as an expression. This test will only pass if the expression wins,
+    // which is what is supposed to happen.
+    db_merge('test_people')
+      ->key(array('job' => 'Speaker'))
+      ->fields(array(
+        'age' => 31,
+        'name' => 'Tiffany',
+      ))
+      ->expression('age', 'age + :age', array(':age' => 4))
+      ->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
+    $this->assertEqual($person->age, $age_before + 4, t('Age updated correctly.'));
+    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
+  }
+
+  /**
+   * Test that we can merge-insert without any update fields.
+   */
+  function testMergeInsertWithoutUpdate() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    db_merge('test_people')
+      ->key(array('job' => 'Presenter'))
+      ->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
+    $this->assertEqual($person->name, '', t('Name set correctly.'));
+    $this->assertEqual($person->age, 0, t('Age set correctly.'));
+    $this->assertEqual($person->job, 'Presenter', t('Job set correctly.'));
+  }
+
+  /**
+   * Confirm that we can merge-update without any update fields.
+   */
+  function testMergeUpdateWithoutUpdate() {
+    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    db_merge('test_people')
+      ->key(array('job' => 'Speaker'))
+      ->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+    $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.'));
+    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
+    $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.'));
+
+    db_merge('test_people')
+      ->key(array('job' => 'Speaker'))
+      ->fields(array('age' => 31))
+      ->updateExcept(array('age'))
+      ->execute();
+
+    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.'));
+
+    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+    $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.'));
+    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
+    $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.'));
+  }
+
+  /**
+   * Test that an invalid merge query throws an exception like it is supposed to.
+   */
+  function testInvalidMerge() {
+    try {
+      // This query should die because there is no key field specified.
+      db_merge('test_people')
+        ->fields(array(
+          'age' => 31,
+          'name' => 'Tiffany',
+        ))
+        ->execute();
+    }
+    catch (InvalidMergeQueryException $e) {
+      $this->pass(t('InvalidMergeQueryException thrown for invalid query.'));
+      return;
+    }
+    $this->fail(t('No InvalidMergeQueryException thrown'));
+  }
+}
+
+/**
+ * Test the SELECT builder.
+ */
+class DatabaseSelectTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Select tests',
+      'description' => 'Test the Select query builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test rudimentary SELECT statements.
+   */
+  function testSimpleSelect() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $result = $query->execute();
+
+    $num_records = 0;
+    foreach ($result as $record) {
+      $num_records++;
+    }
+
+    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test rudimentary SELECT statement with a COMMENT.
+   */
+  function testSimpleComment() {
+    $query = db_select('test')->comment('Testing query comments');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $result = $query->execute();
+
+    $num_records = 0;
+    foreach ($result as $record) {
+      $num_records++;
+    }
+
+    $query = (string)$query;
+    $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
+
+    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+    $this->assertEqual($query, $expected, t('The flattened query contains the comment string.'));
+  }
+
+  /**
+   * Test basic conditionals on SELECT statements.
+   */
+  function testSimpleSelectConditional() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $query->condition('age', 27);
+    $result = $query->execute();
+
+    // Check that the aliases are being created the way we want.
+    $this->assertEqual($name_field, 'name', t('Name field alias is correct.'));
+    $this->assertEqual($age_field, 'age', t('Age field alias is correct.'));
+
+    // Ensure that we got the right record.
+    $record = $result->fetch();
+    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
+    $this->assertEqual($record->$age_field, 27, t('Fetched age is correct.'));
+  }
+
+  /**
+   * Test SELECT statements with expressions.
+   */
+  function testSimpleSelectExpression() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addExpression("age*2", 'double_age');
+    $query->condition('age', 27);
+    $result = $query->execute();
+
+    // Check that the aliases are being created the way we want.
+    $this->assertEqual($name_field, 'name', t('Name field alias is correct.'));
+    $this->assertEqual($age_field, 'double_age', t('Age field alias is correct.'));
+
+    // Ensure that we got the right record.
+    $record = $result->fetch();
+    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
+    $this->assertEqual($record->$age_field, 27*2, t('Fetched age expression is correct.'));
+  }
+
+  /**
+   * Test SELECT statements with multiple expressions.
+   */
+  function testSimpleSelectExpressionMultiple() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_double_field = $query->addExpression("age*2");
+    $age_triple_field = $query->addExpression("age*3");
+    $query->condition('age', 27);
+    $result = $query->execute();
+
+    // Check that the aliases are being created the way we want.
+    $this->assertEqual($age_double_field, 'expression', t('Double age field alias is correct.'));
+    $this->assertEqual($age_triple_field, 'expression_2', t('Triple age field alias is correct.'));
+
+    // Ensure that we got the right record.
+    $record = $result->fetch();
+    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
+    $this->assertEqual($record->$age_double_field, 27*2, t('Fetched double age expression is correct.'));
+    $this->assertEqual($record->$age_triple_field, 27*3, t('Fetched triple age expression is correct.'));
+  }
+
+  /**
+   * Test adding multiple fields to a select statement at the same time.
+   */
+  function testSimpleSelectMultipleFields() {
+    $record = db_select('test')
+      ->fields('test', array('id', 'name', 'age', 'job'))
+      ->condition('age', 27)
+      ->execute()->fetchObject();
+
+    // Check that all fields we asked for are present.
+    $this->assertNotNull($record->id, t('ID field is present.'));
+    $this->assertNotNull($record->name, t('Name field is present.'));
+    $this->assertNotNull($record->age, t('Age field is present.'));
+    $this->assertNotNull($record->job, t('Job field is present.'));
+
+    // Ensure that we got the right record.
+    // Check that all fields we asked for are present.
+    $this->assertEqual($record->id, 2, t('ID field has the correct value.'));
+    $this->assertEqual($record->name, 'George', t('Name field has the correct value.'));
+    $this->assertEqual($record->age, 27, t('Age field has the correct value.'));
+    $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.'));
+  }
+
+  /**
+   * Test adding all fields from a given table to a select statement.
+   */
+  function testSimpleSelectAllFields() {
+    $record = db_select('test')
+      ->fields('test')
+      ->condition('age', 27)
+      ->execute()->fetchObject();
+
+    // Check that all fields we asked for are present.
+    $this->assertNotNull($record->id, t('ID field is present.'));
+    $this->assertNotNull($record->name, t('Name field is present.'));
+    $this->assertNotNull($record->age, t('Age field is present.'));
+    $this->assertNotNull($record->job, t('Job field is present.'));
+
+    // Ensure that we got the right record.
+    // Check that all fields we asked for are present.
+    $this->assertEqual($record->id, 2, t('ID field has the correct value.'));
+    $this->assertEqual($record->name, 'George', t('Name field has the correct value.'));
+    $this->assertEqual($record->age, 27, t('Age field has the correct value.'));
+    $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.'));
+  }
+
+  /**
+   * Test that we can find a record with a NULL value.
+   */
+  function testNullCondition() {
+    $this->ensureSampleDataNull();
+
+    $names = db_select('test_null', 'tn')
+      ->fields('tn', array('name'))
+      ->isNull('age')
+      ->execute()->fetchCol();
+
+    $this->assertEqual(count($names), 1, t('Correct number of records found with NULL age.'));
+    $this->assertEqual($names[0], 'Fozzie', t('Correct record returned for NULL age.'));
+  }
+
+  /**
+   * Test that we can find a record without a NULL value.
+   */
+  function testNotNullCondition() {
+    $this->ensureSampleDataNull();
+
+    $names = db_select('test_null', 'tn')
+      ->fields('tn', array('name'))
+      ->isNotNull('tn.age')
+      ->orderBy('name')
+      ->execute()->fetchCol();
+
+    $this->assertEqual(count($names), 2, t('Correct number of records found withNOT NULL age.'));
+    $this->assertEqual($names[0], 'Gonzo', t('Correct record returned for NOT NULL age.'));
+    $this->assertEqual($names[1], 'Kermit', t('Correct record returned for NOT NULL age.'));
+  }
+
+  /**
+   * Test that we can UNION multiple Select queries together. This is
+   * semantically equal to UNION DISTINCT, so we don't explicity test that.
+   */
+  function testUnion() {
+    $query_1 = db_select('test', 't')
+      ->fields('t', array('name'))
+      ->condition('age', array(27, 28), 'IN');
+
+    $query_2 = db_select('test', 't')
+      ->fields('t', array('name'))
+      ->condition('age', 28);
+
+    $query_1->union($query_2);
+
+    $names = $query_1->execute()->fetchCol();
+
+    // Ensure we only get 2 records.
+    $this->assertEqual(count($names), 2, t('UNION correctly discarded duplicates.'));
+
+    $this->assertEqual($names[0], 'George', t('First query returned correct name.'));
+    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct name.'));
+  }
+
+  /**
+   * Test that we can UNION ALL multiple Select queries together.
+   */
+  function testUnionAll() {
+    $query_1 = db_select('test', 't')
+      ->fields('t', array('name'))
+      ->condition('age', array(27, 28), 'IN');
+
+    $query_2 = db_select('test', 't')
+      ->fields('t', array('name'))
+      ->condition('age', 28);
+
+    $query_1->union($query_2, 'ALL');
+
+    $names = $query_1->execute()->fetchCol();
+
+    // Ensure we get all 3 records.
+    $this->assertEqual(count($names), 3, t('UNION ALL correctly preserved duplicates.'));
+
+    $this->assertEqual($names[0], 'George', t('First query returned correct first name.'));
+    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct second name.'));
+    $this->assertEqual($names[2], 'Ringo', t('Third query returned correct name.'));
+  }
+
+  /**
+   * Test that random ordering of queries works.
+   *
+   * We take the approach of testing the Drupal layer only, rather than trying
+   * to test that the database's random number generator actually produces
+   * random queries (which is very difficult to do without an unacceptable risk
+   * of the test failing by accident).
+   *
+   * Therefore, in this test we simply run the same query twice and assert that
+   * the two results are reordered versions of each other (as well as of the
+   * same query without the random ordering). It is reasonable to assume that
+   * if we run the same select query twice and the results are in a different
+   * order each time, the only way this could happen is if we have successfully
+   * triggered the database's random ordering functionality.
+   */
+  function testRandomOrder() {
+    // Use 52 items, so the chance that this test fails by accident will be the
+    // same as the chance that a deck of cards will come out in the same order
+    // after shuffling it (in other words, nearly impossible).
+    $number_of_items = 52;
+    while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
+      db_insert('test')->fields(array('name' => $this->randomName()))->execute();
+    }
+
+    // First select the items in order and make sure we get an ordered list.
+    $expected_ids = range(1, $number_of_items);
+    $ordered_ids = db_select('test', 't')
+      ->fields('t', array('id'))
+      ->range(0, $number_of_items)
+      ->orderBy('id')
+      ->execute()
+      ->fetchCol();
+    $this->assertEqual($ordered_ids, $expected_ids, t('A query without random ordering returns IDs in the correct order.'));
+
+    // Now perform the same query, but instead choose a random ordering. We
+    // expect this to contain a differently ordered version of the original
+    // result.
+    $randomized_ids = db_select('test', 't')
+      ->fields('t', array('id'))
+      ->range(0, $number_of_items)
+      ->orderRandom()
+      ->execute()
+      ->fetchCol();
+    $this->assertNotEqual($randomized_ids, $ordered_ids, t('A query with random ordering returns an unordered set of IDs.'));
+    $sorted_ids = $randomized_ids;
+    sort($sorted_ids);
+    $this->assertEqual($sorted_ids, $ordered_ids, t('After sorting the random list, the result matches the original query.'));
+
+    // Now perform the exact same query again, and make sure the order is
+    // different.
+    $randomized_ids_second_set = db_select('test', 't')
+      ->fields('t', array('id'))
+      ->range(0, $number_of_items)
+      ->orderRandom()
+      ->execute()
+      ->fetchCol();
+    $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, t('Performing the query with random ordering a second time returns IDs in a different order.'));
+    $sorted_ids_second_set = $randomized_ids_second_set;
+    sort($sorted_ids_second_set);
+    $this->assertEqual($sorted_ids_second_set, $sorted_ids, t('After sorting the second random list, the result matches the sorted version of the first random list.'));
+  }
+
+  /**
+   * Test that aliases are renamed when duplicates.
+   */
+  function testSelectDuplicateAlias() {
+    $query = db_select('test', 't');
+    $alias1 = $query->addField('t', 'name', 'the_alias');
+    $alias2 = $query->addField('t', 'age', 'the_alias');
+    $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
+  }
+}
+
+/**
+ * Test case for subselects in a dynamic SELECT query.
+ */
+class DatabaseSelectSubqueryTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Select tests, subqueries',
+      'description' => 'Test the Select query builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test that we can use a subquery in a FROM clause.
+   */
+  function testFromSubquerySelect() {
+    // Create a subquery, which is just a normal query object.
+    $subquery = db_select('test_task', 'tt');
+    $subquery->addField('tt', 'pid', 'pid');
+    $subquery->addField('tt', 'task', 'task');
+    $subquery->condition('priority', 1);
+
+    // Create another query that joins against the virtual table resulting
+    // from the subquery.
+    $select = db_select($subquery, 'tt2');
+    $select->join('test', 't', 't.id=tt2.pid');
+    $select->addField('t', 'name');
+
+    $select->condition('task', 'code');
+
+    // The resulting query should be equivalent to:
+    // SELECT t.name
+    // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
+    //   INNER JOIN test t ON t.id=tt.pid
+    // WHERE tt.task = 'code'
+    $people = $select->execute()->fetchCol();
+
+    $this->assertEqual(count($people), 1, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test that we can use a subquery in a FROM clause with a limit.
+   */
+  function testFromSubquerySelectWithLimit() {
+    // Create a subquery, which is just a normal query object.
+    $subquery = db_select('test_task', 'tt');
+    $subquery->addField('tt', 'pid', 'pid');
+    $subquery->addField('tt', 'task', 'task');
+    $subquery->orderBy('priority', 'DESC');
+    $subquery->range(0, 1);
+
+    // Create another query that joins against the virtual table resulting
+    // from the subquery.
+    $select = db_select($subquery, 'tt2');
+    $select->join('test', 't', 't.id=tt2.pid');
+    $select->addField('t', 'name');
+
+    // The resulting query should be equivalent to:
+    // SELECT t.name
+    // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
+    //   INNER JOIN test t ON t.id=tt.pid
+    $people = $select->execute()->fetchCol();
+
+    $this->assertEqual(count($people), 1, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test that we can use a subquery in a WHERE clause.
+   */
+  function testConditionSubquerySelect() {
+    // Create a subquery, which is just a normal query object.
+    $subquery = db_select('test_task', 'tt');
+    $subquery->addField('tt', 'pid', 'pid');
+    $subquery->condition('tt.priority', 1);
+
+    // Create another query that joins against the virtual table resulting
+    // from the subquery.
+    $select = db_select('test_task', 'tt2');
+    $select->addField('tt2', 'task');
+    $select->condition('tt2.pid', $subquery, 'IN');
+
+    // The resulting query should be equivalent to:
+    // SELECT tt2.name
+    // FROM test tt2
+    // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
+    $people = $select->execute()->fetchCol();
+    $this->assertEqual(count($people), 5, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test that we can use a subquery in a JOIN clause.
+   */
+  function testJoinSubquerySelect() {
+    // Create a subquery, which is just a normal query object.
+    $subquery = db_select('test_task', 'tt');
+    $subquery->addField('tt', 'pid', 'pid');
+    $subquery->condition('priority', 1);
+
+    // Create another query that joins against the virtual table resulting
+    // from the subquery.
+    $select = db_select('test', 't');
+    $select->join($subquery, 'tt', 't.id=tt.pid');
+    $select->addField('t', 'name');
+
+    // The resulting query should be equivalent to:
+    // SELECT t.name
+    // FROM test t
+    //   INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
+    $people = $select->execute()->fetchCol();
+
+    $this->assertEqual(count($people), 2, t('Returned the correct number of rows.'));
+  }
+}
+
+/**
+ * Test select with order by clauses.
+ */
+class DatabaseSelectOrderedTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Select tests, ordered',
+      'description' => 'Test the Select query builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test basic order by.
+   */
+  function testSimpleSelectOrdered() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $query->orderBy($age_field);
+    $result = $query->execute();
+
+    $num_records = 0;
+    $last_age = 0;
+    foreach ($result as $record) {
+      $num_records++;
+      $this->assertTrue($record->age >= $last_age, t('Results returned in correct order.'));
+      $last_age = $record->age;
+    }
+
+    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test multiple order by.
+   */
+  function testSimpleSelectMultiOrdered() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $job_field = $query->addField('test', 'job');
+    $query->orderBy($job_field);
+    $query->orderBy($age_field);
+    $result = $query->execute();
+
+    $num_records = 0;
+    $expected = array(
+      array('Ringo', 28, 'Drummer'),
+      array('John', 25, 'Singer'),
+      array('George', 27, 'Singer'),
+      array('Paul', 26, 'Songwriter'),
+    );
+    $results = $result->fetchAll(PDO::FETCH_NUM);
+    foreach ($expected as $k => $record) {
+      $num_records++;
+      foreach ($record as $kk => $col) {
+        if ($expected[$k][$kk] != $results[$k][$kk]) {
+          $this->assertTrue(FALSE, t('Results returned in correct order.'));
+        }
+      }
+    }
+    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test order by descending.
+   */
+  function testSimpleSelectOrderedDesc() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $query->orderBy($age_field, 'DESC');
+    $result = $query->execute();
+
+    $num_records = 0;
+    $last_age = 100000000;
+    foreach ($result as $record) {
+      $num_records++;
+      $this->assertTrue($record->age <= $last_age, t('Results returned in correct order.'));
+      $last_age = $record->age;
+    }
+
+    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+  }
+}
+
+/**
+ * Test more complex select statements.
+ */
+class DatabaseSelectComplexTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Select tests, complex',
+      'description' => 'Test the Select query builder with more complex queries.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test simple JOIN statements.
+   */
+  function testDefaultJoin() {
+    $query = db_select('test_task', 't');
+    $people_alias = $query->join('test', 'p', 't.pid = p.id');
+    $name_field = $query->addField($people_alias, 'name', 'name');
+    $task_field = $query->addField('t', 'task', 'task');
+    $priority_field = $query->addField('t', 'priority', 'priority');
+
+    $query->orderBy($priority_field);
+    $result = $query->execute();
+
+    $num_records = 0;
+    $last_priority = 0;
+    foreach ($result as $record) {
+      $num_records++;
+      $this->assertTrue($record->$priority_field >= $last_priority, t('Results returned in correct order.'));
+      $this->assertNotEqual($record->$name_field, 'Ringo', t('Taskless person not selected.'));
+      $last_priority = $record->$priority_field;
+    }
+
+    $this->assertEqual($num_records, 7, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test LEFT OUTER joins.
+   */
+  function testLeftOuterJoin() {
+    $query = db_select('test', 'p');
+    $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
+    $name_field = $query->addField('p', 'name', 'name');
+    $task_field = $query->addField($people_alias, 'task', 'task');
+    $priority_field = $query->addField($people_alias, 'priority', 'priority');
+
+    $query->orderBy($name_field);
+    $result = $query->execute();
+
+    $num_records = 0;
+    $last_name = 0;
+
+    foreach ($result as $record) {
+      $num_records++;
+      $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, t('Results returned in correct order.'));
+      $last_priority = $record->$name_field;
+    }
+
+    $this->assertEqual($num_records, 8, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test GROUP BY clauses.
+   */
+  function testGroupBy() {
+    $query = db_select('test_task', 't');
+    $count_field = $query->addExpression('COUNT(task)', 'num');
+    $task_field = $query->addField('t', 'task');
+    $query->orderBy($count_field);
+    $query->groupBy($task_field);
+    $result = $query->execute();
+
+    $num_records = 0;
+    $last_count = 0;
+    $records = array();
+    foreach ($result as $record) {
+      $num_records++;
+      $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
+      $last_count = $record->$count_field;
+      $records[$record->$task_field] = $record->$count_field;
+    }
+
+    $correct_results = array(
+      'eat' => 1,
+      'sleep' => 2,
+      'code' => 1,
+      'found new band' => 1,
+      'perform at superbowl' => 1,
+    );
+
+    foreach ($correct_results as $task => $count) {
+      $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
+    }
+
+    $this->assertEqual($num_records, 6, t('Returned the correct number of total rows.'));
+  }
+
+  /**
+   * Test GROUP BY and HAVING clauses together.
+   */
+  function testGroupByAndHaving() {
+    $query = db_select('test_task', 't');
+    $count_field = $query->addExpression('COUNT(task)', 'num');
+    $task_field = $query->addField('t', 'task');
+    $query->orderBy($count_field);
+    $query->groupBy($task_field);
+    $query->having('COUNT(task) >= 2');
+    $result = $query->execute();
+
+    $num_records = 0;
+    $last_count = 0;
+    $records = array();
+    foreach ($result as $record) {
+      $num_records++;
+      $this->assertTrue($record->$count_field >= 2, t('Record has the minimum count.'));
+      $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
+      $last_count = $record->$count_field;
+      $records[$record->$task_field] = $record->$count_field;
+    }
+
+    $correct_results = array(
+      'sleep' => 2,
+    );
+
+    foreach ($correct_results as $task => $count) {
+      $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
+    }
+
+    $this->assertEqual($num_records, 1, t('Returned the correct number of total rows.'));
+  }
+
+  /**
+   * Test range queries. The SQL clause varies with the database.
+   */
+  function testRange() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $query->range(0, 2);
+    $result = $query->execute();
+
+    $num_records = 0;
+    foreach ($result as $record) {
+      $num_records++;
+    }
+
+    $this->assertEqual($num_records, 2, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test distinct queries.
+   */
+  function testDistinct() {
+    $query = db_select('test_task');
+    $task_field = $query->addField('test_task', 'task');
+    $query->distinct();
+    $result = $query->execute();
+
+    $num_records = 0;
+    foreach ($result as $record) {
+      $num_records++;
+    }
+
+    $this->assertEqual($num_records, 6, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test that we can generate a count query from a built query.
+   */
+  function testCountQuery() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $query->orderBy('name');
+
+    $count = $query->countQuery()->execute()->fetchField();
+
+    $this->assertEqual($count, 4, t('Counted the correct number of records.'));
+
+    // Now make sure we didn't break the original query!  We should still have
+    // all of the fields we asked for.
+    $record = $query->execute()->fetch();
+    $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.'));
+    $this->assertEqual($record->$age_field, 27, t('Correct data retrieved.'));
+  }
+
+  /**
+   * Test that countQuery properly removes 'all_fields' statements and
+   * ordering clauses.
+   */
+  function testCountQueryRemovals() {
+    $query = db_select('test');
+    $query->fields('test');
+    $query->orderBy('name');
+    $count = $query->countQuery();
+
+    // Check that the 'all_fields' statement is handled properly.
+    $tables = $query->getTables();
+    $this->assertEqual($tables['test']['all_fields'], 1, t('Query correctly sets \'all_fields\' statement.'));
+    $tables = $count->getTables();
+    $this->assertFalse(isset($tables['test']['all_fields']), t('Count query correctly unsets \'all_fields\' statement.'));
+
+    // Check that the ordering clause is handled properly.
+    $orderby = $query->getOrderBy();
+    $this->assertEqual($orderby['name'], 'ASC', t('Query correctly sets ordering clause.'));
+    $orderby = $count->getOrderBy();
+    $this->assertFalse(isset($orderby['name']), t('Count query correctly unsets ordering caluse.'));
+
+    // Make sure that the count query works.
+    $count = $count->execute()->fetchField();
+
+    $this->assertEqual($count, 4, t('Counted the correct number of records.'));
+  }
+
+  /**
+   * Test that we can generate a count query from a query with distinct.
+   */
+  function testCountQueryDistinct() {
+    $query = db_select('test_task');
+    $task_field = $query->addField('test_task', 'task');
+    $query->distinct();
+
+    $count = $query->countQuery()->execute()->fetchField();
+
+    $this->assertEqual($count, 6, t('Counted the correct number of records.'));
+  }
+
+  /**
+   * Confirm that we can properly nest conditional clauses.
+   */
+  function testNestedConditions() {
+    // This query should translate to:
+    // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
+    // That should find only one record. Yes it's a non-optimal way of writing
+    // that query but that's not the point!
+    $query = db_select('test');
+    $query->addField('test', 'job');
+    $query->condition('name', 'Paul');
+    $query->condition(db_or()->condition('age', 26)->condition('age', 27));
+
+    $job = $query->execute()->fetchField();
+    $this->assertEqual($job, 'Songwriter', t('Correct data retrieved.'));
+  }
+
+  /**
+   * Confirm we can join on a single table twice with a dynamic alias.
+   */
+  function testJoinTwice() {
+    $query = db_select('test')->fields('test');
+    $alias = $query->join('test', 'test', 'test.job = %alias.job');
+    $query->addField($alias, 'name', 'othername');
+    $query->addField($alias, 'job', 'otherjob');
+    $query->where("$alias.name <> test.name");
+    $crowded_job = $query->execute()->fetch();
+    $this->assertEqual($crowded_job->job, $crowded_job->otherjob, t('Correctly joined same table twice.'));
+    $this->assertNotEqual($crowded_job->name, $crowded_job->othername, t('Correctly joined same table twice.'));
+  }
+}
+
+class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Pager query tests',
+      'description' => 'Test the pager query extender.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that a pager query returns the correct results.
+   *
+   * Note that we have to make an HTTP request to a test page handler
+   * because the pager depends on GET parameters.
+   */
+  function testEvenPagerQuery() {
+    // To keep the test from being too brittle, we determine up front
+    // what the page count should be dynamically, and pass the control
+    // information forward to the actual query on the other side of the
+    // HTTP request.
+    $limit = 2;
+    $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    $correct_number = $limit;
+    $num_pages = floor($count / $limit);
+
+    // If there is no remainder from rounding, subtract 1 since we index from 0.
+    if (!($num_pages * $limit < $count)) {
+      $num_pages--;
+    }
+
+    for ($page = 0; $page <= $num_pages; ++$page) {
+      $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
+      $data = json_decode($this->drupalGetContent());
+
+      if ($page == $num_pages) {
+        $correct_number = $count - ($limit * $page);
+      }
+
+      $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
+    }
+  }
+
+  /**
+   * Confirm that a pager query returns the correct results.
+   *
+   * Note that we have to make an HTTP request to a test page handler
+   * because the pager depends on GET parameters.
+   */
+  function testOddPagerQuery() {
+    // To keep the test from being too brittle, we determine up front
+    // what the page count should be dynamically, and pass the control
+    // information forward to the actual query on the other side of the
+    // HTTP request.
+    $limit = 2;
+    $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
+
+    $correct_number = $limit;
+    $num_pages = floor($count / $limit);
+
+    // If there is no remainder from rounding, subtract 1 since we index from 0.
+    if (!($num_pages * $limit < $count)) {
+      $num_pages--;
+    }
+
+    for ($page = 0; $page <= $num_pages; ++$page) {
+      $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
+      $data = json_decode($this->drupalGetContent());
+
+      if ($page == $num_pages) {
+        $correct_number = $count - ($limit * $page);
+      }
+
+      $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
+    }
+  }
+
+  /**
+   * Confirm that a pager query with inner pager query returns valid results.
+   *
+   * This is a regression test for #467984.
+   */
+  function testInnerPagerQuery() {
+    $query = db_select('test', 't')->extend('PagerDefault');
+    $query
+      ->fields('t', array('age'))
+      ->orderBy('age')
+      ->limit(5);
+
+    $outer_query = db_select($query);
+    $outer_query->addField('subquery', 'age');
+
+    $ages = $outer_query
+      ->execute()
+      ->fetchCol();
+    $this->assertEqual($ages, array(25, 26, 27, 28), t('Inner pager query returned the correct ages.'));
+  }
+
+  /**
+   * Confirm that a paging query with a having expression returns valid results.
+   *
+   * This is a regression test for #467984.
+   */
+  function testHavingPagerQuery() {
+    $query = db_select('test', 't')->extend('PagerDefault');
+    $query
+      ->fields('t', array('name'))
+      ->orderBy('name')
+      ->groupBy('name')
+      ->having('MAX(age) > :count', array(':count' => 26))
+      ->limit(5);
+
+    $ages = $query
+      ->execute()
+      ->fetchCol();
+    $this->assertEqual($ages, array('George', 'Ringo'), t('Pager query with having expression returned the correct ages.'));
+  }
+}
+
+
+class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Tablesort query tests',
+      'description' => 'Test the tablesort query extender.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that a tablesort query returns the correct results.
+   *
+   * Note that we have to make an HTTP request to a test page handler
+   * because the pager depends on GET parameters.
+   */
+  function testTableSortQuery() {
+    $sorts = array(
+      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
+      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
+      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
+      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
+      // more elements here
+
+    );
+
+    foreach ($sorts as $sort) {
+      $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
+      $data = json_decode($this->drupalGetContent());
+
+      $first = array_shift($data->tasks);
+      $last = array_pop($data->tasks);
+
+      $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order.'));
+      $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order.'));
+    }
+  }
+
+  /**
+   * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first.
+   *
+   */
+  function testTableSortQueryFirst() {
+    $sorts = array(
+      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
+      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
+      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
+      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
+      // more elements here
+
+    );
+
+    foreach ($sorts as $sort) {
+      $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
+      $data = json_decode($this->drupalGetContent());
+
+      $first = array_shift($data->tasks);
+      $last = array_pop($data->tasks);
+
+      $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
+      $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
+    }
+  }
+}
+
+/**
+ * Select tagging tests.
+ *
+ * Tags are a way to flag queries for alter hooks so they know
+ * what type of query it is, such as "node_access".
+ */
+class DatabaseTaggingTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Query tagging tests',
+      'description' => 'Test the tagging capabilities of the Select builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Confirm that a query has a "tag" added to it.
+   */
+  function testHasTag() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+
+    $query->addTag('test');
+
+    $this->assertTrue($query->hasTag('test'), t('hasTag() returned true.'));
+    $this->assertFalse($query->hasTag('other'), t('hasTag() returned false.'));
+  }
+
+  /**
+   * Test query tagging "has all of these tags" functionality.
+   */
+  function testHasAllTags() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+
+    $query->addTag('test');
+    $query->addTag('other');
+
+    $this->assertTrue($query->hasAllTags('test', 'other'), t('hasAllTags() returned true.'));
+    $this->assertFalse($query->hasAllTags('test', 'stuff'), t('hasAllTags() returned false.'));
+  }
+
+  /**
+   * Test query tagging "has at least one of these tags" functionality.
+   */
+  function testHasAnyTag() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+
+    $query->addTag('test');
+
+    $this->assertTrue($query->hasAnyTag('test', 'other'), t('hasAnyTag() returned true.'));
+    $this->assertFalse($query->hasAnyTag('other', 'stuff'), t('hasAnyTag() returned false.'));
+  }
+
+  /**
+   * Test that we can attach meta data to a query object.
+   *
+   * This is how we pass additional context to alter hooks.
+   */
+  function testMetaData() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+
+    $data = array(
+      'a' => 'A',
+      'b' => 'B',
+    );
+
+    $query->addMetaData('test', $data);
+
+    $return = $query->getMetaData('test');
+    $this->assertEqual($data, $return, t('Corect metadata returned.'));
+
+    $return = $query->getMetaData('nothere');
+    $this->assertNull($return, t('Non-existent key returned NULL.'));
+  }
+}
+
+/**
+ * Select alter tests.
+ *
+ * @see database_test_query_alter()
+ */
+class DatabaseAlterTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Query altering tests',
+      'description' => 'Test the hook_query_alter capabilities of the Select builder.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test that we can do basic alters.
+   */
+  function testSimpleAlter() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+    $query->addTag('database_test_alter_add_range');
+
+    $result = $query->execute();
+
+    $num_records = 0;
+    foreach ($result as $record) {
+      $num_records++;
+    }
+
+    $this->assertEqual($num_records, 2, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test that we can alter the joins on a query.
+   */
+  function testAlterWithJoin() {
+    $query = db_select('test_task');
+    $tid_field = $query->addField('test_task', 'tid');
+    $task_field = $query->addField('test_task', 'task');
+    $query->orderBy($task_field);
+    $query->addTag('database_test_alter_add_join');
+
+    $result = $query->execute();
+
+    $records = $result->fetchAll();
+
+    $this->assertEqual(count($records), 2, t('Returned the correct number of rows.'));
+
+    $this->assertEqual($records[0]->name, 'George', t('Correct data retrieved.'));
+    $this->assertEqual($records[0]->$tid_field, 4, t('Correct data retrieved.'));
+    $this->assertEqual($records[0]->$task_field, 'sing', t('Correct data retrieved.'));
+    $this->assertEqual($records[1]->name, 'George', t('Correct data retrieved.'));
+    $this->assertEqual($records[1]->$tid_field, 5, t('Correct data retrieved.'));
+    $this->assertEqual($records[1]->$task_field, 'sleep', t('Correct data retrieved.'));
+  }
+
+  /**
+   * Test that we can alter a query's conditionals.
+   */
+  function testAlterChangeConditional() {
+    $query = db_select('test_task');
+    $tid_field = $query->addField('test_task', 'tid');
+    $pid_field = $query->addField('test_task', 'pid');
+    $task_field = $query->addField('test_task', 'task');
+    $people_alias = $query->join('test', 'people', "test_task.pid = people.id");
+    $name_field = $query->addField($people_alias, 'name', 'name');
+    $query->condition('test_task.tid', '1');
+    $query->orderBy($tid_field);
+    $query->addTag('database_test_alter_change_conditional');
+
+    $result = $query->execute();
+
+    $records = $result->fetchAll();
+
+    $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
+    $this->assertEqual($records[0]->$name_field, 'John', t('Correct data retrieved.'));
+    $this->assertEqual($records[0]->$tid_field, 2, t('Correct data retrieved.'));
+    $this->assertEqual($records[0]->$pid_field, 1, t('Correct data retrieved.'));
+    $this->assertEqual($records[0]->$task_field, 'sleep', t('Correct data retrieved.'));
+  }
+
+  /**
+   * Test that we can alter the fields of a query.
+   */
+  function testAlterChangeFields() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addField('test', 'age', 'age');
+    $query->orderBy('name');
+    $query->addTag('database_test_alter_change_fields');
+
+    $record = $query->execute()->fetch();
+    $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.'));
+    $this->assertFalse(isset($record->$age_field), t('Age field not found, as intended.'));
+  }
+
+  /**
+   * Test that we can alter expressions in the query.
+   */
+  function testAlterExpression() {
+    $query = db_select('test');
+    $name_field = $query->addField('test', 'name');
+    $age_field = $query->addExpression("age*2", 'double_age');
+    $query->condition('age', 27);
+    $query->addTag('database_test_alter_change_expressions');
+    $result = $query->execute();
+
+    // Ensure that we got the right record.
+    $record = $result->fetch();
+
+    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
+    $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.'));
+  }
+
+  /**
+   * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter().
+   */
+  function testAlterRemoveRange() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+    $query->range(0, 2);
+    $query->addTag('database_test_alter_remove_range');
+
+    $num_records = count($query->execute()->fetchAll());
+
+    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+  }
+
+  /**
+   * Test that we can do basic alters on subqueries.
+   */
+  function testSimpleAlterSubquery() {
+    // Create a sub-query with an alter tag.
+    $subquery = db_select('test', 'p');
+    $subquery->addField('p', 'name');
+    $subquery->addField('p', 'id');
+    // Pick out George.
+    $subquery->condition('age', 27);
+    $subquery->addExpression("age*2", 'double_age');
+    // This query alter should change it to age * 3.
+    $subquery->addTag('database_test_alter_change_expressions');
+
+    // Create a main query and join to sub-query.
+    $query = db_select('test_task', 'tt');
+    $query->join($subquery, 'pq', 'pq.id = tt.pid');
+    $age_field = $query->addField('pq', 'double_age');
+    $name_field = $query->addField('pq', 'name');
+
+    $record = $query->execute()->fetch();
+    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
+    $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.'));
+  }
+}
+
+/**
+ * Regression tests.
+ */
+class DatabaseRegressionTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Regression tests',
+      'description' => 'Regression tests cases for the database layer.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Regression test for #310447.
+   *
+   * Tries to insert non-ascii UTF-8 data in a database column and checks
+   * if its stored properly.
+   */
+  function testRegression_310447() {
+    // That's a 255 character UTF-8 string.
+    $name = str_repeat("é", 255);
+    db_insert('test')
+      ->fields(array(
+        'name' => $name,
+        'age' => 20,
+        'job' => 'Dancer',
+      ))->execute();
+
+    $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
+    $this->assertIdentical($name, $from_database, t("The database handles UTF-8 characters cleanly."));
+  }
+
+  /**
+   * Test the db_table_exists() function.
+   */
+  function testDBTableExists() {
+    $this->assertIdentical(TRUE, db_table_exists('node'), t('Returns true for existent table.'));
+    $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), t('Returns false for nonexistent table.'));
+  }
+
+  /**
+   * Test the db_field_exists() function.
+   */
+  function testDBFieldExists() {
+    $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), t('Returns true for existent column.'));
+    $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), t('Returns false for nonexistent column.'));
+  }
+
+  /**
+   * Test the db_index_exists() function.
+   */
+  function testDBIndexExists() {
+    $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), t('Returns true for existent index.'));
+    $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), t('Returns false for nonexistent index.'));
+  }
+}
+
+/**
+ * Query logging tests.
+ */
+class DatabaseLoggingTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Query logging',
+      'description' => 'Test the query logging facility.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Test that we can log the existence of a query.
+   */
+  function testEnableLogging() {
+    Database::startLog('testing');
+
+    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
+
+    $queries = Database::getLog('testing', 'default');
+
+    $this->assertEqual(count($queries), 2, t('Correct number of queries recorded.'));
+
+    foreach ($queries as $query) {
+      $this->assertEqual($query['caller']['function'], __FUNCTION__, t('Correct function in query log.'));
+    }
+  }
+
+  /**
+   * Test that we can run two logs in parallel.
+   */
+  function testEnableMultiLogging() {
+    Database::startLog('testing1');
+
+    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+    Database::startLog('testing2');
+
+    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
+
+    $queries1 = Database::getLog('testing1');
+    $queries2 = Database::getLog('testing2');
+
+    $this->assertEqual(count($queries1), 2, t('Correct number of queries recorded for log 1.'));
+    $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for log 2.'));
+  }
+
+  /**
+   * Test that we can log queries against multiple targets on the same connection.
+   */
+  function testEnableTargetLogging() {
+    // Clone the master credentials to a slave connection and to another fake
+    // connection.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+    Database::startLog('testing1');
+
+    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol();
+
+    $queries1 = Database::getLog('testing1');
+
+    $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.'));
+    $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.'));
+    $this->assertEqual($queries1[1]['target'], 'slave', t('Second query used slave target.'));
+  }
+
+  /**
+   * Test that logs to separate targets collapse to the same connection properly.
+   *
+   * This test is identical to the one above, except that it doesn't create
+   * a fake target so the query should fall back to running on the default
+   * target.
+   */
+  function testEnableTargetLoggingNoTarget() {
+    Database::startLog('testing1');
+
+    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+    // We use "fake" here as a target because any non-existent target will do.
+    // However, because all of the tests in this class share a single page
+    // request there is likely to be a target of "slave" from one of the other
+    // unit tests, so we use a target here that we know with absolute certainty
+    // does not exist.
+    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();
+
+    $queries1 = Database::getLog('testing1');
+
+    $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.'));
+    $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.'));
+    $this->assertEqual($queries1[1]['target'], 'default', t('Second query used default target as fallback.'));
+  }
+
+  /**
+   * Test that we can log queries separately on different connections.
+   */
+  function testEnableMultiConnectionLogging() {
+    // Clone the master credentials to a fake connection.
+    // That both connections point to the same physical database is irrelevant.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::addConnectionInfo('test2', 'default', $connection_info['default']);
+
+    Database::startLog('testing1');
+    Database::startLog('testing1', 'test2');
+
+    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+    $old_key = db_set_active('test2');
+
+    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol();
+
+    db_set_active($old_key);
+
+    $queries1 = Database::getLog('testing1');
+    $queries2 = Database::getLog('testing1', 'test2');
+
+    $this->assertEqual(count($queries1), 1, t('Correct number of queries recorded for first connection.'));
+    $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for second connection.'));
+  }
+}
+
+/**
+ * Range query tests.
+ */
+class DatabaseRangeQueryTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Range query test',
+      'description' => 'Test the Range query functionality.',
+      'group' => 'Database',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('database_test');
+  }
+
+  /**
+   * Confirm that range query work and return correct result.
+   */
+  function testRangeQuery() {
+    // Test if return correct number of rows.
+    $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll();
+    $this->assertEqual(count($range_rows), 3, t('Range query work and return correct number of rows.'));
+
+    // Test if return target data.
+    $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll();
+    $raw_rows = array_slice($raw_rows, 2, 3);
+    $this->assertEqual($range_rows, $raw_rows, t('Range query work and return target data.'));
+  }
+}
+
+/**
+ * Temporary query tests.
+ */
+class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Temporary query test',
+      'description' => 'Test the temporary query functionality.',
+      'group' => 'Database',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('database_test');
+  }
+
+  /**
+   * Return the number of rows of a table.
+   */
+  function countTableRows($table_name) {
+    return db_select($table_name)->countQuery()->execute()->fetchField();
+  }
+
+  /**
+   * Confirm that temporary tables work and are limited to one request.
+   */
+  function testTemporaryQuery() {
+    $this->drupalGet('database_test/db_query_temporary');
+    $data = json_decode($this->drupalGetContent());
+    if ($data) {
+      $this->assertEqual($this->countTableRows("system"), $data->row_count, t('The temporary table contains the correct amount of rows.'));
+      $this->assertFalse(db_table_exists($data->table_name), t('The temporary table is, indeed, temporary.'));
+    }
+    else {
+      $this->fail(t("The creation of the temporary table failed."));
+    }
+
+    // Now try to run two db_query_temporary() in the same request.
+    $table_name_system = db_query_temporary('SELECT status FROM {system}', array());
+    $table_name_users = db_query_temporary('SELECT uid FROM {users}', array());
+
+    $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), t('A temporary table was created successfully in this request.'));
+    $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), t('A second temporary table was created successfully in this request.'));
+  }
+}
+
+/**
+ * Test how the current database driver interprets the SQL syntax.
+ *
+ * In order to ensure consistent SQL handling throughout Drupal
+ * across multiple kinds of database systems, we test that the
+ * database system interprets SQL syntax in an expected fashion.
+ */
+class DatabaseBasicSyntaxTestCase extends DatabaseTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Basic SQL syntax tests',
+      'description' => 'Test SQL syntax interpretation.',
+      'group' => 'Database',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('database_test');
+  }
+
+  /**
+   * Test for string concatenation.
+   */
+  function testBasicConcat() {
+    $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
+      ':a1' => 'This',
+      ':a2' => ' ',
+      ':a3' => 'is',
+      ':a4' => ' a ',
+      ':a5' => 'test.',
+    ));
+    $this->assertIdentical($result->fetchField(), 'This is a test.', t('Basic CONCAT works.'));
+  }
+
+  /**
+   * Test for string concatenation with field values.
+   */
+  function testFieldConcat() {
+    $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
+      ':a1' => 'The age of ',
+      ':a2' => ' is ',
+      ':a3' => '.',
+      ':age' => 25,
+    ));
+    $this->assertIdentical($result->fetchField(), 'The age of John is 25.', t('Field CONCAT works.'));
+  }
+
+  /**
+   * Test escaping of LIKE wildcards.
+   */
+  function testLikeEscape() {
+    db_insert('test')
+      ->fields(array(
+        'name' => 'Ring_',
+      ))
+      ->execute();
+
+    // Match both "Ringo" and "Ring_".
+    $num_matches = db_select('test', 't')
+      ->condition('name', 'Ring_', 'LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Found 2 records.'));
+    // Match only "Ring_" using a LIKE expression with no wildcards.
+    $num_matches = db_select('test', 't')
+      ->condition('name', db_like('Ring_'), 'LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    $this->assertIdentical($num_matches, '1', t('Found 1 record.'));
+  }
+
+  /**
+   * Test LIKE query containing a backslash.
+   */
+  function testLikeBackslash() {
+    db_insert('test')
+      ->fields(array('name'))
+      ->values(array(
+        'name' => 'abcde\f',
+      ))
+      ->values(array(
+        'name' => 'abc%\_',
+      ))
+      ->execute();
+
+    // Match both rows using a LIKE expression with two wildcards and a verbatim
+    // backslash.
+    $num_matches = db_select('test', 't')
+      ->condition('name', 'abc%\\\\_', 'LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    $this->assertIdentical($num_matches, '2', t('Found 2 records.'));
+    // Match only the former using a LIKE expression with no wildcards.
+    $num_matches = db_select('test', 't')
+      ->condition('name', db_like('abc%\_'), 'LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    $this->assertIdentical($num_matches, '1', t('Found 1 record.'));
+  }
+}
+
+/**
+ * Test invalid data handling.
+ */
+class DatabaseInvalidDataTestCase extends DatabaseTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Invalid data',
+      'description' => 'Test handling of some invalid data.',
+      'group' => 'Database',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('database_test');
+  }
+
+  /**
+   * Traditional SQL database systems abort inserts when invalid data is encountered.
+   */
+  function testInsertDuplicateData() {
+    // Try to insert multiple records where at least one has bad data.
+    try {
+      db_insert('test')
+        ->fields(array('name', 'age', 'job'))
+        ->values(array(
+          'name' => 'Elvis',
+          'age' => 63,
+          'job' => 'Singer',
+        ))->values(array(
+          'name' => 'John', // <-- Duplicate value on unique field.
+          'age' => 17,
+          'job' => 'Consultant',
+        ))
+        ->values(array(
+          'name' => 'Frank',
+          'age' => 75,
+          'job' => 'Singer',
+        ))
+        ->execute();
+      $this->fail(t('Insert succeedded when it should not have.'));
+    }
+    catch (Exception $e) {
+      // Check if the first record was inserted.
+      $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
+
+      if ($name == 'Elvis') {
+        if (!Database::getConnection()->supportsTransactions()) {
+          // This is an expected fail.
+          // Database engines that don't support transactions can leave partial
+          // inserts in place when an error occurs. This is the case for MySQL
+          // when running on a MyISAM table.
+          $this->pass(t("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions"));
+        }
+        else {
+          $this->fail(t('The whole transaction is rolled back when a duplicate key insert occurs.'));
+        }
+      }
+      else {
+        $this->pass(t('The whole transaction is rolled back when a duplicate key insert occurs.'));
+      }
+
+      // Ensure the other values were not inserted.
+      $record = db_select('test')
+        ->fields('test', array('name', 'age'))
+        ->condition('age', array(17, 75), 'IN')
+        ->execute()->fetchObject();
+
+      $this->assertFalse($record, t('The rest of the insert aborted as expected.'));
+    }
+  }
+
+}
+
+/**
+ * Drupal-specific SQL syntax tests.
+ */
+class DatabaseQueryTestCase extends DatabaseTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom query syntax tests',
+      'description' => 'Test Drupal\'s extended prepared statement syntax..',
+      'group' => 'Database',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('database_test');
+  }
+
+  /**
+   * Test that we can specify an array of values in the query by simply passing in an array.
+   */
+  function testArraySubstitution() {
+    $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll();
+
+    $this->assertEqual(count($names), 3, t('Correct number of names returned'));
+  }
+}
+
+/**
+ * Test transaction support, particularly nesting.
+ *
+ * We test nesting by having two transaction layers, an outer and inner. The
+ * outer layer encapsulates the inner layer. Our transaction nesting abstraction
+ * should allow the outer layer function to call any function it wants,
+ * especially the inner layer that starts its own transaction, and be
+ * confident that, when the function it calls returns, its own transaction
+ * is still "alive."
+ *
+ * Call structure:
+ *   transactionOuterLayer()
+ *     Start transaction
+ *     transactionInnerLayer()
+ *       Start transaction (does nothing in database)
+ *       [Maybe decide to roll back]
+ *     Do more stuff
+ *     Should still be in transaction A
+ *
+ */
+class DatabaseTransactionTestCase extends DatabaseTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Transaction tests',
+      'description' => 'Test the transaction abstraction system.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   * Helper method for transaction unit test. This "outer layer" transaction
+   * starts and then encapsulates the "inner layer" transaction. This nesting
+   * is used to evaluate whether the the database transaction API properly
+   * supports nesting. By "properly supports," we mean the outer transaction
+   * continues to exist regardless of what functions are called and whether
+   * those functions start their own transactions.
+   *
+   * In contrast, a typical database would commit the outer transaction, start
+   * a new transaction for the inner layer, commit the inner layer transaction,
+   * and then be confused when the outer layer transaction tries to commit its
+   * transaction (which was already committed when the inner transaction
+   * started).
+   *
+   * @param $suffix
+   *   Suffix to add to field values to differentiate tests.
+   * @param $rollback
+   *   Whether or not to try rolling back the transaction when we're done.
+   */
+  protected function transactionOuterLayer($suffix, $rollback = FALSE) {
+    $connection = Database::getConnection();
+    $depth = $connection->transactionDepth();
+    $txn = db_transaction();
+
+    // Insert a single row into the testing table.
+    db_insert('test')
+      ->fields(array(
+        'name' => 'David' . $suffix,
+        'age' => '24',
+      ))
+      ->execute();
+
+    $this->assertTrue($connection->inTransaction(), t('In transaction before calling nested transaction.'));
+
+    // We're already in a transaction, but we call ->transactionInnerLayer
+    // to nest another transaction inside the current one.
+    $this->transactionInnerLayer($suffix, $rollback);
+
+    $this->assertTrue($connection->inTransaction(), t('In transaction after calling nested transaction.'));
+
+    if ($rollback) {
+      // Roll back the transaction, if requested.
+      // This rollback should propagate to the last savepoint.
+      $txn->rollback();
+      $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().'));
+    }
+  }
+
+  /**
+   * Helper method for transaction unit tests. This "inner layer" transaction
+   * is either used alone or nested inside of the "outer layer" transaction.
+   *
+   * @param $suffix
+   *   Suffix to add to field values to differentiate tests.
+   * @param $rollback
+   *   Whether or not to try rolling back the transaction when we're done.
+   */
+  protected function transactionInnerLayer($suffix, $rollback = FALSE) {
+    $connection = Database::getConnection();
+
+    $this->assertTrue($connection->inTransaction(), t('In transaction in nested transaction.'));
+
+    $depth = $connection->transactionDepth();
+    // Start a transaction. If we're being called from ->transactionOuterLayer,
+    // then we're already in a transaction. Normally, that would make starting
+    // a transaction here dangerous, but the database API handles this problem
+    // for us by tracking the nesting and avoiding the danger.
+    $txn = db_transaction();
+
+    $depth2 = $connection->transactionDepth();
+    $this->assertTrue($depth < $depth2, t('Transaction depth is has increased with new transaction.'));
+
+    // Insert a single row into the testing table.
+    db_insert('test')
+      ->fields(array(
+        'name' => 'Daniel' . $suffix,
+        'age' => '19',
+      ))
+      ->execute();
+
+    $this->assertTrue($connection->inTransaction(), t('In transaction inside nested transaction.'));
+
+    if ($rollback) {
+      // Roll back the transaction, if requested.
+      // This rollback should propagate to the last savepoint.
+      $txn->rollback();
+      $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().'));
+    }
+  }
+
+  /**
+   * Test transaction rollback on a database that supports transactions.
+   *
+   * If the active connection does not support transactions, this test does nothing.
+   */
+  function testTransactionRollBackSupported() {
+    // This test won't work right if transactions are not supported.
+    if (!Database::getConnection()->supportsTransactions()) {
+      return;
+    }
+    try {
+      // Create two nested transactions. Roll back from the inner one.
+      $this->transactionOuterLayer('B', TRUE);
+
+      // Neither of the rows we inserted in the two transaction layers
+      // should be present in the tables post-rollback.
+      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
+      $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidB row after commit.'));
+      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
+      $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielB row after commit.'));
+    }
+    catch (Exception $e) {
+      $this->fail($e->getMessage());
+    }
+  }
+
+  /**
+   * Test transaction rollback on a database that does not support transactions.
+   *
+   * If the active driver supports transactions, this test does nothing.
+   */
+  function testTransactionRollBackNotSupported() {
+    // This test won't work right if transactions are supported.
+    if (Database::getConnection()->supportsTransactions()) {
+      return;
+    }
+    try {
+      // Create two nested transactions. Attempt to roll back from the inner one.
+      $this->transactionOuterLayer('B', TRUE);
+
+      // Because our current database claims to not support transactions,
+      // the inserted rows should be present despite the attempt to roll back.
+      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
+      $this->assertIdentical($saved_age, '24', t('DavidB not rolled back, since transactions are not supported.'));
+      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
+      $this->assertIdentical($saved_age, '19', t('DanielB not rolled back, since transactions are not supported.'));
+    }
+    catch (Exception $e) {
+      $this->fail($e->getMessage());
+    }
+  }
+
+  /**
+   * Test committed transaction.
+   *
+   * The behavior of this test should be identical for connections that support
+   * transactions and those that do not.
+   */
+  function testCommittedTransaction() {
+    try {
+      // Create two nested transactions. The changes should be committed.
+      $this->transactionOuterLayer('A');
+
+      // Because we committed, both of the inserted rows should be present.
+      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
+      $this->assertIdentical($saved_age, '24', t('Can retrieve DavidA row after commit.'));
+      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
+      $this->assertIdentical($saved_age, '19', t('Can retrieve DanielA row after commit.'));
+    }
+    catch (Exception $e) {
+      $this->fail($e->getMessage());
+    }
+  }
+}
+
+
+/**
+ * Check the sequences API.
+ */
+class DatabaseNextIdCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => t('Sequences API'),
+      'description' => t('Test the secondary sequences API.'),
+      'group' => t('Database'),
+    );
+  }
+
+  /**
+   * Test that the sequences API work.
+   */
+  function testDbNextId() {
+    $first = db_next_id();
+    $second = db_next_id();
+    // We can test for exact increase in here because we know there is no
+    // other process operating on these tables -- normally we could only
+    // expect $second > $first.
+    $this->assertEqual($first + 1, $second, t('The second call from a sequence provides a number increased by one.'));
+    $result = db_next_id(1000);
+    $this->assertEqual($result, 1001, t('Sequence provides a larger number than the existing ID.'));
+  }
+}
+
+/**
+ * Tests the empty pseudo-statement class.
+ */
+class DatabaseEmptyStatementTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => t('Empty statement'),
+      'description' => t('Test the empty pseudo-statement class.'),
+      'group' => t('Database'),
+    );
+  }
+
+  /**
+   * Test that the empty result set behaves as empty.
+   */
+  function testEmpty() {
+    $result = new DatabaseStatementEmpty();
+
+    $this->assertTrue($result instanceof DatabaseStatementInterface, t('Class implements expected interface'));
+    $this->assertNull($result->fetchObject(), t('Null result returned.'));
+  }
+
+  /**
+   * Test that the empty result set iterates safely.
+   */
+  function testEmptyIteration() {
+    $result = new DatabaseStatementEmpty();
+
+    foreach ($result as $record) {
+      $this->fail(t('Iterating empty result set should not iterate.'));
+      return;
+    }
+
+    $this->pass(t('Iterating empty result set skipped iteration.'));
+  }
+
+  /**
+   * Test that the empty result set mass-fetches in an expected way.
+   */
+  function testEmptyFetchAll() {
+    $result = new DatabaseStatementEmpty();
+
+    $this->assertEqual($result->fetchAll(), array(), t('Empty array returned from empty result set.'));
+  }
+}
diff --git a/modules/system/tests/entity_query.test b/modules/system/tests/entity_query.test
new file mode 100644
index 0000000..39a176f
--- /dev/null
+++ b/modules/system/tests/entity_query.test
@@ -0,0 +1,930 @@
+<?php
+
+// $Id: entity_query.test,v 1.5 2010/06/26 02:16:23 dries Exp $
+
+/**
+ * @file
+ * Unit test file for the entity API.
+ */
+
+/**
+ * Tests EntityFieldQuery.
+ */
+class EntityFieldQueryTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity query',
+      'description' => 'Test the EntityFieldQuery class.',
+      'group' => 'Entity API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('field_test'));
+
+    field_attach_create_bundle('bundle1', 'test_entity_bundle_key');
+    field_attach_create_bundle('bundle2', 'test_entity_bundle_key');
+    field_attach_create_bundle('test_bundle', 'test_entity');
+    field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle');
+
+    $instances = array();
+    $this->fields = array();
+    $this->field_names[0] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 4);
+    $field = field_create_field($field);
+    $this->fields[0] = $field;
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => '',
+      'bundle' => '',
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_instance_setting' => $this->randomName(),
+      ),
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'label' => 'Test Field',
+        'settings' => array(
+          'test_widget_setting' => $this->randomName(),
+        )
+      )
+    );
+
+    $instances[0] = $instance;
+
+    // Add an instance to that bundle.
+    $instances[0]['bundle'] = 'bundle1';
+    $instances[0]['entity_type'] = 'test_entity_bundle_key';
+    field_create_instance($instances[0]);
+    $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle';
+    field_create_instance($instances[0]);
+
+    $this->field_names[1] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
+    $field = field_create_field($field);
+    $this->fields[1] = $field;
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => '',
+      'bundle' => '',
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_instance_setting' => $this->randomName(),
+      ),
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'label' => 'Test Field',
+        'settings' => array(
+          'test_widget_setting' => $this->randomName(),
+        )
+      )
+    );
+
+    $instances[1] = $instance;
+
+    // Add an instance to that bundle.
+    $instances[1]['bundle'] = 'bundle1';
+    $instances[1]['entity_type'] = 'test_entity_bundle_key';
+    field_create_instance($instances[1]);
+    $instances[1]['bundle'] = $instances[1]['entity_type'] = 'test_entity_bundle';
+    field_create_instance($instances[1]);
+
+    $this->instances = $instances;
+    // Write entity base table if there is one.
+    $entities = array();
+
+    // Create entities which have a 'bundle key' defined.
+    for ($i = 1; $i < 7; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = $i;
+      $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2';
+
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+      drupal_write_record('test_entity_bundle_key', $entity);
+      field_attach_insert('test_entity_bundle_key', $entity);
+    }
+
+    $entity = new stdClass();
+    $entity->ftid = 5;
+    $entity->fttype = 'bundle2';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['shape'] = 'square';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['color'] = 'red';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['shape'] = 'circle';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['color'] = 'blue';
+    drupal_write_record('test_entity_bundle', $entity);
+    field_attach_insert('test_entity_bundle', $entity);
+
+    $instances[2] = $instance;
+    $instances[2]['bundle'] = 'test_bundle';
+    $instances[2]['field_name'] = $this->field_names[0];
+    $instances[2]['entity_type'] = 'test_entity';
+    field_create_instance($instances[2]);
+
+    // Create entities with support for revisions.
+    for ($i = 1; $i < 5; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = $i;
+      $entity->ftvid = $i;
+      $entity->fttype = 'test_bundle';
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+
+      drupal_write_record('test_entity', $entity);
+      field_attach_insert('test_entity', $entity);
+      drupal_write_record('test_entity_revision', $entity);
+    }
+
+    // Add two revisions to an entity.
+    for ($i = 100; $i < 102; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = 4;
+      $entity->ftvid = $i;
+      $entity->fttype = 'test_bundle';
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+
+      drupal_write_record('test_entity', $entity, 'ftid');
+      drupal_write_record('test_entity_revision', $entity);
+
+      db_update('test_entity')
+       ->fields(array('ftvid' => $entity->ftvid))
+       ->condition('ftid', $entity->ftid)
+       ->execute();
+
+      field_attach_update('test_entity', $entity);
+    }
+  }
+
+  /**
+   * Tests EntityFieldQuery.
+   */
+  function testEntityFieldQuery() {
+    // Test entity_type condition.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test entity entity_type condition.'));
+
+    // Test entity_id condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('entity_id', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+    ), t('Test entity entity_id condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+    ), t('Test entity entity_id condition and entity_id property condition.'));
+
+    // Test bundle condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('bundle', 'bundle1');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test entity bundle condition: bundle1.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('bundle', 'bundle2');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test entity bundle condition: bundle2.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('fttype', 'bundle2');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test entity bundle condition and bundle property condition.'));
+
+    // Test revision_id condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->entityCondition('revision_id', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 3),
+    ), t('Test entity revision_id condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->propertyCondition('ftvid', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 3),
+    ), t('Test entity revision_id condition and revision_id property condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->fieldCondition($this->fields[0], 'value', 100, '>=')
+      ->age(FIELD_LOAD_REVISION);
+    $this->assertEntityFieldQuery($query, array(
+        array('test_entity', 100),
+        array('test_entity', 101),
+    ), t('Test revision age.'));
+
+    // Test that fields attached to the non-revision supporting entity
+    // 'test_entity_bundle_key' are reachable in FIELD_LOAD_REVISION.
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[0], 'value', 100, '<')
+      ->age(FIELD_LOAD_REVISION);
+    $this->assertEntityFieldQuery($query, array(
+        array('test_entity_bundle_key', 1),
+        array('test_entity_bundle_key', 2),
+        array('test_entity_bundle_key', 3),
+        array('test_entity_bundle_key', 4),
+        array('test_entity', 1),
+        array('test_entity', 2),
+        array('test_entity', 3),
+        array('test_entity', 4),
+    ), t('Test that fields are reachable from FIELD_LOAD_REVISION even for non-revision entities.'));
+
+    // Test entity sort by entity_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('entity_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity entity_id in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('entity_id', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity entity_id in descending order.'), TRUE);
+
+    // Test property sort by entity id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity entity_id property in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity entity_id property in descending order.'), TRUE);
+
+    // Test entity sort by bundle.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('bundle', 'ASC')
+      ->propertyOrderBy('ftid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity bundle in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('bundle', 'DESC')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity bundle in descending order.'), TRUE);
+
+    // Test entity sort by revision_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->entityOrderBy('revision_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test sort entity revision_id in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->entityOrderBy('revision_id', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 4),
+      array('test_entity', 3),
+      array('test_entity', 2),
+      array('test_entity', 1),
+    ), t('Test sort entity revision_id in descending order.'), TRUE);
+
+    // Test property sort by revision_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->propertyOrderBy('ftvid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test sort entity revision_id property in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->propertyOrderBy('ftvid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 4),
+      array('test_entity', 3),
+      array('test_entity', 2),
+      array('test_entity', 1),
+    ), t('Test sort entity revision_id property in descending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test sort field in ascending order without field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort field in descending order without field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->fieldOrderBy($this->fields[0], 'value', 'asc');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test sort field in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->fieldOrderBy($this->fields[0], 'value', 'desc');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort field in descending order.'), TRUE);
+
+    // Test "in" operation with entity entity_type condition and entity_id
+    // property condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(1, 3, 4), 'IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test "in" operation with entity entity_type condition and entity_id property condition.'));
+
+    // Test "in" operation with entity entity_type condition and entity_id
+    // property condition. Sort in descending order by entity_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(1, 3, 4), 'IN')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 1),
+    ), t('Test "in" operation with entity entity_type condition and entity_id property condition. Sort entity_id in descending order.'), TRUE);
+
+    // Test query count
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 6, t('Test query count on entity condition.'));
+
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', '1')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 1, t('Test query count on entity and property condition.'));
+
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', '4', '>')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 2, t('Test query count on entity and property condition with operator.'));
+
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 3, '=')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 1, t('Test query count on field condition.'));
+
+    // First, test without options.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 1, 'CONTAINS');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+    ), t('Test the "contains" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, 'CONTAINS');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 3),
+    ), t('Test the "contains" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 1, '=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+    ), t('Test the "equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, '=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 3),
+    ), t('Test the "equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 3, '!=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "not equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, '!=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 4),
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 4),
+    ), t('Test the "not equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 2, '<');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+    ), t('Test the "less than" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '<');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity', 1),
+    ), t('Test the "less than" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 2, '<=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test the "less than or equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '<=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity', 1),
+      array('test_entity', 2),
+    ), t('Test the "less than or equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 4, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "greater than" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test the "greater than" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 4, '>=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "greater than or equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, '>=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test the "greater than or equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(3, 4), 'NOT IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "not in" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(3, 4, 100, 101), 'NOT IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity', 1),
+      array('test_entity', 2),
+    ), t('Test the "not in" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(3, 4), 'IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test the "in" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(2, 3), 'IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 2),
+      array('test_entity', 3),
+    ), t('Test the "in" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(1, 3), 'BETWEEN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+    ), t('Test the "between" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(1, 3), 'BETWEEN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+    ), t('Test the "between" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('fttype', 'bun', 'STARTS_WITH');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "starts_with" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test the "starts_with" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 3),
+    ), t('Test omission of an operator with a single item.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(2, 3));
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 2),
+      array('test_entity', 3),
+    ), t('Test omission of an operator with multiple items.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 1, '>')
+      ->fieldCondition($this->fields[0], 'value', 4, '<');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+    ), t('Test entity, property and field conditions.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('bundle', 'bundle', 'STARTS_WITH')
+      ->propertyCondition('ftid', 4)
+      ->fieldCondition($this->fields[0], 'value', 4);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+    ), t('Test entity condition with "starts_with" operation, and property and field conditions.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'asc')
+      ->range(0, 2);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test limit on a property.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>=')
+      ->fieldOrderBy($this->fields[0], 'value', 'asc')
+      ->range(0, 2);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test limit on a field.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'asc')
+      ->range(4, 6);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test offset on a property.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->fieldOrderBy($this->fields[0], 'value', 'asc')
+      ->range(2, 4);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test offset on a field.'), TRUE);
+
+    for ($i = 6; $i < 10; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = $i;
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i - 5;
+      drupal_write_record('test_entity_bundle', $entity);
+      field_attach_insert('test_entity_bundle', $entity);
+    }
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity', 3),
+      array('test_entity', 4),
+      array('test_entity_bundle', 8),
+      array('test_entity_bundle', 9),
+    ), t('Select a field across multiple entities.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[1], 'shape', 'square')
+      ->fieldCondition($this->fields[1], 'color', 'blue');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test without a delta group.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[1], 'shape', 'square', '=', 'group')
+      ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'group');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a delta group.'));
+
+    // Test query on a deleted field.
+    field_attach_delete_bundle('test_entity_bundle_key', 'bundle1');
+    field_attach_delete_bundle('test_entity', 'test_bundle');
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 8),
+    ), t('Test query on a field after deleting field from some entities.'));
+
+    field_attach_delete_bundle('test_entity_bundle', 'test_entity_bundle');
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertEntityFieldQuery($query, array(), t('Test query on a field after deleting field from all entities.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[0], 'value', '3')
+      ->deleted(TRUE);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle', 8),
+      array('test_entity', 3),
+    ), t('Test query on a deleted field with deleted option set to TRUE.'));
+
+    $pass = FALSE;
+    $query = new EntityFieldQuery();
+    try {
+      $query->execute();
+    }
+    catch (EntityFieldQueryException $exception) {
+      $pass = ($exception->getMessage() == t('For this query an entity type must be specified.'));
+    }
+    $this->assertTrue($pass, t("Can't query the universe."));
+  }
+
+  /**
+   * Tests the routing feature of EntityFieldQuery.
+   */
+  function testEntityFieldQueryRouting() {
+    // Entity-only query.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    $this->assertIdentical($query->queryCallback(), array($query, 'propertyQuery'), t('Entity-only queries are handled by the propertyQuery handler.'));
+
+    // Field-only query.
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Pure field queries are handled by the Field storage handler.'));
+
+    // Mixed entity and field query.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Mixed queries are handled by the Field storage handler.'));
+
+    // Overriding with $query->executeCallback.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    $query->executeCallback = 'field_test_dummy_field_storage_query';
+    $this->assertEntityFieldQuery($query, array(
+      array('user', 1),
+    ), t('executeCallback can override the query handler.'));
+
+    // Overriding with $query->executeCallback via hook_entity_query_alter().
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    // Add a flag that will be caught by field_test_entity_query_alter().
+    $query->alterMyExecuteCallbackPlease = TRUE;
+    $this->assertEntityFieldQuery($query, array(
+      array('user', 1),
+    ), t('executeCallback can override the query handler when set in a hook_entity_query_alter().'));
+
+    // Mixed-storage queries.
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[0], 'value', '3')
+      ->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
+    // Alter the storage of the field.
+    $query->fields[1]['storage']['module'] = 'dummy_storage';
+    try {
+      $query->queryCallback();
+    }
+    catch (EntityFieldQueryException $exception) {
+      $pass = ($exception->getMessage() == t("Can't handle more than one field storage engine"));
+    }
+    $this->assertTrue($pass, t('Cannot query across field storage engines.'));
+  }
+
+  /**
+   * Fetches the results of an EntityFieldQuery and compares.
+   *
+   * @param $query
+   *   An EntityFieldQuery to run.
+   * @param $intended_results
+   *   A list of results, every entry is again a list, first being the entity
+   *   type, the second being the entity_id.
+   * @param $message
+   *   The message to be displayed as the result of this test.
+   * @param $ordered
+   *   If FALSE then the result of EntityFieldQuery will match
+   *   $intended_results even if the order is not the same. If TRUE then order
+   *   should match too.
+   */
+  function assertEntityFieldQuery($query, $intended_results, $message, $ordered = FALSE) {
+    $results = array();
+    foreach ($query->execute() as $entity_type => $entity_ids) {
+      foreach ($entity_ids as $entity_id => $stub_entity) {
+        $results[] = array($entity_type, $entity_id);
+      }
+    }
+    if (!isset($ordered) || !$ordered) {
+      sort($results);
+      sort($intended_results);
+    }
+    $this->assertEqual($results, $intended_results, $message);
+  }
+}
diff --git a/modules/system/tests/error.test b/modules/system/tests/error.test
new file mode 100644
index 0000000..b0d1991
--- /dev/null
+++ b/modules/system/tests/error.test
@@ -0,0 +1,120 @@
+<?php
+// $Id: error.test,v 1.6 2009/08/17 19:14:41 webchick Exp $
+
+/**
+ * Tests Drupal error and exception handlers.
+ */
+class DrupalErrorHandlerUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal error handlers',
+      'description' => 'Performs tests on the Drupal error and exception handler.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('error_test');
+  }
+
+  /**
+   * Test the error handler.
+   */
+  function testErrorHandler() {
+    $error_notice = array(
+      '%type' => 'Notice',
+      '%message' => 'Undefined variable: bananas',
+      '%function' => 'error_test_generate_warnings()',
+      '%line' => 44,
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+    );
+    $error_warning = array(
+      '%type' => 'Warning',
+      '%message' => 'Division by zero',
+      '%function' => 'error_test_generate_warnings()',
+      '%line' => 46,
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+    );
+    $error_user_notice = array(
+      '%type' => 'User warning',
+      '%message' => 'Drupal is awesome',
+      '%function' => 'error_test_generate_warnings()',
+      '%line' => 48,
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+    );
+
+    // Set error reporting to collect notices.
+    variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL);
+    $this->drupalGet('error-test/generate-warnings');
+    $this->assertResponse(200, t('Received expected HTTP status code.'));
+    $this->assertErrorMessage($error_notice);
+    $this->assertErrorMessage($error_warning);
+    $this->assertErrorMessage($error_user_notice);
+
+    // Set error reporting to not collect notices.
+    variable_set('error_level', ERROR_REPORTING_DISPLAY_SOME);
+    $this->drupalGet('error-test/generate-warnings');
+    $this->assertResponse(200, t('Received expected HTTP status code.'));
+    $this->assertNoErrorMessage($error_notice);
+    $this->assertErrorMessage($error_warning);
+    $this->assertErrorMessage($error_user_notice);
+
+    // Set error reporting to not show any errors.
+    variable_set('error_level', ERROR_REPORTING_HIDE);
+    $this->drupalGet('error-test/generate-warnings');
+    $this->assertResponse(200, t('Received expected HTTP status code.'));
+    $this->assertNoErrorMessage($error_notice);
+    $this->assertNoErrorMessage($error_warning);
+    $this->assertNoErrorMessage($error_user_notice);
+  }
+
+  /**
+   * Test the exception handler.
+   */
+  function testExceptionHandler() {
+    $error_exception = array(
+      '%type' => 'Exception',
+      '%message' => 'Drupal is awesome',
+      '%function' => 'error_test_trigger_exception()',
+      '%line' => 57,
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+    );
+    $error_pdo_exception = array(
+      '%type' => 'PDOException',
+      '%message' => 'SELECT * FROM bananas_are_awesome',
+      '%function' => 'error_test_trigger_pdo_exception()',
+      '%line' => 65,
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+    );
+
+    $this->drupalGet('error-test/trigger-exception');
+    $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), t('Received expected HTTP status line.'));
+    $this->assertErrorMessage($error_exception);
+
+    $this->drupalGet('error-test/trigger-pdo-exception');
+    $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), t('Received expected HTTP status line.'));
+    // We cannot use assertErrorMessage() since the extact error reported
+    // varies from database to database. Check that the SQL string is displayed.
+    $this->assertText($error_pdo_exception['%type'], t('Found %type in error page.', $error_pdo_exception));
+    $this->assertText($error_pdo_exception['%message'], t('Found %message in error page.', $error_pdo_exception));
+    $error_details = t('in %function (line %line of %file)', $error_pdo_exception);
+    $this->assertRaw($error_details, t("Found '!message' in error page.", array('!message' => $error_details)));
+  }
+
+  /**
+   * Helper function: assert that the error message is found.
+   */
+  function assertErrorMessage(array $error) {
+    $message = t('%type: %message in %function (line %line of %file).', $error);
+    $this->assertRaw($message, t('Error !message found.', array('!message' => $message)));
+  }
+
+  /**
+   * Helper function: assert that the error message is not found.
+   */
+  function assertNoErrorMessage(array $error) {
+    $message = t('%type: %message in %function (line %line of %file).', $error);
+    $this->assertNoRaw($message, t('Error !message not found.', array('!message' => $message)));
+  }
+}
+
diff --git a/modules/system/tests/file.test b/modules/system/tests/file.test
new file mode 100644
index 0000000..e1648b1
--- /dev/null
+++ b/modules/system/tests/file.test
@@ -0,0 +1,2464 @@
+<?php
+// $Id: file.test,v 1.59 2010/07/26 03:04:29 dries Exp $
+
+/**
+ *  @file
+ *  This provides SimpleTests for the core file handling functionality.
+ *  These include FileValidateTest and FileSaveTest.
+ */
+
+/**
+ * Helper validator that returns the $errors parameter.
+ */
+function file_test_validator($file, $errors) {
+  return $errors;
+}
+
+/**
+ * Helper function for testing file_scan_directory().
+ *
+ * Each time the function is called the file is stored in a static variable.
+ * When the function is called with no $filepath parameter, the results are
+ * returned.
+ *
+ * @param $filepath
+ *   File path
+ * @return
+ *   If $filepath is NULL, an array of all previous $filepath parameters
+ */
+function file_test_file_scan_callback($filepath = NULL) {
+  $files = &drupal_static(__FUNCTION__, array());
+  if (isset($filepath)) {
+    $files[] = $filepath;
+  }
+  else {
+    return $files;
+  }
+}
+
+/**
+ * Reset static variables used by file_test_file_scan_callback().
+ */
+function file_test_file_scan_callback_reset() {
+  drupal_static_reset('file_test_file_scan_callback');
+}
+
+/**
+ * Base class for file tests that adds some additional file specific
+ * assertions and helper functions.
+ */
+class FileTestCase extends DrupalWebTestCase {
+  /**
+   * Check that two files have the same values for all fields other than the
+   * timestamp.
+   *
+   * @param $before
+   *   File object to compare.
+   * @param $after
+   *   File object to compare.
+   */
+  function assertFileUnchanged($before, $after) {
+    $this->assertEqual($before->fid, $after->fid, t('File id is the same: %file1 == %file2.', array('%file1' => $before->fid, '%file2' => $after->fid)), 'File unchanged');
+    $this->assertEqual($before->uid, $after->uid, t('File owner is the same: %file1 == %file2.', array('%file1' => $before->uid, '%file2' => $after->uid)), 'File unchanged');
+    $this->assertEqual($before->filename, $after->filename, t('File name is the same: %file1 == %file2.', array('%file1' => $before->filename, '%file2' => $after->filename)), 'File unchanged');
+    $this->assertEqual($before->uri, $after->uri, t('File path is the same: %file1 == %file2.', array('%file1' => $before->uri, '%file2' => $after->uri)), 'File unchanged');
+    $this->assertEqual($before->filemime, $after->filemime, t('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->filemime, '%file2' => $after->filemime)), 'File unchanged');
+    $this->assertEqual($before->filesize, $after->filesize, t('File size is the same: %file1 == %file2.', array('%file1' => $before->filesize, '%file2' => $after->filesize)), 'File unchanged');
+    $this->assertEqual($before->status, $after->status, t('File status is the same: %file1 == %file2.', array('%file1' => $before->status, '%file2' => $after->status)), 'File unchanged');
+  }
+
+  /**
+   * Check that two files are not the same by comparing the fid and filepath.
+   *
+   * @param $file1
+   *   File object to compare.
+   * @param $file2
+   *   File object to compare.
+   */
+  function assertDifferentFile($file1, $file2) {
+    $this->assertNotEqual($file1->fid, $file2->fid, t('Files have different ids: %file1 != %file2.', array('%file1' => $file1->fid, '%file2' => $file2->fid)), 'Different file');
+    $this->assertNotEqual($file1->uri, $file2->uri, t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Different file');
+  }
+
+  /**
+   * Check that two files are the same by comparing the fid and filepath.
+   *
+   * @param $file1
+   *   File object to compare.
+   * @param $file2
+   *   File object to compare.
+   */
+  function assertSameFile($file1, $file2) {
+    $this->assertEqual($file1->fid, $file2->fid, t('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->fid, '%file2-fid' => $file2->fid)), 'Same file');
+    $this->assertEqual($file1->uri, $file2->uri, t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Same file');
+  }
+
+  /**
+   * Helper function to test the permissions of a file.
+   *
+   * @param $filepath
+   *   String file path.
+   * @param $expected_mode
+   *   Octal integer like 0664 or 0777.
+   * @param $message
+   *   Optional message.
+   */
+  function assertFilePermissions($filepath, $expected_mode, $message = NULL) {
+    // Clear out PHP's file stat cache to be sure we see the current value.
+    clearstatcache();
+
+    // Mask out all but the last three octets.
+    $actual_mode = fileperms($filepath) & 511;
+    if (!isset($message)) {
+      $message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
+    }
+    $this->assertEqual($actual_mode, $expected_mode, $message);
+  }
+
+  /**
+   * Helper function to test the permissions of a directory.
+   *
+   * @param $directory
+   *   String directory path.
+   * @param $expected_mode
+   *   Octal integer like 0664 or 0777.
+   * @param $message
+   *   Optional message.
+   */
+  function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) {
+    // Clear out PHP's file stat cache to be sure we see the current value.
+    clearstatcache();
+
+    // Mask out all but the last three octets.
+    $actual_mode = fileperms($directory) & 511;
+    if (!isset($message)) {
+      $message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
+    }
+    $this->assertEqual($actual_mode, $expected_mode, $message);
+  }
+
+  /**
+   * Create a directory and assert it exists.
+   *
+   * @param $path
+   *   Optional string with a directory path. If none is provided, a random
+   *   name in the site's files directory will be used.
+   * @return
+   *   The path to the directory.
+   */
+  function createDirectory($path = NULL) {
+    // A directory to operate on.
+    if (is_null($path)) {
+      $path = file_directory_path() . '/' . $this->randomName();
+    }
+    $this->assertTrue(drupal_mkdir($path) && is_dir($path), t('Directory was created successfully.'));
+    return $path;
+  }
+
+  /**
+   * Create a file and save it to the files table and assert that it occurs
+   * correctly.
+   *
+   * @param $filepath
+   *   Optional string specifying the file path. If none is provided then a
+   *   randomly named file will be created in the site's files directory.
+   * @param $contents
+   *   Optional contents to save into the file. If a NULL value is provided an
+   *   arbitrary string will be used.
+   * @param $scheme
+   *   Optional string indicating the stream scheme to use. Drupal core includes
+   *   public, private, and temporary. The public wrapper is the default.
+   * @return
+   *   File object.
+   */
+  function createFile($filepath = NULL, $contents = NULL, $scheme = 'public') {
+    if (is_null($filepath)) {
+      $filepath = $this->randomName();
+    }
+    $filepath = $scheme . '://' . $filepath;
+
+    if (is_null($contents)) {
+      $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";
+    }
+
+    file_put_contents($filepath, $contents);
+    $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file');
+
+    $file = new stdClass();
+    $file->uri = $filepath;
+    $file->filename = basename($file->uri);
+    $file->filemime = 'text/plain';
+    $file->uid = 1;
+    $file->timestamp = REQUEST_TIME;
+    $file->filesize = filesize($file->uri);
+    $file->status = 0;
+    // Write the record directly rather than calling file_save() so we don't
+    // invoke the hooks.
+    $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, t('The file was added to the database.'), 'Create test file');
+
+    return $file;
+  }
+}
+
+/**
+ * Base class for file tests that use the file_test module to test uploads and
+ * hooks.
+ */
+class FileHookTestCase extends FileTestCase {
+  function setUp() {
+    // Install file_test module
+    parent::setUp('file_test');
+    // Clear out any hook calls.
+    file_test_reset();
+  }
+
+  /**
+   * Assert that all of the specified hook_file_* hooks were called once, other
+   * values result in failure.
+   *
+   * @param $expected
+   *   Array with string containing with the hook name, e.g. 'load', 'save',
+   *   'insert', etc.
+   */
+  function assertFileHooksCalled($expected) {
+    // Determine which hooks were called.
+    $actual = array_keys(array_filter(file_test_get_all_calls()));
+
+    // Determine if there were any expected that were not called.
+    $uncalled = array_diff($expected, $actual);
+    if (count($uncalled)) {
+      $this->assertTrue(FALSE, t('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled))));
+    }
+    else {
+      $this->assertTrue(TRUE, t('All the expected hooks were called: %expected', array('%expected' => implode(', ', $expected))));
+    }
+
+    // Determine if there were any unexpected calls.
+    $unexpected = array_diff($actual, $expected);
+    if (count($unexpected)) {
+      $this->assertTrue(FALSE, t('Unexpected hooks were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected))));
+    }
+    else {
+      $this->assertTrue(TRUE, t('No unexpected hooks were called.'));
+    }
+  }
+
+  /**
+   * Assert that a hook_file_* hook was called a certain number of times.
+   *
+   * @param $hook
+   *   String with the hook name, e.g. 'load', 'save', 'insert', etc.
+   * @param $expected_count
+   *   Optional integer count.
+   * @param $message
+   *   Optional translated string message.
+   */
+  function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) {
+    $actual_count = count(file_test_get_calls($hook));
+
+    if (is_null($message)) {
+      if ($actual_count == $expected_count) {
+        $message = t('hook_file_@name was called correctly.', array('@name' => $hook));
+      }
+      elseif ($expected_count == 0) {
+        $message = format_plural($actual_count, 'hook_file_@name was not expected to be called but was actually called once.', 'hook_file_@name was not expected to be called but was actually called @count times.', array('@name' => $hook, '@count' => $actual_count));
+      }
+      else {
+        $message = t('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count));
+      }
+    }
+    $this->assertEqual($actual_count, $expected_count, $message);
+  }
+}
+
+
+/**
+ *  This will run tests against the file_space_used() function.
+ */
+class FileSpaceUsedTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File space used tests',
+      'description' => 'Tests the file_space_used() function.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    // Create records for a couple of users with different sizes.
+    $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT);
+    drupal_write_record('file_managed', $file);
+    $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT);
+    drupal_write_record('file_managed', $file);
+    $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT);
+    drupal_write_record('file_managed', $file);
+    $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT);
+    drupal_write_record('file_managed', $file);
+
+    // Now create some non-permanent files.
+    $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0);
+    drupal_write_record('file_managed', $file);
+    $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0);
+    drupal_write_record('file_managed', $file);
+  }
+
+  /**
+   * Test different users with the default status.
+   */
+  function testFileSpaceUsed() {
+    // Test different users with default status.
+    $this->assertEqual(file_space_used(2), 70);
+    $this->assertEqual(file_space_used(3), 300);
+    $this->assertEqual(file_space_used(), 370);
+
+    // Test the status fields
+    $this->assertEqual(file_space_used(NULL, 0), 4);
+    $this->assertEqual(file_space_used(NULL, FILE_STATUS_PERMANENT), 370);
+
+    // Test both the user and status.
+    $this->assertEqual(file_space_used(1, 0), 0);
+    $this->assertEqual(file_space_used(1, FILE_STATUS_PERMANENT), 0);
+    $this->assertEqual(file_space_used(2, 0), 1);
+    $this->assertEqual(file_space_used(2, FILE_STATUS_PERMANENT), 70);
+    $this->assertEqual(file_space_used(3, 0), 3);
+    $this->assertEqual(file_space_used(3, FILE_STATUS_PERMANENT), 300);
+  }
+}
+
+/**
+ *  This will run tests against the file validation functions (file_validate_*).
+ */
+class FileValidatorTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File validator tests',
+      'description' => 'Tests the functions used to validate uploaded files.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->image = new stdClass();
+    $this->image->uri = 'misc/druplicon.png';
+    $this->image->filename = basename($this->image->uri);
+
+    $this->non_image = new stdClass();
+    $this->non_image->uri = 'misc/jquery.js';
+    $this->non_image->filename = basename($this->non_image->uri);
+  }
+
+  /**
+   * Test the file_validate_extensions() function.
+   */
+  function testFileValidateExtensions() {
+    $file = new stdClass();
+    $file->filename = 'asdf.txt';
+    $errors = file_validate_extensions($file, 'asdf txt pork');
+    $this->assertEqual(count($errors), 0, t('Valid extension accepted.'), 'File');
+
+    $file->filename = 'asdf.txt';
+    $errors = file_validate_extensions($file, 'exe png');
+    $this->assertEqual(count($errors), 1, t('Invalid extension blocked.'), 'File');
+  }
+
+  /**
+   *  This ensures a specific file is actually an image.
+   */
+  function testFileValidateIsImage() {
+    $this->assertTrue(file_exists($this->image->uri), t('The image being tested exists.'), 'File');
+    $errors = file_validate_is_image($this->image);
+    $this->assertEqual(count($errors), 0, t('No error reported for our image file.'), 'File');
+
+    $this->assertTrue(file_exists($this->non_image->uri), t('The non-image being tested exists.'), 'File');
+    $errors = file_validate_is_image($this->non_image);
+    $this->assertEqual(count($errors), 1, t('An error reported for our non-image file.'), 'File');
+  }
+
+  /**
+   *  This ensures the resolution of a specific file is within bounds.
+   *  The image will be resized if it's too large.
+   */
+  function testFileValidateImageResolution() {
+    // Non-images.
+    $errors = file_validate_image_resolution($this->non_image);
+    $this->assertEqual(count($errors), 0, t("Shouldn't get any errors for a non-image file."), 'File');
+    $errors = file_validate_image_resolution($this->non_image, '50x50', '100x100');
+    $this->assertEqual(count($errors), 0, t("Don't check the resolution on non files."), 'File');
+
+    // Minimum size.
+    $errors = file_validate_image_resolution($this->image);
+    $this->assertEqual(count($errors), 0, t('No errors for an image when there is no minimum or maximum resolution.'), 'File');
+    $errors = file_validate_image_resolution($this->image, 0, '200x1');
+    $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't wide enough."), 'File');
+    $errors = file_validate_image_resolution($this->image, 0, '1x200');
+    $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't tall enough."), 'File');
+    $errors = file_validate_image_resolution($this->image, 0, '200x200');
+    $this->assertEqual(count($errors), 1, t('Small images report an error.'), 'File');
+
+    // Maximum size.
+    if (image_get_toolkit()) {
+      // Copy the image so that the original doesn't get resized.
+      $temp_dir = file_directory_path('temporary');
+      copy(drupal_realpath('misc/druplicon.png'), drupal_realpath($temp_dir) . '/druplicon.png');
+      $this->image->uri = $temp_dir . '/druplicon.png';
+
+      $errors = file_validate_image_resolution($this->image, '10x5');
+      $this->assertEqual(count($errors), 0, t('No errors should be reported when an oversized image can be scaled down.'), 'File');
+
+      $info = image_get_info($this->image->uri);
+      $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File');
+      $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File');
+
+      unlink(drupal_realpath($temp_dir . '/druplicon.png'));
+    }
+    else {
+      // TODO: should check that the error is returned if no toolkit is available.
+      $errors = file_validate_image_resolution($this->image, '5x10');
+      $this->assertEqual(count($errors), 1, t("Oversize images that can't be scaled get an error."), 'File');
+    }
+  }
+
+  /**
+   *  This will ensure the filename length is valid.
+   */
+  function testFileValidateNameLength() {
+    // Create a new file object.
+    $file = new stdClass();
+
+    // Add a filename with an allowed length and test it.
+    $file->filename = str_repeat('x', 240);
+    $this->assertEqual(strlen($file->filename), 240);
+    $errors = file_validate_name_length($file);
+    $this->assertEqual(count($errors), 0, t('No errors reported for 240 length filename.'), 'File');
+
+    // Add a filename with a length too long and test it.
+    $file->filename = str_repeat('x', 241);
+    $errors = file_validate_name_length($file);
+    $this->assertEqual(count($errors), 1, t('An error reported for 241 length filename.'), 'File');
+
+    // Add a filename with an empty string and test it.
+    $file->filename = '';
+    $errors = file_validate_name_length($file);
+    $this->assertEqual(count($errors), 1, t('An error reported for 0 length filename.'), 'File');
+  }
+
+
+  /**
+   * Test file_validate_size().
+   */
+  function testFileValidateSize() {
+    global $user;
+    $original_user = $user;
+    drupal_save_session(FALSE);
+
+    // Run these test as uid = 1.
+    $user = user_load(1);
+
+    $file = new stdClass();
+    $file->filesize = 999999;
+    $errors = file_validate_size($file, 1, 1);
+    $this->assertEqual(count($errors), 0, t('No size limits enforced on uid=1.'), 'File');
+
+    // Run these tests as a regular user.
+    $user = $this->drupalCreateUser();
+
+    // Create a file with a size of 1000 bytes, and quotas of only 1 byte.
+    $file = new stdClass();
+    $file->filesize = 1000;
+    $errors = file_validate_size($file, 0, 0);
+    $this->assertEqual(count($errors), 0, t('No limits means no errors.'), 'File');
+    $errors = file_validate_size($file, 1, 0);
+    $this->assertEqual(count($errors), 1, t('Error for the file being over the limit.'), 'File');
+    $errors = file_validate_size($file, 0, 1);
+    $this->assertEqual(count($errors), 1, t('Error for the user being over their limit.'), 'File');
+    $errors = file_validate_size($file, 1, 1);
+    $this->assertEqual(count($errors), 2, t('Errors for both the file and their limit.'), 'File');
+
+    $user = $original_user;
+    drupal_save_session(TRUE);
+  }
+}
+
+
+
+/**
+ *  Tests the file_unmanaged_save_data() function.
+ */
+class FileUnmanagedSaveDataTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Unmanaged file save data',
+      'description' => 'Tests the unmanaged file save data function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Test the file_unmanaged_save_data() function.
+   */
+  function testFileSaveData() {
+    $contents = $this->randomName(8);
+
+    // No filename.
+    $filepath = file_unmanaged_save_data($contents);
+    $this->assertTrue($filepath, t('Unnamed file saved correctly.'));
+    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($filepath)), t("File was placed in Drupal's files directory."));
+    $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.'));
+
+    // Provide a filename.
+    $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE);
+    $this->assertTrue($filepath, t('Unnamed file saved correctly.'));
+    $this->assertEqual(file_directory_path('public'), file_directory_path(file_uri_scheme($filepath)), t("File was placed in Drupal's files directory."));
+    $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.'));
+    $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.'));
+    $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664));
+  }
+}
+
+/**
+ * Test the file_save_upload() function.
+ */
+class FileSaveUploadTest extends FileHookTestCase {
+  /**
+   * An image file path for uploading.
+   */
+  protected $image;
+
+  /**
+   * A PHP file path for upload security testing.
+   */
+  protected $phpfile;
+
+  /**
+   * The largest file id when the test starts.
+   */
+  protected $maxFidBefore;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'File uploading',
+      'description' => 'Tests the file uploading functions.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    $account = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($account);
+
+    $this->image = current($this->drupalGetTestFiles('image'));
+    list(, $this->image_extension) = explode('.', $this->image->filename);
+    $this->assertTrue(is_file($this->image->uri), t("The image file we're going to upload exists."));
+
+    $this->phpfile = current($this->drupalGetTestFiles('php'));
+    $this->assertTrue(is_file($this->phpfile->uri), t("The PHP file we're going to upload exists."));
+
+    $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+
+    // Upload with replace to guarantee there's something there.
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+    );
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called then clean out the hook
+    // counters.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+    file_test_reset();
+  }
+
+  /**
+   * Test the file_save_upload() function.
+   */
+  function testNormal() {
+    $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+    $this->assertTrue($max_fid_after > $this->maxFidBefore, t('A new file was created.'));
+    $file1 = file_load($max_fid_after);
+    $this->assertTrue($file1, t('Loaded the file.'));
+    // MIME type of the uploaded image may be either image/jpeg or image/png.
+    $this->assertEqual(substr($file1->filemime, 0, 5), 'image', 'A MIME type was set.');
+
+    // Reset the hook counters to get rid of the 'load' we just called.
+    file_test_reset();
+
+    // Upload a second file.
+    $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+    $image2 = current($this->drupalGetTestFiles('image'));
+    $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri));
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('You WIN!'));
+    $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+
+    $file2 = file_load($max_fid_after);
+    $this->assertTrue($file2);
+    // MIME type of the uploaded image may be either image/jpeg or image/png.
+    $this->assertEqual(substr($file2->filemime, 0, 5), 'image', 'A MIME type was set.');
+
+    // Load both files using file_load_multiple().
+    $files = file_load_multiple(array($file1->fid, $file2->fid));
+    $this->assertTrue(isset($files[$file1->fid]), t('File was loaded successfully'));
+    $this->assertTrue(isset($files[$file2->fid]), t('File was loaded successfully'));
+
+    // Upload a third file to a subdirectory.
+    $image3 = current($this->drupalGetTestFiles('image'));
+    $image3_realpath = drupal_realpath($image3->uri);
+    $dir = $this->randomName();
+    $edit = array(
+      'files[file_test_upload]' => $image3_realpath,
+      'file_subdir' => $dir,
+    );
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('You WIN!'));
+    $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(basename($image3_realpath))));
+
+    // Check that file_load_multiple() with no arguments returns FALSE.
+    $this->assertFalse(file_load_multiple(), t('No files were loaded.'));
+  }
+
+  /**
+   * Test extension handling.
+   */
+  function testHandleExtension() {
+    // The file being tested is a .gif which is in the default safe list
+    // of extensions to allow when the extension validator isn't used. This is
+    // implicitly tested at the testNormal() test. Here we tell
+    // file_save_upload() to only allow ".foo".
+    $extensions = 'foo';
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'extensions' => $extensions,
+    );
+
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $message = t('Only files with the following extensions are allowed: ') . '<em class="placeholder">' . $extensions . '</em>';
+    $this->assertRaw($message, t('Can\'t upload a disallowed extension'));
+    $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate'));
+
+    // Reset the hook counters.
+    file_test_reset();
+
+    $extensions = 'foo ' . $this->image_extension;
+    // Now tell file_save_upload() to allow the extension of our test image.
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'extensions' => $extensions,
+    );
+
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload an allowed extension.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'load', 'update'));
+
+    // Reset the hook counters.
+    file_test_reset();
+
+    // Now tell file_save_upload() to allow any extension.
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'allow_all_extensions' => TRUE,
+    );
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload any extension.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'load', 'update'));
+  }
+
+  /**
+   * Test dangerous file handling.
+   */
+  function testHandleDangerousFile() {
+    // Allow the .php extension and make sure it gets renamed to .txt for
+    // safety. Also check to make sure its MIME type was changed.
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => drupal_realpath($this->phpfile->uri),
+      'is_image_file' => FALSE,
+      'extensions' => 'php',
+    );
+
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $message = t('For security reasons, your upload has been renamed to ') . '<em class="placeholder">' . $this->phpfile->filename . '.txt' . '</em>';
+    $this->assertRaw($message, t('Dangerous file was renamed.'));
+    $this->assertRaw(t('File MIME type is text/plain.'), t('Dangerous file\'s MIME type was changed.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+
+    // Ensure dangerous files are not renamed when insecure uploads is TRUE.
+    // Turn on insecure uploads.
+    variable_set('allow_insecure_uploads', 1);
+    // Reset the hook counters.
+    file_test_reset();
+
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.'));
+    $this->assertRaw(t('File name is !filename', array('!filename' => $this->phpfile->filename)), t('Dangerous file was not renamed when insecure uploads is TRUE.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+
+    // Turn off insecure uploads.
+    variable_set('allow_insecure_uploads', 0);
+  }
+
+  /**
+   * Test file munge handling.
+   */
+  function testHandleFileMunge() {
+    // Ensure insecure uploads are disabled for this test.
+    variable_set('allow_insecure_uploads', 0);
+    $this->image = file_move($this->image, $this->image->uri . '.foo.' . $this->image_extension);
+
+    // Reset the hook counters to get rid of the 'move' we just called.
+    file_test_reset();
+
+    $extensions = $this->image_extension;
+    $edit = array(
+      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'extensions' => $extensions,
+    );
+
+    $munged_filename = $this->image->filename;
+    $munged_filename = substr($munged_filename, 0, strrpos($munged_filename, '.'));
+    $munged_filename .= '_.' . $this->image_extension;
+
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('For security reasons, your upload has been renamed'), t('Found security message.'));
+    $this->assertRaw(t('File name is !filename', array('!filename' => $munged_filename)), t('File was successfully munged.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+
+    // Ensure we don't munge files if we're allowing any extension.
+    // Reset the hook counters.
+    file_test_reset();
+
+    $edit = array(
+      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'allow_all_extensions' => TRUE,
+    );
+
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.'));
+    $this->assertRaw(t('File name is !filename', array('!filename' => $this->image->filename)), t('File was not munged when allowing any extension.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+  }
+
+  /**
+   * Test renaming when uploading over a file that already exists.
+   */
+  function testExistingRename() {
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_RENAME,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri)
+    );
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'insert'));
+  }
+
+  /**
+   * Test replacement when uploading over a file that already exists.
+   */
+  function testExistingReplace() {
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri)
+    );
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('You WIN!'), t('Found the success message.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('validate', 'load', 'update'));
+  }
+
+  /**
+   * Test for failure when uploading over a file that already exists.
+   */
+  function testExistingError() {
+    $edit = array(
+      'file_test_replace' => FILE_EXISTS_ERROR,
+      'files[file_test_upload]' => drupal_realpath($this->image->uri)
+    );
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t('Received a 200 response for posted test file.'));
+    $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.'));
+
+    // Check that the no hooks were called while failing.
+    $this->assertFileHooksCalled(array());
+  }
+
+  /**
+   * Test for no failures when not uploading a file.
+   */
+  function testNoUpload() {
+    $this->drupalPost('file-test/upload', array(), t('Submit'));
+    $this->assertNoRaw(t('Epic upload FAIL!'), t('Failure message not found.'));
+  }
+}
+
+/**
+ * Directory related tests.
+ */
+class FileDirectoryTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File paths and directories',
+      'description' => 'Tests operations dealing with directories.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Test the file_directory_path() function.
+   */
+  function testFileCheckDirectory() {
+    // A directory to operate on.
+    $directory = file_directory_path() . '/' . $this->randomName() . '/' . $this->randomName();
+    $this->assertFalse(is_dir($directory), t('Directory does not exist prior to testing.'));
+
+    // Non-existent directory.
+    $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for non-existing directory.'), 'File');
+
+    // Make a directory.
+    $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), t('No error reported when creating a new directory.'), 'File');
+
+    // Make sure directory actually exists.
+    $this->assertTrue(is_dir($directory), t('Directory actually exists.'), 'File');
+
+    // Make directory read only.
+    @chmod($directory, 0444);
+    $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File');
+
+    // Test directory permission modification.
+    $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File');
+
+    // Test directory permission modification actually set correct permissions.
+    $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775));
+
+    // Remove .htaccess file to then test that it gets re-created.
+    $directory = file_directory_path();
+    @unlink($directory . '/.htaccess');
+    $this->assertFalse(is_file($directory . '/.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File');
+    file_ensure_htaccess();
+    $this->assertTrue(is_file($directory . '/.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File');
+    // Verify contents of .htaccess file.
+    $file = file_get_contents(file_directory_path() . '/.htaccess');
+    $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", t('The .htaccess file contains the proper content.'), 'File');
+  }
+
+  /**
+   * Check file_directory_path('public').
+   */
+  function testFileDirectoryPath() {
+    // Directory path.
+    $path = variable_get('file_public_path', '');
+    $this->assertEqual($path, file_directory_path('public'), t('Properly returns the stored file directory path.'), 'File');
+  }
+
+  /**
+   * Check file_directory_path() and file_directory_path('temporary').
+   */
+  function testFileDirectoryTemp() {
+    // Temporary directory handling.
+    variable_set('file_directory_temp', NULL);
+    $temp = file_directory_temp();
+    $this->assertTrue(!is_null($temp), t('Properly set and retrieved temp directory %directory.', array('%directory' => $temp)), 'File');
+  }
+
+  /**
+   * This will take a directory and path, and find a valid filepath that is not
+   * taken by another file.
+   */
+  function testFileCreateNewFilepath() {
+    // First we test against an imaginary file that does not exist in a
+    // directory.
+    $basename = 'xyz.txt';
+    $directory = 'misc';
+    $original = $directory . '/' . $basename;
+    $path = file_create_filename($basename, $directory);
+    $this->assertEqual($path, $original, t('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File');
+
+    // Then we test against a file that already exists within that directory.
+    $basename = 'druplicon.png';
+    $original = $directory . '/' . $basename;
+    $expected = $directory . '/druplicon_0.png';
+    $path = file_create_filename($basename, $directory);
+    $this->assertEqual($path, $expected, t('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File');
+
+    // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
+  }
+
+  /**
+   * This will test the filepath for a destination based on passed flags and
+   * whether or not the file exists.
+   *
+   * If a file exists, file_destination($destination, $replace) will either
+   * return:
+   * - the existing filepath, if $replace is FILE_EXISTS_REPLACE
+   * - a new filepath if FILE_EXISTS_RENAME
+   * - an error (returning FALSE) if FILE_EXISTS_ERROR.
+   * If the file doesn't currently exist, then it will simply return the
+   * filepath.
+   */
+  function testFileDestination() {
+    // First test for non-existent file.
+    $destination = 'misc/xyz.txt';
+    $path = file_destination($destination, FILE_EXISTS_REPLACE);
+    $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.'), 'File');
+    $path = file_destination($destination, FILE_EXISTS_RENAME);
+    $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_RENAME.'), 'File');
+    $path = file_destination($destination, FILE_EXISTS_ERROR);
+    $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_ERROR.'), 'File');
+
+    $destination = 'misc/druplicon.png';
+    $path = file_destination($destination, FILE_EXISTS_REPLACE);
+    $this->assertEqual($path, $destination, t('Existing filepath destination remains the same with FILE_EXISTS_REPLACE.'), 'File');
+    $path = file_destination($destination, FILE_EXISTS_RENAME);
+    $this->assertNotEqual($path, $destination, t('A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.'), 'File');
+    $path = file_destination($destination, FILE_EXISTS_ERROR);
+    $this->assertEqual($path, FALSE, t('An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.'), 'File');
+  }
+}
+
+
+/**
+ * Tests the file_scan_directory() function.
+ */
+class FileScanDirectoryTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File scan directory',
+      'description' => 'Tests the file_scan_directory() function.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    $this->path = drupal_get_path('module', 'simpletest') . '/files';
+  }
+
+  /**
+   * Check the format of the returned values.
+   */
+  function testReturn() {
+    // Grab a listing of all the JavaSscript files and check that they're
+    // passed to the callback.
+    $all_files = file_scan_directory($this->path, '/^javascript-/');
+    ksort($all_files);
+    $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
+
+    // Check the first file.
+    $file = reset($all_files);
+    $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the first returned file.'));
+    $this->assertEqual($file->uri, $this->path . '/javascript-1.txt', t('First file name was set correctly.'));
+    $this->assertEqual($file->filename, 'javascript-1.txt', t('First basename was set correctly'));
+    $this->assertEqual($file->name, 'javascript-1', t('First name was set correctly.'));
+
+    // Check the second file.
+    $file = next($all_files);
+    $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the second returned file.'));
+    $this->assertEqual($file->uri, $this->path . '/javascript-2.script', t('Second file name was set correctly.'));
+    $this->assertEqual($file->filename, 'javascript-2.script', t('Second basename was set correctly'));
+    $this->assertEqual($file->name, 'javascript-2', t('Second name was set correctly.'));
+  }
+
+  /**
+   * Check that the callback function is called correctly.
+   */
+  function testOptionCallback() {
+    // When nothing is matched nothing should be passed to the callback.
+    $all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback'));
+    $this->assertEqual(0, count($all_files), t('No files were found.'));
+    $results = file_test_file_scan_callback();
+    file_test_file_scan_callback_reset();
+    $this->assertEqual(0, count($results), t('No files were passed to the callback.'));
+
+    // Grab a listing of all the JavaSscript files and check that they're
+    // passed to the callback.
+    $all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback'));
+    $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
+    $results = file_test_file_scan_callback();
+    file_test_file_scan_callback_reset();
+    $this->assertEqual(2, count($results), t('Files were passed to the callback.'));
+  }
+
+  /**
+   * Check that the no-mask parameter is honored.
+   */
+  function testOptionNoMask() {
+    // Grab a listing of all the JavaSscript files.
+    $all_files = file_scan_directory($this->path, '/^javascript-/');
+    $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
+
+    // Now use the nomast parameter to filter out the .script file.
+    $filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/'));
+    $this->assertEqual(1, count($filtered_files), t('Filtered correctly.'));
+  }
+
+  /**
+   * Check that key parameter sets the return value's key.
+   */
+  function testOptionKey() {
+    // "filename", for the path starting with $dir.
+    $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
+    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath')));
+    sort($actual);
+    $this->assertEqual($expected, $actual, t('Returned the correct values for the filename key.'));
+
+    // "basename", for the basename of the file.
+    $expected = array('javascript-1.txt', 'javascript-2.script');
+    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename')));
+    sort($actual);
+    $this->assertEqual($expected, $actual, t('Returned the correct values for the basename key.'));
+
+    // "name" for the name of the file without an extension.
+    $expected = array('javascript-1', 'javascript-2');
+    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name')));
+    sort($actual);
+    $this->assertEqual($expected, $actual, t('Returned the correct values for the name key.'));
+
+    // Invalid option that should default back to "filename".
+    $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
+    $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID')));
+    sort($actual);
+    $this->assertEqual($expected, $actual, t('An invalid key defaulted back to the default.'));
+  }
+
+  /**
+   * Check that the recurse option decends into subdirectories.
+   */
+  function testOptionRecurse() {
+    $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => FALSE));
+    $this->assertTrue(empty($files), t("Without recursion couldn't find javascript files."));
+
+    $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => TRUE));
+    $this->assertEqual(2, count($files), t('With recursion we found the expected javascript files.'));
+  }
+
+
+  /**
+   * Check that the min_depth options lets us ignore files in the starting
+   * directory.
+   */
+  function testOptionMinDepth() {
+    $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0));
+    $this->assertEqual(2, count($files), t('No minimum-depth gets files in current directory.'));
+
+    $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1));
+    $this->assertTrue(empty($files), t("Minimum-depth of 1 successfully excludes files from current directory."));
+  }
+}
+
+
+/**
+ * Deletion related tests.
+ */
+class FileUnmanagedDeleteTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Unmanaged file delete',
+      'description' => 'Tests the unmanaged file delete function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Delete a normal file.
+   */
+  function testNormal() {
+    // Create a file for testing
+    $file = $this->createFile();
+
+    // Delete a regular file
+    $this->assertTrue(file_unmanaged_delete($file->uri), t('Deleted worked.'));
+    $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.'));
+  }
+
+  /**
+   * Try deleting a missing file.
+   */
+  function testMissing() {
+    // Try to delete a non-existing file
+    $this->assertTrue(file_unmanaged_delete(file_directory_path() . '/' . $this->randomName()), t('Returns true when deleting a non-existent file.'));
+  }
+
+  /**
+   * Try deleting a directory.
+   */
+  function testDirectory() {
+    // A directory to operate on.
+    $directory = $this->createDirectory();
+
+    // Try to delete a directory
+    $this->assertFalse(file_unmanaged_delete($directory), t('Could not delete the delete directory.'));
+    $this->assertTrue(file_exists($directory), t('Directory has not been deleted.'));
+  }
+}
+
+
+/**
+ * Deletion related tests.
+ */
+class FileUnmanagedDeleteRecursiveTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Unmanaged recursive file delete',
+      'description' => 'Tests the unmanaged file delete recursive function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Delete a normal file.
+   */
+  function testSingleFile() {
+    // Create a file for testing
+    $filepath = file_directory_path() . '/' . $this->randomName();
+    file_put_contents($filepath, '');
+
+    // Delete the file.
+    $this->assertTrue(file_unmanaged_delete_recursive($filepath), t('Function reported success.'));
+    $this->assertFalse(file_exists($filepath), t('Test file has been deleted.'));
+  }
+
+  /**
+   * Try deleting an empty directory.
+   */
+  function testEmptyDirectory() {
+    // A directory to operate on.
+    $directory = $this->createDirectory();
+
+    // Delete the directory.
+    $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.'));
+    $this->assertFalse(file_exists($directory), t('Directory has been deleted.'));
+  }
+
+  /**
+   * Try deleting a directory with some files.
+   */
+  function testDirectory() {
+    // A directory to operate on.
+    $directory = $this->createDirectory();
+    $filepathA = $directory . '/A';
+    $filepathB = $directory . '/B';
+    file_put_contents($filepathA, '');
+    file_put_contents($filepathB, '');
+
+    // Delete the directory.
+    $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.'));
+    $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.'));
+    $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.'));
+    $this->assertFalse(file_exists($directory), t('Directory has been deleted.'));
+  }
+
+  /**
+   * Try deleting subdirectories with some files.
+   */
+  function testSubDirectory() {
+    // A directory to operate on.
+    $directory = $this->createDirectory();
+    $subdirectory = $this->createDirectory($directory . '/sub');
+    $filepathA = $directory . '/A';
+    $filepathB = $subdirectory . '/B';
+    file_put_contents($filepathA, '');
+    file_put_contents($filepathB, '');
+
+    // Delete the directory.
+    $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.'));
+    $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.'));
+    $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.'));
+    $this->assertFalse(file_exists($subdirectory), t('Subdirectory has been deleted.'));
+    $this->assertFalse(file_exists($directory), t('Directory has been deleted.'));
+  }
+}
+
+
+/**
+ * Unmanaged move related tests.
+ */
+class FileUnmanagedMoveTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Unmanaged file moving',
+      'description' => 'Tests the unmanaged file move function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Move a normal file.
+   */
+  function testNormal() {
+    // Create a file for testing
+    $file = $this->createFile();
+
+    // Moving to a new name.
+    $desired_filepath = 'public://' . $this->randomName();
+    $new_filepath = file_unmanaged_move($file->uri, $desired_filepath, FILE_EXISTS_ERROR);
+    $this->assertTrue($new_filepath, t('Move was successful.'));
+    $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.'));
+    $this->assertTrue(file_exists($new_filepath), t('File exists at the new location.'));
+    $this->assertFalse(file_exists($file->uri), t('No file remains at the old location.'));
+    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
+
+    // Moving with rename.
+    $desired_filepath = 'public://' . $this->randomName();
+    $this->assertTrue(file_exists($new_filepath), t('File exists before moving.'));
+    $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.'));
+    $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
+    $this->assertTrue($newer_filepath, t('Move was successful.'));
+    $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.'));
+    $this->assertTrue(file_exists($newer_filepath), t('File exists at the new location.'));
+    $this->assertFalse(file_exists($new_filepath), t('No file remains at the old location.'));
+    $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664));
+
+    // TODO: test moving to a directory (rather than full directory/file path)
+    // TODO: test creating and moving normal files (rather than streams)
+  }
+
+  /**
+   * Try to move a missing file.
+   */
+  function testMissing() {
+    // Move non-existent file.
+    $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName());
+    $this->assertFalse($new_filepath, t('Moving a missing file fails.'));
+  }
+
+  /**
+   * Try to move a file onto itself.
+   */
+  function testOverwriteSelf() {
+    // Create a file for testing.
+    $file = $this->createFile();
+
+    // Move the file onto itself without renaming shouldn't make changes.
+    $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_REPLACE);
+    $this->assertFalse($new_filepath, t('Moving onto itself without renaming fails.'));
+    $this->assertTrue(file_exists($file->uri), t('File exists after moving onto itself.'));
+
+    // Move the file onto itself with renaming will result in a new filename.
+    $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_RENAME);
+    $this->assertTrue($new_filepath, t('Moving onto itself with renaming works.'));
+    $this->assertFalse(file_exists($file->uri), t('Original file has been removed.'));
+    $this->assertTrue(file_exists($new_filepath), t('File exists after moving onto itself.'));
+  }
+}
+
+
+/**
+ * Unmanaged copy related tests.
+ */
+class FileUnmanagedCopyTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Unmanaged file copying',
+      'description' => 'Tests the unmanaged file copy function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Copy a normal file.
+   */
+  function testNormal() {
+    // Create a file for testing
+    $file = $this->createFile();
+
+    // Copying to a new name.
+    $desired_filepath = 'public://' . $this->randomName();
+    $new_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_ERROR);
+    $this->assertTrue($new_filepath, t('Copy was successful.'));
+    $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.'));
+    $this->assertTrue(file_exists($file->uri), t('Original file remains.'));
+    $this->assertTrue(file_exists($new_filepath), t('New file exists.'));
+    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
+
+    // Copying with rename.
+    $desired_filepath = 'public://' . $this->randomName();
+    $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.'));
+    $newer_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_RENAME);
+    $this->assertTrue($newer_filepath, t('Copy was successful.'));
+    $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.'));
+    $this->assertTrue(file_exists($file->uri), t('Original file remains.'));
+    $this->assertTrue(file_exists($newer_filepath), t('New file exists.'));
+    $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664));
+
+    // TODO: test copying to a directory (rather than full directory/file path)
+    // TODO: test copying normal files using normal paths (rather than only streams)
+  }
+
+  /**
+   * Copy a non-existent file.
+   */
+  function testNonExistent() {
+    // Copy non-existent file
+    $desired_filepath = $this->randomName();
+    $this->assertFalse(file_exists($desired_filepath), t("Randomly named file doesn't exists."));
+    $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomName());
+    $this->assertFalse($new_filepath, t('Copying a missing file fails.'));
+  }
+
+  /**
+   * Copy a file onto itself.
+   */
+  function testOverwriteSelf() {
+    // Create a file for testing
+    $file = $this->createFile();
+
+    // Copy the file onto itself with renaming works.
+    $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_RENAME);
+    $this->assertTrue($new_filepath, t('Copying onto itself with renaming works.'));
+    $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.'));
+    $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.'));
+    $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.'));
+    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
+
+    // Copy the file onto itself without renaming fails.
+    $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_ERROR);
+    $this->assertFalse($new_filepath, t('Copying onto itself without renaming fails.'));
+    $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.'));
+
+    // Copy the file into same directory without renaming fails.
+    $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_ERROR);
+    $this->assertFalse($new_filepath, t('Copying onto itself fails.'));
+    $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.'));
+
+    // Copy the file into same directory with renaming works.
+    $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_RENAME);
+    $this->assertTrue($new_filepath, t('Copying into same directory works.'));
+    $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.'));
+    $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.'));
+    $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.'));
+    $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
+  }
+}
+
+/**
+ * Deletion related tests.
+ */
+class FileDeleteTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File delete',
+      'description' => 'Tests the file delete function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Try deleting a normal file (as opposed to a directory, symlink, etc).
+   */
+  function testNormal() {
+    $file = $this->createFile();
+
+    // Check that deletion removes the file and database record.
+    $this->assertTrue(is_file($file->uri), t("File exists."));
+    $this->assertIdentical(file_delete($file), TRUE, t("Delete worked."));
+    $this->assertFileHooksCalled(array('references', 'delete'));
+    $this->assertFalse(file_exists($file->uri), t("Test file has actually been deleted."));
+    $this->assertFalse(file_load($file->fid), t('File was removed from the database.'));
+
+    // TODO: implement hook_file_references() in file_test.module and report a
+    // file in use and test the $force parameter.
+  }
+}
+
+
+/**
+ * Move related tests
+ */
+class FileMoveTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File moving',
+      'description' => 'Tests the file move function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Move a normal file.
+   */
+  function testNormal() {
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+    $desired_filepath = 'public://' . $this->randomName();
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_move(clone $source, $desired_filepath, FILE_EXISTS_ERROR);
+
+    // Check the return status and that the contents changed.
+    $this->assertTrue($result, t('File moved sucessfully.'));
+    $this->assertFalse(file_exists($source->uri));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('move', 'update'));
+
+    // Make sure we got the same file back.
+    $this->assertEqual($source->fid, $result->fid, t("Source file id's' %fid is unchanged after move.", array('%fid' => $source->fid)));
+
+    // Reload the file from the database and check that the changes were
+    // actually saved.
+    $loaded_file = file_load($result->fid, TRUE);
+    $this->assertTrue($loaded_file, t('File can be loaded from the database.'));
+    $this->assertFileUnchanged($result, $loaded_file);
+  }
+
+  /**
+   * Test renaming when moving onto a file that already exists.
+   */
+  function testExistingRename() {
+    // Setup a file to overwrite.
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+    $target = $this->createFile();
+    $this->assertDifferentFile($source, $target);
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_move(clone $source, $target->uri, FILE_EXISTS_RENAME);
+
+    // Check the return status and that the contents changed.
+    $this->assertTrue($result, t('File moved sucessfully.'));
+    $this->assertFalse(file_exists($source->uri));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('move', 'update'));
+
+    // Compare the returned value to what made it into the database.
+    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
+    // The target file should not have been altered.
+    $this->assertFileUnchanged($target, file_load($target->fid, TRUE));
+    // Make sure we end up with two distinct files afterwards.
+    $this->assertDifferentFile($target, $result);
+
+    // Compare the source and results.
+    $loaded_source = file_load($source->fid, TRUE);
+    $this->assertEqual($loaded_source->fid, $result->fid, t("Returned file's id matches the source."));
+    $this->assertNotEqual($loaded_source->uri, $source->uri, t("Returned file path has changed from the original."));
+  }
+
+  /**
+   * Test replacement when moving onto a file that already exists.
+   */
+  function testExistingReplace() {
+    // Setup a file to overwrite.
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+    $target = $this->createFile();
+    $this->assertDifferentFile($source, $target);
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_move(clone $source, $target->uri, FILE_EXISTS_REPLACE);
+
+    // Look at the results.
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.'));
+    $this->assertFalse(file_exists($source->uri));
+    $this->assertTrue($result, t('File moved sucessfully.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('move', 'update', 'delete', 'references', 'load'));
+
+    // Reload the file from the database and check that the changes were
+    // actually saved.
+    $loaded_result = file_load($result->fid, TRUE);
+    $this->assertFileUnchanged($result, $loaded_result);
+    // Check that target was re-used.
+    $this->assertSameFile($target, $loaded_result);
+    // Source and result should be totally different.
+    $this->assertDifferentFile($source, $loaded_result);
+  }
+
+  /**
+   * Test replacement when moving onto itself.
+   */
+  function testExistingReplaceSelf() {
+    // Setup a file to overwrite.
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+
+    // Copy the file over itself. Clone the object so we don't have to worry
+    // about the function changing our reference copy.
+    $result = file_move(clone $source, $source->uri, FILE_EXISTS_REPLACE);
+    $this->assertFalse($result, t('File move failed.'));
+    $this->assertEqual($contents, file_get_contents($source->uri), t('Contents of file were not altered.'));
+
+    // Check that no hooks were called while failing.
+    $this->assertFileHooksCalled(array());
+
+    // Load the file from the database and make sure it is identical to what
+    // was returned.
+    $this->assertFileUnchanged($source, file_load($source->fid, TRUE));
+  }
+
+  /**
+   * Test that moving onto an existing file fails when FILE_EXISTS_ERROR is
+   * specified.
+   */
+  function testExistingError() {
+    $contents = $this->randomName(10);
+    $source = $this->createFile();
+    $target = $this->createFile(NULL, $contents);
+    $this->assertDifferentFile($source, $target);
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_move(clone $source, $target->uri, FILE_EXISTS_ERROR);
+
+    // Check the return status and that the contents did not change.
+    $this->assertFalse($result, t('File move failed.'));
+    $this->assertTrue(file_exists($source->uri));
+    $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.'));
+
+    // Check that no hooks were called while failing.
+    $this->assertFileHooksCalled(array());
+
+    // Load the file from the database and make sure it is identical to what
+    // was returned.
+    $this->assertFileUnchanged($source, file_load($source->fid, TRUE));
+    $this->assertFileUnchanged($target, file_load($target->fid, TRUE));
+  }
+}
+
+
+/**
+ * Copy related tests.
+ */
+class FileCopyTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File copying',
+      'description' => 'Tests the file copy function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Test file copying in the normal, base case.
+   */
+  function testNormal() {
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+    $desired_uri = 'public://' . $this->randomName();
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_copy(clone $source, $desired_uri, FILE_EXISTS_ERROR);
+
+    // Check the return status and that the contents changed.
+    $this->assertTrue($result, t('File copied sucessfully.'));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('copy', 'insert'));
+
+    $this->assertDifferentFile($source, $result);
+    $this->assertEqual($result->uri, $desired_uri, t('The copied file object has the desired filepath.'));
+    $this->assertTrue(file_exists($source->uri), t('The original file still exists.'));
+    $this->assertTrue(file_exists($result->uri), t('The copied file exists.'));
+
+    // Reload the file from the database and check that the changes were
+    // actually saved.
+    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
+  }
+
+  /**
+   * Test renaming when copying over a file that already exists.
+   */
+  function testExistingRename() {
+    // Setup a file to overwrite.
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+    $target = $this->createFile();
+    $this->assertDifferentFile($source, $target);
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_copy(clone $source, $target->uri, FILE_EXISTS_RENAME);
+
+    // Check the return status and that the contents changed.
+    $this->assertTrue($result, t('File copied sucessfully.'));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.'));
+    $this->assertNotEqual($result->uri, $source->uri, t('Returned file path has changed from the original.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('copy', 'insert'));
+
+    // Load all the affected files to check the changes that actually made it
+    // to the database.
+    $loaded_source = file_load($source->fid, TRUE);
+    $loaded_target = file_load($target->fid, TRUE);
+    $loaded_result = file_load($result->fid, TRUE);
+
+    // Verify that the source file wasn't changed.
+    $this->assertFileUnchanged($source, $loaded_source);
+
+    // Verify that what was returned is what's in the database.
+    $this->assertFileUnchanged($result, $loaded_result);
+
+    // Make sure we end up with three distinct files afterwards.
+    $this->assertDifferentFile($loaded_source, $loaded_target);
+    $this->assertDifferentFile($loaded_target, $loaded_result);
+    $this->assertDifferentFile($loaded_source, $loaded_result);
+  }
+
+  /**
+   * Test replacement when copying over a file that already exists.
+   */
+  function testExistingReplace() {
+    // Setup a file to overwrite.
+    $contents = $this->randomName(10);
+    $source = $this->createFile(NULL, $contents);
+    $target = $this->createFile();
+    $this->assertDifferentFile($source, $target);
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_copy(clone $source, $target->uri, FILE_EXISTS_REPLACE);
+
+    // Check the return status and that the contents changed.
+    $this->assertTrue($result, t('File copied sucessfully.'));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.'));
+    $this->assertDifferentFile($source, $result);
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('load', 'copy', 'update'));
+
+    // Load all the affected files to check the changes that actually made it
+    // to the database.
+    $loaded_source = file_load($source->fid, TRUE);
+    $loaded_target = file_load($target->fid, TRUE);
+    $loaded_result = file_load($result->fid, TRUE);
+
+    // Verify that the source file wasn't changed.
+    $this->assertFileUnchanged($source, $loaded_source);
+
+    // Verify that what was returned is what's in the database.
+    $this->assertFileUnchanged($result, $loaded_result);
+
+    // Target file was reused for the result.
+    $this->assertFileUnchanged($loaded_target, $loaded_result);
+  }
+
+  /**
+   * Test that copying over an existing file fails when FILE_EXISTS_ERROR is
+   * specified.
+   */
+  function testExistingError() {
+    $contents = $this->randomName(10);
+    $source = $this->createFile();
+    $target = $this->createFile(NULL, $contents);
+    $this->assertDifferentFile($source, $target);
+
+    // Clone the object so we don't have to worry about the function changing
+    // our reference copy.
+    $result = file_copy(clone $source, $target->uri, FILE_EXISTS_ERROR);
+
+    // Check the return status and that the contents were not changed.
+    $this->assertFalse($result, t('File copy failed.'));
+    $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.'));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array());
+
+    $this->assertFileUnchanged($source, file_load($source->fid, TRUE));
+    $this->assertFileUnchanged($target, file_load($target->fid, TRUE));
+  }
+}
+
+
+/**
+ * Tests the file_load() function.
+ */
+class FileLoadTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File loading',
+      'description' => 'Tests the file_load() function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Try to load a non-existent file by fid.
+   */
+  function testLoadMissingFid() {
+    $this->assertFalse(file_load(-1), t("Try to load an invalid fid fails."));
+    $this->assertFileHooksCalled(array());
+  }
+
+  /**
+   * Try to load a non-existent file by URI.
+   */
+  function testLoadMissingFilepath() {
+    $files = file_load_multiple(array(), array('uri' => 'foobar://misc/druplicon.png'));
+    $this->assertFalse(reset($files), t("Try to load a file that doesn't exist in the database fails."));
+    $this->assertFileHooksCalled(array());
+  }
+
+  /**
+   * Try to load a non-existent file by status.
+   */
+  function testLoadInvalidStatus() {
+    $files = file_load_multiple(array(), array('status' => -99));
+    $this->assertFalse(reset($files), t("Trying to load a file with an invalid status fails."));
+    $this->assertFileHooksCalled(array());
+  }
+
+  /**
+   * Load a single file and ensure that the correct values are returned.
+   */
+  function testSingleValues() {
+    // Create a new file object from scratch so we know the values.
+    $file = $this->createFile('druplicon.txt', NULL, 'public');
+
+    $by_fid_file = file_load($file->fid);
+    $this->assertFileHookCalled('load');
+    $this->assertTrue(is_object($by_fid_file), t('file_load() returned an object.'));
+    $this->assertEqual($by_fid_file->fid, $file->fid, t("Loading by fid got the same fid."), 'File');
+    $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File');
+    $this->assertEqual($by_fid_file->filename, $file->filename, t("Loading by fid got the correct filename."), 'File');
+    $this->assertEqual($by_fid_file->filemime, $file->filemime, t("Loading by fid got the correct MIME type."), 'File');
+    $this->assertEqual($by_fid_file->status, $file->status, t("Loading by fid got the correct status."), 'File');
+    $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
+  }
+
+  /**
+   * This will test loading file data from the database.
+   */
+  function testMultiple() {
+    // Create a new file object.
+    $file = $this->createFile('druplicon.txt', NULL, 'public');
+
+    // Load by path.
+    file_test_reset();
+    $by_path_files = file_load_multiple(array(), array('uri' => $file->uri));
+    $this->assertFileHookCalled('load');
+    $this->assertEqual(1, count($by_path_files), t('file_load_multiple() returned an array of the correct size.'));
+    $by_path_file = reset($by_path_files);
+    $this->assertTrue($by_path_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
+    $this->assertEqual($by_path_file->fid, $file->fid, t("Loading by filepath got the correct fid."), 'File');
+
+    // Load by fid.
+    file_test_reset();
+    $by_fid_files = file_load_multiple(array($file->fid), array());
+    $this->assertFileHookCalled('load');
+    $this->assertEqual(1, count($by_fid_files), t('file_load_multiple() returned an array of the correct size.'));
+    $by_fid_file = reset($by_fid_files);
+    $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
+    $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File');
+  }
+}
+
+/**
+ * Tests the file_save() function.
+ */
+class FileSaveTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File saving',
+      'description' => 'Tests the file_save() function.',
+      'group' => 'File API',
+    );
+  }
+
+  function testFileSave() {
+    // Create a new file object.
+    $file = array(
+      'uid' => 1,
+      'filename' => 'druplicon.txt',
+      'uri' => 'public://druplicon.txt',
+      'filemime' => 'text/plain',
+      'timestamp' => 1,
+      'status' => FILE_STATUS_PERMANENT,
+    );
+    $file = (object) $file;
+    file_put_contents($file->uri, 'hello world');
+
+    // Save it, inserting a new record.
+    $saved_file = file_save($file);
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('insert'));
+
+    $this->assertNotNull($saved_file, t("Saving the file should give us back a file object."), 'File');
+    $this->assertTrue($saved_file->fid > 0, t("A new file ID is set when saving a new file to the database."), 'File');
+    $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ);
+    $this->assertNotNull($loaded_file, t("Record exists in the database."));
+    $this->assertEqual($loaded_file->status, $file->status, t("Status was saved correctly."));
+    $this->assertEqual($saved_file->filesize, filesize($file->uri), t("File size was set correctly."), 'File');
+    $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File');
+
+
+    // Resave the file, updating the existing record.
+    file_test_reset();
+    $saved_file->status = 7;
+    $resaved_file = file_save($saved_file);
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('update'));
+
+    $this->assertEqual($resaved_file->fid, $saved_file->fid, t("The file ID of an existing file is not changed when updating the database."), 'File');
+    $this->assertTrue($resaved_file->timestamp >= $saved_file->timestamp, t("Timestamp didn't go backwards."), 'File');
+    $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ);
+    $this->assertNotNull($loaded_file, t("Record still exists in the database."), 'File');
+    $this->assertEqual($loaded_file->status, $saved_file->status, t("Status was saved correctly."));
+  }
+}
+
+
+/**
+ * Tests the file_validate() function..
+ */
+class FileValidateTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File validate',
+      'description' => 'Tests the file_validate() function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Test that the validators passed into are checked.
+   */
+  function testCallerValidation() {
+    $file = $this->createFile();
+
+    // Empty validators.
+    $this->assertEqual(file_validate($file, array()), array(), t('Validating an empty array works succesfully.'));
+    $this->assertFileHooksCalled(array('validate'));
+
+    // Use the file_test.module's test validator to ensure that passing tests
+    // return correctly.
+    file_test_reset();
+    file_test_set_return('validate', array());
+    $passing = array('file_test_validator' => array(array()));
+    $this->assertEqual(file_validate($file, $passing), array(), t('Validating passes.'));
+    $this->assertFileHooksCalled(array('validate'));
+
+    // Now test for failures in validators passed in and by hook_validate.
+    file_test_reset();
+    file_test_set_return('validate', array('Epic fail'));
+    $failing = array('file_test_validator' => array(array('Failed', 'Badly')));
+    $this->assertEqual(file_validate($file, $failing), array('Failed', 'Badly', 'Epic fail'), t('Validating returns errors.'));
+    $this->assertFileHooksCalled(array('validate'));
+  }
+}
+
+/**
+ *  Tests the file_save_data() function.
+ */
+class FileSaveDataTest extends FileHookTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File save data',
+      'description' => 'Tests the file save data function.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Test the file_save_data() function when no filename is provided.
+   */
+  function testWithoutFilename() {
+    $contents = $this->randomName(8);
+
+    $result = file_save_data($contents);
+    $this->assertTrue($result, t('Unnamed file saved correctly.'));
+
+    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
+    $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename."));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
+    $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.'));
+    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('insert'));
+
+    // Verify that what was returned is what's in the database.
+    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
+  }
+
+  /**
+   * Test the file_save_data() function when a filename is provided.
+   */
+  function testWithFilename() {
+    $contents = $this->randomName(8);
+
+    $result = file_save_data($contents, 'public://' . 'asdf.txt');
+    $this->assertTrue($result, t('Unnamed file saved correctly.'));
+
+    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
+    $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.'));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
+    $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.'));
+    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('insert'));
+
+    // Verify that what was returned is what's in the database.
+    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
+  }
+
+  /**
+   * Test file_save_data() when renaming around an existing file.
+   */
+  function testExistingRename() {
+    // Setup a file to overwrite.
+    $existing = $this->createFile();
+    $contents = $this->randomName(8);
+
+    $result = file_save_data($contents, $existing->uri, FILE_EXISTS_RENAME);
+    $this->assertTrue($result, t("File saved sucessfully."));
+
+    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
+    $this->assertEqual($result->filename, $existing->filename, t("Filename was set to the basename of the source, rather than that of the renamed file."));
+    $this->assertEqual($contents, file_get_contents($result->uri), t("Contents of the file are correct."));
+    $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set."));
+    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('insert'));
+
+    // Ensure that the existing file wasn't overwritten.
+    $this->assertDifferentFile($existing, $result);
+    $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE));
+
+    // Verify that was returned is what's in the database.
+    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
+  }
+
+  /**
+   * Test file_save_data() when replacing an existing file.
+   */
+  function testExistingReplace() {
+    // Setup a file to overwrite.
+    $existing = $this->createFile();
+    $contents = $this->randomName(8);
+
+    $result = file_save_data($contents, $existing->uri, FILE_EXISTS_REPLACE);
+    $this->assertTrue($result, t('File saved sucessfully.'));
+
+    $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory."));
+    $this->assertEqual($result->filename, $existing->filename, t('Filename was set to the basename of the existing file, rather than preserving the original name.'));
+    $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
+    $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.'));
+    $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled(array('load', 'update'));
+
+    // Verify that the existing file was re-used.
+    $this->assertSameFile($existing, $result);
+
+    // Verify that what was returned is what's in the database.
+    $this->assertFileUnchanged($result, file_load($result->fid, TRUE));
+  }
+
+  /**
+   * Test that file_save_data() fails overwriting an existing file.
+   */
+  function testExistingError() {
+    $contents = $this->randomName(8);
+    $existing = $this->createFile(NULL, $contents);
+
+    // Check the overwrite error.
+    $result = file_save_data('asdf', $existing->uri, FILE_EXISTS_ERROR);
+    $this->assertFalse($result, t('Overwriting a file fails when FILE_EXISTS_ERROR is specified.'));
+    $this->assertEqual($contents, file_get_contents($existing->uri), t('Contents of existing file were unchanged.'));
+
+    // Check that no hooks were called while failing.
+    $this->assertFileHooksCalled(array());
+
+    // Ensure that the existing file wasn't overwritten.
+    $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE));
+  }
+}
+
+/**
+ * Tests for download/file transfer functions.
+ */
+class FileDownloadTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File download',
+      'description' => 'Tests for file download/transfer functions.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('file_test');
+    // Clear out any hook calls.
+    file_test_reset();
+  }
+
+  /**
+   * Test the public file transfer system.
+   */
+  function testPublicFileTransfer() {
+    // Test generating an URL to a created file.
+    $file = $this->createFile();
+    $url = file_create_url($file->uri);
+    $this->assertEqual($GLOBALS['base_url'] . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.'));
+    $this->drupalHead($url);
+    $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.'));
+
+    // Test generating an URL to a shipped file (i.e. a file that is part of
+    // Drupal core, a module or a theme, for example a JavaScript file).
+    $filepath = 'misc/jquery.js';
+    $url = file_create_url($filepath);
+    $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath, $url, t('Correctly generated a URL for a shipped file.'));
+    $this->drupalHead($url);
+    $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
+  }
+
+  /**
+   * Test the private file transfer system.
+   */
+  function testPrivateFileTransfer() {
+    // Set file downloads to private so handler functions get called.
+
+    // Create a file.
+    $file = $this->createFile(NULL, NULL, 'private');
+    $url  = file_create_url($file->uri);
+
+    // Set file_test access header to allow the download.
+    file_test_set_return('download', array('x-foo' => 'Bar'));
+    $this->drupalHead($url);
+    $headers = $this->drupalGetHeaders();
+    $this->assertEqual($headers['x-foo'] , 'Bar', t('Found header set by file_test module on private download.'));
+    $this->assertResponse(200, t('Correctly allowed access to a file when file_test provides headers.'));
+
+    // Deny access to all downloads via a -1 header.
+    file_test_set_return('download', -1);
+    $this->drupalHead($url);
+    $this->assertResponse(403, t('Correctly denied access to a file when file_test sets the header to -1.'));
+
+    // Try non-existent file.
+    $url = file_create_url('private://' . $this->randomName());
+    $this->drupalHead($url);
+    $this->assertResponse(404, t('Correctly returned 404 response for a non-existent file.'));
+  }
+
+  /**
+   * Test file_create_url().
+   */
+  function testFileCreateUrl() {
+    global $base_url;
+
+    // Tilde (~) is excluded from this test because it is encoded by
+    // rawurlencode() in PHP 5.2 but not in PHP 5.3, as per RFC 3986.
+    // @see http://www.php.net/manual/en/function.rawurlencode.php#86506
+    $basename = " -._!$'\"()*@[]?&+%#,;=:\n\x00" . // "Special" ASCII characters.
+      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
+      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
+    $basename_encoded = '%20-._%21%24%27%22%28%29%2A%40%5B%5D%3F%26%2B%25%23%2C%3B%3D%3A__' .
+      '%2523%2525%2526%252B%252F%253F' .
+      '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E';
+
+    $this->checkUrl('public', '', $basename, $base_url . '/' . file_directory_path() . '/' . $basename_encoded);
+    $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded);
+    $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0');
+  }
+
+  /**
+   * Download a file from the URL generated by file_create_url().
+   *
+   * Create a file with the specified scheme, directory and filename; check that
+   * the URL generated by file_create_url() for the specified file equals the
+   * specified URL; fetch the URL and then compare the contents to the file.
+   *
+   * @param $scheme
+   *   A scheme, e.g. "public"
+   * @param $directory
+   *   A directory, possibly ""
+   * @param $filename
+   *   A filename
+   * @param $expected_url
+   *   The expected URL
+   * @param $clean_url
+   *   The value of the clean_url setting
+   */
+  private function checkUrl($scheme, $directory, $filename, $expected_url, $clean_url = '1') {
+    variable_set('clean_url', $clean_url);
+
+    // Convert $filename to a valid filename, i.e. strip characters not
+    // supported by the filesystem, and create the file in the specified
+    // directory.
+    $filepath = file_create_filename($filename, $directory);
+    $directory_uri = $scheme . '://' . dirname($filepath);
+    file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY);
+    $file = $this->createFile($filepath, NULL, $scheme);
+
+    $url = file_create_url($file->uri);
+    $this->assertEqual($url, $expected_url, t('Generated URL matches expected URL.'));
+
+    if ($scheme == 'private') {
+      // Tell the implementation of hook_file_download() in file_test.module
+      // that this file may be downloaded.
+      file_test_set_return('download', array('x-foo' => 'Bar'));
+    }
+
+    $this->drupalGet($url);
+    if ($this->assertResponse(200) == 'pass') {
+      $this->assertRaw(file_get_contents($file->uri), t('Contents of the file are correct.'));
+    }
+
+    file_delete($file);
+  }
+}
+
+/**
+ * Tests for file URL rewriting.
+ */
+class FileURLRewritingTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File URL rewriting',
+      'description' => 'Tests for file URL rewriting.',
+      'group' => 'File',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('file_test');
+  }
+
+  /**
+   * Test the generating of rewritten shipped file URLs.
+   */
+  function testShippedFileURL()  {
+    // Test generating an URL to a shipped file (i.e. a file that is part of
+    // Drupal core, a module or a theme, for example a JavaScript file).
+
+    // Test alteration of file URLs to use a CDN.
+    variable_set('file_test_hook_file_url_alter', 'cdn');
+    $filepath = 'misc/jquery.js';
+    $url = file_create_url($filepath);
+    $this->assertEqual(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.'));
+    $filepath = 'misc/favicon.ico';
+    $url = file_create_url($filepath);
+    $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.'));
+
+    // Test alteration of file URLs to use root-relative URLs.
+    variable_set('file_test_hook_file_url_alter', 'root-relative');
+    $filepath = 'misc/jquery.js';
+    $url = file_create_url($filepath);
+    $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.'));
+    $filepath = 'misc/favicon.ico';
+    $url = file_create_url($filepath);
+    $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.'));
+
+    // Test alteration of file URLs to use protocol-relative URLs.
+    variable_set('file_test_hook_file_url_alter', 'protocol-relative');
+    $filepath = 'misc/jquery.js';
+    $url = file_create_url($filepath);
+    $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.'));
+    $filepath = 'misc/favicon.ico';
+    $url = file_create_url($filepath);
+    $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.'));
+  }
+
+  /**
+   * Test the generating of rewritten public created file URLs.
+   */
+  function testPublicCreatedFileURL() {
+    // Test generating an URL to a created file.
+
+    // Test alteration of file URLs to use a CDN.
+    variable_set('file_test_hook_file_url_alter', 'cdn');
+    $file = $this->createFile();
+    $url = file_create_url($file->uri);
+    $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a CDN URL for a created file.'));
+
+    // Test alteration of file URLs to use root-relative URLs.
+    variable_set('file_test_hook_file_url_alter', 'root-relative');
+    $file = $this->createFile();
+    $url = file_create_url($file->uri);
+    $this->assertEqual(base_path() . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a root-relative URL for a created file.'));
+
+    // Test alteration of file URLs to use a protocol-relative URLs.
+    variable_set('file_test_hook_file_url_alter', 'protocol-relative');
+    $file = $this->createFile();
+    $url = file_create_url($file->uri);
+    $this->assertEqual('/' . base_path() . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a protocol-relative URL for a created file.'));
+  }
+}
+
+/**
+ * Tests for file_munge_filename() and file_unmunge_filename().
+ */
+class FileNameMungingTest extends FileTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File naming',
+      'description' => 'Test filename munging and unmunging.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    $this->bad_extension = 'php';
+    $this->name = $this->randomName() . '.' . $this->bad_extension . '.txt';
+  }
+
+  /**
+   * Create a file and munge/unmunge the name.
+   */
+  function testMunging() {
+    // Disable insecure uploads.
+    variable_set('allow_insecure_uploads', 0);
+    $munged_name = file_munge_filename($this->name, '', TRUE);
+    $messages = drupal_get_messages();
+    $this->assertTrue(in_array(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $munged_name)), $messages['status']), t('Alert properly set when a file is renamed.'));
+    $this->assertNotEqual($munged_name, $this->name, t('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name)));
+  }
+
+  /**
+   * If the allow_insecure_uploads variable evaluates to true, the file should
+   * come out untouched, no matter how evil the filename.
+   */
+  function testMungeIgnoreInsecure() {
+    variable_set('allow_insecure_uploads', 1);
+    $munged_name = file_munge_filename($this->name, '');
+    $this->assertIdentical($munged_name, $this->name, t('The original filename (%original) matches the munged filename (%munged) when insecure uploads are enabled.', array('%munged' => $munged_name, '%original' => $this->name)));
+  }
+
+  /**
+   * White listed extensions are ignored by file_munge_filename().
+   */
+  function testMungeIgnoreWhitelisted() {
+    // Declare our extension as whitelisted.
+    $munged_name = file_munge_filename($this->name, $this->bad_extension);
+    $this->assertIdentical($munged_name, $this->name, t('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->name)));
+  }
+
+  /**
+   * Ensure that unmunge gets your name back.
+   */
+  function testUnMunge() {
+    $munged_name = file_munge_filename($this->name, '', FALSE);
+    $unmunged_name = file_unmunge_filename($munged_name);
+    // @TODO uncomment when this tests passes reliably, see
+    // http://drupal.org/node/368502
+    // $this->assertIdentical($unmunged_name, $this->name, t('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name)));
+  }
+}
+
+/**
+ * Tests for file_get_mimetype().
+ */
+class FileMimeTypeTest extends DrupalWebTestCase {
+  function setUp() {
+    parent::setUp('file_test');
+  }
+
+  public static function getInfo() {
+    return array(
+      'name' => 'File mimetypes',
+      'description' => 'Test filename mimetype detection.',
+      'group' => 'File API',
+    );
+  }
+
+  /**
+   * Test mapping of mimetypes from filenames.
+   */
+  public function testFileMimeTypeDetection() {
+    $prefix = 'simpletest://';
+
+    $test_case = array(
+      'test.jar' => 'application/java-archive',
+      'test.jpeg' => 'image/jpeg',
+      'test.JPEG' => 'image/jpeg',
+      'test.jpg' => 'image/jpeg',
+      'test.jar.jpg' => 'image/jpeg',
+      'test.jpg.jar' => 'application/java-archive',
+      'test.pcf.Z' => 'application/x-font',
+      'pcf.z' => 'application/octet-stream',
+      'jar' => 'application/octet-stream',
+      'some.junk' => 'application/octet-stream',
+      'foo.file_test_1' => 'madeup/file_test_1',
+      'foo.file_test_2' => 'madeup/file_test_2',
+      'foo.doc' => 'madeup/doc',
+    );
+
+    // Test using default mappings.
+    foreach ($test_case as $input => $expected) {
+      // Test stream [URI].
+      $output = file_get_mimetype($prefix . $input);
+      $this->assertIdentical($output, $expected, t('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
+
+      // Test normal path equivalent
+      $output = file_get_mimetype($input);
+      $this->assertIdentical($output, $expected, t('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
+    }
+
+    // Now test passing in the map.
+    $mapping = array(
+      'mimetypes' => array(
+        0 => 'application/java-archive',
+        1 => 'image/jpeg',
+      ),
+      'extensions' => array(
+         'jar' => 0,
+         'jpg' => 1,
+      )
+    );
+
+    $test_case = array(
+      'test.jar' => 'application/java-archive',
+      'test.jpeg' => 'application/octet-stream',
+      'test.jpg' => 'image/jpeg',
+      'test.jar.jpg' => 'image/jpeg',
+      'test.jpg.jar' => 'application/java-archive',
+      'test.pcf.z' => 'application/octet-stream',
+      'pcf.z' => 'application/octet-stream',
+      'jar' => 'application/octet-stream',
+      'some.junk' => 'application/octet-stream',
+      'foo.file_test_1' => 'application/octet-stream',
+      'foo.file_test_2' => 'application/octet-stream',
+      'foo.doc' => 'application/octet-stream',
+    );
+
+    foreach ($test_case as $input => $expected) {
+      $output = file_get_mimetype($input, $mapping);
+      $this->assertIdentical($output, $expected, t('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
+    }
+  }
+}
+
+/**
+ * Tests stream wrapper functions.
+ */
+class StreamWrapperTest extends DrupalWebTestCase {
+
+  protected $scheme = 'dummy';
+  protected $classname = 'DrupalDummyStreamWrapper';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Stream wrappers',
+      'description' => 'Tests stream wrapper functions.',
+      'group' => 'File API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('file_test');
+    drupal_static_reset('file_get_stream_wrappers');
+  }
+
+  function tearDown() {
+    parent::tearDown();
+    stream_wrapper_unregister($this->scheme);
+  }
+
+  /**
+   * Test the getClassName() function.
+   */
+  function testGetClassName() {
+    // Check the dummy scheme.
+    $this->assertEqual($this->classname, file_stream_wrapper_get_class($this->scheme), t('Got correct class name for dummy scheme.'));
+    // Check core's scheme.
+    $this->assertEqual('DrupalPublicStreamWrapper', file_stream_wrapper_get_class('public'), t('Got correct class name for public scheme.'));
+  }
+
+  /**
+   * Test the file_stream_wrapper_get_instance_by_scheme() function.
+   */
+  function testGetInstanceByScheme() {
+    $instance = file_stream_wrapper_get_instance_by_scheme($this->scheme);
+    $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy scheme.'));
+
+    $instance = file_stream_wrapper_get_instance_by_scheme('public');
+    $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public scheme.'));
+  }
+
+  /**
+   * Test the URI and target functions.
+   */
+  function testUriFunctions() {
+    $instance = file_stream_wrapper_get_instance_by_uri($this->scheme . '://foo');
+    $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy URI.'));
+
+    $instance = file_stream_wrapper_get_instance_by_uri('public://foo');
+    $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.'));
+
+    // Test file_uri_target().
+    $this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', t('Got a valid stream target from public://foo/bar.txt.'));
+    $this->assertFalse(file_uri_target('foo/bar.txt'), t('foo/bar.txt is not a valid stream.'));
+
+    // Test file_build_uri() and file_directory_path().
+    $this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', t('Expected scheme was added.'));
+    $this->assertEqual(file_directory_path(), variable_get('file_public_path'), t('Expected default directory path was returned.'));
+    $this->assertEqual(file_directory_path('public'), variable_get('file_public_path'), t('Expected public directory path was returned.'));
+    $this->assertEqual(file_directory_path('temporary'), variable_get('file_temporary_path'), t('Expected temporary directory path was returned.'));
+    $this->assertEqual(file_directory_path($this->scheme), variable_get('stream_public_path', 'sites/default/files'), t('Expected dummy directory path was returned.'));
+    $this->assertFalse(file_directory_path('non-existent'), t('No directory path returned for invalid scheme.'));
+    variable_set('file_default_scheme', 'private');
+    $this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', t('Got a valid URI from foo/bar.txt.'));
+    $this->assertEqual(file_directory_path(), variable_get('file_private_path'), t('Expected default directory path was returned.'));
+    $this->assertEqual(file_directory_path('private'), variable_get('file_private_path'), t('Expected private directory path was returned.'));
+  }
+
+  /**
+   * Test the scheme functions.
+   */
+  function testGetValidStreamScheme() {
+    $this->assertEqual('foo', file_uri_scheme('foo://pork//chops'), t('Got the correct scheme from foo://asdf'));
+    $this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), t('Got a valid stream scheme from public://asdf'));
+    $this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), t('Did not get a valid stream scheme from foo://asdf'));
+  }
+}
diff --git a/modules/system/tests/filetransfer.test b/modules/system/tests/filetransfer.test
new file mode 100644
index 0000000..b9a79f7
--- /dev/null
+++ b/modules/system/tests/filetransfer.test
@@ -0,0 +1,169 @@
+<?php
+// $Id: filetransfer.test,v 1.6 2009/11/10 17:27:53 webchick Exp $
+
+
+class FileTranferTest extends DrupalWebTestCase {
+  protected $hostname = 'localhost';
+  protected $username = 'drupal';
+  protected $password = 'password';
+  protected $port = '42';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'FileTransfer unit tests',
+      'description' => 'Test that the jail is respected and that protocols using recursive file move operations work.',
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    $this->testConnection = TestFileTransfer::factory(DRUPAL_ROOT, array('hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port));
+  }
+
+  function _getFakeModuleFiles() {
+    $files = array(
+      'fake.module',
+      'fake.info',
+      'theme' => array(
+        'fake.tpl.php'
+      ),
+      'inc' => array(
+        'fake.inc'
+      )
+    );
+    return $files;
+  }
+
+  function _buildFakeModule() {
+    $location = file_directory_path('temporary') . '/fake';
+    if (is_dir($location)) {
+      $ret = 0;
+      $output = array();
+      exec('rm -Rf ' . escapeshellarg($location), $output, $ret);
+      if ($ret != 0) {
+        throw new Exception('Error removing fake module directory.');
+      }
+    }
+
+    $files = $this->_getFakeModuleFiles();
+    $this->_writeDirectory($location, $files);
+    return $location;
+  }
+
+  function _writeDirectory($base, $files = array()) {
+    mkdir($base);
+    foreach ($files as $key => $file) {
+      if (is_array($file)) {
+        $this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file);
+      }
+      else {
+        //just write the filename into the file
+        file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file);
+      }
+    }
+  }
+
+  function testJail() {
+    $source = $this->_buildFakeModule();
+
+    // This convoluted piece of code is here because our testing framework does
+    // not support expecting exceptions.
+    $gotit = FALSE;
+    try {
+      $this->testConnection->copyDirectory($source, '/tmp');
+    }
+    catch (FileTransferException $e) {
+      $gotit = TRUE;
+    }
+    $this->assertTrue($gotit, 'Was not able to copy a directory outside of the jailed area.');
+
+    $gotit = TRUE;
+    try {
+      $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. file_directory_path());
+    }
+    catch (FileTransferException $e) {
+      $gotit = FALSE;
+    }
+    $this->assertTrue($gotit, 'Was able to copy a directory inside of the jailed area');
+  }
+}
+
+/**
+ * Mock FileTransfer object for test case.
+ */
+class TestFileTransfer extends FileTransfer {
+  protected $host = NULL;
+  protected $username = NULL;
+  protected $password = NULL;
+  protected $port = NULL;
+
+  /**
+   * This is for testing the CopyRecursive logic.
+   */
+  public $shouldIsDirectoryReturnTrue = FALSE;
+
+  function __construct($jail, $username, $password, $hostname = 'localhost', $port = 9999) {
+    parent::__construct($jail, $username, $password, $hostname, $port);
+  }
+
+  static function factory($jail, $settings) {
+    return new TestFileTransfer($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']);
+  }
+
+  function connect() {
+    $parts = explode(':', $this->hostname);
+    $port = (count($parts) == 2) ? $parts[1] : $this->port;
+    $this->connection = new MockTestConnection();
+    $this->connection->connectionString = 'test://' . urlencode($this->username) . ':' . urlencode($this->password) . "@$this->host:$this->port/";
+  }
+
+  function copyFileJailed($source, $destination) {
+    $this->connection->run("copyFile $source $destination");
+  }
+
+  protected function removeDirectoryJailed($directory) {
+    $this->connection->run("rmdir $directory");
+  }
+
+  function createDirectoryJailed($directory) {
+    $this->connection->run("mkdir $directory");
+  }
+
+  function removeFileJailed($destination) {
+    if (!ftp_delete($this->connection, $item)) {
+      throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
+    }
+  }
+
+  function isDirectory($path) {
+    return $this->shouldIsDirectoryReturnTrue;
+  }
+
+  function isFile($path) {
+    return FALSE;
+  }
+
+  function chmodJailed($path, $mode, $recursive) {
+    return;
+  }
+}
+
+/**
+ * Mock connection object for test case.
+ */
+class MockTestConnection {
+
+  var $commandsRun = array();
+  var $connectionString;
+
+  function run($cmd) {
+    $this->commandsRun[] = $cmd;
+  }
+
+  function flushCommands() {
+    $out = $this->commandsRun;
+    $this->commandsRun = array();
+    return $out;
+  }
+}
diff --git a/modules/system/tests/form.test b/modules/system/tests/form.test
new file mode 100644
index 0000000..1dc8c7b
--- /dev/null
+++ b/modules/system/tests/form.test
@@ -0,0 +1,1226 @@
+<?php
+// $Id: form.test,v 1.56 2010/07/26 13:07:59 dries Exp $
+
+/**
+ * @file
+ * Unit tests for the Drupal Form API.
+ */
+
+class FormsTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Form element validation',
+      'description' => 'Tests various form element validation mechanisms.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Check several empty values for required forms elements.
+   *
+   * Carriage returns, tabs, spaces, and unchecked checkbox elements are not
+   * valid content for a required field.
+   *
+   * If the form field is found in form_get_errors() then the test pass.
+   */
+  function testRequiredFields() {
+    // Originates from http://drupal.org/node/117748
+    // Sets of empty strings and arrays.
+    $empty_strings = array('""' => "", '"\n"' => "\n", '" "' => " ", '"\t"' => "\t", '" \n\t "' => " \n\t ", '"\n\n\n\n\n"' => "\n\n\n\n\n");
+    $empty_arrays = array('array()' => array());
+    $empty_checkbox = array(NULL);
+
+    $elements['textfield']['element'] = array('#title' => $this->randomName(), '#type' => 'textfield');
+    $elements['textfield']['empty_values'] = $empty_strings;
+
+    $elements['password']['element'] = array('#title' => $this->randomName(), '#type' => 'password');
+    $elements['password']['empty_values'] = $empty_strings;
+
+    $elements['password_confirm']['element'] = array('#title' => $this->randomName(), '#type' => 'password_confirm');
+    $elements['password_confirm']['empty_values'] = $empty_strings;
+
+    $elements['textarea']['element'] = array('#title' => $this->randomName(), '#type' => 'textarea');
+    $elements['textarea']['empty_values'] = $empty_strings;
+
+    $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array($this->randomName(), $this->randomName(), $this->randomName()));
+    $elements['radios']['empty_values'] = $empty_arrays;
+
+    $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName());
+    $elements['checkbox']['empty_values'] = $empty_checkbox;
+
+    $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName()));
+    $elements['checkboxes']['empty_values'] = $empty_arrays;
+
+    $elements['select']['element'] = array('#title' => $this->randomName(), '#type' => 'select', '#options' => array($this->randomName(), $this->randomName(), $this->randomName()));
+    $elements['select']['empty_values'] = $empty_strings;
+
+    $elements['file']['element'] = array('#title' => $this->randomName(), '#type' => 'file');
+    $elements['file']['empty_values'] = $empty_strings;
+
+    // Regular expression to find the expected marker on required elements.
+    $required_marker_preg = '@<label.*<span class="form-required" title="This field is required\.">\*</span></label>@';
+
+    // Go through all the elements and all the empty values for them.
+    foreach ($elements as $type => $data) {
+      foreach ($data['empty_values'] as $key => $empty) {
+        foreach (array(TRUE, FALSE) as $required) {
+          $form_id = $this->randomName();
+          $form = array();
+          $form_state = form_state_defaults();
+          form_clear_error();
+          $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
+          $element = $data['element']['#title'];
+          $form[$element] = $data['element'];
+          $form[$element]['#required'] = $required;
+          $form_state['values'][$element] = $empty;
+          $form_state['input'] = $form_state['values'];
+          $form_state['input']['form_id'] = $form_id;
+          $form_state['method'] = 'post';
+          drupal_prepare_form($form_id, $form, $form_state);
+          drupal_process_form($form_id, $form, $form_state);
+          $errors = form_get_errors();
+          // Form elements of type 'radios' throw all sorts of PHP notices
+          // when you try to render them like this, so we ignore those for
+          // testing the required marker.
+          // @todo Fix this work-around (http://drupal.org/node/588438).
+          $form_output = ($type == 'radios') ? '' : drupal_render($form);
+          if ($required) {
+            // Make sure we have a form error for this element.
+            $this->assertTrue(isset($errors[$element]), "Check empty($key) '$type' field '$element'");
+            if (!empty($form_output)) {
+              // Make sure the form element is marked as required.
+              $this->assertTrue(preg_match($required_marker_preg, $form_output), "Required '$type' field is marked as required");
+            }
+          }
+          else {
+            if (!empty($form_output)) {
+              // Make sure the form element is *not* marked as required.
+              $this->assertFalse(preg_match($required_marker_preg, $form_output), "Optional '$type' field is not marked as required");
+            }
+            if ($type == 'select') {
+              // Select elements are going to have validation errors with empty
+              // input, since those are illegal choices. Just make sure the
+              // error is not "field is required".
+              $this->assertTrue((empty($errors[$element]) || strpos('field is required', $errors[$element]) === FALSE), "Optional '$type' field '$element' is not treated as a required element");
+            }
+            else {
+              // Make sure there is *no* form error for this element.
+              $this->assertTrue(empty($errors[$element]), "Optional '$type' field '$element' has no errors with empty input");
+            }
+          }
+        }
+      }
+    }
+    // Clear the expected form error messages so they don't appear as exceptions.
+    drupal_get_messages();
+  }
+
+  /**
+   * Test default value handling for checkboxes.
+   *
+   * @see _form_test_checkbox()
+   */
+  function testCheckboxProcessing() {
+    // First, try to submit without the required checkbox.
+    $edit = array();
+    $this->drupalPost('form-test/checkbox', $edit, t('Submit'));
+    $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
+
+    // Now try to submit the form correctly.
+    $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit')));
+    $expected_values = array(
+      'disabled_checkbox_on' => 'disabled_checkbox_on',
+      'disabled_checkbox_off' => '',
+      'checkbox_on' => 'checkbox_on',
+      'checkbox_off' => '',
+      'zero_checkbox_on' => '0',
+      'zero_checkbox_off' => '',
+    );
+    foreach ($expected_values as $widget => $expected_value) {
+      $this->assertEqual($values[$widget], $expected_value, t('Checkbox %widget returns expected value (expected: %expected, got: %value)', array(
+        '%widget' => var_export($widget, TRUE),
+        '%expected' => var_export($expected_value, TRUE),
+        '%value' => var_export($values[$widget], TRUE),
+      )));
+    }
+  }
+
+  /**
+   * Test handling of disabled elements.
+   *
+   * @see _form_test_disabled_elements()
+   */
+  function testDisabledElements() {
+    // Get the raw form in its original state.
+    $form_state = array();
+    $form = _form_test_disabled_elements(array(), $form_state);
+
+    // Build a submission that tries to hijack the form by submitting input for
+    // elements that are disabled.
+    $edit = array();
+    foreach (element_children($form) as $key) {
+      if (isset($form[$key]['#test_hijack_value'])) {
+        if (is_array($form[$key]['#test_hijack_value'])) {
+          foreach ($form[$key]['#test_hijack_value'] as $subkey => $value) {
+            $edit[$key . '[' . $subkey . ']'] = $value;
+          }
+        }
+        else {
+          $edit[$key] = $form[$key]['#test_hijack_value'];
+        }
+      }
+    }
+
+    // Submit the form with no input, as the browser does for disabled elements,
+    // and fetch the $form_state['values'] that is passed to the submit handler.
+    $this->drupalPost('form-test/disabled-elements', array(), t('Submit'));
+    $returned_values['normal'] = drupal_json_decode($this->content);
+
+    // Do the same with input, as could happen if JavaScript un-disables an
+    // element. drupalPost() emulates a browser by not submitting input for
+    // disabled elements, so we need to un-disable those elements first.
+    $this->drupalGet('form-test/disabled-elements');
+    $disabled_elements = array();
+    foreach ($this->xpath('//*[@disabled]') as $element) {
+      $disabled_elements[] = (string) $element['name'];
+      unset($element['disabled']);
+    }
+
+    // All the elements should be marked as disabled, including the ones below
+    // the disabled container.
+    $this->assertEqual(count($disabled_elements), 20, t('The correct elements have the disabled property in the HTML code.'));
+
+    $this->drupalPost(NULL, $edit, t('Submit'));
+    $returned_values['hijacked'] = drupal_json_decode($this->content);
+
+    // Ensure that the returned values match the form's default values in both
+    // cases.
+    foreach ($returned_values as $type => $values) {
+      $this->assertFormValuesDefault($values, $form);
+    }
+  }
+
+  /**
+   * Assert that the values submitted to a form matches the default values of the elements.
+   */
+  function assertFormValuesDefault($values, $form) {
+    foreach (element_children($form) as $key) {
+      if (isset($form[$key]['#default_value'])) {
+        $expected_value = $form[$key]['#default_value'];
+
+        if ($key == 'checkboxes_multiple') {
+          // Checkboxes values are not filtered out.
+          $values[$key] = array_filter($values[$key]);
+        }
+        $this->assertIdentical($expected_value, $values[$key], t('Default value for %type: expected %expected, returned %returned.', array('%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE))));
+      }
+
+      // Recurse children.
+      $this->assertFormValuesDefault($values, $form[$key]);
+    }
+  }
+
+  /**
+   * Test Form API protections against input forgery.
+   *
+   * @see _form_test_input_forgery()
+   */
+  function testInputForgery() {
+    $this->drupalGet('form-test/input-forgery');
+    $checkbox = $this->xpath('//input[@name="checkboxes[two]"]');
+    $checkbox[0]['value'] = 'FORGERY';
+    $this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit'));
+    $this->assertText('An illegal choice has been detected.', t('Input forgery was detected.'));
+  }
+}
+
+/**
+ * Test form alter hooks.
+ */
+class FormAlterTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form alter hooks',
+      'description' => 'Tests hook_form_alter() and hook_form_FORM_ID_alter().',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter().
+   */
+  function testExecutionOrder() {
+    $this->drupalGet('form-test/alter');
+    // Ensure that the order is first by module, then for a given module, the
+    // id-specific one after the generic one.
+    $expected = array(
+      'block_form_form_test_alter_form_alter() executed.',
+      'form_test_form_alter() executed.',
+      'form_test_form_form_test_alter_form_alter() executed.',
+      'system_form_form_test_alter_form_alter() executed.',
+    );
+    $content = preg_replace('/\s+/', ' ', filter_xss($this->content, array()));
+    $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, t('Form alter hooks executed in the expected order.'));
+  }
+}
+
+/**
+ * Test form validation handlers.
+ */
+class FormValidationTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form validation handlers',
+      'description' => 'Tests form processing and alteration via form validation handlers.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests form alterations by #element_validate, #validate, and form_set_value().
+   */
+  function testValidate() {
+    $this->drupalGet('form-test/validate');
+    // Verify that #element_validate handlers can alter the form and submitted
+    // form values.
+    $edit = array(
+      'name' => 'element_validate',
+    );
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertFieldByName('name', '#value changed by #element_validate', t('Form element #value was altered.'));
+    $this->assertText('Name value: value changed by form_set_value() in #element_validate', t('Form element value in $form_state was altered.'));
+
+    // Verify that #validate handlers can alter the form and submitted
+    // form values.
+    $edit = array(
+      'name' => 'validate',
+    );
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertFieldByName('name', '#value changed by #validate', t('Form element #value was altered.'));
+    $this->assertText('Name value: value changed by form_set_value() in #validate', t('Form element value in $form_state was altered.'));
+
+    // Verify that #element_validate handlers can make form elements
+    // inaccessible, but values persist.
+    $edit = array(
+      'name' => 'element_validate_access',
+    );
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertNoFieldByName('name', t('Form element was hidden.'));
+    $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.'));
+
+    // Verify that value for inaccessible form element persists.
+    $this->drupalPost(NULL, array(), 'Save');
+    $this->assertNoFieldByName('name', t('Form element was hidden.'));
+    $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.'));
+  }
+
+  /**
+   * Tests partial form validation through #limit_validation_errors.
+   */
+  function testValidateLimitErrors() {
+    $edit = array('test' => 'invalid');
+    $path = 'form-test/limit-validation-errors';
+
+    // Submit the form by pressing the button with #limit_validation_errors and
+    // ensure that the title field is not validated, but the #element_validate
+    // handler for the 'test' field is triggered.
+    $this->drupalPost($path, $edit, t('Partial validate'));
+    $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText('Test element is invalid');
+
+    // Now test full form validation and ensure that the #element_validate
+    // handler is still triggered.
+    $this->drupalPost($path, $edit, t('Full validate'));
+    $this->assertText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText('Test element is invalid');
+  }
+}
+
+/**
+ * Test form element labels, required markers and associated output.
+ */
+class FormsElementsLabelsTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Form element and label output test',
+      'description' => 'Test form element labels, required markers and associated output.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Test form elements, labels, title attibutes and required marks output
+   * correctly and have the correct label option class if needed.
+   */
+  function testFormLabels() {
+    $this->drupalGet('form_test/form-labels');
+
+    // Check that the checkbox/radio processing is not interfering with
+    // basic placement.
+    $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]');
+    $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular checkboxes."));
+
+    $elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]');
+    $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular radios."));
+
+    // Exercise various defaults for checkboxes and modifications to ensure
+    // appropriate override and correct behaviour.
+    $elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]');
+    $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for a checkbox by default."));
+
+    // Exercise various defaults for textboxes and modifications to ensure
+    // appropriate override and correct behaviour.
+    $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required"]/child::span[@class="form-required"]/parent::*/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
+    $this->assertTrue(isset($elements[0]), t("Label preceeds textfield, with required marker inside label."));
+
+    $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required"]/span[@class="form-required"]');
+    $this->assertTrue(isset($elements[0]), t("Label tag with required marker preceeds required textfield with no title."));
+
+    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="form-required"]');
+    $this->assertFalse(isset($elements[0]), t("No required marker on non-required field."));
+
+    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');
+    $this->assertTrue(isset($elements[0]), t("Label after field and label option class correct for text field."));
+
+    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/following-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="element-invisible"]');
+    $this->assertTrue(isset($elements[0]), t("Label after field and label class is element-invisible."));
+
+    $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]');
+    $this->assertFalse(isset($elements[0]), t("No label tag when title set not to display."));
+
+    // Check #field_prefix and #field_suffix placement.
+    $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]');
+    $this->assertTrue(isset($elements[0]), t("Properly placed the #field_prefix element after the label and before the field."));
+
+    $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]');
+    $this->assertTrue(isset($elements[0]), t("Properly places the #field_suffix element immediately after the form field."));
+
+    // Check #prefix and #suffix placement.
+    $elements = $this->xpath('//div[@id="form-test-textfield-title-prefix"]/following-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]');
+    $this->assertTrue(isset($elements[0]), t("Properly places the #prefix element before the form item."));
+
+    $elements = $this->xpath('//div[@id="form-test-textfield-title-suffix"]/preceding-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]');
+    $this->assertTrue(isset($elements[0]), t("Properly places the #suffix element before the form item."));
+  }
+}
+
+/**
+ * Test the tableselect form element for expected behavior.
+ */
+class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Tableselect form element type test',
+      'description' => 'Test the tableselect element for expected behavior',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+
+  /**
+   * Test the display of checkboxes when #multiple is TRUE.
+   */
+  function testMultipleTrue() {
+
+    $this->drupalGet('form_test/tableselect/multiple-true');
+
+    $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.'));
+
+    // Test for the presence of the Select all rows tableheader.
+    $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Presence of the "Select all" checkbox.'));
+
+    $rows = array('row1', 'row2', 'row3');
+    foreach ($rows as $row) {
+      $this->assertFieldByXPath('//input[@type="checkbox"]', $row, t('Checkbox for value @row.', array('@row' => $row)));
+    }
+  }
+
+  /**
+   * Test the display of radios when #multiple is FALSE.
+   */
+  function testMultipleFalse() {
+    $this->drupalGet('form_test/tableselect/multiple-false');
+
+    $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.'));
+
+    // Test for the absence of the Select all rows tableheader.
+    $this->assertNoFieldByXPath('//th[@class="select-all"]', '', t('Absence of the "Select all" checkbox.'));
+
+    $rows = array('row1', 'row2', 'row3');
+    foreach ($rows as $row) {
+      $this->assertFieldByXPath('//input[@type="radio"]', $row, t('Radio button for value @row.', array('@row' => $row)));
+    }
+  }
+
+  /**
+   * Test the display of the #empty text when #options is an empty array.
+   */
+  function testEmptyText() {
+    $this->drupalGet('form_test/tableselect/empty-text');
+    $this->assertText(t('Empty text.'), t('Empty text should be displayed.'));
+  }
+
+  /**
+   * Test the submission of single and multiple values when #multiple is TRUE.
+   */
+  function testMultipleTrueSubmit() {
+
+    // Test a submission with one checkbox checked.
+    $edit = array();
+    $edit['tableselect[row1]'] = TRUE;
+    $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit');
+
+    $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1'));
+    $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.'));
+    $this->assertText(t('Submitted: row3 = 0'), t('Unchecked checkbox row3.'));
+
+    // Test a submission with multiple checkboxes checked.
+    $edit['tableselect[row1]'] = TRUE;
+    $edit['tableselect[row3]'] = TRUE;
+    $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit');
+
+    $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1.'));
+    $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.'));
+    $this->assertText(t('Submitted: row3 = row3'), t('Checked checkbox row3.'));
+
+  }
+
+  /**
+   * Test submission of values when #multiple is FALSE.
+   */
+  function testMultipleFalseSubmit() {
+    $edit['tableselect'] = 'row1';
+    $this->drupalPost('form_test/tableselect/multiple-false', $edit, 'Submit');
+    $this->assertText(t('Submitted: row1'), t('Selected radio button'));
+  }
+
+  /**
+   * Test the #js_select property.
+   */
+  function testAdvancedSelect() {
+    // When #multiple = TRUE a Select all checkbox should be displayed by default.
+    $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-default');
+    $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Display a "Select all" checkbox by default when #multiple is TRUE.'));
+
+    // When #js_select is set to FALSE, a "Select all" checkbox should not be displayed.
+    $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-no-advanced-select');
+    $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #js_select is FALSE.'));
+
+    // A "Select all" checkbox never makes sense when #multiple = FALSE, regardless of the value of #js_select.
+    $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-default');
+    $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE.'));
+
+    $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-advanced-select');
+    $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE, even when #js_select is TRUE.'));
+  }
+
+
+  /**
+   * Test the whether the option checker gives an error on invalid tableselect values for checkboxes.
+   */
+  function testMultipleTrueOptionchecker() {
+
+    list($header, $options) = _form_test_tableselect_get_data();
+
+    $form['tableselect'] = array(
+      '#type' => 'tableselect',
+      '#header' => $header,
+      '#options' => $options,
+    );
+
+    // Test with a valid value.
+    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1'));
+    $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for checkboxes.'));
+
+    // Test with an invalid value.
+    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value'));
+    $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for checkboxes.'));
+
+  }
+
+
+  /**
+   * Test the whether the option checker gives an error on invalid tableselect values for radios.
+   */
+  function testMultipleFalseOptionchecker() {
+
+    list($header, $options) = _form_test_tableselect_get_data();
+
+    $form['tableselect'] = array(
+      '#type' => 'tableselect',
+      '#header' => $header,
+      '#options' => $options,
+      '#multiple' => FALSE,
+    );
+
+    // Test with a valid value.
+    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1'));
+    $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for radio buttons.'));
+
+    // Test with an invalid value.
+    list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value'));
+    $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for radio buttons.'));
+  }
+
+
+  /**
+   * Helper function for the option check test to submit a form while collecting errors.
+   *
+   * @param $form_element
+   *   A form element to test.
+   * @param $edit
+   *   An array containing post data.
+   *
+   * @return
+   *   An array containing the processed form, the form_state and any errors.
+   */
+  private function formSubmitHelper($form, $edit) {
+    $form_id = $this->randomName();
+    $form_state = form_state_defaults();
+
+    $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
+
+    $form_state['input'] = $edit;
+    $form_state['input']['form_id'] = $form_id;
+
+    drupal_prepare_form($form_id, $form, $form_state);
+
+    drupal_process_form($form_id, $form, $form_state);
+
+    $errors = form_get_errors();
+
+    // Clear errors and messages.
+    drupal_get_messages();
+    form_clear_error();
+
+    // Return the processed form together with form_state and errors
+    // to allow the caller lowlevel access to the form.
+    return array($form, $form_state, $errors);
+  }
+
+}
+
+/**
+ * Test the form storage on a multistep form.
+ *
+ * The tested form puts data into the storage during the initial form
+ * construction. These tests verify that there are no duplicate form
+ * constructions, with and without manual form caching activiated. Furthermore
+ * when a validation error occurs, it makes sure that changed form element
+ * values aren't lost due to a wrong form rebuild.
+ */
+class FormsFormStorageTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name'  => 'Multistep form using form storage',
+      'description'  => 'Tests a multistep form using form storage and makes sure validation and caching works right.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+
+    $this->web_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Tests using the form in a usual way.
+   */
+  function testForm() {
+    $this->drupalGet('form_test/form-storage');
+    $this->assertText('Form constructions: 1');
+
+    $edit = array('title' => 'new', 'value' => 'value_is_set');
+    // Reload the form, but don't rebuild.
+    $this->drupalPost(NULL, $edit, 'Reload');
+    $this->assertText('Form constructions: 2');
+
+    // Now use form rebuilding triggered by a submit button.
+    $this->drupalPost(NULL, $edit, 'Continue submit');
+    $this->assertText('Form constructions: 3');
+    $this->assertText('Form constructions: 4');
+
+    // Reset the form to the values of the storage, using a form rebuild
+    // triggered by button of type button.
+    $this->drupalPost(NULL, array('title' => 'changed'), 'Reset');
+    $this->assertFieldByName('title', 'new', 'Values have been resetted.');
+    // After rebuilding, the form has been cached.
+    $this->assertText('Form constructions: 5');
+
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertText('Form constructions: 5');
+    $this->assertText('Title: new', t('The form storage has stored the values.'));
+  }
+
+  /**
+   * Tests using the form with an activated $form_state['cache'] property.
+   */
+  function testFormCached() {
+    $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1)));
+    $this->assertText('Form constructions: 1');
+
+    $edit = array('title' => 'new', 'value' => 'value_is_set');
+    // Reload the form, but don't rebuild.
+    $this->drupalPost(NULL, $edit, 'Reload');
+    $this->assertNoText('Form constructions');
+
+    // Now use form rebuilding triggered by a submit button.
+    $this->drupalPost(NULL, $edit, 'Continue submit');
+    $this->assertText('Form constructions: 2');
+
+    // Reset the form to the values of the storage, using a form rebuild
+    // triggered by button of type button.
+    $this->drupalPost(NULL, array('title' => 'changed'), 'Reset');
+    $this->assertFieldByName('title', 'new', 'Values have been resetted.');
+    $this->assertText('Form constructions: 3');
+
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertText('Form constructions: 3');
+    $this->assertText('Title: new', t('The form storage has stored the values.'));
+  }
+
+  /**
+   * Tests validation when form storage is used.
+   */
+  function testValidation() {
+    $this->drupalPost('form_test/form-storage', array('title' => '', 'value' => 'value_is_set'), 'Continue submit');
+    $this->assertPattern('/value_is_set/', t('The input values have been kept.'));
+  }
+
+  /**
+   * Tests updating cached form storage during form validation.
+   *
+   * If form caching is enabled and a form stores data in the form storage, then
+   * the form storage also has to be updated in case of a validation error in
+   * the form. This test re-uses the existing form for multi-step tests, but
+   * triggers a special #element_validate handler to update the form storage
+   * during form validation, while another, required element in the form
+   * triggers a form validation error.
+   */
+  function testCachedFormStorageValidation() {
+    // Request the form with 'cache' query parameter to enable form caching.
+    $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1)));
+
+    // Skip step 1 of the multi-step form, since the first step copies over
+    // 'title' into form storage, but we want to verify that changes in the form
+    // storage are updated in the cache during form validation.
+    $edit = array('title' => 'foo');
+    $this->drupalPost(NULL, $edit, 'Continue submit');
+
+    // In step 2, trigger a validation error for the required 'title' field, and
+    // post the special 'change_title' value for the 'value' field, which
+    // conditionally invokes the #element_validate handler to update the form
+    // storage.
+    $edit = array('title' => '', 'value' => 'change_title');
+    $this->drupalPost(NULL, $edit, 'Save');
+
+    // At this point, the form storage should contain updated values, but we do
+    // not see them, because the form has not been rebuilt yet due to the
+    // validation error. Post again and verify that the rebuilt form contains
+    // the values of the updated form storage.
+    $this->drupalPost(NULL, array('title' => 'foo', 'value' => 'bar'), 'Save');
+    $this->assertText("The thing has been changed.", 'The altered form storage value was updated in cache and taken over.');
+  }
+
+  /**
+   * Tests a form using form state without using 'storage' to pass data from the
+   * constructor to a submit handler. The data has to persist even when caching
+   * gets activated, what may happen when a modules alter the form and adds
+   * #ajax properties.
+   */
+  function testFormStatePersist() {
+    // Test the form one time with caching activated and one time without.
+    $run_options = array(
+      array(),
+      array('query' => array('cache' => 1)),
+    );
+    foreach($run_options as $options) {
+      $this->drupalPost('form-test/state-persist', array(), t('Submit'), $options);
+      // The submit handler outputs the value in $form_state, assert it's there.
+      $this->assertText('State persisted.');
+
+      // Test it again, but first trigger a validation error, then test.
+      $this->drupalPost('form-test/state-persist', array('title' => ''), t('Submit'), $options);
+      $this->assertText(t('!name field is required.', array('!name' => 'title')));
+      // Submit the form again triggering no validation error.
+      $this->drupalPost(NULL, array('title' => 'foo'), t('Submit'), $options);
+      $this->assertText('State persisted.');
+
+      // Now post to the rebuilt form and verify it's still there afterwards.
+      $this->drupalPost(NULL, array('title' => 'bar'), t('Submit'), $options);
+      $this->assertText('State persisted.');
+    }
+  }
+}
+
+/**
+ * Test wrapper form callbacks.
+ */
+class FormsFormWrapperTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form wrapper callback',
+      'description' => 'Tests form wrapper callbacks to pass a prebuilt form to form builder functions.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests using the form in a usual way.
+   */
+  function testWrapperCallback() {
+    $this->drupalGet('form_test/wrapper-callback');
+    $this->assertText('Form wrapper callback element output.', t('The form contains form wrapper elements.'));
+    $this->assertText('Form builder element output.', t('The form contains form builder elements.'));
+  }
+}
+
+/**
+ * Test $form_state clearance.
+ */
+class FormStateValuesCleanTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form state values clearance',
+      'description' => 'Test proper removal of submitted form values using form_state_values_clean().',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests form_state_values_clean().
+   */
+  function testFormStateValuesClean() {
+    $values = drupal_json_decode($this->drupalPost('form_test/form-state-values-clean', array(), t('Submit')));
+
+    // Setup the expected result.
+    $result = array(
+      'beer' => 1000,
+      'baz' => array('beer' => 2000),
+    );
+
+    // Verify that all internal Form API elements were removed.
+    $this->assertFalse(isset($values['form_id']), t('%element was removed.', array('%element' => 'form_id')));
+    $this->assertFalse(isset($values['form_token']), t('%element was removed.', array('%element' => 'form_token')));
+    $this->assertFalse(isset($values['form_build_id']), t('%element was removed.', array('%element' => 'form_build_id')));
+    $this->assertFalse(isset($values['op']), t('%element was removed.', array('%element' => 'op')));
+
+    // Verify that all buttons were removed.
+    $this->assertFalse(isset($values['foo']), t('%element was removed.', array('%element' => 'foo')));
+    $this->assertFalse(isset($values['bar']), t('%element was removed.', array('%element' => 'bar')));
+    $this->assertFalse(isset($values['baz']['foo']), t('%element was removed.', array('%element' => 'foo')));
+    $this->assertFalse(isset($values['baz']['baz']), t('%element was removed.', array('%element' => 'baz')));
+
+    // Verify that nested form value still exists.
+    $this->assertTrue(isset($values['baz']['beer']), t('Nested form value still exists.'));
+
+    // Verify that actual form values equal resulting form values.
+    $this->assertEqual($values, $result, t('Expected form values equal actual form values.'));
+  }
+}
+
+/**
+ * Tests form rebuilding.
+ *
+ * @todo Add tests for other aspects of form rebuilding.
+ */
+class FormsRebuildTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form rebuilding',
+      'description' => 'Tests functionality of drupal_rebuild_form().',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+
+    $this->web_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Tests preservation of values.
+   */
+  function testRebuildPreservesValues() {
+    $edit = array(
+      'checkbox_1_default_off' => TRUE,
+      'checkbox_1_default_on' => FALSE,
+      'text_1' => 'foo',
+    );
+    $this->drupalPost('form-test/form-rebuild-preserve-values', $edit, 'Add more');
+
+    // Verify that initial elements retained their submitted values.
+    $this->assertFieldChecked('edit-checkbox-1-default-off', t('A submitted checked checkbox retained its checked state during a rebuild.'));
+    $this->assertNoFieldChecked('edit-checkbox-1-default-on', t('A submitted unchecked checkbox retained its unchecked state during a rebuild.'));
+    $this->assertFieldById('edit-text-1', 'foo', t('A textfield retained its submitted value during a rebuild.'));
+
+    // Verify that newly added elements were initialized with their default values.
+    $this->assertFieldChecked('edit-checkbox-2-default-on', t('A newly added checkbox was initialized with a default checked state.'));
+    $this->assertNoFieldChecked('edit-checkbox-2-default-off', t('A newly added checkbox was initialized with a default unchecked state.'));
+    $this->assertFieldById('edit-text-2', 'DEFAULT 2', t('A newly added textfield was initialized with its default value.'));
+  }
+
+  /**
+   * Tests that a form's action is retained after an AJAX submission.
+   *
+   * The 'action' attribute of a form should not change after an AJAX submission
+   * followed by a non-AJAX submission, which triggers a validation error.
+   */
+  function testPreserveFormActionAfterAJAX() {
+    // Create a multi-valued field for 'page' nodes to use for AJAX testing.
+    $field_name = 'field_ajax_test';
+    $field = array(
+      'field_name' => $field_name,
+      'type' => 'text',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    );
+    field_create_field($field);
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'node',
+      'bundle' => 'page',
+    );
+    field_create_instance($instance);
+
+    // Log in a user who can create 'page' nodes.
+    $this->web_user = $this->drupalCreateUser(array('create page content'));
+    $this->drupalLogin($this->web_user);
+
+    // Get the form for adding a 'page' node. Submit an "add another item" AJAX
+    // submission and verify it worked by ensuring the updated page has two text
+    // field items in the field for which we just added an item.
+    $this->drupalGet('node/add/page');
+    preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
+    $settings = drupal_json_decode($matches[1]);
+    $button = $this->xpath('//input[@name="field_ajax_test_add_more"]');
+    $button_id = (string) $button[0]['id'];
+    $this->drupalPostAJAX(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), 'system/ajax', array(), array(), 'page-node-form', $settings['ajax'][$button_id]);
+    $this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, t('AJAX submission succeeded.'));
+
+    // Submit the form with the non-AJAX "Save" button, leaving the title field
+    // blank to trigger a validation error, and ensure that a validation error
+    // occurred, because this test is for testing what happens when a form is
+    // re-rendered without being re-built, which is what happens when there's
+    // a validation error.
+    $this->drupalPost(NULL, array(), t('Save'));
+    $this->assertText('Title field is required.', t('Non-AJAX submission correctly triggered a validation error.'));
+
+    // Ensure that the form's action is correct.
+    $forms = $this->xpath('//form[contains(@class, "node-page-form")]');
+    $this->assert(count($forms) == 1 && $forms[0]['action'] == url('node/add/page'), t('Re-rendered form contains the correct action value.'));
+  }
+}
+
+/**
+ * Test the programmatic form submission behavior.
+ */
+class FormsProgrammaticTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Programmatic form submissions',
+      'description' => 'Test the programmatic form submission behavior.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Test the programmatic form submission workflow.
+   */
+  function testSubmissionWorkflow() {
+    // Backup the current batch status and reset it to avoid conflicts while
+    // processing the dummy form submit handler.
+    $current_batch = $batch =& batch_get();
+    $batch = array();
+
+    // Test that a programmatic form submission is rejected when a required
+    // textfield is omitted and correctly processed when it is provided.
+    $this->submitForm(array(), FALSE);
+    $this->submitForm(array('textfield' => 'test 1'), TRUE);
+    $this->submitForm(array(), FALSE);
+    $this->submitForm(array('textfield' => 'test 2'), TRUE);
+
+    // Test that a programmatic form submission can turn on and off checkboxes
+    // which are, by default, checked.
+    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => 2)), TRUE);
+    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => NULL)), TRUE);
+    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => 2)), TRUE);
+    $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => NULL)), TRUE);
+
+    // Restore the current batch status.
+    $batch = $current_batch;
+  }
+
+  /**
+   * Helper function used to programmatically submit the form defined in
+   * form_test.module with the given values.
+   *
+   * @param $values
+   *   An array of field values to be submitted.
+   * @param $valid_input
+   *   A boolean indicating whether or not the form submission is expected to
+   *   be valid.
+   */
+  private function submitForm($values, $valid_input) {
+    // Programmatically submit the given values.
+    $form_state = array('values' => $values);
+    drupal_form_submit('form_test_programmatic_form', $form_state);
+
+    // Check that the form returns an error when expected, and vice versa.
+    $errors = form_get_errors();
+    $valid_form = empty($errors);
+    $args = array(
+      '%values' => print_r($values, TRUE),
+      '%errors' => $valid_form ? t('None') : implode(' ', $errors),
+    );
+    $this->assertTrue($valid_input == $valid_form, t('Input values: %values<br/>Validation handler errors: %errors', $args));
+
+    // We check submitted values only if we have a valid input.
+    if ($valid_input) {
+      // By fetching the values from $form_state['storage'] we ensure that the
+      // submission handler was properly executed.
+      $stored_values = $form_state['storage']['programmatic_form_submit'];
+      foreach ($values as $key => $value) {
+        $this->assertTrue(isset($stored_values[$key]) && $stored_values[$key] == $value, t('Submission handler correctly executed: %stored_key is %stored_value', array('%stored_key' => $key, '%stored_value' => print_r($value, TRUE))));
+      }
+    }
+  }
+}
+
+/**
+ * Test that FAPI correctly determines $form_state['triggering_element'].
+ */
+class FormsTriggeringElementTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Form triggering element determination',
+      'description' => 'Test the determination of $form_state[\'triggering_element\'].',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Test the determination of $form_state['triggering_element'] when no button
+   * information is included in the POST data, as is sometimes the case when
+   * the ENTER key is pressed in a textfield in Internet Explorer.
+   */
+  function testNoButtonInfoInPost() {
+    $path = 'form-test/clicked-button';
+    $edit = array();
+    $form_html_id = 'form-test-clicked-button';
+
+    // Ensure submitting a form with no buttons results in no
+    // $form_state['triggering_element'] and the form submit handler not
+    // running.
+    $this->drupalPost($path, $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('There is no clicked button.', t('$form_state[\'triggering_element\'] set to NULL.'));
+    $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.'));
+
+    // Ensure submitting a form with one or more submit buttons results in
+    // $form_state['triggering_element'] being set to the first one the user has
+    // access to. An argument with 'r' in it indicates a restricted
+    // (#access=FALSE) button.
+    $this->drupalPost($path . '/s', $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to only button.'));
+    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
+
+    $this->drupalPost($path . '/s/s', $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
+    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
+
+    $this->drupalPost($path . '/rs/s', $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] set to first available button.'));
+    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
+
+    // Ensure submitting a form with buttons of different types results in
+    // $form_state['triggering_element'] being set to the first button,
+    // regardless of type. For the FAPI 'button' type, this should result in the
+    // submit handler not executing. The types are 's'(ubmit), 'b'(utton), and
+    // 'i'(mage_button).
+    $this->drupalPost($path . '/s/b/i', $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
+    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
+
+    $this->drupalPost($path . '/b/s/i', $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
+    $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.'));
+
+    $this->drupalPost($path . '/i/s/b', $edit, NULL, array(), array(), $form_html_id);
+    $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.'));
+    $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.'));
+  }
+
+  /**
+   * Test that $form_state['triggering_element'] does not get set to a button
+   * with #access=FALSE.
+   */
+  function testAttemptAccessControlBypass() {
+    $path = 'form-test/clicked-button';
+    $form_html_id = 'form-test-clicked-button';
+
+    // Retrieve a form where 'button1' has #access=FALSE and 'button2' doesn't.
+    $this->drupalGet($path . '/rs/s');
+
+    // Submit the form with 'button1=button1' in the POST data, which someone
+    // trying to get around security safeguards could easily do. We have to do
+    // a little trickery here, to work around the safeguards in drupalPost(): by
+    // renaming the text field that is in the form to 'button1', we can get the
+    // data we want into $_POST.
+    $elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="text"]');
+    $elements[0]['name'] = 'button1';
+    $this->drupalPost(NULL, array('button1' => 'button1'), NULL, array(), array(), $form_html_id);
+
+    // Ensure that $form_state['triggering_element'] was not set to the
+    // restricted button. Do this with both a negative and positive assertion,
+    // because negative assertions alone can be brittle. See
+    // testNoButtonInfoInPost() for why the triggering element gets set to
+    // 'button2'.
+    $this->assertNoText('The clicked button is button1.', t('$form_state[\'triggering_element\'] not set to a restricted button.'));
+    $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] not set to a restricted button.'));
+  }
+}
+
+/**
+ * Tests rebuilding of arbitrary forms by altering them.
+ */
+class FormsArbitraryRebuildTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Rebuild arbitrary forms',
+      'description' => 'Tests altering forms to be rebuilt so there are multiple steps.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+    // Auto-create a field for testing.
+    $field = array(
+      'field_name' => 'test_multiple',
+      'type' => 'text',
+      'cardinality' => -1,
+      'translatable' => FALSE,
+    );
+    field_create_field($field);
+
+    $instance = array(
+      'entity_type' => 'node',
+      'field_name' => 'test_multiple',
+      'bundle' => 'page',
+      'label' => 'Test a multiple valued field',
+      'widget' => array(
+        'type' => 'text_textfield',
+        'weight' => 0,
+      ),
+    );
+    field_create_instance($instance);
+    variable_set('user_register', USER_REGISTER_VISITORS);
+  }
+
+  /**
+   * Tests a basic rebuild with the user registration form.
+   */
+  function testUserRegistrationRebuild() {
+    $edit = array(
+      'name' => 'foo',
+      'mail' => 'bar@example.com',
+    );
+    $this->drupalPost('user/register', $edit, 'Rebuild');
+    $this->assertText('Form rebuilt.');
+    $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.');
+    $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.');
+  }
+
+  /**
+   * Tests a rebuild caused by a multiple value field.
+   */
+  function testUserRegistrationMultipleField() {
+    $edit = array(
+      'name' => 'foo',
+      'mail' => 'bar@example.com',
+    );
+    $this->drupalPost('user/register', $edit, t('Add another item'), array('query' => array('field' => TRUE)));
+    $this->assertText('Test a multiple valued field', 'Form has been rebuilt.');
+    $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.');
+    $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.');
+  }
+}
+
+/**
+ * Tests form API file inclusion.
+ */
+class FormsFileInclusionTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Form API file inclusion',
+      'description' => 'Tests form API file inclusion.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests loading an include specified in hook_menu().
+   */
+  function testLoadMenuInclude() {
+    $this->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), 'system/ajax', array(), array(), 'form-test-load-include-menu');
+    $this->assertText('Submit callback called.');
+  }
+
+  /**
+   * Tests loading a custom specified inlcude.
+   */
+  function testLoadCustomInclude() {
+    $this->drupalPost('form-test/load-include-custom', array(), t('Save'));
+    $this->assertText('Submit callback called.');
+  }
+}
diff --git a/modules/system/tests/graph.test b/modules/system/tests/graph.test
new file mode 100644
index 0000000..1751fe2
--- /dev/null
+++ b/modules/system/tests/graph.test
@@ -0,0 +1,194 @@
+<?php
+// $Id: graph.test,v 1.7 2010/05/18 18:11:13 dries Exp $
+
+/**
+ * @file
+ * Provides unit tests for graph.inc.
+ */
+
+/**
+ * Unit tests for the graph handling features.
+ */
+class GraphUnitTest extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Graph',
+      'description' => 'Graph handling unit tests.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Test depth-first-search features.
+   */
+  function testDepthFirstSearch() {
+    // Provoke the inclusion of graph.inc.
+    require_once DRUPAL_ROOT . '/includes/graph.inc';
+
+    // The sample graph used is:
+    // 1 --> 2 --> 3     5 ---> 6
+    //       |     ^     ^
+    //       |     |     |
+    //       |     |     |
+    //       +---> 4 <-- 7      8 ---> 9
+    $graph = $this->normalizeGraph(array(
+      1 => array(2),
+      2 => array(3, 4),
+      3 => array(),
+      4 => array(3),
+      5 => array(6),
+      7 => array(4, 5),
+      8 => array(9),
+      9 => array(),
+    ));
+    drupal_depth_first_search($graph);
+
+    $expected_paths = array(
+      1 => array(2, 3, 4),
+      2 => array(3, 4),
+      3 => array(),
+      4 => array(3),
+      5 => array(6),
+      7 => array(4, 3, 5, 6),
+      8 => array(9),
+      9 => array(),
+    );
+    $this->assertPaths($graph, $expected_paths);
+
+    $expected_reverse_paths = array(
+      1 => array(),
+      2 => array(1),
+      3 => array(2, 1, 4, 7),
+      4 => array(2, 1, 7),
+      5 => array(7),
+      7 => array(),
+      8 => array(),
+      9 => array(8),
+    );
+    $this->assertReversePaths($graph, $expected_reverse_paths);
+
+    // Assert that DFS didn't created "missing" vertexes automatically.
+    $this->assertFALSE(isset($graph[6]), t('Vertex 6 has not been created'));
+
+    $expected_components = array(
+      array(1, 2, 3, 4, 5, 7),
+      array(8, 9),
+    );
+    $this->assertComponents($graph, $expected_components);
+
+    $expected_weights = array(
+      array(1, 2, 3),
+      array(2, 4, 3),
+      array(7, 4, 3),
+      array(7, 5),
+      array(8, 9),
+    );
+    $this->assertWeights($graph, $expected_weights);
+  }
+
+  /**
+   * Return a normalized version of a graph.
+   */
+  function normalizeGraph($graph) {
+    $normalized_graph = array();
+    foreach ($graph as $vertex => $edges) {
+      // Create vertex even if it hasn't any edges.
+      $normalized_graph[$vertex] = array();
+      foreach ($edges as $edge) {
+        $normalized_graph[$vertex]['edges'][$edge] = TRUE;
+      }
+    }
+    return $normalized_graph;
+  }
+
+  /**
+   * Verify expected paths in a graph.
+   *
+   * @param $graph
+   *   A graph array processed by drupal_depth_first_search().
+   * @param $expected_paths
+   *   An associative array containing vertices with their expected paths.
+   */
+  function assertPaths($graph, $expected_paths) {
+    foreach ($expected_paths as $vertex => $paths) {
+      // Build an array with keys = $paths and values = TRUE.
+      $expected = array_fill_keys($paths, TRUE);
+      $result = isset($graph[$vertex]['paths']) ? $graph[$vertex]['paths'] : array();
+      $this->assertEqual($expected, $result, t('Expected paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE))));
+    }
+  }
+
+  /**
+   * Verify expected reverse paths in a graph.
+   *
+   * @param $graph
+   *   A graph array processed by drupal_depth_first_search().
+   * @param $expected_reverse_paths
+   *   An associative array containing vertices with their expected reverse
+   *   paths.
+   */
+  function assertReversePaths($graph, $expected_reverse_paths) {
+    foreach ($expected_reverse_paths as $vertex => $paths) {
+      // Build an array with keys = $paths and values = TRUE.
+      $expected = array_fill_keys($paths, TRUE);
+      $result = isset($graph[$vertex]['reverse_paths']) ? $graph[$vertex]['reverse_paths'] : array();
+      $this->assertEqual($expected, $result, t('Expected reverse paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE))));
+    }
+  }
+
+  /**
+   * Verify expected components in a graph.
+   *
+   * @param $graph
+   *   A graph array processed by drupal_depth_first_search().
+   * @param $expected_components
+   *   An array containing of components defined as a list of their vertices.
+   */
+  function assertComponents($graph, $expected_components) {
+    $unassigned_vertices = array_fill_keys(array_keys($graph), TRUE);
+    foreach ($expected_components as $component) {
+      $result_components = array();
+      foreach ($component as $vertex) {
+        $result_components[] = $graph[$vertex]['component'];
+        unset($unassigned_vertices[$vertex]);
+      }
+      $this->assertEqual(1, count(array_unique($result_components)), t('Expected one unique component for vertices @vertices, got @components', array('@vertices' => $this->displayArray($component), '@components' => $this->displayArray($result_components))));
+    }
+    $this->assertEqual(array(), $unassigned_vertices, t('Vertices not assigned to a component: @vertices', array('@vertices' => $this->displayArray($unassigned_vertices, TRUE))));
+  }
+
+  /**
+   * Verify expected order in a graph.
+   *
+   * @param $graph
+   *   A graph array processed by drupal_depth_first_search().
+   * @param $expected_orders
+   *   An array containing lists of vertices in their expected order.
+   */
+  function assertWeights($graph, $expected_orders) {
+    foreach ($expected_orders as $order) {
+      $previous_vertex = array_shift($order);
+      foreach ($order as $vertex) {
+        $this->assertTrue($graph[$previous_vertex]['weight'] < $graph[$vertex]['weight'], t('Weights of @previous-vertex and @vertex are correct relative to each other', array('@previous-vertex' => $previous_vertex, '@vertex' => $vertex)));
+      }
+    }
+  }
+
+  /**
+   * Helper function to output vertices as comma-separated list.
+   *
+   * @param $paths
+   *   An array containing a list of vertices.
+   * @param $keys
+   *   (optional) Whether to output the keys of $paths instead of the values.
+   */
+  function displayArray($paths, $keys = FALSE) {
+    if (!empty($paths)) {
+      return implode(', ', $keys ? array_keys($paths) : $paths);
+    }
+    else {
+      return '(empty)';
+    }
+  }
+}
+
diff --git a/modules/system/tests/image.test b/modules/system/tests/image.test
new file mode 100644
index 0000000..d1b7fc7
--- /dev/null
+++ b/modules/system/tests/image.test
@@ -0,0 +1,461 @@
+<?php
+// $Id: image.test,v 1.13 2010/04/23 05:10:35 webchick Exp $
+
+/**
+ * @file
+ * Tests for core image handling API.
+ */
+
+/**
+ * Base class for image manipulation testing.
+ */
+class ImageToolkitTestCase extends DrupalWebTestCase {
+  protected $toolkit;
+  protected $file;
+  protected $image;
+
+  function setUp() {
+    parent::setUp('image_test');
+
+    // Use the image_test.module's test toolkit.
+    $this->toolkit = 'test';
+
+    // Pick a file for testing.
+    $file = current($this->drupalGetTestFiles('image'));
+    $this->file = $file->uri;
+
+    // Setup a dummy image to work with, this replicate image_load() so we
+    // can avoid calling it.
+    $this->image = new stdClass();
+    $this->image->source = $this->file;
+    $this->image->info = image_get_info($this->file);
+    $this->image->toolkit = $this->toolkit;
+
+    // Clear out any hook calls.
+    image_test_reset();
+  }
+
+  /**
+   * Assert that all of the specified image toolkit operations were called
+   * exactly once once, other values result in failure.
+   *
+   * @param $expected
+   *   Array with string containing with the operation name, e.g. 'load',
+   *   'save', 'crop', etc.
+   */
+  function assertToolkitOperationsCalled(array $expected) {
+    // Determine which operations were called.
+    $actual = array_keys(array_filter(image_test_get_all_calls()));
+
+    // Determine if there were any expected that were not called.
+    $uncalled = array_diff($expected, $actual);
+    if (count($uncalled)) {
+      $this->assertTrue(FALSE, t('Expected operations %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled))));
+    }
+    else {
+      $this->assertTrue(TRUE, t('All the expected operations were called: %expected', array('%expected' => implode(', ', $expected))));
+    }
+
+    // Determine if there were any unexpected calls.
+    $unexpected = array_diff($actual, $expected);
+    if (count($unexpected)) {
+      $this->assertTrue(FALSE, t('Unexpected operations were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected))));
+    }
+    else {
+      $this->assertTrue(TRUE, t('No unexpected operations were called.'));
+    }
+  }
+}
+
+/**
+ * Test that the functions in image.inc correctly pass data to the toolkit.
+ */
+class ImageToolkitUnitTest extends ImageToolkitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Image toolkit tests',
+      'description' => 'Check image toolkit functions.',
+      'group' => 'Image',
+    );
+  }
+
+  /**
+   * Check that hook_image_toolkits() is called and only available toolkits are
+   * returned.
+   */
+  function testGetAvailableToolkits() {
+    $toolkits = image_get_available_toolkits();
+    $this->assertTrue(isset($toolkits['test']), t('The working toolkit was returned.'));
+    $this->assertFalse(isset($toolkits['broken']), t('The toolkit marked unavailable was not returned'));
+    $this->assertToolkitOperationsCalled(array());
+  }
+
+  /**
+   * Test the image_load() function.
+   */
+  function testLoad() {
+    $image = image_load($this->file, $this->toolkit);
+    $this->assertTrue(is_object($image), t('Returned an object.'));
+    $this->assertEqual($this->toolkit, $image->toolkit, t('Image had toolkit set.'));
+    $this->assertToolkitOperationsCalled(array('load', 'get_info'));
+  }
+
+  /**
+   * Test the image_save() function.
+   */
+  function testSave() {
+    $this->assertFalse(image_save($this->image), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('save'));
+  }
+
+  /**
+   * Test the image_resize() function.
+   */
+  function testResize() {
+    $this->assertTrue(image_resize($this->image, 1, 2), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('resize'));
+
+    // Check the parameters.
+    $calls = image_test_get_all_calls();
+    $this->assertEqual($calls['resize'][0][1], 1, t('Width was passed correctly'));
+    $this->assertEqual($calls['resize'][0][2], 2, t('Height was passed correctly'));
+  }
+
+  /**
+   * Test the image_scale() function.
+   */
+  function testScale() {
+// TODO: need to test upscaling
+    $this->assertTrue(image_scale($this->image, 10, 10), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('resize'));
+
+    // Check the parameters.
+    $calls = image_test_get_all_calls();
+    $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly'));
+    $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly'));
+  }
+
+  /**
+   * Test the image_scale_and_crop() function.
+   */
+  function testScaleAndCrop() {
+    $this->assertTrue(image_scale_and_crop($this->image, 5, 10), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('resize', 'crop'));
+
+    // Check the parameters.
+    $calls = image_test_get_all_calls();
+
+    $this->assertEqual($calls['crop'][0][1], 7.5, t('X was computed and passed correctly'));
+    $this->assertEqual($calls['crop'][0][2], 0, t('Y was computed and passed correctly'));
+    $this->assertEqual($calls['crop'][0][3], 5, t('Width was computed and passed correctly'));
+    $this->assertEqual($calls['crop'][0][4], 10, t('Height was computed and passed correctly'));
+  }
+
+  /**
+   * Test the image_rotate() function.
+   */
+  function testRotate() {
+    $this->assertTrue(image_rotate($this->image, 90, 1), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('rotate'));
+
+    // Check the parameters.
+    $calls = image_test_get_all_calls();
+    $this->assertEqual($calls['rotate'][0][1], 90, t('Degrees were passed correctly'));
+    $this->assertEqual($calls['rotate'][0][2], 1, t('Background color was passed correctly'));
+  }
+
+  /**
+   * Test the image_crop() function.
+   */
+  function testCrop() {
+    $this->assertTrue(image_crop($this->image, 1, 2, 3, 4), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('crop'));
+
+    // Check the parameters.
+    $calls = image_test_get_all_calls();
+    $this->assertEqual($calls['crop'][0][1], 1, t('X was passed correctly'));
+    $this->assertEqual($calls['crop'][0][2], 2, t('Y was passed correctly'));
+    $this->assertEqual($calls['crop'][0][3], 3, t('Width was passed correctly'));
+    $this->assertEqual($calls['crop'][0][4], 4, t('Height was passed correctly'));
+  }
+
+  /**
+   * Test the image_desaturate() function.
+   */
+  function testDesaturate() {
+    $this->assertTrue(image_desaturate($this->image), t('Function returned the expected value.'));
+    $this->assertToolkitOperationsCalled(array('desaturate'));
+
+    // Check the parameters.
+    $calls = image_test_get_all_calls();
+    $this->assertEqual(count($calls['desaturate'][0]), 1, t('Only the image was passed.'));
+  }
+}
+
+/**
+ * Test the core GD image manipulation functions.
+ */
+class ImageToolkitGdTestCase extends DrupalWebTestCase {
+  // Colors that are used in testing.
+  protected $black       = array(0, 0, 0, 0);
+  protected $red         = array(255, 0, 0, 0);
+  protected $green       = array(0, 255, 0, 0);
+  protected $blue        = array(0, 0, 255, 0);
+  protected $yellow      = array(255, 255, 0, 0);
+  protected $fuchsia     = array(255, 0, 255, 0); // Used as background colors.
+  protected $transparent = array(0, 0, 0, 127);
+  protected $white       = array(255, 255, 255, 0);
+
+  protected $width = 40;
+  protected $height = 20;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Image GD manipulation tests',
+      'description' => 'Check that core image manipulations work properly: scale, resize, rotate, crop, scale and crop, and desaturate.',
+      'group' => 'Image',
+    );
+  }
+
+  /**
+   * Function to compare two colors by RGBa.
+   */
+  function colorsAreEqual($color_a, $color_b) {
+    // Fully transparent pixels are equal, regardless of RGB.
+    if ($color_a[3] == 127 && $color_b[3] == 127) {
+      return TRUE;
+    }
+
+    foreach ($color_a as $key => $value) {
+      if ($color_b[$key] != $value) {
+        return FALSE;
+      }
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Function for finding a pixel's RGBa values.
+   */
+  function getPixelColor($image, $x, $y) {
+    $color_index = imagecolorat($image->resource, $x, $y);
+
+    $transparent_index = imagecolortransparent($image->resource);
+    if ($color_index == $transparent_index) {
+      return array(0, 0, 0, 127);
+    }
+
+    return array_values(imagecolorsforindex($image->resource, $color_index));
+  }
+
+  /**
+   * Since PHP can't visually check that our images have been manipulated
+   * properly, build a list of expected color values for each of the corners and
+   * the expected height and widths for the final images.
+   */
+  function testManipulations() {
+    // If GD isn't available don't bother testing this.
+    if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
+      $this->pass(t('Image manipulations for the GD toolkit were skipped because the GD toolkit is not available.'));
+      return;
+    }
+
+    // Typically the corner colors will be unchanged. These colors are in the
+    // order of top-left, top-right, bottom-right, bottom-left.
+    $default_corners = array($this->red, $this->green, $this->blue, $this->transparent);
+
+    // A list of files that will be tested.
+    $files = array(
+      'image-test.png',
+      'image-test.gif',
+      'image-test.jpg',
+    );
+
+    // Setup a list of tests to perform on each type.
+    $operations = array(
+      'resize' => array(
+        'function' => 'resize',
+        'arguments' => array(20, 10),
+        'width' => 20,
+        'height' => 10,
+        'corners' => $default_corners,
+      ),
+      'scale_x' => array(
+        'function' => 'scale',
+        'arguments' => array(20, NULL),
+        'width' => 20,
+        'height' => 10,
+        'corners' => $default_corners,
+      ),
+      'scale_y' => array(
+        'function' => 'scale',
+        'arguments' => array(NULL, 10),
+        'width' => 20,
+        'height' => 10,
+        'corners' => $default_corners,
+      ),
+      'upscale_x' => array(
+        'function' => 'scale',
+        'arguments' => array(80, NULL, TRUE),
+        'width' => 80,
+        'height' => 40,
+        'corners' => $default_corners,
+      ),
+      'upscale_y' => array(
+        'function' => 'scale',
+        'arguments' => array(NULL, 40, TRUE),
+        'width' => 80,
+        'height' => 40,
+        'corners' => $default_corners,
+      ),
+      'crop' => array(
+        'function' => 'crop',
+        'arguments' => array(12, 4, 16, 12),
+        'width' => 16,
+        'height' => 12,
+        'corners' => array_fill(0, 4, $this->white),
+      ),
+      'scale_and_crop' => array(
+        'function' => 'scale_and_crop',
+        'arguments' => array(10, 8),
+        'width' => 10,
+        'height' => 8,
+        'corners' => array_fill(0, 4, $this->black),
+      ),
+    );
+
+    // Systems using non-bundled GD2 don't have imagerotate. Test if available.
+    if (function_exists('imagerotate')) {
+      $operations += array(
+        'rotate_5' => array(
+          'function' => 'rotate',
+          'arguments' => array(5, 0xFF00FF), // Fuchsia background.
+          'width' => 42,
+          'height' => 24,
+          'corners' => array_fill(0, 4, $this->fuchsia),
+        ),
+        'rotate_90' => array(
+          'function' => 'rotate',
+          'arguments' => array(90, 0xFF00FF), // Fuchsia background.
+          'width' => 20,
+          'height' => 40,
+          'corners' => array($this->fuchsia, $this->red, $this->green, $this->blue),
+        ),
+        'rotate_transparent_5' => array(
+          'function' => 'rotate',
+          'arguments' => array(5),
+          'width' => 42,
+          'height' => 24,
+          'corners' => array_fill(0, 4, $this->transparent),
+        ),
+        'rotate_transparent_90' => array(
+          'function' => 'rotate',
+          'arguments' => array(90),
+          'width' => 20,
+          'height' => 40,
+          'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
+        ),
+      );
+    }
+
+    // Systems using non-bundled GD2 don't have imagefilter. Test if available.
+    if (function_exists('imagefilter')) {
+      $operations += array(
+        'desaturate' => array(
+          'function' => 'desaturate',
+          'arguments' => array(),
+          'height' => 20,
+          'width' => 40,
+          // Grayscale corners are a bit funky. Each of the corners are a shade of
+          // gray. The values of these were determined simply by looking at the
+          // final image to see what desaturated colors end up being.
+          'corners' => array(
+            array_fill(0, 3, 76) + array(3 => 0),
+            array_fill(0, 3, 149) + array(3 => 0),
+            array_fill(0, 3, 29) + array(3 => 0),
+            array_fill(0, 3, 0) + array(3 => 127)
+          ),
+        ),
+      );
+    }
+
+    foreach ($files as $file) {
+      foreach ($operations as $op => $values) {
+        // Load up a fresh image.
+        $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd');
+        if (!$image) {
+          $this->fail(t('Could not load image %file.', array('%file' => $file)));
+          continue 2;
+        }
+
+        // Transparent GIFs and the imagefilter function don't work together.
+        // There is a todo in image.gd.inc to correct this.
+        if ($image->info['extension'] == 'gif') {
+          if ($op == 'desaturate') {
+            $values['corners'][3] = $this->white;
+          }
+        }
+
+        // Perform our operation.
+        $function = 'image_' . $values['function'];
+        $arguments = array();
+        $arguments[] = &$image;
+        $arguments = array_merge($arguments, $values['arguments']);
+        call_user_func_array($function, $arguments);
+
+        // To keep from flooding the test with assert values, make a general
+        // value for whether each group of values fail.
+        $correct_dimensions_real = TRUE;
+        $correct_dimensions_object = TRUE;
+        $correct_colors = TRUE;
+
+        // Check the real dimensions of the image first.
+        if (imagesy($image->resource) != $values['height'] || imagesx($image->resource) != $values['width']) {
+          $correct_dimensions_real = FALSE;
+        }
+
+        // Check that the image object has an accurate record of the dimensions.
+        if ($image->info['width'] != $values['width'] || $image->info['height'] != $values['height']) {
+          $correct_dimensions_object = FALSE;
+        }
+        // Now check each of the corners to ensure color correctness.
+        foreach ($values['corners'] as $key => $corner) {
+          // Get the location of the corner.
+          switch ($key) {
+            case 0:
+              $x = 0;
+              $y = 0;
+              break;
+            case 1:
+              $x = $values['width'] - 1;
+              $y = 0;
+              break;
+            case 2:
+              $x = $values['width'] - 1;
+              $y = $values['height'] - 1;
+              break;
+            case 3:
+              $x = 0;
+              $y = $values['height'] - 1;
+              break;
+          }
+          $color = $this->getPixelColor($image, $x, $y);
+          $correct_colors = $this->colorsAreEqual($color, $corner);
+        }
+
+        $directory = file_directory_path() . '/imagetests';
+        file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
+        image_save($image, $directory . '/' . $op . '.' . $image->info['extension']);
+
+        $this->assertTrue($correct_dimensions_real, t('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
+        $this->assertTrue($correct_dimensions_object, t('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
+        // JPEG colors will always be messed up due to compression.
+        if ($image->info['extension'] != 'jpg') {
+          $this->assertTrue($correct_colors, t('Image %file object after %action action has the correct color placement.', array('%file' => $file, '%action' => $op)));
+        }
+      }
+    }
+
+  }
+}
diff --git a/modules/system/tests/lock.test b/modules/system/tests/lock.test
new file mode 100644
index 0000000..5df032b
--- /dev/null
+++ b/modules/system/tests/lock.test
@@ -0,0 +1,58 @@
+<?php
+// $Id: lock.test,v 1.1 2009/08/17 20:32:30 dries Exp $
+
+/**
+ * Tests for the lock system.
+ */
+class LockFunctionalTest extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Locking framework tests',
+      'description' => 'Confirm locking works between two separate requests.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('system_test');
+  }
+
+  /**
+   * Confirm that we can acquire and release locks in two parallel requests.
+   */
+  function testLockAcquire() {
+    $lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
+    $lock_not_acquired = 'FALSE: Lock not acquired in system_test_lock_acquire()';
+    $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
+    $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock extended by this request.'), t('Lock'));
+    lock_release('system_test_lock_acquire');
+
+    // Cause another request to acquire the lock.
+    $this->drupalGet('system-test/lock-acquire');
+    $this->assertText($lock_acquired, t('Lock acquired by the other request.'), t('Lock'));
+    // The other request has finished, thus it should have released its lock.
+    $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
+    // This request holds the lock, so the other request cannot acquire it.
+    $this->drupalGet('system-test/lock-acquire');
+    $this->assertText($lock_not_acquired, t('Lock not acquired by the other request.'), t('Lock'));
+    lock_release('system_test_lock_acquire');
+
+    // Try a very short timeout and lock breaking.
+    $this->assertTrue(lock_acquire('system_test_lock_acquire', 0.5), t('Lock acquired by this request.'), t('Lock'));
+    sleep(1);
+    // The other request should break our lock.
+    $this->drupalGet('system-test/lock-acquire');
+    $this->assertText($lock_acquired, t('Lock acquired by the other request, breaking our lock.'), t('Lock'));
+    // We cannot renew it, since the other thread took it.
+    $this->assertFalse(lock_acquire('system_test_lock_acquire'), t('Lock cannot be extended by this request.'), t('Lock'));
+
+    // Check the shut-down function.
+    $lock_acquired_exit = 'TRUE: Lock successfully acquired in system_test_lock_exit()';
+    $lock_not_acquired_exit = 'FALSE: Lock not acquired in system_test_lock_exit()';
+    $this->drupalGet('system-test/lock-exit');
+    $this->assertText($lock_acquired_exit, t('Lock acquired by the other request before exit.'), t('Lock'));
+    $this->assertTrue(lock_acquire('system_test_lock_exit'), t('Lock acquired by this request after the other request exits.'), t('Lock'));
+  }
+}
+
diff --git a/modules/system/tests/mail.test b/modules/system/tests/mail.test
new file mode 100644
index 0000000..f5cb76a
--- /dev/null
+++ b/modules/system/tests/mail.test
@@ -0,0 +1,66 @@
+<?php
+// $Id: mail.test,v 1.3 2010/04/11 18:33:44 dries Exp $
+
+/**
+ * Test the Drupal mailing system.
+ */
+class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
+  /**
+   * The most recent message that was sent through the test case.
+   *
+   * We take advantage here of the fact that static variables are shared among
+   * all instance of the same class.
+   */
+  private static $sent_message;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Mail system',
+      'description' => 'Performs tests on the pluggable mailing framework.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    // Set MailTestCase (i.e. this class) as the SMTP library
+    variable_set('mail_system', array('default-system' => 'MailTestCase'));
+  }
+
+  /**
+   * Assert that the pluggable mail system is functional.
+   */
+  function testPluggableFramework() {
+    global $language;
+
+    // Use MailTestCase for sending a message.
+    $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language);
+
+    // Assert whether the message was sent through the send function.
+    $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.'));
+  }
+
+  /**
+   * Concatenate and wrap the e-mail body for plain-text mails.
+   *
+   * @see DefaultMailSystem
+   */
+  public function format(array $message) {
+    // Join the body array into one string.
+    $message['body'] = implode("\n\n", $message['body']);
+    // Convert any HTML to plain-text.
+    $message['body'] = drupal_html_to_text($message['body']);
+    // Wrap the mail body for sending.
+    $message['body'] = drupal_wrap_mail($message['body']);
+    return $message;
+  }
+
+  /**
+   * Send function that is called through the mail system.
+   */
+  public function mail(array $message) {
+    self::$sent_message = $message;
+  }
+}
+
diff --git a/modules/system/tests/menu.test b/modules/system/tests/menu.test
new file mode 100644
index 0000000..b108d06
--- /dev/null
+++ b/modules/system/tests/menu.test
@@ -0,0 +1,527 @@
+<?php
+// $Id: menu.test,v 1.31 2010/07/08 03:41:27 webchick Exp $
+
+/**
+ * @file
+ * Provides SimpleTests for menu.inc.
+ */
+
+class MenuRouterTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Menu router',
+      'description' => 'Tests menu router and hook_menu() functionality.',
+      'group' => 'Menu',
+    );
+  }
+
+  function setUp() {
+    // Enable dummy module that implements hook_menu.
+    parent::setUp('menu_test');
+    // Make the tests below more robust by explicitly setting the default theme
+    // and administrative theme that they expect.
+    theme_enable(array('garland'));
+    variable_set('theme_default', 'garland');
+    variable_set('admin_theme', 'seven');
+  }
+
+  /**
+   * Test title callback set to FALSE.
+   */
+  function testTitleCallbackFalse() {
+    $this->drupalGet('node');
+    $this->assertText('A title with @placeholder', t('Raw text found on the page'));
+    $this->assertNoText(t('A title with @placeholder', array('@placeholder' => 'some other text')), t('Text with placeholder substitutions not found.'));
+  }
+
+  /**
+   * Test the theme callback when it is set to use an administrative theme.
+   */
+  function testThemeCallbackAdministrative() {
+    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
+    $this->assertText('Custom theme: seven. Actual theme: seven.', t('The administrative theme can be correctly set in a theme callback.'));
+    $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
+  }
+
+  /**
+   * Test that the theme callback is properly inherited.
+   */
+  function testThemeCallbackInheritance() {
+    $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance');
+    $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', t('Theme callback inheritance correctly uses the administrative theme.'));
+    $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
+  }
+
+  /**
+   * Test that 'page callback', 'file' and 'file path' keys are properly
+   * inherited from parent menu paths.
+   */
+  function testFileInheritance() {
+    $this->drupalGet('admin/config/development/file-inheritance');
+    $this->assertText('File inheritance test description', t('File inheritance works.'));
+  }
+
+  /**
+   * Test path containing "exotic" characters.
+   */
+  function testExoticPath() {
+    $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
+      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
+      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
+    $this->drupalGet($path);
+    $this->assertRaw('This is menu_test_callback().');
+  }
+
+  /**
+   * Test the theme callback when the site is in maintenance mode.
+   */
+  function testThemeCallbackMaintenanceMode() {
+    variable_set('maintenance_mode', TRUE);
+
+    // For a regular user, the fact that the site is in maintenance mode means
+    // we expect the theme callback system to be bypassed entirely.
+    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
+    $this->assertRaw('garland/style.css', t("The maintenance theme's CSS appears on the page."));
+
+    // An administrator, however, should continue to see the requested theme.
+    $admin_user = $this->drupalCreateUser(array('access site in maintenance mode'));
+    $this->drupalLogin($admin_user);
+    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
+    $this->assertText('Custom theme: seven. Actual theme: seven.', t('The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.'));
+    $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
+  }
+
+  /**
+   * Make sure the maintenance mode can be bypassed using hook_menu_site_status_alter().
+   *
+   * @see hook_menu_site_status_alter().
+   */
+  function testMaintenanceModeLoginPaths() {
+    variable_set('maintenance_mode', TRUE);
+
+    $offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')));
+    $this->drupalLogout();
+    $this->drupalGet('node');
+    $this->assertText($offline_message);
+    $this->drupalGet('menu_login_callback');
+    $this->assertText('This is menu_login_callback().', t('Maintenance mode can be bypassed through hook_login_paths().'));
+  }
+
+  /**
+   * Test that an authenticated user hitting 'user/login' gets redirected to
+   * 'user' and 'user/register' gets redirected to the user edit page.
+   */
+  function testAuthUserUserLogin() {
+    $loggedInUser = $this->drupalCreateUser(array());
+    $this->drupalLogin($loggedInUser);
+
+    $this->DrupalGet('user/login');
+    // Check that we got to 'user'.
+    $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to q=user on accessing q=user/login"));
+
+    // user/register should redirect to user/UID/edit.
+    $this->DrupalGet('user/register');
+    $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to q=user/UID/edit on accessing q=user/register"));
+  }
+
+  /**
+   * Test the theme callback when it is set to use an optional theme.
+   */
+  function testThemeCallbackOptionalTheme() {
+    // Request a theme that is not enabled.
+    $this->drupalGet('menu-test/theme-callback/use-stark-theme');
+    $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when a theme that is not enabled is requested.'));
+    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page."));
+
+    // Now enable the theme and request it again.
+    theme_enable(array('stark'));
+    $this->drupalGet('menu-test/theme-callback/use-stark-theme');
+    $this->assertText('Custom theme: stark. Actual theme: stark.', t('The theme callback system uses an optional theme once it has been enabled.'));
+    $this->assertRaw('stark/layout.css', t("The optional theme's CSS appears on the page."));
+  }
+
+  /**
+   * Test the theme callback when it is set to use a theme that does not exist.
+   */
+  function testThemeCallbackFakeTheme() {
+    $this->drupalGet('menu-test/theme-callback/use-fake-theme');
+    $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when a theme that does not exist is requested.'));
+    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page."));
+  }
+
+  /**
+   * Test the theme callback when no theme is requested.
+   */
+  function testThemeCallbackNoThemeRequested() {
+    $this->drupalGet('menu-test/theme-callback/no-theme-requested');
+    $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when no theme is requested.'));
+    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page."));
+  }
+
+  /**
+   * Test that the result of hook_custom_theme() overrides the theme callback.
+   */
+  function testHookCustomTheme() {
+    // Trigger hook_custom_theme() to dynamically request the Stark theme for
+    // the requested page.
+    variable_set('menu_test_hook_custom_theme_name', 'stark');
+
+    // Request a page whose theme callback returns the Seven theme. Since Stark
+    // is not a currently enabled theme, our above request should be ignored,
+    // and Seven should still be used.
+    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
+    $this->assertText('Custom theme: seven. Actual theme: seven.', t('The result of hook_custom_theme() does not override a theme callback when it returns a theme that is not enabled.'));
+    $this->assertRaw('seven/style.css', t("The Seven theme's CSS appears on the page."));
+
+    // Now enable the Stark theme and request the same page as above. This
+    // time, we expect hook_custom_theme() to prevail.
+    theme_enable(array('stark'));
+    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
+    $this->assertText('Custom theme: stark. Actual theme: stark.', t('The result of hook_custom_theme() overrides what was set in a theme callback.'));
+    $this->assertRaw('stark/layout.css', t("The Stark theme's CSS appears on the page."));
+  }
+
+  /**
+   * Tests for menu_link_maintain().
+   */
+  function testMenuLinkMaintain() {
+    $admin_user = $this->drupalCreateUser(array('administer site configuration'));
+    $this->drupalLogin($admin_user);
+
+    // Create three menu items.
+    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1');
+    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1-1');
+    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/2', 'Menu link #2');
+
+    // Move second link to the main-menu, to test caching later on.
+    db_update('menu_links')
+      ->fields(array('menu_name' => 'main-menu'))
+      ->condition('link_title', 'Menu link #1-1')
+      ->condition('customized', 0)
+      ->condition('module', 'menu_test')
+      ->execute();
+    menu_cache_clear('main-menu');
+
+    // Load front page.
+    $this->drupalGet('node');
+    $this->assertLink(t('Menu link #1'), 0, 'Found menu link #1');
+    $this->assertLink(t('Menu link #1-1'), 0, 'Found menu link #1-1');
+    $this->assertLink(t('Menu link #2'), 0, 'Found menu link #2');
+
+    // Rename all links for the given path.
+    menu_link_maintain('menu_test', 'update', 'menu_test_maintain/1', 'Menu link updated');
+    // Load a different page to be sure that we have up to date information.
+    $this->drupalGet('menu_test_maintain/1');
+    $this->assertLink(t('Menu link updated'), 0, t('Found updated menu link'));
+    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1'));
+    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1'));
+    $this->assertLink(t('Menu link #2'), 0, t('Found menu link #2'));
+
+    // Delete all links for the given path.
+    menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/1', '');
+    // Load a different page to be sure that we have up to date information.
+    $this->drupalGet('menu_test_maintain/2');
+    $this->assertNoLink(t('Menu link updated'), 0, t('Not found deleted menu link'));
+    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1'));
+    $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1'));
+    $this->assertLink(t('Menu link #2'), 0, t('Found menu link #2'));
+  }
+
+  /**
+   * Test menu_get_names().
+   */
+  function testMenuGetNames() {
+    // Create three menu items.
+    for ($i = 0; $i < 3; $i++) {
+      $menu_link = array(
+        'link_title' => 'Menu link #' . $i,
+        'link_path' => 'menu_test/' . $i,
+        'module' => 'menu_test',
+        'menu_name' => 'menu_test_' . $i,
+      );
+      menu_link_save($menu_link);
+    }
+
+    drupal_static_reset('menu_get_names');
+
+    // Verify that the menu names are correctly reported by menu_get_names().
+    $menu_names = menu_get_names();
+    $this->pass(implode(' | ', $menu_names));
+    for ($i = 0; $i < 3; $i++) {
+      $this->assertTrue(in_array('menu_test_' . $i, $menu_names), t('Expected menu name %expected is returned.', array('%expected' => 'menu_test_' . $i)));
+    }
+  }
+
+  /**
+   * Tests for menu_name parameter for hook_menu().
+   */
+  function testMenuName() {
+    $admin_user = $this->drupalCreateUser(array('administer site configuration'));
+    $this->drupalLogin($admin_user);
+
+    $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
+    $name = db_query($sql)->fetchField();
+    $this->assertEqual($name, 'original', t('Menu name is "original".'));
+
+    // Change the menu_name parameter in menu_test.module, then force a menu
+    // rebuild.
+    menu_test_menu_name('changed');
+    menu_rebuild();
+
+    $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
+    $name = db_query($sql)->fetchField();
+    $this->assertEqual($name, 'changed', t('Menu name was successfully changed after rebuild.'));
+  }
+
+  /**
+   * Tests for menu hierarchy.
+   */
+  function testMenuHierarchy() {
+    $parent_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent'))->fetchAssoc();
+    $child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child'))->fetchAssoc();
+    $unattached_child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child2/child'))->fetchAssoc();
+
+    $this->assertEqual($child_link['plid'], $parent_link['mlid'], t('The parent of a directly attached child is correct.'));
+    $this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], t('The parent of a non-directly attached child is correct.'));
+  }
+
+  /**
+   * Tests menu link depth and parents of local tasks and menu callbacks.
+   */
+  function testMenuHidden() {
+    // Verify links for one dynamic argument.
+    $links = db_select('menu_links', 'ml')
+      ->fields('ml')
+      ->condition('ml.router_path', 'menu-test/hidden/menu%', 'LIKE')
+      ->orderBy('ml.router_path')
+      ->execute()
+      ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
+
+    $parent = $links['menu-test/hidden/menu'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/menu/list'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/add'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/settings'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $parent = $links['menu-test/hidden/menu/manage/%'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/menu/manage/%/list'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%/add'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%/edit'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%/delete'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    // Verify links for two dynamic arguments.
+    $links = db_select('menu_links', 'ml')
+      ->fields('ml')
+      ->condition('ml.router_path', 'menu-test/hidden/block%', 'LIKE')
+      ->orderBy('ml.router_path')
+      ->execute()
+      ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
+
+    $parent = $links['menu-test/hidden/block'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/block/list'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/block/add'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/block/manage/%/%'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $parent = $links['menu-test/hidden/block/manage/%/%'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/block/manage/%/%/configure'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/block/manage/%/%/delete'];
+    $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+  }
+
+  /**
+   * Test menu_set_item().
+   */
+  function testMenuSetItem() {
+    $item = menu_get_item('node');
+
+    $this->assertEqual($item['path'], 'node', t("Path from menu_get_item('node') is equal to 'node'"), 'menu');
+
+    // Modify the path for the item then save it.
+    $item['path'] = 'node_test';
+    $item['href'] = 'node_test';
+
+    menu_set_item('node', $item);
+    $compare_item = menu_get_item('node');
+    $this->assertEqual($compare_item, $item, t('Modified menu item is equal to newly retrieved menu item.'), 'menu');
+  }
+
+  /**
+   * Test menu maintainance hooks.
+   */
+  function testMenuItemHooks() {
+    // Create an item.
+    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/4', 'Menu link #4');
+    $this->assertEqual(menu_test_static_variable(), 'insert', t('hook_menu_link_insert() fired correctly'));
+    // Update the item.
+    menu_link_maintain('menu_test', 'update', 'menu_test_maintain/4', 'Menu link updated');
+    $this->assertEqual(menu_test_static_variable(), 'update', t('hook_menu_link_update() fired correctly'));
+    // Delete the item.
+    menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/4', '');
+    $this->assertEqual(menu_test_static_variable(), 'delete', t('hook_menu_link_delete() fired correctly'));
+  }
+
+  /**
+   * Test menu link 'options' storage and rendering.
+   */
+  function testMenuLinkOptions() {
+    // Create a menu link with options.
+    $menu_link = array(
+      'link_title' => 'Menu link options test',
+      'link_path' => 'node',
+      'module' => 'menu_test',
+      'options' => array(
+        'attributes' => array(
+          'title' => 'Test title attribute',
+        ),
+        'query' => array(
+          'testparam' => 'testvalue',
+        ),
+      ),
+    );
+    menu_link_save($menu_link);
+
+    // Load front page.
+    $this->drupalGet('node');
+    $this->assertRaw('title="Test title attribute"', t('Title attribute of a menu link renders.'));
+    $this->assertRaw('testparam=testvalue', t('Query parameter added to menu link.'));
+  }
+}
+
+/**
+ * Tests rebuilding the menu by setting 'menu_rebuild_needed.'
+ */
+class MenuRebuildTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Menu rebuild test',
+      'description' => 'Test rebuilding of menu.',
+      'group' => 'Menu',
+    );
+  }
+
+  /**
+   * Test if the 'menu_rebuild_needed' variable triggers a menu_rebuild() call.
+   */
+  function testMenuRebuildByVariable() {
+    // Check if 'admin' path exists.
+    $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
+    $this->assertEqual($admin_exists, 'admin', t("The path 'admin/' exists prior to deleting."));
+
+    // Delete the path item 'admin', and test that the path doesn't exist in the database.
+    $delete = db_delete('menu_router')
+      ->condition('path', 'admin')
+      ->execute();
+    $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
+    $this->assertFalse($admin_exists, t("The path 'admin/' has been deleted and doesn't exist in the database."));
+
+    // Now we enable the rebuild variable and trigger menu_execute_active_handler()
+    // to rebuild the menu item. Now 'admin' should exist.
+    variable_set('menu_rebuild_needed', TRUE);
+    // menu_execute_active_handler() should trigger the rebuild.
+    $this->drupalGet('<front>');
+    $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
+    $this->assertEqual($admin_exists, 'admin', t("The menu has been rebuilt, the path 'admin' now exists again."));
+  }
+
+}
+
+/**
+ * Menu tree data related tests.
+ */
+class MenuTreeDataTestCase extends DrupalUnitTestCase {
+  /**
+   * Dummy link structure acceptable for menu_tree_data().
+   */
+  var $links = array(
+    1 => array('mlid' => 1, 'depth' => 1),
+    2 => array('mlid' => 2, 'depth' => 1),
+    3 => array('mlid' => 3, 'depth' => 2),
+    4 => array('mlid' => 4, 'depth' => 3),
+    5 => array('mlid' => 5, 'depth' => 1),
+  );
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Menu tree generation',
+      'description' => 'Tests recursive menu tree generation functions.',
+      'group' => 'Menu',
+    );
+  }
+
+  /**
+   * Validate the generation of a proper menu tree hierarchy.
+   */
+  function testMenuTreeData() {
+    $tree = menu_tree_data($this->links);
+
+    // Validate that parent items #1, #2, and #5 exist on the root level.
+    $this->assertSameLink($this->links[1], $tree[1]['link'], t('Parent item #1 exists.'));
+    $this->assertSameLink($this->links[2], $tree[2]['link'], t('Parent item #2 exists.'));
+    $this->assertSameLink($this->links[5], $tree[5]['link'], t('Parent item #5 exists.'));
+
+    // Validate that child item #4 exists at the correct location in the hierarchy.
+    $this->assertSameLink($this->links[4], $tree[2]['below'][3]['below'][4]['link'], t('Child item #4 exists in the hierarchy.'));
+  }
+
+  /**
+   * Check that two menu links are the same by comparing the mlid.
+   *
+   * @param $link1
+   *   A menu link item.
+   * @param $link2
+   *   A menu link item.
+   * @param $message
+   *   The message to display along with the assertion.
+   * @return
+   *   TRUE if the assertion succeeded, FALSE otherwise.
+   */
+  protected function assertSameLink($link1, $link2, $message = '') {
+    return $this->assert($link1['mlid'] == $link2['mlid'], $message ? $message : t('First link is identical to second link'));
+  }
+}
diff --git a/modules/system/tests/module.test b/modules/system/tests/module.test
new file mode 100644
index 0000000..a9b16c4
--- /dev/null
+++ b/modules/system/tests/module.test
@@ -0,0 +1,205 @@
+<?php
+// $Id: module.test,v 1.21 2010/04/22 18:56:31 dries Exp $
+
+/**
+ * @file
+ * Tests for the module API.
+ */
+
+/**
+ * Unit tests for the module API.
+ */
+class ModuleUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Module API',
+      'description' => 'Test low-level module functions.',
+      'group' => 'Module',
+    );
+  }
+
+  /**
+   * The basic functionality of module_list().
+   */
+  function testModuleList() {
+    // Build a list of modules, sorted alphabetically.
+    $profile_info = install_profile_info('standard', 'en');
+    $module_list = $profile_info['dependencies'];
+
+    // Install profile is a module that is expected to be loaded.
+    $module_list[] = 'standard';
+
+    sort($module_list);
+    // Compare this list to the one returned by module_list(). We expect them
+    // to match, since all default profile modules have a weight equal to 0
+    // (except for block.module, which has a lower weight but comes first in
+    // the alphabet anyway).
+    $this->assertModuleList($module_list, t('Standard profile'));
+
+    // Try to install a new module.
+    module_enable(array('contact'));
+    $module_list[] = 'contact';
+    sort($module_list);
+    $this->assertModuleList($module_list, t('After adding a module'));
+
+    // Try to mess with the module weights.
+    db_update('system')
+      ->fields(array('weight' => 20))
+      ->condition('name', 'contact')
+      ->condition('type', 'module')
+      ->execute();
+    // Reset the module list.
+    module_list(TRUE);
+    // Move contact to the end of the array.
+    unset($module_list[array_search('contact', $module_list)]);
+    $module_list[] = 'contact';
+    $this->assertModuleList($module_list, t('After changing weights'));
+
+    // Test the fixed list feature.
+    $fixed_list = array(
+      'system' => array('filename' => drupal_get_path('module', 'system')),
+      'menu' => array('filename' => drupal_get_path('module', 'menu')),
+    );
+    module_list(FALSE, FALSE, FALSE, $fixed_list);
+    $new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list));
+    $this->assertModuleList($new_module_list, t('When using a fixed list'));
+
+    // Reset the module list.
+    module_list(TRUE);
+    $this->assertModuleList($module_list, t('After reset'));
+  }
+
+  /**
+   * Assert that module_list() return the expected values.
+   *
+   * @param $expected_values
+   *   The expected values, sorted by weight and module name.
+   */
+  protected function assertModuleList(Array $expected_values, $condition) {
+    $expected_values = array_combine($expected_values, $expected_values);
+    $this->assertEqual($expected_values, module_list(), t('@condition: module_list() returns correct results', array('@condition' => $condition)));
+    ksort($expected_values);
+    $this->assertIdentical($expected_values, module_list(FALSE, FALSE, TRUE), t('@condition: module_list() returns correctly sorted results', array('@condition' => $condition)));
+  }
+
+  /**
+   * Test module_implements() caching.
+   */
+  function testModuleImplements() {
+    // Clear the cache.
+    cache_clear_all('module_implements', 'cache_bootstrap');
+    $this->assertFalse(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is empty.'));
+    $this->drupalGet('');
+    $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.'));
+
+    // Test again with an authenticated user.
+    $this->user = $this->drupalCreateUser();
+    $this->drupalLogin($this->user);
+    cache_clear_all('module_implements', 'cache_bootstrap');
+    $this->drupalGet('');
+    $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.'));
+
+    // Make sure group include files are detected properly even when the file is
+    // already loaded when the cache is rebuilt.
+    // For that activate the module_test which provides the file to load.
+    module_enable(array('module_test'));
+
+    module_load_include('inc', 'module_test', 'module_test.file');
+    $modules = module_implements('test_hook');
+    $static = drupal_static('module_implements');
+    $this->assertTrue(in_array('module_test', $modules), 'Hook found.');
+    $this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.');
+  }
+
+  /**
+   * Test dependency resolution.
+   */
+  function testDependencyResolution() {
+    module_enable(array('module_test'), FALSE);
+    $this->assertTrue(module_exists('module_test'), t('Test module is enabled.'));
+
+    // First, create a fake missing dependency. Forum depends on poll, which
+    // depends on a made-up module, foo. Nothing should be installed.
+    variable_set('dependency_test', 'missing dependency');
+    $result = module_enable(array('forum'));
+    $this->assertFalse($result, t('module_enable() returns FALSE if dependencies are missing.'));
+    $this->assertFalse(module_exists('forum'), t('module_enable() aborts if dependencies are missing.'));
+
+    // Now, fix the missing dependency. module_enable() should work.
+    variable_set('dependency_test', 'dependency');
+    $result = module_enable(array('forum'));
+    $this->assertTrue($result, t('module_enable() returns the correct value.'));
+    // Verify that the fake dependency chain was installed.
+    $this->assertTrue(module_exists('poll') && module_exists('php'), t('Dependency chain was installed by module_enable().'));
+    // Finally, verify that the original module was installed.
+    $this->assertTrue(module_exists('forum'), t('Module installation with unlisted dependencies succeeded.'));
+  }
+}
+
+/**
+ * Unit tests for module installation.
+ */
+class ModuleInstallTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Module installation',
+      'description' => 'Tests the installation of modules.',
+      'group' => 'Module',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('module_test');
+  }
+
+  /**
+   * Test that calls to drupal_write_record() work during module installation.
+   *
+   * This is a useful function to test because modules often use it to insert
+   * initial data in their database tables when they are being installed or
+   * enabled. Furthermore, drupal_write_record() relies on the module schema
+   * information being available, so this also checks that the data from one of
+   * the module's hook implementations, in particular hook_schema(), is
+   * properly available during this time. Therefore, this test helps ensure
+   * that modules are fully functional while Drupal is installing and enabling
+   * them.
+   */
+  function testDrupalWriteRecord() {
+    // Check for data that was inserted using drupal_write_record() while the
+    // 'module_test' module was being installed and enabled.
+    $data = db_query("SELECT data FROM {module_test}")->fetchCol();
+    $this->assertTrue(in_array('Data inserted in hook_install()', $data), t('Data inserted using drupal_write_record() in hook_install() is correctly saved.'));
+    $this->assertTrue(in_array('Data inserted in hook_enable()', $data), t('Data inserted using drupal_write_record() in hook_enable() is correctly saved.'));
+  }
+}
+
+/**
+ * Unit tests for module uninstallation and related hooks.
+ */
+class ModuleUninstallTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Module uninstallation',
+      'description' => 'Tests the uninstallation of modules.',
+      'group' => 'Module',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('module_test', 'user');
+  }
+
+  /**
+   * Tests the hook_modules_uninstalled() of the user module.
+   */
+  function testUserPermsUninstalled() {
+    // Uninstalls the module_test module, so hook_modules_uninstalled()
+    // is executed.
+    module_disable(array('module_test'));
+    drupal_uninstall_modules(array('module_test'));
+
+    // Are the perms defined by module_test removed from {role_permission}.
+    $count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField();
+    $this->assertEqual(0, $count, t('Permissions were all removed.'));
+  }
+}
diff --git a/modules/system/tests/path.test b/modules/system/tests/path.test
new file mode 100644
index 0000000..5fb8ced
--- /dev/null
+++ b/modules/system/tests/path.test
@@ -0,0 +1,238 @@
+<?php
+// $Id: path.test,v 1.5 2010/04/06 19:49:03 dries Exp $
+
+/**
+ * @file
+ * Tests for path.inc.
+ */
+
+/**
+ * Unit tests for the drupal_match_path() function in path.inc.
+ *
+ * @see drupal_match_path().
+ */
+class DrupalMatchPathTestCase extends DrupalWebTestCase {
+  protected $front;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal match path',
+      'description' => 'Tests the drupal_match_path() function to make sure it works properly.',
+      'group' => 'Path API',
+    );
+  }
+
+  function setUp() {
+    // Set up the database and testing environment.
+    parent::setUp();
+
+    // Set up a random site front page to test the '<front>' placeholder.
+    $this->front = $this->randomName();
+    variable_set('site_frontpage', $this->front);
+    // Refresh our static variables from the database.
+    $this->refreshVariables();
+  }
+
+  /**
+   * Run through our test cases, making sure each one works as expected.
+   */
+  function testDrupalMatchPath() {
+    // Set up our test cases.
+    $tests = $this->drupalMatchPathTests();
+    foreach ($tests as $patterns => $cases) {
+      foreach ($cases as $path => $expected_result) {
+        $actual_result = drupal_match_path($path, $patterns);
+        $this->assertIdentical($actual_result, $expected_result, t('Tried matching the path <code>@path</code> to the pattern <pre>@patterns</pre> - expected @expected, got @actual.', array('@path' => $path, '@patterns' => $patterns, '@expected' => var_export($expected_result, TRUE), '@actual' => var_export($actual_result, TRUE))));
+      }
+    }
+  }
+
+  /**
+   * Helper function for testDrupalMatchPath(): set up an array of test cases.
+   *
+   * @return
+   *   An array of test cases to cycle through.
+   */
+  private function drupalMatchPathTests() {
+    return array(
+      // Single absolute paths.
+      'blog/1' => array(
+        'blog/1' => TRUE,
+        'blog/2' => FALSE,
+        'test' => FALSE,
+      ),
+      // Single paths with wildcards.
+      'blog/*' => array(
+        'blog/1' => TRUE,
+        'blog/2' => TRUE,
+        'blog/3/edit' => TRUE,
+        'blog/' => TRUE,
+        'blog' => FALSE,
+        'test' => FALSE,
+      ),
+      // Single paths with multiple wildcards.
+      'node/*/revisions/*' => array(
+        'node/1/revisions/3' => TRUE,
+        'node/345/revisions/test' => TRUE,
+        'node/23/edit' => FALSE,
+        'test' => FALSE,
+      ),
+      // Single paths with '<front>'.
+      '<front>' => array(
+        $this->front => TRUE,
+        "$this->front/" => FALSE,
+        "$this->front/edit" => FALSE,
+        'node' => FALSE,
+        '' => FALSE,
+      ),
+      // Paths with both '<front>' and wildcards (should not work).
+      '<front>/*' => array(
+        $this->front => FALSE,
+        "$this->front/" => FALSE,
+        "$this->front/edit" => FALSE,
+        'node/12' => FALSE,
+        '' => FALSE,
+      ),
+      // Multiple paths with the \n delimiter.
+      "node/*\nnode/*/edit" => array(
+        'node/1' => TRUE,
+        'node/view' => TRUE,
+        'node/32/edit' => TRUE,
+        'node/delete/edit' => TRUE,
+        'node/50/delete' => TRUE,
+        'test/example' => FALSE,
+      ),
+      // Multiple paths with the \r delimiter.
+      "user/*\rblog/*" => array(
+        'user/1' => TRUE,
+        'blog/1' => TRUE,
+        'user/1/blog/1' => TRUE,
+        'user/blog' => TRUE,
+        'test/example' => FALSE,
+        'user' => FALSE,
+        'blog' => FALSE,
+      ),
+      // Multiple paths with the \r\n delimiter.
+      "test\r\n<front>" => array(
+        'test' => TRUE,
+        $this->front => TRUE,
+        'example' => FALSE,
+      ),
+      // Test existing regular expressions (should be escaped).
+      '[^/]+?/[0-9]' => array(
+        'test/1' => FALSE,
+        '[^/]+?/[0-9]' => TRUE,
+      ),
+    );
+  }
+}
+
+/**
+ * Tests hook_url_alter functions.
+ */
+class UrlAlterFunctionalTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => t('URL altering'),
+      'description' => t('Tests hook_url_inbound_alter() and hook_url_outbound_alter().'),
+      'group' => t('Path API'),
+    );
+  }
+
+  function setUp() {
+    parent::setUp('path', 'forum', 'url_alter_test');
+  }
+
+  /**
+   * Test that URL altering works and that it occurs in the correct order.
+   */
+  function testUrlAlter() {
+    $account = $this->drupalCreateUser(array('administer url aliases'));
+    $this->drupalLogin($account);
+
+    $uid = $account->uid;
+    $name = $account->name;
+
+    // Test a single altered path.
+    $this->assertUrlInboundAlter("user/$name", "user/$uid");
+    $this->assertUrlOutboundAlter("user/$uid", "user/$name");
+
+    // Test that a path always uses its alias.
+    $path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1');
+    path_save($path);
+    $this->assertUrlInboundAlter('alias/test1', "user/$uid/test1");
+    $this->assertUrlOutboundAlter("user/$uid/test1", 'alias/test1');
+
+    // Test that alias source paths are normalized in the interface.
+    $edit = array('source' => "user/$name/edit", 'alias' => 'alias/test2');
+    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
+    $this->assertText(t('The alias has been saved.'));
+
+    // Test that a path always uses its alias.
+    $this->assertUrlInboundAlter('alias/test2', "user/$uid/edit");
+    $this->assertUrlOutboundAlter("user/$uid/edit", 'alias/test2');
+
+    // Test a non-existant user is not altered.
+    $uid++;
+    $this->assertUrlInboundAlter("user/$uid", "user/$uid");
+    $this->assertUrlOutboundAlter("user/$uid", "user/$uid");
+
+    // Test that 'forum' is altered to 'community' correctly, both at the root
+    // level and for a specific existing forum.
+    $this->assertUrlInboundAlter('community', 'forum');
+    $this->assertUrlOutboundAlter('forum', 'community');
+    $forum_vid = db_query("SELECT vid FROM {taxonomy_vocabulary} WHERE module = 'forum'")->fetchField();
+    $tid = db_insert('taxonomy_term_data')
+      ->fields(array(
+        'name' => $this->randomName(),
+        'vid' => $forum_vid,
+      ))
+      ->execute();
+    $this->assertUrlInboundAlter("community/$tid", "forum/$tid");
+    $this->assertUrlOutboundAlter("forum/$tid", "community/$tid");
+  }
+
+  /**
+   * Test current_path() and request_path().
+   */
+  function testCurrentUrlRequestedPath() {
+    $this->drupalGet('url-alter-test/bar');
+    $this->assertRaw('request_path=url-alter-test/bar', t('request_path() returns the requested path.'));
+    $this->assertRaw('current_path=url-alter-test/foo', t('current_path() returns the internal path.'));
+  }
+
+  /**
+   * Assert that an outbound path is altered to an expected value.
+   *
+   * @param $original
+   *   A string with the original path that is run through url().
+   * @param $final
+   *   A string with the expected result after url().
+   * @return
+   *   TRUE if $original was correctly altered to $final, FALSE otherwise.
+   */
+  protected function assertUrlOutboundAlter($original, $final) {
+    // Test outbound altering.
+    $result = url($original);
+    $base_path = base_path() . (variable_get('clean_url', '0') ? '' : '?q=');
+    $result = substr($result, strlen($base_path));
+    $this->assertIdentical($result, $final, t('Altered outbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
+  }
+
+  /**
+   * Assert that a inbound path is altered to an expected value.
+   *
+   * @param $original
+   *   A string with the aliased or un-normal path that is run through
+   *   drupal_get_normal_path().
+   * @param $final
+   *   A string with the expected result after url().
+   * @return
+   *   TRUE if $original was correctly altered to $final, FALSE otherwise.
+   */
+  protected function assertUrlInboundAlter($original, $final) {
+    // Test inbound altering.
+    $result = drupal_get_normal_path($original);
+    $this->assertIdentical($result, $final, t('Altered inbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
+  }
+}
diff --git a/modules/system/tests/registry.test b/modules/system/tests/registry.test
new file mode 100644
index 0000000..ab05468
--- /dev/null
+++ b/modules/system/tests/registry.test
@@ -0,0 +1,143 @@
+<?php
+// $Id: registry.test,v 1.18 2010/07/10 01:57:32 dries Exp $
+
+class RegistryParseFileTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Registry parse file test',
+      'description' => 'Parse a simple file and check that its resources are saved to the database.',
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    $chrs = hash('sha256', microtime() . mt_rand());
+    $this->fileName = 'registry_test_' . substr($chrs, 0, 16);
+    $this->className = 'registry_test_class' . substr($chrs, 16, 16);
+    $this->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16);
+    parent::setUp();
+  }
+
+  /**
+   * testRegistryParseFile
+   */
+  function testRegistryParseFile() {
+    _registry_parse_file($this->fileName, $this->getFileContents());
+    foreach (array('className', 'interfaceName') as $resource) {
+      $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$resource))->fetchField();
+      $this->assertTrue($this->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$resource)));
+    }
+  }
+
+  /**
+   * getFileContents
+   */
+  function getFileContents() {
+    $file_contents = <<<CONTENTS
+<?php
+
+class {$this->className} {}
+
+interface {$this->interfaceName} {}
+
+CONTENTS;
+    return $file_contents;
+  }
+
+}
+
+class RegistryParseFilesTestCase extends DrupalWebTestCase {
+  protected $fileTypes = array('new', 'existing_changed');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Registry parse files test',
+      'description' => 'Read two a simple files from disc, and check that their resources are saved to the database.',
+      'group' => 'System'
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    // Create files with some php to parse - one 'new', one 'existing' so
+    // we test all the important code paths in _registry_parse_files.
+    foreach ($this->fileTypes as $fileType) {
+      $chrs = hash('sha256', microtime() . mt_rand());
+      $this->$fileType = new stdClass();
+      $this->$fileType->fileName = file_directory_path() . '/registry_test_' . substr($chrs, 0, 16);
+      $this->$fileType->className = 'registry_test_class' . substr($chrs, 16, 16);
+      $this->$fileType->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16);
+      $this->$fileType->contents = $this->getFileContents($fileType);
+      file_save_data($this->$fileType->contents, $this->$fileType->fileName);
+
+      if ($fileType == 'existing_changed') {
+        // Add a record with an incorrect hash.
+        $this->$fileType->fakeHash = hash('sha256', mt_rand());
+        db_insert('registry_file')
+          ->fields(array(
+            'hash' => $this->$fileType->fakeHash,
+            'filename' => $this->$fileType->fileName,
+          ))
+          ->execute();
+
+        // Insert some fake resource records.
+        foreach (array('class', 'interface') as $type) {
+          db_insert('registry')
+            ->fields(array(
+              'name' => $type . hash('sha256', microtime() . mt_rand()),
+              'type' => $type,
+              'filename' => $this->$fileType->fileName,
+            ))
+            ->execute();
+        }
+      }
+    }
+  }
+
+  /**
+   * testRegistryParseFiles
+   */
+  function testRegistryParseFiles() {
+    _registry_parse_files($this->getFiles());
+    foreach ($this->fileTypes as $fileType) {
+      // Test that we have all the right resources.
+      foreach (array('className', 'interfaceName') as $resource) {
+        $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$fileType->$resource))->fetchField();
+        $this->assertTrue($this->$fileType->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$fileType->$resource)));
+      }
+      // Test that we have the right hash.
+      $hash = db_query('SELECT hash FROM {registry_file} WHERE filename = :filename', array(':filename' => $this->$fileType->fileName))->fetchField();
+      $this->assertTrue(hash('sha256', $this->$fileType->contents) == $hash, t('sha-256 for "@filename" matched.' . $fileType . $hash, array('@filename' => $this->$fileType->fileName)));
+    }
+  }
+
+  /**
+   * getFiles
+   */
+  function getFiles() {
+    $files = array();
+    foreach ($this->fileTypes as $fileType) {
+      $files[$this->$fileType->fileName] = array('module' => '', 'weight' => 0);
+      if ($fileType == 'existing_changed') {
+        $files[$this->$fileType->fileName]['hash'] = $this->$fileType->fakeHash;
+      }
+    }
+    return $files;
+  }
+
+  /**
+   * getFileContents
+   */
+  function getFileContents($fileType) {
+    $file_contents = <<<CONTENTS
+<?php
+
+class {$this->$fileType->className} {}
+
+interface {$this->$fileType->interfaceName} {}
+
+CONTENTS;
+    return $file_contents;
+  }
+
+}
diff --git a/modules/system/tests/schema.test b/modules/system/tests/schema.test
new file mode 100644
index 0000000..c8c297e
--- /dev/null
+++ b/modules/system/tests/schema.test
@@ -0,0 +1,199 @@
+<?php
+// $Id: schema.test,v 1.19 2010/03/28 11:45:11 dries Exp $
+
+/**
+ * @file
+ * Tests for the Database Schema API.
+ */
+
+/**
+ * Unit tests for the Schema API.
+ */
+class SchemaTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Schema API',
+      'description' => 'Tests table creation and modification via the schema API.',
+      'group' => 'Database',
+    );
+  }
+
+  /**
+   *
+   */
+  function testSchema() {
+    // Try creating a table.
+    $table_specification = array(
+      'description' => 'Schema table description.',
+      'fields' => array(
+        'id'  => array(
+          'type' => 'int',
+          'default' => NULL,
+        ),
+        'test_field'  => array(
+          'type' => 'int',
+          'not null' => TRUE,
+          'description' => 'Schema column description.',
+        ),
+      ),
+    );
+    db_create_table('test_table', $table_specification);
+
+    // Assert that the table exists.
+    $this->assertTrue(db_table_exists('test_table'), t('The table exists.'));
+
+    // Assert that the table comment has been set.
+    $this->checkSchemaComment($table_specification['description'], 'test_table');
+
+    // Assert that the column comment has been set.
+    $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
+
+    // An insert without a value for the column 'test_table' should fail.
+    $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
+
+    // Add a default value to the column.
+    db_field_set_default('test_table', 'test_field', 0);
+    // The insert should now succeed.
+    $this->assertTrue($this->tryInsert(), t('Insert with a default succeeded.'));
+
+    // Remove the default.
+    db_field_set_no_default('test_table', 'test_field');
+    // The insert should fail again.
+    $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
+
+    // Test for fake index and test for the boolean result of indexExists().
+    $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
+    $this->assertIdentical($index_exists, FALSE, t('Fake index does not exists'));
+    // Add index.
+    db_add_index('test_table', 'test_field', array('test_field'));
+    // Test for created index and test for the boolean result of indexExists().
+    $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
+    $this->assertIdentical($index_exists, TRUE, t('Index created.'));
+
+    // Rename the table.
+    db_rename_table('test_table', 'test_table2');
+
+    // Index should be renamed.
+    $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
+    $this->assertTrue($index_exists, t('Index was renamed.'));
+
+    // We need the default so that we can insert after the rename.
+    db_field_set_default('test_table2', 'test_field', 0);
+    $this->assertFalse($this->tryInsert(), t('Insert into the old table failed.'));
+    $this->assertTrue($this->tryInsert('test_table2'), t('Insert into the new table succeeded.'));
+
+    // We should have successfully inserted exactly two rows.
+    $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
+    $this->assertEqual($count, 2, t('Two fields were successfully inserted.'));
+
+    // Try to drop the table.
+    db_drop_table('test_table2');
+    $this->assertFalse(db_table_exists('test_table2'), t('The dropped table does not exist.'));
+
+    // Recreate the table.
+    db_create_table('test_table', $table_specification);
+    db_field_set_default('test_table', 'test_field', 0);
+    db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.'));
+
+    // Assert that the column comment has been set.
+    $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
+
+    // Change the new field to a serial column.
+    db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial')));
+
+    // Assert that the column comment has been set.
+    $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
+
+    $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
+    $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
+    $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
+    $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
+    $this->assertTrue($max2 > $max1, t('The serial is monotone.'));
+
+    $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
+    $this->assertEqual($count, 2, t('There were two rows.'));
+  }
+
+  function tryInsert($table = 'test_table') {
+    try {
+       db_insert($table)
+         ->fields(array('id' => mt_rand(10, 20)))
+         ->execute();
+      return TRUE;
+    }
+    catch (Exception $e) {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Checks that a table or column comment matches a given description.
+   *
+   * @param $description
+   *   The asserted description.
+   * @param $table
+   *   The table to test.
+   * @param $column
+   *   Optional column to test.
+   */
+  function checkSchemaComment($description, $table, $column = NULL) {
+    if (method_exists(Database::getConnection()->schema(), 'getComment')) {
+      $comment = Database::getConnection()->schema()->getComment($table, $column);
+      $this->assertEqual($comment, $description, t('The comment matches the schema description.'));
+    }
+  }
+
+  /**
+   * Tests creating unsigned columns and data integrity thereof.
+   */
+  function testUnsignedColumns() {
+    // First create the table with just a serial column.
+    $table_name = 'unsigned_table';
+    $table_spec = array(
+      'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)),
+      'primary key' => array('serial_column'),
+    );
+    $ret = array();
+    db_create_table($table_name, $table_spec);
+
+    // Now set up columns for the other types.
+    $types = array('int', 'float', 'numeric');
+    foreach ($types as $type) {
+      $column_spec = array('type' => $type, 'unsigned'=> TRUE);
+      if ($type == 'numeric') {
+        $column_spec += array('precision' => 10, 'scale' => 0);
+      }
+      $column_name = $type . '_column';
+      $table_spec['fields'][$column_name] = $column_spec;
+      db_add_field($table_name, $column_name, $column_spec);
+    }
+
+    // Finally, check each column and try to insert invalid values into them.
+    foreach($table_spec['fields'] as $column_name => $column_spec) {
+      $this->assertTrue(db_field_exists($table_name, $column_name), t('Unsigned @type column was created.', array('@type' => $column_spec['type'])));
+      $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), t('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type'])));
+    }
+  }
+
+  /**
+   * Tries to insert a negative value into columns defined as unsigned.
+   *
+   * @param $table_name
+   *   The table to insert
+   * @param $column_name
+   *   The column to insert
+   * @return
+   *   TRUE if the insert succeeded, FALSE otherwise
+   */
+  function tryUnsignedInsert($table_name, $column_name) {
+    try {
+      db_insert($table_name)
+         ->fields(array($column_name => -1))
+         ->execute();
+      return TRUE;
+    }
+    catch (Exception $e) {
+      return FALSE;
+    }
+  }
+}
diff --git a/modules/system/tests/session.test b/modules/system/tests/session.test
new file mode 100644
index 0000000..552f8a8
--- /dev/null
+++ b/modules/system/tests/session.test
@@ -0,0 +1,424 @@
+<?php
+// $Id: session.test,v 1.29 2010/06/14 12:31:46 dries Exp $
+
+/**
+ * @file
+ * Provides SimpleTests for core session handling functionality.
+ */
+
+class SessionTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Session tests',
+      'description' => 'Drupal session handling tests.',
+      'group' => 'Session'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('session_test');
+  }
+
+  /**
+   * Tests for drupal_save_session() and drupal_session_regenerate().
+   */
+  function testSessionSaveRegenerate() {
+    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session'));
+    $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
+    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
+    $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
+    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
+
+    // Test session hardening code from SA-2008-044.
+    $user = $this->drupalCreateUser(array('access content'));
+
+    // Enable sessions.
+    $this->sessionReset($user->uid);
+
+    // Make sure the session cookie is set as HttpOnly.
+    $this->drupalLogin($user);
+    $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.'));
+    $this->drupalLogout();
+
+    // Verify that the session is regenerated if a module calls exit
+    // in hook_user_login().
+    user_save($user, array('name' => 'session_test_user'));
+    $user->name = 'session_test_user';
+    $this->drupalGet('session-test/id');
+    $matches = array();
+    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
+    $this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
+    $original_session = $matches[1];
+
+    // We cannot use $this->drupalLogin($user); because we exit in
+    // session_test_user_login() which breaks a normal assertion.
+    $edit = array(
+      'name' => $user->name,
+      'pass' => $user->pass_raw
+    );
+    $this->drupalPost('user', $edit, t('Log in'));
+    $this->drupalGet('user');
+    $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
+    $this->_logged_in = $pass;
+
+    $this->drupalGet('session-test/id');
+    $matches = array();
+    preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
+    $this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
+    $this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));
+  }
+
+  /**
+   * Test data persistence via the session_test module callbacks. Also tests
+   * drupal_session_count() since session data is already generated here.
+   */
+  function testDataPersistence() {
+    $user = $this->drupalCreateUser(array('access content'));
+    // Enable sessions.
+    $this->sessionReset($user->uid);
+
+    $this->drupalLogin($user);
+
+    $value_1 = $this->randomName();
+    $this->drupalGet('session-test/set/' . $value_1);
+    $this->assertText($value_1, t('The session value was stored.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));
+
+    // Attempt to write over val_1. If drupal_save_session(FALSE) is working.
+    // properly, val_1 will still be set.
+    $value_2 = $this->randomName();
+    $this->drupalGet('session-test/no-set/' . $value_2);
+    $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
+
+    // Switch browser cookie to anonymous user, then back to user 1.
+    $this->sessionReset();
+    $this->sessionReset($user->uid);
+    $this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
+
+    // Logout the user and make sure the stored value no longer persists.
+    $this->drupalLogout();
+    $this->sessionReset();
+    $this->drupalGet('session-test/get');
+    $this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
+
+    // Now try to store some data as an anonymous user.
+    $value_3 = $this->randomName();
+    $this->drupalGet('session-test/set/' . $value_3);
+    $this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
+
+    // Try to store data when drupal_save_session(FALSE).
+    $value_4 = $this->randomName();
+    $this->drupalGet('session-test/no-set/' . $value_4);
+    $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
+    $this->drupalGet('session-test/get');
+    $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
+
+    // Login, the data should persist.
+    $this->drupalLogin($user);
+    $this->sessionReset($user->uid);
+    $this->drupalGet('session-test/get');
+    $this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
+
+    // Change session and create another user.
+    $user2 = $this->drupalCreateUser(array('access content'));
+    $this->sessionReset($user2->uid);
+    $this->drupalLogin($user2);
+  }
+
+  /**
+   * Test that empty anonymous sessions are destroyed.
+   */
+  function testEmptyAnonymousSession() {
+    // Verify that no session is automatically created for anonymous user.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+
+    // The same behavior is expected when caching is enabled.
+    variable_set('cache', 1);
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
+
+    // Start a new session by setting a message.
+    $this->drupalGet('session-test/set-message');
+    $this->assertSessionCookie(TRUE);
+    $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
+
+    // Display the message, during the same request the session is destroyed
+    // and the session cookie is unset.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(FALSE);
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
+    $this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
+    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
+
+    // Verify that session was destroyed.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
+    $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
+
+    // Verify that no session is created if drupal_save_session(FALSE) is called.
+    $this->drupalGet('session-test/set-message-but-dont-save');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+
+    // Verify that no message is displayed.
+    $this->drupalGet('');
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.'));
+  }
+
+  /**
+   * Reset the cookie file so that it refers to the specified user.
+   *
+   * @param $uid User id to set as the active session.
+   */
+  function sessionReset($uid = 0) {
+    // Close the internal browser.
+    $this->curlClose();
+    $this->loggedInUser = FALSE;
+
+    // Change cookie file for user.
+    $this->cookieFile = file_directory_path('temporary') . '/cookie.' . $uid . '.txt';
+    $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile;
+    $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE;
+    $this->drupalGet('session-test/get');
+    $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
+  }
+
+  /**
+   * Assert whether the SimpleTest browser sent a session cookie.
+   */
+  function assertSessionCookie($sent) {
+    if ($sent) {
+      $this->assertNotNull($this->session_id, t('Session cookie was sent.'));
+    }
+    else {
+      $this->assertNull($this->session_id, t('Session cookie was not sent.'));
+    }
+  }
+
+  /**
+   * Assert whether $_SESSION is empty at the beginning of the request.
+   */
+  function assertSessionEmpty($empty) {
+    if ($empty) {
+      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
+    }
+    else {
+      $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
+    }
+  }
+}
+
+/**
+ * Ensure that when running under https two session cookies are generated.
+ */
+class SessionHttpsTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Session https handling',
+      'description' => 'Ensure that when running under https two session cookies are generated.',
+      'group' => 'Session'
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('session_test');
+  }
+
+  protected function testHttpsSession() {
+    global $is_https;
+
+    if ($is_https) {
+      $secure_session_name = session_name();
+      $insecure_session_name = substr(session_name(), 1);
+    }
+    else {
+      $secure_session_name = 'S' . session_name();
+      $insecure_session_name = session_name();
+    }
+
+    $user = $this->drupalCreateUser(array('access administration pages'));
+
+    // Test HTTPS session handling by altering the form action to submit the
+    // login form through https.php, which creates a mock HTTPS request.
+    $this->drupalGet('user');
+    $form = $this->xpath('//form[@id="user-login"]');
+    $form[0]['action'] = $this->httpsUrl('user');
+    $edit = array('name' => $user->name, 'pass' => $user->pass_raw);
+    $this->drupalPost(NULL, $edit, t('Log in'));
+
+    // Test a second concurrent session.
+    $this->curlClose();
+    $this->drupalGet('user');
+    $form = $this->xpath('//form[@id="user-login"]');
+    $form[0]['action'] = $this->httpsUrl('user');
+    $this->drupalPost(NULL, $edit, t('Log in'));
+
+    // Check secure cookie on secure page.
+    $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute');
+    // Check insecure cookie is not set.
+    $this->assertFalse(isset($this->cookies[$insecure_session_name]));
+    $ssid = $this->cookies[$secure_session_name]['value'];
+    $this->assertSessionIds('', $ssid, 'Session has NULL for SID and a correct secure SID.');
+    $cookie = $secure_session_name . '=' . $ssid;
+
+    // Verify that user is logged in on secure URL.
+    $this->curlClose();
+    $this->drupalGet($this->httpsUrl('admin/config'), array(), array('Cookie: ' . $cookie));
+    $this->assertText(t('Configuration'));
+    $this->assertResponse(200);
+
+    // Verify that user is not logged in on non-secure URL.
+    if (!$is_https) {
+      $this->curlClose();
+      $this->drupalGet('admin/config', array(), array('Cookie: ' . $cookie));
+      $this->assertNoText(t('Configuration'));
+      $this->assertResponse(403);
+    }
+
+    // Clear browser cookie jar.
+    $this->cookies = array();
+
+    if ($is_https) {
+      // The functionality does not make sense when running on https.
+      return;
+    }
+
+    // Enable secure pages.
+    variable_set('https', TRUE);
+
+    $this->curlClose();
+    // Start an anonymous session on the insecure site.
+    $session_data = $this->randomName();
+    $this->drupalGet('session-test/set/' . $session_data);
+    // Check secure cookie on insecure page.
+    $this->assertFalse(isset($this->cookies[$secure_session_name]), 'The secure cookie is not sent on insecure pages.');
+    // Check insecure cookie on insecure page.
+    $this->assertFalse($this->cookies[$insecure_session_name]['secure'], 'The insecure cookie does not have the secure attribute');
+
+    // Store the anonymous cookie so we can validate that its session is killed
+    // after login.
+    $anonymous_cookie = $insecure_session_name . '=' . $this->cookies[$insecure_session_name]['value'];
+
+    // Check that password request form action is not secure.
+    $this->drupalGet('user/password');
+    $form = $this->xpath('//form[@id="user-pass"]');
+    $this->assertNotEqual(substr($form[0]['action'], 0, 6), 'https:', 'Password request form action is not secure');
+    $form[0]['action'] = $this->httpsUrl('user');
+
+    // Check that user login form action is secure.
+    $this->drupalGet('user');
+    $form = $this->xpath('//form[@id="user-login"]');
+    $this->assertEqual(substr($form[0]['action'], 0, 6), 'https:', 'Login form action is secure');
+    $form[0]['action'] = $this->httpsUrl('user');
+
+    $edit = array(
+      'name' => $user->name,
+      'pass' => $user->pass_raw,
+    );
+    $this->drupalPost(NULL, $edit, t('Log in'));
+    // Check secure cookie on secure page.
+    $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute');
+    // Check insecure cookie on secure page.
+    $this->assertFalse($this->cookies[$insecure_session_name]['secure'], 'The insecure cookie does not have the secure attribute');
+
+    $sid = $this->cookies[$insecure_session_name]['value'];
+    $ssid = $this->cookies[$secure_session_name]['value'];
+    $this->assertSessionIds($sid, $ssid, 'Session has both secure and insecure SIDs');
+    $cookies = array(
+      $insecure_session_name . '=' . $sid,
+      $secure_session_name . '=' . $ssid,
+    );
+
+    // Test that session data saved before login is still available on the
+    // authenticated session.
+    $this->drupalGet('session-test/get');
+    $this->assertText($session_data, 'Session correctly returned the stored data set by the anonymous session.');
+
+    foreach ($cookies as $cookie_key => $cookie) {
+      foreach (array('admin/config', $this->httpsUrl('admin/config')) as $url_key => $url) {
+        $this->curlClose();
+
+        $this->drupalGet($url, array(), array('Cookie: ' . $cookie));
+        if ($cookie_key == $url_key) {
+          $this->assertText(t('Configuration'));
+          $this->assertResponse(200);
+        }
+        else {
+          $this->assertNoText(t('Configuration'));
+          $this->assertResponse(403);
+        }
+      }
+    }
+
+    // Test that session data saved before login is not available using the 
+    // pre-login anonymous cookie.
+    $this->cookies = array();
+    $this->drupalGet('session-test/get', array('Cookie: ' . $anonymous_cookie));
+    $this->assertNoText($session_data, 'Initial anonymous session is inactive after login.');
+
+    // Clear browser cookie jar.
+    $this->cookies = array();
+
+    // Start an anonymous session on the secure site.
+    $this->drupalGet($this->httpsUrl('session-test/set/1'));
+
+    // Mock a login to the secure site using the secure session cookie.
+    $this->drupalGet('user');
+    $form = $this->xpath('//form[@id="user-login"]');
+    $form[0]['action'] = $this->httpsUrl('user');
+    $this->drupalPost(NULL, $edit, t('Log in'), array(), array('Cookie: ' . $secure_session_name . '=' . $this->cookies[$secure_session_name]['value']));
+
+    // Get the insecure session cookie set by the secure login POST request.
+    $headers = $this->drupalGetHeaders(TRUE);
+    strtok($headers[0]['set-cookie'], ';=');
+    $session_id = strtok(';=');
+
+    // Test that the user is also authenticated on the insecure site.
+    $this->drupalGet("user/{$user->uid}/edit", array(), array('Cookie: ' . $insecure_session_name . '=' . $session_id));
+    $this->assertResponse(200);
+  }
+
+  /**
+   * Test that there exists a session with two specific session IDs.
+   *
+   * @param $sid
+   *   The insecure session ID to search for.
+   * @param $ssid
+   *   The secure session ID to search for.
+   * @param $assertion_text
+   *   The text to display when we perform the assertion.
+   *
+   * @return
+   *   The result of assertTrue() that there's a session in the system that
+   *   has the given insecure and secure session IDs.
+   */
+  protected function assertSessionIds($sid, $ssid, $assertion_text) {
+    $args = array(
+      ':sid' => $sid,
+      ':ssid' => $ssid,
+    );
+    return $this->assertTrue(db_query('SELECT timestamp FROM {sessions} WHERE sid = :sid AND ssid = :ssid', $args)->fetchField(), $assertion_text);
+  }
+
+  protected function httpsUrl($url) {
+    global $base_url;
+    return $base_url . '/modules/simpletest/tests/https.php?q=' . $url;
+  }
+}
+
diff --git a/modules/system/tests/theme.test b/modules/system/tests/theme.test
new file mode 100644
index 0000000..2d27c26
--- /dev/null
+++ b/modules/system/tests/theme.test
@@ -0,0 +1,184 @@
+<?php
+// $Id: theme.test,v 1.16 2010/04/29 05:22:06 webchick Exp $
+
+/**
+ * @file
+ * Tests for the theme API.
+ */
+
+/**
+ * Unit tests for the Theme API.
+ */
+class ThemeUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme API',
+      'description' => 'Test low-level theme functions.',
+      'group' => 'Theme',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('theme_test');
+    theme_enable(array('test_theme'));
+  }
+
+  /**
+   * Test function theme_get_suggestions() for SA-CORE-2009-003.
+   */
+  function testThemeSuggestions() {
+    // Set the front page as something random otherwise the CLI
+    // test runner fails.
+    variable_set('site_frontpage', 'nobody-home');
+    $args = array('node', '1', 'edit');
+    $suggestions = theme_get_suggestions($args, 'page');
+    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1', 'page__node__edit'), t('Found expected node edit page suggestions'));
+    // Check attack vectors.
+    $args = array('node', '\\1');
+    $suggestions = theme_get_suggestions($args, 'page');
+    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\ from suggestions'));
+    $args = array('node', '1/');
+    $suggestions = theme_get_suggestions($args, 'page');
+    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid / from suggestions'));
+    $args = array('node', "1\0");
+    $suggestions = theme_get_suggestions($args, 'page');
+    $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\0 from suggestions'));
+  }
+
+  /**
+  * Preprocess functions for the base hook should run even for suggestion implementations.
+  */
+  function testPreprocessForSuggestions() {
+    $this->drupalGet('theme-test/suggestion');
+    $this->assertText('test_theme_breadcrumb__suggestion: 1', t('Theme hook suggestion ran with data available from a preprocess function for the base hook.'));
+  }
+
+  /**
+   * Ensure page-front template suggestion is added when on front page.
+   */
+  function testFrontPageThemeSuggestion() {
+    $q = $_GET['q'];
+    // Set $_GET['q'] to node because theme_get_suggestions() will query it to
+    // see if we are on the front page.
+    $_GET['q'] = variable_get('site_frontpage', 'node');
+    $suggestions = theme_get_suggestions(explode('/', $_GET['q']), 'page');
+    // Set it back to not annoy the batch runner.
+    $_GET['q'] = $q;
+    $this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.'));
+  }
+}
+
+/**
+ * Unit tests for theme_table().
+ */
+class ThemeTableUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme Table',
+      'description' => 'Tests built-in theme functions.',
+      'group' => 'Theme',
+    );
+  }
+
+  /**
+   * Tableheader.js provides 'sticky' table headers, and is included by default.
+   */
+  function testThemeTableStickyHeaders() {
+    $header = array('one', 'two', 'three');
+    $rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
+    $this->content = theme('table', array('header' => $header, 'rows' => $rows));
+    $js = drupal_add_js();
+    $this->assertTrue(isset($js['misc/tableheader.js']), t('tableheader.js was included when $sticky = TRUE.'));
+    $this->assertRaw('sticky-enabled',  t('Table has a class of sticky-enabled when $sticky = TRUE.'));
+    drupal_static_reset('drupal_add_js');
+  }
+
+  /**
+   * If $sticky is FALSE, no tableheader.js should be included.
+   */
+  function testThemeTableNoStickyHeaders() {
+    $header = array('one', 'two', 'three');
+    $rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
+    $attributes = array();
+    $caption = NULL;
+    $colgroups = array();
+    $this->content = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes, 'caption' => $caption, 'colgroups' => $colgroups, 'sticky' => FALSE));
+    $js = drupal_add_js();
+    $this->assertFalse(isset($js['misc/tableheader.js']), t('tableheader.js was not included because $sticky = FALSE.'));
+    $this->assertNoRaw('sticky-enabled',  t('Table does not have a class of sticky-enabled because $sticky = FALSE.'));
+    drupal_static_reset('drupal_add_js');
+  }
+
+  /**
+   * Tests that the table header is printed correctly even if there are no rows,
+   * and that the empty text is displayed correctly.
+   */
+  function testThemeTableWithEmptyMessage() {
+    $header = array(
+      t('Header 1'),
+      array(
+        'data' => t('Header 2'),
+        'colspan' => 2,
+      ),
+    );
+    $this->content = theme('table', array('header' => $header, 'rows' => array(), 'empty' => t('No strings available.')));
+    $this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">No strings available.</td>', t('Correct colspan was set on empty message.'));
+    $this->assertRaw('<thead><tr><th>Header 1</th>', t('Table header was printed.'));
+  }
+
+}
+
+/**
+ * Unit tests for theme_item_list().
+ */
+class ThemeItemListUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme item list',
+      'description' => 'Test the theme_item_list() function.',
+      'group' => 'Theme',
+    );
+  }
+
+  /**
+   * Test nested list rendering.
+   */
+  function testNestedList() {
+    $items = array('a', array('data' => 'b', 'children' => array('c', 'd')), 'e');
+    $expected = '<div class="item-list"><ul><li class="first">a</li>
+<li>b<div class="item-list"><ul><li class="first">c</li>
+<li class="last">d</li>
+</ul></div></li>
+<li class="last">e</li>
+</ul></div>';
+    $output = theme('item_list', array('items' => $items, 'type' => 'ul', 'title' => NULL, 'attributes' => array()));
+    $this->assertIdentical($expected, $output, 'Nested list is rendered correctly.');
+  }
+}
+
+/**
+ * Functional test for initialization of the theme system in hook_init().
+ */
+class ThemeHookInitUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme initialization in hook_init()',
+      'description' => 'Tests that the theme system can be correctly initialized in hook_init().',
+      'group' => 'Theme',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('theme_test');
+    variable_set('theme_default', 'garland');
+  }
+
+  /**
+   * Test that the theme system can generate output when called by hook_init().
+   */
+  function testThemeInitializationHookInit() {
+    $this->drupalGet('theme-test/hook-init');
+    $this->assertRaw('Themed output generated in hook_init()', t('Themed output generated in hook_init() correctly appears on the page.'));
+    $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init()."));
+  }
+}
diff --git a/modules/system/tests/unicode.test b/modules/system/tests/unicode.test
new file mode 100644
index 0000000..42cb987
--- /dev/null
+++ b/modules/system/tests/unicode.test
@@ -0,0 +1,336 @@
+<?php
+// $Id: unicode.test,v 1.6 2010/06/10 15:20:48 dries Exp $
+
+/**
+ * @file
+ * Various unicode handling tests.
+ */
+
+/**
+ * Test unicode handling features implemented in unicode.inc.
+ */
+class UnicodeUnitTest extends DrupalWebTestCase {
+
+  /**
+   * Whether to run the extended version of the tests (including non latin1 characters).
+   *
+   * @var boolean
+   */
+  protected $extendedMode = FALSE;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Unicode handling',
+      'description' => 'Tests Drupal Unicode handling.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Test full unicode features implemented using the mbstring extension.
+   */
+  function testMbStringUnicode() {
+    global $multibyte;
+
+    // mbstring was not detected on this installation, there is no way to test
+    // multibyte features. Treat that as an exception.
+    if ($multibyte == UNICODE_SINGLEBYTE) {
+      $this->error(t('Unable to test Multibyte features: mbstring extension was not detected.'));
+    }
+
+    $multibyte = UNICODE_MULTIBYTE;
+
+    $this->extendedMode = TRUE;
+    $this->pass(t('Testing in mbstring mode'));
+
+    $this->helperTestStrToLower();
+    $this->helperTestStrToUpper();
+    $this->helperTestUcFirst();
+    $this->helperTestStrLen();
+    $this->helperTestSubStr();
+    $this->helperTestTruncate();
+  }
+
+  /**
+   * Test emulated unicode features.
+   */
+  function testEmulatedUnicode() {
+    global $multibyte;
+
+    $multibyte = UNICODE_SINGLEBYTE;
+
+    $this->extendedMode = FALSE;
+
+    $this->pass(t('Testing in emulated (best-effort) mode'));
+
+    $this->helperTestStrToLower();
+    $this->helperTestStrToUpper();
+    $this->helperTestUcFirst();
+    $this->helperTestStrLen();
+    $this->helperTestSubStr();
+    $this->helperTestTruncate();
+  }
+
+  function helperTestStrToLower() {
+    $testcase = array(
+      'tHe QUIcK bRoWn' => 'the quick brown',
+      'FrançAIS is ÜBER-åwesome' => 'français is über-åwesome',
+    );
+    if ($this->extendedMode) {
+      $testcase['ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ'] = 'αβγδεζηθικλμνξοσὠ';
+    }
+
+    foreach ($testcase as $input => $output) {
+      $this->assertEqual(drupal_strtolower($input), $output, t('%input is lowercased as %output', array('%input' => $input, '%output' => $output)));
+    }
+  }
+
+  function helperTestStrToUpper() {
+    $testcase = array(
+      'tHe QUIcK bRoWn' => 'THE QUICK BROWN',
+      'FrançAIS is ÜBER-åwesome' => 'FRANÇAIS IS ÜBER-ÅWESOME',
+    );
+    if ($this->extendedMode) {
+      $testcase['αβγδεζηθικλμνξοσὠ'] = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ';
+    }
+
+    foreach ($testcase as $input => $output) {
+      $this->assertEqual(drupal_strtoupper($input), $output, t('%input is uppercased as %output', array('%input' => $input, '%output' => $output)));
+    }
+  }
+
+  function helperTestUcFirst() {
+    $testcase = array(
+      'tHe QUIcK bRoWn' => 'THe QUIcK bRoWn',
+      'françAIS' => 'FrançAIS',
+      'über' => 'Über',
+      'åwesome' => 'Åwesome'
+    );
+    if ($this->extendedMode) {
+      $testcase['σion'] = 'Σion';
+    }
+
+    foreach ($testcase as $input => $output) {
+      $this->assertEqual(drupal_ucfirst($input), $output, t('%input is ucfirst-ed as %output', array('%input' => $input, '%output' => $output)));
+    }
+  }
+
+  function helperTestStrLen() {
+    $testcase = array(
+      'tHe QUIcK bRoWn' => 15,
+      'ÜBER-åwesome' => 12,
+    );
+
+    foreach ($testcase as $input => $output) {
+      $this->assertEqual(drupal_strlen($input), $output, t('%input length is %output', array('%input' => $input, '%output' => $output)));
+    }
+  }
+
+  function helperTestSubStr() {
+    $testcase = array(
+      //     012345678901234567890123
+      array('frànçAIS is über-åwesome', 0, 0,
+            ''),
+      array('frànçAIS is über-åwesome', 0, 1,
+            'f'),
+      array('frànçAIS is über-åwesome', 0, 8,
+            'frànçAIS'),
+      array('frànçAIS is über-åwesome', 0, 23,
+            'frànçAIS is über-åwesom'),
+      array('frànçAIS is über-åwesome', 0, 24,
+            'frànçAIS is über-åwesome'),
+      array('frànçAIS is über-åwesome', 0, 25,
+            'frànçAIS is über-åwesome'),
+      array('frànçAIS is über-åwesome', 0, 100,
+            'frànçAIS is über-åwesome'),
+      array('frànçAIS is über-åwesome', 4, 4,
+                'çAIS'),
+      array('frànçAIS is über-åwesome', 1, 0,
+            ''),
+      array('frànçAIS is über-åwesome', 100, 0,
+            ''),
+      array('frànçAIS is über-åwesome', -4, 2,
+                                'so'),
+      array('frànçAIS is über-åwesome', -4, 3,
+                                'som'),
+      array('frànçAIS is über-åwesome', -4, 4,
+                                'some'),
+      array('frànçAIS is über-åwesome', -4, 5,
+                                'some'),
+      array('frànçAIS is über-åwesome', -7, 10,
+                             'åwesome'),
+      array('frànçAIS is über-åwesome', 5, -10,
+                 'AIS is üb'),
+      array('frànçAIS is über-åwesome', 0, -10,
+            'frànçAIS is üb'),
+      array('frànçAIS is über-åwesome', 0, -1,
+        'frànçAIS is über-åwesom'),
+      array('frànçAIS is über-åwesome', -7, -2,
+                             'åweso'),
+      array('frànçAIS is über-åwesome', -7, -6,
+                             'å'),
+      array('frànçAIS is über-åwesome', -7, -7,
+                             ''),
+      array('frànçAIS is über-åwesome', -7, -8,
+                             ''),
+      array('...', 0, 2, '..'),
+      array('以呂波耳・ほへとち。リヌルヲ。', 1, 3,
+              '呂波耳'),
+
+    );
+
+    foreach ($testcase as $test) {
+      list($input, $start, $length, $output) = $test;
+      $result = drupal_substr($input, $start, $length);
+      $this->assertEqual($result, $output, t('%input substring at offset %offset for %length characters is %output (got %result)', array('%input' => $input, '%offset' => $start, '%length' => $length, '%output' => $output, '%result' => $result)));
+    }
+  }
+
+  /**
+   * Test decode_entities().
+   */
+  function testDecodeEntities() {
+    $testcase = array(
+      'Drupal' => 'Drupal',
+      '<script>' => '<script>',
+      '&lt;script&gt;' => '<script>',
+      '&#60;script&#62;' => '<script>',
+      '&amp;lt;script&amp;gt;' => '&lt;script&gt;',
+      '"' => '"',
+      '&#34;' => '"',
+      '&amp;#34;' => '&#34;',
+      '&quot;' => '"',
+      '&amp;quot;' => '&quot;',
+      "'" => "'",
+      '&#39;' => "'",
+      '&amp;#39;' => '&#39;',
+      '©' => '©',
+      '&copy;' => '©',
+      '&#169;' => '©',
+      '→' => '→',
+      '&#8594;' => '→',
+      '➼' => '➼',
+      '&#10172;' => '➼',
+      '&euro;' => '€',
+    );
+    foreach ($testcase as $input => $output) {
+      $this->assertEqual(decode_entities($input), $output, t('Make sure the decoded entity of @input is @output', array('@input' => $input, '@output' => $output)));
+    }
+  }
+
+  function testDecodeEntitiesExclusion() {
+    $testcase = array(
+      'Drupal' => 'Drupal',
+      '<script>' => '<script>',
+      '&lt;script&gt;' => '&lt;script>',
+      '&#60;script&#62;' => '&#60;script>',
+      '&amp;lt;script&amp;gt;' => '&amp;lt;script&amp;gt;',
+      '"' => '"',
+      '&#34;' => '&#34;',
+      '&amp;#34;' => '&amp;#34;',
+      '&quot;' => '&quot;',
+      '&amp;quot;' => '&amp;quot;',
+      "'" => "'",
+      '&#39;' => "'",
+      '&amp;#39;' => '&amp;#39;',
+      '©' => '©',
+      '&copy;' => '©',
+      '&#169;' => '©',
+      '→' => '→',
+      '&#8594;' => '→',
+      '➼' => '➼',
+      '&#10172;' => '➼',
+      '&euro;' => '€',
+    );
+    $exclude = array('<', '&', '"');
+    foreach ($testcase as $input => $output) {
+      $this->assertIdentical(decode_entities($input, $exclude), $output, t('Make sure the decoded entity of %input, excluding %excludes, is %output', array('%input' => $input, '%excludes' => implode(',', $exclude), '%output' => $output)));
+    }
+  }
+
+  /**
+   * Tests truncate_utf8().
+   */
+  function helperTestTruncate() {
+    // Each case is an array with input string, length to truncate to, and
+    // expected return value.
+
+    // Test non-wordsafe, non-ellipsis cases.
+    $non_wordsafe_non_ellipsis_cases = array(
+      array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
+      array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwesom'),
+      array('frànçAIS is über-åwesome', 17, 'frànçAIS is über-'),
+      array('以呂波耳・ほへとち。リヌルヲ。', 6, '以呂波耳・ほ'),
+    );
+    $this->runTruncateTests($non_wordsafe_non_ellipsis_cases, FALSE, FALSE);
+
+    // Test non-wordsafe, ellipsis cases.
+    $non_wordsafe_ellipsis_cases = array(
+      array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
+      array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwe...'),
+      array('frànçAIS is über-åwesome', 17, 'frànçAIS is üb...'),
+    );
+    $this->runTruncateTests($non_wordsafe_ellipsis_cases, FALSE, TRUE);
+
+    // Test wordsafe, ellipsis cases.
+    $wordsafe_ellipsis_cases = array(
+      array('123', 1, '.'),
+      array('123', 2, '..'),
+      array('123', 3, '123'),
+      array('1234', 3, '...'),
+      array('1234567890', 10, '1234567890'),
+      array('12345678901', 10, '1234567...'),
+      array('12345678901', 11, '12345678901'),
+      array('123456789012', 11, '12345678...'),
+      array('12345 7890', 10, '12345 7890'),
+      array('12345 7890', 9, '12345...'),
+      array('123 567 90', 10, '123 567 90'),
+      array('123 567 901', 10, '123 567...'),
+      array('Stop. Hammertime.', 17, 'Stop. Hammertime.'),
+      array('Stop. Hammertime.', 16, 'Stop....'),
+      array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
+      array('frànçAIS is über-åwesome', 23, 'frànçAIS is über...'),
+      array('frànçAIS is über-åwesome', 17, 'frànçAIS is...'),
+      array('¿Dónde está el niño?', 20, '¿Dónde está el niño?'),
+      array('¿Dónde está el niño?', 19, '¿Dónde está el...'),
+      array('¿Dónde está el niño?', 15, '¿Dónde está...'),
+      array('¿Dónde está el niño?', 10, '¿Dónde...'),
+      array('Help! Help! Help!', 17, 'Help! Help! Help!'),
+      array('Help! Help! Help!', 16, 'Help! Help!...'),
+      array('Help! Help! Help!', 15, 'Help! Help!...'),
+      array('Help! Help! Help!', 14, 'Help! Help!...'),
+      array('Help! Help! Help!', 13, 'Help! Help...'),
+      array('Help! Help! Help!', 12, 'Help!...'),
+      array('Help! Help! Help!', 11, 'Help!...'),
+      array('Help! Help! Help!', 10, 'Help!...'),
+      array('Help! Help! Help!', 9, 'Help!...'),
+      array('Help! Help! Help!', 8, 'Help!...'),
+      array('Help! Help! Help!', 7, 'Help...'),
+      array('Help! Help! Help!', 6, 'Hel...'),
+      array('Help! Help! Help!', 5, 'He...'),
+    );
+    $this->runTruncateTests($wordsafe_ellipsis_cases, TRUE, TRUE);
+  }
+
+  /**
+   * Runs test cases for helperTestTruncate().
+   *
+   * Runs each test case through truncate_utf8() and compares the output
+   * to the expected output.
+   *
+   * @param $cases
+   *   Cases array. Each case is an array with the input string, length to
+   *   truncate to, and expected output.
+   * @param $wordsafe
+   *   TRUE to use word-safe truncation, FALSE to not use word-safe truncation.
+   * @param $ellipsis
+   *   TRUE to append ... if the input is truncated, FALSE to not append ....
+   */
+  function runTruncateTests($cases, $wordsafe, $ellipsis) {
+    foreach ($cases as $case) {
+      list($input, $max_length, $expected) = $case;
+      $output = truncate_utf8($input, $max_length, $wordsafe, $ellipsis);
+      $this->assertEqual($output, $expected, t('%input truncate to %length characters with %wordsafe, %ellipsis is %expected (got %output)', array('%input' => $input, '%length' => $max_length, '%output' => $output, '%expected' => $expected, '%wordsafe' => ($wordsafe ? 'word-safe' : 'not word-safe'), '%ellipsis' => ($ellipsis ? 'ellipsis' : 'not ellipsis'))));
+    }
+  }
+}
diff --git a/modules/system/tests/update.test b/modules/system/tests/update.test
new file mode 100644
index 0000000..944dba7
--- /dev/null
+++ b/modules/system/tests/update.test
@@ -0,0 +1,116 @@
+<?php
+// $Id: update.test,v 1.2 2010/04/28 05:28:22 webchick Exp $
+
+/**
+ * @file
+ * Tests for the update system.
+ */
+
+/**
+ * Tests for the update dependency ordering system.
+ */
+class UpdateDependencyOrderingTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Update dependency ordering',
+      'description' => 'Test that update functions are run in the proper order.',
+      'group' => 'Update API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('update_test_1', 'update_test_2', 'update_test_3');
+    require_once DRUPAL_ROOT . '/includes/update.inc';
+  }
+
+  /**
+   * Test that updates within a single module run in the correct order.
+   */
+  function testUpdateOrderingSingleModule() {
+    $starting_updates = array(
+      'update_test_1' => 7000,
+    );
+    $expected_updates = array(
+      'update_test_1_update_7000',
+      'update_test_1_update_7001',
+      'update_test_1_update_7002',
+    );
+    $actual_updates = array_keys(update_resolve_dependencies($starting_updates));
+    $this->assertEqual($expected_updates, $actual_updates, t('Updates within a single module run in the correct order.'));
+  }
+
+  /**
+   * Test that dependencies between modules are resolved correctly.
+   */
+  function testUpdateOrderingModuleInterdependency() {
+    $starting_updates = array(
+      'update_test_2' => 7000,
+      'update_test_3' => 7000,
+    );
+    $update_order = array_keys(update_resolve_dependencies($starting_updates));
+    // Make sure that each dependency is satisfied.
+    $first_dependency_satisfied = array_search('update_test_2_update_7000', $update_order) < array_search('update_test_3_update_7000', $update_order);
+    $this->assertTrue($first_dependency_satisfied, t('The dependency of the second module on the first module is respected by the update function order.'));
+    $second_dependency_satisfied = array_search('update_test_3_update_7000', $update_order) < array_search('update_test_2_update_7001', $update_order);
+    $this->assertTrue($second_dependency_satisfied, t('The dependency of the first module on the second module is respected by the update function order.'));
+  }
+}
+
+/**
+ * Tests for missing update dependencies.
+ */
+class UpdateDependencyMissingTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Missing update dependencies',
+      'description' => 'Test that missing update dependencies are correctly flagged.',
+      'group' => 'Update API',
+    );
+  }
+
+  function setUp() {
+    // Only install update_test_2.module, even though its updates have a
+    // dependency on update_test_3.module.
+    parent::setUp('update_test_2');
+    require_once DRUPAL_ROOT . '/includes/update.inc';
+  }
+
+  function testMissingUpdate() {
+    $starting_updates = array(
+      'update_test_2' => 7000,
+    );
+    $update_graph = update_resolve_dependencies($starting_updates);
+    $this->assertTrue($update_graph['update_test_2_update_7000']['allowed'], t("The module's first update function is allowed to run, since it does not have any missing dependencies."));
+    $this->assertFalse($update_graph['update_test_2_update_7001']['allowed'], t("The module's second update function is not allowed to run, since it has a direct dependency on a missing update."));
+    $this->assertFalse($update_graph['update_test_2_update_7002']['allowed'], t("The module's third update function is not allowed to run, since it has an indirect dependency on a missing update."));
+  }
+}
+
+/**
+ * Tests for the invocation of hook_update_dependencies().
+ */
+class UpdateDependencyHookInvocationTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Update dependency hook invocation',
+      'description' => 'Test that the hook invocation for determining update dependencies works correctly.',
+      'group' => 'Update API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('update_test_1', 'update_test_2');
+    require_once DRUPAL_ROOT . '/includes/update.inc';
+  }
+
+  /**
+   * Test the structure of the array returned by hook_update_dependencies().
+   */
+  function testHookUpdateDependencies() {
+    $update_dependencies = update_retrieve_dependencies();
+    $this->assertTrue($update_dependencies['system'][7000]['update_test_1'] == 7000, t('An update function that has a dependency on two separate modules has the first dependency recorded correctly.'));
+    $this->assertTrue($update_dependencies['system'][7000]['update_test_2'] == 7001, t('An update function that has a dependency on two separate modules has the second dependency recorded correctly.'));
+    $this->assertTrue($update_dependencies['system'][7001]['update_test_1'] == 7002, t('An update function that depends on more than one update from the same module only has the dependency on the higher-numbered update function recorded.'));
+  }
+}
+
diff --git a/modules/system/tests/upgrade.test b/modules/system/tests/upgrade.test
new file mode 100644
index 0000000..7f2ddd2
--- /dev/null
+++ b/modules/system/tests/upgrade.test
@@ -0,0 +1,351 @@
+<?php
+// $Id: upgrade.test,v 1.2 2010/06/29 00:21:00 webchick Exp $
+
+/**
+ * Perform end-to-end tests of the upgrade path.
+ */
+abstract class UpgradePathTestCase extends DrupalWebTestCase {
+
+  /**
+   * The file path to the dumped database to load into the child site.
+   */
+  var $databaseDumpFile = NULL;
+
+  /**
+   * Flag that indicates whether the child site has been upgraded.
+   */
+  var $upgradedSite = FALSE;
+
+  /**
+   * Array of errors triggered during the upgrade process.
+   */
+  var $upgradeErrors = array();
+
+  /**
+   * Override of DrupalWebTestCase::setUp() specialized for upgrade testing.
+   */
+  protected function setUp() {
+    global $user, $language, $conf;
+
+    // Reset flags.
+    $this->upgradedSite = FALSE;
+    $this->upgradeErrors = array();
+
+    // Generate a temporary prefixed database to ensure that tests have a clean starting point.
+    $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
+    db_update('simpletest_test_id')
+      ->fields(array('last_prefix' => $this->databasePrefix))
+      ->condition('test_id', $this->testId)
+      ->execute();
+
+    // Clone the current connection and replace the current prefix.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::renameConnection('default', 'simpletest_original_default');
+    foreach ($connection_info as $target => $value) {
+      $connection_info[$target]['prefix'] = array(
+        'default' => $value['prefix']['default'] . $this->databasePrefix,
+      );
+    }
+    Database::addConnectionInfo('default', 'default', $connection_info['default']);
+
+    // Store necessary current values before switching to prefixed database.
+    $this->originalLanguage = $language;
+    $this->originalLanguageDefault = variable_get('language_default');
+    $this->originalFileDirectory = file_directory_path();
+    $this->originalProfile = drupal_get_profile();
+    $clean_url_original = variable_get('clean_url', 0);
+
+    // Unregister the registry.
+    // This is required to make sure that the database layer works properly.
+    spl_autoload_unregister('drupal_autoload_class');
+    spl_autoload_unregister('drupal_autoload_interface');
+
+    // Create test directories ahead of installation so fatal errors and debug
+    // information can be logged during installation process.
+    // Use mock files directories with the same prefix as the database.
+    $public_files_directory  = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10);
+    $private_files_directory = $public_files_directory . '/private';
+    $temp_files_directory    = $private_files_directory . '/temp';
+
+    // Create the directories.
+    file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY);
+    file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY);
+    $this->generatedTestFiles = FALSE;
+
+    // Log fatal errors.
+    ini_set('log_errors', 1);
+    ini_set('error_log', $public_files_directory . '/error.log');
+
+    // Reset all statics and variables to perform tests in a clean environment.
+    $conf = array();
+
+    // Load the database from the portable PHP dump.
+    require $this->databaseDumpFile;
+
+    // Set path variables.
+    $this->variable_set('file_public_path', $public_files_directory);
+    $this->variable_set('file_private_path', $private_files_directory);
+    $this->variable_set('file_temporary_path', $temp_files_directory);
+
+    $this->pass('Finished loading the dump.');
+
+    // Load user 1.
+    $this->originalUser = $user;
+    drupal_save_session(FALSE);
+    $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject();
+
+    // Generate and set a session cookie.
+    $this->curlInitialize();
+    $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
+    curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($this->session_name) . '=' . rawurlencode($sid));
+
+    // Force our way into the session of the child site.
+    drupal_save_session(TRUE);
+    _drupal_session_write($sid, '');
+    drupal_save_session(FALSE);
+
+    // Restore necessary variables.
+    $this->variable_set('clean_url', $clean_url_original);
+    $this->variable_set('site_mail', 'simpletest@example.com');
+
+    drupal_set_time_limit($this->timeLimit);
+  }
+
+  /**
+   * Override of DrupalWebTestCase::tearDown() specialized for upgrade testing.
+   */
+  protected function tearDown() {
+    global $user, $language;
+
+    // In case a fatal error occured that was not in the test process read the
+    // log to pick up any fatal errors.
+    simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
+
+    // Delete temporary files directory.
+    file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
+
+    // Get back to the original connection.
+    Database::removeConnection('default');
+    Database::renameConnection('simpletest_original_default', 'default');
+
+    // Remove all prefixed tables.
+    $tables = db_find_tables($this->databasePrefix . '%');
+    foreach ($tables as $table) {
+      db_drop_table($table);
+    }
+
+    // Return the user to the original one.
+    $user = $this->originalUser;
+    drupal_save_session(TRUE);
+
+    // Ensure that internal logged in variable and cURL options are reset.
+    $this->loggedInUser = FALSE;
+    $this->additionalCurlOptions = array();
+
+    // Reload module list and implementations to ensure that test module hooks
+    // aren't called after tests.
+    module_list(TRUE);
+    module_implements('', FALSE, TRUE);
+
+    // Reset the Field API.
+    field_cache_clear();
+
+    // Rebuild caches.
+    parent::refreshVariables();
+
+    // Reset language.
+    $language = $this->originalLanguage;
+    if ($this->originalLanguageDefault) {
+      $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
+    }
+
+    // Close the CURL handler.
+    $this->curlClose();
+  }
+
+  /**
+   * Specialized variable_set() that works even if the child site is not upgraded.
+   *
+   * @param $name
+   *   The name of the variable to set.
+   * @param $value
+   *   The value to set. This can be any PHP data type; these functions take care
+   *   of serialization as necessary.
+   */
+  protected function variable_set($name, $value) {
+    db_delete('variable')
+      ->condition('name', $name)
+      ->execute();
+    db_insert('variable')
+      ->fields(array(
+        'name' => $name,
+        'value' => serialize($value),
+      ))
+      ->execute();
+
+    try {
+      cache_clear_all('variables', 'cache');
+      cache_clear_all('variables', 'cache_bootstrap');
+    }
+    // Since cache_bootstrap won't exist in a Drupal 6 site, ignore the
+    // exception if the above fails.
+    catch (Exception $e) {}
+  }
+
+  /**
+   * Specialized refreshVariables().
+   */
+  protected function refreshVariables() {
+    // No operation if the child has not been upgraded yet.
+    if (!$this->upgradedSite) {
+      return parent::refreshVariables();
+    }
+  }
+
+  /**
+   * Perform the upgrade.
+   *
+   * @param $register_errors
+   *   Register the errors during the upgrade process as failures.
+   * @return
+   *   TRUE if the upgrade succeeded, FALSE otherwise.
+   */
+  protected function performUpgrade($register_errors = TRUE) {
+    $update_url = $GLOBALS['base_url'] . '/update.php';
+
+    // Load the first update screen.
+    $this->drupalGet($update_url, array('external' => TRUE));
+    if (!$this->assertResponse(200)) {
+      return FALSE;
+    }
+
+    // Continue.
+    $this->drupalPost(NULL, array(), t('Continue'));
+    if (!$this->assertResponse(200)) {
+      return FALSE;
+    }
+
+    // Go!
+    $this->drupalPost(NULL, array(), t('Apply pending updates'));
+    if (!$this->assertResponse(200)) {
+      return FALSE;
+    }
+
+    // Check for errors during the update process.
+    foreach ($this->xpath('//li[@class=:class]', array(':class' => 'failure')) as $element) {
+      $message = strip_tags($element->asXML());
+      $this->upgradeErrors[] = $message;
+      if ($register_errors) {
+        $this->fail($message);
+      }
+    }
+
+    if (!empty($this->upgradeErrors)) {
+      // Upgrade failed, the installation might be in an inconsistent state,
+      // don't process.
+      return FALSE;
+    }
+
+    // Check if there still are pending updates.
+    $this->drupalGet($update_url, array('external' => TRUE));
+    $this->drupalPost(NULL, array(), t('Continue'));
+    if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) {
+      return FALSE;
+    }
+
+    // Upgrade succeed, rebuild the environment so that we can call the API
+    // of the child site directly from this request.
+    $this->upgradedSite = TRUE;
+
+    // Reload module list and implementations.
+    module_list(TRUE);
+    module_implements('', FALSE, TRUE);
+
+    // Rebuild caches.
+    drupal_static_reset();
+    drupal_flush_all_caches();
+
+    // Register actions declared by any modules.
+    actions_synchronize();
+
+    // Reload global $conf array and permissions.
+    $this->refreshVariables();
+    $this->checkPermissions(array(), TRUE);
+
+    return TRUE;
+  }
+
+}
+
+/**
+ * Perform basic upgrade tests.
+ *
+ * Load a bare installation of Drupal 6 and run the upgrade process on it.
+ *
+ * The install only contains dblog (although it's optional, it's only so that
+ * another hook_watchdog module can take its place, the site is not functional
+ * without watchdog) and update.
+ */
+class BasicUpgradePath extends UpgradePathTestCase {
+  public static function getInfo() {
+    return array(
+      'name'  => 'Basic upgrade path',
+      'description'  => 'Basic upgrade path tests.',
+      'group' => 'Upgrade path',
+    );
+  }
+
+  public function setUp() {
+    // Path to the database dump.
+    $this->databaseDumpFile = drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.bare.database.php';
+    parent::setUp();
+  }
+
+  /**
+   * Test a failed upgrade, and verify that the failure is reported.
+   */
+  public function testFailedUpgrade() {
+    // Destroy a table that the upgrade process needs.
+    db_drop_table('access');
+    // Assert that the upgrade fails.
+    $this->assertFalse($this->performUpgrade(FALSE), t('A failed upgrade should return messages.'));
+  }
+
+  /**
+   * Test a successful upgrade.
+   */
+  public function testBasicUpgrade() {
+    $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+
+    // Hit the frontpage.
+    $this->drupalGet('');
+    $this->assertResponse(200);
+
+    // Verify that we are still logged in.
+    $this->drupalGet('user');
+    $this->clickLink(t('Edit'));
+    $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.'));
+
+    // Logout and verify that we can login back in with our initial password.
+    $this->drupalLogout();
+    $this->drupalLogin((object) array(
+      'uid' => 1,
+      'name' => 'admin',
+      'pass_raw' => 'admin',
+    ));
+
+    // Test that the site name is correctly displayed.
+    $this->assertText('Drupal 6', t('The site name is correctly displayed.'));
+
+    // Verify that the main admin sections are available.
+    $this->drupalGet('admin');
+    $this->assertText(t('Content'));
+    $this->assertText(t('Appearance'));
+    $this->assertText(t('People'));
+    $this->assertText(t('Configuration'));
+    $this->assertText(t('Reports'));
+    $this->assertText(t('Structure'));
+    $this->assertText(t('Modules'));
+  }
+}
diff --git a/modules/system/tests/xmlrpc.test b/modules/system/tests/xmlrpc.test
new file mode 100644
index 0000000..7625a8f
--- /dev/null
+++ b/modules/system/tests/xmlrpc.test
@@ -0,0 +1,220 @@
+<?php
+// $Id: xmlrpc.test,v 1.17 2010/05/06 05:59:31 webchick Exp $
+
+/**
+ * Perform basic XML-RPC tests that do not require addition callbacks.
+ */
+class XMLRPCBasicTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name'  => 'XML-RPC basic',
+      'description'  => 'Perform basic XML-RPC tests that do not require additional callbacks.',
+      'group' => 'XML-RPC',
+    );
+  }
+
+  /**
+   * Ensure that a basic XML-RPC call with no parameters works.
+   */
+  protected function testListMethods() {
+    // Minimum list of methods that should be included.
+    $minimum = array(
+      'system.multicall',
+      'system.methodSignature',
+      'system.getCapabilities',
+      'system.listMethods',
+      'system.methodHelp',
+    );
+
+    // Invoke XML-RPC call to get list of methods.
+    $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
+    $methods = xmlrpc($url, 'system.listMethods');
+
+    // Ensure that the minimum methods were found.
+    $count = 0;
+    foreach ($methods as $method) {
+      if (in_array($method, $minimum)) {
+        $count++;
+      }
+    }
+
+    $this->assertEqual($count, count($minimum), 'system.listMethods returned at least the minimum listing');
+  }
+
+  /**
+   * Ensure that XML-RPC correctly handles invalid messages when parsing.
+   */
+  protected function testInvalidMessageParsing() {
+    $invalid_messages = array(
+      array(
+        'message' => xmlrpc_message(''),
+        'assertion' => t('Empty message correctly rejected during parsing.'),
+      ),
+      array(
+        'message' => xmlrpc_message('<?xml version="1.0" encoding="ISO-8859-1"?>'),
+        'assertion' => t('Empty message with XML declaration correctly rejected during parsing.'),
+      ),
+      array(
+        'message' => xmlrpc_message('<?xml version="1.0"?><params><param><value><string>value</string></value></param></params>'),
+        'assertion' => t('Non-empty message without a valid message type is rejected during parsing.'),
+      ),
+      array(
+        'message' => xmlrpc_message('<methodResponse><params><param><value><string>value</string></value></param></methodResponse>'),
+        'assertion' => t('Non-empty malformed message is rejected during parsing.'),
+      ),
+    );
+
+    foreach ($invalid_messages as $assertion) {
+      $this->assertFalse(xmlrpc_message_parse($assertion['message']), $assertion['assertion']);
+    }
+  }
+}
+
+class XMLRPCValidator1IncTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name'  => 'XML-RPC validator',
+      'description'  => 'See !validator-link.', array('!validator-link' => l('the xmlrpc validator1 specification', 'http://www.xmlrpc.com/validator1Docs')),
+      'group' => 'XML-RPC',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('xmlrpc_test');
+  }
+
+  /**
+   * Run validator1 tests.
+   */
+  function testValidator1() {
+    $xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
+    srand();
+    mt_srand();
+
+
+    $array_1 = array(array('curly' => mt_rand(-100, 100)),
+                  array('curly' => mt_rand(-100, 100)),
+                  array('larry' => mt_rand(-100, 100)),
+                  array('larry' => mt_rand(-100, 100)),
+                  array('moe' => mt_rand(-100, 100)),
+                  array('moe' => mt_rand(-100, 100)),
+                  array('larry' => mt_rand(-100, 100)));
+    shuffle($array_1);
+    $l_res_1 = xmlrpc_test_arrayOfStructsTest($array_1);
+    $r_res_1 = xmlrpc($xml_url, 'validator1.arrayOfStructsTest', $array_1);
+    $this->assertIdentical($l_res_1, $r_res_1, 'array of structs test: %s');
+
+
+    $string_2 = 't\'&>>zf"md>yr>xlcev<h<"k&j<og"w&&>">>uai"np&s>>q\'&b<>"&&&';
+    $l_res_2 = xmlrpc_test_countTheEntities($string_2);
+    $r_res_2 = xmlrpc($xml_url, 'validator1.countTheEntities', $string_2);
+    $this->assertIdentical($l_res_2, $r_res_2, 'count the entities test: %s');
+
+
+    $struct_3 = array('moe' => mt_rand(-100, 100), 'larry' => mt_rand(-100, 100), 'curly' => mt_rand(-100, 100), 'homer' => mt_rand(-100, 100));
+    $l_res_3 = xmlrpc_test_easyStructTest($struct_3);
+    $r_res_3 = xmlrpc($xml_url, 'validator1.easyStructTest', $struct_3);
+    $this->assertIdentical($l_res_3, $r_res_3, 'easy struct test: %s');
+
+
+    $struct_4 = array('sub1' => array('bar' => 13),
+                    'sub2' => 14,
+                    'sub3' => array('foo' => 1, 'baz' => 2),
+                    'sub4' => array('ss' => array('sss' => array('ssss' => 'sssss'))));
+    $l_res_4 = xmlrpc_test_echoStructTest($struct_4);
+    $r_res_4 = xmlrpc($xml_url, 'validator1.echoStructTest', $struct_4);
+    $this->assertIdentical($l_res_4, $r_res_4, 'echo struct test: %s');
+
+    $int_5     = mt_rand(-100, 100);
+    $bool_5    = (($int_5 % 2) == 0);
+    $string_5  = $this->randomName();
+    $double_5  = (double)(mt_rand(-1000, 1000) / 100);
+    $time_5    = REQUEST_TIME;
+    $base64_5  = $this->randomName(100);
+    $l_res_5 = xmlrpc_test_manyTypesTest($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), $base64_5);
+    $l_res_5[5] = $l_res_5[5]->data; /* override warpping */
+    $r_res_5 = xmlrpc($xml_url, 'validator1.manyTypesTest', $int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5));
+    /* Contains objects, objects are not equal */
+    // See http://drupal.org/node/37766 why this currently fails
+    $this->assertEqual($l_res_5, $r_res_5, 'many types test: %s');
+
+
+    $size = mt_rand(100, 200);
+    $array_6 = array();
+    for ($i = 0; $i < $size; $i++) {
+      $array_6[] = $this->randomName(mt_rand(8, 12));
+    }
+
+    $l_res_6 = xmlrpc_test_moderateSizeArrayCheck($array_6);
+    $r_res_6 = xmlrpc($xml_url, 'validator1.moderateSizeArrayCheck', $array_6);
+    $this->assertIdentical($l_res_6, $r_res_6, 'moderate size array check: %s');
+
+
+    $struct_7 = array();
+    for ($y = 2000; $y < 2002; $y++) {
+      for ($m = 3; $m < 5; $m++) {
+        for ($d = 1; $d < 6; $d++) {
+          $ys = (string) $y;
+          $ms = sprintf('%02d', $m);
+          $ds = sprintf('%02d', $d);
+          $struct_7[$ys][$ms][$ds]['moe']   = mt_rand(-100, 100);
+          $struct_7[$ys][$ms][$ds]['larry'] = mt_rand(-100, 100);
+          $struct_7[$ys][$ms][$ds]['curly'] = mt_rand(-100, 100);
+        }
+      }
+    }
+    $l_res_7 = xmlrpc_test_nestedStructTest($struct_7);
+    $r_res_7 = xmlrpc($xml_url, 'validator1.nestedStructTest', $struct_7);
+    $this->assertIdentical($l_res_7, $r_res_7, 'nested struct test: %s');
+
+
+    $int_8 = mt_rand(-100, 100);
+    $l_res_8 = xmlrpc_test_simpleStructReturnTest($int_8);
+    $r_res_8 = xmlrpc($xml_url, 'validator1.simpleStructReturnTest', $int_8);
+    $this->assertIdentical($l_res_8, $r_res_8, 'simple struct test: %s');
+
+    /* Now test multicall */
+    $x = array();
+    $x[] = array('validator1.arrayOfStructsTest',     $array_1);
+    $x[] = array('validator1.countTheEntities',       $string_2);
+    $x[] = array('validator1.easyStructTest',         $struct_3);
+    $x[] = array('validator1.echoStructTest',         $struct_4);
+    $x[] = array('validator1.manyTypesTest',          $int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5));
+    $x[] = array('validator1.moderateSizeArrayCheck', $array_6);
+    $x[] = array('validator1.nestedStructTest',       $struct_7);
+    $x[] = array('validator1.simpleStructReturnTest', $int_8);
+
+    $a_l_res = array($l_res_1, $l_res_2, $l_res_3, $l_res_4, $l_res_5, $l_res_6, $l_res_7, $l_res_8);
+    $a_r_res = xmlrpc($xml_url, $x);
+    $this->assertEqual($a_l_res, $a_r_res, 'multicall equals result');
+  }
+}
+
+class XMLRPCMessagesTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name'  => 'XML-RPC message',
+      'description' => 'Test large messages.',
+      'group' => 'XML-RPC',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('xmlrpc_test');
+  }
+
+  /**
+   * Make sure that XML-RPC can transfer large messages.
+   */
+  function testSizedMessages() {
+    $xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
+    $sizes = array(8, 80, 160);
+    foreach ($sizes as $size) {
+      $xml_message_l = xmlrpc_test_message_sized_in_kb($size);
+      $xml_message_r = xmlrpc($xml_url, 'messages.messageSizedInKB', $size);
+
+      $this->assertEqual($xml_message_l, $xml_message_r, t('XML-RPC messages.messageSizedInKB of %s Kb size received', array('%s' => $size)));
+    }
+  }
+}
