1 package Test::SQL::Translator;
3 # ----------------------------------------------------------------------
4 # Copyright (C) 2003 The SQLFairy Authors
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; version 2.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 # -------------------------------------------------------------------
25 Test::SQL::Translator - Test::More test functions for the Schema objects.
32 use base qw(Exporter);
34 use vars qw($VERSION @EXPORT @EXPORT_OK);
49 use SQL::Translator::Schema::Constants;
51 # $ATTRIBUTES{ <schema_object_name> } = { <attribname> => <default>, ... }
53 $ATTRIBUTES{field} = {
56 default_value => undef,
62 is_auto_increment => 0,
65 # foreign_key_reference,
69 $ATTRIBUTES{constraint} = {
80 reference_fields => [],
81 reference_table => '',
84 $ATTRIBUTES{'index'} = {
92 $ATTRIBUTES{'view'} = {
99 $ATTRIBUTES{'trigger'} = {
101 perform_action_when => undef,
102 database_event => undef,
108 $ATTRIBUTES{'procedure'} = {
116 $ATTRIBUTES{table} = {
119 #primary_key => undef, # pkey constraint
123 constraints => undef,
128 $ATTRIBUTES{schema} = {
131 procedures => undef, # [] when set
132 tables => undef, # [] when set
133 triggers => undef, # [] when set
134 views => undef, # [] when set
141 # Given a test hash and schema object name set any attribute keys not present in
142 # the test hash to their default value for that schema object type.
143 # e.g. default_attribs( $test, "field" );
144 sub default_attribs {
145 my ($foo, $what) = @_;
146 die "Can't add default attibs - unkown Scheam object type '$what'."
147 unless exists $ATTRIBUTES{$what};
148 $foo->{$_} = $ATTRIBUTES{$what}{$_}
149 foreach grep !exists($foo->{$_}), keys %{$ATTRIBUTES{$what}};
153 # Format test name so it will prepend the test names used below.
157 $name = "$name - " if $name;
162 my ($f1,$test,$name) = @_;
163 my $t_name = t_name($name);
164 default_attribs($test,"field");
167 fail " Field '$test->{name}' doesn't exist!";
168 # TODO Do a skip on the following tests. Currently the test counts wont
169 # match at the end. So at least it fails.
173 my $full_name = $f1->table->name.".".$test->{name};
175 is( $f1->name, $test->{name}, "${t_name}Field '$full_name'" );
177 is( $f1->is_valid, $test->{is_valid},
178 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
180 is( $f1->data_type, $test->{data_type},
181 "$t_name type is '$test->{data_type}'" );
183 is( $f1->size, $test->{size}, "$t_name size is '$test->{size}'" );
185 is( $f1->default_value, $test->{default_value},
186 "$t_name default value is "
187 .(defined($test->{default_value}) ? "'$test->{default_value}'" : "UNDEF" )
190 is( $f1->is_nullable, $test->{is_nullable},
191 "$t_name ".($test->{is_nullable} ? 'can' : 'cannot').' be null' );
193 is( $f1->is_unique, $test->{is_unique},
194 "$t_name ".($test->{is_unique} ? 'can' : 'cannot').' be unique' );
196 is( $f1->is_primary_key, $test->{is_primary_key},
197 "$t_name is ".($test->{is_primary_key} ? '' : 'not ').'a primary_key' );
199 is( $f1->is_foreign_key, $test->{is_foreign_key},
200 "$t_name is ".($test->{is_foreign_key} ? '' : 'not').' a foreign_key' );
202 is( $f1->is_auto_increment, $test->{is_auto_increment},
204 .($test->{is_auto_increment} ? '' : 'not ').'an auto_increment' );
206 is( $f1->comments, $test->{comments}, "$t_name comments" );
208 is_deeply( { $f1->extra }, $test->{extra}, "$t_name extra" );
212 my ($obj,$test,$name) = @_;
213 my $t_name = t_name($name);
214 default_attribs($test,"constraint");
216 is( $obj->name, $test->{name}, "${t_name}Constraint '$test->{name}'" );
218 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
220 is( $obj->deferrable, $test->{deferrable},
221 "$t_name ".($test->{deferrable} ? 'can' : 'cannot').' be deferred' );
223 is( $obj->is_valid, $test->{is_valid},
224 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
226 is($obj->table->name,$test->{table},"$t_name table is '$test->{table}'" );
228 is( $obj->expression, $test->{expression},
229 "$t_name expression is '$test->{expression}'" );
231 is_deeply( [$obj->fields], $test->{fields},
232 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
234 is( $obj->reference_table, $test->{reference_table},
235 "$t_name reference_table is '$test->{reference_table}'" );
237 is_deeply( [$obj->reference_fields], $test->{reference_fields},
238 "$t_name reference_fields are '".join(",",@{$test->{reference_fields}})."'" );
240 is( $obj->match_type, $test->{match_type},
241 "$t_name match_type is '$test->{match_type}'" );
243 is( $obj->on_delete, $test->{on_delete},
244 "$t_name on_delete is '$test->{on_delete}'" );
246 is( $obj->on_update, $test->{on_update},
247 "$t_name on_update is '$test->{on_update}'" );
249 is_deeply( [$obj->options], $test->{options},
250 "$t_name options are '".join(",",@{$test->{options}})."'" );
252 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
256 my ($obj,$test,$name) = @_;
257 my $t_name = t_name($name);
258 default_attribs($test,"index");
260 is( $obj->name, $test->{name}, "${t_name}Index '$test->{name}'" );
262 is( $obj->is_valid, $test->{is_valid},
263 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
265 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
267 is_deeply( [$obj->fields], $test->{fields},
268 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
270 is_deeply( [$obj->options], $test->{options},
271 "$t_name options are '".join(",",@{$test->{options}})."'" );
273 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
277 my ($obj,$test,$name) = @_;
278 my $t_name = t_name($name);
279 default_attribs($test,"index");
281 is( $obj->name, $test->{name}, "${t_name}Trigger '$test->{name}'" );
283 is( $obj->is_valid, $test->{is_valid},
284 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
286 is( $obj->perform_action_when, $test->{perform_action_when},
287 "$t_name perform_action_when is '$test->{perform_action_when}'" );
289 is( $obj->database_event, $test->{database_event},
290 "$t_name database_event is '$test->{database_event}'" );
292 is( $obj->on_table, $test->{on_table},
293 "$t_name on_table is '$test->{on_table}'" );
295 is( $obj->action, $test->{action}, "$t_name action is '$test->{action}'" );
297 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
301 my ($obj,$test,$name) = @_;
302 my $t_name = t_name($name);
303 default_attribs($test,"index");
305 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
307 is( $obj->name, $test->{name}, "${t_name}View '$test->{name}'" );
309 is( $obj->is_valid, $test->{is_valid},
310 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
312 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
314 is_deeply( [$obj->fields], $test->{fields},
315 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
317 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
321 my ($obj,$test,$name) = @_;
322 my $t_name = t_name($name);
323 default_attribs($test,"index");
325 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
327 is( $obj->name, $test->{name}, "${t_name}Procedure '$test->{name}'" );
329 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
331 is_deeply( [$obj->parameters], $test->{parameters},
332 "$t_name parameters are '".join(",",@{$test->{parameters}})."'" );
334 is( $obj->comments, $test->{comments},
335 "$t_name comments is '$test->{comments}'" );
337 is( $obj->owner, $test->{owner}, "$t_name owner is '$test->{owner}'" );
339 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
343 my ($obj,$test,$name) = @_;
344 my $t_name = t_name($name);
345 default_attribs($test,"table");
348 my $tbl_name = $arg{name} || die "Need a table name to test.";
349 is( $obj->{name}, $arg{name}, "${t_name}Table '$arg{name}'" );
351 is_deeply( [$obj->options], $test->{options},
352 "$t_name options are '".join(",",@{$test->{options}})."'" );
354 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
357 if ( $arg{fields} ) {
358 my @fldnames = map {$_->{name}} @{$arg{fields}};
360 [ map {$_->name} $obj->get_fields ],
362 "${t_name} field names are ".join(", ",@fldnames)
364 foreach ( @{$arg{fields}} ) {
365 my $f_name = $_->{name} || die "Need a field name to test.";
366 next unless my $fld = $obj->get_field($f_name);
367 field_ok( $fld, $_, $name );
371 is(scalar($obj->get_fields), undef,
372 "${t_name} has no fields.");
375 # Constraints and Indices
376 _test_kids($obj, $test, $name, {
377 constraint => "constraints",
378 'index' => "indices",
383 my ($obj, $test, $name, $kids) = @_;
384 my $t_name = t_name($name);
385 my $obj_name = ref $obj;
386 ($obj_name) = $obj_name =~ m/^.*::(.*)$/;
388 while ( my($foo,$plural) = each %$kids ) {
389 next unless defined $test->{$plural};
390 if ( my @tfoo = @{$test->{$plural}} ) {
391 my $meth = "get_$plural";
392 my @foo = $obj->$meth;
393 is(scalar(@foo), scalar(@tfoo),
394 "${t_name}$obj_name has ".scalar(@tfoo)." $plural");
396 my $ans = { lc($obj_name) => $obj->name, %{shift @tfoo}};
397 #my $ans = shift @tfoo;
398 my $meth = "${foo}_ok";
400 $meth->( $_, $ans, $name );
408 my ($obj,$test,$name) = @_;
409 my $t_name = t_name($name);
410 default_attribs($test,"schema");
412 is( $obj->name, $test->{name}, "${t_name}Schema name is '$test->{name}'" );
414 is( $obj->database, $test->{database},
415 "$t_name database is '$test->{database}'" );
417 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
419 is( $obj->is_valid, $test->{is_valid},
420 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
423 if ( $test->{tables} ) {
424 is_deeply( [ map {$_->name} $obj->get_tables ],
425 [ map {$_->{name}} @{$test->{tables}} ],
426 "${t_name} table names match" );
427 foreach ( @{$test->{tables}} ) {
428 my $t_name = $_->{name} || die "Need a table name to test.";
429 table_ok( $obj->get_table($t_name), $_, $name );
433 is(scalar($obj->get_tables), undef,
434 "${t_name} has no tables.");
437 # Procedures, Triggers, Views
438 _test_kids($obj, $test, $name, {
439 procedure => "procedures",
440 trigger => "triggers",
445 # maybe_plan($ntests, @modules)
447 # Calls plan $ntests if @modules can all be loaded; otherwise,
448 # calls skip_all with an explanation of why the tests were skipped.
450 my ($ntests, @modules) = @_;
453 for my $module (@modules) {
455 if ($@ && $@ =~ /Can't locate (\S+)/) {
464 my $msg = sprintf "Missing dependenc%s: %s",
465 @errors == 1 ? 'y' : 'ies',
467 plan skip_all => $msg;
470 plan tests => $ntests;
474 1; # compile please ===========================================================
485 use Test::SQL::Translator;
488 my $sqlt = SQL::Translator->new(
490 filename => "$Bin/data/magic/test.magic",
494 my $schema = $sqlt->schema;
496 # Test the table it produced.
497 table_ok( $schema->get_table("Customer"), {
501 name => "CustomerID",
504 default_value => undef,
510 data_type => "VARCHAR",
517 type => "PRIMARY KEY",
518 fields => "CustomerID",
531 Provides a set of Test::More tests for Schema objects. Testing a parsed
532 schema is then as easy as writing a perl data structure describing how you
533 expect the schema to look. Also provides maybe_plan for conditionally running
534 tests based on their dependencies.
536 The data structures given to the test subs don't have to include all the
537 possible values, only the ones you expect to have changed. Any left out will be
538 tested to make sure they are still at their default value. This is a usefull
539 check that you your parser hasn't accidentally set schema values you didn't
542 For an example of the output run the t/16xml-parser.t test.
546 All the tests take a first arg of the schema object to test, followed by a
547 hash ref describing how you expect that object to look (you only need give the
548 attributes you expect to have changed from the default).
549 The 3rd arg is an optional test name to pre-pend to all the generated test
566 =head1 CONDITIONAL TESTS
568 The C<maybe_plan> function handles conditionally running an individual
569 test. It is here to enable running the test suite even when dependencies
570 are missing; not having (for example) GraphViz installed should not keep
571 the test suite from passing.
573 C<maybe_plan> takes the number of tests to (maybe) run, and a list of
574 modules on which test execution depends:
576 maybe_plan(180, 'SQL::Translator::Parser::MySQL');
578 If one of C<SQL::Translator::Parser::MySQL>'s dependencies does not exist,
579 then the test will be skipped.
583 table_ok, field_ok, constraint_ok, index_ok, view_ok, trigger_ok, procedure_ok,
590 =item Test the tests!
592 =item Test Count Constants
594 Constants to give the number of tests each *_ok sub uses. e.g. How many tests
595 does field_ok run? Can then use these to set up the test plan easily.
599 As the test subs wrap up lots of tests in one call you can't skip idividual
600 tests only whole sets e.g. a whole table or field.
601 We could add skip_* items to the test hashes to allow per test skips. e.g.
603 skip_is_primary_key => "Need to fix primary key parsing.",
605 =item yaml test specs
607 Maybe have the test subs also accept yaml for the test hash ref as its a much
608 nicer for writing big data structures. We can then define tests as in input
609 schema file and test yaml file to compare it against.
617 Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>, Darren Chamberlain <darren@cpan.org>.
619 Thanks to Ken Y. Clark for the original table and field test code taken from
624 perl(1), SQL::Translator, SQL::Translator::Schema, Test::More.