willow-0.1.0.0: test/Test/Willow/Property/Common.hs
{-# LANGUAGE CPP #-}
{-|
Description:
Copyright: (c) 2020 Samuel May
License: MPL-2.0
Maintainer: ag.eitilt@gmail.com
Stability: experimental
Portability: portable
-}
module Test.Willow.Property.Common
( Test
, packGroup
, packTest
, genText
, checkEndOfStream
, checkInvalid
, checkInvalidInit
, checkInvalidAll
, checkTrailing
, checkTrailingInit
, checkTrailingAll
#if MIN_VERSION_base(4,11,0)
#else
, (<>)
#endif
) where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Short as BS.SH
import qualified Data.String as S
import qualified Data.Text as T
import qualified Data.Word as W
import qualified Hedgehog as H
import qualified Hedgehog.Gen as H.G
import qualified Hedgehog.Range as H.R
import Web.Willow.Common.Encoding
import Hedgehog ( (===) )
#if MIN_VERSION_base(4,11,0)
#else
import Data.Semigroup ( (<>) )
#endif
type Test = (H.PropertyName, H.Property)
packGroup :: String -> [Test] -> H.Group
packGroup name tests = H.Group
{ H.groupName = S.fromString name
, H.groupProperties = tests
}
packTest :: String -> H.PropertyT IO () -> Test
packTest name prop = (S.fromString name, H.property prop)
genText :: H.Gen T.Text
genText = H.G.text (H.R.linear 0 64) (H.G.choice [H.G.latin1, H.G.unicode])
type CheckRightFunction
= BS.ByteString
-> DecoderState
-> [Either BS.SH.ShortByteString String]
-> H.PropertyT IO ()
type CheckLeftFunction
= BS.SH.ShortByteString
-> [W.Word8]
-> BS.ByteString
-> DecoderState
-> [Either BS.SH.ShortByteString String]
-> H.PropertyT IO ()
check :: CheckRightFunction -> CheckLeftFunction -> Encoding -> [W.Word8] -> H.PropertyT IO ()
check success failure enc char = do
trail <- H.forAll . H.G.bytes $ H.R.linear 0 16
let bs = BS.pack char <> trail
(cs, state', bs') = decodeStep (initialDecoderState enc) bs
case cs of
Nothing -> H.footnote "decoder returned nothing"
Just (Left err) -> failure err char trail state' $ finalize state' bs'
_ -> success trail state' $ finalize state' bs'
case finalizeDecode state' of
[] -> return ()
_ -> H.footnote "unexpected data in recovery"
finalize :: DecoderState -> BS.ByteString -> [Either BS.SH.ShortByteString String]
finalize state bs =
let (cs, state') = decode state bs
in cs <> finalizeDecode state'
expectSuccess :: CheckRightFunction
expectSuccess trail state' bs' = bs' === finalize state' trail
expectFailure :: CheckRightFunction
expectFailure _ _ _ = H.footnote "unexpected parsed character"
error1 :: CheckLeftFunction
error1 err char trail state' bs' = do
BS.SH.unpack err === take 1 char
bs' === finalize state' trail'
where trail' = BS.pack (drop 1 char) <> trail
errorInit :: Word -> CheckLeftFunction
errorInit l err char trail state' bs' = do
BS.SH.unpack err === take l' char
bs' === finalize state' trail'
where l' = length char - fromIntegral l
trail' = BS.pack (drop l' char) <> trail
errorAll :: CheckLeftFunction
errorAll err char trail state' bs' = do
BS.SH.unpack err === char
bs' === finalize state' trail
checkInvalid :: Encoding -> [W.Word8] -> H.PropertyT IO ()
checkInvalid = check expectFailure error1
checkInvalidInit :: Word -> Encoding -> [W.Word8] -> H.PropertyT IO ()
checkInvalidInit l = check expectFailure $ errorInit l
checkInvalidAll :: Encoding -> [W.Word8] -> H.PropertyT IO ()
checkInvalidAll = check expectFailure errorAll
checkTrailing :: Encoding -> [W.Word8] -> H.PropertyT IO ()
checkTrailing = check expectSuccess error1
checkTrailingInit :: Word -> Encoding -> [W.Word8] -> H.PropertyT IO ()
checkTrailingInit l = check expectSuccess $ errorInit l
checkTrailingAll :: Encoding -> [W.Word8] -> H.PropertyT IO ()
checkTrailingAll = check expectSuccess errorAll
checkEndOfStream :: Encoding -> [W.Word8] -> H.PropertyT IO ()
checkEndOfStream enc char = do
bs <- H.forAll $ H.G.element
[ take i char
| i <- [1..length char]
]
let (cs, state', bs') = decodeStep (initialDecoderState enc) $ BS.pack bs
case cs of
Nothing -> H.footnote "decoder returned nothing"
Just (Right _) -> expectFailure BS.empty state' []
_ | not (BS.null bs') -> H.footnote "stream not completely consumed"
_ -> return ()
case finalizeDecode state' of
[] -> H.footnote "no character fallback recovered"
[Left err] -> err === BS.SH.pack bs
[Right _] -> expectFailure BS.empty state' []
_ -> H.footnote "unexpected trailing data in recovery"