packages feed

pantry-0.8.3: test/Pantry/Internal/StaticBytesSpec.hs

{-# LANGUAGE NoImplicitPrelude   #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Pantry.Internal.StaticBytesSpec
  ( spec
  ) where

import           Control.Monad ( replicateM )
import qualified Data.ByteString as B
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Vector.Primitive as VP
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Unboxed as VU
import           Pantry.Internal.StaticBytes
import           RIO
import           Test.Hspec
import           Test.Hspec.QuickCheck
import           Test.QuickCheck

spec :: Spec
spec = do
  describe "ByteString" $ tests B.pack
  describe "Storable Vector" $ tests VS.fromList
  describe "Unboxed Vector" $ tests VU.fromList
  describe "Primitive Vector" $ tests VP.fromList

tests :: (Eq dbytes, Show dbytes, DynamicBytes dbytes) => ([Word8] -> dbytes) -> Spec
tests pack = do
  it "disallows 4 bytes" $ property $ \(w1,w2,w3,w4) ->
    toStaticExact (pack [w1,w2,w3,w4]) `shouldBe` (Left NotEnoughBytes :: Either StaticBytesException Bytes8)
  it "toStaticExact matches ByteString" $ property $ \(w1,w2,w3,w4) -> property $ \(w5,w6,w7,w8) -> do
    let octets =  [w1,w2,w3,w4,w5,w6,w7,w8]
        (expected :: Bytes8) = either impureThrow id $ toStaticExact (B.pack octets)
        actual = either impureThrow id $ toStaticExact (pack octets)
    actual `shouldBe` expected

  it "fromStatic round trips" $ property $ \(w1,w2,w3,w4) -> property $ \(w5,w6,w7,w8) -> do
    let octets = [w1,w2,w3,w4,w5,w6,w7,w8]
        v1 = pack octets
        (b8 :: Bytes8) = either impureThrow id $ toStaticExact v1
        v2 = fromStatic b8
    v2 `shouldBe` v1

  it "allows 8 bytes" $ property $ \(w1,w2,w3,w4) -> property $ \(w5,w6,w7,w8) -> do
    let bs = pack [w1,w2,w3,w4,w5,w6,w7,w8]
    case toStaticExact bs of
      Left e -> throwIO e
      Right b8 -> fromStatic (b8 :: Bytes8) `shouldBe` bs
    toStaticExact bs `shouldBe` (Left NotEnoughBytes :: Either StaticBytesException Bytes16)
  it "padding is the same as trailing nulls" $ property $ \(w1,w2,w3,w4) -> do
    let ws = [w1,w2,w3,w4]
        bs1 = pack $ ws ++ replicate 4 0
        bs2 = pack ws
    Right (toStaticPadTruncate bs2 :: Bytes8) `shouldBe` toStaticExact bs1

  prop "handles bytes16" $ \octets -> do
    let bs = pack $ take 16 octets
        (b16 :: Bytes16) = either impureThrow id $ toStaticPad bs
    fromStatic b16 `shouldBe` pack (take 16 (octets ++ replicate 16 0))

  it "spot check bytes16" $ forAll (replicateM 16 arbitrary) $ \ws -> do
    let bs = pack ws
        (b16 :: Bytes16) = either impureThrow id $ toStaticPad bs
    fromStatic b16 `shouldBe` pack ws

  prop "handles bytes32" $ \octets -> do
    let bs = pack $ take 32 octets
        (b32 :: Bytes32) = either impureThrow id $ toStaticPad bs
    fromStatic b32 `shouldBe` pack (take 32 (take 32 octets ++ replicate 32 0))

  prop "fuzz with encodeUtf8" $ \chars -> do
    let t = T.pack $ filter (/= '\0') chars
        bs = TE.encodeUtf8 t
        bs128 = pack $ B.unpack $ B.take 128 $ bs `B.append` B.replicate 128 0
        b128 = toStaticPadTruncate (pack $ B.unpack bs) :: Bytes128

    fromStatic b128 `shouldBe` bs128