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