File Coverage

blib/lib/Test/LectroTest/TestRunner.pm
Criterion Covered Total %
statement 183 187 97.8
branch 48 50 96.0
condition 5 5 100.0
subroutine 31 32 96.8
pod 4 4 100.0
total 271 278 97.4


line stmt bran cond sub pod time code
1             package Test::LectroTest::TestRunner;
2             {
3             $Test::LectroTest::TestRunner::VERSION = '0.5001';
4             }
5              
6 4     4   774 use strict;
  4         7  
  4         153  
7 4     4   23 use warnings;
  4         8  
  4         122  
8              
9 4     4   23 use Carp;
  4         5  
  4         363  
10 4     4   4653 use Data::Dumper;
  4         55635  
  4         319  
11 4     4   76 use Scalar::Util qw(blessed);
  4         8  
  4         408  
12              
13 4     4   14437 use Test::LectroTest::Property qw( NO_FILTER );
  4         11  
  4         36  
14 4     4   5151 use Test::LectroTest::FailureRecorder;
  4         12  
  4         132  
15 4     4   9064 use Test::LectroTest::Generator qw( Unit );
  4         8  
  4         638  
16              
17             =head1 NAME
18              
19             Test::LectroTest::TestRunner - Configurable TAP-compatible engine for running LectroTest property checks
20              
21             =head1 VERSION
22              
23             version 0.5001
24              
25             =head1 SYNOPSIS
26              
27             use Test::LectroTest::TestRunner;
28              
29             my @args = trials => 1_000, retries => 20_000;
30             my $runner = Test::LectroTest::TestRunner->new( @args );
31              
32             # test a single property and print details upon failure
33             my $result = $runner->run( $a_single_lectrotest_property );
34             print $result->details unless $result->success;
35              
36             # test a suite of properties, w/ Test::Harness::TAP output
37             my $num_successful = $runner->run_suite( @properties );
38             print "# All passed!" if $num_successful == @properties;
39              
40             =head1 DESCRIPTION
41              
42             B If you just want to write and run simple tests, see
43             L. If you really want to learn about the
44             property-checking apparatus or turn its control knobs, read on.
45              
46             This module provides Test::LectroTest::TestRunner, a class of objects
47             that tests properties by running repeated random trials. Create a
48             TestRunner, configure it, and then call its C or C
49             methods to test properties individually or in groups.
50              
51             =head1 METHODS
52              
53             The following methods are available.
54              
55             =cut
56              
57             our %defaults = (
58             trials => 1_000,
59             retries => 20_000,
60             scalefn => sub { $_[0] / 2 + 1 },
61             number => 1,
62             verbose => 1,
63             record_failures => undef,
64             playback_failures => undef,
65             );
66              
67             # build field accessors
68              
69             for my $field (keys %defaults) {
70 4     4   32 no strict 'refs';
  4         7  
  4         5816  
71             *{$field} = sub {
72 1388     1388   1684 my $self = shift;
73 1388 100       2649 $self->{$field} = $_[0] if @_;
74 1388         3305 $self->{$field}
75             };
76             }
77              
78             sub regressions {
79 0     0 1 0 my ($self, $value) = @_;
80 0         0 $self->record_failures($value);
81 0         0 $self->playback_failures($value);
82             }
83              
84             =pod
85              
86             =head2 new(I)
87              
88             my $runner = new Test::LectroTest::TestRunner(
89             trials => 1_000,
90             retries => 20_000,
91             scalefn => sub { $_[0] / 2 + 1 },
92             verbose => 1,
93             regressions => "/path/to/regression_suite.txt",
94             );
95              
96             Creates a new Test::LectroTest::TestRunner and configures it with the
97             given named parameters, if any. Typically, you need only provide the
98             C parameter because the other values are reasonable for almost
99             all situations. Here is what each parameter means:
100              
101             =over 4
102              
103             =item trials
104              
105             The number of trials to run against each property checked.
106             The default is 1_000.
107              
108             =item retries
109              
110             The number of times to allow a property to retry trials (via
111             C<$tcon-Eretry>) during the entire property check before aborting
112             the check. This is used to prevent infinite looping, should
113             the property retry every attempt.
114              
115             =item scalefn
116              
117             A subroutine that scales the sizing guidance given to input
118             generators.
119              
120             The TestRunner starts with an initial guidance of 1 at the beginning
121             of a property check. For each trial (or retry) of the property, the
122             guidance value is incremented. This causes successive trials to be
123             tried using successively more complex inputs. The C
124             subroutine gets to adjust this guidance on the way to the input
125             generators. Typically, you would change the C subroutine if
126             you wanted to change the rate and which inputs grow during the course
127             of the trials.
128              
129             =item verbose
130              
131             If this paramter is set to true (the default) the TestRunner will use
132             verbose output that includes things like label frequencies and
133             counterexamples. Otherwise, only one-line summaries will be output.
134             Unless you have a good reason to do otherwise, leave this parameter
135             alone because verbose output is almost always what you want.
136              
137             =item record_failures
138              
139             If this parameter is set to a file's pathname (or a FailureRecorder
140             object), the TestRunner will record property-check failures to the
141             file (or recorder). (This is an easy way to build a
142             regression-testing suite.) If the file cannot be created or
143             written to, this parameter will be ignored. Set this parameter to
144             C (the default) to turn off recording.
145              
146             =item playback_failures
147              
148             If this parameter is set to a file's pathname (or a FailureRecorder
149             object), the TestRunner will load previously recorded failures from
150             the file (or recorder) and use them as I test cases when
151             checking properties. If the file cannot be read, this option will be
152             ignored. Set this parameter to C (the default) to turn off
153             recording.
154              
155             =item regressions
156              
157             If this parameter is set to a file's pathname (or a FailureRecorder
158             object), the TestRunner will load failures from and record failures to
159             the file (or recorder). Setting this parameter is a shortcut for, and
160             exactly equivalent to, setting I and
161             to the same value, which is typically what you
162             want when managing a persistent suite of regression tests.
163              
164             This is a write-only accessor.
165              
166             =back
167              
168             You can also set and get the values of the configuration properties
169             using accessors of the same name. For example:
170              
171             $runner->trials( 10_000 );
172              
173             =cut
174              
175             sub new {
176 36     36 1 8388 my $class = shift;
177 36         287 my $self = bless { %defaults, @_ }, $class;
178 36 50       154 if (defined(my $val = delete $self->{regressions})) {
179 0         0 $self->regressions($val);
180             }
181 36         102 return $self;
182             }
183              
184             =pod
185              
186             =head2 run(I)
187              
188             $results = $runner->run( $a_property );
189             print $results->summary, "\n";
190             if ($results->success) {
191             # celebrate!
192             }
193              
194             Checks whether the given property holds by running repeated random
195             trials. The result is a Test::LectroTest::TestRunner::results object,
196             which you can query for fined-grained information about the outcome of
197             the check.
198              
199             The C method takes an optional second argument which gives
200             the test number. If it is not provided (usually the case), the
201             next number available from the TestRunner's internal counter is
202             used.
203              
204             $results = $runner->run( $third_property, 3 );
205              
206             Additionally, if the TestRunner's I parameter is
207             defined, this method will play back any relevant failure cases from
208             the given playback file (or FailureRecorder).
209              
210             Additionally, if the TestRunner's I parameter is
211             defined, this method will record any new failures to the given file
212             (or FailureRecorder).
213              
214             =cut
215              
216             sub run {
217 75     75 1 172 my ($self, $prop, $number) = @_;
218              
219             # if a test number wasn't provided, take the next from our counter
220              
221 75 100       176 unless (defined $number) {
222 74         165 $number = $self->number;
223 74         169 $self->number( $number + 1);
224             }
225              
226             # create a new results object to hold our results; run trials
227              
228 75         1949 my ($inputs_list, $testfn, $name) = @$prop{qw/inputs test name/};
229 75         1934 my $results = Test::LectroTest::TestRunner::results->new(
230             name => $name, number => $number
231             );
232              
233             # create an empty label store and start at attempts = 0
234              
235 75         4009 my %labels;
236 75         102 my $attempts = 0;
237 75         75 my $in_regressions = 1;
238              
239             # for each set of input-generators, run a series of trials
240              
241 75         102 for my $gen_specs (@{$self->_regression_generators($name)},
  75         185  
242             undef, # separator
243             @$inputs_list) {
244              
245             # an undef value separates the regression-test generators (if
246             # any) from the property's own generators; we use it to turn
247             # on failure recording after the regression-test generators
248             # have all been used. (we don't record failures during
249             # regression testing because they have already been recorded)
250              
251 253 100       513 if (!defined($gen_specs)) {
252 75         80 $in_regressions = 0;
253 75         448 next;
254             }
255              
256 178         197 my $retries = 0;
257 178         191 my $base_size = 0;
258 178         575 my @vars = sort keys %$gen_specs;
259 178         442 my $scalefn = $self->scalefn;
260              
261 178 100       564 for (1 .. ($in_regressions ? 1 : $self->trials)) {
262              
263             # run a trial
264              
265 9435         10650 $base_size++;
266 9435         177447 my $controller=Test::LectroTest::TestRunner::testcontroller->new;
267 9435         201964 my $size = $scalefn->($base_size);
268 9435         21779 my $inputs = { "WARNING" => "EXCEPTION FROM WITHIN GENERATOR" };
269 9435         12061 my $success = eval {
270 9435         13162 $inputs = { map {($_, $gen_specs->{$_}->generate($size))}
  7365         21383  
271             @vars };
272 9433         29478 $testfn->($controller, @$inputs{@vars});
273             };
274              
275             # did the trial bail out because of an exception?
276              
277 9435 100       74570 $results->exception( do { my $ex=$@; chomp $ex; $ex } ) if $@;
  3         5  
  3         6  
  3         64  
278              
279             # was it retried?
280              
281 9435 100       181443 if ($controller->retried) {
282 966         5977 $retries++;
283 966 100       1851 if ($retries >= $self->retries) {
284 2         45 $results->incomplete("$retries retries exceeded");
285 2         47 $results->attempts( $attempts );
286 2         21 return $results;
287             }
288 964         2550 redo; # re-run the trial w/ new inputs
289             }
290              
291             # the trial ran to completion, so count the attempt
292              
293 8469         50733 $attempts++;
294              
295             # and count the trial toward the bin with matching labels
296              
297 8469 100       170403 if ($controller->labels) {
298 2191         12870 local $" = " & ";
299 2191         2139 my @cl = sort @{$controller->labels};
  2191         40461  
300 2191 100       17318 $labels{"@cl"}++ if @cl;
301             }
302              
303             # if the trial outcome was failure, return a counterexample
304              
305 8469 100       61156 unless ( $success ) {
306 20         379 $results->counterexample_( $inputs );
307 20         441 $results->notes_( $controller->notes );
308 20         1580 $results->attempts( $attempts );
309 20 50       148 $self->_record_regression( $name, $inputs )
310             unless $in_regressions;
311 20         142 return $results;
312             }
313              
314             # otherwise, loop up to the next trial
315             }
316             }
317              
318 53         1455 $results->success(1);
319 53         1256 $results->attempts( $attempts );
320 53         1338 $results->labels( \%labels );
321 53         399 return $results;
322             }
323              
324             sub _recorder_for_writes {
325 20     20   35 shift->_get_recorder('record_failures');
326             }
327              
328             sub _recorder_for_reads {
329 75     75   188 shift->_get_recorder('playback_failures');
330             }
331              
332             sub _get_recorder {
333 95     95   139 my ($self, $attr) = @_;
334 95         150 my $val = $self->{$attr};
335 95 100 100     335 if ($val && ! ref $val) {
336 3         27 $val = $self->{$attr} = Test::LectroTest::FailureRecorder->new($val);
337             }
338 95         841 return $val;
339             }
340              
341             sub _regression_generators {
342              
343 75     75   109 my ($self, $prop_name) = @_;
344              
345             # if we get an error reading failures from the recorder, ignore it
346             # because if we're building a new regression suite, there may not
347             # even be a failure-recording file yet
348              
349 75   100     103 my $failures = eval {
350             $self->_recorder_for_reads->get_failures_for_property($prop_name);
351             } || [];
352              
353 75         140 my @gens;
354              
355 75         235 for my $inputs (@$failures) {
356              
357             # convert the failure case's inputs into a set of generator
358             # bindings that will generate the failure case
359              
360 100         103 my %gen_bindings;
361 100         412 $gen_bindings{$_} = Unit($inputs->{$_}) for keys %$inputs;
362 100         237 push @gens, \%gen_bindings;
363             }
364              
365 75         245 return \@gens;
366             }
367              
368             sub _record_regression {
369 20     20   32 my ($self, $name, $inputs) = @_;
370 20         27 eval {
371 20         39 $self->_recorder_for_writes # may be undef
372             ->record_failure_for_property($name, $inputs);
373             };
374             }
375              
376              
377             =pod
378              
379             =head2 run_suite(I...)
380              
381             my $num_successful = $runner->run_suite( @properties );
382             if ($num_successful == @properties) {
383             # celebrate most jubilantly!
384             }
385              
386             Checks a suite of properties, sending the results of each
387             property checked to C in a form that is compatible with
388             L. For example:
389              
390             1..5
391             ok 1 - Property->new disallows use of 'tcon' in bindings
392             ok 2 - magic Property syntax disallows use of 'tcon' in bindings
393             ok 3 - exceptions are caught and reported as failures
394             ok 4 - pre-flight check catches new w/ no args
395             ok 5 - pre-flight check catches unbalanced arguments list
396              
397             By default, labeling statistics and counterexamples (if any) are
398             included in the output if the TestRunner's C property is
399             true. You may override the default by passing the C named
400             parameter after all of the properties in the argument list:
401              
402             my $num_successes = $runner->run_suite( @properties,
403             verbose => 1 );
404             my $num_failed = @properties - $num_successes;
405              
406             =cut
407              
408 49 100   49   384 sub _prop($) { blessed $_[0] && $_[0]->isa("Test::LectroTest::Property") }
409              
410             sub run_suite {
411 9     9 1 36 local $| = 1;
412 9         18 my $self = shift;
413 9         13 my @tests;
414             my @opts;
415 9         29 while (@_) {
416 49 100       93 if (_prop $_[0]) { push @tests, shift; }
  48         125  
417 1         5 else { push @opts, shift, shift; }
418             }
419 9         31 my %opts = (verbose => $self->verbose, @opts);
420 9         17 my $verbose = $opts{verbose};
421 9         27 $self->number(1); # reset test-number count
422 9         11 my $successful = 0; # reset success count
423 9         868 print "1..", scalar @tests, "\n";
424 9         25 for (@tests) {
425 48         441 my $results = $self->run($_);
426 48 100       197 print $verbose ? $results->details : $results->summary ."\n";
427 48 100       1216 $successful += $results->success ? 1 : 0;
428             }
429 9         145 return $successful;
430             }
431              
432             =pod
433              
434             =head1 HELPER OBJECTS
435              
436             There are two kinds of objects that TestRunner uses as helpers.
437             Neither is meant to be created by you. Rather, a TestRunner
438             will create them on your behalf when they are needed.
439              
440             The objects are described in the following subsections.
441              
442              
443             =head2 Test::LectroTest::TestRunner::results
444              
445             my $results = $runner->run( $a_property );
446             print "Property name: ", $results->name, ": ";
447             print $results->success ? "Winner!" : "Loser!";
448              
449             This is the object that you get back from C. It contains all of
450             the information available about the outcome of a property check
451             and provides the following methods:
452              
453             =over 4
454              
455             =item success
456              
457             Boolean value: True if the property checked out successfully;
458             false otherwise.
459              
460             =item summary
461              
462             Returns a one line summary of the property-check outcome. It does not
463             end with a newline. Example:
464              
465             ok 1 - Property->new disallows use of 'tcon' in bindings
466              
467             =item details
468              
469             Returns all relevant information about the property-check outcome as a
470             series of lines. The last line is terminated with a newline. The
471             details are identical to the summary (except for the terminating
472             newline) unless label frequencies are present or a counterexample is
473             present, in which case the details will have these extras (the
474             summary does not). Example:
475              
476             1..1
477             not ok 1 - 'my_sqrt meets defn of sqrt' falsified in 1 attempts
478             # Counterexample:
479             # $x = '0.546384454460178';
480              
481             =item name
482              
483             Returns the name of the property to which the results pertain.
484              
485             =item number
486              
487             The number assigned to the property that was checked.
488              
489             =item counterexample
490              
491             Returns the counterexample that "broke" the code being tested, if
492             there is one. Otherwise, returns an empty string. If any notes
493             have been attached to the failing trial, they will be included.
494              
495             =item labels
496              
497             Label counts. If any labels were applied to trials during the
498             property check, this value will be a reference to a hash mapping each
499             combination of labels to the count of trials that had that particular
500             combination. Otherwise, it will be undefined.
501              
502             Note that each trial is counted only once -- for the I
503             combination of labels that was applied to it. For example, consider
504             the following labeling logic:
505              
506             Property {
507             ##[ x <- Int ]##
508             $tcon->label("negative") if $x < 0;
509             $tcon->label("odd") if $x % 2;
510             1;
511             }, name => "negative/odd labeling example";
512              
513             For a particular trial, if I was 2 (positive and even), the trial
514             would receive no labels. If I was 3 (positive and odd), the trial
515             would be labeled "odd". If I was -2 (negative and even), the trial
516             would be labeled "negative". If I was -3 (negative and odd), the
517             trial would be labeled "negative & odd".
518              
519             =item label_frequencies
520              
521             Returns a string containing a line-by-line accounting of labels
522             applied during the series of trials:
523              
524             print $results->label_frequencies;
525              
526             The corresponding output looks like this:
527              
528             25% negative
529             25% negative & odd
530             25% odd
531              
532             If no labels were applied, an empty string is returned.
533              
534             =item exception
535              
536             Returns the text of the exception or error that caused the series of
537             trials to be aborted, if the trials were aborted because an exception
538             or error was intercepted by LectroTest. Otherwise, returns an empty
539             string.
540              
541             =item attempts
542              
543             Returns the count of trials performed.
544              
545             =item incomplete
546              
547             In the event that the series of trials was halted before it was
548             completed (such as when the retry count was exhausted), this method will
549             return the reason. Otherwise, it returns an empty string.
550              
551             Note that a series of trials I complete if a counterexample was
552             found.
553              
554             =back
555              
556             =cut
557              
558             package Test::LectroTest::TestRunner::results;
559             {
560             $Test::LectroTest::TestRunner::results::VERSION = '0.5001';
561             }
562 4     4   6200 use Class::Struct;
  4         8472  
  4         29  
563             import Data::Dumper;
564              
565             struct( name => '$',
566             success => '$',
567             labels => '$',
568             counterexample_ => '$',
569             notes_ => '$',
570             exception => '$',
571             attempts => '$',
572             incomplete => '$',
573             number => '$',
574             );
575              
576             sub summary {
577 80     80   281 my $self = shift;
578 80         1672 my ($name, $attempts) = ($self->name, $self->attempts);
579 80         2396 my $incomplete = $self->incomplete;
580 80         2237 my $number = $self->number;
581 80         538 local $" = " / ";
582 80 100       1504 return $self->success
    100          
583             ? "ok $number - '$name' ($attempts attempts)"
584             : $incomplete
585             ? "not ok $number - '$name' incomplete ($incomplete)"
586             : "not ok $number - '$name' falsified in $attempts attempts";
587             }
588              
589             sub details {
590 72     72   106 my $self = shift;
591 72         199 my $summary = $self->summary . "\n";
592 72         841 my $details .= $self->label_frequencies;
593 72         217 my $cx = $self->counterexample;
594 72 100       245 if ( $cx ) {
595 17         33 $details .= "Counterexample:\n$cx";
596             }
597 72         1444 my $ex = $self->exception;
598 72 100       787 if ( $ex ) {
599 3         4 local $Data::Dumper::Terse = 1;
600 3         10 $details .= "Caught exception: " . Dumper($ex);
601             }
602 72 100       393 $details =~ s/^/\# /mg if $details; # mark as TAP comments
603 72         2544 return "$summary$details";
604             }
605              
606             sub label_frequencies {
607 72     72   101 my $self = shift;
608 72         1438 my $l = $self->labels;
609 72         7470 my $total = $self->attempts;
610 72         557 my @keys = sort { $l->{$b} <=> $l->{$a} } keys %$l;
  7         25  
611 16         121 join( "\n",
612 72         431 (map {sprintf "% 3d%% %s", (200*$l->{$_}+1)/(2*$total), $_} @keys),
613             ""
614             );
615             }
616              
617             sub counterexample {
618 72     72   290 my $self = shift;
619 72         1764 my $vars = $self->counterexample_;
620 72 100       549 return "" unless $vars; # no counterexample
621 19         61 my $sorted_keys = [ sort keys %$vars ];
622 4     4   2417 no warnings 'once';
  4         10  
  4         1977  
623 19         36 local $Data::Dumper::Sortkeys = 1;
624 19         25 local $Data::Dumper::Useqq = 1;
625 19         109 return Data::Dumper->new([@$vars{@$sorted_keys}], $sorted_keys)->Dump .
626             $self->notes;
627             }
628              
629             sub notes {
630 19     19   904 my $self = shift;
631 19         436 my $notes = $self->notes_;
632 19 100       204 return $notes ? join("\n", "Notes:", @$notes, "") : "";
633             }
634              
635             =pod
636              
637             =head2 Test::LectroTest::TestRunner::testcontroller
638              
639             During a live property-check trial, the variable C<$tcon> is
640             available to your Properties. It lets you label the current
641             trial or request that it be re-tried with new inputs.
642              
643             The following methods are available.
644              
645             =cut
646              
647             package Test::LectroTest::TestRunner::testcontroller;
648             {
649             $Test::LectroTest::TestRunner::testcontroller::VERSION = '0.5001';
650             }
651             import Class::Struct;
652              
653             struct ( labels => '$', retried => '$', notes => '$' );
654              
655             =pod
656              
657             =over 4
658              
659             =item retry
660              
661             Property {
662             ##[ x <- Int ]##
663             return $tcon->retry if $x == 0;
664             }, ... ;
665              
666              
667             Stops the current trial and tells the TestRunner to re-try it
668             with new inputs. Typically used to reject a particular case
669             of inputs that doesn't make for a good or valid test. While
670             not required, you will probably want to call C<$tcon-Eretry>
671             as part of a C statement to prevent further execution
672             of your property's logic, the results of which will be thrown
673             out should it run to completion.
674              
675             The return value of C<$tcon-Eretry> is itself meaningless; it is
676             the side-effect of calling it that causes the current trial to be
677             thrown out and re-tried.
678              
679             =cut
680              
681             sub retry {
682 966     966   21570 shift->retried(1);
683             }
684              
685              
686             =pod
687              
688             =item label(I)
689              
690             Property {
691             ##[ x <- Int ]##
692             $tcon->label("negative") if $x < 0;
693             $tcon->label("odd") if $x % 2;
694             }, ... ;
695              
696             Applies a label to the current trial. At the end of the trial, all of
697             the labels are gathered together, and the trial is dropped into a
698             bucket bearing the combined label. See the discussion of
699             L for more.
700              
701             =cut
702              
703              
704             sub label {
705 2193     2193   7188 my $self = shift;
706 2193         40708 my $labels = $self->labels;
707 2193         12060 push @$labels, @_;
708 2193         37101 $self->labels( $labels );
709             }
710              
711             =pod
712              
713             =item trivial
714              
715             Property {
716             ##[ x <- Int ]##
717             $tcon->trivial if $x == 0;
718             }, ... ;
719              
720             Applies the label "trivial" to the current trial. It is identical to
721             calling C
722              
723             =cut
724              
725             sub trivial {
726 100     100   359 shift->label("trivial");
727             }
728              
729              
730             =pod
731              
732             =item note(I...)
733              
734             Property {
735             ##[ s <- String( charset=>"A-Za-z0-9" ) ]##
736             my $s_enc = encode($s);
737             my $s_enc_dec = decode($s_enc);
738             $tcon->note("s_enc = $s_enc",
739             "s_enc_dec = $s_enc_dec");
740             $s eq $s_enc_dec;
741             }, name => "decode is encode's inverse" ;
742              
743             Adds a note (or notes) to the current trial. In the event that the
744             trial fails, these notes will be emitted as part of the
745             counterexample. For example:
746              
747             1..1
748             not ok 1 - property 'decode is encode's inverse' \
749             falsified in 68 attempts
750             # Counterexample:
751             # $s = "0";
752             # Notes:
753             # $s_enc = "";
754             # $s_enc_dec = "";
755              
756             Notes can help you debug your code when something goes wrong. Use
757             them as debugging hints to yourself. For example, you can use notes
758             to record the output of each stage of a multi-stage test. That way,
759             if the test fails, you can see what happened in each stage without
760             having to plug the counterexample into your code under a debugger.
761              
762             If you want to include complicated values or data structures in your
763             notes, see the C method, next, which may be more appropriate.
764              
765              
766             =cut
767              
768             sub note {
769 35     35   1007 my $self = shift;
770 35         725 my $notes = $self->notes;
771 35         202 push @$notes, @_;
772 35         674 $self->notes( $notes );
773             }
774              
775             =pod
776              
777             =item dump(I, I)
778              
779             Property {
780             ##[ s <- String ]##
781             my $s_enc = encode($s);
782             my $s_enc_dec = decode($s_enc);
783             $tcon->dump($s_enc, "s_enc");
784             $tcon->dump($s_enc_dec, "s_enc_dec");
785             $s eq $s_enc_dec;
786             }, name => "decode is encode's inverse" ;
787              
788             Adds a note to the current trial in which the given I is
789             dumped. The value will be dumped via L and thus may
790             be complex and contain weird control characters and so on. If you
791             supply a I, it will be used to name the dumped value. Returns
792             I as its result.
793              
794             In the event that the trial fails, the note (and any others) will be
795             emitted as part of the counterexample.
796              
797              
798             See C above for more.
799              
800             =cut
801              
802             sub dump {
803 24     24   92 my $self = shift;
804 24         30 my ($val, $name) = @_;
805 24         32 local $Data::Dumper::Sortkeys = 1;
806 24         24 local $Data::Dumper::Useqq = 1;
807 24         26 local $Data::Dumper::Indent = 0;
808 24 100       63 my @names = $name ? ([$name]) : ();
809 24         102 $self->note( Data::Dumper->new( [$val], @names )->Dump );
810 24         323 return $val;
811             }
812              
813              
814             =pod
815              
816             =back
817              
818             =cut
819              
820              
821              
822             package Test::LectroTest::TestRunner;
823              
824             1;
825              
826              
827             =head1 SEE ALSO
828              
829             L explains in detail what
830             you can put inside of your property specifications.
831              
832             L explains how to test for
833             regressions and corner cases using LectroTest.
834              
835             L documents the Test Anything Protocol,
836             Perl's simple text-based interface between testing modules such
837             as L and the test harness L.
838              
839              
840             =head1 AUTHOR
841              
842             Tom Moertel (tom@moertel.com)
843              
844              
845             =head1 INSPIRATION
846              
847             The LectroTest project was inspired by Haskell's
848             QuickCheck module by Koen Claessen and John Hughes:
849             http://www.cs.chalmers.se/~rjmh/QuickCheck/.
850              
851              
852             =head1 COPYRIGHT and LICENSE
853              
854             Copyright (c) 2004-13 by Thomas G Moertel. All rights reserved.
855              
856             This program is free software; you can redistribute it and/or
857             modify it under the same terms as Perl itself.
858              
859             =cut