Bumping version to 1.62
[dbsrgits/SQL-Translator.git] / lib / Test / SQL / Translator.pm
CommitLineData
9bbbf403 1package Test::SQL::Translator;
2
9bbbf403 3=pod
4
5=head1 NAME
6
7Test::SQL::Translator - Test::More test functions for the Schema objects.
8
9=cut
10
11use strict;
12use warnings;
11fee3e0 13use Test::More;
14use SQL::Translator::Schema::Constants;
9bbbf403 15
16use base qw(Exporter);
0c04c5a2 17our @EXPORT_OK;
f769b7e8 18our $VERSION = '1.62';
0c04c5a2 19our @EXPORT = qw(
a0eb602d 20 schema_ok
9bbbf403 21 table_ok
22 field_ok
23 constraint_ok
24 index_ok
25 view_ok
26 trigger_ok
27 procedure_ok
49133ae7 28 maybe_plan
9bbbf403 29);
9bbbf403 30
9bbbf403 31# $ATTRIBUTES{ <schema_object_name> } = { <attribname> => <default>, ... }
11fee3e0 32my %ATTRIBUTES = (
33 field => {
34 name => undef,
35 data_type => '',
36 default_value => undef,
37 size => '0',
38 is_primary_key => 0,
39 is_unique => 0,
40 is_nullable => 1,
41 is_foreign_key => 0,
42 is_auto_increment => 0,
43 comments => '',
44 extra => {},
45 # foreign_key_reference,
46 is_valid => 1,
47 # order
48 },
49 constraint => {
50 name => '',
51 type => '',
52 deferrable => 1,
53 expression => '',
54 is_valid => 1,
55 fields => [],
56 match_type => '',
57 options => [],
58 on_delete => '',
59 on_update => '',
60 reference_fields => [],
61 reference_table => '',
62 extra => {},
63 },
64 index => {
65 fields => [],
66 is_valid => 1,
67 name => "",
68 options => [],
69 type => NORMAL,
70 extra => {},
71 },
72 view => {
73 name => "",
74 sql => "",
75 fields => [],
76 is_valid => 1,
77 extra => {},
78 },
79 trigger => {
80 name => '',
81 perform_action_when => undef,
82 database_events => undef,
83 on_table => undef,
84 action => undef,
85 is_valid => 1,
86 extra => {},
87 },
88 procedure => {
89 name => '',
90 sql => '',
91 parameters => [],
92 owner => '',
93 comments => '',
94 extra => {},
95 },
96 table => {
97 comments => undef,
98 name => '',
99 #primary_key => undef, # pkey constraint
100 options => [],
101 #order => 0,
102 fields => undef,
103 constraints => undef,
104 indices => undef,
105 is_valid => 1,
106 extra => {},
107 },
108 schema => {
109 name => '',
110 database => '',
111 procedures => undef, # [] when set
112 tables => undef, # [] when set
113 triggers => undef, # [] when set
114 views => undef, # [] when set
115 is_valid => 1,
116 extra => {},
117 }
118);
9bbbf403 119
120# Given a test hash and schema object name set any attribute keys not present in
121# the test hash to their default value for that schema object type.
122# e.g. default_attribs( $test, "field" );
123sub default_attribs {
11fee3e0 124 my ($hashref, $object_type) = @_;
125
126 if ( !exists $ATTRIBUTES{ $object_type } ) {
ea93df61 127 die "Can't add default attribs for unknown Schema "
11fee3e0 128 . "object type '$object_type'.";
129 }
130
ea93df61 131 for my $attr (
11fee3e0 132 grep { !exists $hashref->{ $_ } }
ea93df61 133 keys %{ $ATTRIBUTES{ $object_type } }
11fee3e0 134 ) {
135 $hashref->{ $attr } = $ATTRIBUTES{ $object_type }{ $attr }
136 }
137
138 return $hashref;
9bbbf403 139}
140
141# Format test name so it will prepend the test names used below.
142sub t_name {
143 my $name = shift;
144 $name ||= "";
145 $name = "$name - " if $name;
146 return $name;
147}
148
149sub field_ok {
150 my ($f1,$test,$name) = @_;
151 my $t_name = t_name($name);
152 default_attribs($test,"field");
153
154 unless ($f1) {
155 fail " Field '$test->{name}' doesn't exist!";
a0eb602d 156 # TODO Do a skip on the following tests. Currently the test counts wont
157 # match at the end. So at least it fails.
9bbbf403 158 return;
159 }
160
a0eb602d 161 my $full_name = $f1->table->name.".".$test->{name};
162
163 is( $f1->name, $test->{name}, "${t_name}Field '$full_name'" );
9bbbf403 164
165 is( $f1->is_valid, $test->{is_valid},
a0eb602d 166 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
9bbbf403 167
168 is( $f1->data_type, $test->{data_type},
169 "$t_name type is '$test->{data_type}'" );
170
171 is( $f1->size, $test->{size}, "$t_name size is '$test->{size}'" );
172
173 is( $f1->default_value, $test->{default_value},
174 "$t_name default value is "
175 .(defined($test->{default_value}) ? "'$test->{default_value}'" : "UNDEF" )
176 );
177
178 is( $f1->is_nullable, $test->{is_nullable},
179 "$t_name ".($test->{is_nullable} ? 'can' : 'cannot').' be null' );
180
181 is( $f1->is_unique, $test->{is_unique},
182 "$t_name ".($test->{is_unique} ? 'can' : 'cannot').' be unique' );
183
184 is( $f1->is_primary_key, $test->{is_primary_key},
185 "$t_name is ".($test->{is_primary_key} ? '' : 'not ').'a primary_key' );
186
187 is( $f1->is_foreign_key, $test->{is_foreign_key},
188 "$t_name is ".($test->{is_foreign_key} ? '' : 'not').' a foreign_key' );
189
190 is( $f1->is_auto_increment, $test->{is_auto_increment},
191 "$t_name is "
192 .($test->{is_auto_increment} ? '' : 'not ').'an auto_increment' );
193
194 is( $f1->comments, $test->{comments}, "$t_name comments" );
195
196 is_deeply( { $f1->extra }, $test->{extra}, "$t_name extra" );
197}
198
199sub constraint_ok {
200 my ($obj,$test,$name) = @_;
201 my $t_name = t_name($name);
202 default_attribs($test,"constraint");
203
204 is( $obj->name, $test->{name}, "${t_name}Constraint '$test->{name}'" );
205
206 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
207
208 is( $obj->deferrable, $test->{deferrable},
209 "$t_name ".($test->{deferrable} ? 'can' : 'cannot').' be deferred' );
210
211 is( $obj->is_valid, $test->{is_valid},
212 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
213
214 is($obj->table->name,$test->{table},"$t_name table is '$test->{table}'" );
215
216 is( $obj->expression, $test->{expression},
217 "$t_name expression is '$test->{expression}'" );
218
219 is_deeply( [$obj->fields], $test->{fields},
220 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
221
222 is( $obj->reference_table, $test->{reference_table},
223 "$t_name reference_table is '$test->{reference_table}'" );
224
225 is_deeply( [$obj->reference_fields], $test->{reference_fields},
226 "$t_name reference_fields are '".join(",",@{$test->{reference_fields}})."'" );
227
228 is( $obj->match_type, $test->{match_type},
229 "$t_name match_type is '$test->{match_type}'" );
230
231 is( $obj->on_delete, $test->{on_delete},
232 "$t_name on_delete is '$test->{on_delete}'" );
233
234 is( $obj->on_update, $test->{on_update},
235 "$t_name on_update is '$test->{on_update}'" );
236
237 is_deeply( [$obj->options], $test->{options},
238 "$t_name options are '".join(",",@{$test->{options}})."'" );
ea93df61 239
b1789409 240 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
9bbbf403 241}
242
243sub index_ok {
244 my ($obj,$test,$name) = @_;
245 my $t_name = t_name($name);
246 default_attribs($test,"index");
247
248 is( $obj->name, $test->{name}, "${t_name}Index '$test->{name}'" );
249
250 is( $obj->is_valid, $test->{is_valid},
251 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
252
253 is( $obj->type, $test->{type}, "$t_name type is '$test->{type}'" );
254
255 is_deeply( [$obj->fields], $test->{fields},
256 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
257
258 is_deeply( [$obj->options], $test->{options},
259 "$t_name options are '".join(",",@{$test->{options}})."'" );
ea93df61 260
b1789409 261 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
9bbbf403 262}
263
264sub trigger_ok {
265 my ($obj,$test,$name) = @_;
266 my $t_name = t_name($name);
267 default_attribs($test,"index");
268
14ad260a 269 is( $obj->name, $test->{name}, "${t_name}Trigger '$test->{name}'" );
9bbbf403 270
271 is( $obj->is_valid, $test->{is_valid},
272 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
273
274 is( $obj->perform_action_when, $test->{perform_action_when},
275 "$t_name perform_action_when is '$test->{perform_action_when}'" );
276
11fee3e0 277 is( join(',', $obj->database_events), $test->{database_events},
278 sprintf("%s database_events is '%s'",
279 $t_name,
280 $test->{'database_events'},
281 )
282 );
9bbbf403 283
284 is( $obj->on_table, $test->{on_table},
285 "$t_name on_table is '$test->{on_table}'" );
286
c0ec0e22 287 is( $obj->scope, $test->{scope}, "$t_name scope is '$test->{scope}'" )
288 if exists $test->{scope};
289
9bbbf403 290 is( $obj->action, $test->{action}, "$t_name action is '$test->{action}'" );
ea93df61 291
b1789409 292 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
9bbbf403 293}
294
295sub view_ok {
296 my ($obj,$test,$name) = @_;
297 my $t_name = t_name($name);
298 default_attribs($test,"index");
299
300 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
301
302 is( $obj->name, $test->{name}, "${t_name}View '$test->{name}'" );
303
304 is( $obj->is_valid, $test->{is_valid},
305 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
306
307 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
308
309 is_deeply( [$obj->fields], $test->{fields},
310 "$t_name fields are '".join(",",@{$test->{fields}})."'" );
ea93df61 311
b1789409 312 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
9bbbf403 313}
314
315sub procedure_ok {
316 my ($obj,$test,$name) = @_;
317 my $t_name = t_name($name);
318 default_attribs($test,"index");
319
320 #isa_ok( $v, 'SQL::Translator::Schema::View', 'View' );
321
322 is( $obj->name, $test->{name}, "${t_name}Procedure '$test->{name}'" );
323
324 is( $obj->sql, $test->{sql}, "$t_name sql is '$test->{sql}'" );
325
326 is_deeply( [$obj->parameters], $test->{parameters},
327 "$t_name parameters are '".join(",",@{$test->{parameters}})."'" );
328
ea93df61 329 is( $obj->comments, $test->{comments},
9bbbf403 330 "$t_name comments is '$test->{comments}'" );
331
332 is( $obj->owner, $test->{owner}, "$t_name owner is '$test->{owner}'" );
ea93df61 333
b1789409 334 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
9bbbf403 335}
336
337sub table_ok {
338 my ($obj,$test,$name) = @_;
339 my $t_name = t_name($name);
340 default_attribs($test,"table");
341 my %arg = %$test;
342
343 my $tbl_name = $arg{name} || die "Need a table name to test.";
a0eb602d 344 is( $obj->{name}, $arg{name}, "${t_name}Table '$arg{name}'" );
9bbbf403 345
346 is_deeply( [$obj->options], $test->{options},
347 "$t_name options are '".join(",",@{$test->{options}})."'" );
348
b1789409 349 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
350
9bbbf403 351 # Fields
352 if ( $arg{fields} ) {
a0eb602d 353 my @fldnames = map {$_->{name}} @{$arg{fields}};
ea93df61 354 is_deeply(
a0eb602d 355 [ map {$_->name} $obj->get_fields ],
356 [ @fldnames ],
357 "${t_name} field names are ".join(", ",@fldnames)
358 );
9bbbf403 359 foreach ( @{$arg{fields}} ) {
360 my $f_name = $_->{name} || die "Need a field name to test.";
a0eb602d 361 next unless my $fld = $obj->get_field($f_name);
362 field_ok( $fld, $_, $name );
9bbbf403 363 }
364 }
365 else {
366 is(scalar($obj->get_fields), undef,
a0eb602d 367 "${t_name} has no fields.");
9bbbf403 368 }
369
a0eb602d 370 # Constraints and Indices
371 _test_kids($obj, $test, $name, {
11fee3e0 372 constraint => 'constraints',
373 index => 'indices',
a0eb602d 374 });
375}
376
377sub _test_kids {
11fee3e0 378 my ( $obj, $test, $name, $kids ) = @_;
379 my $t_name = t_name($name);
a0eb602d 380 my $obj_name = ref $obj;
381 ($obj_name) = $obj_name =~ m/^.*::(.*)$/;
382
11fee3e0 383 while ( my ( $object_type, $plural ) = each %$kids ) {
384 next unless defined $test->{ $plural };
385
386 if ( my @tests = @{ $test->{ $plural } } ) {
9bbbf403 387 my $meth = "get_$plural";
11fee3e0 388 my @objects = $obj->$meth;
389 is( scalar(@objects), scalar(@tests),
ea93df61 390 "${t_name}$obj_name has " . scalar(@tests) . " $plural"
11fee3e0 391 );
392
393 for my $object (@objects) {
394 my $ans = { lc($obj_name) => $obj->name, %{ shift @tests } };
395
396 my $meth = "${object_type}_ok";
397 {
398 no strict 'refs';
399 $meth->( $object, $ans, $name );
9bbbf403 400 }
401 }
402 }
403 }
404}
405
406sub schema_ok {
407 my ($obj,$test,$name) = @_;
408 my $t_name = t_name($name);
409 default_attribs($test,"schema");
a0eb602d 410
411 is( $obj->name, $test->{name}, "${t_name}Schema name is '$test->{name}'" );
412
413 is( $obj->database, $test->{database},
414 "$t_name database is '$test->{database}'" );
ea93df61 415
b1789409 416 is_deeply( { $obj->extra }, $test->{extra}, "$t_name extra" );
a0eb602d 417
418 is( $obj->is_valid, $test->{is_valid},
419 "$t_name is ".($test->{is_valid} ? '' : 'not ').'valid' );
420
421 # Tables
422 if ( $test->{tables} ) {
423 is_deeply( [ map {$_->name} $obj->get_tables ],
424 [ map {$_->{name}} @{$test->{tables}} ],
425 "${t_name} table names match" );
426 foreach ( @{$test->{tables}} ) {
427 my $t_name = $_->{name} || die "Need a table name to test.";
428 table_ok( $obj->get_table($t_name), $_, $name );
429 }
430 }
431 else {
432 is(scalar($obj->get_tables), undef,
433 "${t_name} has no tables.");
434 }
435
436 # Procedures, Triggers, Views
437 _test_kids($obj, $test, $name, {
11fee3e0 438 procedure => 'procedures',
439 trigger => 'triggers',
440 view => 'views',
a0eb602d 441 });
9bbbf403 442}
443
49133ae7 444# maybe_plan($ntests, @modules)
445#
446# Calls plan $ntests if @modules can all be loaded; otherwise,
447# calls skip_all with an explanation of why the tests were skipped.
448sub maybe_plan {
449 my ($ntests, @modules) = @_;
450 my @errors;
451
452 for my $module (@modules) {
453 eval "use $module;";
559aa8a9 454 next if !$@;
455
456 if ($@ =~ /Can't locate (\S+)/) {
49133ae7 457 my $mod = $1;
458 $mod =~ s/\.pm$//;
459 $mod =~ s#/#::#g;
460 push @errors, $mod;
461 }
559aa8a9 462 elsif ($@ =~ /([\w\:]+ version [\d\.]+) required.+?this is only version/) {
463 push @errors, $1;
464 }
ac8d330a 465 elsif ($@ =~ /Can't load .+? for module .+?DynaLoader\.pm/i ) {
466 push @errors, $module;
467 }
c481687c 468 else {
9d226a8f 469 (my $err = $@) =~ s/\n+/\\n/g; # Can't have newlines in the skip message
470 push @errors, "$module: $err";
c481687c 471 }
49133ae7 472 }
473
474 if (@errors) {
475 my $msg = sprintf "Missing dependenc%s: %s",
476 @errors == 1 ? 'y' : 'ies',
477 join ", ", @errors;
478 plan skip_all => $msg;
479 }
0142de97 480 return unless defined $ntests;
481
482 if ($ntests ne 'no_plan') {
49133ae7 483 plan tests => $ntests;
484 }
dc34f950 485 else {
486 plan 'no_plan';
487 }
49133ae7 488}
489
9bbbf403 4901; # compile please ===========================================================
491__END__
492
493=pod
494
495=head1 SYNOPSIS
496
497 # t/magic.t
498
499 use FindBin '$Bin';
500 use Test::More;
501 use Test::SQL::Translator;
502
503 # Run parse
504 my $sqlt = SQL::Translator->new(
505 parser => "Magic",
506 filename => "$Bin/data/magic/test.magic",
e2a489c2 507 ...
9bbbf403 508 );
509 ...
510 my $schema = $sqlt->schema;
e2a489c2 511
9bbbf403 512 # Test the table it produced.
513 table_ok( $schema->get_table("Customer"), {
514 name => "Customer",
515 fields => [
516 {
517 name => "CustomerID",
518 data_type => "INT",
519 size => 12,
520 default_value => undef,
521 is_nullable => 0,
522 is_primary_key => 1,
523 },
524 {
525 name => "bar",
526 data_type => "VARCHAR",
527 size => 255,
528 is_nullable => 0,
529 },
530 ],
531 constraints => [
532 {
533 type => "PRIMARY KEY",
534 fields => "CustomerID",
535 },
536 ],
537 indices => [
538 {
539 name => "barindex",
540 fields => ["bar"],
541 },
542 ],
543 });
544
13eb146e 545=head1 DESCRIPTION
9bbbf403 546
e2a489c2 547Provides a set of Test::More tests for Schema objects. Testing a parsed
9bbbf403 548schema is then as easy as writing a perl data structure describing how you
9aabed4a 549expect the schema to look. Also provides C<maybe_plan> for conditionally running
e2a489c2 550tests based on their dependencies.
9bbbf403 551
e2a489c2 552The data structures given to the test subs don't have to include all the
9bbbf403 553possible values, only the ones you expect to have changed. Any left out will be
10f70490 554tested to make sure they are still at their default value. This is a useful
9bbbf403 555check that you your parser hasn't accidentally set schema values you didn't
e2a489c2 556expect it to.
9bbbf403 557
13eb146e 558For an example of the output run the F<t/16xml-parser.t> test.
9bbbf403 559
560=head1 Tests
561
e2a489c2 562All the tests take a first arg of the schema object to test, followed by a
9bbbf403 563hash ref describing how you expect that object to look (you only need give the
564attributes you expect to have changed from the default).
13eb146e 565The 3rd arg is an optional test name to prepend to all the generated test
9bbbf403 566names.
567
568=head2 table_ok
569
570=head2 field_ok
571
572=head2 constraint_ok
573
574=head2 index_ok
575
576=head2 view_ok
577
578=head2 trigger_ok
579
580=head2 procedure_ok
581
49133ae7 582=head1 CONDITIONAL TESTS
583
584The C<maybe_plan> function handles conditionally running an individual
585test. It is here to enable running the test suite even when dependencies
586are missing; not having (for example) GraphViz installed should not keep
587the test suite from passing.
588
589C<maybe_plan> takes the number of tests to (maybe) run, and a list of
590modules on which test execution depends:
591
592 maybe_plan(180, 'SQL::Translator::Parser::MySQL');
593
594If one of C<SQL::Translator::Parser::MySQL>'s dependencies does not exist,
595then the test will be skipped.
596
0142de97 597Instead of a number of tests, you can pass C<undef> if you're using
598C<done_testing()>, or C<'no_plan'> if you don't want a plan at all.
599
9bbbf403 600=head1 EXPORTS
601
49133ae7 602table_ok, field_ok, constraint_ok, index_ok, view_ok, trigger_ok, procedure_ok,
603maybe_plan
9bbbf403 604
605=head1 TODO
606
607=over 4
608
609=item Test the tests!
610
19458c2f 611=item Test Count Constants
612
9aabed4a 613Constants to give the number of tests each C<*_ok> sub uses. e.g. How many tests
614does C<field_ok> run? Can then use these to set up the test plan easily.
19458c2f 615
9bbbf403 616=item Test skipping
617
4b2d9113 618As the test subs wrap up lots of tests in one call you can't skip individual
9bbbf403 619tests only whole sets e.g. a whole table or field.
9aabed4a 620We could add C<skip_*> items to the test hashes to allow per test skips. e.g.
9bbbf403 621
622 skip_is_primary_key => "Need to fix primary key parsing.",
623
624=item yaml test specs
625
13eb146e 626Maybe have the test subs also accept yaml for the test hash ref as it is much
9bbbf403 627nicer for writing big data structures. We can then define tests as in input
628schema file and test yaml file to compare it against.
629
630=back
631
9bbbf403 632=head1 AUTHOR
633
ea93df61 634Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>,
11fee3e0 635Darren Chamberlain <darren@cpan.org>.
9bbbf403 636
11ad2df9 637Thanks to Ken Y. Clark for the original table and field test code taken from
638his mysql test.
9bbbf403 639
640=head1 SEE ALSO
641
642perl(1), SQL::Translator, SQL::Translator::Schema, Test::More.
643
644=cut