? crud_simpletest.patch ? tests.patch Index: content.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/content.module,v retrieving revision 1.245 diff -u -p -r1.245 content.module --- content.module 27 Jan 2008 18:52:05 -0000 1.245 +++ content.module 30 Jan 2008 22:31:02 -0000 @@ -1861,3 +1861,13 @@ function content_extra_field_weight($typ return $type['extra'][$pseudo_field_name]['weight']; } } + +/** + * Implementation of hook_simpletest(). + */ +function content_simpletest() { + $module_name = 'content'; + $dir = drupal_get_path('module', $module_name). '/tests'; + $tests = file_scan_directory($dir, '\.test$'); + return array_keys($tests); +} Index: tests/content_crud.test =================================================================== RCS file: tests/content_crud.test diff -N tests/content_crud.test --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/content_crud.test 30 Jan 2008 22:31:02 -0000 @@ -0,0 +1,372 @@ + t('CCK CRUD API'), + 'desc' => t('Tests the CRUD (create, read, update, delete) API for content types.'), + 'group' => t('CCK'), + ); + } + + var $enabled_schema = FALSE; + var $previous_node_changes; + var $previous_node_changes_nid; + var $content_types = array(); + var $nodes = array(); + var $last_field; + + /** + * Checks that the given database table does NOT exist + * @param $table Name of the table to check + * @param $message The translated message to describe the assertion + */ + function assertTableNotExists($table) { + $this->assertFalse(db_table_exists($table), t('Table !table should not exist', array('!table' => $table))); + } + + function assertSchemaMatches($table, $columns) { + $schema = drupal_get_schema($table, TRUE); + $mismatches = array(); + if ($schema === FALSE) { + $mismatches[] = t('table does not exist'); + } + else { + $fields = $schema['fields']; + $fields_count = count($columns); + foreach ($columns as $field) { + if (!isset($fields[$field])) { + $mismatches[] = t('field !field is missing', array('!field' => $field)); + } + else { + --$fields_count; + } + } + if ($fields_count != 0) { + $mismatches[] = t('table contains unexpected fields'); + } + } + $this->assertEqual(count($mismatches), 0, t('Table !table matches schema: !details', + array('!table' => $table, '!details' => implode($mismatches, ', ')))); + + if (!$this->enabled_schema) { + $this->enabled_schema = $this->drupalModuleEnable('schema'); + } + if ($this->enabled_schema) { + // Clunky workaround for http://drupal.org/node/215198 + $table = db_prefix_tables('{'. $table .'}'); + $inspect = schema_invoke('inspect', $table); + $inspect = isset($inspect[$table]) ? $inspect[$table] : NULL; + $compare = schema_compare_table($schema, $inspect); + if ($compare['status'] == 'missing') { + $compare['reasons'] = array(t('table does not exist')); + } + } + else { + $compare = array('status' => 'unknown', 'reasons' => array(t('cannot enable schema module'))); + } + $this->assertEqual($compare['status'], 'same', t('Table schema for !table matches database: !details', + array('!table' => $table, '!details' => implode($compare['reasons'], ', ')))); + } + + function assertSchemaMatchesField($field, $extra_columns) { + if (!isset($field)) { + $field = $this->last_field; + } + $columns = array('nid', 'vid'); + if ($field['multiple'] == 1) { + $columns[] = 'delta'; + } + foreach ($extra_columns as $extra) { + $columns[] = $field['field_name'] .'_'. $extra; + } + $this->assertSchemaMatches($this->getFieldTableName($field), $columns); + } + + function assertSchemaMatchesContentType($content_type, $field_columns = array()) { + if (isset($this->content_types[$content_type])) { + $content_type = $this->content_types[$content_type]; + } + $columns = array('nid', 'vid'); + foreach ($field_columns as $field => $f_columns) { + foreach ($f_columns as $column) { + $columns[] = $field .'_'. $column; + } + } + $this->assertSchemaMatches($this->getContentTypeTableName($content_type), $columns); + } + + /** + * Helper function for assertNodeSaveLoadUnchanged. Recursively checks that + * all the keys of a table are present in a second and have the same value + */ + function _compareArrayForChanges($fields, $data, $message, $prefix = '') { + foreach ($fields as $key => $value) { + $newprefix = ($prefix == '') ? $key : $prefix .']['. $key; + if (is_array($value)) { + $compare_to = isset($data[$key]) ? $data[$key] : array(); + $this->_compareArrayForChanges($value, $compare_to, $message, $newprefix); + } + else { + $this->assertEqual($value, $data[$key], t($message, array('!key' => $newprefix))); + } + } + } + + /** + * Checks that changes can be made to a node through the web interface, and that + * these changes are saved to the database and can be retrieved by node_load. + */ + function assertNodeSaveLoadUnchanged($node, $fields) { + if (isset($this->nodes[$node])) { + $node = $this->nodes[$node]; + } + $node = $fields + (array)$node; + $node = (object)$node; + node_save($node); + $this->previous_node_changes_nid = $node->nid; + $this->previous_node_changes = $fields; + $this->assertNodeStillUnchanged($node->nid); + return $fields; + } + + function assertNodeStillUnchanged($nid = NULL, $changes = NULL) { + if (!isset($nid)) { + $nid = $this->previous_node_changes_nid; + } + if (!isset($changes)) { + $changes = $this->previous_node_changes; + } + $node = node_load($nid, NULL, TRUE); + $this->_compareArrayForChanges($changes, (array)$node, 'Node data [!key] is correct'); + } + + function assertNodeMissingFields($nid, $fields) { + $node = (array)node_load($nid, NULL, TRUE); + foreach ($fields as $field) { + $this->assertFalse(isset($node[$field]), t('Node should be lacking field !key', array('!key' => $field))); + } + } + + function loginWithPermissions($permissions = NULL) { + if (!isset($permissions)) { + $permissions = array( + 'access content', + 'administer content types', + 'administer nodes', + 'administer filters', + ); + } + $user = $this->drupalCreateUserRolePerm($permissions); + $this->drupalLoginUser($user); + } + + function acquireContentTypes($count) { + $this->content_types = array(); + for($i = 0; $i < $count; $i++) { + $this->content_types[$i] = $this->drupalCreateContentType(); + } + content_clear_type_cache(); + } + + function acquireNodes($count = NULL) { + $this->nodes = array(); + $content_types_count = count($this->content_types); + if (!isset($count)) { + $count = $content_types_count; + } + for($i = 0; $i < $count; $i++) { + $type = $this->content_types[$i % $content_types_count]; + $this->nodes[$i] = $this->drupalCreateNode(array('type' => $type->type)); + } + } + + function createField($settings, $typeindex = 0) { + $defaults = array( + 'field_name' => strtolower($this->randomName(4, 'field_')), + 'type_name' => $this->content_types[$typeindex]->type, + ); + $settings = $settings + $defaults; + $this->last_field = content_field_instance_create($settings); + return $this->last_field; + } + + function createFieldText($settings = array(), $typeindex = 0) { + $defaults = array( + 'type' => 'text', + 'widget_type' => 'text_textfield', + 'text_processing' => 1, + ); + $settings = $settings + $defaults; + return $this->createField($settings, $typeindex); + } + + function updateField($settings, $field = NULL) { + if (!isset($field)) { + $field = $this->last_field; + } + $defaults = array( + 'field_name' => $field['field_name'], + 'type_name' => $field['type_name'] , + ); + $settings = $settings + $defaults; + $this->last_field = content_field_instance_update($settings); + return $this->last_field; + } + + function shareField($new_content_type, $field = NULL) { + if (!isset($field)) { + $field = $this->last_field; + } + if (isset($this->content_types[$new_content_type])) { + $new_content_type = $this->content_types[$new_content_type]; + } + $field['type_name'] = $new_content_type->type; + $this->last_field = content_field_instance_create($field); + return $this->last_field; + } + + function deleteField($content_type, $field = NULL) { + if (!isset($field)) { + $field = $this->last_field; + } + if (isset($this->content_types[$content_type])) { + $content_type = $this->content_types[$content_type]; + } + content_field_instance_delete($field['field_name'], $content_type->type); + } + + function createRandomTextFieldData() { + return array( + 'value' => '!SimpleTest! test value' . $this->randomName(60), + 'format' => 2, + ); + } + + function getContentTypeTableName($content_type) { + if (is_numeric($content_type) && isset($this->content_types[$content_type])) { + $content_type = $this->content_types[$content_type]; + } + return 'content_type_'. $content_type->type; + } + + function getFieldTableName($field = NULL) { + if (!isset($field)) { + $field = $this->last_field; + } + return 'content_'. $field['field_name']; + } + + function testSingleToMultiple() { + // Acquire the context + $this->loginWithPermissions(); + $this->acquireContentTypes(3); + $this->acquireNodes(); + + // Create a simple text field + $field = $this->createFieldText(); + $field_name = $field['field_name']; + $this->assertSchemaMatchesContentType(0, array($field_name => array('value', 'format'))); + $this->assertNodeSaveLoadUnchanged(0, array( + $field_name => array( + 0 => $this->createRandomTextFieldData(), + ) + )); + + // Change the text field to allow multiple values + $this->updateField(array('multiple' => 1)); + $this->assertSchemaMatchesContentType(0); + $this->assertSchemaMatchesField(NULL, array('value', 'format')); + $this->assertNodeStillUnchanged(); + + // Share the text field with other content types + for ($share_with = 1; $share_with <= 2; $share_with++) { + $this->shareField($share_with); + $this->assertSchemaMatchesContentType($share_with); + $this->assertNodeSaveLoadUnchanged($share_with, array( + $field_name => array( + 0 => $this->createRandomTextFieldData(), + ) + )); + } + + // Delete the text field from all content types + for ($delete_from = 2; $delete_from >= 0; $delete_from--) { + $this->deleteField($delete_from); + $this->assertTableNotExists($this->getContentTypeTableName($delete_from)); + $this->assertNodeMissingFields($this->nodes[$delete_from], array($field_name)); + } + } + + function testMultipleToSingle() { + // Acquire the context + $this->loginWithPermissions(); + $this->acquireContentTypes(3); + $this->acquireNodes(); + + // Create a multivalue text field + $field = $this->createFieldText(array('multiple' => 1)); + $field_name = $field['field_name']; + $this->assertSchemaMatchesContentType(0); + $this->assertSchemaMatchesField(NULL, array('value', 'format')); + $this->assertNodeSaveLoadUnchanged(0, array( + $field_name => array( + 0 => $this->createRandomTextFieldData(), + 1 => $this->createRandomTextFieldData(), + 2 => $this->createRandomTextFieldData(), + ) + )); + + // Change to a simple text field + $this->updateField(array('multiple' => 0)); + $this->assertSchemaMatchesContentType(0, array($field_name => array('value', 'format'))); + $this->assertTableNotExists($this->getFieldTableName()); + $node0values = $this->assertNodeSaveLoadUnchanged(0, array( + $field_name => array( + 0 => $this->createRandomTextFieldData(), + ) + )); + + // Share the text field with other content type + $this->shareField(1); + $this->assertSchemaMatchesContentType(1); + $this->assertSchemaMatchesContentType(0); + $this->assertSchemaMatchesField(NULL, array('value', 'format')); + $node1values = $this->assertNodeSaveLoadUnchanged(1, array( + $field_name => array( + 0 => $this->createRandomTextFieldData(), + ) + )); + $this->assertNodeStillUnchanged($this->nodes[0]->nid, $node0values); + + // Share the text field with a 3rd type + $this->shareField(2); + $this->assertSchemaMatchesContentType(2); + $this->assertNodeSaveLoadUnchanged(1, array( + $field_name => array( + 0 => $this->createRandomTextFieldData(), + ) + )); + $this->assertNodeStillUnchanged($this->nodes[0]->nid, $node0values); + $this->assertNodeStillUnchanged($this->nodes[1]->nid, $node1values); + + // Remove text field from 3rd type + $this->deleteField(2); + $this->assertTableNotExists($this->getContentTypeTableName(2)); + $this->assertNodeMissingFields($this->nodes[2], array($field_name)); + + // Remove text field from other type + $this->deleteField(1); + $this->assertTableNotExists($this->getContentTypeTableName(1)); + $this->assertTableNotExists($this->getFieldTableName()); + $this->assertSchemaMatchesContentType(0, array($field_name => array('value', 'format'))); + $this->assertNodeMissingFields($this->nodes[1], array($field_name)); + $this->assertNodeStillUnchanged($this->nodes[0]->nid, $node0values); + + // Remove text field from original type + $this->deleteField(0); + $this->assertTableNotExists($this->getContentTypeTableName(0)); + $this->assertNodeMissingFields($this->nodes[0], array($field_name)); + } +}