packages feed

bytestring-encodings-0.2.0.2: tests/Ascii.hs

{-# 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)