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