1Test::LectroTest::GenerUasteorr(C3o)ntributed Perl DocumTeenstta:t:iLoenctroTest::Generator(3)
2
3
4

NAME

6       Test::LectroTest::Generator - Random value generators and combinators
7

VERSION

9       version 0.5001
10

SYNOPSIS

12        use Test::LectroTest::Generator qw(:common :combinators);
13
14        my $int_gen = Int;
15        my $pct_gen = Int( range=>[0,100] );
16        my $flt_gen = Float( range=>[0,1] );
17        my $bln_gen = Bool;
18        my $chr_gen = Char( charset=>"a-z" );
19        my $str_gen = String( charset=>"A-Z0-9", length=>[3,] );
20        my $ary_gen = List( Int(sized=>0) );
21        my $hsh_gen = Hash( $str_gen, $pct_gen );
22        my $uni_gen = Unit( "e" );  # always returns "e"
23        my $elm_gen = Elements("e1", "e2", "e3", "e4");
24
25        for my $sizing_guidance (1..100) {
26            my $i = $int_gen->generate( $sizing_guidance );
27            print "$i ";
28        }
29        print "\n";
30
31        # generates single digits
32        my $digit_gen  = Elements( 0..9 );  # or Int(range=>[0,9],sized=>0)
33
34        # generates SSNs like "910-77-2236"
35        my $ssn_gen    = Paste( Paste( ($digit_gen) x 3 ),
36                                Paste( ($digit_gen) x 2 ),
37                                Paste( ($digit_gen) x 4 ),
38                                glue => "-"                );
39
40        # print 10 SSNs
41        print( map {$ssn_gen->generate($_)."\n"} 1..10 );
42
43        my $english_dist_vowel_gen =
44            Frequency( [8.167,Unit("a")], [12.702,Unit("e")],
45                       [6.996,Unit("i")], [ 7.507,Unit("o")],
46                       [2.758,Unit("u")] );
47            # Source: http://www.csm.astate.edu/~rossa/datasec/frequency.html
48

DESCRIPTION

50       This module provides random value generators for common data types and
51       provides an interface and tools for creating your own generators.  It
52       also provides generator combinators that can be used to create more-
53       complex generators by combining simple ones.
54
55       A generator is an object having a method "generate", which takes a
56       single argument, size and returns a new random value.  The generated
57       value is always a scalar.  Generators that produce data structures
58       return references to them.
59
60   Sizing guidance
61       The "generate" method interprets its size argument as guidance about
62       the complexity of the value it should create.  Typically, smaller size
63       values result in smaller generated numbers and shorter generated
64       strings and lists.  Some generators, for which sizing doesn't make
65       sense, ignore sizing guidance altogether; those that do use sizing
66       guidance can be told to ignore it via the sized modifier.
67
68       The purpose of sizing is to allow LectroTest to generate simple values
69       at first and then, as testing progresses, to slowly ramp up the
70       complexity.  In this way, counterexamples for obvious problems will be
71       easier for you to understand.
72
73   Generators
74       The following functions create fully-formed generators, ready to use.
75       These functions are exported into your code's namespace if you ask for
76       ":generators" or ":all" when you "use" this module.
77
78       Each generator has a "generate" method that you can call to extract a
79       new, random value from the generator.
80
81       Int
82               my $gen = Int( range=>[0,9], sized=>0 );
83
84           Creates a generator for integer values, by default in the range
85           [-32768,32767], inclusive, but this can be changed via the optional
86           range modifier.
87
88           Int( range=>[low, high] )
89               Causes the generated values to be constrained to the range
90               [low, high], inclusive.  By default, the range is [-32768,
91               32767].
92
93               Note: If your range is empty (i.e., low > high), LectroTest
94               will complain.
95
96               Note: If zero is not within the range you provide, sizing makes
97               no sense because the intersection of your range and the sizing
98               range can be empty, and thus you must turn off sizing with
99               "sized=>0".  If you forget, LectroTest will complain.
100
101           Int( sized=>bool )
102               If true (the default), constrains the absolute value of the
103               generated integers to the sizing guidance provided to the
104               "generate" method.  Otherwise, the generated values are
105               constrained only by the range.
106
107       Float
108               my $gen = Float( range=>[-2.0,2.0], sized=>1 );
109
110           Creates a generator for floating-point values, by default in the
111           range [-32768.0,32768.0), but this can be changed via the optional
112           range modifier.  By default Float generators are sized.
113
114           Float( range=>[low, high] )
115               Causes the generated values to be constrained to the range
116               [low, high).  By default, the range is [-32768.0,32768.0).
117               (Note that the high value itself can never be generated, but
118               values infinitesimally close to it can.)
119
120               Note: If your range is empty (i.e., low > high), LectroTest
121               will complain.
122
123               Note: If zero is not within the range you provide, sizing makes
124               no sense because the intersection of your range and the sizing
125               range can be empty, and thus you must turn off sizing with
126               "sized=>0".  If you forget, LectroTest will complain.
127
128           Float( sized=>bool )
129               If true (the default), constrains the absolute value of the
130               generated values to the sizing guidance provided to the
131               "generate" method.  Otherwise, the generated values are
132               constrained only by the range.
133
134       Bool
135               my $gen = Bool;
136
137           Creates a generator for boolean values: 0 for false, 1 for true.
138           The generator ignores sizing guidance.
139
140       Char
141               my $gen = Char( charset=>"A-Za-z0-9_" );
142
143           Creates a generator for characters.  By default the characters are
144           in the ASCII range [0,127], inclusive, but this behavior can be
145           changed with the charset modifier:
146
147           Char( charset=>cset )
148               Characters will be drawn from the character set given by the
149               character-set specification cset.  The syntax of cset is
150               similar the Perl "tr" built-in and is a string comprised of
151               characters and character ranges:
152
153               c   Adds the character c to the set.
154
155               c-d Adds the characters in the range c through d (inclusive) to
156                   the set.  Note: If c is lexicographically greater than d,
157                   the range is empty, and no characters will be added to the
158                   set.
159
160               Examples:
161
162               charset=>"abcdwxyz"
163                   The characters "a", "b", "c", "d", "w", "x", "y", and "z"
164                   are in the set.
165
166               charset=>"a-dx-z"
167                   Shorter version of the previous example.
168
169               charset=>"\x00-\x7f"
170                   The ASCII character set.
171
172               charset=>"-_A-Za-z0-9"
173                   The character set contains "-", "_", upper- and lower-case
174                   ASCII letters, and the digits 0-9.  Notice that the dash
175                   must occur first so that it is not misinterpreted as
176                   denoting a range of characters.
177
178       List(elemgen)
179               my $gen = List( Bool, length=>[1,10] );
180
181           Creates a generator for lists (which are returned as array refs).
182           The elements of the lists are generated by the generator given as
183           elemgen.  The lengths of the generated lists are constrained by
184           sizing guidance at the time of generation.  You can override the
185           default sizing behavior using the optional length modifier:
186
187           When the list generator calls the element generator, it divides the
188           sizing guidance by the length of the list.  For example, if the
189           list being generated will have 7 elements, when the list generator
190           calls the element generator to generate each element, it will scale
191           the sizing guidance by 1/7.  In this way the sizing guidance
192           provides a rough constraint on the total number of elements
193           produced, regardless of the depth of the list structure being
194           generated.
195
196           List( ..., length=>N )
197               Generated lists are exactly length N.
198
199           List( ..., length=>[M,] )
200               Generated lists are at least length M.  (Maximum length is
201               constrained by sizing factor.)
202
203           List( ..., length=>[M,N] )
204               Generated lists are of length between M and N, inclusive.
205               Sizing guidance is ignored.
206
207           Advanced Note: If more than one elemgen is given, they will be used
208           in turn to create successive elements. In this case, the length of
209           the list will be multiplied by the number of generators given.  For
210           example, providing two generators will create double-length lists.
211
212       Hash(keygen, valgen)
213               my $gen = Hash( String( charset=>"A-Z", length=>3 ),
214                               Float( range=>[0.0, 100.0] );
215
216           Creates a generator for hashes (which are returned as hash refs).
217           The keys of the hash are generated by the generator given as
218           keygen, and the values are generated by the generator valgen.
219
220           The Hash generator takes an optional length modifier that specifies
221           the desired hash length (= number of keys):
222
223           Hash( ..., length=>length-spec )
224               Specifies the desired length of the generated hashes, using the
225               same length-spec syntax as for the List generator.  Note that
226               the generated hashes may be smaller than expected because of
227               key collision.
228
229       String
230               my $gen = String( length=>[3,], charset=>"A-Z" );
231
232           Creates a generator for strings.  By default the strings will be
233           drawn from the ASCII character set (0 through 127) and be of length
234           constrained by the sizing factor.  Both defaults can be changed
235           using modifiers:
236
237           String( charset=>cset )
238               Characters will be drawn from the character set given by the
239               character-set specification cset.  The syntax of cset is
240               similar the Perl "tr" operator and is a string comprised of
241               characters and character ranges.  See Char for a full
242               description.
243
244           String( length=>length-spec )
245               Specifies the desired length of generated strings, using the
246               same length-spec syntax as for the List generator.
247
248       Elements(e1, e2, ...)
249               my $gen = Elements( "alpha", "beta", "gamma" );
250
251           Creates a generator that chooses among the given elements e1, e2,
252           ... with equal probability.  Each call to the "generate" method
253           will return one of the element values.  Sizing guidance has no
254           effect on this generator.
255
256           Note: This generator builder does not accept modifiers.  If you
257           pass any, they will be interpreted as elements to be added to the
258           pool from which the generator randomly selects, which is probably
259           not what you want.
260
261       Unit(e)
262               my $gen = Unit( "alpha" );
263
264           Creates a generator that always returns the value e.  Not too
265           useful on its own but can be handy as a building block for
266           combinators to chew on.  Naturally, sizing guidance has no effect
267           on this generator.
268
269           Note: This generator builder does not accept modifiers.
270
271   Generator combinators
272       The following combinators allow you to build more complicated
273       generators from simpler ones.  These combinators are exported into your
274       code's namespace if you ask for ":combinators" or ":all" when you "use"
275       this module.
276
277       Paste(gens..., glue=>str)
278               my $gen = Paste( (String(charset=>"0-9",length=>4)) x 4,
279                                glue => " " );
280               # gens credit-card numbers like "4592 9459 9023 1369"
281
282               my $lgen = Paste( List( String(charset=>"0-9",length=>4)
283                                     , length=>4 ), glue => " " );
284               # another way of doing the same
285
286           Creates a combined generator that generates values by joining the
287           values generated by each of the supplied sub-generators gens.
288           (Generated list values will have their elements "flattened" into
289           the rest of the generated results before joining.) The resulting
290           string is returned.
291
292           The values are joined using the given glue string str.  If no glue
293           modifier is provided, the default glue is the empty string.
294
295           The sizing guidance given to the combined generator will be passed
296           unchanged to each of the sub-generators.
297
298       OneOf(gens...)
299               my $gen = OneOf( Unit(0), List(Int,length=>3) );
300               # generates scalar 0 or a 3-element list of integers
301
302           Creates a combined generator that generates each value by selecting
303           at random (with equal probability) one of the sub-generators in
304           gens and using that generator to generate the output value.
305
306           The sizing guidance given to the combined generator will be passed
307           unchanged to the selected sub-generator.
308
309           Note: This combinator does not accept modifiers.
310
311       Frequency([freq1, gen1], [freq2, gen2], ...)
312               my $gen = Frequency( [50, Unit("common"     )],
313                                    [35, Unit("less common")],
314                                    [15, Unit("uncommon"   )] );
315               # generates one of "common", "less common", or
316               # "uncommon" with respective probabilities
317               # 50%, 35%, and 15%.
318
319           Creates a combined generator that generates each value by selecting
320           at random one of the generators gen1 or gen2 or ... and using that
321           generator to generate the output value.  Each generator is selected
322           with probability proportional to its associated frequency.  (If all
323           of the given frequencies are the same, the Frequency combinator
324           effectively becomes OneOf.)  The frequencies can be any non-
325           negative numerical values you want and will be normalized to a
326           0-to-1 scale internally.  At least one frequency must be greater
327           than zero.
328
329           The sizing guidance given to the combined generator will be passed
330           unchanged to the selected sub-generator.
331
332           Note: This combinator does not accept modifiers.
333
334       Each(gens...)
335               my $gen = Each( Unit(1), Unit("X") );
336               # always generates [ 1, "X" ]
337
338           Creates a generator that returns a list (array ref) whose
339           successive elements are the successive values generated by the
340           given generators gens.
341
342           The sizing guidance given to the combined generator will be passed
343           unchanged to each sub-generator.
344
345           Note: This combinator does not accept modifiers.
346
347           (Note for technical buffs: "Each(...)" is exactly equivalent to
348           "List(..., length=>1)").
349
350       Apply(fn, gens...)
351               my $gen = Apply( sub { $_[0] x $_[1] }
352                              , Unit("X"), Unit(4) );
353               # always generates "XXXX"
354
355           Creates a generator that applies the given function fn to arguments
356           generated from each of the given sub-generators gens and returns
357           the resulting value.  Each sub-generator contributes one value, and
358           the values are passed to fn as arguments in the same order as the
359           sub-generators were given to Apply.
360
361           The sizing guidance given to the combined generator will be passed
362           unchanged to each sub-generator.
363
364           Note: The function fn is always evaluated in scalar context.  If
365           you need to generate an array, return it as an array reference.
366
367           Note: This combinator does not accept modifiers.
368
369       Map(fn, gens...)
370               my $gen = Map( sub { "X" x $_[0] }
371                            , Unit(4), Unit(3), Unit(0) );
372               # always generates [ "XXXX", "XXX", "" ]
373
374           Creates a generator that applies the given function fn to the
375           values generated by the given generators gen one at a time and
376           returns a list (array ref) whose elements are each of the
377           successive results.
378
379           The sizing guidance given to the combined generator will be passed
380           unchanged to each sub-generator.
381
382           Note: The function fn is always evaluated in scalar context.  If
383           you need to generate an array, return it as an array reference.
384
385           Note: This combinator does not accept modifiers.
386
387       Concat(gens...)
388               my $gen = Concat( List( Unit(1),   length=>3 )
389                               , List( Unit("x"), length=>1 ) );
390               # always generates [1, 1, 1, "x"]
391
392           Creates a generator that concatenates the values generated by each
393           of its sub-generators, resulting in a list (which is returned as a
394           array reference).  The values returned by the sub-generators are
395           expected to be lists (array refs).  If a sub-generator returns a
396           scalar value, it will be treated like a single-element list that
397           contains the value.
398
399           The sizing guidance given to the combined generator will be passed
400           unchanged to each sub-generator.
401
402           Note: If a sub-generator returns something other than a list or
403           scalar, you will get a run-time error.
404
405           Note: This combinator does not accept modifiers.
406
407       Flatten(gens...)
408               my $gen = Flatten( Unit( [[[[[[ 1 ]]]]]] ) );
409               # generates [1]
410
411           Flatten is just like Concat except that it recursively flattens any
412           sublists generated by the generators gen and then concatenates them
413           to generate a final a list of depth one, regardless of the depth of
414           any sublists.
415
416           The sizing guidance given to the combined generator will be passed
417           unchanged to each sub-generator.
418
419           Note: If a sub-generator returns something other than a list or
420           scalar, you will get a run-time error.
421
422           Note: This combinator does not accept modifiers.
423
424       ConcatMap(fn, gens)
425               sub take_odds { my $x = shift;
426                               $x % 2 ? [$x] : [] }
427               my $gen = ConcatMap( \&take_odds
428                                  , Unit(1), Unit(2), Unit(3) );
429               # generates [1, 3]
430
431           Creates a generator that applies the function fn to each of the
432           values generated by the given generators gen in turn, and then
433           concatenates the results.
434
435           The sizing guidance given to the combined generator will be passed
436           unchanged to each sub-generator.
437
438           Note: The function fn is always evaluated in scalar context.  If
439           you need to generate an array, return it as an array reference.
440
441           Note: If a sub-generator returns something other than a list or
442           scalar, you will get a run-time error.
443
444           Note: This combinator does not accept modifiers.
445
446       FlattenMap(fn, gens)
447               my $gen = FlattenMap( sub { [ ($_[0]) x 3 ] }
448                                   , Unit([1]), Unit([[2]]) );
449               # generates [1, 1, 1, 2, 2, 2]
450
451           Creates a generator that applies the function fn to each of the
452           values generated by the given generators gen in turn, and then
453           flattens and concatenates the results.
454
455           The sizing guidance given to the combined generator will be passed
456           unchanged to each sub-generator.
457
458           Note: The function fn is always evaluated in scalar context.  If
459           you need to generate an array, return it as an array reference.
460
461           Note: If a sub-generator returns something other than a list or
462           scalar, you will get a run-time error.
463
464           Note: This combinator does not accept modifiers.
465
466       Sized(fn, gen)
467               my $gen = Sized { 2 * $_[0] } List(Int);
468                   # ^ magnify sizing guidance by factor of two
469               my $gen2 = Sized { 10 } Int;
470                   # ^ use constant guidance of 10
471
472           Creates a generator that adjusts sizing guidance by passing it
473           through the function fn. Then it calls the generator gen with the
474           adjusted guidance and returns the result.
475
476           Note: This combinator does not accept modifiers.
477
478   Rolling your own generators
479       You can create your own generators by creating any object that has a
480       "generate" method.  Your method should accept as its first argument
481       sizing guidance size and, if it makes sense, adjust the complexity of
482       the values it generates accordingly.
483
484       The easiest way to create a generator is by using the magic function
485       "Gen".  It promotes a block of code into a generator.  For example,
486       here's a home-brew generator for times in ctime(3) format that is built
487       on top of an Int generator:
488
489         use Test::LectroTest::Generator qw( :common Gen );
490
491         my $time_gen = Int(range=>[0, 2_147_483_647], sized=>0);
492         my $ctime_gen = Gen {
493             scalar localtime $time_gen->generate( @_ );
494         };
495
496         print($ctime_gen->generate($_), "\n") for 1..5;
497         # Fri Jun  2 18:13:21 1978
498         # Thu Mar 28 00:55:51 1974
499         # Wed Mar 26 06:41:09 2025
500         # Sun Sep 11 15:39:44 2016
501         # Fri Dec 26 00:39:31 1975
502
503       Alternatively, we could build the generator using the Apply combinator:
504
505         my $ctime_gen2 = Apply { localtime $_[0] } $time_gen;
506
507       Note: "Gen" is not exported into your code's namespace by default.  If
508       you want to use it, you must import it by name or import ":all" when
509       you use this module.
510

EXAMPLES

512       Here are some examples to consider.
513
514   Simple examples
515        use strict;
516        use Test::LectroTest::Generator qw(:common);
517
518        show("Ints (sized by default)", Int);
519
520        show("Floats (sized by default)", Float);
521
522        show("Percentages (unsized)",
523             Int( range=>[0,100], sized=>0 ));
524
525        show("Lists (sized by default) of Ints (unsized) in [0,10]",
526             List( Int( sized=>0, range=>[0,10] ) ));
527
528        show("Uppercase-alpha identifiers at least 3 chars long",
529             String( length=>[3,], charset=>"A-Z" ));
530
531
532        show("Hashes (sized by default) of form AAA=>Digit",
533             Hash( String( length=>3, charset=>"A-Z" ),
534                   Int( sized=>0, range=>[0,9] ) ));
535
536        sub show {
537            print "\n", shift(), "\n";
538            my ($gen) = @_;
539            for (1..10) {
540                my $val = $gen->generate($_);
541                printf "Size %2d:  ", $_;
542                if (ref $val eq "HASH") {
543                    my @pairs = map {"$_=>$val->{$_}"} keys %$val;
544                    print "{ @pairs }";
545                }
546                elsif (ref $val eq "ARRAY") {
547                    print "[ @$val ]"
548                }
549                else {
550                    print $val;
551                }
552                print "\n";
553            }
554        }
555
556   Advanced examples
557       For these examples we use "Data::Dumper" to inspect the data structures
558       we generate.  Also, we import not only the common generator
559       constructors (like Int) but also the generic Gen constructor, which
560       lets us build generators out of blocks on the fly.
561
562           use Data::Dumper;
563           use Test::LectroTest::Generator qw(:common Gen);
564
565       First, here's a recipe for building a list of lists of integers:
566
567           my $loloi_gen = List( List( Int(sized=>0) ) );
568           print Dumper($loloi_gen->generate(10));
569
570       You may want to run the example several times to get a feel for the
571       distribution of the generated output.
572
573       Now, a more complicated example.  Here we build sized trees of random
574       depth using a recursive set of generators.
575
576           my $tree_gen = do {
577               my $density = 0.5;
578               my $leaf_gen = Int( sized=>0 );
579               my $tree_helper = \1;
580               my $branch_gen = List( Gen { $$tree_helper->generate(@_) } );
581               $tree_helper = \Gen {
582                   my ($size) = @_;
583                   return rand($size) < $density
584                       ? $leaf_gen->generate($size)
585                       : $branch_gen->generate($size + 1);
586               };
587               $$tree_helper;
588           };
589
590           print Dumper($tree_gen->generate(30));
591
592       We define a tree as either a leaf or a branch, and we randomly decide
593       between the two at each node in the growing tree.  Leaves are just
594       integers and become more likely when the sizing guidance diminishes
595       (which happens as we go deeper).  The code uses $density as a control
596       knob for leaf density.  (Try re-running the above code after changing
597       the value of $density.  Try 0, 1, and 2.)  Branches, on the other hand,
598       are lists of trees.  Because branches generate trees, and trees
599       generate branches, we use a reference trick to set up the mutually
600       recursive relationship.  This we encapsulate within a do block for
601       tidiness.
602

SEE ALSO

604       Test::LectroTest gives a quick overview of automatic, specification-
605       based testing with LectroTest.
606

AUTHOR

608       Tom Moertel (tom@moertel.com)
609

INSPIRATION

611       The LectroTest project was inspired by Haskell's QuickCheck module by
612       Koen Claessen and John Hughes:
613       http://www.cs.chalmers.se/~rjmh/QuickCheck/.
614
616       Copyright (c) 2004-13 by Thomas G Moertel.  All rights reserved.
617
618       This program is free software; you can redistribute it and/or modify it
619       under the same terms as Perl itself.
620
621
622
623perl v5.30.0                      2019-07-26    Test::LectroTest::Generator(3)
Impressum