remove refkind stuff from insert
[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;
ba3f5e40 5use base qw(Test::Builder::Module);
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
70af236b 23our $tb; # not documented, but someone might be overriding it anyway
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
70af236b 55 my $tb = $tb || __PACKAGE__->builder;
1a828f61 56 my $ret = $tb->ok($same_sql && $same_bind, $msg);
fffe6900 57
58 # add debugging info
59 if (!$same_sql) {
e7827ba2 60 _sql_differ_diag($sql1, $sql2);
fffe6900 61 }
62 if (!$same_bind) {
e7827ba2 63 _bind_differ_diag($bind_ref1, $bind_ref2);
fffe6900 64 }
1a828f61 65
66 # pass ok() result further
67 return $ret;
fffe6900 68}
69
e7827ba2 70sub is_same_sql {
71 my ($sql1, $sql2, $msg) = @_;
72
73 # compare
70c6f0e9 74 my $same_sql = eq_sql($sql1, $sql2);
e7827ba2 75
76 # call Test::Builder::ok
70af236b 77 my $tb = $tb || __PACKAGE__->builder;
1a828f61 78 my $ret = $tb->ok($same_sql, $msg);
e7827ba2 79
80 # add debugging info
81 if (!$same_sql) {
82 _sql_differ_diag($sql1, $sql2);
83 }
1a828f61 84
85 # pass ok() result further
86 return $ret;
e7827ba2 87}
88
89sub is_same_bind {
90 my ($bind_ref1, $bind_ref2, $msg) = @_;
91
92 # compare
93 my $same_bind = eq_bind($bind_ref1, $bind_ref2);
94
95 # call Test::Builder::ok
70af236b 96 my $tb = $tb || __PACKAGE__->builder;
1a828f61 97 my $ret = $tb->ok($same_bind, $msg);
e7827ba2 98
99 # add debugging info
100 if (!$same_bind) {
101 _bind_differ_diag($bind_ref1, $bind_ref2);
102 }
1a828f61 103
104 # pass ok() result further
105 return $ret;
e7827ba2 106}
107
2fadf08e 108sub dumper {
70c6f0e9 109 # FIXME
110 # if we save the instance, we will end up with $VARx references
111 # no time to figure out how to avoid this (Deepcopy is *not* an option)
112 require Data::Dumper;
113 Data::Dumper->new([])->Terse(1)->Indent(1)->Useqq(1)->Deparse(1)->Quotekeys(0)->Sortkeys(1)->Maxdepth(0)
114 ->Values([@_])->Dump;
2fadf08e 115}
116
117sub diag_where{
70af236b 118 my $tb = $tb || __PACKAGE__->builder;
ca4f826a 119 $tb->diag("Search term:\n" . &dumper);
2fadf08e 120}
121
e7827ba2 122sub _sql_differ_diag {
70c6f0e9 123 my $sql1 = shift || '';
124 my $sql2 = shift || '';
e7827ba2 125
70af236b 126 my $tb = $tb || __PACKAGE__->builder;
9a9b40f8 127
128 if (my $profile = $ENV{SQL_ABSTRACT_TEST_TREE_PROFILE}) {
129 my $sqlat = SQL::Abstract::Tree->new(profile => $profile);
130 $_ = $sqlat->format($_) for ($sql1, $sql2);
131 }
132
ca4f826a 133 $tb->${\($tb->in_todo ? 'note' : 'diag')} (
b3437526 134 "SQL expressions differ\n"
70c6f0e9 135 ." got: $sql1\n"
136 ."want: $sql2\n"
137 ."\nmismatch around\n$sql_differ\n"
b3437526 138 );
e7827ba2 139}
140
141sub _bind_differ_diag {
142 my ($bind_ref1, $bind_ref2) = @_;
143
e026e02a 144 my $tb = $tb || __PACKAGE__->builder;
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.