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