Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using QuickCheck to generate multiple arbitrary parameters for a given function

Context

I have the following function:

prop_SignAndVerify :: (PrivKey a b) => Blind a -> BS.ByteString -> Bool
prop_SignAndVerify bsk msg = case verify pk msg sig of
                             Left e -> error e
                             Right b -> b
  where
    sk  = getBlind bsk
    pk  = toPublic sk
    sig = case sign sk msg of
               Left e -> error e
               Right s -> s

I would like to do something like:

-- instance PrivKey RSA.PrivateKey RSA.PublicKey where...
genRSA :: Gen RSA.PrivateKey
genRSAMessage :: Gen BS.ByteString

main = do
  quickCheck . verbose 
  $ forAll genRSA 
  $ forAll genRSAMessage prop_SignAndVerify

That is, I would like to use explicit generators to generate arbitrary values for Blind a and BS.ByteString in the parameters of prop_SignAndVerify.

The code above, however, doesn't work, because the function forAll has type signature:

forAll :: (Show a, Testable prop) => Gen a -> (a -> prop) -> Property

This function runs the generator and apples the generated arbitrary value to (a -> prop), returning a Property. This Property however cannot be further partially applied; it hides the underlying function.

I think what we need for the above to work would be something like:

forAll' :: (Show a,  Testable prop) => Gen a -> (a -> prop) -> prop

Question

So my question is, how can I use genRSA and genRSAMessage over the parameters of prop_SignAndVerify, or is there an alternative approach?

Thanks

like image 366
plint Avatar asked May 06 '26 00:05

plint


2 Answers

You want to check prop_SignAndVerify key message for all many keys and messages. So if we had a fixed key, our tests would look like that:

main = do
  quickCheck . verbose $ 
    let key = someGeneratedKey
    in forAll genRSAMessage $ \message ->
         prop_SignAndVerify key message

If we had a fixed message, our test would look like this:

main = do
  quickCheck . verbose $ 
    forAll genRSAMessage $ \key ->
      let message = someMessage
      in prop_SignAndVerify key message

All we have to do is to combine both variants:

main = do
  quickCheck . verbose $ 
    forAll genRSA        $ \key ->
    forAll genRSAMessage $ \message ->
      prop_SignAndVerify key message

You can get rid of the message due to eta conversion, but in my opinion tests should be easily readable.

like image 169
Zeta Avatar answered May 07 '26 14:05

Zeta


You could take advantage of the monadic nature of Gen to compose a more complex Gen value from your property:

main =
  let g = do
            key <- genRSA
            message <- genRSAMessage
            return $ prop_SignAndVerify (Blind key) message
  in quickCheck . verbose $ forAll g id

Here, g is a Gen Bool value.

Alternatively, you can take advantage of the applicative nature of Gen and compose g using <*>:

main =
  let g =
        return (\key message -> prop_SignAndVerify (Blind key) message)
        <*> genRSA
        <*> genRSAMessage
  in quickCheck . verbose $ forAll g id

g is still a Gen Bool value here as well.

like image 20
Mark Seemann Avatar answered May 07 '26 13:05

Mark Seemann