Newer
Older
Dries Buytaert
committed
/**
* @file
Dries Buytaert
committed
* Tests for field.module.
Dries Buytaert
committed
*/
/**
Angie Byron
committed
* Parent class for Field API tests.
Dries Buytaert
committed
*/
Angie Byron
committed
class FieldTestCase extends DrupalWebTestCase {
Dries Buytaert
committed
var $default_storage = 'field_sql_storage';
/**
* Set the default field storage backend for fields created during tests.
*/
Dries Buytaert
committed
function setUp() {
Angie Byron
committed
// Since this is a base class for many test cases, support the same
// flexibility that DrupalWebTestCase::setUp() has for the modules to be
// passed in as either an array or a variable number of string arguments.
Dries Buytaert
committed
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
Angie Byron
committed
}
parent::setUp($modules);
Dries Buytaert
committed
// Set default storage backend.
variable_set('field_storage_default', $this->default_storage);
}
Angie Byron
committed
/**
* Generate random values for a field_test field.
*
* @param $cardinality
* Number of values to generate.
* @return
* An array of random values, in the format expected for field values.
*/
function _generateTestFieldValues($cardinality) {
$values = array();
for ($i = 0; $i < $cardinality; $i++) {
// field_test fields treat 0 as 'empty value'.
$values[$i]['value'] = mt_rand(1, 127);
}
return $values;
Dries Buytaert
committed
/**
* Assert that a field has the expected values in an entity.
*
* This function only checks a single column in the field values.
*
* @param $entity
* The entity to test.
* @param $field_name
* The name of the field to test
* @param $langcode
* The language code for the values.
* @param $expected_values
* The array of expected values.
* @param $column
* (Optional) the name of the column to check.
*/
function assertFieldValues($entity, $field_name, $langcode, $expected_values, $column = 'value') {
$e = clone $entity;
field_attach_load('test_entity', array($e->ftid => $e));
$values = isset($e->{$field_name}[$langcode]) ? $e->{$field_name}[$langcode] : array();
$this->assertEqual(count($values), count($expected_values), t('Expected number of values were saved.'));
Dries Buytaert
committed
foreach ($expected_values as $key => $value) {
$this->assertEqual($values[$key][$column], $value, t('Value @value was saved correctly.', array('@value' => $value)));
Dries Buytaert
committed
}
}
Angie Byron
committed
}
Angie Byron
committed
class FieldAttachTestCase extends FieldTestCase {
Angie Byron
committed
function setUp($modules = array()) {
// Since this is a base class for many test cases, support the same
// flexibility that DrupalWebTestCase::setUp() has for the modules to be
// passed in as either an array or a variable number of string arguments.
if (!is_array($modules)) {
$modules = func_get_args();
}
if (!in_array('field_test', $modules)) {
$modules[] = 'field_test';
}
parent::setUp($modules);
Dries Buytaert
committed
$this->field_name = drupal_strtolower($this->randomName() . '_field_name');
$this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
Dries Buytaert
committed
$this->field = field_create_field($this->field);
$this->field_id = $this->field['id'];
$this->instance = array(
'field_name' => $this->field_name,
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
Dries Buytaert
committed
'label' => $this->randomName() . '_label',
'description' => $this->randomName() . '_description',
'weight' => mt_rand(0, 127),
'settings' => array(
'test_instance_setting' => $this->randomName(),
),
'widget' => array(
'type' => 'test_field_widget',
'label' => 'Test Field',
'settings' => array(
'test_widget_setting' => $this->randomName(),
)
)
);
field_create_instance($this->instance);
}
Angie Byron
committed
}
/**
* Unit test class for storage-related field_attach_* functions.
*
* All field_attach_* test work with all field_storage plugins and
* all hook_field_attach_pre_{load,insert,update}() hooks.
*/
class FieldAttachStorageTestCase extends FieldAttachTestCase {
public static function getInfo() {
return array(
'name' => 'Field attach tests (storage-related)',
'description' => 'Test storage-related Field Attach API functions.',
'group' => 'Field API',
Angie Byron
committed
);
}
Dries Buytaert
committed
/**
* Check field values insert, update and load.
Dries Buytaert
committed
*
* Works independently of the underlying field storage backend. Inserts or
* updates random field data and then loads and verifies the data.
Dries Buytaert
committed
*/
function testFieldAttachSaveLoad() {
Angie Byron
committed
// Configure the instance so that we test hook_field_load() (see
// field_test_field_load() in field_test.module).
$this->instance['settings']['test_hook_field_load'] = TRUE;
field_update_instance($this->instance);
$langcode = LANGUAGE_NONE;
Angie Byron
committed
$entity_type = 'test_entity';
$values = array();
Dries Buytaert
committed
// TODO : test empty values filtering and "compression" (store consecutive deltas).
// Preparation: create three revisions and store them in $revision array.
Dries Buytaert
committed
for ($revision_id = 0; $revision_id < 3; $revision_id++) {
$revision[$revision_id] = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
// Note: we try to insert one extra value.
$values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
Dries Buytaert
committed
$current_revision = $revision_id;
// If this is the first revision do an insert.
Dries Buytaert
committed
if (!$revision_id) {
Angie Byron
committed
$revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
Dries Buytaert
committed
field_attach_insert($entity_type, $revision[$revision_id]);
// Otherwise do an update.
Angie Byron
committed
$revision[$revision_id]->{$this->field_name}[$langcode] = $values[$revision_id];
Dries Buytaert
committed
field_attach_update($entity_type, $revision[$revision_id]);
// Confirm current revision loads the correct data.
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
field_attach_load($entity_type, array(0 => $entity));
Dries Buytaert
committed
// Number of values per field loaded equals the field cardinality.
$this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Current revision: expected number of values'));
Dries Buytaert
committed
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
// The field value loaded matches the one inserted or updated.
$this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'] , $values[$current_revision][$delta]['value'], t('Current revision: expected value %delta was found.', array('%delta' => $delta)));
Angie Byron
committed
// The value added in hook_field_load() is found.
$this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', t('Current revision: extra information for value %delta was found', array('%delta' => $delta)));
Dries Buytaert
committed
}
// Confirm each revision loads the correct data.
foreach (array_keys($revision) as $revision_id) {
$entity = field_test_create_stub_entity(0, $revision_id, $this->instance['bundle']);
field_attach_load_revision($entity_type, array(0 => $entity));
Dries Buytaert
committed
// Number of values per field loaded equals the field cardinality.
$this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], t('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
Dries Buytaert
committed
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
// The field value loaded matches the one inserted or updated.
$this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $values[$revision_id][$delta]['value'], t('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
Angie Byron
committed
// The value added in hook_field_load() is found.
$this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['additional_key'], 'additional_value', t('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
Angie Byron
committed
}
}
}
/**
* Test the 'multiple' load feature.
*/
function testFieldAttachLoadMultiple() {
$entity_type = 'test_entity';
$langcode = LANGUAGE_NONE;
Angie Byron
committed
// Define 2 bundles.
$bundles = array(
1 => 'test_bundle_1',
2 => 'test_bundle_2',
);
field_test_create_bundle($bundles[1]);
field_test_create_bundle($bundles[2]);
// Define 3 fields:
// - field_1 is in bundle_1 and bundle_2,
// - field_2 is in bundle_1,
// - field_3 is in bundle_2.
$field_bundles_map = array(
1 => array(1, 2),
2 => array(1),
3 => array(2),
);
for ($i = 1; $i <= 3; $i++) {
$field_names[$i] = 'field_' . $i;
$field = array('field_name' => $field_names[$i], 'type' => 'test_field');
Dries Buytaert
committed
$field = field_create_field($field);
$field_ids[$i] = $field['id'];
Angie Byron
committed
foreach ($field_bundles_map[$i] as $bundle) {
$instance = array(
'field_name' => $field_names[$i],
'entity_type' => 'test_entity',
Angie Byron
committed
'bundle' => $bundles[$bundle],
'settings' => array(
// Configure the instance so that we test hook_field_load()
// (see field_test_field_load() in field_test.module).
'test_hook_field_load' => TRUE,
),
);
field_create_instance($instance);
}
}
// Create one test entity per bundle, with random values.
foreach ($bundles as $index => $bundle) {
$entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
$entity = clone($entities[$index]);
Dries Buytaert
committed
$instances = field_info_instances('test_entity', $bundle);
Angie Byron
committed
foreach ($instances as $field_name => $instance) {
$values[$index][$field_name] = mt_rand(1, 127);
Angie Byron
committed
$entity->$field_name = array($langcode => array(array('value' => $values[$index][$field_name])));
Angie Byron
committed
}
field_attach_insert($entity_type, $entity);
}
// Check that a single load correctly loads field values for both entities.
field_attach_load($entity_type, $entities);
foreach ($entities as $index => $entity) {
Dries Buytaert
committed
$instances = field_info_instances($entity_type, $bundles[$index]);
Angie Byron
committed
foreach ($instances as $field_name => $instance) {
// The field value loaded matches the one inserted.
$this->assertEqual($entity->{$field_name}[$langcode][0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
Angie Byron
committed
// The value added in hook_field_load() is found.
$this->assertEqual($entity->{$field_name}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
// Check that the single-field load option works.
$entity = field_test_create_stub_entity(1, 1, $bundles[1]);
Dries Buytaert
committed
field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('field_id' => $field_ids[1]));
$this->assertEqual($entity->{$field_names[1]}[$langcode][0]['value'], $values[1][$field_names[1]], t('Entity %index: expected value was found.', array('%index' => 1)));
$this->assertEqual($entity->{$field_names[1]}[$langcode][0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => 1)));
$this->assert(!isset($entity->{$field_names[2]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
$this->assert(!isset($entity->{$field_names[3]}), t('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
Dries Buytaert
committed
/**
* Test saving and loading fields using different storage backends.
*/
function testFieldAttachSaveLoadDifferentStorage() {
$entity_type = 'test_entity';
$langcode = LANGUAGE_NONE;
Dries Buytaert
committed
// Create two fields using different storage backends, and their instances.
$fields = array(
array(
'field_name' => 'field_1',
'type' => 'test_field',
'cardinality' => 4,
'storage' => array('type' => 'field_sql_storage')
),
array(
'field_name' => 'field_2',
'type' => 'test_field',
'cardinality' => 4,
'storage' => array('type' => 'field_test_storage')
),
);
foreach ($fields as $field) {
field_create_field($field);
$instance = array(
'field_name' => $field['field_name'],
'entity_type' => 'test_entity',
Dries Buytaert
committed
'bundle' => 'test_bundle',
);
field_create_instance($instance);
}
$entity_init = field_test_create_stub_entity();
// Create entity and insert random values.
$entity = clone($entity_init);
$values = array();
foreach ($fields as $field) {
$values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
$entity->{$field['field_name']}[$langcode] = $values[$field['field_name']];
}
field_attach_insert($entity_type, $entity);
// Check that values are loaded as expected.
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
foreach ($fields as $field) {
$this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}[$langcode], t('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
Dries Buytaert
committed
}
}
Dries Buytaert
committed
/**
* Test storage details alteration.
*
* @see field_test_storage_details_alter()
*/
function testFieldStorageDetailsAlter() {
$field_name = 'field_test_change_my_details';
$field = array(
'field_name' => $field_name,
'type' => 'test_field',
'cardinality' => 4,
'storage' => array('type' => 'field_test_storage'),
);
$field = field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'test_entity',
Dries Buytaert
committed
'bundle' => 'test_bundle',
);
field_create_instance($instance);
$field = field_info_field($instance['field_name']);
$instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
Dries Buytaert
committed
// The storage details are indexed by a storage engine type.
$this->assertTrue(array_key_exists('drupal_variables', $field['storage']['details']), t('The storage type is Drupal variables.'));
Dries Buytaert
committed
Angie Byron
committed
$details = $field['storage']['details']['drupal_variables'];
Dries Buytaert
committed
// The field_test storage details are indexed by variable name. The details
// are altered, so moon and mars are correct for this test.
$this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), t('Moon is available in the instance array.'));
$this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), t('Mars is available in the instance array.'));
Dries Buytaert
committed
// Test current and revision storage details together because the columns
// are the same.
foreach ((array) $field['columns'] as $column_name => $attributes) {
$this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
$this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
Dries Buytaert
committed
}
}
Dries Buytaert
committed
/**
* Tests insert and update with missing or NULL fields.
*/
function testFieldAttachSaveMissingData() {
$entity_type = 'test_entity';
$entity_init = field_test_create_stub_entity();
$langcode = LANGUAGE_NONE;
Dries Buytaert
committed
// Insert: Field is missing.
$entity = clone($entity_init);
field_attach_insert($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
Dries Buytaert
committed
// Insert: Field is NULL.
field_cache_clear();
$entity = clone($entity_init);
Dries Buytaert
committed
$entity->{$this->field_name} = NULL;
field_attach_insert($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
// Add some real data.
field_cache_clear();
$entity = clone($entity_init);
$values = $this->_generateTestFieldValues(1);
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = $values;
field_attach_insert($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
// Update: Field is missing. Data should survive.
field_cache_clear();
$entity = clone($entity_init);
field_attach_update($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Update: missing field leaves existing values in place'));
// Update: Field is NULL. Data should be wiped.
field_cache_clear();
$entity = clone($entity_init);
Dries Buytaert
committed
$entity->{$this->field_name} = NULL;
field_attach_update($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
Dries Buytaert
committed
// Re-add some data.
field_cache_clear();
$entity = clone($entity_init);
$values = $this->_generateTestFieldValues(1);
$entity->{$this->field_name}[$langcode] = $values;
field_attach_update($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Field data saved'));
Dries Buytaert
committed
// Update: Field is empty array. Data should be wiped.
field_cache_clear();
$entity = clone($entity_init);
$entity->{$this->field_name} = array();
field_attach_update($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertTrue(empty($entity->{$this->field_name}), t('Update: empty array removes existing values'));
}
/**
* Test insert with missing or NULL fields, with default value.
*/
function testFieldAttachSaveMissingDataDefaultValue() {
// Add a default value function.
$this->instance['default_value_function'] = 'field_test_default_value';
field_update_instance($this->instance);
$entity_type = 'test_entity';
$entity_init = field_test_create_stub_entity();
$langcode = LANGUAGE_NONE;
// Insert: Field is NULL.
$entity = clone($entity_init);
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = NULL;
field_attach_insert($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertTrue(empty($entity->{$this->field_name}[$langcode]), t('Insert: NULL field results in no value saved'));
// Insert: Field is missing.
field_cache_clear();
$entity = clone($entity_init);
field_attach_insert($entity_type, $entity);
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
$values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
$this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Insert: missing field results in default value saved'));
Angie Byron
committed
/**
* Test field_attach_delete().
*/
function testFieldAttachDelete() {
$entity_type = 'test_entity';
$langcode = LANGUAGE_NONE;
Angie Byron
committed
$rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
// Create revision 0
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$rev[0]->{$this->field_name}[$langcode] = $values;
field_attach_insert($entity_type, $rev[0]);
// Create revision 1
$rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
$rev[1]->{$this->field_name}[$langcode] = $values;
field_attach_update($entity_type, $rev[1]);
// Create revision 2
$rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
$rev[2]->{$this->field_name}[$langcode] = $values;
field_attach_update($entity_type, $rev[2]);
// Confirm each revision loads
foreach (array_keys($rev) as $vid) {
$read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
field_attach_load_revision($entity_type, array(0 => $read));
$this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
Angie Byron
committed
}
// Delete revision 1, confirm the other two still load.
field_attach_delete_revision($entity_type, $rev[1]);
foreach (array(0, 2) as $vid) {
$read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
field_attach_load_revision($entity_type, array(0 => $read));
$this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
Angie Byron
committed
}
// Confirm the current revision still loads
$read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
field_attach_load($entity_type, array(0 => $read));
$this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
Angie Byron
committed
// Delete all field data, confirm nothing loads
field_attach_delete($entity_type, $rev[2]);
foreach (array(0, 1, 2) as $vid) {
$read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']);
field_attach_load_revision($entity_type, array(0 => $read));
$this->assertIdentical($read->{$this->field_name}, array(), "The test entity revision $vid is deleted.");
Angie Byron
committed
}
$read = field_test_create_stub_entity(0, 2, $this->instance['bundle']);
field_attach_load($entity_type, array(0 => $read));
$this->assertIdentical($read->{$this->field_name}, array(), t('The test entity current revision is deleted.'));
Angie Byron
committed
}
/**
* Test field_attach_create_bundle() and field_attach_rename_bundle().
*/
function testFieldAttachCreateRenameBundle() {
// Create a new bundle. This has to be initiated by the module so that its
// hook_entity_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_create_bundle($new_bundle);
// Add an instance to that bundle.
$this->instance['bundle'] = $new_bundle;
field_create_instance($this->instance);
// Save an entity with data in the field.
Angie Byron
committed
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
Angie Byron
committed
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity->{$this->field_name}[$langcode] = $values;
$entity_type = 'test_entity';
field_attach_insert($entity_type, $entity);
// Verify the field data is present on load.
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
field_attach_load($entity_type, array(0 => $entity));
$this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
// Rename the bundle. This has to be initiated by the module so that its
// hook_entity_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_rename_bundle($this->instance['bundle'], $new_bundle);
// Check that the instance definition has been updated.
Dries Buytaert
committed
$this->instance = field_info_instance($entity_type, $this->field_name, $new_bundle);
Angie Byron
committed
$this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
// Verify the field data is present on load.
Dries Buytaert
committed
$entity = field_test_create_stub_entity(0, 0, $new_bundle);
Angie Byron
committed
field_attach_load($entity_type, array(0 => $entity));
$this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage");
}
/**
* Test field_attach_delete_bundle().
*/
function testFieldAttachDeleteBundle() {
// Create a new bundle. This has to be initiated by the module so that its
// hook_entity_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_create_bundle($new_bundle);
// Add an instance to that bundle.
$this->instance['bundle'] = $new_bundle;
field_create_instance($this->instance);
// Create a second field for the test bundle
$field_name = drupal_strtolower($this->randomName() . '_field_name');
$field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'test_entity',
Angie Byron
committed
'bundle' => $this->instance['bundle'],
'label' => $this->randomName() . '_label',
'description' => $this->randomName() . '_description',
'weight' => mt_rand(0, 127),
// test_field has no instance settings
'widget' => array(
'type' => 'test_field_widget',
'settings' => array(
'size' => mt_rand(0, 255))));
field_create_instance($instance);
// Save an entity with data for both fields
Angie Byron
committed
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
Angie Byron
committed
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity->{$this->field_name}[$langcode] = $values;
$entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1);
Dries Buytaert
committed
field_attach_insert('test_entity', $entity);
Angie Byron
committed
// Verify the fields are present on load
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
Dries Buytaert
committed
field_attach_load('test_entity', array(0 => $entity));
Angie Byron
committed
$this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded');
$this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded');
// Delete the bundle. This has to be initiated by the module so that its
// hook_entity_info() is consistent.
field_test_delete_bundle($this->instance['bundle']);
// Verify no data gets loaded
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
Dries Buytaert
committed
field_attach_load('test_entity', array(0 => $entity));
Angie Byron
committed
$this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field');
$this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field');
// Verify that the instances are gone
Dries Buytaert
committed
$this->assertFalse(field_read_instance('test_entity', $this->field_name, $this->instance['bundle']), "First field is deleted");
$this->assertFalse(field_read_instance('test_entity', $field_name, $instance['bundle']), "Second field is deleted");
Angie Byron
committed
}
}
Angie Byron
committed
/**
* Unit test class for non-storage related field_attach_* functions.
*/
class FieldAttachOtherTestCase extends FieldAttachTestCase {
public static function getInfo() {
return array(
'name' => 'Field attach tests (other)',
'description' => 'Test other Field Attach API functions.',
'group' => 'Field API',
Angie Byron
committed
);
}
/**
Dries Buytaert
committed
* Test field_attach_view() and field_attach_prepare_view().
Angie Byron
committed
*/
function testFieldAttachView() {
$entity_type = 'test_entity';
$entity_init = field_test_create_stub_entity();
$langcode = LANGUAGE_NONE;
// Populate values to be displayed.
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity_init->{$this->field_name}[$langcode] = $values;
// Simple formatter, label displayed.
$entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
'type' => 'field_test_default',
'settings' => array(
'test_formatter_setting' => $formatter_setting,
)
),
);
field_update_instance($this->instance);
Dries Buytaert
committed
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
$entity->content = field_attach_view($entity_type, $entity, 'full');
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertRaw($this->instance['label'], "Label is displayed.");
foreach ($values as $delta => $value) {
$this->content = $output;
$this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
}
// Label hidden.
$entity = clone($entity_init);
$this->instance['display']['full']['label'] = 'hidden';
field_update_instance($this->instance);
Dries Buytaert
committed
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
$entity->content = field_attach_view($entity_type, $entity, 'full');
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
// Field hidden.
$entity = clone($entity_init);
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
'type' => 'hidden',
),
);
field_update_instance($this->instance);
Dries Buytaert
committed
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
$entity->content = field_attach_view($entity_type, $entity, 'full');
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed.");
foreach ($values as $delta => $value) {
$this->assertNoRaw($value['value'], "Hidden field: value $delta is not displayed.");
}
// Multiple formatter.
$entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
'type' => 'field_test_multiple',
'settings' => array(
'test_formatter_setting_multiple' => $formatter_setting,
)
),
);
field_update_instance($this->instance);
Dries Buytaert
committed
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
$entity->content = field_attach_view($entity_type, $entity, 'full');
$output = drupal_render($entity->content);
$display = $formatter_setting;
foreach ($values as $delta => $value) {
$display .= "|$delta:{$value['value']}";
}
$this->content = $output;
$this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
Dries Buytaert
committed
// Test a formatter that uses hook_field_formatter_prepare_view().
$entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
Dries Buytaert
committed
'type' => 'field_test_with_prepare_view',
'settings' => array(
'test_formatter_setting_additional' => $formatter_setting,
)
),
);
field_update_instance($this->instance);
Dries Buytaert
committed
field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full');
$entity->content = field_attach_view($entity_type, $entity, 'full');
$output = drupal_render($entity->content);
$this->content = $output;
foreach ($values as $delta => $value) {
$this->content = $output;
$expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
$this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
}
// TODO:
// - check display order with several fields
Angie Byron
committed
// Preprocess template.
$variables = array();
field_attach_preprocess($entity_type, $entity, $entity->content, $variables);
$result = TRUE;
foreach ($values as $delta => $item) {
if ($variables[$this->field_name][$delta]['value'] !== $item['value']) {
$result = FALSE;
break;
}
}
$this->assertTrue($result, t('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name)));
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
/**
* Tests the 'multiple entity' behavior of field_attach_prepare_view().
*/
function testFieldAttachPrepareViewMultiple() {
$entity_type = 'test_entity';
$langcode = LANGUAGE_NONE;
// Set the instance to be hidden.
$this->instance['display']['full']['type'] = 'hidden';
field_update_instance($this->instance);
// Set up a second instance on another bundle, with a formatter that uses
// hook_field_formatter_prepare_view().
field_test_create_bundle('test_bundle_2');
$formatter_setting = $this->randomName();
$this->instance2 = $this->instance;
$this->instance2['bundle'] = 'test_bundle_2';
$this->instance2['display']['full'] = array(
'type' => 'field_test_with_prepare_view',
'settings' => array(
'test_formatter_setting_additional' => $formatter_setting,
)
);
field_create_instance($this->instance2);
// Create one entity in each bundle.
$entity1_init = field_test_create_stub_entity(1, 1, 'test_bundle');
$values1 = $this->_generateTestFieldValues($this->field['cardinality']);
$entity1_init->{$this->field_name}[$langcode] = $values1;
$entity2_init = field_test_create_stub_entity(2, 2, 'test_bundle_2');
$values2 = $this->_generateTestFieldValues($this->field['cardinality']);
$entity2_init->{$this->field_name}[$langcode] = $values2;
// Run prepare_view, and check that the entities come out as expected.
$entity1 = clone($entity1_init);
$entity2 = clone($entity2_init);
field_attach_prepare_view($entity_type, array($entity1->ftid => $entity1, $entity2->ftid => $entity2), 'full');
$this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.');
$this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.');
// Same thing, reversed order.
$entity1 = clone($entity1_init);
$entity2 = clone($entity2_init);
field_attach_prepare_view($entity_type, array($entity2->ftid => $entity2, $entity1->ftid => $entity1), 'full');
$this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.');
$this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.');
}
Angie Byron
committed
/**
* Test field cache.
*/
function testFieldAttachCache() {
Angie Byron
committed
// Initialize random values and a test entity.
$entity_init = field_test_create_stub_entity(1, 1, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
$values = $this->_generateTestFieldValues($this->field['cardinality']);
// Non-cacheable entity type.
Dries Buytaert
committed
$entity_type = 'test_entity';
$cid = "field:$entity_type:{$entity_init->ftid}";
// Check that no initial cache entry is present.
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Non-cached: no initial cache entry'));
// Save, and check that no cache entry is present.
$entity = clone($entity_init);
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = $values;
Dries Buytaert
committed
field_attach_insert($entity_type, $entity);
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Non-cached: no cache entry on insert'));
// Load, and check that no cache entry is present.
$entity = clone($entity_init);
Dries Buytaert
committed
field_attach_load($entity_type, array($entity->ftid => $entity));
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Non-cached: no cache entry on load'));
Angie Byron
committed
// Cacheable entity type.
Dries Buytaert
committed
$entity_type = 'test_cacheable_entity';
$cid = "field:$entity_type:{$entity_init->ftid}";
$instance = $this->instance;
$instance['entity_type'] = $entity_type;
Dries Buytaert
committed
field_create_instance($instance);
// Check that no initial cache entry is present.
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Cached: no initial cache entry'));
// Save, and check that no cache entry is present.
$entity = clone($entity_init);
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = $values;
Dries Buytaert
committed
field_attach_insert($entity_type, $entity);
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on insert'));
Dries Buytaert
committed
// Load a single field, and check that no cache entry is present.
$entity = clone($entity_init);
Dries Buytaert
committed
field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id));
Dries Buytaert
committed
$cache = cache('field')->get($cid);
$this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on loading a single field'));
Dries Buytaert
committed
// Load, and check that a cache entry is present with the expected values.
$entity = clone($entity_init);
Dries Buytaert
committed
field_attach_load($entity_type, array($entity->ftid => $entity));
Dries Buytaert
committed
$cache = cache('field')->get($cid);
$this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
// Update with different values, and check that the cache entry is wiped.
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity = clone($entity_init);
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = $values;
Dries Buytaert
committed
field_attach_update($entity_type, $entity);
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on update'));
// Load, and check that a cache entry is present with the expected values.
$entity = clone($entity_init);
Dries Buytaert
committed
field_attach_load($entity_type, array($entity->ftid => $entity));
Dries Buytaert
committed
$cache = cache('field')->get($cid);
$this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
// Create a new revision, and check that the cache entry is wiped.
$entity_init = field_test_create_stub_entity(1, 2, $this->instance['bundle']);
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity = clone($entity_init);
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = $values;
Dries Buytaert
committed
field_attach_update($entity_type, $entity);
Dries Buytaert
committed
$cache = cache('field')->get($cid);
$this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on new revision creation'));
// Load, and check that a cache entry is present with the expected values.
$entity = clone($entity_init);
Dries Buytaert
committed
field_attach_load($entity_type, array($entity->ftid => $entity));
Dries Buytaert
committed
$cache = cache('field')->get($cid);
$this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
// Delete, and check that the cache entry is wiped.
Dries Buytaert
committed
field_attach_delete($entity_type, $entity);
Dries Buytaert
committed
$this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry after delete'));
Angie Byron
committed
/**
* Test field_attach_validate().
*
* Verify that field_attach_validate() invokes the correct
* hook_field_validate.
*/
function testFieldAttachValidate() {
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$langcode = LANGUAGE_NONE;
// Set up values to generate errors
$values = array();
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
$values[$delta]['value'] = -1;
}
// Arrange for item 1 not to generate an error
$values[1]['value'] = 1;
Angie Byron
committed
$entity->{$this->field_name}[$langcode] = $values;
Angie Byron
committed
try {
field_attach_validate($entity_type, $entity);
}
catch (FieldValidationException $e) {
$errors = $e->errors;
}
foreach ($values as $delta => $value) {
if ($value['value'] != 1) {
Angie Byron
committed
$this->assertIdentical($errors[$this->field_name][$langcode][$delta][0]['error'], 'field_test_invalid', "Error set on value $delta");
$this->assertEqual(count($errors[$this->field_name][$langcode][$delta]), 1, "Only one error set on value $delta");
unset($errors[$this->field_name][$langcode][$delta]);
Angie Byron
committed
$this->assertFalse(isset($errors[$this->field_name][$langcode][$delta]), "No error set on value $delta");
Angie Byron
committed
$this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set');
Dries Buytaert
committed
// Check that cardinality is validated.
$entity->{$this->field_name}[$langcode] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
try {
field_attach_validate($entity_type, $entity);
}
catch (FieldValidationException $e) {
$errors = $e->errors;
}
$this->assertEqual($errors[$this->field_name][$langcode][0][0]['error'], 'field_cardinality', t('Cardinality validation failed.'));
Dries Buytaert
committed
Angie Byron
committed
/**
* Test field_attach_form().
*
* This could be much more thorough, but it does verify that the correct
* widgets show up.
*/
function testFieldAttachForm() {
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
Dries Buytaert
committed
$form = array();
$form_state = form_state_defaults();
field_attach_form($entity_type, $entity, $form, $form_state);
$langcode = LANGUAGE_NONE;
Angie Byron
committed
$this->assertEqual($form[$this->field_name][$langcode]['#title'], $this->instance['label'], "Form title is {$this->instance['label']}");
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
// field_test_widget uses 'textfield'
Angie Byron
committed
$this->assertEqual($form[$this->field_name][$langcode][$delta]['value']['#type'], 'textfield', "Form delta $delta widget is textfield");
Angie Byron
committed
/**
* Test field_attach_submit().
*/
function testFieldAttachSubmit() {
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
// Build the form.
Dries Buytaert
committed
$form = array();
$form_state = form_state_defaults();
field_attach_form($entity_type, $entity, $form, $form_state);
// Simulate incoming values.
$values = array();
$weights = array();
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
$values[$delta]['value'] = mt_rand(1, 127);
// Assign random weight.
do {
$weight = mt_rand(0, $this->field['cardinality']);
} while (in_array($weight, $weights));
$weights[$delta] = $weight;
$values[$delta]['_weight'] = $weight;
}
// Leave an empty value. 'field_test' fields are empty if empty().
$values[1]['value'] = 0;