migrate _parenthesis_unroll to SQL::Abstract::Tree
[scpubgit/Q-Branch.git] / lib / SQL / Abstract / Test.pm
CommitLineData
fffe6900 1package SQL::Abstract::Test; # see doc at end of file
2
3use strict;
4use warnings;
5aad8cf3 5use base qw/Test::Builder::Module Exporter/;
fffe6900 6use Data::Dumper;
4abea32b 7use Test::Builder;
01dd4e4f 8use SQL::Abstract::Tree;
fffe6900 9
e7827ba2 10our @EXPORT_OK = qw/&is_same_sql_bind &is_same_sql &is_same_bind
01dd4e4f 11 &eq_sql_bind &eq_sql &eq_bind
fffe6900 12 $case_sensitive $sql_differ/;
13
a24cc3a0 14my $sqlat = SQL::Abstract::Tree->new;
15
fffe6900 16our $case_sensitive = 0;
e40f5df9 17our $parenthesis_significant = 0;
fffe6900 18our $sql_differ; # keeps track of differing portion between SQLs
5aad8cf3 19our $tb = __PACKAGE__->builder;
fffe6900 20
21sub is_same_sql_bind {
22 my ($sql1, $bind_ref1, $sql2, $bind_ref2, $msg) = @_;
23
24 # compare
25823711 25 my $same_sql = eq_sql($sql1, $sql2);
fffe6900 26 my $same_bind = eq_bind($bind_ref1, $bind_ref2);
27
a6daa642 28 # call Test::Builder::ok
1a828f61 29 my $ret = $tb->ok($same_sql && $same_bind, $msg);
fffe6900 30
31 # add debugging info
32 if (!$same_sql) {
e7827ba2 33 _sql_differ_diag($sql1, $sql2);
fffe6900 34 }
35 if (!$same_bind) {
e7827ba2 36 _bind_differ_diag($bind_ref1, $bind_ref2);
fffe6900 37 }
1a828f61 38
39 # pass ok() result further
40 return $ret;
fffe6900 41}
42
e7827ba2 43sub is_same_sql {
44 my ($sql1, $sql2, $msg) = @_;
45
46 # compare
47 my $same_sql = eq_sql($sql1, $sql2);
48
49 # call Test::Builder::ok
1a828f61 50 my $ret = $tb->ok($same_sql, $msg);
e7827ba2 51
52 # add debugging info
53 if (!$same_sql) {
54 _sql_differ_diag($sql1, $sql2);
55 }
1a828f61 56
57 # pass ok() result further
58 return $ret;
e7827ba2 59}
60
61sub is_same_bind {
62 my ($bind_ref1, $bind_ref2, $msg) = @_;
63
64 # compare
65 my $same_bind = eq_bind($bind_ref1, $bind_ref2);
66
67 # call Test::Builder::ok
1a828f61 68 my $ret = $tb->ok($same_bind, $msg);
e7827ba2 69
70 # add debugging info
71 if (!$same_bind) {
72 _bind_differ_diag($bind_ref1, $bind_ref2);
73 }
1a828f61 74
75 # pass ok() result further
76 return $ret;
e7827ba2 77}
78
79sub _sql_differ_diag {
80 my ($sql1, $sql2) = @_;
81
82 $tb->diag("SQL expressions differ\n"
83 ." got: $sql1\n"
84 ."expected: $sql2\n"
85 ."differing in :\n$sql_differ\n"
86 );
87}
88
89sub _bind_differ_diag {
90 my ($bind_ref1, $bind_ref2) = @_;
91
92 $tb->diag("BIND values differ\n"
93 ." got: " . Dumper($bind_ref1)
94 ."expected: " . Dumper($bind_ref2)
95 );
96}
97
98sub eq_sql_bind {
99 my ($sql1, $bind_ref1, $sql2, $bind_ref2) = @_;
100
101 return eq_sql($sql1, $sql2) && eq_bind($bind_ref1, $bind_ref2);
102}
103
104
fffe6900 105sub eq_bind {
106 my ($bind_ref1, $bind_ref2) = @_;
fffe6900 107
fdfbbc65 108 local $Data::Dumper::Useqq = 1;
109 local $Data::Dumper::Sortkeys = 1;
110
111 return Dumper($bind_ref1) eq Dumper($bind_ref2);
fffe6900 112}
113
114sub eq_sql {
25823711 115 my ($sql1, $sql2) = @_;
116
117 # parse
a24cc3a0 118 my $tree1 = $sqlat->parse($sql1);
119 my $tree2 = $sqlat->parse($sql2);
25823711 120
1b17d1b0 121 return 1 if _eq_sql($tree1, $tree2);
25823711 122}
123
124sub _eq_sql {
fffe6900 125 my ($left, $right) = @_;
126
939db550 127 # one is defined the other not
128 if ( (defined $left) xor (defined $right) ) {
129 return 0;
130 }
131 # one is undefined, then so is the other
132 elsif (not defined $left) {
d15c14cc 133 return 1;
134 }
0769ac0e 135 # different amount of elements
136 elsif (@$left != @$right) {
137 $sql_differ = sprintf ("left: %s\nright: %s\n", map { $sqlat->unparse ($_) } ($left, $right) );
138 return 0;
139 }
140 # one is empty - so is the other
141 elsif (@$left == 0) {
142 return 1;
143 }
1b17d1b0 144 # one is a list, the other is an op with a list
145 elsif (ref $left->[0] xor ref $right->[0]) {
a24cc3a0 146 $sql_differ = sprintf ("left: %s\nright: %s\n", map { $sqlat->unparse ($_) } ($left, $right) );
fffe6900 147 return 0;
148 }
1b17d1b0 149 # one is a list, so is the other
150 elsif (ref $left->[0]) {
151 for (my $i = 0; $i <= $#$left or $i <= $#$right; $i++ ) {
152 return 0 if (not _eq_sql ($left->[$i], $right->[$i]) );
153 }
154 return 1;
155 }
156 # both are an op-list combo
157 else {
158
e40f5df9 159 # unroll parenthesis if possible/allowed
bb54fcb4 160 $parenthesis_significant || $sqlat->_parenthesis_unroll($_) for $left, $right;
1b17d1b0 161
162 # if operators are different
b9a4fdae 163 if ( $left->[0] ne $right->[0] ) {
1b17d1b0 164 $sql_differ = sprintf "OP [$left->[0]] != [$right->[0]] in\nleft: %s\nright: %s\n",
a24cc3a0 165 $sqlat->unparse($left),
166 $sqlat->unparse($right);
1b17d1b0 167 return 0;
168 }
169 # elsif operators are identical, compare operands
01dd4e4f 170 else {
b9a4fdae 171 if ($left->[0] eq 'LITERAL' ) { # unary
01b64cb7 172 (my $l = " $left->[1][0] " ) =~ s/\s+/ /g;
173 (my $r = " $right->[1][0] ") =~ s/\s+/ /g;
1b17d1b0 174 my $eq = $case_sensitive ? $l eq $r : uc($l) eq uc($r);
01b64cb7 175 $sql_differ = "[$l] != [$r]\n" if not $eq;
1b17d1b0 176 return $eq;
177 }
178 else {
179 my $eq = _eq_sql($left->[1], $right->[1]);
a24cc3a0 180 $sql_differ ||= sprintf ("left: %s\nright: %s\n", map { $sqlat->unparse ($_) } ($left, $right) ) if not $eq;
1b17d1b0 181 return $eq;
182 }
fffe6900 183 }
184 }
185}
186
7853a177 187sub parse { $sqlat->parse(@_) }
fffe6900 1881;
189
190
191__END__
192
193=head1 NAME
194
195SQL::Abstract::Test - Helper function for testing SQL::Abstract
196
197=head1 SYNOPSIS
198
199 use SQL::Abstract;
200 use Test::More;
e7827ba2 201 use SQL::Abstract::Test import => [qw/
202 is_same_sql_bind is_same_sql is_same_bind
203 eq_sql_bind eq_sql eq_bind
204 /];
ec9af79e 205
fffe6900 206 my ($sql, @bind) = SQL::Abstract->new->select(%args);
e7827ba2 207
01dd4e4f 208 is_same_sql_bind($given_sql, \@given_bind,
fffe6900 209 $expected_sql, \@expected_bind, $test_msg);
210
e7827ba2 211 is_same_sql($given_sql, $expected_sql, $test_msg);
212 is_same_bind(\@given_bind, \@expected_bind, $test_msg);
213
01dd4e4f 214 my $is_same = eq_sql_bind($given_sql, \@given_bind,
e7827ba2 215 $expected_sql, \@expected_bind);
216
217 my $sql_same = eq_sql($given_sql, $expected_sql);
218 my $bind_same = eq_bind(\@given_bind, \@expected_bind);
219
fffe6900 220=head1 DESCRIPTION
221
222This module is only intended for authors of tests on
223L<SQL::Abstract|SQL::Abstract> and related modules;
224it exports functions for comparing two SQL statements
225and their bound values.
226
227The SQL comparison is performed on I<abstract syntax>,
228ignoring differences in spaces or in levels of parentheses.
229Therefore the tests will pass as long as the semantics
230is preserved, even if the surface syntax has changed.
231
ec9af79e 232B<Disclaimer> : the semantic equivalence handling is pretty limited.
233A lot of effort goes into distinguishing significant from
234non-significant parenthesis, including AND/OR operator associativity.
235Currently this module does not support commutativity and more
236intelligent transformations like Morgan laws, etc.
237
01dd4e4f 238For a good overview of what this test framework is capable of refer
ec9af79e 239to C<t/10test.t>
fffe6900 240
241=head1 FUNCTIONS
242
243=head2 is_same_sql_bind
244
01dd4e4f 245 is_same_sql_bind($given_sql, \@given_bind,
fffe6900 246 $expected_sql, \@expected_bind, $test_msg);
247
248Compares given and expected pairs of C<($sql, \@bind)>, and calls
e7827ba2 249L<Test::Builder/ok> on the result, with C<$test_msg> as message. If the test
250fails, a detailed diagnostic is printed. For clients which use L<Test::More>,
251this is the one of the three functions (L</is_same_sql_bind>, L</is_same_sql>,
252L</is_same_bind>) that needs to be imported.
253
254=head2 is_same_sql
255
256 is_same_sql($given_sql, $expected_sql, $test_msg);
257
258Compares given and expected SQL statements, and calls L<Test::Builder/ok> on
259the result, with C<$test_msg> as message. If the test fails, a detailed
260diagnostic is printed. For clients which use L<Test::More>, this is the one of
261the three functions (L</is_same_sql_bind>, L</is_same_sql>, L</is_same_bind>)
262that needs to be imported.
263
264=head2 is_same_bind
265
266 is_same_bind(\@given_bind, \@expected_bind, $test_msg);
267
268Compares given and expected bind values, and calls L<Test::Builder/ok> on the
269result, with C<$test_msg> as message. If the test fails, a detailed diagnostic
270is printed. For clients which use L<Test::More>, this is the one of the three
271functions (L</is_same_sql_bind>, L</is_same_sql>, L</is_same_bind>) that needs
272to be imported.
273
274=head2 eq_sql_bind
275
01dd4e4f 276 my $is_same = eq_sql_bind($given_sql, \@given_bind,
e7827ba2 277 $expected_sql, \@expected_bind);
278
279Compares given and expected pairs of C<($sql, \@bind)>. Similar to
280L</is_same_sql_bind>, but it just returns a boolean value and does not print
281diagnostics or talk to L<Test::Builder>.
fffe6900 282
283=head2 eq_sql
284
285 my $is_same = eq_sql($given_sql, $expected_sql);
286
e7827ba2 287Compares the abstract syntax of two SQL statements. Similar to L</is_same_sql>,
288but it just returns a boolean value and does not print diagnostics or talk to
289L<Test::Builder>. If the result is false, the global variable L</$sql_differ>
290will contain the SQL portion where a difference was encountered; this is useful
291for printing diagnostics.
fffe6900 292
293=head2 eq_bind
294
295 my $is_same = eq_sql(\@given_bind, \@expected_bind);
296
e7827ba2 297Compares two lists of bind values, taking into account the fact that some of
298the values may be arrayrefs (see L<SQL::Abstract/bindtype>). Similar to
299L</is_same_bind>, but it just returns a boolean value and does not print
300diagnostics or talk to L<Test::Builder>.
fffe6900 301
302=head1 GLOBAL VARIABLES
303
e7827ba2 304=head2 $case_sensitive
fffe6900 305
306If true, SQL comparisons will be case-sensitive. Default is false;
307
e40f5df9 308=head2 $parenthesis_significant
309
310If true, SQL comparison will preserve and report difference in nested
311parenthesis. Useful for testing the C<-nest> modifier. Defaults to false;
312
e7827ba2 313=head2 $sql_differ
fffe6900 314
315When L</eq_sql> returns false, the global variable
316C<$sql_differ> contains the SQL portion
317where a difference was encountered.
318
319
320=head1 SEE ALSO
321
a6daa642 322L<SQL::Abstract>, L<Test::More>, L<Test::Builder>.
fffe6900 323
25823711 324=head1 AUTHORS
fffe6900 325
326Laurent Dami, E<lt>laurent.dami AT etat geneve chE<gt>
327
25823711 328Norbert Buchmuller <norbi@nix.hu>
329
e96c510a 330Peter Rabbitson <ribasushi@cpan.org>
331
fffe6900 332=head1 COPYRIGHT AND LICENSE
333
334Copyright 2008 by Laurent Dami.
335
336This library is free software; you can redistribute it and/or modify
01dd4e4f 337it under the same terms as Perl itself.