1 package Test::SQL::Translator;
3 # ----------------------------------------------------------------------
4 # $Id: Translator.pm,v 1.6 2004-07-08 17:29:56 grommit Exp $
5 # ----------------------------------------------------------------------
6 # Copyright (C) 2003 The SQLFairy Authors
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License as
10 # published by the Free Software Foundation; version 2.
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 # -------------------------------------------------------------------
27 Test::SQL::Translator - Test::More test functions for the Schema objects.
34 use base qw(Exporter);
36 use vars qw($VERSION @EXPORT @EXPORT_OK);
37 $VERSION = sprintf "%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/;
51 use SQL::Translator::Schema::Constants;
53 # $ATTRIBUTES{ <schema_object_name> } = { <attribname> => <default>, ... }
55 $ATTRIBUTES{field} = {
58 default_value => undef,
64 is_auto_increment => 0,
67 # foreign_key_reference,
71 $ATTRIBUTES{constraint} = {
82 reference_fields => [],
83 reference_table => '',
85 $ATTRIBUTES{'index'} = {
92 $ATTRIBUTES{'view'} = {
98 $ATTRIBUTES{'trigger'} = {
100 perform_action_when => undef,
101 database_event => undef,
106 $ATTRIBUTES{'procedure'} = {
113 $ATTRIBUTES{table} = {
116 #primary_key => undef, # pkey constraint
120 constraints => undef,
124 $ATTRIBUTES{schema} = {
127 procedures => undef, # [] when set
128 tables => undef, # [] when set
129 triggers => undef, # [] when set
130 views => undef, # [] when set
136 # Given a test hash and schema object name set any attribute keys not present in
137 # the test hash to their default value for that schema object type.
138 # e.g. default_attribs( $test, "field" );
139 sub default_attribs {
140 my ($foo, $what) = @_;
141 die "Can't add default attibs - unkown Scheam object type '$what'."
142 unless exists $ATTRIBUTES{$what};
143 $foo->{$_} = $ATTRIBUTES{$what}{$_}
144 foreach grep !exists($foo->{$_}), keys %{$ATTRIBUTES{$what}};
148 # Format test name so it will prepend the test names used below.
152 $name = "$name - " if $name;
157 my ($f1,$test,$name) = @_;
158 my $t_name = t_name($name);
159 default_attribs($test,"field");
162 fail " Field '$test->{name}' doesn't exist!";
163 # TODO Do a skip on the following tests. Currently the test counts wont
164 # match at the end. So at least it fails.
168 my $full_name = $f1->table->name.".".$test->{name};
170 is( $f1->name, $test->{name}, "${t_name}Field '$full_name'" );
172 is( $f1->is_valid, $test->{is_valid},
173 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
175 is( $f1->data_type, $test->{data_type},
176 "$t_name type is '$test->{data_type}'" );
178 is( $f1->size, $test->{size}, "$t_name size is '$test->{size}'" );
180 is( $f1->default_value, $test->{default_value},
181 "$t_name default value is "
182 .(defined($test->{default_value}) ? "'$test->{default_value}'" : "UNDEF" )
185 is( $f1->is_nullable, $test->{is_nullable},
186 "$t_name ".($test->{is_nullable} ? 'can' : 'cannot').' be null' );
188 is( $f1->is_unique, $test->{is_unique},
189 "$t_name ".($test->{is_unique} ? 'can' : 'cannot').' be unique' );
191 is( $f1->is_primary_key, $test->{is_primary_key},
192 "$t_name is ".($test->{is_primary_key} ? '' : 'not ').'a primary_key' );
194 is( $f1->is_foreign_key, $test->{is_foreign_key},
195 "$t_name is ".($test->{is_foreign_key} ? '' : 'not').' a foreign_key' );
197 is( $f1->is_auto_increment, $test->{is_auto_increment},
199 .($test->{is_auto_increment} ? '' : 'not ').'an auto_increment' );
201 is( $f1->comments, $test->{comments}, "$t_name comments" );
203 is_deeply( { $f1->extra }, $test->{extra}, "$t_name extra" );
207 my ($obj,$test,$name) = @_;
208 my $t_name = t_name($name);
209 default_attribs($test,"constraint");
211 is( $obj->name, $test->{name}, "${t_name}Constraint '$test->{name}'" );
213 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
215 is( $obj->deferrable, $test->{deferrable},
216 "$t_name ".($test->{deferrable} ? 'can' : 'cannot').' be deferred' );
218 is( $obj->is_valid, $test->{is_valid},
219 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
221 is($obj->table->name,$test->{table},"$t_name table is '$test->{table}'" );
223 is( $obj->expression, $test->{expression},
224 "$t_name expression is '$test->{expression}'" );
226 is_deeply( [$obj->fields], $test->{fields},
227 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
229 is( $obj->reference_table, $test->{reference_table},
230 "$t_name reference_table is '$test->{reference_table}'" );
232 is_deeply( [$obj->reference_fields], $test->{reference_fields},
233 "$t_name reference_fields are '".join(",",@{$test->{reference_fields}})."'" );
235 is( $obj->match_type, $test->{match_type},
236 "$t_name match_type is '$test->{match_type}'" );
238 is( $obj->on_delete, $test->{on_delete},
239 "$t_name on_delete is '$test->{on_delete}'" );
241 is( $obj->on_update, $test->{on_update},
242 "$t_name on_update is '$test->{on_update}'" );
244 is_deeply( [$obj->options], $test->{options},
245 "$t_name options are '".join(",",@{$test->{options}})."'" );
249 my ($obj,$test,$name) = @_;
250 my $t_name = t_name($name);
251 default_attribs($test,"index");
253 is( $obj->name, $test->{name}, "${t_name}Index '$test->{name}'" );
255 is( $obj->is_valid, $test->{is_valid},
256 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
258 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
260 is_deeply( [$obj->fields], $test->{fields},
261 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
263 is_deeply( [$obj->options], $test->{options},
264 "$t_name options are '".join(",",@{$test->{options}})."'" );
268 my ($obj,$test,$name) = @_;
269 my $t_name = t_name($name);
270 default_attribs($test,"index");
272 is( $obj->name, $test->{name}, "${t_name}Constraint '$test->{name}'" );
274 is( $obj->is_valid, $test->{is_valid},
275 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
277 is( $obj->perform_action_when, $test->{perform_action_when},
278 "$t_name perform_action_when is '$test->{perform_action_when}'" );
280 is( $obj->database_event, $test->{database_event},
281 "$t_name database_event is '$test->{database_event}'" );
283 is( $obj->on_table, $test->{on_table},
284 "$t_name on_table is '$test->{on_table}'" );
286 is( $obj->action, $test->{action}, "$t_name action is '$test->{action}'" );
290 my ($obj,$test,$name) = @_;
291 my $t_name = t_name($name);
292 default_attribs($test,"index");
294 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
296 is( $obj->name, $test->{name}, "${t_name}View '$test->{name}'" );
298 is( $obj->is_valid, $test->{is_valid},
299 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
301 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
303 is_deeply( [$obj->fields], $test->{fields},
304 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
308 my ($obj,$test,$name) = @_;
309 my $t_name = t_name($name);
310 default_attribs($test,"index");
312 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
314 is( $obj->name, $test->{name}, "${t_name}Procedure '$test->{name}'" );
316 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
318 is_deeply( [$obj->parameters], $test->{parameters},
319 "$t_name parameters are '".join(",",@{$test->{parameters}})."'" );
321 is( $obj->comments, $test->{comments},
322 "$t_name comments is '$test->{comments}'" );
324 is( $obj->owner, $test->{owner}, "$t_name owner is '$test->{owner}'" );
328 my ($obj,$test,$name) = @_;
329 my $t_name = t_name($name);
330 default_attribs($test,"table");
333 my $tbl_name = $arg{name} || die "Need a table name to test.";
334 is( $obj->{name}, $arg{name}, "${t_name}Table '$arg{name}'" );
336 is_deeply( [$obj->options], $test->{options},
337 "$t_name options are '".join(",",@{$test->{options}})."'" );
340 if ( $arg{fields} ) {
341 my @fldnames = map {$_->{name}} @{$arg{fields}};
343 [ map {$_->name} $obj->get_fields ],
345 "${t_name} field names are ".join(", ",@fldnames)
347 foreach ( @{$arg{fields}} ) {
348 my $f_name = $_->{name} || die "Need a field name to test.";
349 next unless my $fld = $obj->get_field($f_name);
350 field_ok( $fld, $_, $name );
354 is(scalar($obj->get_fields), undef,
355 "${t_name} has no fields.");
358 # Constraints and Indices
359 _test_kids($obj, $test, $name, {
360 constraint => "constraints",
361 'index' => "indices",
366 my ($obj, $test, $name, $kids) = @_;
367 my $t_name = t_name($name);
368 my $obj_name = ref $obj;
369 ($obj_name) = $obj_name =~ m/^.*::(.*)$/;
371 while ( my($foo,$plural) = each %$kids ) {
372 next unless defined $test->{$plural};
373 if ( my @tfoo = @{$test->{$plural}} ) {
374 my $meth = "get_$plural";
375 my @foo = $obj->$meth;
376 is(scalar(@foo), scalar(@tfoo),
377 "${t_name}$obj_name has ".scalar(@tfoo)." $plural");
379 my $ans = { lc($obj_name) => $obj->name, %{shift @tfoo}};
380 #my $ans = shift @tfoo;
381 my $meth = "${foo}_ok";
383 $meth->( $_, $ans, $name );
391 my ($obj,$test,$name) = @_;
392 my $t_name = t_name($name);
393 default_attribs($test,"schema");
395 is( $obj->name, $test->{name}, "${t_name}Schema name is '$test->{name}'" );
397 is( $obj->database, $test->{database},
398 "$t_name database is '$test->{database}'" );
400 is( $obj->is_valid, $test->{is_valid},
401 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
404 if ( $test->{tables} ) {
405 is_deeply( [ map {$_->name} $obj->get_tables ],
406 [ map {$_->{name}} @{$test->{tables}} ],
407 "${t_name} table names match" );
408 foreach ( @{$test->{tables}} ) {
409 my $t_name = $_->{name} || die "Need a table name to test.";
410 table_ok( $obj->get_table($t_name), $_, $name );
414 is(scalar($obj->get_tables), undef,
415 "${t_name} has no tables.");
418 # Procedures, Triggers, Views
419 _test_kids($obj, $test, $name, {
420 procedure => "procedures",
421 trigger => "triggers",
426 # maybe_plan($ntests, @modules)
428 # Calls plan $ntests if @modules can all be loaded; otherwise,
429 # calls skip_all with an explanation of why the tests were skipped.
431 my ($ntests, @modules) = @_;
434 for my $module (@modules) {
436 if ($@ && $@ =~ /Can't locate (\S+)/) {
445 my $msg = sprintf "Missing dependenc%s: %s",
446 @errors == 1 ? 'y' : 'ies',
448 plan skip_all => $msg;
451 plan tests => $ntests;
455 1; # compile please ===========================================================
466 use Test::SQL::Translator;
469 my $sqlt = SQL::Translator->new(
471 filename => "$Bin/data/magic/test.magic",
475 my $schema = $sqlt->schema;
477 # Test the table it produced.
478 table_ok( $schema->get_table("Customer"), {
482 name => "CustomerID",
485 default_value => undef,
491 data_type => "VARCHAR",
498 type => "PRIMARY KEY",
499 fields => "CustomerID",
512 Provides a set of Test::More tests for Schema objects. Testing a parsed
513 schema is then as easy as writing a perl data structure describing how you
514 expect the schema to look. Also provides maybe_plan for conditionally running
515 tests based on their dependencies.
517 The data structures given to the test subs don't have to include all the
518 possible values, only the ones you expect to have changed. Any left out will be
519 tested to make sure they are still at their default value. This is a usefull
520 check that you your parser hasn't accidentally set schema values you didn't
523 For an example of the output run the t/16xml-parser.t test.
527 All the tests take a first arg of the schema object to test, followed by a
528 hash ref describing how you expect that object to look (you only need give the
529 attributes you expect to have changed from the default).
530 The 3rd arg is an optional test name to pre-pend to all the generated test
547 =head1 CONDITIONAL TESTS
549 The C<maybe_plan> function handles conditionally running an individual
550 test. It is here to enable running the test suite even when dependencies
551 are missing; not having (for example) GraphViz installed should not keep
552 the test suite from passing.
554 C<maybe_plan> takes the number of tests to (maybe) run, and a list of
555 modules on which test execution depends:
557 maybe_plan(180, 'SQL::Translator::Parser::MySQL');
559 If one of C<SQL::Translator::Parser::MySQL>'s dependencies does not exist,
560 then the test will be skipped.
564 table_ok, field_ok, constraint_ok, index_ok, view_ok, trigger_ok, procedure_ok,
571 =item Test the tests!
573 =item Test Count Constants
575 Constants to give the number of tests each *_ok sub uses. e.g. How many tests
576 does field_ok run? Can then use these to set up the test plan easily.
580 As the test subs wrap up lots of tests in one call you can't skip idividual
581 tests only whole sets e.g. a whole table or field.
582 We could add skip_* items to the test hashes to allow per test skips. e.g.
584 skip_is_primary_key => "Need to fix primary key parsing.",
586 =item yaml test specs
588 Maybe have the test subs also accept yaml for the test hash ref as its a much
589 nicer for writing big data structures. We can then define tests as in input
590 schema file and test yaml file to compare it against.
598 Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>, Darren Chamberlain <darren@cpan.org>.
600 Thanks to Ken Y. Clark for the original table and field test code taken from
605 perl(1), SQL::Translator, SQL::Translator::Schema, Test::More.