Reputation: 25146
I am using QuickCheck
to run arbitrary test cases on my code. However, in one portion of my code I have the type synonym:
type Vector = [Double]
I also have a few functions that accept a number of Vector
s as input. However, all of these functions require that the Vector
s be of the same length.
Is there a way to constrain QuickCheck
so that it only generates lists of length n?
Upvotes: 0
Views: 299
Reputation: 153172
Here's one possibility. We'll define a new class for types that can build a size-dependent random value. Then you can make a type-level list or tree or whatever and declare one Arbitrary
instance for these once and for all.
import Control.Monad
import Test.QuickCheck
class SizedArbitrary a where
sizedArbitrary :: Int -> Gen a
instance Arbitrary a => SizedArbitrary [a] where
sizedArbitrary n = replicateM n arbitrary
data Branch a b = a :+ b deriving (Eq, Ord, Show, Read)
instance (SizedArbitrary a, SizedArbitrary b) => SizedArbitrary (Branch a b) where
sizedArbitrary n = liftM2 (:+) (sizedArbitrary n) (sizedArbitrary n)
instance (SizedArbitrary a, SizedArbitrary b) => Arbitrary (Branch a b) where
arbitrary = arbitrarySizedIntegral >>= sizedArbitrary . abs
Then we can load it up in ghci and check out that it works:
*Main> let allSameLength (xs:xss) = all (==length xs) (map length xss)
*Main> quickCheck (\(xs :+ ys) -> allSameLength [xs, ys])
+++ OK, passed 100 tests.
*Main> quickCheck (\(ws :+ xs :+ ys :+ zs) -> allSameLength [ws, xs, ys, zs])
+++ OK, passed 100 tests.
Upvotes: 1
Reputation: 153172
The other obvious solution is to generate a list of tuples and unzip them. For example, in ghci:
> let allSameLength (xs:xss) = all (==length xs) (map length xss)
> quickCheck (\xys -> let (xs, ys) = unzip xys in allSameLength [xs, ys])
+++ OK, passed 100 tests.
> :{
| quickCheck (\wxyzs -> let
| (wxs, yzs) = unzip wxyzs
| (ws, xs) = unzip wxs
| (ys, zs) = unzip yzs
| in allSameLength [ws, xs, ys, zs])
| :}
+++ OK, passed 100 tests.
Upvotes: 2
Reputation: 2918
You can set constraints with the ==>
notation.
an example is:
prop_test xs = minimum xs == (head $ sort xs)
which fails:
*** Failed! Exception: 'Prelude.minimum: empty list' (after 1 test):
[]
now with a constraint:
prop_test xs = not (null xs) ==> minimum xs == (head $ sort xs)
it works:
*Main> quickCheck prop_test
+++ OK, passed 100 tests.
in your case:
prop_test xs ys = length xs == length ys ==> undefined -- whatever you want
Upvotes: 3
Reputation: 53901
A simple solution is to not have an arbitrary instance but instead to do something like
import Test.QuickCheck
import Control.Monad
prop_vec :: Int -> Gen [Double]
prop_vec = flip replicateM arbitrary . abs
prop_addComm :: Int -> Gen Bool
prop_addComm i = do
v <- prop_vec i
u <- prop_vec i
return $ u + v = v + u --assuming you'd added a Num instance for your vectors
There's never a typeclass so you get less helpful failures, but it's simpler to whip up.
Upvotes: 3