3
votes

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

2

2 Answers

8
votes

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.

2
votes

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.