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