? 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 @@
+<?php
+// $Id$
+
+class ContentCrudTest extends DrupalTestCase {
+  function get_info() {
+    return array(
+      'name' => 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));
+  }
+}
