packages feed

Barracuda-1.0.2: Tests/QuickCheck/Parser.hs

module Tests.QuickCheck.Parser
	(testGeneratorParserId) where

import Control.Monad
import Test.QuickCheck hiding (generate)

import Network.AdHoc.ParserStrict
import Network.AdHoc.Encryption
import Network.AdHoc.Generator (generateMessage)
import Network.AdHoc.Message
import Network.AdHoc.UserID
import Network.AdHoc.Channel
import Network.AdHoc.Signature

import Text.XML.HaXml.Pretty(document)
import Text.XML.HaXml.Parse(xmlParse)

import Data.ByteString (pack)
import Data.Word
import System.Random
import Data.Time.Clock
import Data.Time.Calendar

genAscii :: Gen Char
genAscii = elements (['a'..'z']++['A'..'Z'])

genAsciiString :: Int -> Gen String
genAsciiString min = sized (\len -> replicateM (len+min) genAscii)

genUser :: Gen UserID
genUser = do
	id <- genAsciiString 1
	host <- genAsciiString 1
	return $ UserID id host

genChannelID :: Gen ChannelID
genChannelID = do
	cvalue <- genAsciiString 1
	chost <- genAsciiString 1
	return $ ChannelID cvalue chost

genList :: Int -> Gen a -> Gen [a]
genList min gen = sized $ \len -> replicateM (min+len) gen

genMaybe :: Gen a -> Gen (Maybe a)
genMaybe gen = oneof [return Nothing,gen>>=return.Just]

genWord8 :: Gen Word8
genWord8 = choose (0,255::Int) >>= return.fromIntegral

genWord64 :: Gen Word64
genWord64 = choose (0,18446744073709551615::Integer) >>= return.fromIntegral

msgGen :: Gen InternalMessage
msgGen = frequency
	[(1,genHello)
	,(1,genAck)
	,(1,genRouting)
	,(6,genTarget)
	,(4,genFlood)
	,(1,genObscure)]

genHello :: Gen InternalMessage
genHello = do
	users <- genList 1 genUser
	vers <- choose (1,100)
	greet <- genMaybe (genAsciiString 0)
	return $ Hello users vers greet

genAck :: Gen InternalMessage
genAck = do
	sender <- genUser
	msgid <- genAsciiString 1
	return $ Ack sender msgid

genRouting :: Gen InternalMessage
genRouting = do
	routes <- sized $ \len -> replicateM len (do
		user <- genUser
		hops <- choose (1,30)
		return (user,hops)
		)
	return $ Routing routes

genTarget :: Gen InternalMessage
genTarget = do
	rt <- fmap (fmap toInternal) $ genRoutedTarget False
	return (Target rt)

genRouted :: Gen a -> Gen (Routed a ExternalSignature)
genRouted inner = do
	ttl <- choose (0,360::Int) >>= return.fromIntegral
	user <- genUser
	msgid <- genAsciiString 1
	cont <- inner
	return $ Routed ttl user msgid cont Nothing

genRoutedTarget :: Bool -> Gen (Routed TargetContent ExternalSignature)
genRoutedTarget in_nack = do
	let withoutNack =
		[genGetCertificate
		,genCertificate
		,genMessage
		,genGetKey
		,genKey]
	genRouted (if in_nack
		then oneof withoutNack
		else oneof $ [genNack]++withoutNack)

genNack :: Gen TargetContent
genNack = do
	rt <- genRoutedTarget True
	return (Nack rt)

genGetCertificate :: Gen TargetContent
genGetCertificate = genUser >>= return.(GetCertificate)

genCertificate :: Gen TargetContent
genCertificate = do
	receivers <- genList 1 genUser
	for <- genUser
	dat <- genList 1 genWord8
	return $ Certificate receivers for (pack dat)

genAttachment :: Gen Attachment
genAttachment = do
	filename <- genAsciiString 1
	appType <- genAsciiString 1
	content <- fmap pack $ genList 1 genWord8
	return $ Attachment filename appType content

genTime :: Gen UTCTime
genTime = do
	day <- choose (20000,60000)
	secs <- choose (0,60*60*24)
	return $ UTCTime
		{utctDay = ModifiedJulianDay day
		,utctDayTime = fromInteger secs
		}

genMessage :: Gen TargetContent
genMessage = do
	receivers <- genList 1 genUser
	cname <- genAsciiString 1
	cid <- genChannelID
	content <- genMessageContent
	time <- genTime
	delay <- choose (0,360::Int) >>= return.fromIntegral
	return $ Message receivers (mkChannelName cname) cid content time delay

genMessageContent :: Gen MessageContent
genMessageContent = do
	txt <- genAsciiString 1
	key <- genMaybe genWord64
	attachments <- genList 0 genAttachment
	case key of
		Just rkey -> do
			iv <- genWord64
			encattach <- mapM (\attach -> do
				aiv <- genWord64
				return $ encryptAttachment rkey aiv attach
				) attachments
			return $ EncryptedMessage (encrypt rkey iv txt) encattach
		Nothing -> return $ UnencryptedMessage txt attachments
genGetKey :: Gen TargetContent
genGetKey = do
	receiver <- genUser
	cname <- genAsciiString 1
	cid <- genChannelID
	return $ GetKey receiver (mkChannelName cname) cid

genCipher :: Gen CipherType
genCipher = oneof [return CipherDES_CBC
		  ,return CipherNone
		  ,genAsciiString 1 >>= return.CipherUnknown]

genKey :: Gen TargetContent
genKey = do
	receiver <- genUser
	cname <- genAsciiString 1
	cid <- genChannelID
	cipher <- genCipher
	key <- fmap RSAEncrypted $ fmap pack $ genList 1 genWord8
	return $ Key receiver (mkChannelName cname) cid cipher key

genFlood :: Gen InternalMessage
genFlood = do
	rt <- fmap (fmap toInternal) $ genRouted (oneof
		[genChannel
		,genJoinLeave True
		,genJoinLeave False
		,genAnonymous
		])
	return $ Flood rt

genChannel :: Gen FloodContent
genChannel = do
	cname <- genAsciiString 1
	cid <- genChannelID
	title <- genAsciiString 1
	users <- genList 1 genUser
	priv <- arbitrary
	return $ Channel (mkChannelName cname) cid title users priv

genJoinLeave :: Bool -> Gen FloodContent
genJoinLeave join = do
	cname <- genAsciiString 1
	cid <- genChannelID
	return $ (if join then Join else Leave) (mkChannelName cname) cid

genAnonymous :: Gen FloodContent
genAnonymous = do
	text <- genAsciiString 1
	attach <- genList 0 genAttachment
	time <- genTime
	delay <- choose (0,360::Int) >>= return.fromIntegral
	return $ Anonymous text attach time delay

genObscure :: Gen InternalMessage
genObscure = fmap Obscure $ fmap (fmap $ const ()) $ genRouted (fmap RSAEncrypted $ fmap pack $ genList 0 genWord8)

classifyMessage :: ProtocolMessage sign -> String
classifyMessage (Hello _ _ _) = "Hello"
classifyMessage (Ack _ _) = "Ack"
classifyMessage (Routing _) = "Routing"
classifyMessage (Target (Routed _ _ _ tc _)) = case tc of
	Nack _ -> "Nack"
	GetCertificate _ -> "GetCertificate"
	Certificate _ _ _ -> "Certificate"
	Message _ _ _ _ _ _ -> "Message"
	GetKey _ _ _ -> "GetKey"
	Key _ _ _ _ _ -> "Key"
classifyMessage (Flood (Routed _ _ _ fc _)) = case fc of
	Channel _ _ _ _ _ -> "Channel"
	Join _ _ -> "Join"
	Leave _ _ -> "Leave"
	Anonymous _ _ _ _ -> "Anonymous"
classifyMessage (Obscure _) = "Obscure"

genParseId parser = forAll msgGen (\msg -> classify True (classifyMessage msg) $
	(parser (xmlParse "inp" (fst (generateMessage (map show [0..]) msg))))
	== Right (fmap (const Nothing) msg))

testGeneratorParserId = genParseId parseMessageNoValidate