1 package Test::SQL::Translator;
3 # ----------------------------------------------------------------------
4 # $Id: Translator.pm,v 1.4 2004-03-04 14:41:49 dlc 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.4 $ =~ /(\d+)\.(\d+)/;
52 use SQL::Translator::Schema::Constants;
54 # $ATTRIBUTES{ <schema_object_name> } = { <attribname> => <default>, ... }
56 $ATTRIBUTES{field} = {
59 default_value => undef,
65 is_auto_increment => 0,
68 # foreign_key_reference,
72 $ATTRIBUTES{constraint} = {
83 reference_fields => [],
84 reference_table => '',
86 $ATTRIBUTES{'index'} = {
93 $ATTRIBUTES{'view'} = {
99 $ATTRIBUTES{'trigger'} = {
101 perform_action_when => undef,
102 database_event => undef,
107 $ATTRIBUTES{'procedure'} = {
114 $ATTRIBUTES{table} = {
117 #primary_key => undef, # pkey constraint
121 constraints => undef,
125 $ATTRIBUTES{schema} = {
128 procedures => undef, # [] when set
129 tables => undef, # [] when set
130 triggers => undef, # [] when set
131 views => undef, # [] when set
137 # Given a test hash and schema object name set any attribute keys not present in
138 # the test hash to their default value for that schema object type.
139 # e.g. default_attribs( $test, "field" );
140 sub default_attribs {
141 my ($foo, $what) = @_;
142 die "Can't add default attibs - unkown Scheam object type '$what'."
143 unless exists $ATTRIBUTES{$what};
144 $foo->{$_} = $ATTRIBUTES{$what}{$_}
145 foreach grep !exists($foo->{$_}), keys %{$ATTRIBUTES{$what}};
149 # Format test name so it will prepend the test names used below.
153 $name = "$name - " if $name;
158 my ($f1,$test,$name) = @_;
159 my $t_name = t_name($name);
160 default_attribs($test,"field");
163 fail " Field '$test->{name}' doesn't exist!";
164 # TODO Do a skip on the following tests. Currently the test counts wont
165 # match at the end. So at least it fails.
169 my $full_name = $f1->table->name.".".$test->{name};
171 is( $f1->name, $test->{name}, "${t_name}Field '$full_name'" );
173 is( $f1->is_valid, $test->{is_valid},
174 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
176 is( $f1->data_type, $test->{data_type},
177 "$t_name type is '$test->{data_type}'" );
179 is( $f1->size, $test->{size}, "$t_name size is '$test->{size}'" );
181 is( $f1->default_value, $test->{default_value},
182 "$t_name default value is "
183 .(defined($test->{default_value}) ? "'$test->{default_value}'" : "UNDEF" )
186 is( $f1->is_nullable, $test->{is_nullable},
187 "$t_name ".($test->{is_nullable} ? 'can' : 'cannot').' be null' );
189 is( $f1->is_unique, $test->{is_unique},
190 "$t_name ".($test->{is_unique} ? 'can' : 'cannot').' be unique' );
192 is( $f1->is_primary_key, $test->{is_primary_key},
193 "$t_name is ".($test->{is_primary_key} ? '' : 'not ').'a primary_key' );
195 is( $f1->is_foreign_key, $test->{is_foreign_key},
196 "$t_name is ".($test->{is_foreign_key} ? '' : 'not').' a foreign_key' );
198 is( $f1->is_auto_increment, $test->{is_auto_increment},
200 .($test->{is_auto_increment} ? '' : 'not ').'an auto_increment' );
202 is( $f1->comments, $test->{comments}, "$t_name comments" );
204 is_deeply( { $f1->extra }, $test->{extra}, "$t_name extra" );
208 my ($obj,$test,$name) = @_;
209 my $t_name = t_name($name);
210 default_attribs($test,"constraint");
212 is( $obj->name, $test->{name}, "${t_name}Constraint '$test->{name}'" );
214 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
216 is( $obj->deferrable, $test->{deferrable},
217 "$t_name ".($test->{deferrable} ? 'can' : 'cannot').' be deferred' );
219 is( $obj->is_valid, $test->{is_valid},
220 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
222 is($obj->table->name,$test->{table},"$t_name table is '$test->{table}'" );
224 is( $obj->expression, $test->{expression},
225 "$t_name expression is '$test->{expression}'" );
227 is_deeply( [$obj->fields], $test->{fields},
228 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
230 is( $obj->reference_table, $test->{reference_table},
231 "$t_name reference_table is '$test->{reference_table}'" );
233 is_deeply( [$obj->reference_fields], $test->{reference_fields},
234 "$t_name reference_fields are '".join(",",@{$test->{reference_fields}})."'" );
236 is( $obj->match_type, $test->{match_type},
237 "$t_name match_type is '$test->{match_type}'" );
239 is( $obj->on_delete, $test->{on_delete},
240 "$t_name on_delete is '$test->{on_delete}'" );
242 is( $obj->on_update, $test->{on_update},
243 "$t_name on_update is '$test->{on_update}'" );
245 is_deeply( [$obj->options], $test->{options},
246 "$t_name options are '".join(",",@{$test->{options}})."'" );
250 my ($obj,$test,$name) = @_;
251 my $t_name = t_name($name);
252 default_attribs($test,"index");
254 is( $obj->name, $test->{name}, "${t_name}Index '$test->{name}'" );
256 is( $obj->is_valid, $test->{is_valid},
257 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
259 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
261 is_deeply( [$obj->fields], $test->{fields},
262 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
264 is_deeply( [$obj->options], $test->{options},
265 "$t_name options are '".join(",",@{$test->{options}})."'" );
269 my ($obj,$test,$name) = @_;
270 my $t_name = t_name($name);
271 default_attribs($test,"index");
273 is( $obj->name, $test->{name}, "${t_name}Constraint '$test->{name}'" );
275 is( $obj->is_valid, $test->{is_valid},
276 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
278 is( $obj->perform_action_when, $test->{perform_action_when},
279 "$t_name perform_action_when is '$test->{perform_action_when}'" );
281 is( $obj->database_event, $test->{database_event},
282 "$t_name database_event is '$test->{database_event}'" );
284 is( $obj->on_table, $test->{on_table},
285 "$t_name on_table is '$test->{on_table}'" );
287 is( $obj->action, $test->{action}, "$t_name action is '$test->{action}'" );
291 my ($obj,$test,$name) = @_;
292 my $t_name = t_name($name);
293 default_attribs($test,"index");
295 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
297 is( $obj->name, $test->{name}, "${t_name}View '$test->{name}'" );
299 is( $obj->is_valid, $test->{is_valid},
300 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
302 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
304 is_deeply( [$obj->fields], $test->{fields},
305 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
309 my ($obj,$test,$name) = @_;
310 my $t_name = t_name($name);
311 default_attribs($test,"index");
313 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
315 is( $obj->name, $test->{name}, "${t_name}Procedure '$test->{name}'" );
317 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
319 is_deeply( [$obj->parameters], $test->{parameters},
320 "$t_name parameters are '".join(",",@{$test->{parameters}})."'" );
322 is( $obj->comments, $test->{comments},
323 "$t_name comments is '$test->{comments}'" );
325 is( $obj->owner, $test->{owner}, "$t_name owner is '$test->{owner}'" );
329 my ($obj,$test,$name) = @_;
330 my $t_name = t_name($name);
331 default_attribs($test,"table");
334 my $tbl_name = $arg{name} || die "Need a table name to test.";
335 is( $obj->{name}, $arg{name}, "${t_name}Table '$arg{name}'" );
337 is_deeply( [$obj->options], $test->{options},
338 "$t_name options are '".join(",",@{$test->{options}})."'" );
341 if ( $arg{fields} ) {
342 my @fldnames = map {$_->{name}} @{$arg{fields}};
344 [ map {$_->name} $obj->get_fields ],
346 "${t_name} field names are ".join(", ",@fldnames)
348 foreach ( @{$arg{fields}} ) {
349 my $f_name = $_->{name} || die "Need a field name to test.";
350 next unless my $fld = $obj->get_field($f_name);
351 field_ok( $fld, $_, $name );
355 is(scalar($obj->get_fields), undef,
356 "${t_name} has no fields.");
359 # Constraints and Indices
360 _test_kids($obj, $test, $name, {
361 constraint => "constraints",
362 'index' => "indices",
367 my ($obj, $test, $name, $kids) = @_;
368 my $t_name = t_name($name);
369 my $obj_name = ref $obj;
370 ($obj_name) = $obj_name =~ m/^.*::(.*)$/;
372 while ( my($foo,$plural) = each %$kids ) {
373 next unless defined $test->{$plural};
374 if ( my @tfoo = @{$test->{$plural}} ) {
375 my $meth = "get_$plural";
376 my @foo = $obj->$meth;
377 is(scalar(@foo), scalar(@tfoo),
378 "${t_name}$obj_name has ".scalar(@tfoo)." $plural");
380 my $ans = { lc($obj_name) => $obj->name, %{shift @tfoo}};
381 #my $ans = shift @tfoo;
382 my $meth = "${foo}_ok";
384 $meth->( $_, $ans, $name );
394 my ($obj,$test,$name) = @_;
395 my $t_name = t_name($name);
396 default_attribs($test,"schema");
398 is( $obj->name, $test->{name}, "${t_name}Schema name is '$test->{name}'" );
400 is( $obj->database, $test->{database},
401 "$t_name database is '$test->{database}'" );
403 is( $obj->is_valid, $test->{is_valid},
404 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
407 if ( $test->{tables} ) {
408 is_deeply( [ map {$_->name} $obj->get_tables ],
409 [ map {$_->{name}} @{$test->{tables}} ],
410 "${t_name} table names match" );
411 foreach ( @{$test->{tables}} ) {
412 my $t_name = $_->{name} || die "Need a table name to test.";
413 table_ok( $obj->get_table($t_name), $_, $name );
417 is(scalar($obj->get_tables), undef,
418 "${t_name} has no tables.");
421 # Procedures, Triggers, Views
422 _test_kids($obj, $test, $name, {
423 procedure => "procedures",
424 trigger => "triggers",
429 # maybe_plan($ntests, @modules)
431 # Calls plan $ntests if @modules can all be loaded; otherwise,
432 # calls skip_all with an explanation of why the tests were skipped.
434 my ($ntests, @modules) = @_;
437 for my $module (@modules) {
439 if ($@ && $@ =~ /Can't locate (\S+)/) {
448 my $msg = sprintf "Missing dependenc%s: %s",
449 @errors == 1 ? 'y' : 'ies',
451 plan skip_all => $msg;
454 plan tests => $ntests;
458 1; # compile please ===========================================================
469 use Test::SQL::Translator;
472 my $sqlt = SQL::Translator->new(
474 filename => "$Bin/data/magic/test.magic",
478 my $schema = $sqlt->schema;
480 # Test the table it produced.
481 table_ok( $schema->get_table("Customer"), {
485 name => "CustomerID",
488 default_value => undef,
494 data_type => "VARCHAR",
501 type => "PRIMARY KEY",
502 fields => "CustomerID",
515 Provides a set of Test::More tests for Schema objects. Tesing a parsed
516 schema is then as easy as writing a perl data structure describing how you
517 expect the schema to look.
519 The data structures given to the test subs don't have to include all the
520 possible values, only the ones you expect to have changed. Any left out will be
521 tested to make sure they are still at their default value. This is a usefull
522 check that you your parser hasn't accidentally set schema values you didn't
523 expect it to. (And makes tests look nice and long ;-)
525 For an example of the output run the t/16xml-parser.t test.
529 All the tests take a first arg of the schema object to test, followed by a
530 hash ref describing how you expect that object to look (you only need give the
531 attributes you expect to have changed from the default).
532 The 3rd arg is an optional test name to pre-pend to all the generated test
549 =head1 CONDITIONAL TESTS
551 The C<maybe_plan> function handles conditionally running an individual
552 test. It is here to enable running the test suite even when dependencies
553 are missing; not having (for example) GraphViz installed should not keep
554 the test suite from passing.
556 C<maybe_plan> takes the number of tests to (maybe) run, and a list of
557 modules on which test execution depends:
559 maybe_plan(180, 'SQL::Translator::Parser::MySQL');
561 If one of C<SQL::Translator::Parser::MySQL>'s dependencies does not exist,
562 then the test will be skipped.
566 table_ok, field_ok, constraint_ok, index_ok, view_ok, trigger_ok, procedure_ok,
573 =item Test the tests!
579 =item Test Count Constants
581 Constants to give the number of tests each *_ok sub uses. e.g. How many tests
582 does field_ok run? Can then use these to set up the test plan easily.
586 As the test subs wrap up lots of tests in one call you can't skip idividual
587 tests only whole sets e.g. a whole table or field.
588 We could add skip_* items to the test hashes to allow per test skips. e.g.
590 skip_is_primary_key => "Need to fix primary key parsing.",
592 =item yaml test specs
594 Maybe have the test subs also accept yaml for the test hash ref as its a much
595 nicer for writing big data structures. We can then define tests as in input
596 schema file and test yaml file to compare it against.
604 Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>.
606 Thanks to Ken Y. Clark for the original table and field test code taken from
611 perl(1), SQL::Translator, SQL::Translator::Schema, Test::More.