Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell QuickCheck to generate only valid list indices for a parameter?

Say I want to write some unit tests for the (!!) function.

my_prop xs n = ...

I want to restrict n to only valid indexes and I know I could do something like

my_prop xs n = (not.null) (drop n xs) ==> ...

But this makes it so that the vast majority of the generated cases are invalid and get thrown away. Is there a way I can set things up so that QuickCheck generates the xs list first and uses its value to generate only valid cases of n?

like image 569
hugomg Avatar asked Oct 10 '12 20:10

hugomg


3 Answers

Using forAll, you can specify a generator for n which depends on the earlier arguments, e.g.

my_prop (NonEmpty xs) = forAll (choose (0, length xs - 1)) $ \n -> ...
like image 88
hammar Avatar answered Nov 15 '22 06:11

hammar


You can make a generator that only creates valid indices and write your property like

import Test.QuickCheck
import Test.QuickCheck.Gen
import System.Random

indices :: [a] -> Gen Int
indices xs = MkGen $ \sg _ -> fst $ randomR (0, length xs - 1) sg

my_prop :: [Char] -> Property
my_prop xs = not (null xs) ==> forAll (indices xs) (\i -> xs !! i /= '0')

eliminating the Int argument.

like image 30
Daniel Fischer Avatar answered Nov 15 '22 06:11

Daniel Fischer


As suggested by Daniel Wagner, one possibility is creating my own datatype and giving it an Arbitrary instance.

data ListAndIndex a = ListAndIndex [a] Int deriving (Show)

instance Arbitrary a => Arbitrary (ListAndIndex a) where
   arbitrary = do
     (NonEmpty xs) <- arbitrary
     n  <- elements [0..(length xs - 1)]
     return $ ListAndIndex xs n

NonEmpty is from a custom type in Test.QuickCheck.Modifiers for generating non empty lists.

like image 5
hugomg Avatar answered Nov 15 '22 06:11

hugomg