{-# LANGUAGE TemplateHaskell #-}
module Ascii
( testAscii
) where
import Control.Applicative (liftA2)
import Data.Bits ((.&.), xor)
import Data.ByteString.Encodings (isAscii)
import Data.Word (Word8, Word64)
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Internal as BI
sizedByteStringSucc :: Range.Size -> Gen B.ByteString
sizedByteStringSucc (Range.Size n) = do
m <- Gen.enum 0 n
fmap B.pack $ Gen.list (Range.constant 0 m) (randWord8)
where
randWord8 :: Gen Word8
randWord8 = fmap (\w -> w .&. 0x7F) (Gen.word8 Range.constantBounded)
randSuccBS :: Gen B.ByteString
randSuccBS = do
bs <- Gen.sized sizedByteStringSucc
n <- Gen.enum 0 7
pure (B.drop n bs) -- to give us some with non-0 offset
-- This should generate a 'ByteString' for which isAscii should short-circuit;
-- i.e. the check of any 'Word8' in the 'ByteString' should fail.
sizedByteString_ShortCircuit :: Range.Size -> Gen B.ByteString
sizedByteString_ShortCircuit (Range.Size n) = do
m <- Gen.enum 0 n
fmap B.pack $ Gen.list (Range.constant 0 m) (randWord8)
where
randWord8 :: Gen Word8
randWord8 = fmap (\w -> if w > 127 then w else w `xor` 0xFF) (Gen.word8 Range.constantBounded)
-- a ByteString b for which isAscii b should never fail
noFailBS :: [Word8]
noFailBS = [0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F]
-- This makes sure we test past the first 8 bytes, and before the last 8 bytes, by generating a failing 'ByteString' of
-- length >= 1, then prepending and appending 'noFailBS' to it.
sizedByteStringFail :: Range.Size -> Gen B.ByteString
sizedByteStringFail (Range.Size n) = do
m <- Gen.enum 1 (n+1)
fmap B.pack $
liftA2 (++) (pure noFailBS) $
liftA2 (++) (Gen.list (Range.constant 8 (m+8)) randWord8) (pure noFailBS)
where
randWord8 :: Gen Word8
randWord8 = fmap (\w -> if w > 127 then w else w `xor` 0xFF) (Gen.word8 Range.constantBounded)
randFailBS_ShortCircuit :: Gen B.ByteString
randFailBS_ShortCircuit = do
bs <- Gen.sized sizedByteString_ShortCircuit
n <- Gen.enum 0 7
pure (B.drop n bs) -- to give us some with non-0 offset
randFailBS :: Gen B.ByteString
randFailBS = do
bs <- Gen.sized sizedByteStringFail
n <- Gen.enum 0 7
pure (B.drop n bs) -- to give us some with non-0 offset
showRawByteString :: B.ByteString -> String
showRawByteString bs@(BI.PS fptr off len) =
"Payload: " ++ show (B.unpack bs) ++ ", ptr: " ++ show fptr ++ ", offset: " ++ show off ++ " len: " ++ show len
prop_noFail :: Property
prop_noFail =
property $ do
isAscii (B.pack noFailBS) === True
prop_isAsciiSucc :: Property
prop_isAsciiSucc =
property $ do
xs <- forAllWith showRawByteString randSuccBS
isAscii xs === True
prop_isAsciiFail_ShortCircuit :: Property
prop_isAsciiFail_ShortCircuit =
property $ do
xs <- forAllWith showRawByteString randFailBS_ShortCircuit
if B.length xs > 0
then isAscii xs === False
else isAscii xs === True
prop_isAsciiFail :: Property
prop_isAsciiFail =
property $ do
xs <- forAllWith showRawByteString randFailBS
if B.length xs > 0
then isAscii xs === False
else isAscii xs === True
testAscii :: IO Bool
testAscii = checkParallel $$(discover)