I've got a protocol that I've typed like so:
data ProtocolPacket
= Packet1 Word8 Text Int8
| Packet2 Text
| Packet3 Int Text Text Text Text
| Packet4 Int Double Double Double Int16 Int16 Int16
...
deriving (Show,Eq)
In addition, I've implemented serialization/deserialization code for each packet. Naturally, I would like to test this protocol in Quickcheck and make sure that serializing and deserializing any packet for any combination of inputs will give me back exactly what I put in. So I go ahead and implement these packets for the Arbitrary
type class like so:
instance Arbitrary ProtocolPacket where
arbitrary = do
packetID <- choose (0x00,...) :: Gen Word8
case packetID of
0x00 -> do
a <- arbitrary
b <- arbitrary
c <- arbitrary
return $ Packet1 a b c
0x01 -> do
a <- arbitrary
return $ Packet2 a
0x02 -> do
a <- arbitrary
b <- arbitrary
c <- arbitrary
d <- arbitrary
e <- arbitrary
return $ Packet3 a b c d e
0x03 -> do
a <- arbitrary
b <- arbitrary
c <- arbitrary
d <- arbitrary
e <- arbitrary
f <- arbitrary
g <- arbitrary
return $ Packet4 a b c d e f g
...
Assume that I've gone ahead and defined Arbitrary
for all relevant data constructor arguments that don't have Arbitrary
defined out of the box, such code needs to be hand-written by me in order for the packet fields to be populated with meaningful data. But that's it.
But as you can see, I'm repeating myself a lot for something that is just grunt work. And this is a small sample of what I'm actually dealing with. Ideally, I would like to be able to just do this:
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data ProtocolPacket
= Packet1 Word8 Text Int8
| Packet2 Text
| Packet3 Int Text Text Text Text
| Packet4 Int Double Double Double Int16 Int16 Int16
...
deriving (Show,Eq,Generic)
instance Arbitrary ProtocolPacket
like I can do with FromJSON
and ToJSON
, but this doesn't work. Is there there a method that does?
Daniel Wagner mentioned in the comments that generic-random is capable of doing this. It was the library I was looking for, but the docs didn't make it obvious to me that such was the case. Recent to the time of this writing, Brent Yorgey posted a pretty clear tutorial on his blog that went into detail as to how to use generic-random
to do what I was asking about and more. The blog post can be found here.
For my case, the solution is simple. Using Generic.Random.Generic
from generic-random
:
{-# LANGUAGE DeriveGeneric #-}
import Generic.Random.Generic
import GHC.Generics
data ProtocolPacket
= Packet1 Word8 Text Int8
| Packet2 Text
| Packet3 Int Text Text Text Text
| Packet4 Int Double Double Double Int16 Int16 Int16
...
deriving (Show,Eq,Generic)
instance Arbitrary ProtocolPacket where
arbitrary = genericArbitrary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With