Pave the road to more global state
[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
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
b9a4fdae 182 if ( $left->[0] ne $right->[0] ) {
1b17d1b0 183 $sql_differ = sprintf "OP [$left->[0]] != [$right->[0]] in\nleft: %s\nright: %s\n",
a24cc3a0 184 $sqlat->unparse($left),
6f2a5b66 185 $sqlat->unparse($right)
186 ;
1b17d1b0 187 return 0;
188 }
6f2a5b66 189
190 # literals have a different arg-sig
191 elsif ($left->[0] eq '-LITERAL') {
192 (my $l = " $left->[1][0] " ) =~ s/\s+/ /g;
193 (my $r = " $right->[1][0] ") =~ s/\s+/ /g;
194 my $eq = $case_sensitive ? $l eq $r : uc($l) eq uc($r);
195 $sql_differ = "[$l] != [$r]\n" if not $eq;
196 return $eq;
197 }
198
199 # if operators are identical, compare operands
01dd4e4f 200 else {
6f2a5b66 201 my $eq = _eq_sql($left->[1], $right->[1]);
202 $sql_differ ||= sprintf ("left: %s\nright: %s\n", map { $sqlat->unparse ($_) } ($left, $right) ) if not $eq;
203 return $eq;
fffe6900 204 }
205 }
206}
207
7853a177 208sub parse { $sqlat->parse(@_) }
fffe6900 2091;
210
211
212__END__
213
214=head1 NAME
215
216SQL::Abstract::Test - Helper function for testing SQL::Abstract
217
218=head1 SYNOPSIS
219
220 use SQL::Abstract;
221 use Test::More;
e7827ba2 222 use SQL::Abstract::Test import => [qw/
223 is_same_sql_bind is_same_sql is_same_bind
224 eq_sql_bind eq_sql eq_bind
225 /];
ec9af79e 226
fffe6900 227 my ($sql, @bind) = SQL::Abstract->new->select(%args);
e7827ba2 228
01dd4e4f 229 is_same_sql_bind($given_sql, \@given_bind,
fffe6900 230 $expected_sql, \@expected_bind, $test_msg);
231
e7827ba2 232 is_same_sql($given_sql, $expected_sql, $test_msg);
233 is_same_bind(\@given_bind, \@expected_bind, $test_msg);
234
01dd4e4f 235 my $is_same = eq_sql_bind($given_sql, \@given_bind,
e7827ba2 236 $expected_sql, \@expected_bind);
237
238 my $sql_same = eq_sql($given_sql, $expected_sql);
239 my $bind_same = eq_bind(\@given_bind, \@expected_bind);
240
fffe6900 241=head1 DESCRIPTION
242
243This module is only intended for authors of tests on
244L<SQL::Abstract|SQL::Abstract> and related modules;
245it exports functions for comparing two SQL statements
246and their bound values.
247
248The SQL comparison is performed on I<abstract syntax>,
249ignoring differences in spaces or in levels of parentheses.
250Therefore the tests will pass as long as the semantics
251is preserved, even if the surface syntax has changed.
252
ec9af79e 253B<Disclaimer> : the semantic equivalence handling is pretty limited.
254A lot of effort goes into distinguishing significant from
255non-significant parenthesis, including AND/OR operator associativity.
256Currently this module does not support commutativity and more
257intelligent transformations like Morgan laws, etc.
258
01dd4e4f 259For a good overview of what this test framework is capable of refer
ec9af79e 260to C<t/10test.t>
fffe6900 261
262=head1 FUNCTIONS
263
264=head2 is_same_sql_bind
265
01dd4e4f 266 is_same_sql_bind($given_sql, \@given_bind,
fffe6900 267 $expected_sql, \@expected_bind, $test_msg);
268
269Compares given and expected pairs of C<($sql, \@bind)>, and calls
e7827ba2 270L<Test::Builder/ok> on the result, with C<$test_msg> as message. If the test
271fails, a detailed diagnostic is printed. For clients which use L<Test::More>,
272this is the one of the three functions (L</is_same_sql_bind>, L</is_same_sql>,
273L</is_same_bind>) that needs to be imported.
274
275=head2 is_same_sql
276
277 is_same_sql($given_sql, $expected_sql, $test_msg);
278
279Compares given and expected SQL statements, and calls L<Test::Builder/ok> on
280the result, with C<$test_msg> as message. If the test fails, a detailed
281diagnostic is printed. For clients which use L<Test::More>, this is the one of
282the three functions (L</is_same_sql_bind>, L</is_same_sql>, L</is_same_bind>)
283that needs to be imported.
284
285=head2 is_same_bind
286
287 is_same_bind(\@given_bind, \@expected_bind, $test_msg);
288
289Compares given and expected bind values, and calls L<Test::Builder/ok> on the
290result, with C<$test_msg> as message. If the test fails, a detailed diagnostic
291is printed. For clients which use L<Test::More>, this is the one of the three
292functions (L</is_same_sql_bind>, L</is_same_sql>, L</is_same_bind>) that needs
293to be imported.
294
295=head2 eq_sql_bind
296
01dd4e4f 297 my $is_same = eq_sql_bind($given_sql, \@given_bind,
e7827ba2 298 $expected_sql, \@expected_bind);
299
300Compares given and expected pairs of C<($sql, \@bind)>. Similar to
301L</is_same_sql_bind>, but it just returns a boolean value and does not print
302diagnostics or talk to L<Test::Builder>.
fffe6900 303
304=head2 eq_sql
305
306 my $is_same = eq_sql($given_sql, $expected_sql);
307
e7827ba2 308Compares the abstract syntax of two SQL statements. Similar to L</is_same_sql>,
309but it just returns a boolean value and does not print diagnostics or talk to
310L<Test::Builder>. If the result is false, the global variable L</$sql_differ>
311will contain the SQL portion where a difference was encountered; this is useful
312for printing diagnostics.
fffe6900 313
314=head2 eq_bind
315
316 my $is_same = eq_sql(\@given_bind, \@expected_bind);
317
e7827ba2 318Compares two lists of bind values, taking into account the fact that some of
319the values may be arrayrefs (see L<SQL::Abstract/bindtype>). Similar to
320L</is_same_bind>, but it just returns a boolean value and does not print
321diagnostics or talk to L<Test::Builder>.
fffe6900 322
323=head1 GLOBAL VARIABLES
324
e7827ba2 325=head2 $case_sensitive
fffe6900 326
327If true, SQL comparisons will be case-sensitive. Default is false;
328
e40f5df9 329=head2 $parenthesis_significant
330
331If true, SQL comparison will preserve and report difference in nested
48d9f5f8 332parenthesis. Useful while testing C<IN (( x ))> vs C<IN ( x )>.
333Defaults to false;
e40f5df9 334
e7827ba2 335=head2 $sql_differ
fffe6900 336
337When L</eq_sql> returns false, the global variable
338C<$sql_differ> contains the SQL portion
339where a difference was encountered.
340
341
342=head1 SEE ALSO
343
a6daa642 344L<SQL::Abstract>, L<Test::More>, L<Test::Builder>.
fffe6900 345
25823711 346=head1 AUTHORS
fffe6900 347
348Laurent Dami, E<lt>laurent.dami AT etat geneve chE<gt>
349
25823711 350Norbert Buchmuller <norbi@nix.hu>
351
e96c510a 352Peter Rabbitson <ribasushi@cpan.org>
353
fffe6900 354=head1 COPYRIGHT AND LICENSE
355
356Copyright 2008 by Laurent Dami.
357
358This library is free software; you can redistribute it and/or modify
01dd4e4f 359it under the same terms as Perl itself.