testing - How to use SmallCheck in Haskell? -


i trying use smallcheck test haskell program, cannot understand how use library test own data types. apparently, need use test.smallcheck.series. however, find documentation extremely confusing. interested in both cookbook-style solutions , understandable explanation of logical (monadic?) structure. here questions have (all related):

  • if have data type data person = snowwhite | dwarf integer, how explain smallcheck valid values dwarf 1 through dwarf 7 (or snowwhite)? if have complicated fairytale data structure , constructor maketale :: [person] -> fairytale, , want smallcheck make fairytale-s lists of person-s using constructor?

    i managed make quickcheck work without getting hands dirty using judicious applications of control.monad.liftm functions maketale. couldn't figure out way smallcheck (please explain me!).

  • what relationship between types serial, series, etc.?

  • (optional) point of coseries? how use positive type smallcheck.series?

  • (optional) elucidation of logic behind should monadic expression, , regular function, in context of smallcheck, appreciated.

if there there intro/tutorial using smallcheck, i'd appreciate link. thank much!

update: should add useful , readable documentation found smallcheck this paper (pdf). not find answer questions there on first look; more of persuasive advertisement tutorial.

update 2: moved question weird identity shows in type of test.smallcheck.list , other places separate question.

note: answer describes pre-1.0 versions of smallcheck. see this blog post important differences between smallcheck 0.6 , 1.0.

smallcheck quickcheck in tests property on part of space of possible types. difference tries exhaustively enumerate series all of "small" values instead of arbitrary subset of smallish values.

as hinted, smallcheck's serial quickcheck's arbitrary.

now serial pretty simple: serial type a has way (series) generate series type function depth -> [a]. or, unpack that, serial objects objects know how enumerate "small" values of. given depth parameter controls how many small values should generate, let's ignore minute.

instance serial bool series _ = [false, true] instance serial char series _ = "abcdefghijklmnopqrstuvwxyz" instance serial => serial (maybe a)   series d = nothing : map (series d) 

in these cases we're doing nothing more ignoring depth parameter , enumerating "all" possible values each type. can automatically types

instance (enum a, bounded a) => serial series _ = [minbound .. maxbound] 

this simple way of testing properties exhaustively—literally test every single possible input! there @ least 2 major pitfalls, though: (1) infinite data types lead infinite loops when testing , (2) nested types lead exponentially larger spaces of examples through. in both cases, smallcheck gets large quickly.

so that's point of depth parameter—it lets system ask keep our series small. documentation, depth the

maximum depth of generated test values

for data values, depth of nested constructor applications.

for functional values, both depth of nested case analysis , depth of results.

so let's rework our examples keep them small.

instance serial bool    series 0 = []   series 1 = [false]   series _ = [false, true] instance serial char    series d = take d "abcdefghijklmnopqrstuvwxyz" instance serial => serial (maybe a)   -- shrink d 1 since we're adding nothing   series d = nothing : map (series (d-1))  instance (enum a, bounded a) => serial series d = take d [minbound .. maxbound] 

much better.


so what's coseries? coarbitrary in arbitrary typeclass of quickcheck, lets build series of "small" functions. note we're writing instance on input type---the result type handed in serial argument (that i'm below calling results).

instance serial bool   coseries results d = [\cond -> if cond r1 else r2 |                          r1 <- results d                         r2 <- results d] 

these take little more ingenuity write , i'll refer use alts methods i'll describe briefly below.


so how can make series of persons? part easy

instance series person   series           d = snowwhite : take (d-1) (map dwarf [1..7])   ... 

but our coseries function needs generate every possible function persons else. can done using altsn series of functions provided smallcheck. here's 1 way write it

 coseries results d = [\person ->                           case person of                            snowwhite -> f 0                            dwarf n   -> f n                        | f <- alts1 results d ] 

the basic idea altsn results generates series of n-ary function n values serial instances serial instance of results. use create function [0..7], defined serial value, whatever need, map our persons numbers , pass 'em in.


so have serial instance person, can use build more complex nested serial instances. "instance", if fairytale list of persons, can use serial => serial [a] instance alongside our serial person instance create serial fairytale:

instance serial fairytale   series = map makefairytale . series   coseries results = map (makefairytale .) . coseries results 

(the (makefairytale .) composes makefairytale each function coseries generates, little confusing)


Comments

Popular posts from this blog

jquery - How can I dynamically add a browser tab? -

node.js - Getting the socket id,user id pair of a logged in user(s) -

keyboard - C++ GetAsyncKeyState alternative -