5 * Tests for field.module.
9 * Parent class for Field API tests.
11 class FieldTestCase
extends DrupalWebTestCase
{
12 var
$default_storage = 'field_sql_storage';
15 * Set the default field storage backend for fields created during tests.
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];
25 parent
::setUp($modules);
26 // Set default storage backend.
27 variable_set('field_storage_default', $this->default_storage
);
31 * Generate random values for a field_test field.
34 * Number of values to generate.
36 * An array of random values, in the format expected for field values.
38 function _generateTestFieldValues($cardinality) {
40 for ($i = 0; $i < $cardinality; $i++) {
41 // field_test fields treat 0 as 'empty value'.
42 $values[$i]['value'] = mt_rand(1, 127);
48 * Assert that a field has the expected values in an entity.
50 * This function only checks a single column in the field values.
55 * The name of the field to test
57 * The language code for the values.
58 * @param $expected_values
59 * The array of expected values.
61 * (Optional) the name of the column to check.
63 function assertFieldValues($entity, $field_name, $langcode, $expected_values, $column = 'value') {
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)));
74 class FieldAttachTestCase
extends FieldTestCase
{
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];
83 if (!in_array('field_test', $modules)) {
84 $modules[] = 'field_test';
86 parent
::setUp($modules);
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),
100 'test_instance_setting' => $this->randomName(),
103 'type' => 'test_field_widget',
104 'label' => 'Test Field',
106 'test_widget_setting' => $this->randomName(),
110 field_create_instance($this->instance
);
115 * Unit test class for storage-related field_attach_* functions.
117 * All field_attach_* test work with all field_storage plugins and
118 * all hook_field_attach_pre_{load,insert,update}() hooks.
120 class FieldAttachStorageTestCase
extends FieldAttachTestCase
{
121 public static
function getInfo() {
123 'name' => 'Field attach tests (storage-related)',
124 'description' => 'Test storage-related Field Attach API functions.',
125 'group' => 'Field API',
130 * Check field values insert, update and load.
132 * Works independently of the underlying field storage backend. Inserts or
133 * updates random field data and then loads and verifies the data.
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
;
142 $entity_type = 'test_entity';
145 // TODO : test empty values filtering and "compression" (store consecutive deltas).
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.
155 $revision[$revision_id]->{$this->field_name
}[$langcode] = $values[$revision_id];
156 field_attach_insert($entity_type, $revision[$revision_id]);
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]);
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)));
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)));
193 * Test the 'multiple' load feature.
195 function testFieldAttachLoadMultiple() {
196 $entity_type = 'test_entity';
197 $langcode = LANGUAGE_NONE
;
201 1 => 'test_bundle_1',
202 2 => 'test_bundle_2',
204 field_test_create_bundle($bundles[1]);
205 field_test_create_bundle($bundles[2]);
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(
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) {
222 'field_name' => $field_names[$i],
223 'entity_type' => 'test_entity',
224 'bundle' => $bundles[$bundle],
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
,
231 field_create_instance($instance);
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])));
244 field_attach_insert($entity_type, $entity);
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)));
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])));
269 * Test saving and loading fields using different storage backends.
271 function testFieldAttachSaveLoadDifferentStorage() {
272 $entity_type = 'test_entity';
273 $langcode = LANGUAGE_NONE
;
275 // Create two fields using different storage backends, and their instances.
278 'field_name' => 'field_1',
279 'type' => 'test_field',
281 'storage' => array('type' => 'field_sql_storage')
284 'field_name' => 'field_2',
285 'type' => 'test_field',
287 'storage' => array('type' => 'field_test_storage')
290 foreach ($fields as
$field) {
291 field_create_field($field);
293 'field_name' => $field['field_name'],
294 'entity_type' => 'test_entity',
295 'bundle' => 'test_bundle',
297 field_create_instance($instance);
300 $entity_init = field_test_create_stub_entity();
302 // Create entity and insert random values.
303 $entity = clone($entity_init);
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']];
309 field_attach_insert($entity_type, $entity);
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'])));
320 * Test storage details alteration.
322 * @see field_test_storage_details_alter()
324 function testFieldStorageDetailsAlter() {
325 $field_name = 'field_test_change_my_details';
327 'field_name' => $field_name,
328 'type' => 'test_field',
330 'storage' => array('type' => 'field_test_storage'),
332 $field = field_create_field($field);
334 'field_name' => $field_name,
335 'entity_type' => 'test_entity',
336 'bundle' => 'test_bundle',
338 field_create_instance($instance);
340 $field = field_info_field($instance['field_name']);
341 $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
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.'));
346 $details = $field['storage']['details']['drupal_variables'];
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.'));
353 // Test current and revision storage details together because the columns
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]')));
362 * Tests insert and update with missing or NULL fields.
364 function testFieldAttachSaveMissingData() {
365 $entity_type = 'test_entity';
366 $entity_init = field_test_create_stub_entity();
367 $langcode = LANGUAGE_NONE
;
369 // Insert: Field is missing.
370 $entity = clone($entity_init);
371 field_attach_insert($entity_type, $entity);
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'));
377 // Insert: Field is NULL.
379 $entity = clone($entity_init);
380 $entity->{$this->field_name
} = NULL
;
381 field_attach_insert($entity_type, $entity);
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'));
387 // Add some real data.
389 $entity = clone($entity_init);
390 $values = $this->_generateTestFieldValues(1);
391 $entity->{$this->field_name
}[$langcode] = $values;
392 field_attach_insert($entity_type, $entity);
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'));
398 // Update: Field is missing. Data should survive.
400 $entity = clone($entity_init);
401 field_attach_update($entity_type, $entity);
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'));
407 // Update: Field is NULL. Data should be wiped.
409 $entity = clone($entity_init);
410 $entity->{$this->field_name
} = NULL
;
411 field_attach_update($entity_type, $entity);
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'));
419 $entity = clone($entity_init);
420 $values = $this->_generateTestFieldValues(1);
421 $entity->{$this->field_name
}[$langcode] = $values;
422 field_attach_update($entity_type, $entity);
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'));
428 // Update: Field is empty array. Data should be wiped.
430 $entity = clone($entity_init);
431 $entity->{$this->field_name
} = array();
432 field_attach_update($entity_type, $entity);
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'));
440 * Test insert with missing or NULL fields, with default value.
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
);
447 $entity_type = 'test_entity';
448 $entity_init = field_test_create_stub_entity();
449 $langcode = LANGUAGE_NONE
;
451 // Insert: Field is NULL.
452 $entity = clone($entity_init);
453 $entity->{$this->field_name
}[$langcode] = NULL
;
454 field_attach_insert($entity_type, $entity);
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'));
460 // Insert: Field is missing.
462 $entity = clone($entity_init);
463 field_attach_insert($entity_type, $entity);
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'));
472 * Test field_attach_delete().
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']);
480 $values = $this->_generateTestFieldValues($this->field
['cardinality']);
481 $rev[0]->{$this->field_name
}[$langcode] = $values;
482 field_attach_insert($entity_type, $rev[0]);
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]);
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]);
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.");
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.");
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.");
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.");
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.'));
527 * Test field_attach_create_bundle() and field_attach_rename_bundle().
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);
535 // Add an instance to that bundle.
536 $this->instance
['bundle'] = $new_bundle;
537 field_create_instance($this->instance
);
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);
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");
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);
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.");
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");
568 * Test field_attach_delete_bundle().
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);
576 // Add an instance to that bundle.
577 $this->instance
['bundle'] = $new_bundle;
578 field_create_instance($this->instance
);
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);
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
593 'type' => 'test_field_widget',
595 'size' => mt_rand(0, 255))));
596 field_create_instance($instance);
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);
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');
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']);
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');
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");
629 * Unit test class for non-storage related field_attach_* functions.
631 class FieldAttachOtherTestCase
extends FieldAttachTestCase
{
632 public static
function getInfo() {
634 'name' => 'Field attach tests (other)',
635 'description' => 'Test other Field Attach API functions.',
636 'group' => 'Field API',
641 * Test field_attach_view() and field_attach_prepare_view().
643 function testFieldAttachView() {
644 $entity_type = 'test_entity';
645 $entity_init = field_test_create_stub_entity();
646 $langcode = LANGUAGE_NONE
;
648 // Populate values to be displayed.
649 $values = $this->_generateTestFieldValues($this->field
['cardinality']);
650 $entity_init->{$this->field_name
}[$langcode] = $values;
652 // Simple formatter, label displayed.
653 $entity = clone($entity_init);
654 $formatter_setting = $this->randomName();
655 $this->instance
['display'] = array(
658 'type' => 'field_test_default',
660 'test_formatter_setting' => $formatter_setting,
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.");
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.");
686 $entity = clone($entity_init);
687 $this->instance
['display'] = array(
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.");
703 // Multiple formatter.
704 $entity = clone($entity_init);
705 $formatter_setting = $this->randomName();
706 $this->instance
['display'] = array(
709 'type' => 'field_test_multiple',
711 'test_formatter_setting_multiple' => $formatter_setting,
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']}";
723 $this->content
= $output;
724 $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
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(
732 'type' => 'field_test_with_prepare_view',
734 'test_formatter_setting_additional' => $formatter_setting,
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.");
750 // - check display order with several fields
752 // Preprocess template.
753 $variables = array();
754 field_attach_preprocess($entity_type, $entity, $entity->content
, $variables);
756 foreach ($values as
$delta => $item) {
757 if ($variables[$this->field_name
][$delta]['value'] !== $item['value']) {
762 $this->assertTrue($result, t('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name
)));
766 * Tests the 'multiple entity' behavior of field_attach_prepare_view().
768 function testFieldAttachPrepareViewMultiple() {
769 $entity_type = 'test_entity';
770 $langcode = LANGUAGE_NONE
;
772 // Set the instance to be hidden.
773 $this->instance
['display']['full']['type'] = 'hidden';
774 field_update_instance($this->instance
);
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',
785 'test_formatter_setting_additional' => $formatter_setting,
788 field_create_instance($this->instance2
);
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;
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;
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.');
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.');
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']);
823 // Non-cacheable entity type.
824 $entity_type = 'test_entity';
825 $cid = "field:$entity_type:{$entity_init->ftid}";
827 // Check that no initial cache entry is present.
828 $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
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'));
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'));
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);
849 // Check that no initial cache entry is present.
850 $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
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'));
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'));
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'));
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'));
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'));
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'));
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'));
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'));
904 * Test field_attach_validate().
906 * Verify that field_attach_validate() invokes the correct
907 * hook_field_validate.
909 function testFieldAttachValidate() {
910 $entity_type = 'test_entity';
911 $entity = field_test_create_stub_entity(0, 0, $this->instance
['bundle']);
912 $langcode = LANGUAGE_NONE
;
914 // Set up values to generate errors
916 for ($delta = 0; $delta < $this->field
['cardinality']; $delta++) {
917 $values[$delta]['value'] = -1;
919 // Arrange for item 1 not to generate an error
920 $values[1]['value'] = 1;
921 $entity->{$this->field_name
}[$langcode] = $values;
924 field_attach_validate($entity_type, $entity);
926 catch (FieldValidationException
$e) {
927 $errors = $e->errors
;
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]);
937 $this->assertFalse(isset($errors[$this->field_name
][$langcode][$delta]), "No error set on value $delta");
940 $this->assertEqual(count($errors[$this->field_name
][$langcode]), 0, 'No extraneous errors set');
942 // Check that cardinality is validated.
943 $entity->{$this->field_name
}[$langcode] = $this->_generateTestFieldValues($this->field
['cardinality'] + 1);
945 field_attach_validate($entity_type, $entity);
947 catch (FieldValidationException
$e) {
948 $errors = $e->errors
;
950 $this->assertEqual($errors[$this->field_name
][$langcode][0][0]['error'], 'field_cardinality', t('Cardinality validation failed.'));
955 * Test field_attach_form().
957 * This could be much more thorough, but it does verify that the correct
960 function testFieldAttachForm() {
961 $entity_type = 'test_entity';
962 $entity = field_test_create_stub_entity(0, 0, $this->instance
['bundle']);
965 $form_state = form_state_defaults();
966 field_attach_form($entity_type, $entity, $form, $form_state);
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");
977 * Test field_attach_submit().
979 function testFieldAttachSubmit() {
980 $entity_type = 'test_entity';
981 $entity = field_test_create_stub_entity(0, 0, $this->instance
['bundle']);
985 $form_state = form_state_defaults();
986 field_attach_form($entity_type, $entity, $form, $form_state);
988 // Simulate incoming values.
991 for ($delta = 0; $delta < $this->field
['cardinality']; $delta++) {
992 $values[$delta]['value'] = mt_rand(1, 127);
993 // Assign random weight.
995 $weight = mt_rand(0, $this->field
['cardinality']);
996 } while (in_array($weight, $weights));
997 $weights[$delta] = $weight;
998 $values[$delta]['_weight'] = $weight;
1000 // Leave an empty value. 'field_test' fields are empty if empty().
1001 $values[1]['value'] = 0;
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);
1011 $expected_values = array();
1012 foreach ($weights as
$key => $value) {
1014 $expected_values[] = array('value' => $values[$key]['value']);
1017 $this->assertIdentical($entity->{$this->field_name
}[$langcode], $expected_values, 'Submit filters empty values');
1021 class FieldInfoTestCase
extends FieldTestCase
{
1023 public static
function getInfo() {
1025 'name' => 'Field info tests',
1026 'description' => 'Get information about existing fields, instances and bundles.',
1027 'group' => 'Field API',
1032 parent
::setUp('field_test');
1036 * Test that field types and field definitions are correcly cached.
1038 function testFieldInfo() {
1039 // Test that field_test module's fields, widgets, and formatters show up.
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
;
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"));
1051 $this->assertEqual($info[$t_key]['module'], 'field_test', t("Field type field_test module appears"));
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"));
1060 $this->assertEqual($info[$f_key]['module'], 'field_test', t("Formatter type field_test module appears"));
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"));
1069 $this->assertEqual($info[$w_key]['module'], 'field_test', t("Widget type field_test module appears"));
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"));
1078 $this->assertEqual($info[$s_key]['module'], 'field_test', t("Storage type field_test module appears"));
1081 // Verify that no unexpected instances exist.
1082 $core_fields = field_info_fields();
1083 $instances = field_info_instances('test_entity', 'test_bundle');
1084 $this->assertTrue(empty($instances), t('With no instances, info bundles is empty.'));
1086 // Create a field, verify it shows up.
1088 'field_name' => drupal_strtolower($this->randomName()),
1089 'type' => 'test_field',
1091 field_create_field($field);
1092 $fields = field_info_fields();
1093 $this->assertEqual(count($fields), count($core_fields) + 1, t('One new field exists'));
1094 $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], t('info fields contains field name'));
1095 $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], t('info fields contains field type'));
1096 $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', t('info fields contains field module'));
1097 $settings = array('test_field_setting' => 'dummy test string');
1098 foreach ($settings as
$key => $val) {
1099 $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, t("Field setting $key has correct default value $val"));
1101 $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, t('info fields contains cardinality 1'));
1102 $this->assertEqual($fields[$field['field_name']]['active'], 1, t('info fields contains active 1'));
1104 // Create an instance, verify that it shows up
1106 'field_name' => $field['field_name'],
1107 'entity_type' => 'test_entity',
1108 'bundle' => 'test_bundle',
1109 'label' => $this->randomName(),
1110 'description' => $this->randomName(),
1111 'weight' => mt_rand(0, 127),
1112 // test_field has no instance settings
1114 'type' => 'test_field_widget',
1115 'settings' => array(
1116 'test_setting' => 999)));
1117 field_create_instance($instance);
1119 $instances = field_info_instances('test_entity', $instance['bundle']);
1120 $this->assertEqual(count($instances), 1, t('One instance shows up in info when attached to a bundle.'));
1121 $this->assertTrue($instance < $instances[$instance['field_name']], t('Instance appears in info correctly'));
1125 * Test that cached field definitions are ready for current runtime context.
1127 function testFieldPrepare() {
1128 $field_definition = array(
1129 'field_name' => 'field',
1130 'type' => 'test_field',
1132 field_create_field($field_definition);
1134 // Simulate a stored field definition missing a field setting (e.g. a
1135 // third-party module adding a new field setting has been enabled, and
1136 // existing fields do not know the setting yet).
1137 $data = db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']))->fetchField();
1138 $data = unserialize($data);
1139 $data['settings'] = array();
1140 db_update('field_config')
1141 ->fields(array('data' => serialize($data)))
1142 ->condition('field_name', $field_definition['field_name'])
1145 field_cache_clear();
1147 // Read the field back.
1148 $field = field_info_field($field_definition['field_name']);
1150 // Check that all expected settings are in place.
1151 $field_type = field_info_field_types($field_definition['type']);
1152 $this->assertIdentical($field['settings'], $field_type['settings'], t('All expected default field settings are present.'));
1156 * Test that cached instance definitions are ready for current runtime context.
1158 function testInstancePrepare() {
1159 $field_definition = array(
1160 'field_name' => 'field',
1161 'type' => 'test_field',
1163 field_create_field($field_definition);
1164 $instance_definition = array(
1165 'field_name' => $field_definition['field_name'],
1166 'entity_type' => 'test_entity',
1167 'bundle' => 'test_bundle',
1169 field_create_instance($instance_definition);
1171 // Simulate a stored instance definition missing various settings (e.g. a
1172 // third-party module adding instance, widget or display settings has been
1173 // enabled, but existing instances do not know the new settings).
1174 $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();
1175 $data = unserialize($data);
1176 $data['settings'] = array();
1177 $data['widget']['settings'] = 'unavailable_widget';
1178 $data['widget']['settings'] = array();
1179 $data['display']['default']['type'] = 'unavailable_formatter';
1180 $data['display']['default']['settings'] = array();
1181 db_update('field_config_instance')
1182 ->fields(array('data' => serialize($data)))
1183 ->condition('field_name', $instance_definition['field_name'])
1184 ->condition('bundle', $instance_definition['bundle'])
1187 field_cache_clear();
1189 // Read the instance back.
1190 $instance = field_info_instance($instance_definition['entity_type'], $instance_definition['field_name'], $instance_definition['bundle']);
1192 // Check that all expected instance settings are in place.
1193 $field_type = field_info_field_types($field_definition['type']);
1194 $this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
1196 // Check that the default widget is used and expected settings are in place.
1197 $this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
1198 $widget_type = field_info_widget_types($instance['widget']['type']);
1199 $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
1201 // Check that display settings are set for the 'default' mode.
1202 $display = $instance['display']['default'];
1203 $this->assertIdentical($display['type'], $field_type['default_formatter'], t("Formatter is set for the 'default' view mode"));
1204 $formatter_type = field_info_formatter_types($display['type']);
1205 $this->assertIdentical($display['settings'], $formatter_type['settings'] , t("Formatter settings are set for the 'default' view mode"));
1209 * Test that instances on disabled entity types are filtered out.
1211 function testInstanceDisabledEntityType() {
1212 // For this test the field type and the entity type must be exposed by
1213 // different modules.
1214 $field_definition = array(
1215 'field_name' => 'field',
1216 'type' => 'test_field',
1218 field_create_field($field_definition);
1219 $instance_definition = array(
1220 'field_name' => 'field',
1221 'entity_type' => 'comment',
1222 'bundle' => 'comment_node_article',
1224 field_create_instance($instance_definition);
1226 // Disable coment module. This clears field_info cache.
1227 module_disable(array('comment'));
1228 $this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), t('No instances are returned on disabled entity types.'));
1232 * Test that the field_info settings convenience functions work.
1234 function testSettingsInfo() {
1235 $info = field_test_field_info();
1236 // We need to account for the existence of user_field_info_alter().
1237 foreach (array_keys($info) as
$name) {
1238 $info[$name]['instance_settings']['user_register_form'] = FALSE
;
1240 foreach ($info as
$type => $data) {
1241 $this->assertIdentical(field_info_field_settings($type), $data['settings'], "field_info_field_settings returns {$type}'s field settings");
1242 $this->assertIdentical(field_info_instance_settings($type), $data['instance_settings'], "field_info_field_settings returns {$type}'s field instance settings");
1245 $info = field_test_field_widget_info();
1246 foreach ($info as
$type => $data) {
1247 $this->assertIdentical(field_info_widget_settings($type), $data['settings'], "field_info_widget_settings returns {$type}'s widget settings");
1250 $info = field_test_field_formatter_info();
1251 foreach ($info as
$type => $data) {
1252 $this->assertIdentical(field_info_formatter_settings($type), $data['settings'], "field_info_formatter_settings returns {$type}'s formatter settings");
1257 class FieldFormTestCase
extends FieldTestCase
{
1258 public static
function getInfo() {
1260 'name' => 'Field form tests',
1261 'description' => 'Test Field form handling.',
1262 'group' => 'Field API',
1267 parent
::setUp('field_test');
1269 $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
1270 $this->drupalLogin($web_user);
1272 $this->field_single
= array('field_name' => 'field_single', 'type' => 'test_field');
1273 $this->field_multiple
= array('field_name' => 'field_multiple', 'type' => 'test_field', 'cardinality' => 4);
1274 $this->field_unlimited
= array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED
);
1276 $this->instance
= array(
1277 'entity_type' => 'test_entity',
1278 'bundle' => 'test_bundle',
1279 'label' => $this->randomName() .
'_label',
1280 'description' => $this->randomName() .
'_description',
1281 'weight' => mt_rand(0, 127),
1282 'settings' => array(
1283 'test_instance_setting' => $this->randomName(),
1286 'type' => 'test_field_widget',
1287 'label' => 'Test Field',
1288 'settings' => array(
1289 'test_widget_setting' => $this->randomName(),
1295 function testFieldFormSingle() {
1296 $this->field
= $this->field_single
;
1297 $this->field_name
= $this->field
['field_name'];
1298 $this->instance
['field_name'] = $this->field_name
;
1299 field_create_field($this->field
);
1300 field_create_instance($this->instance
);
1301 $langcode = LANGUAGE_NONE
;
1303 // Display creation form.
1304 $this->drupalGet('test-entity/add/test-bundle');
1305 $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
1306 $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
1307 // TODO : check that the widget is populated with default value ?
1309 // Submit with invalid value (field-level validation).
1310 $edit = array("{$this->field_name}[$langcode][0][value]" => -1);
1311 $this->drupalPost(NULL
, $edit, t('Save'));
1312 $this->assertRaw(t('%name does not accept the value -1.', array('%name' => $this->instance
['label'])), 'Field validation fails with invalid input.');
1313 // TODO : check that the correct field is flagged for error.
1316 $value = mt_rand(1, 127);
1317 $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1318 $this->drupalPost(NULL
, $edit, t('Save'));
1319 preg_match('|test-entity/manage/(\d+)/edit|', $this->url
, $match);
1321 $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
1322 $entity = field_test_entity_test_load($id);
1323 $this->assertEqual($entity->{$this->field_name
}[$langcode][0]['value'], $value, 'Field value was saved');
1325 // Display edit form.
1326 $this->drupalGet('test-entity/manage/' .
$id .
'/edit');
1327 $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", $value, 'Widget is displayed with the correct default value');
1328 $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
1330 // Update the entity.
1331 $value = mt_rand(1, 127);
1332 $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1333 $this->drupalPost(NULL
, $edit, t('Save'));
1334 $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
1335 $entity = field_test_entity_test_load($id);
1336 $this->assertEqual($entity->{$this->field_name
}[$langcode][0]['value'], $value, 'Field value was updated');
1340 $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1341 $this->drupalPost('test-entity/manage/' .
$id .
'/edit', $edit, t('Save'));
1342 $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
1343 $entity = field_test_entity_test_load($id);
1344 $this->assertIdentical($entity->{$this->field_name
}, array(), 'Field was emptied');
1348 function testFieldFormSingleRequired() {
1349 $this->field
= $this->field_single
;
1350 $this->field_name
= $this->field
['field_name'];
1351 $this->instance
['field_name'] = $this->field_name
;
1352 $this->instance
['required'] = TRUE
;
1353 field_create_field($this->field
);
1354 field_create_instance($this->instance
);
1355 $langcode = LANGUAGE_NONE
;
1357 // Submit with missing required value.
1359 $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
1360 $this->assertRaw(t('!name field is required.', array('!name' => $this->instance
['label'])), 'Required field with no value fails validation');
1363 $value = mt_rand(1, 127);
1364 $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1365 $this->drupalPost(NULL
, $edit, t('Save'));
1366 preg_match('|test-entity/manage/(\d+)/edit|', $this->url
, $match);
1368 $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
1369 $entity = field_test_entity_test_load($id);
1370 $this->assertEqual($entity->{$this->field_name
}[$langcode][0]['value'], $value, 'Field value was saved');
1372 // Edit with missing required value.
1374 $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
1375 $this->drupalPost('test-entity/manage/' .
$id .
'/edit', $edit, t('Save'));
1376 $this->assertRaw(t('!name field is required.', array('!name' => $this->instance
['label'])), 'Required field with no value fails validation');
1379 // function testFieldFormMultiple() {
1380 // $this->field = $this->field_multiple;
1381 // $this->field_name = $this->field['field_name'];
1382 // $this->instance['field_name'] = $this->field_name;
1383 // field_create_field($this->field);
1384 // field_create_instance($this->instance);
1387 function testFieldFormUnlimited() {
1388 $this->field
= $this->field_unlimited
;
1389 $this->field_name
= $this->field
['field_name'];
1390 $this->instance
['field_name'] = $this->field_name
;
1391 field_create_field($this->field
);
1392 field_create_instance($this->instance
);
1393 $langcode = LANGUAGE_NONE
;
1395 // Display creation form -> 1 widget.
1396 $this->drupalGet('test-entity/add/test-bundle');
1397 $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
1398 $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
1400 // Press 'add more' button -> 2 widgets.
1401 $this->drupalPost(NULL
, array(), t('Add another item'));
1402 $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget 1 is displayed');
1403 $this->assertFieldByName("{$this->field_name}[$langcode][1][value]", '', 'New widget is displayed');
1404 $this->assertNoField("{$this->field_name}[$langcode][2][value]", 'No extraneous widget is displayed');
1405 // TODO : check that non-field inpurs are preserved ('title')...
1407 // Yet another time so that we can play with more values -> 3 widgets.
1408 $this->drupalPost(NULL
, array(), t('Add another item'));
1410 // Prepare values and weights.
1412 $delta_range = $count - 1;
1413 $values = $weights = $pattern = $expected_values = $edit = array();
1414 for ($delta = 0; $delta <= $delta_range; $delta++) {
1415 // Assign unique random values and weights.
1417 $value = mt_rand(1, 127);
1418 } while (in_array($value, $values));
1420 $weight = mt_rand(-$delta_range, $delta_range);
1421 } while (in_array($weight, $weights));
1422 $edit["$this->field_name[$langcode][$delta][value]"] = $value;
1423 $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
1424 // We'll need three slightly different formats to check the values.
1425 $values[$delta] = $value;
1426 $weights[$delta] = $weight;
1427 $field_values[$weight]['value'] = (string) $value;
1428 $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
1431 // Press 'add more' button -> 4 widgets
1432 $this->drupalPost(NULL
, $edit, t('Add another item'));
1433 for ($delta = 0; $delta <= $delta_range; $delta++) {
1434 $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
1435 $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
1438 $pattern = implode('.*', array_values($pattern));
1439 $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
1440 $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
1441 $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
1442 $this->assertNoField("$this->field_name[$langcode][" .
($delta + 1) .
'][value]', 'No extraneous widget is displayed');
1444 // Submit the form and create the entity.
1445 $this->drupalPost(NULL
, $edit, t('Save'));
1446 preg_match('|test-entity/manage/(\d+)/edit|', $this->url
, $match);
1448 $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
1449 $entity = field_test_entity_test_load($id);
1450 ksort($field_values);
1451 $field_values = array_values($field_values);
1452 $this->assertIdentical($entity->{$this->field_name
}[$langcode], $field_values, 'Field values were saved in the correct order');
1454 // Display edit form: check that the expected number of widgets is
1455 // displayed, with correct values change values, reorder, leave an empty
1456 // value in the middle.
1457 // Submit: check that the entity is updated with correct values
1458 // Re-submit: check that the field can be emptied.
1460 // Test with several multiple fields in a form
1463 function testFieldFormJSAddMore() {
1464 $this->field
= $this->field_unlimited
;
1465 $this->field_name
= $this->field
['field_name'];
1466 $this->instance
['field_name'] = $this->field_name
;
1467 field_create_field($this->field
);
1468 field_create_instance($this->instance
);
1469 $langcode = LANGUAGE_NONE
;
1471 // Display creation form -> 1 widget.
1472 $this->drupalGet('test-entity/add/test-bundle');
1474 // Press 'add more' button a couple times -> 3 widgets.
1475 // drupalPostAJAX() will not work iteratively, so we add those through
1476 // non-JS submission.
1477 $this->drupalPost(NULL
, array(), t('Add another item'));
1478 $this->drupalPost(NULL
, array(), t('Add another item'));
1480 // Prepare values and weights.
1482 $delta_range = $count - 1;
1483 $values = $weights = $pattern = $expected_values = $edit = array();
1484 for ($delta = 0; $delta <= $delta_range; $delta++) {
1485 // Assign unique random values and weights.
1487 $value = mt_rand(1, 127);
1488 } while (in_array($value, $values));
1490 $weight = mt_rand(-$delta_range, $delta_range);
1491 } while (in_array($weight, $weights));
1492 $edit["$this->field_name[$langcode][$delta][value]"] = $value;
1493 $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
1494 // We'll need three slightly different formats to check the values.
1495 $values[$delta] = $value;
1496 $weights[$delta] = $weight;
1497 $field_values[$weight]['value'] = (string) $value;
1498 $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
1500 // Press 'add more' button through Ajax, and place the expected HTML result
1501 // as the tested content.
1502 $commands = $this->drupalPostAJAX(NULL
, $edit, $this->field_name .
'_add_more');
1503 $this->content
= $commands[1]['data'];
1505 for ($delta = 0; $delta <= $delta_range; $delta++) {
1506 $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
1507 $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
1510 $pattern = implode('.*', array_values($pattern));
1511 $this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
1512 $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", '', "New widget is displayed");
1513 $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "New widget has the right weight");
1514 $this->assertNoField("$this->field_name[$langcode][" .
($delta + 1) .
'][value]', 'No extraneous widget is displayed');
1518 * Tests widgets handling multiple values.
1520 function testFieldFormMultipleWidget() {
1521 // Create a field with fixed cardinality and an instance using a multiple
1523 $this->field
= $this->field_multiple
;
1524 $this->field_name
= $this->field
['field_name'];
1525 $this->instance
['field_name'] = $this->field_name
;
1526 $this->instance
['widget']['type'] = 'test_field_widget_multiple';
1527 field_create_field($this->field
);
1528 field_create_instance($this->instance
);
1529 $langcode = LANGUAGE_NONE
;
1531 // Display creation form.
1532 $this->drupalGet('test-entity/add/test-bundle');
1533 $this->assertFieldByName("{$this->field_name}[$langcode]", '', t('Widget is displayed.'));
1535 // Create entity with three values.
1536 $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3');
1537 $this->drupalPost(NULL
, $edit, t('Save'));
1538 preg_match('|test-entity/manage/(\d+)/edit|', $this->url
, $match);
1541 // Check that the values were saved.
1542 $entity_init = field_test_create_stub_entity($id);
1543 $this->assertFieldValues($entity_init, $this->field_name
, $langcode, array(1, 2, 3));
1545 // Display the form, check that the values are correctly filled in.
1546 $this->drupalGet('test-entity/manage/' .
$id .
'/edit');
1547 $this->assertFieldByName("{$this->field_name}[$langcode]", '1, 2, 3', t('Widget is displayed.'));
1549 // Submit the form with more values than the field accepts.
1550 $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3, 4, 5');
1551 $this->drupalPost(NULL
, $edit, t('Save'));
1552 $this->assertRaw('this field cannot hold more than 4 values', t('Form validation failed.'));
1553 // Check that the field values were not submitted.
1554 $this->assertFieldValues($entity_init, $this->field_name
, $langcode, array(1, 2, 3));
1558 * Tests fields with no 'edit' access.
1560 function testFieldFormAccess() {
1561 // Create a "regular" field.
1562 $field = $this->field_single
;
1563 $field_name = $field['field_name'];
1564 $instance = $this->instance
;
1565 $instance['field_name'] = $field_name;
1566 field_create_field($field);
1567 field_create_instance($instance);
1569 // Create a field with no edit access - see field_test_field_access().
1570 $field_no_access = array(
1571 'field_name' => 'field_no_edit_access',
1572 'type' => 'test_field',
1574 $field_name_no_access = $field_no_access['field_name'];
1575 $instance_no_access = array(
1576 'field_name' => $field_name_no_access,
1577 'entity_type' => 'test_entity',
1578 'bundle' => 'test_bundle',
1579 'default_value' => array(0 => array('value' => 99)),
1581 field_create_field($field_no_access);
1582 field_create_instance($instance_no_access);
1584 $langcode = LANGUAGE_NONE
;
1586 // Display creation form.
1587 $this->drupalGet('test-entity/add/test-bundle');
1588 $this->assertNoFieldByName("{$field_name_no_access}[$langcode][0][value]", '', t('Widget is not displayed if field access is denied.'));
1591 $edit = array("{$field_name}[$langcode][0][value]" => 1);
1592 $this->drupalPost(NULL
, $edit, t('Save'));
1593 preg_match('|test-entity/manage/(\d+)/edit|', $this->url
, $match);
1596 // Check that the default value was saved.
1597 $entity = field_test_entity_test_load($id);
1598 $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('Default value was saved for the field with no edit access.'));
1599 $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 1, t('Entered value vas saved for the field with edit access.'));
1601 // Create a new revision.
1602 $edit = array("{$field_name}[$langcode][0][value]" => 2, 'revision' => TRUE
);
1603 $this->drupalPost('test-entity/manage/' .
$id .
'/edit', $edit, t('Save'));
1605 // Check that the new revision has the expected values.
1606 $entity = field_test_entity_test_load($id);
1607 $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.'));
1608 $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
1610 // Check that the revision is also saved in the revisions table.
1611 $entity = field_test_entity_test_load($id, $entity->ftvid
);
1612 $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.'));
1613 $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
1617 * Tests Field API form integration within a subform.
1619 function testNestedFieldForm() {
1620 // Add two instances on the 'test_bundle'
1621 field_create_field($this->field_single
);
1622 field_create_field($this->field_unlimited
);
1623 $this->instance
['field_name'] = 'field_single';
1624 $this->instance
['label'] = 'Single field';
1625 field_create_instance($this->instance
);
1626 $this->instance
['field_name'] = 'field_unlimited';
1627 $this->instance
['label'] = 'Unlimited field';
1628 field_create_instance($this->instance
);
1630 // Create two entities.
1631 $entity_1 = field_test_create_stub_entity(1, 1);
1632 $entity_1->is_new
= TRUE
;
1633 $entity_1->field_single
[LANGUAGE_NONE
][] = array('value' => 0);
1634 $entity_1->field_unlimited
[LANGUAGE_NONE
][] = array('value' => 1);
1635 field_test_entity_save($entity_1);
1637 $entity_2 = field_test_create_stub_entity(2, 2);
1638 $entity_2->is_new
= TRUE
;
1639 $entity_2->field_single
[LANGUAGE_NONE
][] = array('value' => 10);
1640 $entity_2->field_unlimited
[LANGUAGE_NONE
][] = array('value' => 11);
1641 field_test_entity_save($entity_2);
1643 // Display the 'combined form'.
1644 $this->drupalGet('test-entity/nested/1/2');
1645 $this->assertFieldByName('field_single[und][0][value]', 0, t('Entity 1: field_single value appears correctly is the form.'));
1646 $this->assertFieldByName('field_unlimited[und][0][value]', 1, t('Entity 1: field_unlimited value 0 appears correctly is the form.'));
1647 $this->assertFieldByName('entity_2[field_single][und][0][value]', 10, t('Entity 2: field_single value appears correctly is the form.'));
1648 $this->assertFieldByName('entity_2[field_unlimited][und][0][value]', 11, t('Entity 2: field_unlimited value 0 appears correctly is the form.'));
1650 // Submit the form and check that the entities are updated accordingly.
1652 'field_single[und][0][value]' => 1,
1653 'field_unlimited[und][0][value]' => 2,
1654 'field_unlimited[und][1][value]' => 3,
1655 'entity_2[field_single][und][0][value]' => 11,
1656 'entity_2[field_unlimited][und][0][value]' => 12,
1657 'entity_2[field_unlimited][und][1][value]' => 13,
1659 $this->drupalPost(NULL
, $edit, t('Save'));
1660 field_cache_clear();
1661 $entity_1 = field_test_create_stub_entity(1);
1662 $entity_2 = field_test_create_stub_entity(2);
1663 $this->assertFieldValues($entity_1, 'field_single', LANGUAGE_NONE
, array(1));
1664 $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE
, array(2, 3));
1665 $this->assertFieldValues($entity_2, 'field_single', LANGUAGE_NONE
, array(11));
1666 $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE
, array(12, 13));
1668 // Submit invalid values and check that errors are reported on the
1671 'field_unlimited[und][1][value]' => -1,
1673 $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
1674 $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), t('Entity 1: the field validation error was reported.'));
1675 $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-field-unlimited-und-1-value'));
1676 $this->assertTrue($error_field, t('Entity 1: the error was flagged on the correct element.'));
1678 'entity_2[field_unlimited][und][1][value]' => -1,
1680 $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
1681 $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), t('Entity 2: the field validation error was reported.'));
1682 $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-entity-2-field-unlimited-und-1-value'));
1683 $this->assertTrue($error_field, t('Entity 2: the error was flagged on the correct element.'));
1685 // Test that reordering works on both entities.
1687 'field_unlimited[und][0][_weight]' => 0,
1688 'field_unlimited[und][1][_weight]' => -1,
1689 'entity_2[field_unlimited][und][0][_weight]' => 0,
1690 'entity_2[field_unlimited][und][1][_weight]' => -1,
1692 $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
1693 field_cache_clear();
1694 $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE
, array(3, 2));
1695 $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE
, array(13, 12));
1697 // Test the 'add more' buttons. Only Ajax submission is tested, because
1698 // the two 'add more' buttons present in the form have the same #value,
1699 // which confuses drupalPost().
1700 // 'Add more' button in the first entity:
1701 $this->drupalGet('test-entity/nested/1/2');
1702 $this->drupalPostAJAX(NULL
, array(), 'field_unlimited_add_more');
1703 $this->assertFieldByName('field_unlimited[und][0][value]', 3, t('Entity 1: field_unlimited value 0 appears correctly is the form.'));
1704 $this->assertFieldByName('field_unlimited[und][1][value]', 2, t('Entity 1: field_unlimited value 1 appears correctly is the form.'));
1705 $this->assertFieldByName('field_unlimited[und][2][value]', '', t('Entity 1: field_unlimited value 2 appears correctly is the form.'));
1706 $this->assertFieldByName('field_unlimited[und][3][value]', '', t('Entity 1: an empty widget was added for field_unlimited value 3.'));
1707 // 'Add more' button in the first entity (changing field values):
1709 'entity_2[field_unlimited][und][0][value]' => 13,
1710 'entity_2[field_unlimited][und][1][value]' => 14,
1711 'entity_2[field_unlimited][und][2][value]' => 15,
1713 $this->drupalPostAJAX(NULL
, $edit, 'entity_2_field_unlimited_add_more');
1714 $this->assertFieldByName('entity_2[field_unlimited][und][0][value]', 13, t('Entity 2: field_unlimited value 0 appears correctly is the form.'));
1715 $this->assertFieldByName('entity_2[field_unlimited][und][1][value]', 14, t('Entity 2: field_unlimited value 1 appears correctly is the form.'));
1716 $this->assertFieldByName('entity_2[field_unlimited][und][2][value]', 15, t('Entity 2: field_unlimited value 2 appears correctly is the form.'));
1717 $this->assertFieldByName('entity_2[field_unlimited][und][3][value]', '', t('Entity 2: an empty widget was added for field_unlimited value 3.'));
1718 // Save the form and check values are saved correclty.
1719 $this->drupalPost(NULL
, array(), t('Save'));
1720 field_cache_clear();
1721 $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE
, array(3, 2));
1722 $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE
, array(13, 14, 15));
1726 class FieldDisplayAPITestCase
extends FieldTestCase
{
1727 public static
function getInfo() {
1729 'name' => 'Field Display API tests',
1730 'description' => 'Test the display API.',
1731 'group' => 'Field API',
1736 parent
::setUp('field_test');
1738 // Create a field and instance.
1739 $this->field_name
= 'test_field';
1740 $this->label
= $this->randomName();
1741 $this->cardinality
= 4;
1743 $this->field
= array(
1744 'field_name' => $this->field_name
,
1745 'type' => 'test_field',
1746 'cardinality' => $this->cardinality
,
1748 $this->instance
= array(
1749 'field_name' => $this->field_name
,
1750 'entity_type' => 'test_entity',
1751 'bundle' => 'test_bundle',
1752 'label' => $this->label
,
1755 'type' => 'field_test_default',
1756 'settings' => array(
1757 'test_formatter_setting' => $this->randomName(),
1761 'type' => 'field_test_default',
1762 'settings' => array(
1763 'test_formatter_setting' => $this->randomName(),
1768 field_create_field($this->field
);
1769 field_create_instance($this->instance
);
1771 // Create an entity with values.
1772 $this->values
= $this->_generateTestFieldValues($this->cardinality
);
1773 $this->entity
= field_test_create_stub_entity();
1774 $this->is_new
= TRUE
;
1775 $this->entity
->{$this->field_name
}[LANGUAGE_NONE
] = $this->values
;
1776 field_test_entity_save($this->entity
);
1780 * Test the field_view_field() function.
1782 function testFieldViewField() {
1783 // No display settings: check that default display settings are used.
1784 $output = field_view_field('test_entity', $this->entity
, $this->field_name
);
1785 $this->drupalSetContent(drupal_render($output));
1786 $settings = field_info_formatter_settings('field_test_default');
1787 $setting = $settings['test_formatter_setting'];
1788 $this->assertText($this->label
, t('Label was displayed.'));
1789 foreach ($this->values as
$delta => $value) {
1790 $this->assertText($setting .
'|' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1793 // Check that explicit display settings are used.
1795 'label' => 'hidden',
1796 'type' => 'field_test_multiple',
1797 'settings' => array(
1798 'test_formatter_setting_multiple' => $this->randomName(),
1802 $output = field_view_field('test_entity', $this->entity
, $this->field_name
, $display);
1803 $this->drupalSetContent(drupal_render($output));
1804 $setting = $display['settings']['test_formatter_setting_multiple'];
1805 $this->assertNoText($this->label
, t('Label was not displayed.'));
1806 $this->assertText('field_test_field_attach_view_alter', t('Alter fired, display passed.'));
1808 foreach ($this->values as
$delta => $value) {
1809 $array[] = $delta .
':' .
$value['value'];
1811 $this->assertText($setting .
'|' .
implode('|', $array), t('Values were displayed with expected setting.'));
1813 // Check the prepare_view steps are invoked.
1815 'label' => 'hidden',
1816 'type' => 'field_test_with_prepare_view',
1817 'settings' => array(
1818 'test_formatter_setting_additional' => $this->randomName(),
1821 $output = field_view_field('test_entity', $this->entity
, $this->field_name
, $display);
1822 $view = drupal_render($output);
1823 $this->drupalSetContent($view);
1824 $setting = $display['settings']['test_formatter_setting_additional'];
1825 $this->assertNoText($this->label
, t('Label was not displayed.'));
1826 $this->assertNoText('field_test_field_attach_view_alter', t('Alter not fired.'));
1827 foreach ($this->values as
$delta => $value) {
1828 $this->assertText($setting .
'|' .
$value['value'] .
'|' .
($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1831 // View mode: check that display settings specified in the instance are
1833 $output = field_view_field('test_entity', $this->entity
, $this->field_name
, 'teaser');
1834 $this->drupalSetContent(drupal_render($output));
1835 $setting = $this->instance
['display']['teaser']['settings']['test_formatter_setting'];
1836 $this->assertText($this->label
, t('Label was displayed.'));
1837 foreach ($this->values as
$delta => $value) {
1838 $this->assertText($setting .
'|' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1841 // Unknown view mode: check that display settings for 'default' view mode
1843 $output = field_view_field('test_entity', $this->entity
, $this->field_name
, 'unknown_view_mode');
1844 $this->drupalSetContent(drupal_render($output));
1845 $setting = $this->instance
['display']['default']['settings']['test_formatter_setting'];
1846 $this->assertText($this->label
, t('Label was displayed.'));
1847 foreach ($this->values as
$delta => $value) {
1848 $this->assertText($setting .
'|' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1853 * Test the field_view_value() function.
1855 function testFieldViewValue() {
1856 // No display settings: check that default display settings are used.
1857 $settings = field_info_formatter_settings('field_test_default');
1858 $setting = $settings['test_formatter_setting'];
1859 foreach ($this->values as
$delta => $value) {
1860 $item = $this->entity
->{$this->field_name
}[LANGUAGE_NONE
][$delta];
1861 $output = field_view_value('test_entity', $this->entity
, $this->field_name
, $item);
1862 $this->drupalSetContent(drupal_render($output));
1863 $this->assertText($setting .
'|' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1866 // Check that explicit display settings are used.
1868 'type' => 'field_test_multiple',
1869 'settings' => array(
1870 'test_formatter_setting_multiple' => $this->randomName(),
1873 $setting = $display['settings']['test_formatter_setting_multiple'];
1875 foreach ($this->values as
$delta => $value) {
1876 $item = $this->entity
->{$this->field_name
}[LANGUAGE_NONE
][$delta];
1877 $output = field_view_value('test_entity', $this->entity
, $this->field_name
, $item, $display);
1878 $this->drupalSetContent(drupal_render($output));
1879 $this->assertText($setting .
'|0:' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1882 // Check that prepare_view steps are invoked.
1884 'type' => 'field_test_with_prepare_view',
1885 'settings' => array(
1886 'test_formatter_setting_additional' => $this->randomName(),
1889 $setting = $display['settings']['test_formatter_setting_additional'];
1891 foreach ($this->values as
$delta => $value) {
1892 $item = $this->entity
->{$this->field_name
}[LANGUAGE_NONE
][$delta];
1893 $output = field_view_value('test_entity', $this->entity
, $this->field_name
, $item, $display);
1894 $this->drupalSetContent(drupal_render($output));
1895 $this->assertText($setting .
'|' .
$value['value'] .
'|' .
($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1898 // View mode: check that display settings specified in the instance are
1900 $setting = $this->instance
['display']['teaser']['settings']['test_formatter_setting'];
1901 foreach ($this->values as
$delta => $value) {
1902 $item = $this->entity
->{$this->field_name
}[LANGUAGE_NONE
][$delta];
1903 $output = field_view_value('test_entity', $this->entity
, $this->field_name
, $item, 'teaser');
1904 $this->drupalSetContent(drupal_render($output));
1905 $this->assertText($setting .
'|' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1908 // Unknown view mode: check that display settings for 'default' view mode
1910 $setting = $this->instance
['display']['default']['settings']['test_formatter_setting'];
1911 foreach ($this->values as
$delta => $value) {
1912 $item = $this->entity
->{$this->field_name
}[LANGUAGE_NONE
][$delta];
1913 $output = field_view_value('test_entity', $this->entity
, $this->field_name
, $item, 'unknown_view_mode');
1914 $this->drupalSetContent(drupal_render($output));
1915 $this->assertText($setting .
'|' .
$value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
1920 class FieldCrudTestCase
extends FieldTestCase
{
1921 public static
function getInfo() {
1923 'name' => 'Field CRUD tests',
1924 'description' => 'Test field create, read, update, and delete.',
1925 'group' => 'Field API',
1930 // field_update_field() tests use number.module
1931 parent
::setUp('field_test', 'number');
1934 // TODO : test creation with
1935 // - a full fledged $field structure, check that all the values are there
1936 // - a minimal $field structure, check all default values are set
1937 // defer actual $field comparison to a helper function, used for the two cases above
1940 * Test the creation of a field.
1942 function testCreateField() {
1943 $field_definition = array(
1944 'field_name' => 'field_2',
1945 'type' => 'test_field',
1947 field_test_memorize();
1948 $field_definition = field_create_field($field_definition);
1949 $mem = field_test_memorize();
1950 $this->assertIdentical($mem['field_test_field_create_field'][0][0], $field_definition, 'hook_field_create_field() called with correct arguments.');
1952 // Read the raw record from the {field_config_instance} table.
1953 $result = db_query('SELECT * FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']));
1954 $record = $result->fetchAssoc();
1955 $record['data'] = unserialize($record['data']);
1957 // Ensure that basic properties are preserved.
1958 $this->assertEqual($record['field_name'], $field_definition['field_name'], t('The field name is properly saved.'));
1959 $this->assertEqual($record['type'], $field_definition['type'], t('The field type is properly saved.'));
1961 // Ensure that cardinality defaults to 1.
1962 $this->assertEqual($record['cardinality'], 1, t('Cardinality defaults to 1.'));
1964 // Ensure that default settings are present.
1965 $field_type = field_info_field_types($field_definition['type']);
1966 $this->assertIdentical($record['data']['settings'], $field_type['settings'], t('Default field settings have been written.'));
1968 // Ensure that default storage was set.
1969 $this->assertEqual($record['storage_type'], variable_get('field_storage_default'), t('The field type is properly saved.'));
1971 // Guarantee that the name is unique.
1973 field_create_field($field_definition);
1974 $this->fail(t('Cannot create two fields with the same name.'));
1976 catch (FieldException
$e) {
1977 $this->pass(t('Cannot create two fields with the same name.'));
1980 // Check that field type is required.
1982 $field_definition = array(
1983 'field_name' => 'field_1',
1985 field_create_field($field_definition);
1986 $this->fail(t('Cannot create a field with no type.'));
1988 catch (FieldException
$e) {
1989 $this->pass(t('Cannot create a field with no type.'));
1992 // Check that field name is required.
1994 $field_definition = array(
1995 'type' => 'test_field'
1997 field_create_field($field_definition);
1998 $this->fail(t('Cannot create an unnamed field.'));
2000 catch (FieldException
$e) {
2001 $this->pass(t('Cannot create an unnamed field.'));
2004 // Check that field name must start with a letter or _.
2006 $field_definition = array(
2007 'field_name' => '2field_2',
2008 'type' => 'test_field',
2010 field_create_field($field_definition);
2011 $this->fail(t('Cannot create a field with a name starting with a digit.'));
2013 catch (FieldException
$e) {
2014 $this->pass(t('Cannot create a field with a name starting with a digit.'));
2017 // Check that field name must only contain lowercase alphanumeric or _.
2019 $field_definition = array(
2020 'field_name' => 'field#_3',
2021 'type' => 'test_field',
2023 field_create_field($field_definition);
2024 $this->fail(t('Cannot create a field with a name containing an illegal character.'));
2026 catch (FieldException
$e) {
2027 $this->pass(t('Cannot create a field with a name containing an illegal character.'));
2030 // Check that field name cannot be longer than 32 characters long.
2032 $field_definition = array(
2033 'field_name' => '_12345678901234567890123456789012',
2034 'type' => 'test_field',
2036 field_create_field($field_definition);
2037 $this->fail(t('Cannot create a field with a name longer than 32 characters.'));
2039 catch (FieldException
$e) {
2040 $this->pass(t('Cannot create a field with a name longer than 32 characters.'));
2043 // Check that field name can not be an entity key.
2044 // "ftvid" is known as an entity key from the "test_entity" type.
2046 $field_definition = array(
2047 'type' => 'test_field',
2048 'field_name' => 'ftvid',
2050 $field = field_create_field($field_definition);
2051 $this->fail(t('Cannot create a field bearing the name of an entity key.'));
2053 catch (FieldException
$e) {
2054 $this->pass(t('Cannot create a field bearing the name of an entity key.'));
2059 * Test failure to create a field.
2061 function testCreateFieldFail() {
2062 $field_name = 'duplicate';
2063 $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
2064 $query = db_select('field_config')->condition('field_name', $field_name)->countQuery();
2066 // The field does not appear in field_config.
2067 $count = $query->execute()->fetchField();
2068 $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
2070 // Try to create the field.
2072 $field = field_create_field($field_definition);
2073 $this->assertTrue(FALSE
, 'Field creation (correctly) fails.');
2075 catch (Exception
$e) {
2076 $this->assertTrue(TRUE
, 'Field creation (correctly) fails.');
2079 // The field does not appear in field_config.
2080 $count = $query->execute()->fetchField();
2081 $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
2085 * Test reading back a field definition.
2087 function testReadField() {
2088 $field_definition = array(
2089 'field_name' => 'field_1',
2090 'type' => 'test_field',
2092 field_create_field($field_definition);
2094 // Read the field back.
2095 $field = field_read_field($field_definition['field_name']);
2096 $this->assertTrue($field_definition < $field, t('The field was properly read.'));
2100 * Test creation of indexes on data column.
2102 function testFieldIndexes() {
2103 // Check that indexes specified by the field type are used by default.
2104 $field_definition = array(
2105 'field_name' => 'field_1',
2106 'type' => 'test_field',
2108 field_create_field($field_definition);
2109 $field = field_read_field($field_definition['field_name']);
2110 $expected_indexes = array('value' => array('value'));
2111 $this->assertEqual($field['indexes'], $expected_indexes, t('Field type indexes saved by default'));
2113 // Check that indexes specified by the field definition override the field
2115 $field_definition = array(
2116 'field_name' => 'field_2',
2117 'type' => 'test_field',
2122 field_create_field($field_definition);
2123 $field = field_read_field($field_definition['field_name']);
2124 $expected_indexes = array('value' => array());
2125 $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes override field type indexes'));
2127 // Check that indexes specified by the field definition add to the field
2129 $field_definition = array(
2130 'field_name' => 'field_3',
2131 'type' => 'test_field',
2133 'value_2' => array('value'),
2136 field_create_field($field_definition);
2137 $field = field_read_field($field_definition['field_name']);
2138 $expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
2139 $this->assertEqual($field['indexes'], $expected_indexes, t('Field definition indexes are merged with field type indexes'));
2143 * Test the deletion of a field.
2145 function testDeleteField() {
2146 // TODO: Also test deletion of the data stored in the field ?
2148 // Create two fields (so we can test that only one is deleted).
2149 $this->field
= array('field_name' => 'field_1', 'type' => 'test_field');
2150 field_create_field($this->field
);
2151 $this->another_field
= array('field_name' => 'field_2', 'type' => 'test_field');
2152 field_create_field($this->another_field
);
2154 // Create instances for each.
2155 $this->instance_definition
= array(
2156 'field_name' => $this->field
['field_name'],
2157 'entity_type' => 'test_entity',
2158 'bundle' => 'test_bundle',
2160 'type' => 'test_field_widget',
2163 field_create_instance($this->instance_definition
);
2164 $this->another_instance_definition
= $this->instance_definition
;
2165 $this->another_instance_definition
['field_name'] = $this->another_field
['field_name'];
2166 field_create_instance($this->another_instance_definition
);
2168 // Test that the first field is not deleted, and then delete it.
2169 $field = field_read_field($this->field
['field_name'], array('include_deleted' => TRUE
));
2170 $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field is not marked for deletion.'));
2171 field_delete_field($this->field
['field_name']);
2173 // Make sure that the field is marked as deleted when it is specifically
2175 $field = field_read_field($this->field
['field_name'], array('include_deleted' => TRUE
));
2176 $this->assertTrue(!empty($field['deleted']), t('A deleted field is marked for deletion.'));
2178 // Make sure that this field's instance is marked as deleted when it is
2179 // specifically loaded.
2180 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle'], array('include_deleted' => TRUE
));
2181 $this->assertTrue(!empty($instance['deleted']), t('An instance for a deleted field is marked for deletion.'));
2183 // Try to load the field normally and make sure it does not show up.
2184 $field = field_read_field($this->field
['field_name']);
2185 $this->assertTrue(empty($field), t('A deleted field is not loaded by default.'));
2187 // Try to load the instance normally and make sure it does not show up.
2188 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2189 $this->assertTrue(empty($instance), t('An instance for a deleted field is not loaded by default.'));
2191 // Make sure the other field (and its field instance) are not deleted.
2192 $another_field = field_read_field($this->another_field
['field_name']);
2193 $this->assertTrue(!empty($another_field) && empty($another_field['deleted']), t('A non-deleted field is not marked for deletion.'));
2194 $another_instance = field_read_instance('test_entity', $this->another_instance_definition
['field_name'], $this->another_instance_definition
['bundle']);
2195 $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('An instance of a non-deleted field is not marked for deletion.'));
2197 // Try to create a new field the same name as a deleted field and
2198 // write data into it.
2199 field_create_field($this->field
);
2200 field_create_instance($this->instance_definition
);
2201 $field = field_read_field($this->field
['field_name']);
2202 $this->assertTrue(!empty($field) && empty($field['deleted']), t('A new field with a previously used name is created.'));
2203 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2204 $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new instance for a previously used field name is created.'));
2206 // Save an entity with data for the field
2207 $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
2208 $langcode = LANGUAGE_NONE
;
2209 $values[0]['value'] = mt_rand(1, 127);
2210 $entity->{$field['field_name']}[$langcode] = $values;
2211 $entity_type = 'test_entity';
2212 field_attach_insert('test_entity', $entity);
2214 // Verify the field is present on load
2215 $entity = field_test_create_stub_entity(0, 0, $this->instance_definition
['bundle']);
2216 field_attach_load($entity_type, array(0 => $entity));
2217 $this->assertIdentical(count($entity->{$field['field_name']}[$langcode]), count($values), "Data in previously deleted field saves and loads correctly");
2218 foreach ($values as
$delta => $value) {
2219 $this->assertEqual($entity->{$field['field_name']}[$langcode][$delta]['value'], $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
2223 function testUpdateNonExistentField() {
2224 $test_field = array('field_name' => 'does_not_exist', 'type' => 'number_decimal');
2226 field_update_field($test_field);
2227 $this->fail(t('Cannot update a field that does not exist.'));
2229 catch (FieldException
$e) {
2230 $this->pass(t('Cannot update a field that does not exist.'));
2234 function testUpdateFieldType() {
2235 $field = array('field_name' => 'field_type', 'type' => 'number_decimal');
2236 $field = field_create_field($field);
2238 $test_field = array('field_name' => 'field_type', 'type' => 'number_integer');
2240 field_update_field($test_field);
2241 $this->fail(t('Cannot update a field to a different type.'));
2243 catch (FieldException
$e) {
2244 $this->pass(t('Cannot update a field to a different type.'));
2249 * Test updating a field.
2251 function testUpdateField() {
2252 // Create a field with a defined cardinality, so that we can ensure it's
2253 // respected. Since cardinality enforcement is consistent across database
2254 // systems, it makes a good test case.
2256 $field_definition = array(
2257 'field_name' => 'field_update',
2258 'type' => 'test_field',
2259 'cardinality' => $cardinality,
2261 $field_definition = field_create_field($field_definition);
2263 'field_name' => 'field_update',
2264 'entity_type' => 'test_entity',
2265 'bundle' => 'test_bundle',
2267 $instance = field_create_instance($instance);
2270 // We need a unique ID for our entity. $cardinality will do.
2272 $entity = field_test_create_stub_entity($id, $id, $instance['bundle']);
2273 // Fill in the entity with more values than $cardinality.
2274 for ($i = 0; $i < 20; $i++) {
2275 $entity->field_update
[LANGUAGE_NONE
][$i]['value'] = $i;
2278 field_attach_insert('test_entity', $entity);
2279 // Load back and assert there are $cardinality number of values.
2280 $entity = field_test_create_stub_entity($id, $id, $instance['bundle']);
2281 field_attach_load('test_entity', array($id => $entity));
2282 $this->assertEqual(count($entity->field_update
[LANGUAGE_NONE
]), $field_definition['cardinality'], 'Cardinality is kept');
2283 // Now check the values themselves.
2284 for ($delta = 0; $delta < $cardinality; $delta++) {
2285 $this->assertEqual($entity->field_update
[LANGUAGE_NONE
][$delta]['value'], $delta, 'Value is kept');
2287 // Increase $cardinality and set the field cardinality to the new value.
2288 $field_definition['cardinality'] = ++$cardinality;
2289 field_update_field($field_definition);
2290 } while ($cardinality < 6);
2294 * Test field type modules forbidding an update.
2296 function testUpdateFieldForbid() {
2297 $field = array('field_name' => 'forbidden', 'type' => 'test_field', 'settings' => array('changeable' => 0, 'unchangeable' => 0));
2298 $field = field_create_field($field);
2299 $field['settings']['changeable']++;
2301 field_update_field($field);
2302 $this->pass(t("A changeable setting can be updated."));
2304 catch (FieldException
$e) {
2305 $this->fail(t("An unchangeable setting cannot be updated."));
2307 $field['settings']['unchangeable']++;
2309 field_update_field($field);
2310 $this->fail(t("An unchangeable setting can be updated."));
2312 catch (FieldException
$e) {
2313 $this->pass(t("An unchangeable setting cannot be updated."));
2318 * Test that fields are properly marked active or inactive.
2320 function testActive() {
2321 $field_definition = array(
2322 'field_name' => 'field_1',
2323 'type' => 'test_field',
2324 // For this test, we need a storage backend provided by a different
2325 // module than field_test.module.
2327 'type' => 'field_sql_storage',
2330 field_create_field($field_definition);
2332 // Test disabling and enabling:
2333 // - the field type module,
2334 // - the storage module,
2336 $this->_testActiveHelper($field_definition, array('field_test'));
2337 $this->_testActiveHelper($field_definition, array('field_sql_storage'));
2338 $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
2342 * Helper function for testActive().
2344 * Test dependency between a field and a set of modules.
2346 * @param $field_definition
2347 * A field definition.
2349 * An aray of module names. The field will be tested to be inactive as long
2350 * as any of those modules is disabled.
2352 function _testActiveHelper($field_definition, $modules) {
2353 $field_name = $field_definition['field_name'];
2356 $field = field_read_field($field_name);
2357 $this->assertTrue($field_definition <= $field, t('The field was properly read.'));
2359 module_disable($modules, FALSE
);
2360 drupal_flush_all_caches();
2362 $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE
));
2363 $this->assertTrue(isset($fields[$field_name]) && $field_definition < $field, t('The field is properly read when explicitly fetching inactive fields.'));
2365 // Re-enable modules one by one, and check that the field is still inactive
2366 // while some modules remain disabled.
2368 $field = field_read_field($field_name);
2369 $this->assertTrue(empty($field), t('%modules disabled. The field is marked inactive.', array('%modules' => implode(', ', $modules))));
2371 $module = array_shift($modules);
2372 module_enable(array($module), FALSE
);
2373 drupal_flush_all_caches();
2376 // Check that the field is active again after all modules have been
2378 $field = field_read_field($field_name);
2379 $this->assertTrue($field_definition <= $field, t('The field was was marked active.'));
2383 class FieldInstanceCrudTestCase
extends FieldTestCase
{
2386 public static
function getInfo() {
2388 'name' => 'Field instance CRUD tests',
2389 'description' => 'Create field entities by attaching fields to entities.',
2390 'group' => 'Field API',
2395 parent
::setUp('field_test');
2397 $this->field
= array(
2398 'field_name' => drupal_strtolower($this->randomName()),
2399 'type' => 'test_field',
2401 field_create_field($this->field
);
2402 $this->instance_definition
= array(
2403 'field_name' => $this->field
['field_name'],
2404 'entity_type' => 'test_entity',
2405 'bundle' => 'test_bundle',
2409 // TODO : test creation with
2410 // - a full fledged $instance structure, check that all the values are there
2411 // - a minimal $instance structure, check all default values are set
2412 // defer actual $instance comparison to a helper function, used for the two cases above,
2413 // and for testUpdateFieldInstance
2416 * Test the creation of a field instance.
2418 function testCreateFieldInstance() {
2419 field_create_instance($this->instance_definition
);
2421 // Read the raw record from the {field_config_instance} table.
2422 $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']));
2423 $record = $result->fetchAssoc();
2424 $record['data'] = unserialize($record['data']);
2426 $field_type = field_info_field_types($this->field
['type']);
2427 $widget_type = field_info_widget_types($field_type['default_widget']);
2428 $formatter_type = field_info_formatter_types($field_type['default_formatter']);
2430 // Check that default values are set.
2431 $this->assertIdentical($record['data']['required'], FALSE
, t('Required defaults to false.'));
2432 $this->assertIdentical($record['data']['label'], $this->instance_definition
['field_name'], t('Label defaults to field name.'));
2433 $this->assertIdentical($record['data']['description'], '', t('Description defaults to empty string.'));
2434 $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], t('Default widget has been written.'));
2435 $this->assertTrue(isset($record['data']['display']['default']), t('Display for "full" view_mode has been written.'));
2436 $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], t('Default formatter for "full" view_mode has been written.'));
2438 // Check that default settings are set.
2439 $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , t('Default instance settings have been written.'));
2440 $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , t('Default widget settings have been written.'));
2441 $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" view_mode have been written.'));
2443 // Guarantee that the field/bundle combination is unique.
2445 field_create_instance($this->instance_definition
);
2446 $this->fail(t('Cannot create two instances with the same field / bundle combination.'));
2448 catch (FieldException
$e) {
2449 $this->pass(t('Cannot create two instances with the same field / bundle combination.'));
2452 // Check that the specified field exists.
2454 $this->instance_definition
['field_name'] = $this->randomName();
2455 field_create_instance($this->instance_definition
);
2456 $this->fail(t('Cannot create an instance of a non-existing field.'));
2458 catch (FieldException
$e) {
2459 $this->pass(t('Cannot create an instance of a non-existing field.'));
2462 // Create a field restricted to a specific entity type.
2463 $field_restricted = array(
2464 'field_name' => drupal_strtolower($this->randomName()),
2465 'type' => 'test_field',
2466 'entity_types' => array('test_cacheable_entity'),
2468 field_create_field($field_restricted);
2470 // Check that an instance can be added to an entity type allowed
2473 $instance = $this->instance_definition
;
2474 $instance['field_name'] = $field_restricted['field_name'];
2475 $instance['entity_type'] = 'test_cacheable_entity';
2476 field_create_instance($instance);
2477 $this->pass(t('Can create an instance on an entity type allowed by the field.'));
2479 catch (FieldException
$e) {
2480 $this->fail(t('Can create an instance on an entity type allowed by the field.'));
2483 // Check that an instance cannot be added to an entity type
2484 // forbidden by the field.
2486 $instance = $this->instance_definition
;
2487 $instance['field_name'] = $field_restricted['field_name'];
2488 field_create_instance($instance);
2489 $this->fail(t('Cannot create an instance on an entity type forbidden by the field.'));
2491 catch (FieldException
$e) {
2492 $this->pass(t('Cannot create an instance on an entity type forbidden by the field.'));
2495 // TODO: test other failures.
2499 * Test reading back an instance definition.
2501 function testReadFieldInstance() {
2502 field_create_instance($this->instance_definition
);
2504 // Read the instance back.
2505 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2506 $this->assertTrue($this->instance_definition
< $instance, t('The field was properly read.'));
2510 * Test the update of a field instance.
2512 function testUpdateFieldInstance() {
2513 field_create_instance($this->instance_definition
);
2514 $field_type = field_info_field_types($this->field
['type']);
2516 // Check that basic changes are saved.
2517 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2518 $instance['required'] = !$instance['required'];
2519 $instance['label'] = $this->randomName();
2520 $instance['description'] = $this->randomName();
2521 $instance['settings']['test_instance_setting'] = $this->randomName();
2522 $instance['widget']['settings']['test_widget_setting'] =$this->randomName();
2523 $instance['widget']['weight']++;
2524 $instance['display']['default']['settings']['test_formatter_setting'] = $this->randomName();
2525 $instance['display']['default']['weight']++;
2526 field_update_instance($instance);
2528 $instance_new = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2529 $this->assertEqual($instance['required'], $instance_new['required'], t('"required" change is saved'));
2530 $this->assertEqual($instance['label'], $instance_new['label'], t('"label" change is saved'));
2531 $this->assertEqual($instance['description'], $instance_new['description'], t('"description" change is saved'));
2532 $this->assertEqual($instance['widget']['settings']['test_widget_setting'], $instance_new['widget']['settings']['test_widget_setting'], t('Widget setting change is saved'));
2533 $this->assertEqual($instance['widget']['weight'], $instance_new['widget']['weight'], t('Widget weight change is saved'));
2534 $this->assertEqual($instance['display']['default']['settings']['test_formatter_setting'], $instance_new['display']['default']['settings']['test_formatter_setting'], t('Formatter setting change is saved'));
2535 $this->assertEqual($instance['display']['default']['weight'], $instance_new['display']['default']['weight'], t('Widget weight change is saved'));
2537 // Check that changing widget and formatter types updates the default settings.
2538 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2539 $instance['widget']['type'] = 'test_field_widget_multiple';
2540 $instance['display']['default']['type'] = 'field_test_multiple';
2541 field_update_instance($instance);
2543 $instance_new = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2544 $this->assertEqual($instance['widget']['type'], $instance_new['widget']['type'] , t('Widget type change is saved.'));
2545 $settings = field_info_widget_settings($instance_new['widget']['type']);
2546 $this->assertIdentical($settings, array_intersect_key($instance_new['widget']['settings'], $settings) , t('Widget type change updates default settings.'));
2547 $this->assertEqual($instance['display']['default']['type'], $instance_new['display']['default']['type'] , t('Formatter type change is saved.'));
2548 $info = field_info_formatter_types($instance_new['display']['default']['type']);
2549 $settings = $info['settings'];
2550 $this->assertIdentical($settings, array_intersect_key($instance_new['display']['default']['settings'], $settings) , t('Changing formatter type updates default settings.'));
2552 // Check that adding a new view mode is saved and gets default settings.
2553 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2554 $instance['display']['teaser'] = array();
2555 field_update_instance($instance);
2557 $instance_new = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2558 $this->assertTrue(isset($instance_new['display']['teaser']), t('Display for the new view_mode has been written.'));
2559 $this->assertIdentical($instance_new['display']['teaser']['type'], $field_type['default_formatter'], t('Default formatter for the new view_mode has been written.'));
2560 $info = field_info_formatter_types($instance_new['display']['teaser']['type']);
2561 $settings = $info['settings'];
2562 $this->assertIdentical($settings, $instance_new['display']['teaser']['settings'] , t('Default formatter settings for the new view_mode have been written.'));
2564 // TODO: test failures.
2568 * Test the deletion of a field instance.
2570 function testDeleteFieldInstance() {
2571 // TODO: Test deletion of the data stored in the field also.
2572 // Need to check that data for a 'deleted' field / instance doesn't get loaded
2573 // Need to check data marked deleted is cleaned on cron (not implemented yet...)
2575 // Create two instances for the same field so we can test that only one
2577 field_create_instance($this->instance_definition
);
2578 $this->another_instance_definition
= $this->instance_definition
;
2579 $this->another_instance_definition
['bundle'] .
= '_another_bundle';
2580 $instance = field_create_instance($this->another_instance_definition
);
2582 // Test that the first instance is not deleted, and then delete it.
2583 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle'], array('include_deleted' => TRUE
));
2584 $this->assertTrue(!empty($instance) && empty($instance['deleted']), t('A new field instance is not marked for deletion.'));
2585 field_delete_instance($instance);
2587 // Make sure the instance is marked as deleted when the instance is
2588 // specifically loaded.
2589 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle'], array('include_deleted' => TRUE
));
2590 $this->assertTrue(!empty($instance['deleted']), t('A deleted field instance is marked for deletion.'));
2592 // Try to load the instance normally and make sure it does not show up.
2593 $instance = field_read_instance('test_entity', $this->instance_definition
['field_name'], $this->instance_definition
['bundle']);
2594 $this->assertTrue(empty($instance), t('A deleted field instance is not loaded by default.'));
2596 // Make sure the other field instance is not deleted.
2597 $another_instance = field_read_instance('test_entity', $this->another_instance_definition
['field_name'], $this->another_instance_definition
['bundle']);
2598 $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), t('A non-deleted field instance is not marked for deletion.'));
2600 // Make sure the field is deleted when its last instance is deleted.
2601 field_delete_instance($another_instance);
2602 $field = field_read_field($another_instance['field_name'], array('include_deleted' => TRUE
));
2603 $this->assertTrue(!empty($field['deleted']), t('A deleted field is marked for deletion after all its instances have been marked for deletion.'));
2608 * Unit test class for the multilanguage fields logic.
2610 * The following tests will check the multilanguage logic of _field_invoke() and
2611 * that only the correct values are returned by field_available_languages().
2613 class FieldTranslationsTestCase
extends FieldTestCase
{
2614 public static
function getInfo() {
2616 'name' => 'Field translations tests',
2617 'description' => 'Test multilanguage fields logic.',
2618 'group' => 'Field API',
2623 parent
::setUp('locale', 'field_test');
2625 $this->field_name
= drupal_strtolower($this->randomName() .
'_field_name');
2627 $this->entity_type
= 'test_entity';
2630 'field_name' => $this->field_name
,
2631 'type' => 'test_field',
2633 'translatable' => TRUE
,
2635 field_create_field($field);
2636 $this->field
= field_read_field($this->field_name
);
2639 'field_name' => $this->field_name
,
2640 'entity_type' => $this->entity_type
,
2641 'bundle' => 'test_bundle',
2643 field_create_instance($instance);
2644 $this->instance
= field_read_instance('test_entity', $this->field_name
, 'test_bundle');
2646 require_once DRUPAL_ROOT .
'/includes/locale.inc';
2647 for ($i = 0; $i < 3; ++$i) {
2648 locale_add_language('l' .
$i, $this->randomString(), $this->randomString());
2653 * Ensures that only valid values are returned by field_available_languages().
2655 function testFieldAvailableLanguages() {
2656 // Test 'translatable' fieldable info.
2657 field_test_entity_info_translatable('test_entity', FALSE
);
2658 $field = $this->field
;
2659 $field['field_name'] .
= '_untranslatable';
2661 // Enable field translations for the entity.
2662 field_test_entity_info_translatable('test_entity', TRUE
);
2664 // Test hook_field_languages() invocation on a translatable field.
2665 variable_set('field_test_field_available_languages_alter', TRUE
);
2666 $enabled_languages = field_content_languages();
2667 $available_languages = field_available_languages($this->entity_type
, $this->field
);
2668 foreach ($available_languages as
$delta => $langcode) {
2669 if ($langcode != 'xx' && $langcode != 'en') {
2670 $this->assertTrue(in_array($langcode, $enabled_languages), t('%language is an enabled language.', array('%language' => $langcode)));
2673 $this->assertTrue(in_array('xx', $available_languages), t('%language was made available.', array('%language' => 'xx')));
2674 $this->assertFalse(in_array('en', $available_languages), t('%language was made unavailable.', array('%language' => 'en')));
2676 // Test field_available_languages() behavior for untranslatable fields.
2677 $this->field
['translatable'] = FALSE
;
2678 field_update_field($this->field
);
2679 $available_languages = field_available_languages($this->entity_type
, $this->field
);
2680 $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === LANGUAGE_NONE
, t('For untranslatable fields only LANGUAGE_NONE is available.'));
2684 * Test the multilanguage logic of _field_invoke().
2686 function testFieldInvoke() {
2687 // Enable field translations for the entity.
2688 field_test_entity_info_translatable('test_entity', TRUE
);
2690 $entity_type = 'test_entity';
2691 $entity = field_test_create_stub_entity(0, 0, $this->instance
['bundle']);
2693 // Populate some extra languages to check if _field_invoke() correctly uses
2694 // the result of field_available_languages().
2696 $extra_languages = mt_rand(1, 4);
2697 $languages = $available_languages = field_available_languages($this->entity_type
, $this->field
);
2698 for ($i = 0; $i < $extra_languages; ++$i) {
2699 $languages[] = $this->randomName(2);
2702 // For each given language provide some random values.
2703 foreach ($languages as
$langcode) {
2704 for ($delta = 0; $delta < $this->field
['cardinality']; $delta++) {
2705 $values[$langcode][$delta]['value'] = mt_rand(1, 127);
2708 $entity->{$this->field_name
} = $values;
2710 $results = _field_invoke('test_op', $entity_type, $entity);
2711 foreach ($results as
$langcode => $result) {
2712 $hash = hash('sha256', serialize(array($entity_type, $entity, $this->field_name
, $langcode, $values[$langcode])));
2713 // Check whether the parameters passed to _field_invoke() were correctly