File Coverage

blib/lib/Test/LectroTest/Compat.pm
Criterion Covered Total %
statement 60 65 92.3
branch 13 22 59.0
condition 2 6 33.3
subroutine 13 13 100.0
pod 1 1 100.0
total 89 107 83.1


line stmt bran cond sub pod time code
1             package Test::LectroTest::Compat;
2             {
3             $Test::LectroTest::Compat::VERSION = '0.5001';
4             }
5              
6 1     1   86686 use warnings;
  1         4  
  1         61  
7 1     1   10 use strict;
  1         2  
  1         59  
8              
9 1     1   103637 use Filter::Util::Call;
  1         225992  
  1         217  
10 1     1   2570 use Test::Builder;
  1         12838  
  1         35  
11 1     1   237844 use Test::LectroTest::TestRunner;
  1         5  
  1         105  
12             require Test::LectroTest::Property;
13             require Test::LectroTest::Generator;
14              
15             =head1 NAME
16              
17             Test::LectroTest::Compat - Use LectroTest property checks in a Test::Simple world
18              
19             =head1 VERSION
20              
21             version 0.5001
22              
23             =head1 SYNOPSIS
24              
25             #!/usr/bin/perl -w
26              
27             use MyModule; # contains code we want to test
28             use Test::More tests => 2;
29             use Test::LectroTest::Compat;
30              
31             # property specs can now use Test::Builder-based
32             # tests such as Test::More's cmp_ok()
33              
34             my $prop_nonnegative = Property {
35             ##[ x <- Int, y <- Int ]##
36             cmp_ok(MyModule::my_function( $x, $y ), '>=', 0);
37             }, name => "my_function output is non-negative" ;
38              
39             # and we can now check whether properties hold
40             # as a Test::Builder-style test that integrates
41             # with other T::B tests
42              
43             holds( $prop_nonnegative ); # test whether prop holds
44             cmp_ok( 0, '<', 1, "trivial 0<1 test" ); # a "normal" test
45              
46             =head1 DESCRIPTION
47              
48             This module lets you use mix LectroTest property checking with other
49             popular Test::* modules. With it, you can use C- and
50             C-style assertions from Test::* modules within your LectroTest
51             property specifications and you can check LectroTest properties as
52             part of a Test::Simple or Test::More test plan. (You can actually
53             take advantage of any module based on Test::Builder, not just
54             Test::Simple and Test::More.)
55              
56             The module exports a single function C which is described
57             below.
58              
59             =head2 holds(I, I...)
60              
61             holds( $prop_nonnegative ); # check prop_nonnegative
62              
63             holds( $prop_nonnegative, trials => 100 );
64              
65             holds(
66             Property {
67             ##[ x <- Int ]##
68             my_function2($x) < 0;
69             }, name => "my_function2 is non-positive"
70             );
71              
72             Checks whether the given property holds.
73              
74             When called, this method creates a new
75             Test::LectroTest::TestRunner, asks the TestRunner to check the
76             property, and then reports the result to Test::Builder, which in
77             turn reports to you as part of a typical Test::Simple- or
78             Test::More-style test plan. Any options you provide to C after
79             the property will be passed to the C so you can change the
80             number of trials to run and so on. (See the docs for C in
81             L for the complete list of
82             options.)
83              
84              
85             =head1 TESTING FOR REGRESSIONS AND CORNER CASES
86              
87             LectroTest can record failure-causing test cases to a file, and it can
88             play those test cases back as part of its normal testing strategy.
89             The easiest way to take advantage of this feature is to set the
90             I parameter when you C this module:
91              
92             use Test::LectroTest::Compat
93             regressions => "regressions.txt";
94              
95             This tells LectroTest to use the file "regressions.txt" for both
96             recording and playing back failures. If you want to record and
97             play back from separate files, or want only to record I play
98             back, use the I and/or
99             I options:
100              
101             use Test::LectroTest::Compat
102             playback_failures => "regression_suite_for_my_module.txt",
103             record_failures => "failures_in_the_field.txt";
104              
105             See L for more.
106              
107             B If you pass any of the recording or playback parameters
108             to Test::LectroTest::Compat, you must have version 0.3500 or
109             greater of LectroTest installed. Module authors, update your
110             modules' build dependencies accordingly.
111              
112              
113             =cut
114              
115             my $Test = Test::Builder->new();
116              
117             sub import {
118 1     1   10 my $self = shift;
119 1         2 my $caller = caller;
120 1     1   7 { no strict 'refs'; *{$caller.'::holds'} = \&holds; }
  1         2  
  1         407  
  1         1  
  1         3  
  1         4  
121 1         6 $Test->exported_to($caller);
122 1         9 $Test->plan(_filter_recorder_opts(@_));
123 1         738 Test::LectroTest::Property->export_to_level(1, $self);
124 1         252 Test::LectroTest::Generator->export_to_level(1, $self, ':all');
125 1         8 filter_add(Test::LectroTest::Property->_make_code_filter);
126             }
127              
128             sub holds {
129 5     5 1 28 my ($diag_store, $results) = _check_property(@_);
130 5         112 my $success = $results->success;
131 5         45 (my $name = $results->summary) =~ s/^.*?- /property /;
132 5         112 $Test->ok($success, $name);
133 5 100       2345 $Test->diag(@$diag_store) if @$diag_store;
134 5         22 my $details = $results->details;
135 5         28 $details =~ s/^.*?\n//; # remove summary line
136 5         13 $details =~ s/^\# / /mg; # replace commenting w/ indent
137 5 100       16 $Test->diag($details) if $details;
138 5 100       61 return $success ? 1 : 0; # same result policy as Test::Builder::ok
139             }
140              
141             my ($playback_failures, $record_failures);
142              
143             sub _check_property {
144 1     1   7 no warnings 'redefine';
  1         3  
  1         447  
145 5     5   10 my $diag_store = [];
146 5         8 my $property = shift;
147 5         21 local *Test::Builder::ok = \&_disconnected_ok;
148 5     1   25 local *Test::Builder::diag = sub { shift; push @$diag_store, @_; 0 };
  1         133  
  1         3  
  1         4  
149              
150             # for efficiency, we recycle any recorders that the TestRunner
151             # may have created (the recorders cache test cases)
152              
153 5 50       26 my @opts = (
    50          
154             $playback_failures ? (playback_failures => $playback_failures) : (),
155             $record_failures ? (record_failures => $record_failures) : (),
156             @_ # passed-in options go last to override defaults
157             );
158 5         28 my $runner = Test::LectroTest::TestRunner->new(@opts);
159 5         35 my @results = ($diag_store, $runner->run($property));
160              
161             # the TestRunner may have converted file names into TestRecorder
162             # objects, so we just "upgrade" to these objects if they exist
163             # and we're still holding filenames
164              
165 5 50 33     22 $playback_failures = $runner->playback_failures
166             if $playback_failures && !ref($playback_failures);
167 5 50 33     19 $record_failures = $runner->record_failures
168             if $record_failures && !ref($record_failures);
169              
170 5         85 return @results;
171             }
172              
173             my @RECORDER_OPTS = (qw( record_failures playback_failures regressions ));
174              
175             sub _filter_recorder_opts {
176 1     1   2 my (@opts);
177 1         11 while (@_) {
178 2 50       8 unless (grep $_ eq $_[0], @RECORDER_OPTS) {
179 2         6 push @opts, shift;
180             }
181             else {
182 0         0 my ($ropt, $rval) = (shift, shift);
183 0 0       0 if ($ropt eq "regressions") {
    0          
184 0         0 $playback_failures = $record_failures = $rval;
185             }
186             elsif ($ropt eq "playback_failures") {
187 0         0 $playback_failures = $rval;
188             }
189             else {
190 0         0 $record_failures = $rval;
191             }
192             }
193             }
194 1         5 return @opts;
195             }
196              
197             # the following sub replaces Test::Builder's
198             # ok() method when we want to disable T::B's
199             # test harness
200              
201 2001 100   2001   172756 sub _disconnected_ok { $_[1] ? 1 : 0 }
202              
203              
204             1;
205              
206             =head1 BUGS
207              
208             In order to integrate with the L testing harness (whose
209             underlying testing model is somewhat incompatible with the needs of
210             random trial-based testing) this module redefines two Test::Builder
211             functions (C and C) for the duration of each property
212             check.
213              
214              
215             =head1 SEE ALSO
216              
217             For a gentle introduction to LectroTest, see
218             L. Also, the slides from my LectroTest
219             talk for the Pittsburgh Perl Mongers make for a great introduction.
220             Download a copy from the LectroTest home (see below).
221              
222             L explains how to test for
223             regressions and corner cases using LectroTest.
224              
225             L explains in detail what
226             you can put inside of your property specifications.
227              
228             L describes the many generators and
229             generator combinators that you can use to define the test or
230             condition space that you want LectroTest to search for bugs.
231              
232             L describes the objects that check your
233             properties and tells you how to turn their control knobs. You'll want
234             to look here if you're interested in customizing the testing
235             procedure.
236              
237             L and L explain how to do simple
238             case-based testing in Perl.
239              
240             L is the test harness upon which this module
241             is built.
242              
243              
244             =head1 AUTHOR
245              
246             Tom Moertel (tom@moertel.com)
247              
248             =head1 INSPIRATION
249              
250             The LectroTest project was inspired by Haskell's
251             QuickCheck module by Koen Claessen and John Hughes:
252             http://www.cs.chalmers.se/~rjmh/QuickCheck/.
253              
254             =head1 COPYRIGHT and LICENSE
255              
256             Copyright (c) 2004-13 by Thomas G Moertel. All rights reserved.
257              
258             This program is free software; you can redistribute it and/or
259             modify it under the same terms as Perl itself.
260              
261             =cut