Drupal PHP Cross Reference Content Management Systems

Source: /modules/field/tests/field.test - 3409 lines - 151525 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * @file
   5   * Tests for field.module.
   6   */
   7  
   8  /**
   9   * Parent class for Field API tests.
  10   */
  11  class FieldTestCase extends DrupalWebTestCase {
  12    var $default_storage = 'field_sql_storage';
  13  
  14    /**
  15     * Set the default field storage backend for fields created during tests.
  16     */
  17    function setUp() {
  18      // Since this is a base class for many test cases, support the same
  19      // flexibility that DrupalWebTestCase::setUp() has for the modules to be
  20      // passed in as either an array or a variable number of string arguments.
  21      $modules = func_get_args();
  22      if (isset($modules[0]) && is_array($modules[0])) {
  23        $modules = $modules[0];
  24      }
  25      parent::setUp($modules);
  26      // Set default storage backend.
  27      variable_set('field_storage_default', $this->default_storage);
  28    }
  29  
  30    /**
  31     * Generate random values for a field_test field.
  32     *
  33     * @param $cardinality
  34     *   Number of values to generate.
  35     * @return
  36     *  An array of random values, in the format expected for field values.
  37     */
  38    function _generateTestFieldValues($cardinality) {
  39      $values = array();
  40      for ($i = 0; $i < $cardinality; $i++) {
  41        // field_test fields treat 0 as 'empty value'.
  42        $values[$i]['value'] = mt_rand(1, 127);
  43      }
  44      return $values;
  45    }
  46  
  47    /**
  48     * Assert that a field has the expected values in an entity.
  49     *
  50     * This function only checks a single column in the field values.
  51     *
  52     * @param $entity
  53     *   The entity to test.
  54     * @param $field_name
  55     *   The name of the field to test
  56     * @param $langcode
  57     *   The language code for the values.
  58     * @param $expected_values
  59     *   The array of expected values.
  60     * @param $column
  61     *   (Optional) the name of the column to check.
  62     */
  63    function assertFieldValues($entity, $field_name, $langcode, $expected_values, $column = 'value') {
  64      $e = clone $entity;
  65      field_attach_load('test_entity', array($e->ftid => $e));
  66      $values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
  67      $this->assertEqual(count($values), count($expected_values), t('Expected number of values were saved.'));
  68      foreach ($expected_values as $key => $value) {
  69        $this->assertEqual($values[$key][$column], $value, t('Value @value was saved correctly.', array('@value' => $value)));
  70      }
  71    }
  72  }
  73  
  74  class FieldAttachTestCase extends FieldTestCase {
  75    function setUp() {
  76      // Since this is a base class for many test cases, support the same
  77      // flexibility that DrupalWebTestCase::setUp() has for the modules to be
  78      // passed in as either an array or a variable number of string arguments.
  79      $modules = func_get_args();
  80      if (isset($modules[0]) && is_array($modules[0])) {
  81        $modules = $modules[0];
  82      }
  83      if (!in_array('field_test', $modules)) {
  84        $modules[] = 'field_test';
  85      }
  86      parent::setUp($modules);
  87  
  88      $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
  89      $this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
  90      $this->field = field_create_field($this->field);
  91      $this->field_id = $this->field['id'];
  92      $this->instance = array(
  93        'field_name' => $this->field_name,
  94        'entity_type' => 'test_entity',
  95        'bundle' => 'test_bundle',
  96        'label' => $this->randomName() . '_label',
  97        'description' => $this->randomName() . '_description',
  98        'weight' => mt_rand(0, 127),
  99        'settings' => array(
 100          'test_instance_setting' => $this->randomName(),
 101        ),
 102        'widget' => array(
 103          'type' => 'test_field_widget',
 104          'label' => 'Test Field',
 105          'settings' => array(
 106            'test_widget_setting' => $this->randomName(),
 107          )
 108        )
 109      );
 110      field_create_instance($this->instance);
 111    }
 112  }
 113  
 114  /**
 115   * Unit test class for storage-related field_attach_* functions.
 116   *
 117   * All field_attach_* test work with all field_storage plugins and
 118   * all hook_field_attach_pre_{load,insert,update}() hooks.
 119   */
 120  class FieldAttachStorageTestCase extends FieldAttachTestCase {
 121    public static function getInfo() {
 122      return array(
 123        'name' => 'Field attach tests (storage-related)',
 124        'description' => 'Test storage-related Field Attach API functions.',
 125        'group' => 'Field API',
 126      );
 127    }
 128  
 129    /**
 130     * Check field values insert, update and load.
 131     *
 132     * Works independently of the underlying field storage backend. Inserts or
 133     * updates random field data and then loads and verifies the data.
 134     */
 135    function testFieldAttachSaveLoad() {
 136      // Configure the instance so that we test hook_field_load() (see
 137      // field_test_field_load() in field_test.module).
 138      $this->instance['settings']['test_hook_field_load'] = TRUE;
 139      field_update_instance($this->instance);
 140      $langcode = LANGUAGE_NONE;
 141  
 142      $entity_type = 'test_entity';
 143      $values = array();
 144  
 145      // TODO : test empty values filtering and "compression" (store consecutive deltas).
 146  
 147      // Preparation: create three revisions and store them in $revision array.
 148      for ($revision_id = 0; $revision_id < 3; $revision_id++) {
 149        $revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
 150        // Note: we try to insert one extra value.
 151        $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
 152        $current_revision = $revision_id;
 153        // If this is the first revision do an insert.
 154        if (!$revision_id) {
 155          $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
 156          field_attach_insert($entity_type, $revision[$revision_id]);
 157        }
 158        else {
 159          // Otherwise do an update.
 160          $revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
 161          field_attach_update($entity_type, $revision[$revision_id]);
 162        }
 163      }
 164  
 165      // Confirm current revision loads the correct data.
 166      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 167      field_attach_load($entity_type, array(0 => $entity));
 168      // Number of values per field loaded equals the field cardinality.
 169      $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Current revision: expected number of values'));
 170      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
 171        // The field value loaded matches the one inserted or updated.
 172        $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)));
 173        // The value added in hook_field_load() is found.
 174        $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)));
 175      }
 176  
 177      // Confirm each revision loads the correct data.
 178      foreach (array_keys($revision) as $revision_id) {
 179        $entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
 180        field_attach_load_revision($entity_type, array(0 => $entity));
 181        // Number of values per field loaded equals the field cardinality.
 182        $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
 183        for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
 184          // The field value loaded matches the one inserted or updated.
 185          $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)));
 186          // The value added in hook_field_load() is found.
 187          $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)));
 188        }
 189      }
 190    }
 191  
 192    /**
 193     * Test the 'multiple' load feature.
 194     */
 195    function testFieldAttachLoadMultiple() {
 196      $entity_type = 'test_entity';
 197      $langcode = LANGUAGE_NONE;
 198  
 199      // Define 2 bundles.
 200      $bundles = array(
 201        1 => 'test_bundle_1',
 202        2 => 'test_bundle_2',
 203      );
 204      field_test_create_bundle($bundles[1]);
 205      field_test_create_bundle($bundles[2]);
 206      // Define 3 fields:
 207      // - field_1 is in bundle_1 and bundle_2,
 208      // - field_2 is in bundle_1,
 209      // - field_3 is in bundle_2.
 210      $field_bundles_map = array(
 211        1 => array(1, 2),
 212        2 => array(1),
 213        3 => array(2),
 214      );
 215      for ($i = 1; $i <= 3; $i++) {
 216        $field_names[$i] = 'field_' . $i;
 217        $field = array('field_name' => $field_names[$i], 'type' => 'test_field');
 218        $field = field_create_field($field);
 219        $field_ids[$i] = $field['id'];
 220        foreach ($field_bundles_map[$i] as $bundle) {
 221          $instance = array(
 222            'field_name' => $field_names[$i],
 223            'entity_type' => 'test_entity',
 224            'bundle' => $bundles[$bundle],
 225            'settings' => array(
 226              // Configure the instance so that we test hook_field_load()
 227              // (see field_test_field_load() in field_test.module).
 228              'test_hook_field_load' => TRUE,
 229            ),
 230          );
 231          field_create_instance($instance);
 232        }
 233      }
 234  
 235      // Create one test entity per bundle, with random values.
 236      foreach ($bundles as $index => $bundle) {
 237        $entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
 238        $entity = clone($entities[$index]);
 239        $instances = field_info_instances('test_entity', $bundle);
 240        foreach ($instances as $field_name => $instance) {
 241          $values[$index][$field_name] = mt_rand(1, 127);
 242          $entity->$field_name = array($langcode => array(array('value' => $values[$index][$field_name])));
 243        }
 244        field_attach_insert($entity_type, $entity);
 245      }
 246  
 247      // Check that a single load correctly loads field values for both entities.
 248      field_attach_load($entity_type, $entities);
 249      foreach ($entities as $index => $entity) {
 250        $instances = field_info_instances($entity_type, $bundles[$index]);
 251        foreach ($instances as $field_name => $instance) {
 252          // The field value loaded matches the one inserted.
 253          $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
 254          // The value added in hook_field_load() is found.
 255          $this->assertEqual($entity->{$field_name}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
 256        }
 257      }
 258  
 259      // Check that the single-field load option works.
 260      $entity = field_test_create_stub_entity(1, 1, $bundles[1]);
 261      field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('field_id' => $field_ids[1]));
 262      $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['value'], $values[1][$field_names[1]], t('Entity %index: expected value was found.', array('%index' => 1)));
 263      $this->assertEqual($entity->{$field_names[1]}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => 1)));
 264      $this->assert(!isset($entity->{$field_names[2]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
 265      $this->assert(!isset($entity->{$field_names[3]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
 266    }
 267  
 268    /**
 269     * Test saving and loading fields using different storage backends.
 270     */
 271    function testFieldAttachSaveLoadDifferentStorage() {
 272      $entity_type = 'test_entity';
 273      $langcode = LANGUAGE_NONE;
 274  
 275      // Create two fields using different storage backends, and their instances.
 276      $fields = array(
 277        array(
 278          'field_name' => 'field_1',
 279          'type' => 'test_field',
 280          'cardinality' => 4,
 281          'storage' => array('type' => 'field_sql_storage')
 282        ),
 283        array(
 284          'field_name' => 'field_2',
 285          'type' => 'test_field',
 286          'cardinality' => 4,
 287          'storage' => array('type' => 'field_test_storage')
 288        ),
 289      );
 290      foreach ($fields as $field) {
 291        field_create_field($field);
 292        $instance = array(
 293          'field_name' => $field['field_name'],
 294          'entity_type' => 'test_entity',
 295          'bundle' => 'test_bundle',
 296        );
 297        field_create_instance($instance);
 298      }
 299  
 300      $entity_init = field_test_create_stub_entity();
 301  
 302      // Create entity and insert random values.
 303      $entity = clone($entity_init);
 304      $values = array();
 305      foreach ($fields as $field) {
 306        $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
 307        $entity->{$field['field_name']}[$langcode] = $values[$field['field_name']];
 308      }
 309      field_attach_insert($entity_type, $entity);
 310  
 311      // Check that values are loaded as expected.
 312      $entity = clone($entity_init);
 313      field_attach_load($entity_type, array($entity->ftid => $entity));
 314      foreach ($fields as $field) {
 315        $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}[$langcode], t('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
 316      }
 317    }
 318  
 319    /**
 320     * Test storage details alteration.
 321     *
 322     * @see field_test_storage_details_alter()
 323     */
 324    function testFieldStorageDetailsAlter() {
 325      $field_name = 'field_test_change_my_details';
 326      $field = array(
 327        'field_name' => $field_name,
 328        'type' => 'test_field',
 329        'cardinality' => 4,
 330        'storage' => array('type' => 'field_test_storage'),
 331      );
 332      $field = field_create_field($field);
 333      $instance = array(
 334        'field_name' => $field_name,
 335        'entity_type' => 'test_entity',
 336        'bundle' => 'test_bundle',
 337      );
 338      field_create_instance($instance);
 339  
 340      $field = field_info_field($instance['field_name']);
 341      $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
 342  
 343      // The storage details are indexed by a storage engine type.
 344      $this->assertTrue(array_key_exists('drupal_variables', $field['storage']['details']), t('The storage type is Drupal variables.'));
 345  
 346      $details = $field['storage']['details']['drupal_variables'];
 347  
 348      // The field_test storage details are indexed by variable name. The details
 349      // are altered, so moon and mars are correct for this test.
 350      $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), t('Moon is available in the instance array.'));
 351      $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), t('Mars is available in the instance array.'));
 352  
 353      // Test current and revision storage details together because the columns
 354      // are the same.
 355      foreach ((array) $field['columns'] as $column_name => $attributes) {
 356        $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]')));
 357        $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]')));
 358      }
 359    }
 360  
 361    /**
 362     * Tests insert and update with missing or NULL fields.
 363     */
 364    function testFieldAttachSaveMissingData() {
 365      $entity_type = 'test_entity';
 366      $entity_init = field_test_create_stub_entity();
 367      $langcode = LANGUAGE_NONE;
 368  
 369      // Insert: Field is missing.
 370      $entity = clone($entity_init);
 371      field_attach_insert($entity_type, $entity);
 372  
 373      $entity = clone($entity_init);
 374      field_attach_load($entity_type, array($entity->ftid => $entity));
 375      $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
 376  
 377      // Insert: Field is NULL.
 378      field_cache_clear();
 379      $entity = clone($entity_init);
 380      $entity->{$this->field_name} = NULL;
 381      field_attach_insert($entity_type, $entity);
 382  
 383      $entity = clone($entity_init);
 384      field_attach_load($entity_type, array($entity->ftid => $entity));
 385      $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
 386  
 387      // Add some real data.
 388      field_cache_clear();
 389      $entity = clone($entity_init);
 390      $values = $this->_generateTestFieldValues(1);
 391      $entity->{$this->field_name}[$langcode] = $values;
 392      field_attach_insert($entity_type, $entity);
 393  
 394      $entity = clone($entity_init);
 395      field_attach_load($entity_type, array($entity->ftid => $entity));
 396      $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
 397  
 398      // Update: Field is missing. Data should survive.
 399      field_cache_clear();
 400      $entity = clone($entity_init);
 401      field_attach_update($entity_type, $entity);
 402  
 403      $entity = clone($entity_init);
 404      field_attach_load($entity_type, array($entity->ftid => $entity));
 405      $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Update: missing field leaves existing values in place'));
 406  
 407      // Update: Field is NULL. Data should be wiped.
 408      field_cache_clear();
 409      $entity = clone($entity_init);
 410      $entity->{$this->field_name} = NULL;
 411      field_attach_update($entity_type, $entity);
 412  
 413      $entity = clone($entity_init);
 414      field_attach_load($entity_type, array($entity->ftid => $entity));
 415      $this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
 416  
 417      // Re-add some data.
 418      field_cache_clear();
 419      $entity = clone($entity_init);
 420      $values = $this->_generateTestFieldValues(1);
 421      $entity->{$this->field_name}[$langcode] = $values;
 422      field_attach_update($entity_type, $entity);
 423  
 424      $entity = clone($entity_init);
 425      field_attach_load($entity_type, array($entity->ftid => $entity));
 426      $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
 427  
 428      // Update: Field is empty array. Data should be wiped.
 429      field_cache_clear();
 430      $entity = clone($entity_init);
 431      $entity->{$this->field_name} = array();
 432      field_attach_update($entity_type, $entity);
 433  
 434      $entity = clone($entity_init);
 435      field_attach_load($entity_type, array($entity->ftid => $entity));
 436      $this->assertTrue(empty($entity->{$this->field_name}), t('Update: empty array removes existing values'));
 437    }
 438  
 439    /**
 440     * Test insert with missing or NULL fields, with default value.
 441     */
 442    function testFieldAttachSaveMissingDataDefaultValue() {
 443      // Add a default value function.
 444      $this->instance['default_value_function'] = 'field_test_default_value';
 445      field_update_instance($this->instance);
 446  
 447      $entity_type = 'test_entity';
 448      $entity_init = field_test_create_stub_entity();
 449      $langcode = LANGUAGE_NONE;
 450  
 451      // Insert: Field is NULL.
 452      $entity = clone($entity_init);
 453      $entity->{$this->field_name}[$langcode] = NULL;
 454      field_attach_insert($entity_type, $entity);
 455  
 456      $entity = clone($entity_init);
 457      field_attach_load($entity_type, array($entity->ftid => $entity));
 458      $this->assertTrue(empty($entity->{$this->field_name}[$langcode]), t('Insert: NULL field results in no value saved'));
 459  
 460      // Insert: Field is missing.
 461      field_cache_clear();
 462      $entity = clone($entity_init);
 463      field_attach_insert($entity_type, $entity);
 464  
 465      $entity = clone($entity_init);
 466      field_attach_load($entity_type, array($entity->ftid => $entity));
 467      $values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
 468      $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Insert: missing field results in default value saved'));
 469    }
 470  
 471    /**
 472     * Test field_attach_delete().
 473     */
 474    function testFieldAttachDelete() {
 475      $entity_type = 'test_entity';
 476      $langcode = LANGUAGE_NONE;
 477      $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 478  
 479      // Create revision 0
 480      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 481      $rev[0]->{$this->field_name}[$langcode] = $values;
 482      field_attach_insert($entity_type, $rev[0]);
 483  
 484      // Create revision 1
 485      $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
 486      $rev[1]->{$this->field_name}[$langcode] = $values;
 487      field_attach_update($entity_type, $rev[1]);
 488  
 489      // Create revision 2
 490      $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
 491      $rev[2]->{$this->field_name}[$langcode] = $values;
 492      field_attach_update($entity_type, $rev[2]);
 493  
 494      // Confirm each revision loads
 495      foreach (array_keys($rev) as $vid) {
 496        $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
 497        field_attach_load_revision($entity_type, array(0 => $read));
 498        $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
 499      }
 500  
 501      // Delete revision 1, confirm the other two still load.
 502      field_attach_delete_revision($entity_type, $rev[1]);
 503      foreach (array(0, 2) as $vid) {
 504        $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
 505        field_attach_load_revision($entity_type, array(0 => $read));
 506        $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
 507      }
 508  
 509      // Confirm the current revision still loads
 510      $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
 511      field_attach_load($entity_type, array(0 => $read));
 512      $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
 513  
 514      // Delete all field data, confirm nothing loads
 515      field_attach_delete($entity_type, $rev[2]);
 516      foreach (array(0, 1, 2) as $vid) {
 517        $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
 518        field_attach_load_revision($entity_type, array(0 => $read));
 519        $this->assertIdentical($read->{$this->field_name}, array(), "The test entity revision $vid is deleted.");
 520      }
 521      $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
 522      field_attach_load($entity_type, array(0 => $read));
 523      $this->assertIdentical($read->{$this->field_name}, array(), t('The test entity current revision is deleted.'));
 524    }
 525  
 526    /**
 527     * Test field_attach_create_bundle() and field_attach_rename_bundle().
 528     */
 529    function testFieldAttachCreateRenameBundle() {
 530      // Create a new bundle. This has to be initiated by the module so that its
 531      // hook_entity_info() is consistent.
 532      $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
 533      field_test_create_bundle($new_bundle);
 534  
 535      // Add an instance to that bundle.
 536      $this->instance['bundle'] = $new_bundle;
 537      field_create_instance($this->instance);
 538  
 539      // Save an entity with data in the field.
 540      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 541      $langcode = LANGUAGE_NONE;
 542      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 543      $entity->{$this->field_name}[$langcode] = $values;
 544      $entity_type = 'test_entity';
 545      field_attach_insert($entity_type, $entity);
 546  
 547      // Verify the field data is present on load.
 548      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 549      field_attach_load($entity_type, array(0 => $entity));
 550      $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
 551  
 552      // Rename the bundle. This has to be initiated by the module so that its
 553      // hook_entity_info() is consistent.
 554      $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
 555      field_test_rename_bundle($this->instance['bundle'], $new_bundle);
 556  
 557      // Check that the instance definition has been updated.
 558      $this->instance = field_info_instance($entity_type, $this->field_name, $new_bundle);
 559      $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
 560  
 561      // Verify the field data is present on load.
 562      $entity = field_test_create_stub_entity(0, 0, $new_bundle);
 563      field_attach_load($entity_type, array(0 => $entity));
 564      $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage");
 565    }
 566  
 567    /**
 568     * Test field_attach_delete_bundle().
 569     */
 570    function testFieldAttachDeleteBundle() {
 571      // Create a new bundle. This has to be initiated by the module so that its
 572      // hook_entity_info() is consistent.
 573      $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
 574      field_test_create_bundle($new_bundle);
 575  
 576      // Add an instance to that bundle.
 577      $this->instance['bundle'] = $new_bundle;
 578      field_create_instance($this->instance);
 579  
 580      // Create a second field for the test bundle
 581      $field_name = drupal_strtolower($this->randomName() . '_field_name');
 582      $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
 583      field_create_field($field);
 584      $instance = array(
 585        'field_name' => $field_name,
 586        'entity_type' => 'test_entity',
 587        'bundle' => $this->instance['bundle'],
 588        'label' => $this->randomName() . '_label',
 589        'description' => $this->randomName() . '_description',
 590        'weight' => mt_rand(0, 127),
 591        // test_field has no instance settings
 592        'widget' => array(
 593          'type' => 'test_field_widget',
 594          'settings' => array(
 595            'size' => mt_rand(0, 255))));
 596      field_create_instance($instance);
 597  
 598      // Save an entity with data for both fields
 599      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 600      $langcode = LANGUAGE_NONE;
 601      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 602      $entity->{$this->field_name}[$langcode] = $values;
 603      $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1);
 604      field_attach_insert('test_entity', $entity);
 605  
 606      // Verify the fields are present on load
 607      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 608      field_attach_load('test_entity', array(0 => $entity));
 609      $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded');
 610      $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded');
 611  
 612      // Delete the bundle. This has to be initiated by the module so that its
 613      // hook_entity_info() is consistent.
 614      field_test_delete_bundle($this->instance['bundle']);
 615  
 616      // Verify no data gets loaded
 617      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 618      field_attach_load('test_entity', array(0 => $entity));
 619      $this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field');
 620      $this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field');
 621  
 622      // Verify that the instances are gone
 623      $this->assertFalse(field_read_instance('test_entity', $this->field_name, $this->instance['bundle']), "First field is deleted");
 624      $this->assertFalse(field_read_instance('test_entity', $field_name, $instance['bundle']), "Second field is deleted");
 625    }
 626  }
 627  
 628  /**
 629   * Unit test class for non-storage related field_attach_* functions.
 630   */
 631  class FieldAttachOtherTestCase extends FieldAttachTestCase {
 632    public static function getInfo() {
 633      return array(
 634        'name' => 'Field attach tests (other)',
 635        'description' => 'Test other Field Attach API functions.',
 636        'group' => 'Field API',
 637      );
 638    }
 639  
 640    /**
 641     * Test field_attach_view() and field_attach_prepare_view().
 642     */
 643    function testFieldAttachView() {
 644      $entity_type = 'test_entity';
 645      $entity_init = field_test_create_stub_entity();
 646      $langcode = LANGUAGE_NONE;
 647  
 648      // Populate values to be displayed.
 649      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 650      $entity_init->{$this->field_name}[$langcode] = $values;
 651  
 652      // Simple formatter, label displayed.
 653      $entity = clone($entity_init);
 654      $formatter_setting = $this->randomName();
 655      $this->instance['display'] = array(
 656        'full' => array(
 657          'label' => 'above',
 658          'type' => 'field_test_default',
 659          'settings' => array(
 660            'test_formatter_setting' => $formatter_setting,
 661          )
 662        ),
 663      );
 664      field_update_instance($this->instance);
 665      field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
 666      $entity->content = field_attach_view($entity_type, $entity, 'full');
 667      $output = drupal_render($entity->content);
 668      $this->content = $output;
 669      $this->assertRaw($this->instance['label'], "Label is displayed.");
 670      foreach ($values as $delta => $value) {
 671        $this->content = $output;
 672        $this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
 673      }
 674  
 675      // Label hidden.
 676      $entity = clone($entity_init);
 677      $this->instance['display']['full']['label'] = 'hidden';
 678      field_update_instance($this->instance);
 679      field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
 680      $entity->content = field_attach_view($entity_type, $entity, 'full');
 681      $output = drupal_render($entity->content);
 682      $this->content = $output;
 683      $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
 684  
 685      // Field hidden.
 686      $entity = clone($entity_init);
 687      $this->instance['display'] = array(
 688        'full' => array(
 689          'label' => 'above',
 690          'type' => 'hidden',
 691        ),
 692      );
 693      field_update_instance($this->instance);
 694      field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
 695      $entity->content = field_attach_view($entity_type, $entity, 'full');
 696      $output = drupal_render($entity->content);
 697      $this->content = $output;
 698      $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
 699      foreach ($values as $delta => $value) {
 700        $this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
 701      }
 702  
 703      // Multiple formatter.
 704      $entity = clone($entity_init);
 705      $formatter_setting = $this->randomName();
 706      $this->instance['display'] = array(
 707        'full' => array(
 708          'label' => 'above',
 709          'type' => 'field_test_multiple',
 710          'settings' => array(
 711            'test_formatter_setting_multiple' => $formatter_setting,
 712          )
 713        ),
 714      );
 715      field_update_instance($this->instance);
 716      field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
 717      $entity->content = field_attach_view($entity_type, $entity, 'full');
 718      $output = drupal_render($entity->content);
 719      $display = $formatter_setting;
 720      foreach ($values as $delta => $value) {
 721        $display .= "|$delta:{$value['value']}";
 722      }
 723      $this->content = $output;
 724      $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
 725  
 726      // Test a formatter that uses hook_field_formatter_prepare_view().
 727      $entity = clone($entity_init);
 728      $formatter_setting = $this->randomName();
 729      $this->instance['display'] = array(
 730        'full' => array(
 731          'label' => 'above',
 732          'type' => 'field_test_with_prepare_view',
 733          'settings' => array(
 734            'test_formatter_setting_additional' => $formatter_setting,
 735          )
 736        ),
 737      );
 738      field_update_instance($this->instance);
 739      field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
 740      $entity->content = field_attach_view($entity_type, $entity, 'full');
 741      $output = drupal_render($entity->content);
 742      $this->content = $output;
 743      foreach ($values as $delta => $value) {
 744        $this->content = $output;
 745        $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
 746        $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
 747      }
 748  
 749      // TODO:
 750      // - check display order with several fields
 751  
 752      // Preprocess template.
 753      $variables = array();
 754      field_attach_preprocess($entity_type, $entity, $entity->content, $variables);
 755      $result = TRUE;
 756      foreach ($values as $delta => $item) {
 757        if ($variables[$this->field_name][$delta]['value'] !== $item['value']) {
 758          $result = FALSE;
 759          break;
 760        }
 761      }
 762      $this->assertTrue($result, t('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name)));
 763    }
 764  
 765    /**
 766     * Tests the 'multiple entity' behavior of field_attach_prepare_view().
 767     */
 768    function testFieldAttachPrepareViewMultiple() {
 769      $entity_type = 'test_entity';
 770      $langcode = LANGUAGE_NONE;
 771  
 772      // Set the instance to be hidden.
 773      $this->instance['display']['full']['type'] = 'hidden';
 774      field_update_instance($this->instance);
 775  
 776      // Set up a second instance on another bundle, with a formatter that uses
 777      // hook_field_formatter_prepare_view().
 778      field_test_create_bundle('test_bundle_2');
 779      $formatter_setting = $this->randomName();
 780      $this->instance2 = $this->instance;
 781      $this->instance2['bundle'] = 'test_bundle_2';
 782      $this->instance2['display']['full'] = array(
 783        'type' => 'field_test_with_prepare_view',
 784        'settings' => array(
 785          'test_formatter_setting_additional' => $formatter_setting,
 786        )
 787      );
 788      field_create_instance($this->instance2);
 789  
 790      // Create one entity in each bundle.
 791      $entity1_init = field_test_create_stub_entity(1, 1, 'test_bundle');
 792      $values1 = $this->_generateTestFieldValues($this->field['cardinality']);
 793      $entity1_init->{$this->field_name}[$langcode] = $values1;
 794  
 795      $entity2_init = field_test_create_stub_entity(2, 2, 'test_bundle_2');
 796      $values2 = $this->_generateTestFieldValues($this->field['cardinality']);
 797      $entity2_init->{$this->field_name}[$langcode] = $values2;
 798  
 799      // Run prepare_view, and check that the entities come out as expected.
 800      $entity1 = clone($entity1_init);
 801      $entity2 = clone($entity2_init);
 802      field_attach_prepare_view($entity_type, array($entity1->ftid => $entity1, $entity2->ftid => $entity2), 'full');
 803      $this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.');
 804      $this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.');
 805  
 806      // Same thing, reversed order.
 807      $entity1 = clone($entity1_init);
 808      $entity2 = clone($entity2_init);
 809      field_attach_prepare_view($entity_type, array($entity2->ftid => $entity2, $entity1->ftid => $entity1), 'full');
 810      $this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.');
 811      $this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.');
 812    }
 813  
 814    /**
 815     * Test field cache.
 816     */
 817    function testFieldAttachCache() {
 818      // Initialize random values and a test entity.
 819      $entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
 820      $langcode = LANGUAGE_NONE;
 821      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 822  
 823      // Non-cacheable entity type.
 824      $entity_type = 'test_entity';
 825      $cid = "field:$entity_type:{$entity_init->ftid}";
 826  
 827      // Check that no initial cache entry is present.
 828      $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
 829  
 830      // Save, and check that no cache entry is present.
 831      $entity = clone($entity_init);
 832      $entity->{$this->field_name}[$langcode] = $values;
 833      field_attach_insert($entity_type, $entity);
 834      $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert'));
 835  
 836      // Load, and check that no cache entry is present.
 837      $entity = clone($entity_init);
 838      field_attach_load($entity_type, array($entity->ftid => $entity));
 839      $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load'));
 840  
 841  
 842      // Cacheable entity type.
 843      $entity_type = 'test_cacheable_entity';
 844      $cid = "field:$entity_type:{$entity_init->ftid}";
 845      $instance = $this->instance;
 846      $instance['entity_type'] = $entity_type;
 847      field_create_instance($instance);
 848  
 849      // Check that no initial cache entry is present.
 850      $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
 851  
 852      // Save, and check that no cache entry is present.
 853      $entity = clone($entity_init);
 854      $entity->{$this->field_name}[$langcode] = $values;
 855      field_attach_insert($entity_type, $entity);
 856      $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert'));
 857  
 858      // Load a single field, and check that no cache entry is present.
 859      $entity = clone($entity_init);
 860      field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id));
 861      $cache = cache_get($cid, 'cache_field');
 862      $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on loading a single field'));
 863  
 864      // Load, and check that a cache entry is present with the expected values.
 865      $entity = clone($entity_init);
 866      field_attach_load($entity_type, array($entity->ftid => $entity));
 867      $cache = cache_get($cid, 'cache_field');
 868      $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
 869  
 870      // Update with different values, and check that the cache entry is wiped.
 871      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 872      $entity = clone($entity_init);
 873      $entity->{$this->field_name}[$langcode] = $values;
 874      field_attach_update($entity_type, $entity);
 875      $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update'));
 876  
 877      // Load, and check that a cache entry is present with the expected values.
 878      $entity = clone($entity_init);
 879      field_attach_load($entity_type, array($entity->ftid => $entity));
 880      $cache = cache_get($cid, 'cache_field');
 881      $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
 882  
 883      // Create a new revision, and check that the cache entry is wiped.
 884      $entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
 885      $values = $this->_generateTestFieldValues($this->field['cardinality']);
 886      $entity = clone($entity_init);
 887      $entity->{$this->field_name}[$langcode] = $values;
 888      field_attach_update($entity_type, $entity);
 889      $cache = cache_get($cid, 'cache_field');
 890      $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation'));
 891  
 892      // Load, and check that a cache entry is present with the expected values.
 893      $entity = clone($entity_init);
 894      field_attach_load($entity_type, array($entity->ftid => $entity));
 895      $cache = cache_get($cid, 'cache_field');
 896      $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
 897  
 898      // Delete, and check that the cache entry is wiped.
 899      field_attach_delete($entity_type, $entity);
 900      $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete'));
 901    }
 902  
 903    /**
 904     * Test field_attach_validate().
 905     *
 906     * Verify that field_attach_validate() invokes the correct
 907     * hook_field_validate.
 908     */
 909    function testFieldAttachValidate() {
 910      $entity_type = 'test_entity';
 911      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 912      $langcode = LANGUAGE_NONE;
 913  
 914      // Set up values to generate errors
 915      $values = array();
 916      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
 917        $values[$delta]['value'] = -1;
 918      }
 919      // Arrange for item 1 not to generate an error
 920      $values[1]['value'] = 1;
 921      $entity->{$this->field_name}[$langcode] = $values;
 922  
 923      try {
 924        field_attach_validate($entity_type, $entity);
 925      }
 926      catch (FieldValidationException $e) {
 927        $errors = $e->errors;
 928      }
 929  
 930      foreach ($values as $delta => $value) {
 931        if ($value['value'] != 1) {
 932          $this->assertIdentical($errors[$this->field_name][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
 933          $this->assertEqual(count($errors[$this->field_name][$langcode][$delta]), 1, "Only one error set on value $delta");
 934          unset($errors[$this->field_name][$langcode][$delta]);
 935        }
 936        else {
 937          $this->assertFalse(isset($errors[$this->field_name][$langcode][$delta]), "No error set on value $delta");
 938        }
 939      }
 940      $this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set');
 941  
 942      // Check that cardinality is validated.
 943      $entity->{$this->field_name}[$langcode] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
 944      try {
 945        field_attach_validate($entity_type, $entity);
 946      }
 947      catch (FieldValidationException $e) {
 948        $errors = $e->errors;
 949      }
 950      $this->assertEqual($errors[$this->field_name][$langcode][0][0]['error'], 'field_cardinality', t('Cardinality validation failed.'));
 951  
 952    }
 953  
 954    /**
 955     * Test field_attach_form().
 956     *
 957     * This could be much more thorough, but it does verify that the correct
 958     * widgets show up.
 959     */
 960    function testFieldAttachForm() {
 961      $entity_type = 'test_entity';
 962      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 963  
 964      $form = array();
 965      $form_state = form_state_defaults();
 966      field_attach_form($entity_type, $entity, $form, $form_state);
 967  
 968      $langcode = LANGUAGE_NONE;
 969      $this->assertEqual($form[$this->field_name][$langcode]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
 970      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
 971        // field_test_widget uses 'textfield'
 972        $this->assertEqual($form[$this->field_name][$langcode][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
 973      }
 974    }
 975  
 976    /**
 977     * Test field_attach_submit().
 978     */
 979    function testFieldAttachSubmit() {
 980      $entity_type = 'test_entity';
 981      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
 982  
 983      // Build the form.
 984      $form = array();
 985      $form_state = form_state_defaults();
 986      field_attach_form($entity_type, $entity, $form, $form_state);
 987  
 988      // Simulate incoming values.
 989      $values = array();
 990      $weights = array();
 991      for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
 992        $values[$delta]['value'] = mt_rand(1, 127);
 993        // Assign random weight.
 994        do {
 995          $weight = mt_rand(0, $this->field['cardinality']);
 996        } while (in_array($weight, $weights));
 997        $weights[$delta] = $weight;
 998        $values[$delta]['_weight'] = $weight;
 999      }
1000      // Leave an empty value. 'field_test' fields are empty if empty().
1001      $values[1]['value'] = 0;
1002  
1003      $langcode = LANGUAGE_NONE;
1004      // Pretend the form has been built.
1005      drupal_prepare_form('field_test_entity_form', $form, $form_state);
1006      drupal_process_form('field_test_entity_form', $form, $form_state);
1007      $form_state['values'][$this->field_name][$langcode] = $values;
1008      field_attach_submit($entity_type, $entity, $form, $form_state);
1009  
1010      asort($weights);
1011      $expected_values = array();
1012      foreach ($weights as $key => $value) {
1013        if ($key != 1) {
1014          $expected_values[] = array('value' => $values[$key]['value']);
1015        }
1016      }
1017      $this->assertIdentical($entity->{$this->field_name}[$langcode], $expected_values, 'Submit filters empty values');
1018    }
1019  }
1020  
1021  class FieldInfoTestCase extends FieldTestCase {
1022  
1023    public static function getInfo() {
1024      return array(
1025        'name' => 'Field info tests',
1026        'description' => 'Get information about existing fields, instances and bundles.',
1027        'group' => 'Field API',
1028      );
1029    }
1030  
1031    function setUp() {
1032      parent::setUp('field_test');
1033    }
1034  
1035    /**
1036     * Test that field types and field definitions are correcly cached.
1037     */
1038    function testFieldInfo() {
1039      // Test that field_test module's fields, widgets, and formatters show up.
1040  
1041      $field_test_info = field_test_field_info();
1042      // We need to account for the existence of user_field_info_alter().
1043      foreach (array_keys($field_test_info) as $name) {
1044        $field_test_info[$name]['instance_settings']['user_register_form'] = FALSE;
1045      }
1046      $info = field_info_field_types();
1047      foreach ($field_test_info as $t_key => $field_type) {
1048        foreach ($field_type as $key => $val) {
1049          $this->assertEqual($info[$t_key][$key], $val, t("Field type $t_key key $key is $val"));
1050        }
1051        $this->assertEqual($info[$t_key]['module'], 'field_test',  t("Field type field_test module appears"));
1052      }
1053  
1054      $formatter_info = field_test_field_formatter_info();
1055      $info = field_info_formatter_types();
1056      foreach ($formatter_info as $f_key => $formatter) {
1057        foreach ($formatter as $key => $val) {
1058          $this->assertEqual($info[$f_key][$key], $val, t("Formatter type $f_key key $key is $val"));
1059        }
1060        $this->assertEqual($info[$f_key]['module'], 'field_test',  t("Formatter type field_test module appears"));
1061      }
1062  
1063      $widget_info = field_test_field_widget_info();
1064      $info = field_info_widget_types();
1065      foreach ($widget_info as $w_key => $widget) {
1066        foreach ($widget as $key => $val) {
1067          $this->assertEqual($info[$w_key][$key], $val, t("Widget type $w_key key $key is $val"));
1068        }
1069        $this->assertEqual($info[$w_key]['module'], 'field_test',  t("Widget type field_test module appears"));
1070      }
1071  
1072      $storage_info = field_test_field_storage_info();
1073      $info = field_info_storage_types();
1074      foreach ($storage_info as $s_key => $storage) {
1075        foreach ($storage as $key => $val) {
1076          $this->assertEqual($info[$s_key][$key], $val, t("Storage type $s_key key $key is $val"));
1077        }
1078        $this->assertEqual($info[$s_key]['module'], 'field_test',  t("Storage type field_test module appears"));
1079      }
1080  
1081      // Verify that no unexpected instances exist.
1082      $instances = field_info_instances('test_entity');
1083      $expected = array('test_bundle' => array());
1084      $this->assertIdentical($instances, $expected, "field_info_instances('test_entity') returns " . var_export($expected, TRUE) . '.');
1085      $instances = field_info_instances('test_entity', 'test_bundle');
1086      $this->assertIdentical($instances, array(), "field_info_instances('test_entity', 'test_bundle') returns an empty array.");
1087  
1088      // Create a field, verify it shows up.
1089      $core_fields = field_info_fields();
1090      $field = array(
1091        'field_name' => drupal_strtolower($this->randomName()),
1092        'type' => 'test_field',
1093      );
1094      field_create_field($field);
1095      $fields = field_info_fields();
1096      $this->assertEqual(count($fields), count($core_fields) + 1, t('One new field exists'));
1097      $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], t('info fields contains field name'));
1098      $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], t('info fields contains field type'));
1099      $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', t('info fields contains field module'));
1100      $settings = array('test_field_setting' => 'dummy test string');
1101      foreach ($settings as $key => $val) {
1102        $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, t("Field setting $key has correct default value $val"));
1103      }
1104      $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, t('info fields contains cardinality 1'));
1105      $this->assertEqual($fields[$field['field_name']]['active'], 1, t('info fields contains active 1'));
1106  
1107      // Create an instance, verify that it shows up
1108      $instance = array(
1109        'field_name' => $field['field_name'],
1110        'entity_type' => 'test_entity',
1111        'bundle' => 'test_bundle',
1112        'label' => $this->randomName(),
1113        'description' => $this->randomName(),
1114        'weight' => mt_rand(0, 127),
1115        // test_field has no instance settings
1116        'widget' => array(
1117          'type' => 'test_field_widget',
1118          'settings' => array(
1119            'test_setting' => 999)));
1120      field_create_instance($instance);
1121  
1122      $info = entity_get_info('test_entity');
1123      $instances = field_info_instances('test_entity', $instance['bundle']);
1124      $this->assertEqual(count($instances), 1, format_string('One instance shows up in info when attached to a bundle on a @label.', array(
1125        '@label' => $info['label']
1126      )));
1127      $this->assertTrue($instance < $instances[$instance['field_name']], t('Instance appears in info correctly'));
1128  
1129      // Test a valid entity type but an invalid bundle.
1130      $instances = field_info_instances('test_entity', 'invalid_bundle');
1131      $this->assertIdentical($instances, array(), "field_info_instances('test_entity', 'invalid_bundle') returns an empty array.");
1132  
1133      // Test invalid entity type and bundle.
1134      $instances = field_info_instances('invalid_entity', $instance['bundle']);
1135      $this->assertIdentical($instances, array(), "field_info_instances('invalid_entity', 'test_bundle') returns an empty array.");
1136  
1137      // Test invalid entity type, no bundle provided.
1138      $instances = field_info_instances('invalid_entity');
1139      $this->assertIdentical($instances, array(), "field_info_instances('invalid_entity') returns an empty array.");
1140  
1141      // Test with an entity type that has no bundles.
1142      $instances = field_info_instances('user');
1143      $expected = array('user' => array());
1144      $this->assertIdentical($instances, $expected, "field_info_instances('user') returns " . var_export($expected, TRUE) . '.');
1145      $instances = field_info_instances('user', 'user');
1146      $this->assertIdentical($instances, array(), "field_info_instances('user', 'user') returns an empty array.");
1147    }
1148  
1149    /**
1150     * Test that cached field definitions are ready for current runtime context.
1151     */
1152    function testFieldPrepare() {
1153      $field_definition = array(
1154        'field_name' => 'field',
1155        'type' => 'test_field',
1156      );
1157      field_create_field($field_definition);
1158  
1159      // Simulate a stored field definition missing a field setting (e.g. a
1160      // third-party module adding a new field setting has been enabled, and
1161      // existing fields do not know the setting yet).
1162      $data = db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']))->fetchField();
1163      $data = unserialize($data);
1164      $data['settings'] = array();
1165      db_update('field_config')
1166        ->fields(array('data' => serialize($data)))
1167        ->condition('field_name', $field_definition['field_name'])
1168        ->execute();
1169  
1170      field_cache_clear();
1171  
1172      // Read the field back.
1173      $field = field_info_field($field_definition['field_name']);
1174  
1175      // Check that all expected settings are in place.
1176      $field_type = field_info_field_types($field_definition['type']);
1177      $this->assertIdentical($field['settings'], $field_type['settings'], t('All expected default field settings are present.'));
1178    }
1179  
1180    /**
1181     * Test that cached instance definitions are ready for current runtime context.
1182     */
1183    function testInstancePrepare() {
1184      $field_definition = array(
1185        'field_name' => 'field',
1186        'type' => 'test_field',
1187      );
1188      field_create_field($field_definition);
1189      $instance_definition = array(
1190        'field_name' => $field_definition['field_name'],
1191        'entity_type' => 'test_entity',
1192        'bundle' => 'test_bundle',
1193      );
1194      field_create_instance($instance_definition);
1195  
1196      // Simulate a stored instance definition missing various settings (e.g. a
1197      // third-party module adding instance, widget or display settings has been
1198      // enabled, but existing instances do not know the new settings).
1199      $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();
1200      $data = unserialize($data);
1201      $data['settings'] = array();
1202      $data['widget']['settings'] = 'unavailable_widget';
1203      $data['widget']['settings'] = array();
1204      $data['display']['default']['type'] = 'unavailable_formatter';
1205      $data['display']['default']['settings'] = array();
1206      db_update('field_config_instance')
1207        ->fields(array('data' => serialize($data)))
1208        ->condition('field_name', $instance_definition['field_name'])
1209        ->condition('bundle', $instance_definition['bundle'])
1210        ->execute();
1211  
1212      field_cache_clear();
1213  
1214      // Read the instance back.
1215      $instance = field_info_instance($instance_definition['entity_type'], $instance_definition['field_name'], $instance_definition['bundle']);
1216  
1217      // Check that all expected instance settings are in place.
1218      $field_type = field_info_field_types($field_definition['type']);
1219      $this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
1220  
1221      // Check that the default widget is used and expected settings are in place.
1222      $this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
1223      $widget_type = field_info_widget_types($instance['widget']['type']);
1224      $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
1225  
1226      // Check that display settings are set for the 'default' mode.
1227      $display = $instance['display']['default'];
1228      $this->assertIdentical($display['type'], $field_type['default_formatter'], t("Formatter is set for the 'default' view mode"));
1229      $formatter_type = field_info_formatter_types($display['type']);
1230      $this->assertIdentical($display['settings'], $formatter_type['settings'] , t("Formatter settings are set for the 'default' view mode"));
1231    }
1232  
1233    /**
1234     * Test that instances on disabled entity types are filtered out.
1235     */
1236    function testInstanceDisabledEntityType() {
1237      // For this test the field type and the entity type must be exposed by
1238      // different modules.
1239      $field_definition = array(
1240        'field_name' => 'field',
1241        'type' => 'test_field',
1242      );
1243      field_create_field($field_definition);
1244      $instance_definition = array(
1245        'field_name' => 'field',
1246        'entity_type' => 'comment',
1247        'bundle' => 'comment_node_article',
1248      );
1249      field_create_instance($instance_definition);
1250  
1251      // Disable coment module. This clears field_info cache.
1252      module_disable(array('comment'));
1253      $this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), t('No instances are returned on disabled entity types.'));
1254    }
1255  
1256    /**
1257     * Test that the field_info settings convenience functions work.
1258     */
1259    function testSettingsInfo() {
1260      $info = field_test_field_info();
1261      // We need to account for the existence of user_field_info_alter().
1262      foreach (array_keys($info) as $name) {
1263        $info[$name]['instance_settings']['user_register_form'] = FALSE;
1264      }
1265      foreach ($info as $type => $data) {
1266        $this->assertIdentical(field_info_field_settings($type), $data['settings'], "field_info_field_settings returns {$type}'s field settings");
1267        $this->assertIdentical(field_info_instance_settings($type), $data['instance_settings'], "field_info_field_settings returns {$type}'s field instance settings");
1268      }
1269  
1270      $info = field_test_field_widget_info();
1271      foreach ($info as $type => $data) {
1272        $this->assertIdentical(field_info_widget_settings($type), $data['settings'], "field_info_widget_settings returns {$type}'s widget settings");
1273      }
1274  
1275      $info = field_test_field_formatter_info();
1276      foreach ($info as $type => $data) {
1277        $this->assertIdentical(field_info_formatter_settings($type), $data['settings'], "field_info_formatter_settings returns {$type}'s formatter settings");
1278      }
1279    }
1280  }
1281  
1282  class FieldFormTestCase extends FieldTestCase {
1283    public static function getInfo() {
1284      return array(
1285        'name' => 'Field form tests',
1286        'description' => 'Test Field form handling.',
1287        'group' => 'Field API',
1288      );
1289    }
1290  
1291    function setUp() {
1292      parent::setUp('field_test');
1293  
1294      $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
1295      $this->drupalLogin($web_user);
1296  
1297      $this->field_single = array('field_name' => 'field_single', 'type' => 'test_field');
1298      $this->field_multiple = array('field_name' => 'field_multiple', 'type' => 'test_field', 'cardinality' => 4);
1299      $this->field_unlimited = array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
1300  
1301      $this->instance = array(
1302        'entity_type' => 'test_entity',
1303        'bundle' => 'test_bundle',
1304        'label' => $this->randomName() . '_label',
1305        'description' => $this->randomName() . '_description',
1306        'weight' => mt_rand(0, 127),
1307        'settings' => array(
1308          'test_instance_setting' => $this->randomName(),
1309        ),
1310        'widget' => array(
1311          'type' => 'test_field_widget',
1312          'label' => 'Test Field',
1313          'settings' => array(
1314            'test_widget_setting' => $this->randomName(),
1315          )
1316        )
1317      );
1318    }
1319  
1320    function testFieldFormSingle() {
1321      $this->field = $this->field_single;
1322      $this->field_name = $this->field['field_name'];
1323      $this->instance['field_name'] = $this->field_name;
1324      field_create_field($this->field);
1325      field_create_instance($this->instance);
1326      $langcode = LANGUAGE_NONE;
1327  
1328      // Display creation form.
1329      $this->drupalGet('test-entity/add/test-bundle');
1330      $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
1331      $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
1332      // TODO : check that the widget is populated with default value ?
1333  
1334      // Submit with invalid value (field-level validation).
1335      $edit = array("{$this->field_name}[$langcode][0][value]" => -1);
1336      $this->drupalPost(NULL, $edit, t('Save'));
1337      $this->assertRaw(t('%name does not accept the value -1.', array('%name' => $this->instance['label'])), 'Field validation fails with invalid input.');
1338      // TODO : check that the correct field is flagged for error.
1339  
1340      // Create an entity
1341      $value = mt_rand(1, 127);
1342      $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1343      $this->drupalPost(NULL, $edit, t('Save'));
1344      preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
1345      $id = $match[1];
1346      $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
1347      $entity = field_test_entity_test_load($id);
1348      $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved');
1349  
1350      // Display edit form.
1351      $this->drupalGet('test-entity/manage/' . $id . '/edit');
1352      $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", $value, 'Widget is displayed with the correct default value');
1353      $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
1354  
1355      // Update the entity.
1356      $value = mt_rand(1, 127);
1357      $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1358      $this->drupalPost(NULL, $edit, t('Save'));
1359      $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
1360      $entity = field_test_entity_test_load($id);
1361      $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was updated');
1362  
1363      // Empty the field.
1364      $value = '';
1365      $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1366      $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save'));
1367      $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
1368      $entity = field_test_entity_test_load($id);
1369      $this->assertIdentical($entity->{$this->field_name}, array(), 'Field was emptied');
1370  
1371    }
1372  
1373    function testFieldFormSingleRequired() {
1374      $this->field = $this->field_single;
1375      $this->field_name = $this->field['field_name'];
1376      $this->instance['field_name'] = $this->field_name;
1377      $this->instance['required'] = TRUE;
1378      field_create_field($this->field);
1379      field_create_instance($this->instance);
1380      $langcode = LANGUAGE_NONE;
1381  
1382      // Submit with missing required value.
1383      $edit = array();
1384      $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
1385      $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
1386  
1387      // Create an entity
1388      $value = mt_rand(1, 127);
1389      $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1390      $this->drupalPost(NULL, $edit, t('Save'));
1391      preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
1392      $id = $match[1];
1393      $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
1394      $entity = field_test_entity_test_load($id);
1395      $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved');
1396  
1397      // Edit with missing required value.
1398      $value = '';
1399      $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1400      $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save'));
1401      $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
1402    }
1403  
1404  //  function testFieldFormMultiple() {
1405  //    $this->field = $this->field_multiple;
1406  //    $this->field_name = $this->field['field_name'];
1407  //    $this->instance['field_name'] = $this->field_name;
1408  //    field_create_field($this->field);
1409  //    field_create_instance($this->instance);
1410  //  }
1411  
1412    function testFieldFormUnlimited() {
1413      $this->field = $this->field_unlimited;
1414      $this->field_name = $this->field['field_name'];
1415      $this->instance['field_name'] = $this->field_name;
1416      field_create_field($this->field);
1417      field_create_instance($this->instance);
1418      $langcode = LANGUAGE_NONE;
1419  
1420      // Display creation form -> 1 widget.
1421      $this->drupalGet('test-entity/add/test-bundle');
1422      $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
1423      $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
1424  
1425      // Press 'add more' button -> 2 widgets.
1426      $this->drupalPost(NULL, array(), t('Add another item'));
1427      $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
1428      $this->assertFieldByName("{$this->field_name}[$langcode][1][value]", '', 'New widget is displayed');
1429      $this->assertNoField("{$this->field_name}[$langcode][2][value]", 'No extraneous widget is displayed');
1430      // TODO : check that non-field inpurs are preserved ('title')...
1431  
1432      // Yet another time so that we can play with more values -> 3 widgets.
1433      $this->drupalPost(NULL, array(), t('Add another item'));
1434  
1435      // Prepare values and weights.
1436      $count = 3;
1437      $delta_range = $count - 1;
1438      $values = $weights = $pattern = $expected_values = $edit = array();
1439      for ($delta = 0; $delta <= $delta_range; $delta++) {
1440        // Assign unique random values and weights.
1441        do {
1442          $value = mt_rand(1, 127);
1443        } while (in_array($value, $values));
1444        do {
1445          $weight = mt_rand(-$delta_range, $delta_range);
1446        } while (in_array($weight, $weights));
1447        $edit["$this->field_name[$langcode][$delta][value]"] = $value;
1448        $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
1449        // We'll need three slightly different formats to check the values.
1450        $values[$delta] = $value;
1451        $weights[$delta] = $weight;
1452        $field_values[$weight]['value'] = (string) $value;
1453        $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
1454      }
1455  
1456      // Press 'add more' button -> 4 widgets
1457      $this->drupalPost(NULL, $edit, t('Add another item'));
1458      for ($delta = 0; $delta <= $delta_range; $delta++) {
1459        $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
1460        $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
1461      }
1462      ksort($pattern);
1463      $pattern = implode('.*', array_values($pattern));
1464      $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
1465      $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
1466      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
1467      $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
1468  
1469      // Submit the form and create the entity.
1470      $this->drupalPost(NULL, $edit, t('Save'));
1471      preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
1472      $id = $match[1];
1473      $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
1474      $entity = field_test_entity_test_load($id);
1475      ksort($field_values);
1476      $field_values = array_values($field_values);
1477      $this->assertIdentical($entity->{$this->field_name}[$langcode], $field_values, 'Field values were saved in the correct order');
1478  
1479      // Display edit form: check that the expected number of widgets is
1480      // displayed, with correct values change values, reorder, leave an empty
1481      // value in the middle.
1482      // Submit: check that the entity is updated with correct values
1483      // Re-submit: check that the field can be emptied.
1484  
1485      // Test with several multiple fields in a form
1486    }
1487  
1488    /**
1489     * Tests widget handling of multiple required radios.
1490     */
1491    function testFieldFormMultivalueWithRequiredRadio() {
1492      // Create a multivalue test field.
1493      $this->field = $this->field_unlimited;
1494      $this->field_name = $this->field['field_name'];
1495      $this->instance['field_name'] = $this->field_name;
1496      field_create_field($this->field);
1497      field_create_instance($this->instance);
1498      $langcode = LANGUAGE_NONE;
1499  
1500      // Add a required radio field.
1501      field_create_field(array(
1502        'field_name' => 'required_radio_test',
1503        'type' => 'list_text',
1504        'settings' => array(
1505          'allowed_values' => array('yes' => 'yes', 'no' => 'no'),
1506        ),
1507      ));
1508      field_create_instance(array(
1509        'field_name' => 'required_radio_test',
1510        'entity_type' => 'test_entity',
1511        'bundle' => 'test_bundle',
1512        'required' => TRUE,
1513        'widget' => array(
1514          'type' => 'options_buttons',
1515        ),
1516      ));
1517  
1518      // Display creation form.
1519      $this->drupalGet('test-entity/add/test-bundle');
1520  
1521      // Press the 'Add more' button.
1522      $this->drupalPost(NULL, array(), t('Add another item'));
1523  
1524      // Verify that no error is thrown by the radio element.
1525      $this->assertNoFieldByXpath('//div[contains(@class, "error")]', FALSE, 'No error message is displayed.');
1526  
1527      // Verify that the widget is added.
1528      $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
1529      $this->assertFieldByName("{$this->field_name}[$langcode][1][value]", '', 'New widget is displayed');
1530      $this->assertNoField("{$this->field_name}[$langcode][2][value]", 'No extraneous widget is displayed');
1531    }
1532  
1533    function testFieldFormJSAddMore() {
1534      $this->field = $this->field_unlimited;
1535      $this->field_name = $this->field['field_name'];
1536      $this->instance['field_name'] = $this->field_name;
1537      field_create_field($this->field);
1538      field_create_instance($this->instance);
1539      $langcode = LANGUAGE_NONE;
1540  
1541      // Display creation form -> 1 widget.
1542      $this->drupalGet('test-entity/add/test-bundle');
1543  
1544      // Press 'add more' button a couple times -> 3 widgets.
1545      // drupalPostAJAX() will not work iteratively, so we add those through
1546      // non-JS submission.
1547      $this->drupalPost(NULL, array(), t('Add another item'));
1548      $this->drupalPost(NULL, array(), t('Add another item'));
1549  
1550      // Prepare values and weights.
1551      $count = 3;
1552      $delta_range = $count - 1;
1553      $values = $weights = $pattern = $expected_values = $edit = array();
1554      for ($delta = 0; $delta <= $delta_range; $delta++) {
1555        // Assign unique random values and weights.
1556        do {
1557          $value = mt_rand(1, 127);
1558        } while (in_array($value, $values));
1559        do {
1560          $weight = mt_rand(-$delta_range, $delta_range);
1561        } while (in_array($weight, $weights));
1562        $edit["$this->field_name[$langcode][$delta][value]"] = $value;
1563        $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
1564        // We'll need three slightly different formats to check the values.
1565        $values[$delta] = $value;
1566        $weights[$delta] = $weight;
1567        $field_values[$weight]['value'] = (string) $value;
1568        $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
1569      }
1570      // Press 'add more' button through Ajax, and place the expected HTML result
1571      // as the tested content.
1572      $commands = $this->drupalPostAJAX(NULL, $edit, $this->field_name . '_add_more');
1573      $this->content = $commands[1]['data'];
1574  
1575      for ($delta = 0; $delta <= $delta_range; $delta++) {
1576        $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
1577        $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
1578      }
1579      ksort($pattern);
1580      $pattern = implode('.*', array_values($pattern));
1581      $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
1582      $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
1583      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
1584      $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
1585    }
1586  
1587    /**
1588     * Tests widgets handling multiple values.
1589     */
1590    function testFieldFormMultipleWidget() {
1591      // Create a field with fixed cardinality and an instance using a multiple
1592      // widget.
1593      $this->field = $this->field_multiple;
1594      $this->field_name = $this->field['field_name'];
1595      $this->instance['field_name'] = $this->field_name;
1596      $this->instance['widget']['type'] = 'test_field_widget_multiple';
1597      field_create_field($this->field);
1598      field_create_instance($this->instance);
1599      $langcode = LANGUAGE_NONE;
1600  
1601      // Display creation form.
1602      $this->drupalGet('test-entity/add/test-bundle');
1603      $this->assertFieldByName("{$this->field_name}[$langcode]", '', t('Widget is displayed.'));
1604  
1605      // Create entity with three values.
1606      $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3');
1607      $this->drupalPost(NULL, $edit, t('Save'));
1608      preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
1609      $id = $match[1];
1610  
1611      // Check that the values were saved.
1612      $entity_init = field_test_create_stub_entity($id);
1613      $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3));
1614  
1615      // Display the form, check that the values are correctly filled in.
1616      $this->drupalGet('test-entity/manage/' . $id . '/edit');
1617      $this->assertFieldByName("{$this->field_name}[$langcode]", '1, 2, 3', t('Widget is displayed.'));
1618  
1619      // Submit the form with more values than the field accepts.
1620      $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3, 4, 5');
1621      $this->drupalPost(NULL, $edit, t('Save'));
1622      $this->assertRaw('this field cannot hold more than 4 values', t('Form validation failed.'));
1623      // Check that the field values were not submitted.
1624      $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3));
1625    }
1626  
1627    /**
1628     * Tests fields with no 'edit' access.
1629     */
1630    function testFieldFormAccess() {
1631      // Create a "regular" field.
1632      $field = $this->field_single;
1633      $field_name = $field['field_name'];
1634      $instance = $this->instance;
1635      $instance['field_name'] = $field_name;
1636      field_create_field($field);
1637      field_create_instance($instance);
1638  
1639      // Create a field with no edit access - see field_test_field_access().
1640      $field_no_access = array(
1641        'field_name' => 'field_no_edit_access',
1642        'type' => 'test_field',
1643      );
1644      $field_name_no_access = $field_no_access['field_name'];
1645      $instance_no_access = array(
1646        'field_name' => $field_name_no_access,
1647        'entity_type' => 'test_entity',
1648        'bundle' => 'test_bundle',
1649        'default_value' => array(0 => array('value' => 99)),
1650      );
1651      field_create_field($field_no_access);
1652      field_create_instance($instance_no_access);
1653  
1654      $langcode = LANGUAGE_NONE;
1655  
1656      // Test that the form structure includes full information for each delta
1657      // apart from #access.
1658      $entity_type = 'test_entity';
1659      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
1660  
1661      $form = array();
1662      $form_state = form_state_defaults();
1663      field_attach_form($entity_type, $entity, $form, $form_state);
1664  
1665      $this->assertEqual($form[$field_name_no_access][$langcode][0]['value']['#entity_type'], $entity_type, 'The correct entity type is set in the field structure.');
1666      $this->assertFalse($form[$field_name_no_access]['#access'], 'Field #access is FALSE for the field without edit access.');
1667  
1668      // Display creation form.
1669      $this->drupalGet('test-entity/add/test-bundle');
1670      $this->assertNoFieldByName("{$field_name_no_access}[$langcode][0][value]", '', t('Widget is not displayed if field access is denied.'));
1671  
1672      // Create entity.
1673      $edit = array("{$field_name}[$langcode][0][value]" => 1);
1674      $this->drupalPost(NULL, $edit, t('Save'));
1675      preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
1676      $id = $match[1];
1677  
1678      // Check that the default value was saved.
1679      $entity = field_test_entity_test_load($id);
1680      $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('Default value was saved for the field with no edit access.'));
1681      $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 1, t('Entered value vas saved for the field with edit access.'));
1682  
1683      // Create a new revision.
1684      $edit = array("{$field_name}[$langcode][0][value]" => 2, 'revision' => TRUE);
1685      $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save'));
1686  
1687      // Check that the new revision has the expected values.
1688      $entity = field_test_entity_test_load($id);
1689      $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.'));
1690      $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
1691  
1692      // Check that the revision is also saved in the revisions table.
1693      $entity = field_test_entity_test_load($id, $entity->ftvid);
1694      $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.'));
1695      $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
1696    }
1697  
1698    /**
1699     * Tests Field API form integration within a subform.
1700     */
1701    function testNestedFieldForm() {
1702      // Add two instances on the 'test_bundle'
1703      field_create_field($this->field_single);
1704      field_create_field($this->field_unlimited);
1705      $this->instance['field_name'] = 'field_single';
1706      $this->instance['label'] = 'Single field';
1707      field_create_instance($this->instance);
1708      $this->instance['field_name'] = 'field_unlimited';
1709      $this->instance['label'] = 'Unlimited field';
1710      field_create_instance($this->instance);
1711  
1712      // Create two entities.
1713      $entity_1 = field_test_create_stub_entity(1, 1);
1714      $entity_1->is_new = TRUE;
1715      $entity_1->field_single[LANGUAGE_NONE][] = array('value' => 0);
1716      $entity_1->field_unlimited[LANGUAGE_NONE][] = array('value' => 1);
1717      field_test_entity_save($entity_1);
1718  
1719      $entity_2 = field_test_create_stub_entity(2, 2);
1720      $entity_2->is_new = TRUE;
1721      $entity_2->field_single[LANGUAGE_NONE][] = array('value' => 10);
1722      $entity_2->field_unlimited[LANGUAGE_NONE][] = array('value' => 11);
1723      field_test_entity_save($entity_2);
1724  
1725      // Display the 'combined form'.
1726      $this->drupalGet('test-entity/nested/1/2');
1727      $this->assertFieldByName('field_single[und][0][value]', 0, t('Entity 1: field_single value appears correctly is the form.'));
1728      $this->assertFieldByName('field_unlimited[und][0][value]', 1, t('Entity 1: field_unlimited value 0 appears correctly is the form.'));
1729      $this->assertFieldByName('entity_2[field_single][und][0][value]', 10, t('Entity 2: field_single value appears correctly is the form.'));
1730      $this->assertFieldByName('entity_2[field_unlimited][und][0][value]', 11, t('Entity 2: field_unlimited value 0 appears correctly is the form.'));
1731  
1732      // Submit the form and check that the entities are updated accordingly.
1733      $edit = array(
1734        'field_single[und][0][value]' => 1,
1735        'field_unlimited[und][0][value]' => 2,
1736        'field_unlimited[und][1][value]' => 3,
1737        'entity_2[field_single][und][0][value]' => 11,
1738        'entity_2[field_unlimited][und][0][value]' => 12,
1739        'entity_2[field_unlimited][und][1][value]' => 13,
1740      );
1741      $this->drupalPost(NULL, $edit, t('Save'));
1742      field_cache_clear();
1743      $entity_1 = field_test_create_stub_entity(1);
1744      $entity_2 = field_test_create_stub_entity(2);
1745      $this->assertFieldValues($entity_1, 'field_single', LANGUAGE_NONE, array(1));
1746      $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE, array(2, 3));
1747      $this->assertFieldValues($entity_2, 'field_single', LANGUAGE_NONE, array(11));
1748      $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE, array(12, 13));
1749  
1750      // Submit invalid values and check that errors are reported on the
1751      // correct widgets.
1752      $edit = array(
1753        'field_unlimited[und][1][value]' => -1,
1754      );
1755      $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
1756      $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), t('Entity 1: the field validation error was reported.'));
1757      $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-field-unlimited-und-1-value'));
1758      $this->assertTrue($error_field, t('Entity 1: the error was flagged on the correct element.'));
1759      $edit = array(
1760        'entity_2[field_unlimited][und][1][value]' => -1,
1761      );
1762      $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
1763      $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), t('Entity 2: the field validation error was reported.'));
1764      $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-entity-2-field-unlimited-und-1-value'));
1765      $this->assertTrue($error_field, t('Entity 2: the error was flagged on the correct element.'));
1766  
1767      // Test that reordering works on both entities.
1768      $edit = array(
1769        'field_unlimited[und][0][_weight]' => 0,
1770        'field_unlimited[und][1][_weight]' => -1,
1771        'entity_2[field_unlimited][und][0][_weight]' => 0,
1772        'entity_2[field_unlimited][und][1][_weight]' => -1,
1773      );
1774      $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
1775      field_cache_clear();
1776      $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE, array(3, 2));
1777      $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE, array(13, 12));
1778  
1779      // Test the 'add more' buttons. Only Ajax submission is tested, because
1780      // the two 'add more' buttons present in the form have the same #value,
1781      // which confuses drupalPost().
1782      // 'Add more' button in the first entity:
1783      $this->drupalGet('test-entity/nested/1/2');
1784      $this->drupalPostAJAX(NULL, array(), 'field_unlimited_add_more');
1785      $this->assertFieldByName('field_unlimited[und][0][value]', 3, t('Entity 1: field_unlimited value 0 appears correctly is the form.'));
1786      $this->assertFieldByName('field_unlimited[und][1][value]', 2, t('Entity 1: field_unlimited value 1 appears correctly is the form.'));
1787      $this->assertFieldByName('field_unlimited[und][2][value]', '', t('Entity 1: field_unlimited value 2 appears correctly is the form.'));
1788      $this->assertFieldByName('field_unlimited[und][3][value]', '', t('Entity 1: an empty widget was added for field_unlimited value 3.'));
1789      // 'Add more' button in the first entity (changing field values):
1790      $edit = array(
1791        'entity_2[field_unlimited][und][0][value]' => 13,
1792        'entity_2[field_unlimited][und][1][value]' => 14,
1793        'entity_2[field_unlimited][und][2][value]' => 15,
1794      );
1795      $this->drupalPostAJAX(NULL, $edit, 'entity_2_field_unlimited_add_more');
1796      $this->assertFieldByName('entity_2[field_unlimited][und][0][value]', 13, t('Entity 2: field_unlimited value 0 appears correctly is the form.'));
1797      $this->assertFieldByName('entity_2[field_unlimited][und][1][value]', 14, t('Entity 2: field_unlimited value 1 appears correctly is the form.'));
1798      $this->assertFieldByName('entity_2[field_unlimited][und][2][value]', 15, t('Entity 2: field_unlimited value 2 appears correctly is the form.'));
1799      $this->assertFieldByName('entity_2[field_unlimited][und][3][value]', '', t('Entity 2: an empty widget was added for field_unlimited value 3.'));
1800      // Save the form and check values are saved correclty.
1801      $this->drupalPost(NULL, array(), t('Save'));
1802      field_cache_clear();
1803      $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE, array(3, 2));
1804      $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE, array(13, 14, 15));
1805    }
1806  }
1807  
1808  class FieldDisplayAPITestCase extends FieldTestCase {
1809    public static function getInfo() {
1810      return array(
1811        'name' => 'Field Display API tests',
1812        'description' => 'Test the display API.',
1813        'group' => 'Field API',
1814      );
1815    }
1816  
1817    function setUp() {
1818      parent::setUp('field_test');
1819  
1820      // Create a field and instance.
1821      $this->field_name = 'test_field';
1822      $this->label = $this->randomName();
1823      $this->cardinality = 4;
1824  
1825      $this->field = array(
1826        'field_name' => $this->field_name,
1827        'type' => 'test_field',
1828        'cardinality' => $this->cardinality,
1829      );
1830      $this->instance = array(
1831        'field_name' => $this->field_name,
1832        'entity_type' => 'test_entity',
1833        'bundle' => 'test_bundle',
1834        'label' => $this->label,
1835        'display' => array(
1836          'default' => array(
1837            'type' => 'field_test_default',
1838            'settings' => array(
1839              'test_formatter_setting' => $this->randomName(),
1840            ),
1841          ),
1842          'teaser' => array(
1843            'type' => 'field_test_default',
1844            'settings' => array(
1845              'test_formatter_setting' => $this->randomName(),
1846            ),
1847          ),
1848        ),
1849      );
1850      field_create_field($this->field);
1851      field_create_instance($this->instance);
1852  
1853      // Create an entity with values.
1854      $this->values = $this->_generateTestFieldValues($this->cardinality);
1855      $this->entity = field_test_create_stub_entity();
1856      $this->is_new = TRUE;
1857      $this->entity->{$this->field_name}[LANGUAGE_NONE] = $this->values;
1858      field_test_entity_save($this->entity);
1859    }
1860  
1861    /**
1862     * Test the field_view_field() function.
1863     */
1864    function testFieldViewField() {
1865      // No display settings: check that default display settings are used.
1866      $output = field_view_field('test_entity', $this->entity, $this->field_name);
1867      $this->drupalSetContent(drupal_render($output));
1868      $settings = field_info_formatter_settings('field_test_default');
1869      $setting = $settings['test_formatter_setting'];
1870      $this->assertText($this->label, t('Label was displayed.'));
1871      foreach ($this->values as $delta => $value) {
1872        $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1873      }
1874  
1875      // Check that explicit display settings are used.
1876      $display = array(
1877        'label' => 'hidden',
1878        'type' => 'field_test_multiple',
1879        'settings' => array(
1880          'test_formatter_setting_multiple' => $this->randomName(),
1881          'alter' => TRUE,
1882        ),
1883      );
1884      $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
1885      $this->drupalSetContent(drupal_render($output));
1886      $setting = $display['settings']['test_formatter_setting_multiple'];
1887      $this->assertNoText($this->label, t('Label was not displayed.'));
1888      $this->assertText('field_test_field_attach_view_alter', t('Alter fired, display passed.'));
1889      $array = array();
1890      foreach ($this->values as $delta => $value) {
1891        $array[] = $delta . ':' . $value['value'];
1892      }
1893      $this->assertText($setting . '|' . implode('|', $array), t('Values were displayed with expected setting.'));
1894  
1895      // Check the prepare_view steps are invoked.
1896      $display = array(
1897        'label' => 'hidden',
1898        'type' => 'field_test_with_prepare_view',
1899        'settings' => array(
1900          'test_formatter_setting_additional' => $this->randomName(),
1901        ),
1902      );
1903      $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
1904      $view = drupal_render($output);
1905      $this->drupalSetContent($view);
1906      $setting = $display['settings']['test_formatter_setting_additional'];
1907      $this->assertNoText($this->label, t('Label was not displayed.'));
1908      $this->assertNoText('field_test_field_attach_view_alter', t('Alter not fired.'));
1909      foreach ($this->values as $delta => $value) {
1910        $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1911      }
1912  
1913      // View mode: check that display settings specified in the instance are
1914      // used.
1915      $output = field_view_field('test_entity', $this->entity, $this->field_name, 'teaser');
1916      $this->drupalSetContent(drupal_render($output));
1917      $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting'];
1918      $this->assertText($this->label, t('Label was displayed.'));
1919      foreach ($this->values as $delta => $value) {
1920        $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1921      }
1922  
1923      // Unknown view mode: check that display settings for 'default' view mode
1924      // are used.
1925      $output = field_view_field('test_entity', $this->entity, $this->field_name, 'unknown_view_mode');
1926      $this->drupalSetContent(drupal_render($output));
1927      $setting = $this->instance['display']['default']['settings']['test_formatter_setting'];
1928      $this->assertText($this->label, t('Label was displayed.'));
1929      foreach ($this->values as $delta => $value) {
1930        $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1931      }
1932    }
1933  
1934    /**
1935     * Test the field_view_value() function.
1936     */
1937    function testFieldViewValue() {
1938      // No display settings: check that default display settings are used.
1939      $settings = field_info_formatter_settings('field_test_default');
1940      $setting = $settings['test_formatter_setting'];
1941      foreach ($this->values as $delta => $value) {
1942        $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
1943        $output = field_view_value('test_entity', $this->entity, $this->field_name, $item);
1944        $this->drupalSetContent(drupal_render($output));
1945        $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1946      }
1947  
1948      // Check that explicit display settings are used.
1949      $display = array(
1950        'type' => 'field_test_multiple',
1951        'settings' => array(
1952          'test_formatter_setting_multiple' => $this->randomName(),
1953        ),
1954      );
1955      $setting = $display['settings']['test_formatter_setting_multiple'];
1956      $array = array();
1957      foreach ($this->values as $delta => $value) {
1958        $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
1959        $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
1960        $this->drupalSetContent(drupal_render($output));
1961        $this->assertText($setting . '|0:' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1962      }
1963  
1964      // Check that prepare_view steps are invoked.
1965      $display = array(
1966        'type' => 'field_test_with_prepare_view',
1967        'settings' => array(
1968          'test_formatter_setting_additional' => $this->randomName(),
1969        ),
1970      );
1971      $setting = $display['settings']['test_formatter_setting_additional'];
1972      $array = array();
1973      foreach ($this->values as $delta => $value) {
1974        $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
1975        $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
1976        $this->drupalSetContent(drupal_render($output));
1977        $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1978      }
1979  
1980      // View mode: check that display settings specified in the instance are
1981      // used.
1982      $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting'];
1983      foreach ($this->values as $delta => $value) {
1984        $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
1985        $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'teaser');
1986        $this->drupalSetContent(drupal_render($output));
1987        $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1988      }
1989  
1990      // Unknown view mode: check that display settings for 'default' view mode
1991      // are used.
1992      $setting = $this->instance['display']['default']['settings']['test_formatter_setting'];
1993      foreach ($this->values as $delta => $value) {
1994        $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
1995        $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'unknown_view_mode');
1996        $this->drupalSetContent(drupal_render($output));
1997        $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1998      }
1999    }
2000  }
2001  
2002  class FieldCrudTestCase extends FieldTestCase {
2003    public static function getInfo() {
2004      return array(
2005        'name' => 'Field CRUD tests',
2006        'description' => 'Test field create, read, update, and delete.',
2007        'group' => 'Field API',
2008      );
2009    }
2010  
2011    function setUp() {
2012      // field_update_field() tests use number.module
2013      parent::setUp('field_test', 'number');
2014    }
2015  
2016    // TODO : test creation with
2017    // - a full fledged $field structure, check that all the values are there
2018    // - a minimal $field structure, check all default values are set
2019    // defer actual $field comparison to a helper function, used for the two cases above
2020  
2021    /**
2022     * Test the creation of a field.
2023     */
2024    function testCreateField() {
2025      $field_definition = array(
2026        'field_name' => 'field_2',
2027        'type' => 'test_field',
2028      );
2029      field_test_memorize();
2030      $field_definition = field_create_field($field_definition);
2031      $mem = field_test_memorize();
2032      $this->assertIdentical($mem['field_test_field_create_field'][0][0], $field_definition, 'hook_field_create_field() called with correct arguments.');
2033  
2034      // Read the raw record from the {field_config_instance} table.
2035      $result = db_query('SELECT * FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']));
2036      $record = $result->fetchAssoc();
2037      $record['data'] = unserialize($record['data']);
2038  
2039      // Ensure that basic properties are preserved.
2040      $this->assertEqual($record['field_name'], $field_definition['field_name'], t('The field name is properly saved.'));
2041      $this->assertEqual($record['type'], $field_definition['type'], t('The field type is properly saved.'));
2042  
2043      // Ensure that cardinality defaults to 1.
2044      $this->assertEqual($record['cardinality'], 1, t('Cardinality defaults to 1.'));
2045  
2046      // Ensure that default settings are present.
2047      $field_type = field_info_field_types($field_definition['type']);
2048      $this->assertIdentical($record['data']['settings'], $field_type['settings'], t('Default field settings have been written.'));
2049  
2050      // Ensure that default storage was set.
2051      $this->assertEqual($record['storage_type'], variable_get('field_storage_default'), t('The field type is properly saved.'));
2052  
2053      // Guarantee that the name is unique.
2054      try {
2055        field_create_field($field_definition);
2056        $this->fail(t('Cannot create two fields with the same name.'));
2057      }
2058      catch (FieldException $e) {
2059        $this->pass(t('Cannot create two fields with the same name.'));
2060      }
2061  
2062      // Check that field type is required.
2063      try {
2064        $field_definition = array(
2065          'field_name' => 'field_1',
2066        );
2067        field_create_field($field_definition);
2068        $this->fail(t('Cannot create a field with no type.'));
2069      }
2070      catch (FieldException $e) {
2071        $this->pass(t('Cannot create a field with no type.'));
2072      }
2073  
2074      // Check that field name is required.
2075      try {
2076        $field_definition = array(
2077          'type' => 'test_field'
2078        );
2079        field_create_field($field_definition);
2080        $this->fail(t('Cannot create an unnamed field.'));
2081      }
2082      catch (FieldException $e) {
2083        $this->pass(t('Cannot create an unnamed field.'));
2084      }
2085  
2086      // Check that field name must start with a letter or _.
2087      try {
2088        $field_definition = array(
2089          'field_name' => '2field_2',
2090          'type' => 'test_field',
2091        );
2092        field_create_field($field_definition);
2093        $this->fail(t('Cannot create a field with a name starting with a digit.'));
2094      }
2095      catch (FieldException $e) {
2096        $this->pass(t('Cannot create a field with a name starting with a digit.'));
2097      }
2098  
2099      // Check that field name must only contain lowercase alphanumeric or _.
2100      try {
2101        $field_definition = array(
2102          'field_name' => 'field#_3',
2103          'type' => 'test_field',
2104        );
2105        field_create_field($field_definition);
2106        $this->fail(t('Cannot create a field with a name containing an illegal character.'));
2107      }
2108      catch (FieldException $e) {
2109        $this->pass(t('Cannot create a field with a name containing an illegal character.'));
2110      }
2111  
2112      // Check that field name cannot be longer than 32 characters long.
2113      try {
2114        $field_definition = array(
2115          'field_name' => '_12345678901234567890123456789012',
2116          'type' => 'test_field',
2117        );
2118        field_create_field($field_definition);
2119        $this->fail(t('Cannot create a field with a name longer than 32 characters.'));
2120      }
2121      catch (FieldException $e) {
2122        $this->pass(t('Cannot create a field with a name longer than 32 characters.'));
2123      }
2124  
2125      // Check that field name can not be an entity key.
2126      // "ftvid" is known as an entity key from the "test_entity" type.
2127      try {
2128        $field_definition = array(
2129          'type' => 'test_field',
2130          'field_name' => 'ftvid',
2131        );
2132        $field = field_create_field($field_definition);
2133        $this->fail(t('Cannot create a field bearing the name of an entity key.'));
2134      }
2135      catch (FieldException $e) {
2136        $this->pass(t('Cannot create a field bearing the name of an entity key.'));
2137      }
2138    }
2139  
2140    /**
2141     * Test failure to create a field.
2142     */
2143    function testCreateFieldFail() {
2144      $field_name = 'duplicate';
2145      $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
2146      $query = db_select('field_config')->condition('field_name', $field_name)->countQuery();
2147  
2148      // The field does not appear in field_config.
2149      $count = $query->execute()->fetchField();
2150      $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
2151  
2152      // Try to create the field.
2153      try {
2154        $field = field_create_field($field_definition);
2155        $this->assertTrue(FALSE, 'Field creation (correctly) fails.');
2156      }
2157      catch (Exception $e) {
2158        $this->assertTrue(TRUE, 'Field creation (correctly) fails.');
2159      }
2160  
2161      // The field does not appear in field_config.
2162      $count = $query->execute()->fetchField();
2163      $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
2164    }
2165  
2166    /**
2167     * Test reading back a field definition.
2168     */
2169    function testReadField() {
2170      $field_definition = array(
2171        'field_name' => 'field_1',
2172        'type' => 'test_field',
2173      );
2174      field_create_field($field_definition);
2175  
2176      // Read the field back.
2177      $field = field_read_field($field_definition['field_name']);
2178      $this->assertTrue($field_definition < $field, t('The field was properly read.'));
2179    }
2180  
2181    /**
2182     * Test creation of indexes on data column.
2183     */
2184    function testFieldIndexes() {
2185      // Check that indexes specified by the field type are used by default.
2186      $field_definition = array(
2187        'field_name' => 'field_1',
2188        'type' => 'test_field',
2189      );
2190      field_create_field($field_definition);
2191      $field = field_read_field($field_definition['field_name']);
2192      $expected_indexes = array('value' => array('value'));
2193      $this->assertEqual($field['indexes'], $expected_indexes, t('Field type indexes saved by default'));
2194  
2195      // Check that indexes specified by the field definition override the field
2196      // type indexes.
2197      $field_definition = array(
2198        'field_name' => 'field_2',
2199        'type' => 'test_field',
2200        'indexes' => array(
2201          'value' => array(),
2202        ),
2203      );
2204      field_create_field($field_definition);
2205      $field = field_read_field($field_definition['field_name']);
2206      $expected_indexes = array('value' => array());
2207      $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes override field type indexes'));
2208  
2209      // Check that indexes specified by the field definition add to the field
2210      // type indexes.
2211      $field_definition = array(
2212        'field_name' => 'field_3',
2213        'type' => 'test_field',
2214        'indexes' => array(
2215          'value_2' => array('value'),
2216        ),
2217      );
2218      field_create_field($field_definition);
2219      $field = field_read_field($field_definition['field_name']);
2220      $expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
2221      $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes are merged with field type indexes'));
2222    }
2223  
2224    /**
2225     * Test the deletion of a field.
2226     */
2227    function testDeleteField() {
2228      // TODO: Also test deletion of the data stored in the field ?
2229  
2230      // Create two fields (so we can test that only one is deleted).
2231      $this->field = array('field_name' => 'field_1', 'type' => 'test_field');
2232      field_create_field($this->field);
2233      $this->another_field = array('field_name' => 'field_2', 'type' => 'test_field');
2234      field_create_field($this->another_field);
2235  
2236      // Create instances for each.
2237      $this->instance_definition = array(
2238        'field_name' => $this->field['field_name'],
2239        'entity_type' => 'test_entity',
2240        'bundle' => 'test_bundle',
2241        'widget' => array(
2242          'type' => 'test_field_widget',
2243        ),
2244      );
2245      field_create_instance($this->instance_definition);
2246      $this->another_instance_definition = $this->instance_definition;
2247      $this->another_instance_definition['field_name'] = $this->another_field['field_name'];
2248      field_create_instance($this->another_instance_definition);
2249  
2250      // Test that the first field is not deleted, and then delete it.
2251      $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
2252      $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field is not marked for deletion.'));
2253      field_delete_field($this->field['field_name']);
2254  
2255      // Make sure that the field is marked as deleted when it is specifically
2256      // loaded.
2257      $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
2258      $this->assertTrue(!empty($field['deleted']), t('A deleted field is marked for deletion.'));
2259  
2260      // Make sure that this field's instance is marked as deleted when it is
2261      // specifically loaded.
2262      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
2263      $this->assertTrue(!empty($instance['deleted']), t('An instance for a deleted field is marked for deletion.'));
2264  
2265      // Try to load the field normally and make sure it does not show up.
2266      $field = field_read_field($this->field['field_name']);
2267      $this->assertTrue(empty($field), t('A deleted field is not loaded by default.'));
2268  
2269      // Try to load the instance normally and make sure it does not show up.
2270      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2271      $this->assertTrue(empty($instance), t('An instance for a deleted field is not loaded by default.'));
2272  
2273      // Make sure the other field (and its field instance) are not deleted.
2274      $another_field = field_read_field($this->another_field['field_name']);
2275      $this->assertTrue(!empty($another_field) && empty($another_field['deleted']), t('A non-deleted field is not marked for deletion.'));
2276      $another_instance = field_read_instance('test_entity', $this->another_instance_definition['field_name'], $this->another_instance_definition['bundle']);
2277      $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('An instance of a non-deleted field is not marked for deletion.'));
2278  
2279      // Try to create a new field the same name as a deleted field and
2280      // write data into it.
2281      field_create_field($this->field);
2282      field_create_instance($this->instance_definition);
2283      $field = field_read_field($this->field['field_name']);
2284      $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field with a previously used name is created.'));
2285      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2286      $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new instance for a previously used field name is created.'));
2287  
2288      // Save an entity with data for the field
2289      $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
2290      $langcode = LANGUAGE_NONE;
2291      $values[0]['value'] = mt_rand(1, 127);
2292      $entity->{$field['field_name']}[$langcode] = $values;
2293      $entity_type = 'test_entity';
2294      field_attach_insert('test_entity', $entity);
2295  
2296      // Verify the field is present on load
2297      $entity = field_test_create_stub_entity(0, 0, $this->instance_definition['bundle']);
2298      field_attach_load($entity_type, array(0 => $entity));
2299      $this->assertIdentical(count($entity->{$field['field_name']}[$langcode]), count($values), "Data in previously deleted field saves and loads correctly");
2300      foreach ($values as $delta => $value) {
2301        $this->assertEqual($entity->{$field['field_name']}[$langcode][$delta]['value'], $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
2302      }
2303    }
2304  
2305    function testUpdateNonExistentField() {
2306      $test_field = array('field_name' => 'does_not_exist', 'type' => 'number_decimal');
2307      try {
2308        field_update_field($test_field);
2309        $this->fail(t('Cannot update a field that does not exist.'));
2310      }
2311      catch (FieldException $e) {
2312        $this->pass(t('Cannot update a field that does not exist.'));
2313      }
2314    }
2315  
2316    function testUpdateFieldType() {
2317      $field = array('field_name' => 'field_type', 'type' => 'number_decimal');
2318      $field = field_create_field($field);
2319  
2320      $test_field = array('field_name' => 'field_type', 'type' => 'number_integer');
2321      try {
2322        field_update_field($test_field);
2323        $this->fail(t('Cannot update a field to a different type.'));
2324      }
2325      catch (FieldException $e) {
2326        $this->pass(t('Cannot update a field to a different type.'));
2327      }
2328    }
2329  
2330    /**
2331     * Test updating a field.
2332     */
2333    function testUpdateField() {
2334      // Create a field with a defined cardinality, so that we can ensure it's
2335      // respected. Since cardinality enforcement is consistent across database
2336      // systems, it makes a good test case.
2337      $cardinality = 4;
2338      $field_definition = array(
2339        'field_name' => 'field_update',
2340        'type' => 'test_field',
2341        'cardinality' => $cardinality,
2342      );
2343      $field_definition = field_create_field($field_definition);
2344      $instance = array(
2345        'field_name' => 'field_update',
2346        'entity_type' => 'test_entity',
2347        'bundle' => 'test_bundle',
2348      );
2349      $instance = field_create_instance($instance);
2350  
2351      do {
2352        // We need a unique ID for our entity. $cardinality will do.
2353        $id = $cardinality;
2354        $entity = field_test_create_stub_entity($id, $id, $instance['bundle']);
2355        // Fill in the entity with more values than $cardinality.
2356        for ($i = 0; $i < 20; $i++) {
2357          $entity->field_update[LANGUAGE_NONE][$i]['value'] = $i;
2358        }
2359        // Save the entity.
2360        field_attach_insert('test_entity', $entity);
2361        // Load back and assert there are $cardinality number of values.
2362        $entity = field_test_create_stub_entity($id, $id, $instance['bundle']);
2363        field_attach_load('test_entity', array($id => $entity));
2364        $this->assertEqual(count($entity->field_update[LANGUAGE_NONE]), $field_definition['cardinality'], 'Cardinality is kept');
2365        // Now check the values themselves.
2366        for ($delta = 0; $delta < $cardinality; $delta++) {
2367          $this->assertEqual($entity->field_update[LANGUAGE_NONE][$delta]['value'], $delta, 'Value is kept');
2368        }
2369        // Increase $cardinality and set the field cardinality to the new value.
2370        $field_definition['cardinality'] = ++$cardinality;
2371        field_update_field($field_definition);
2372      } while ($cardinality < 6);
2373    }
2374  
2375    /**
2376     * Test field type modules forbidding an update.
2377     */
2378    function testUpdateFieldForbid() {
2379      $field = array('field_name' => 'forbidden', 'type' => 'test_field', 'settings' => array('changeable' => 0, 'unchangeable' => 0));
2380      $field = field_create_field($field);
2381      $field['settings']['changeable']++;
2382      try {
2383        field_update_field($field);
2384        $this->pass(t("A changeable setting can be updated."));
2385      }
2386      catch (FieldException $e) {
2387        $this->fail(t("An unchangeable setting cannot be updated."));
2388      }
2389      $field['settings']['unchangeable']++;
2390      try {
2391        field_update_field($field);
2392        $this->fail(t("An unchangeable setting can be updated."));
2393      }
2394      catch (FieldException $e) {
2395        $this->pass(t("An unchangeable setting cannot be updated."));
2396      }
2397    }
2398  
2399    /**
2400     * Test that fields are properly marked active or inactive.
2401     */
2402    function testActive() {
2403      $field_definition = array(
2404        'field_name' => 'field_1',
2405        'type' => 'test_field',
2406        // For this test, we need a storage backend provided by a different
2407        // module than field_test.module.
2408        'storage' => array(
2409          'type' => 'field_sql_storage',
2410        ),
2411      );
2412      field_create_field($field_definition);
2413  
2414      // Test disabling and enabling:
2415      // - the field type module,
2416      // - the storage module,
2417      // - both.
2418      $this->_testActiveHelper($field_definition, array('field_test'));
2419      $this->_testActiveHelper($field_definition, array('field_sql_storage'));
2420      $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
2421    }
2422  
2423    /**
2424     * Helper function for testActive().
2425     *
2426     * Test dependency between a field and a set of modules.
2427     *
2428     * @param $field_definition
2429     *   A field definition.
2430     * @param $modules
2431     *   An aray of module names. The field will be tested to be inactive as long
2432     *   as any of those modules is disabled.
2433     */
2434    function _testActiveHelper($field_definition, $modules) {
2435      $field_name = $field_definition['field_name'];
2436  
2437      // Read the field.
2438      $field = field_read_field($field_name);
2439      $this->assertTrue($field_definition <= $field, t('The field was properly read.'));
2440  
2441      module_disable($modules, FALSE);
2442  
2443      $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
2444      $this->assertTrue(isset($fields[$field_name]) && $field_definition < $field, t('The field is properly read when explicitly fetching inactive fields.'));
2445  
2446      // Re-enable modules one by one, and check that the field is still inactive
2447      // while some modules remain disabled.
2448      while ($modules) {
2449        $field = field_read_field($field_name);
2450        $this->assertTrue(empty($field), t('%modules disabled. The field is marked inactive.', array('%modules' => implode(', ', $modules))));
2451  
2452        $module = array_shift($modules);
2453        module_enable(array($module), FALSE);
2454      }
2455  
2456      // Check that the field is active again after all modules have been
2457      // enabled.
2458      $field = field_read_field($field_name);
2459      $this->assertTrue($field_definition <= $field, t('The field was was marked active.'));
2460    }
2461  }
2462  
2463  class FieldInstanceCrudTestCase extends FieldTestCase {
2464    protected $field;
2465  
2466    public static function getInfo() {
2467      return array(
2468        'name' => 'Field instance CRUD tests',
2469        'description' => 'Create field entities by attaching fields to entities.',
2470        'group' => 'Field API',
2471      );
2472    }
2473  
2474    function setUp() {
2475      parent::setUp('field_test');
2476  
2477      $this->field = array(
2478        'field_name' => drupal_strtolower($this->randomName()),
2479        'type' => 'test_field',
2480      );
2481      field_create_field($this->field);
2482      $this->instance_definition = array(
2483        'field_name' => $this->field['field_name'],
2484        'entity_type' => 'test_entity',
2485        'bundle' => 'test_bundle',
2486      );
2487    }
2488  
2489    // TODO : test creation with
2490    // - a full fledged $instance structure, check that all the values are there
2491    // - a minimal $instance structure, check all default values are set
2492    // defer actual $instance comparison to a helper function, used for the two cases above,
2493    // and for testUpdateFieldInstance
2494  
2495    /**
2496     * Test the creation of a field instance.
2497     */
2498    function testCreateFieldInstance() {
2499      field_create_instance($this->instance_definition);
2500  
2501      // Read the raw record from the {field_config_instance} table.
2502      $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']));
2503      $record = $result->fetchAssoc();
2504      $record['data'] = unserialize($record['data']);
2505  
2506      $field_type = field_info_field_types($this->field['type']);
2507      $widget_type = field_info_widget_types($field_type['default_widget']);
2508      $formatter_type = field_info_formatter_types($field_type['default_formatter']);
2509  
2510      // Check that default values are set.
2511      $this->assertIdentical($record['data']['required'], FALSE, t('Required defaults to false.'));
2512      $this->assertIdentical($record['data']['label'], $this->instance_definition['field_name'], t('Label defaults to field name.'));
2513      $this->assertIdentical($record['data']['description'], '', t('Description defaults to empty string.'));
2514      $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], t('Default widget has been written.'));
2515      $this->assertTrue(isset($record['data']['display']['default']), t('Display for "full" view_mode has been written.'));
2516      $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], t('Default formatter for "full" view_mode has been written.'));
2517  
2518      // Check that default settings are set.
2519      $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , t('Default instance settings have been written.'));
2520      $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , t('Default widget settings have been written.'));
2521      $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" view_mode have been written.'));
2522  
2523      // Guarantee that the field/bundle combination is unique.
2524      try {
2525        field_create_instance($this->instance_definition);
2526        $this->fail(t('Cannot create two instances with the same field / bundle combination.'));
2527      }
2528      catch (FieldException $e) {
2529        $this->pass(t('Cannot create two instances with the same field / bundle combination.'));
2530      }
2531  
2532      // Check that the specified field exists.
2533      try {
2534        $this->instance_definition['field_name'] = $this->randomName();
2535        field_create_instance($this->instance_definition);
2536        $this->fail(t('Cannot create an instance of a non-existing field.'));
2537      }
2538      catch (FieldException $e) {
2539        $this->pass(t('Cannot create an instance of a non-existing field.'));
2540      }
2541  
2542      // Create a field restricted to a specific entity type.
2543      $field_restricted = array(
2544        'field_name' => drupal_strtolower($this->randomName()),
2545        'type' => 'test_field',
2546        'entity_types' => array('test_cacheable_entity'),
2547      );
2548      field_create_field($field_restricted);
2549  
2550      // Check that an instance can be added to an entity type allowed
2551      // by the field.
2552      try {
2553        $instance = $this->instance_definition;
2554        $instance['field_name'] = $field_restricted['field_name'];
2555        $instance['entity_type'] = 'test_cacheable_entity';
2556        field_create_instance($instance);
2557        $this->pass(t('Can create an instance on an entity type allowed by the field.'));
2558      }
2559      catch (FieldException $e) {
2560        $this->fail(t('Can create an instance on an entity type allowed by the field.'));
2561      }
2562  
2563      // Check that an instance cannot be added to an entity type
2564      // forbidden by the field.
2565      try {
2566        $instance = $this->instance_definition;
2567        $instance['field_name'] = $field_restricted['field_name'];
2568        field_create_instance($instance);
2569        $this->fail(t('Cannot create an instance on an entity type forbidden by the field.'));
2570      }
2571      catch (FieldException $e) {
2572        $this->pass(t('Cannot create an instance on an entity type forbidden by the field.'));
2573      }
2574  
2575      // TODO: test other failures.
2576    }
2577  
2578    /**
2579     * Test reading back an instance definition.
2580     */
2581    function testReadFieldInstance() {
2582      field_create_instance($this->instance_definition);
2583  
2584      // Read the instance back.
2585      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2586      $this->assertTrue($this->instance_definition < $instance, t('The field was properly read.'));
2587    }
2588  
2589    /**
2590     * Test the update of a field instance.
2591     */
2592    function testUpdateFieldInstance() {
2593      field_create_instance($this->instance_definition);
2594      $field_type = field_info_field_types($this->field['type']);
2595  
2596      // Check that basic changes are saved.
2597      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2598      $instance['required'] = !$instance['required'];
2599      $instance['label'] = $this->randomName();
2600      $instance['description'] = $this->randomName();
2601      $instance['settings']['test_instance_setting'] = $this->randomName();
2602      $instance['widget']['settings']['test_widget_setting'] =$this->randomName();
2603      $instance['widget']['weight']++;
2604      $instance['display']['default']['settings']['test_formatter_setting'] = $this->randomName();
2605      $instance['display']['default']['weight']++;
2606      field_update_instance($instance);
2607  
2608      $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2609      $this->assertEqual($instance['required'], $instance_new['required'], t('"required" change is saved'));
2610      $this->assertEqual($instance['label'], $instance_new['label'], t('"label" change is saved'));
2611      $this->assertEqual($instance['description'], $instance_new['description'], t('"description" change is saved'));
2612      $this->assertEqual($instance['widget']['settings']['test_widget_setting'], $instance_new['widget']['settings']['test_widget_setting'], t('Widget setting change is saved'));
2613      $this->assertEqual($instance['widget']['weight'], $instance_new['widget']['weight'], t('Widget weight change is saved'));
2614      $this->assertEqual($instance['display']['default']['settings']['test_formatter_setting'], $instance_new['display']['default']['settings']['test_formatter_setting'], t('Formatter setting change is saved'));
2615      $this->assertEqual($instance['display']['default']['weight'], $instance_new['display']['default']['weight'], t('Widget weight change is saved'));
2616  
2617      // Check that changing widget and formatter types updates the default settings.
2618      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2619      $instance['widget']['type'] = 'test_field_widget_multiple';
2620      $instance['display']['default']['type'] = 'field_test_multiple';
2621      field_update_instance($instance);
2622  
2623      $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2624      $this->assertEqual($instance['widget']['type'], $instance_new['widget']['type'] , t('Widget type change is saved.'));
2625      $settings = field_info_widget_settings($instance_new['widget']['type']);
2626      $this->assertIdentical($settings, array_intersect_key($instance_new['widget']['settings'], $settings) , t('Widget type change updates default settings.'));
2627      $this->assertEqual($instance['display']['default']['type'], $instance_new['display']['default']['type'] , t('Formatter type change is saved.'));
2628      $info = field_info_formatter_types($instance_new['display']['default']['type']);
2629      $settings = $info['settings'];
2630      $this->assertIdentical($settings, array_intersect_key($instance_new['display']['default']['settings'], $settings) , t('Changing formatter type updates default settings.'));
2631  
2632      // Check that adding a new view mode is saved and gets default settings.
2633      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2634      $instance['display']['teaser'] = array();
2635      field_update_instance($instance);
2636  
2637      $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2638      $this->assertTrue(isset($instance_new['display']['teaser']), t('Display for the new view_mode has been written.'));
2639      $this->assertIdentical($instance_new['display']['teaser']['type'], $field_type['default_formatter'], t('Default formatter for the new view_mode has been written.'));
2640      $info = field_info_formatter_types($instance_new['display']['teaser']['type']);
2641      $settings = $info['settings'];
2642      $this->assertIdentical($settings, $instance_new['display']['teaser']['settings'] , t('Default formatter settings for the new view_mode have been written.'));
2643  
2644      // TODO: test failures.
2645    }
2646  
2647    /**
2648     * Test the deletion of a field instance.
2649     */
2650    function testDeleteFieldInstance() {
2651      // TODO: Test deletion of the data stored in the field also.
2652      // Need to check that data for a 'deleted' field / instance doesn't get loaded
2653      // Need to check data marked deleted is cleaned on cron (not implemented yet...)
2654  
2655      // Create two instances for the same field so we can test that only one
2656      // is deleted.
2657      field_create_instance($this->instance_definition);
2658      $this->another_instance_definition = $this->instance_definition;
2659      $this->another_instance_definition['bundle'] .= '_another_bundle';
2660      $instance = field_create_instance($this->another_instance_definition);
2661  
2662      // Test that the first instance is not deleted, and then delete it.
2663      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
2664      $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new field instance is not marked for deletion.'));
2665      field_delete_instance($instance);
2666  
2667      // Make sure the instance is marked as deleted when the instance is
2668      // specifically loaded.
2669      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle'], array('include_deleted' => TRUE));
2670      $this->assertTrue(!empty($instance['deleted']), t('A deleted field instance is marked for deletion.'));
2671  
2672      // Try to load the instance normally and make sure it does not show up.
2673      $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
2674      $this->assertTrue(empty($instance), t('A deleted field instance is not loaded by default.'));
2675  
2676      // Make sure the other field instance is not deleted.
2677      $another_instance = field_read_instance('test_entity', $this->another_instance_definition['field_name'], $this->another_instance_definition['bundle']);
2678      $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('A non-deleted field instance is not marked for deletion.'));
2679  
2680      // Make sure the field is deleted when its last instance is deleted.
2681      field_delete_instance($another_instance);
2682      $field = field_read_field($another_instance['field_name'], array('include_deleted' => TRUE));
2683      $this->assertTrue(!empty($field['deleted']), t('A deleted field is marked for deletion after all its instances have been marked for deletion.'));
2684    }
2685  }
2686  
2687  /**
2688   * Unit test class for the multilanguage fields logic.
2689   *
2690   * The following tests will check the multilanguage logic of _field_invoke() and
2691   * that only the correct values are returned by field_available_languages().
2692   */
2693  class FieldTranslationsTestCase extends FieldTestCase {
2694    public static function getInfo() {
2695      return array(
2696        'name' => 'Field translations tests',
2697        'description' => 'Test multilanguage fields logic.',
2698        'group' => 'Field API',
2699      );
2700    }
2701  
2702    function setUp() {
2703      parent::setUp('locale', 'field_test');
2704  
2705      $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
2706  
2707      $this->entity_type = 'test_entity';
2708  
2709      $field = array(
2710        'field_name' => $this->field_name,
2711        'type' => 'test_field',
2712        'cardinality' => 4,
2713        'translatable' => TRUE,
2714      );
2715      field_create_field($field);
2716      $this->field = field_read_field($this->field_name);
2717  
2718      $instance = array(
2719        'field_name' => $this->field_name,
2720        'entity_type' => $this->entity_type,
2721        'bundle' => 'test_bundle',
2722      );
2723      field_create_instance($instance);
2724      $this->instance = field_read_instance('test_entity', $this->field_name, 'test_bundle');
2725  
2726      require_once  DRUPAL_ROOT . '/includes/locale.inc';
2727      for ($i = 0; $i < 3; ++$i) {
2728        locale_add_language('l' . $i, $this->randomString(), $this->randomString());
2729      }
2730    }
2731  
2732    /**
2733     * Ensures that only valid values are returned by field_available_languages().
2734     */
2735    function testFieldAvailableLanguages() {
2736      // Test 'translatable' fieldable info.
2737      field_test_entity_info_translatable('test_entity', FALSE);
2738      $field = $this->field;
2739      $field['field_name'] .= '_untranslatable';
2740  
2741      // Enable field translations for the entity.
2742      field_test_entity_info_translatable('test_entity', TRUE);
2743  
2744      // Test hook_field_languages() invocation on a translatable field.
2745      variable_set('field_test_field_available_languages_alter', TRUE);
2746      $enabled_languages = field_content_languages();
2747      $available_languages = field_available_languages($this->entity_type, $this->field);
2748      foreach ($available_languages as $delta => $langcode) {
2749        if ($langcode != 'xx' && $langcode != 'en') {
2750          $this->assertTrue(in_array($langcode, $enabled_languages), t('%language is an enabled language.', array('%language' => $langcode)));
2751        }
2752      }
2753      $this->assertTrue(in_array('xx', $available_languages), t('%language was made available.', array('%language' => 'xx')));
2754      $this->assertFalse(in_array('en', $available_languages), t('%language was made unavailable.', array('%language' => 'en')));
2755  
2756      // Test field_available_languages() behavior for untranslatable fields.
2757      $this->field['translatable'] = FALSE;
2758      field_update_field($this->field);
2759      $available_languages = field_available_languages($this->entity_type, $this->field);
2760      $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === LANGUAGE_NONE, t('For untranslatable fields only LANGUAGE_NONE is available.'));
2761    }
2762  
2763    /**
2764     * Test the multilanguage logic of _field_invoke().
2765     */
2766    function testFieldInvoke() {
2767      // Enable field translations for the entity.
2768      field_test_entity_info_translatable('test_entity', TRUE);
2769  
2770      $entity_type = 'test_entity';
2771      $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
2772  
2773      // Populate some extra languages to check if _field_invoke() correctly uses
2774      // the result of field_available_languages().
2775      $values = array();
2776      $extra_languages = mt_rand(1, 4);
2777      $languages = $available_languages = field_available_languages($this->entity_type, $this->field);
2778      for ($i = 0; $i < $extra_languages; ++$i) {
2779        $languages[] = $this->randomName(2);
2780      }
2781  
2782      // For each given language provide some random values.
2783      foreach ($languages as $langcode) {
2784        for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
2785          $values[$langcode][$delta]['value'] = mt_rand(1, 127);
2786        }
2787      }
2788      $entity->{$this->field_name} = $values;
2789  
2790      $results = _field_invoke('test_op', $entity_type, $entity);
2791      foreach ($results as $langcode => $result) {
2792        $hash = hash('sha256', serialize(array($entity_type, $entity, $this->field_name, $langcode, $values[$langcode])));
2793        // Check whether the parameters passed to _field_invoke() were correctly
2794        // forwarded to the callback function.
2795        $this->assertEqual($hash, $result, t('The result for %language is correctly stored.', array('%language' => $langcode)));
2796      }
2797  
2798      $this->assertEqual(count($results), count($available_languages), t('No unavailable language has been processed.'));
2799    }
2800  
2801    /**
2802     * Test the multilanguage logic of _field_invoke_multiple().
2803     */
2804    function testFieldInvokeMultiple() {
2805      // Enable field translations for the entity.
2806      field_test_entity_info_translatable('test_entity', TRUE);
2807  
2808      $values = array();
2809      $options = array();
2810      $entities = array();
2811      $entity_type = 'test_entity';
2812      $entity_count = 5;
2813      $available_languages = field_available_languages($this->entity_type, $this->field);
2814  
2815      for ($id = 1; $id <= $entity_count; ++$id) {
2816        $entity = field_test_create_stub_entity($id, $id, $this->instance['bundle']);
2817        $languages = $available_languages;
2818  
2819        // Populate some extra languages to check whether _field_invoke()
2820        // correctly uses the result of field_available_languages().
2821        $extra_languages = mt_rand(1, 4);
2822        for ($i = 0; $i < $extra_languages; ++$i) {
2823          $languages[] = $this->randomName(2);
2824        }
2825  
2826        // For each given language provide some random values.
2827        $language_count = count($languages);
2828        for ($i = 0; $i < $language_count; ++$i) {
2829          $langcode = $languages[$i];
2830          // Avoid to populate at least one field translation to check that
2831          // per-entity language suggestions work even when available field values
2832          // are different for each language.
2833          if ($i !== $id) {
2834            for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
2835              $values[$id][$langcode][$delta]['value'] = mt_rand(1, 127);
2836            }
2837          }
2838          // Ensure that a language for which there is no field translation is
2839          // used as display language to prepare per-entity language suggestions.
2840          elseif (!isset($display_language)) {
2841            $display_language = $langcode;
2842          }
2843        }
2844  
2845        $entity->{$this->field_name} = $values[$id];
2846        $entities[$id] = $entity;
2847  
2848        // Store per-entity language suggestions.
2849        $options['language'][$id] = field_language($entity_type, $entity, NULL, $display_language);
2850      }
2851  
2852      $grouped_results = _field_invoke_multiple('test_op_multiple', $entity_type, $entities);
2853      foreach ($grouped_results as $id => $results) {
2854        foreach ($results as $langcode => $result) {
2855          if (isset($values[$id][$langcode])) {
2856            $hash = hash('sha256', serialize(array($entity_type, $entities[$id], $this->field_name, $langcode, $values[$id][$langcode])));
2857            // Check whether the parameters passed to _field_invoke_multiple()
2858            // were correctly forwarded to the callback function.
2859            $this->assertEqual($hash, $result, t('The result for entity %id/%language is correctly stored.', array('%id' => $id, '%language' => $langcode)));
2860          }
2861        }
2862        $this->assertEqual(count($results), count($available_languages), t('No unavailable language has been processed for entity %id.', array('%id' => $id)));
2863      }
2864  
2865      $null = NULL;
2866      $grouped_results = _field_invoke_multiple('test_op_multiple', $entity_type, $entities, $null, $null, $options);
2867      foreach ($grouped_results as $id => $results) {
2868        foreach ($results as $langcode => $result) {
2869          $this->assertTrue(isset($options['language'][$id]), t('The result language %language for entity %id was correctly suggested (display language: %display_language).', array('%id' => $id, '%language' => $langcode, '%display_language' => $display_language)));
2870        }
2871      }
2872    }
2873  
2874    /**
2875     * Test translatable fields storage/retrieval.
2876     */
2877    function testTranslatableFieldSaveLoad() {
2878      // Enable field translations for nodes.
2879      field_test_entity_info_translatable('node', TRUE);
2880      $entity_info = entity_get_info('node');
2881      $this->assertTrue(count($entity_info['translation']), t('Nodes are translatable.'));
2882  
2883      // Prepare the field translations.
2884      field_test_entity_info_translatable('test_entity', TRUE);
2885      $eid = $evid = 1;
2886      $entity_type = 'test_entity';
2887      $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
2888      $field_translations = array();
2889      $available_languages = field_available_languages($entity_type, $this->field);
2890      $this->assertTrue(count($available_languages) > 1, t('Field is translatable.'));
2891      foreach ($available_languages as $langcode) {
2892        $field_translations[$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
2893      }
2894  
2895      // Save and reload the field translations.
2896      $entity->{$this->field_name} = $field_translations;
2897      field_attach_insert($entity_type, $entity);
2898      unset($entity->{$this->field_name});
2899      field_attach_load($entity_type, array($eid => $entity));
2900  
2901      // Check if the correct values were saved/loaded.
2902      foreach ($field_translations as $langcode => $items) {
2903        $result = TRUE;
2904        foreach ($items as $delta => $item) {
2905          $result = $result && $item['value'] == $entity->{$this->field_name}[$langcode][$delta]['value'];
2906        }
2907        $this->assertTrue($result, t('%language translation correctly handled.', array('%language' => $langcode)));
2908      }
2909    }
2910  
2911    /**
2912     * Tests display language logic for translatable fields.
2913     */
2914    function testFieldDisplayLanguage() {
2915      $field_name = drupal_strtolower($this->randomName() . '_field_name');
2916      $entity_type = 'test_entity';
2917  
2918      // We need an additional field here to properly test display language
2919      // suggestions.
2920      $field = array(
2921        'field_name' => $field_name,
2922        'type' => 'test_field',
2923        'cardinality' => 2,
2924        'translatable' => TRUE,
2925      );
2926      field_create_field($field);
2927  
2928      $instance = array(
2929        'field_name' => $field['field_name'],
2930        'entity_type' => $entity_type,
2931        'bundle' => 'test_bundle',
2932      );
2933      field_create_instance($instance);
2934  
2935      $entity = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
2936      $instances = field_info_instances($entity_type, $this->instance['bundle']);
2937  
2938      $enabled_languages = field_content_languages();
2939      $languages = array();
2940  
2941      // Generate field translations for languages different from the first
2942      // enabled.
2943      foreach ($instances as $instance) {
2944        $field_name = $instance['field_name'];
2945        $field = field_info_field($field_name);
2946        do {
2947          // Index 0 is reserved for the requested language, this way we ensure
2948          // that no field is actually populated with it.
2949          $langcode = $enabled_languages[mt_rand(1, count($enabled_languages) - 1)];
2950        }
2951        while (isset($languages[$l