Projet

Général

Profil

Paste
Télécharger (15,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / simpletest / tests / schema.test @ 01dfd3b5

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'), '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(), '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(), '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(), '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, '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, '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, '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(), 'Insert into the old table failed.');
87
    $this->assertTrue($this->tryInsert('test_table2'), '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, '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'), '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(), 'Insert with a serial succeeded.');
112
    $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
113
    $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
114
    $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
115
    $this->assertTrue($max2 > $max1, 'The serial is monotone.');
116

    
117
    $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
118
    $this->assertEqual($count, 2, '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'), '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, '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), format_string('Unsigned @type column was created.', array('@type' => $column_spec['type'])));
197
      $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('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(format_string('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(format_string('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(format_string('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, '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'], 'Default value registered.');
380
    }
381

    
382
    db_drop_field($table_name, $field_name);
383
  }
384

    
385
  /**
386
   * Tests the findTables() method.
387
   */
388
  public function testFindTables() {
389
    // We will be testing with three tables, two of them using the default
390
    // prefix and the third one with an individually specified prefix.
391

    
392
    // Set up a new connection with different connection info.
393
    $connection_info = Database::getConnectionInfo();
394

    
395
    // Add per-table prefix to the second table.
396
    $new_connection_info = $connection_info['default'];
397
    $new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
398
    Database::addConnectionInfo('test', 'default', $new_connection_info);
399

    
400
    Database::setActiveConnection('test');
401

    
402
    // Create the tables.
403
    $table_specification = array(
404
      'description' => 'Test table.',
405
      'fields' => array(
406
        'id'  => array(
407
          'type' => 'int',
408
          'default' => NULL,
409
        ),
410
      ),
411
    );
412
    Database::getConnection()->schema()->createTable('test_1_table', $table_specification);
413
    Database::getConnection()->schema()->createTable('test_2_table', $table_specification);
414
    Database::getConnection()->schema()->createTable('the_third_table', $table_specification);
415

    
416
    // Check the "all tables" syntax.
417
    $tables = Database::getConnection()->schema()->findTablesD8('%');
418
    sort($tables);
419
    $expected = array(
420
      'test_1_table',
421
      // This table uses a per-table prefix, yet it is returned as un-prefixed.
422
      'test_2_table',
423
      'the_third_table',
424
    );
425

    
426
    $this->assertTrue(!array_diff($expected, $tables), 'All tables were found.');
427

    
428
    // Check the restrictive syntax.
429
    $tables = Database::getConnection()->schema()->findTablesD8('test_%');
430
    sort($tables);
431
    $expected = array(
432
      'test_1_table',
433
      'test_2_table',
434
    );
435
    $this->assertEqual($tables, $expected, 'Two tables were found.');
436

    
437
    // Go back to the initial connection.
438
    Database::setActiveConnection('default');
439
  }
440
}