Drupal PHP Cross Reference Content Management Systems

Source: /modules/simpletest/tests/schema.test - 384 lines - 13769 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * @file
   5   * Tests for the Database Schema API.
   6   */
   7  
   8  /**
   9   * Unit tests for the Schema API.
  10   */
  11  class SchemaTestCase extends DrupalWebTestCase {
  12    /**
  13     * A global counter for table and field creation.
  14     */
  15    var $counter;
  16  
  17    public static function getInfo() {
  18      return array(
  19        'name' => 'Schema API',
  20        'description' => 'Tests table creation and modification via the schema API.',
  21        'group' => 'Database',
  22      );
  23    }
  24  
  25    /**
  26     *
  27     */
  28    function testSchema() {
  29      // Try creating a table.
  30      $table_specification = array(
  31        'description' => 'Schema table description.',
  32        'fields' => array(
  33          'id'  => array(
  34            'type' => 'int',
  35            'default' => NULL,
  36          ),
  37          'test_field'  => array(
  38            'type' => 'int',
  39            'not null' => TRUE,
  40            'description' => 'Schema column description.',
  41          ),
  42        ),
  43      );
  44      db_create_table('test_table', $table_specification);
  45  
  46      // Assert that the table exists.
  47      $this->assertTrue(db_table_exists('test_table'), t('The table exists.'));
  48  
  49      // Assert that the table comment has been set.
  50      $this->checkSchemaComment($table_specification['description'], 'test_table');
  51  
  52      // Assert that the column comment has been set.
  53      $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
  54  
  55      // An insert without a value for the column 'test_table' should fail.
  56      $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
  57  
  58      // Add a default value to the column.
  59      db_field_set_default('test_table', 'test_field', 0);
  60      // The insert should now succeed.
  61      $this->assertTrue($this->tryInsert(), t('Insert with a default succeeded.'));
  62  
  63      // Remove the default.
  64      db_field_set_no_default('test_table', 'test_field');
  65      // The insert should fail again.
  66      $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
  67  
  68      // Test for fake index and test for the boolean result of indexExists().
  69      $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
  70      $this->assertIdentical($index_exists, FALSE, t('Fake index does not exists'));
  71      // Add index.
  72      db_add_index('test_table', 'test_field', array('test_field'));
  73      // Test for created index and test for the boolean result of indexExists().
  74      $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
  75      $this->assertIdentical($index_exists, TRUE, t('Index created.'));
  76  
  77      // Rename the table.
  78      db_rename_table('test_table', 'test_table2');
  79  
  80      // Index should be renamed.
  81      $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
  82      $this->assertTrue($index_exists, t('Index was renamed.'));
  83  
  84      // We need the default so that we can insert after the rename.
  85      db_field_set_default('test_table2', 'test_field', 0);
  86      $this->assertFalse($this->tryInsert(), t('Insert into the old table failed.'));
  87      $this->assertTrue($this->tryInsert('test_table2'), t('Insert into the new table succeeded.'));
  88  
  89      // We should have successfully inserted exactly two rows.
  90      $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
  91      $this->assertEqual($count, 2, t('Two fields were successfully inserted.'));
  92  
  93      // Try to drop the table.
  94      db_drop_table('test_table2');
  95      $this->assertFalse(db_table_exists('test_table2'), t('The dropped table does not exist.'));
  96  
  97      // Recreate the table.
  98      db_create_table('test_table', $table_specification);
  99      db_field_set_default('test_table', 'test_field', 0);
 100      db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.'));
 101  
 102      // Assert that the column comment has been set.
 103      $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
 104  
 105      // Change the new field to a serial column.
 106      db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial')));
 107  
 108      // Assert that the column comment has been set.
 109      $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
 110  
 111      $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
 112      $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
 113      $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
 114      $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
 115      $this->assertTrue($max2 > $max1, t('The serial is monotone.'));
 116  
 117      $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
 118      $this->assertEqual($count, 2, t('There were two rows.'));
 119  
 120      // Use database specific data type and ensure that table is created.
 121      $table_specification = array(
 122        'description' => 'Schema table description.',
 123        'fields' => array(
 124          'timestamp'  => array(
 125            'mysql_type' => 'timestamp',
 126            'pgsql_type' => 'timestamp',
 127            'sqlite_type' => 'datetime',
 128            'not null' => FALSE,
 129            'default' => NULL,
 130          ),
 131        ),
 132      );
 133      try {
 134        db_create_table('test_timestamp', $table_specification);
 135      }
 136      catch (Exception $e) {}
 137      $this->assertTrue(db_table_exists('test_timestamp'), t('Table with database specific datatype was created.'));
 138    }
 139  
 140    function tryInsert($table = 'test_table') {
 141      try {
 142         db_insert($table)
 143           ->fields(array('id' => mt_rand(10, 20)))
 144           ->execute();
 145        return TRUE;
 146      }
 147      catch (Exception $e) {
 148        return FALSE;
 149      }
 150    }
 151  
 152    /**
 153     * Checks that a table or column comment matches a given description.
 154     *
 155     * @param $description
 156     *   The asserted description.
 157     * @param $table
 158     *   The table to test.
 159     * @param $column
 160     *   Optional column to test.
 161     */
 162    function checkSchemaComment($description, $table, $column = NULL) {
 163      if (method_exists(Database::getConnection()->schema(), 'getComment')) {
 164        $comment = Database::getConnection()->schema()->getComment($table, $column);
 165        $this->assertEqual($comment, $description, t('The comment matches the schema description.'));
 166      }
 167    }
 168  
 169    /**
 170     * Tests creating unsigned columns and data integrity thereof.
 171     */
 172    function testUnsignedColumns() {
 173      // First create the table with just a serial column.
 174      $table_name = 'unsigned_table';
 175      $table_spec = array(
 176        'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)),
 177        'primary key' => array('serial_column'),
 178      );
 179      $ret = array();
 180      db_create_table($table_name, $table_spec);
 181  
 182      // Now set up columns for the other types.
 183      $types = array('int', 'float', 'numeric');
 184      foreach ($types as $type) {
 185        $column_spec = array('type' => $type, 'unsigned'=> TRUE);
 186        if ($type == 'numeric') {
 187          $column_spec += array('precision' => 10, 'scale' => 0);
 188        }
 189        $column_name = $type . '_column';
 190        $table_spec['fields'][$column_name] = $column_spec;
 191        db_add_field($table_name, $column_name, $column_spec);
 192      }
 193  
 194      // Finally, check each column and try to insert invalid values into them.
 195      foreach ($table_spec['fields'] as $column_name => $column_spec) {
 196        $this->assertTrue(db_field_exists($table_name, $column_name), t('Unsigned @type column was created.', array('@type' => $column_spec['type'])));
 197        $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), t('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type'])));
 198      }
 199    }
 200  
 201    /**
 202     * Tries to insert a negative value into columns defined as unsigned.
 203     *
 204     * @param $table_name
 205     *   The table to insert
 206     * @param $column_name
 207     *   The column to insert
 208     * @return
 209     *   TRUE if the insert succeeded, FALSE otherwise
 210     */
 211    function tryUnsignedInsert($table_name, $column_name) {
 212      try {
 213        db_insert($table_name)
 214           ->fields(array($column_name => -1))
 215           ->execute();
 216        return TRUE;
 217      }
 218      catch (Exception $e) {
 219        return FALSE;
 220      }
 221    }
 222  
 223    /**
 224     * Test adding columns to an existing table.
 225     */
 226    function testSchemaAddField() {
 227      // Test varchar types.
 228      foreach (array(1, 32, 128, 256, 512) as $length) {
 229        $base_field_spec = array(
 230          'type' => 'varchar',
 231          'length' => $length,
 232        );
 233        $variations = array(
 234          array('not null' => FALSE),
 235          array('not null' => FALSE, 'default' => '7'),
 236          array('not null' => TRUE, 'initial' => 'd'),
 237          array('not null' => TRUE, 'initial' => 'd', 'default' => '7'),
 238        );
 239  
 240        foreach ($variations as $variation) {
 241          $field_spec = $variation + $base_field_spec;
 242          $this->assertFieldAdditionRemoval($field_spec);
 243        }
 244      }
 245  
 246      // Test int and float types.
 247      foreach (array('int', 'float') as $type) {
 248        foreach (array('tiny', 'small', 'medium', 'normal', 'big') as $size) {
 249          $base_field_spec = array(
 250            'type' => $type,
 251            'size' => $size,
 252          );
 253          $variations = array(
 254            array('not null' => FALSE),
 255            array('not null' => FALSE, 'default' => 7),
 256            array('not null' => TRUE, 'initial' => 1),
 257            array('not null' => TRUE, 'initial' => 1, 'default' => 7),
 258          );
 259  
 260          foreach ($variations as $variation) {
 261            $field_spec = $variation + $base_field_spec;
 262            $this->assertFieldAdditionRemoval($field_spec);
 263          }
 264        }
 265      }
 266  
 267      // Test numeric types.
 268      foreach (array(1, 5, 10, 40, 65) as $precision) {
 269        foreach (array(0, 2, 10, 30) as $scale) {
 270          if ($precision <= $scale) {
 271            // Precision must be smaller then scale.
 272            continue;
 273          }
 274  
 275          $base_field_spec = array(
 276            'type' => 'numeric',
 277            'scale' => $scale,
 278            'precision' => $precision,
 279          );
 280          $variations = array(
 281            array('not null' => FALSE),
 282            array('not null' => FALSE, 'default' => 7),
 283            array('not null' => TRUE, 'initial' => 1),
 284            array('not null' => TRUE, 'initial' => 1, 'default' => 7),
 285          );
 286  
 287          foreach ($variations as $variation) {
 288            $field_spec = $variation + $base_field_spec;
 289            $this->assertFieldAdditionRemoval($field_spec);
 290          }
 291        }
 292      }
 293    }
 294  
 295    /**
 296     * Assert that a given field can be added and removed from a table.
 297     *
 298     * The addition test covers both defining a field of a given specification
 299     * when initially creating at table and extending an existing table.
 300     *
 301     * @param $field_spec
 302     *   The schema specification of the field.
 303     */
 304    protected function assertFieldAdditionRemoval($field_spec) {
 305      // Try creating the field on a new table.
 306      $table_name = 'test_table_' . ($this->counter++);
 307      $table_spec = array(
 308        'fields' => array(
 309          'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
 310          'test_field' => $field_spec,
 311        ),
 312        'primary key' => array('serial_column'),
 313      );
 314      db_create_table($table_name, $table_spec);
 315      $this->pass(t('Table %table created.', array('%table' => $table_name)));
 316  
 317      // Check the characteristics of the field.
 318      $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
 319  
 320      // Clean-up.
 321      db_drop_table($table_name);
 322  
 323      // Try adding a field to an existing table.
 324      $table_name = 'test_table_' . ($this->counter++);
 325      $table_spec = array(
 326        'fields' => array(
 327          'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
 328        ),
 329        'primary key' => array('serial_column'),
 330      );
 331      db_create_table($table_name, $table_spec);
 332      $this->pass(t('Table %table created.', array('%table' => $table_name)));
 333  
 334      // Insert some rows to the table to test the handling of initial values.
 335      for ($i = 0; $i < 3; $i++) {
 336        db_insert($table_name)
 337          ->useDefaults(array('serial_column'))
 338          ->execute();
 339      }
 340  
 341      db_add_field($table_name, 'test_field', $field_spec);
 342      $this->pass(t('Column %column created.', array('%column' => 'test_field')));
 343  
 344      // Check the characteristics of the field.
 345      $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
 346  
 347      // Clean-up.
 348      db_drop_field($table_name, 'test_field');
 349      db_drop_table($table_name);
 350    }
 351  
 352    /**
 353     * Assert that a newly added field has the correct characteristics.
 354     */
 355    protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) {
 356      // Check that the initial value has been registered.
 357      if (isset($field_spec['initial'])) {
 358        // There should be no row with a value different then $field_spec['initial'].
 359        $count = db_select($table_name)
 360          ->fields($table_name, array('serial_column'))
 361          ->condition($field_name, $field_spec['initial'], '<>')
 362          ->countQuery()
 363          ->execute()
 364          ->fetchField();
 365        $this->assertEqual($count, 0, t('Initial values filled out.'));
 366      }
 367  
 368      // Check that the default value has been registered.
 369      if (isset($field_spec['default'])) {
 370        // Try inserting a row, and check the resulting value of the new column.
 371        $id = db_insert($table_name)
 372          ->useDefaults(array('serial_column'))
 373          ->execute();
 374        $field_value = db_select($table_name)
 375          ->fields($table_name, array($field_name))
 376          ->condition('serial_column', $id)
 377          ->execute()
 378          ->fetchField();
 379        $this->assertEqual($field_value, $field_spec['default'], t('Default value registered.'));
 380      }
 381  
 382      db_drop_field($table_name, $field_name);
 383    }
 384  }

title

Description

title

Description

title

Description

title

title

Body