1 package SQL::Abstract::Test; # see doc at end of file
5 use base qw(Test::Builder::Module Exporter);
8 use SQL::Abstract::Tree;
12 if ($class = $ENV{SQL_ABSTRACT_TEST_AGAINST}) {
13 my $mod = join('/', split '::', $class).".pm";
15 eval qq{sub SQL::Abstract () { "\Q${class}\E" }; 1}
16 or die "Failed to create const sub for ${class}: $@";
18 if ($ENV{SQL_ABSTRACT_TEST_EXPAND_STABILITY}) {
19 $class ||= do { require SQL::Abstract; 'SQL::Abstract' };
20 my $orig = $class->can('expand_expr');
21 require Data::Dumper::Concise;
23 my ($self, @args) = @_;
24 my $e1 = $self->$orig(@args);
25 return $e1 if our $Stab_Check_Rec;
26 local $Stab_Check_Rec = 1;
27 my $e2 = $self->$orig($e1);
28 my ($d1, $d2) = map Data::Dumper::Concise::Dumper($_), $e1, $e2;
31 'expand_expr stability ok'
34 Path::Tiny->new('e1')->spew($d1);
35 Path::Tiny->new('e2')->spew($d2);
36 system('diff -u e1 e2 1>&2');
37 die "Differences between e1 and e2, bailing out";
41 no strict 'refs'; no warnings 'redefine';
42 *{"${class}::expand_expr"} = $wrapped;
47 is_same_sql_bind is_same_sql is_same_bind
48 eq_sql_bind eq_sql eq_bind dumper diag_where
49 $case_sensitive $sql_differ
52 my $sqlat = SQL::Abstract::Tree->new;
54 our $case_sensitive = 0;
55 our $parenthesis_significant = 0;
56 our $order_by_asc_significant = 0;
58 our $sql_differ; # keeps track of differing portion between SQLs
59 our $tb = __PACKAGE__->builder;
61 sub _unpack_arrayrefref {
67 if (ref $chunk eq 'REF' and ref $$chunk eq 'ARRAY') {
68 my ($sql, @bind) = @$$chunk;
69 push @args, ($sql, \@bind);
72 push @args, $chunk, shift @_;
77 # maybe $msg and ... stuff
83 sub is_same_sql_bind {
84 my ($sql1, $bind_ref1, $sql2, $bind_ref2, $msg) = &_unpack_arrayrefref;
87 my $same_sql = eq_sql($sql1, $sql2);
88 my $same_bind = eq_bind($bind_ref1, $bind_ref2);
90 # call Test::Builder::ok
91 my $ret = $tb->ok($same_sql && $same_bind, $msg);
95 _sql_differ_diag($sql1, $sql2);
98 _bind_differ_diag($bind_ref1, $bind_ref2);
101 # pass ok() result further
106 my ($sql1, $sql2, $msg) = @_;
109 my $same_sql = eq_sql($sql1, $sql2);
111 # call Test::Builder::ok
112 my $ret = $tb->ok($same_sql, $msg);
116 _sql_differ_diag($sql1, $sql2);
119 # pass ok() result further
124 my ($bind_ref1, $bind_ref2, $msg) = @_;
127 my $same_bind = eq_bind($bind_ref1, $bind_ref2);
129 # call Test::Builder::ok
130 my $ret = $tb->ok($same_bind, $msg);
134 _bind_differ_diag($bind_ref1, $bind_ref2);
137 # pass ok() result further
143 # if we save the instance, we will end up with $VARx references
144 # no time to figure out how to avoid this (Deepcopy is *not* an option)
145 require Data::Dumper;
146 Data::Dumper->new([])->Terse(1)->Indent(1)->Useqq(1)->Deparse(1)->Quotekeys(0)->Sortkeys(1)->Maxdepth(0)
147 ->Values([@_])->Dump;
151 $tb->diag("Search term:\n" . &dumper);
154 sub _sql_differ_diag {
155 my $sql1 = shift || '';
156 my $sql2 = shift || '';
158 if (my $profile = $ENV{SQL_ABSTRACT_TEST_TREE_PROFILE}) {
159 my $sqlat = SQL::Abstract::Tree->new(profile => $profile);
160 $_ = $sqlat->format($_) for ($sql1, $sql2);
163 $tb->${\($tb->in_todo ? 'note' : 'diag')} (
164 "SQL expressions differ\n"
167 ."\nmismatch around\n$sql_differ\n"
171 sub _bind_differ_diag {
172 my ($bind_ref1, $bind_ref2) = @_;
174 $tb->${\($tb->in_todo ? 'note' : 'diag')} (
175 "BIND values differ " . dumper({ got => $bind_ref1, want => $bind_ref2 })
180 my ($sql1, $bind_ref1, $sql2, $bind_ref2) = &_unpack_arrayrefref;
182 return eq_sql($sql1, $sql2) && eq_bind($bind_ref1, $bind_ref2);
186 sub eq_bind { goto &Test::Deep::eq_deeply };
189 my ($sql1, $sql2) = @_;
192 my $tree1 = $sqlat->parse($sql1);
193 my $tree2 = $sqlat->parse($sql2);
196 return 1 if _eq_sql($tree1, $tree2);
200 my ($left, $right) = @_;
202 # one is defined the other not
203 if ((defined $left) xor (defined $right)) {
204 $sql_differ = sprintf ("[%s] != [%s]\n", map { defined $_ ? $sqlat->unparse($_) : 'N/A' } ($left, $right) );
208 # one is undefined, then so is the other
209 elsif (not defined $left) {
214 elsif (@$left == 0 and @$right == 0) {
219 if (@$left == 0 or @$right == 0) {
220 $sql_differ = sprintf ("left: %s\nright: %s\n", map { @$_ ? $sqlat->unparse($_) : 'N/A'} ($left, $right) );
224 # one is a list, the other is an op with a list
225 elsif (ref $left->[0] xor ref $right->[0]) {
226 $sql_differ = sprintf ("[%s] != [%s]\nleft: %s\nright: %s\n", map
227 { ref $_ ? $sqlat->unparse($_) : $_ }
228 ($left->[0], $right->[0], $left, $right)
234 elsif (ref $left->[0]) {
235 for (my $i = 0; $i <= $#$left or $i <= $#$right; $i++ ) {
236 if (not _eq_sql ($left->[$i], $right->[$i]) ) {
237 if (! $sql_differ or $sql_differ !~ /left\:\s .+ right:\s/xs) {
239 $sql_differ .= "\n" unless $sql_differ =~ /\n\z/;
240 $sql_differ .= sprintf ("left: %s\nright: %s\n", map { $sqlat->unparse($_) } ($left, $right) );
251 # unroll parenthesis if possible/allowed
252 unless ($parenthesis_significant) {
253 $sqlat->_parenthesis_unroll($_) for $left, $right;
256 # unroll ASC order by's
257 unless ($order_by_asc_significant) {
258 $sqlat->_strip_asc_from_order_by($_) for $left, $right;
261 if ($left->[0] ne $right->[0]) {
262 $sql_differ = sprintf "OP [$left->[0]] != [$right->[0]] in\nleft: %s\nright: %s\n",
263 $sqlat->unparse($left),
264 $sqlat->unparse($right)
269 # literals have a different arg-sig
270 elsif ($left->[0] eq '-LITERAL') {
271 (my $l = " $left->[1][0] " ) =~ s/\s+/ /g;
272 (my $r = " $right->[1][0] ") =~ s/\s+/ /g;
273 my $eq = $case_sensitive ? $l eq $r : uc($l) eq uc($r);
274 $sql_differ = "[$l] != [$r]\n" if not $eq;
278 # if operators are identical, compare operands
280 my $eq = _eq_sql($left->[1], $right->[1]);
281 $sql_differ ||= sprintf ("left: %s\nright: %s\n", map { $sqlat->unparse($_) } ($left, $right) ) if not $eq;
287 sub parse { $sqlat->parse(@_) }
295 SQL::Abstract::Test - Helper function for testing SQL::Abstract
301 use SQL::Abstract::Test import => [qw/
302 is_same_sql_bind is_same_sql is_same_bind
303 eq_sql_bind eq_sql eq_bind
306 my ($sql, @bind) = SQL::Abstract->new->select(%args);
308 is_same_sql_bind($given_sql, \@given_bind,
309 $expected_sql, \@expected_bind, $test_msg);
311 is_same_sql($given_sql, $expected_sql, $test_msg);
312 is_same_bind(\@given_bind, \@expected_bind, $test_msg);
314 my $is_same = eq_sql_bind($given_sql, \@given_bind,
315 $expected_sql, \@expected_bind);
317 my $sql_same = eq_sql($given_sql, $expected_sql);
318 my $bind_same = eq_bind(\@given_bind, \@expected_bind);
322 This module is only intended for authors of tests on
323 L<SQL::Abstract|SQL::Abstract> and related modules;
324 it exports functions for comparing two SQL statements
325 and their bound values.
327 The SQL comparison is performed on I<abstract syntax>,
328 ignoring differences in spaces or in levels of parentheses.
329 Therefore the tests will pass as long as the semantics
330 is preserved, even if the surface syntax has changed.
332 B<Disclaimer> : the semantic equivalence handling is pretty limited.
333 A lot of effort goes into distinguishing significant from
334 non-significant parenthesis, including AND/OR operator associativity.
335 Currently this module does not support commutativity and more
336 intelligent transformations like L<De Morgan's laws
337 |http://en.wikipedia.org/wiki/De_Morgan's_laws>, etc.
339 For a good overview of what this test framework is currently capable of refer
344 =head2 is_same_sql_bind
347 $given_sql, \@given_bind,
348 $expected_sql, \@expected_bind,
353 \[$given_sql, @given_bind],
354 \[$expected_sql, @expected_bind],
360 $expected_sql, \@expected_bind,
364 Compares given and expected pairs of C<($sql, \@bind)> by unpacking C<@_>
365 as shown in the examples above and passing the arguments to L</eq_sql> and
366 L</eq_bind>. Calls L<Test::Builder/ok> with the combined result, with
367 C<$test_msg> as message.
368 If the test fails, a detailed diagnostic is printed.
378 Compares given and expected SQL statements via L</eq_sql>, and calls
379 L<Test::Builder/ok> on the result, with C<$test_msg> as message.
380 If the test fails, a detailed diagnostic is printed.
390 Compares given and expected bind values via L</eq_bind>, and calls
391 L<Test::Builder/ok> on the result, with C<$test_msg> as message.
392 If the test fails, a detailed diagnostic is printed.
396 my $is_same = eq_sql_bind(
397 $given_sql, \@given_bind,
398 $expected_sql, \@expected_bind,
401 my $is_same = eq_sql_bind(
402 \[$given_sql, @given_bind],
403 \[$expected_sql, @expected_bind],
406 my $is_same = eq_sql_bind(
408 $expected_sql, \@expected_bind,
411 Unpacks C<@_> depending on the given arguments and calls L</eq_sql> and
412 L</eq_bind>, returning their combined result.
416 my $is_same = eq_sql($given_sql, $expected_sql);
418 Compares the abstract syntax of two SQL statements. Similar to L</is_same_sql>,
419 but it just returns a boolean value and does not print diagnostics or talk to
420 L<Test::Builder>. If the result is false, the global variable L</$sql_differ>
421 will contain the SQL portion where a difference was encountered; this is useful
422 for printing diagnostics.
426 my $is_same = eq_sql(\@given_bind, \@expected_bind);
428 Compares two lists of bind values, taking into account the fact that some of
429 the values may be arrayrefs (see L<SQL::Abstract/bindtype>). Similar to
430 L</is_same_bind>, but it just returns a boolean value and does not print
431 diagnostics or talk to L<Test::Builder>.
433 =head1 GLOBAL VARIABLES
435 =head2 $case_sensitive
437 If true, SQL comparisons will be case-sensitive. Default is false;
439 =head2 $parenthesis_significant
441 If true, SQL comparison will preserve and report difference in nested
442 parenthesis. Useful while testing C<IN (( x ))> vs C<IN ( x )>.
445 =head2 $order_by_asc_significant
447 If true SQL comparison will consider C<ORDER BY foo ASC> and
448 C<ORDER BY foo> to be different. Default is false;
452 When L</eq_sql> returns false, the global variable
453 C<$sql_differ> contains the SQL portion
454 where a difference was encountered.
458 L<SQL::Abstract>, L<Test::More>, L<Test::Builder>.
462 Laurent Dami <laurent.dami AT etat geneve ch>
464 Norbert Buchmuller <norbi@nix.hu>
466 Peter Rabbitson <ribasushi@cpan.org>
468 =head1 COPYRIGHT AND LICENSE
470 Copyright 2008 by Laurent Dami.
472 This library is free software; you can redistribute it and/or modify
473 it under the same terms as Perl itself.