From: Justin Hunter Date: Mon, 17 Aug 2009 18:33:39 +0000 (-0700) Subject: roundtrip test X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=ecc147736f565116e517a19c6f78f9aebfdc8974;hp=70ada8ac7ae70e09134d3b4886d858d791c557b0;p=dbsrgits%2FSQL-Translator-2.0-ish.git roundtrip test --- diff --git a/t/60roundtrip.t b/t/60roundtrip.t new file mode 100644 index 0000000..246d779 --- /dev/null +++ b/t/60roundtrip.t @@ -0,0 +1,253 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use Test::More qw/no_plan/; +use Test::Exception; +use Test::Differences; +use FindBin qw/$Bin/; + +use SQL::Translator; +$Carp::Internal{'MooseX::Method::Signatures::Meta::Method'} = 1; +### Set $ENV{SQLTTEST_RT_DEBUG} = 1 for more output + +# What tests to run - parser/producer name, and optional args +my $plan = [ +# { +# engine => 'XML', +# req => 'XML::LibXML', +# }, + { + engine => 'YAML', + }, +# { +# engine => 'SQLite', +# producer_args => {}, +# parser_args => {}, +# }, +# { +# engine => 'MySQL', +# producer_args => {}, +# parser_args => {}, +# }, +# { +# engine => 'MySQL', +# name => 'MySQL 5.0', +# producer_args => { mysql_version => 5 }, +# parser_args => { mysql_parser_version => 5 }, +# }, +# { +# engine => 'MySQL', +# name => 'MySQL 5.1', +# producer_args => { mysql_version => '5.1' }, +# parser_args => { mysql_parser_version => '5.1' }, +# }, +# { +# engine => 'PostgreSQL', +# producer_args => {}, +# parser_args => {}, +# }, +# { +# engine => 'SQLServer', +# producer_args => {}, +# parser_args => {}, +# }, + +# { +# engine => 'Oracle', +# producer_args => {}, +# parser_args => {}, +# todo => 'Needs volunteers', +# }, +# { +# engine => 'Sybase', +# producer_args => {}, +# parser_args => {}, +# todo => 'Needs volunteers', +# }, +# { +# engine => 'DB2', +# producer_args => {}, +# parser_args => {}, +# todo => 'Needs volunteers', +# }, + +# There is no Access producer +# { +# engine => 'Access', +# producer_args => {}, +# parser_args => {}, +# }, +]; + + +# This data file has the right mix of table/view/procedure/trigger +# definitions, and lists enough quirks to trip up most combos +my $base_file = "$Bin/data/roundtrip_autogen.yaml"; +open (my $base_fh, '<', $base_file) or die "$base_file: $!"; + +my $base_t = SQL::Translator->new; +#$base_t->$_ (1) for qw/add_drop_table no_comments/; + +my $base_schema = $base_t->translate ( + parser => 'YAML', + data => do { local $/; <$base_fh>; }, +) or die $base_t->error; + +#assume there is at least one table +my $string_re = { + XML => qr/\s* qr/\A---\n.+tables\:/s, + SQL => qr/^\s*CREATE TABLE/m, +}; + +for my $args (@$plan) { + SKIP: { + $args->{name} ||= $args->{engine}; + + my @req = ref $args->{req} ? @{$args->{req}} : $args->{req}||(); + my @missing; + for (@req) { + eval "require $_"; + push @missing, $_ if ($@); + } + if (@missing) { + skip sprintf ('Need %s for %s roundtrip test', + join (', ', @missing), + $args->{name}, + ); + } + + TODO: { + local $TODO = $args->{todo} if $args->{todo}; + + lives_ok ( +# sub { check_roundtrip ($args, $base_schema) }, + sub { check_roundtrip ($args, $base_t, $base_schema) }, +# sub { check_roundtrip ($args, $base_schema) }, + "Round trip for $args->{name} did not throw an exception", + ); + } + } +} + + +sub check_roundtrip { +# my ($args, $base_schema) = @_; +# my $base_t = $base_schema->translator; + my ($args, $base_t, $base_schema) = @_; + +# create some output from the submitted schema + my $base_out = $base_t->translate ( + data => $base_schema, + producer => $args->{engine}, +# producer_args => $args->{producer_args}, + ); + + like ( + $base_out, + $string_re->{$args->{engine}} || $string_re->{SQL}, + "Received some meaningful output from the first $args->{name} production", + ) or do { + diag ( _gen_diag ($base_t->error) ); + return; + }; + +# parse the sql back + my $parser_t = SQL::Translator->new({ parser => 'YAML' }); + #$parser_t->$_ (1) for qw/add_drop_table no_comments/; + my $mid_schema = $parser_t->translate ( + data => $base_out, + parser => $args->{engine}, +# parser_args => $args->{parser_args}, + ); +warn "MID SCHEMA FIRST (PRODUCED) BASE SCHEMA SECOND (PARSED)"; + isa_ok ($mid_schema, 'SQL::Translator::Object::Schema', "First $args->{name} parser pass produced a schema:") + or do { + diag (_gen_diag ( $parser_t->error, $base_out ) ); + return; + }; + +# schemas should be comparable at least as far as table/field numbers go + is_deeply ( + _get_table_info ($mid_schema->get_tables), + _get_table_info ($base_schema->get_tables), + "Schema tables generally match afer $args->{name} parser trip", + ) or return; + +# and produce sql once again + +# Producing a schema with a Translator different from the one the schema was generated +# from does not work. This is arguably a bug, 61translator_agnostic.t works with that +# my $producer_t = SQL::Translator->new; +# $producer_t->$_ (1) for qw/add_drop_table no_comments/; + +# my $rt_sql = $producer_t->translate ( +# data => $mid_schema, +# producer => $args->{engine}, +# producer_args => $args->{producer_args}, +# ); + + my $rt_out = $parser_t->translate ( + data => $mid_schema, + producer => $args->{engine}, +# producer_args => $args->{producer_args}, + ); + + like ( + $rt_out, + $string_re->{$args->{engine}} || $string_re->{SQL}, + "Received some meaningful output from the second $args->{name} production", + ) or do { + diag ( _gen_diag ( $parser_t->error ) ); + return; + }; + +# the two sql strings should be identical + my $msg = "$args->{name} SQL roundtrip successful - SQL statements match"; + $ENV{SQLTTEST_RT_DEBUG} #stringify below because IO::Scalar does not behave nice + ? eq_or_diff ("$rt_out", "$base_out", $msg) + : ok ("$rt_out" eq "$base_out", $msg) + ; +} + +sub _get_table_info { + my @tables = @_; + + my @info; + + for my $t (@tables) { warn $t->name . "\t" . (join ', ', map { $_->name } $t->get_fields) . "\n"; + push @info, { + name => $t->name, + fields => [ + map { $_->name } ($t->get_fields), + ], + }; + } + + return \@info; +} + +# takes an error string and an optional output block +# returns the string conctenated with a line-numbered block for +# easier reading +sub _gen_diag { + my ($err, $out) = @_; + + return 'Unknown error' unless $err; + + + if ($out and $ENV{SQLTTEST_RT_DEBUG}) { + my @lines; + for (split /\n/, $out) { + push @lines, sprintf ('%03d: %s', + scalar @lines + 1, + $_, + ); + } + + return "$err\n\n" . join ("\n", @lines); + } + + return $err; +} diff --git a/t/data/roundtrip_autogen.yaml b/t/data/roundtrip_autogen.yaml new file mode 100644 index 0000000..1eeb3f3 --- /dev/null +++ b/t/data/roundtrip_autogen.yaml @@ -0,0 +1,260 @@ +--- +schema: + procedures: + foo_proc: + comments: Go Sox! + extra: + bar: baz + foo: bar + hello: world + name: foo_proc + order: 1 + owner: Nomar + parameters: + - foo + - bar + sql: select foo from bar + tables: + Another: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + extra: + bar: baz + foo: bar + hello: world + mysql_table_type: InnoDB + fields: + id: + data_type: int + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 10 + indices: [] + name: Another + options: [] + order: 2 + Basic: + constraints: + - deferrable: 1 + expression: '' + extra: + bar: baz + foo: bar + hello: world + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - email + match_type: '' + name: emailuniqueindex + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 1 + expression: '' + fields: + - another_id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: + - id + reference_table: Another + type: FOREIGN KEY + extra: + bar: baz + foo: bar + hello: world + mysql_table_type: InnoDB + fields: + another_id: + data_type: int + default_value: 2 + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: another_id + order: 8 + size: + - 10 + description: + data_type: text + default_value: '' + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: description + order: 3 + size: + - 0 + email: + data_type: varchar + default_value: ~ + extra: + bar: baz + foo: bar + hello: world + is_nullable: 1 + is_primary_key: 0 + is_unique: 1 + name: email + order: 4 + size: + - 500 + emptytagdef: + comments: Hello emptytagdef + data_type: varchar + default_value: '' + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: emptytagdef + order: 7 + size: + - 0 + explicitemptystring: + data_type: varchar + default_value: '' + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: explicitemptystring + order: 6 + size: + - 0 + explicitnulldef: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: explicitnulldef + order: 5 + size: + - 0 + id: + data_type: int + default_value: ~ + extra: + ZEROFILL: 1 + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 10 + timest: + data_type: timestamp + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: timest + order: 9 + size: + - 0 + title: + data_type: varchar + default_value: hello + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: title + order: 2 + size: + - 100 + indices: + - extra: + bar: baz + foo: bar + hello: world + fields: + - title + name: titleindex + options: [] + type: NORMAL + name: Basic + options: [] + order: 1 + triggers: + bar_trigger: + action: update modified2=timestamp(); + database_events: + - insert + - update + extra: + hello: aliens + fields: ~ + name: bar_trigger + on_table: Basic + order: 2 + perform_action_when: before + foo_trigger: + action: update modified=timestamp(); + database_events: + - insert + extra: + bar: baz + foo: bar + hello: world + fields: ~ + name: foo_trigger + on_table: Basic + order: 1 + perform_action_when: after + views: + email_list: + extra: + bar: baz + foo: bar + hello: world + fields: + - email + name: email_list + order: 1 + sql: SELECT email FROM Basic WHERE (email IS NOT NULL) +translator: + add_drop_table: 0 + filename: t/data/roundtrip.xml + no_comments: 0 + parser_args: {} + parser_type: SQL::Translator::Parser::XML + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 0.09007