streamly-0.9.0: test/Streamly/Test/Data/Unbox.hs
{-# LANGUAGE StandaloneDeriving, DeriveAnyClass #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# HLINT ignore "Use newtype instead of data" #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}
-- |
-- Module : Streamly.Test.Data.Unbox
-- Copyright : (c) 2022 Composewell technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
module Streamly.Test.Data.Unbox (main) where
--------------------------------------------------------------------------------
-- Imports
--------------------------------------------------------------------------------
import Data.Complex (Complex ((:+)))
import Data.Functor.Const (Const (..))
import Data.Functor.Identity (Identity (..))
import Data.Proxy (Proxy(..))
import GHC.Generics (Generic, Rep(..))
import GHC.Real (Ratio(..))
import Streamly.Internal.Data.Unboxed
( PeekRep(..)
, PokeRep(..)
, SizeOfRep(..)
, Unbox(..)
, genericPeekByteIndex
, genericPokeByteIndex
, genericSizeOf
, newUnpinnedBytes
, pokeByteIndex
)
import Test.Hspec as H
--------------------------------------------------------------------------------
-- Types
--------------------------------------------------------------------------------
-- Unit instance uses a hack, so test all cases
data Unit =
Unit
deriving (Unbox, Show, Generic, Eq)
data UnarySum
= Sum1
| Sum2
deriving (Unbox, Show, Generic, Eq)
data UnarySum2
= UnitSum1 Unit
| UnitSum2 Unit
deriving (Generic, Unbox, Eq, Show)
data Unit1 =
Unit1 Unit
deriving (Generic, Unbox, Eq, Show)
data Unit2 =
Unit2 Unit Unit
deriving (Generic, Unbox, Eq, Show)
data Unit3 =
Unit3 Int Unit Int
deriving (Generic, Unbox, Eq, Show)
data Unit4 =
Unit4 Int Unit1 Int
deriving (Generic, Unbox, Eq, Show)
{-# ANN Single "HLint: ignore" #-}
data Single =
Single Int
deriving (Unbox, Show, Generic, Eq)
data Product2 =
Product2 Int Char
deriving (Unbox, Show, Generic, Eq)
data SumOfProducts
= SOP0
| SOP1 Int
| SOP2 Int Char
| SOP3 Int Int Int
deriving (Unbox, Show, Generic, Eq)
data NestedSOP
= NSOP0 SumOfProducts
| NSOP1 SumOfProducts
deriving (Unbox, Show, Generic, Eq)
--------------------------------------------------------------------------------
-- Standalone derivations
--------------------------------------------------------------------------------
-- Ratio does not have a Generic instance by default
deriving instance Generic (Ratio Int)
--------------------------------------------------------------------------------
-- Test helpers
--------------------------------------------------------------------------------
testSerialization ::
forall a. (Eq a, Show a, Unbox a)
=> a
-> IO ()
testSerialization val = do
arr <- newUnpinnedBytes (sizeOf (Proxy :: Proxy a))
pokeByteIndex 0 arr val
peekByteIndex 0 arr `shouldReturn` val
testGenericConsistency ::
forall a.
( Eq a
, Show a
, Unbox a
, Generic a
, SizeOfRep (Rep a)
, PeekRep (Rep a)
, PokeRep (Rep a)
)
=> a
-> IO ()
testGenericConsistency val = do
-- Test the generic sizeOf
sizeOf (Proxy :: Proxy a) `shouldBe` genericSizeOf (Proxy :: Proxy a)
-- Test the serialization and deserialization
arr <- newUnpinnedBytes (sizeOf (Proxy :: Proxy a))
pokeByteIndex 0 arr val
genericPeekByteIndex arr 0 `shouldReturn` val
genericPokeByteIndex arr 0 val
peekByteIndex 0 arr `shouldReturn` val
-- Size is also implicitly tested while serializing and deserializing.
checkSizeOf :: forall a. Unbox a => Proxy a -> Int -> IO ()
checkSizeOf _ size = sizeOf (Proxy :: Proxy a) `shouldBe` size
--------------------------------------------------------------------------------
-- CPP helpers
--------------------------------------------------------------------------------
#define CHECK_SIZE(type, expectation) \
it "checkSizeOf type" $ checkSizeOf (Proxy :: Proxy type) expectation
--------------------------------------------------------------------------------
-- Tests
--------------------------------------------------------------------------------
testCases :: Spec
testCases = do
it "Unit" $ testSerialization Unit
it "Unit1" $ testSerialization (Unit1 Unit)
it "Unit2" $ testSerialization (Unit2 Unit Unit)
it "Unit3" $ testSerialization (Unit3 1234 Unit 4567)
it "Unit4" $ testSerialization (Unit4 1234 (Unit1 Unit) 4567)
it "UnarySum Sum1" $ testSerialization Sum1
it "UnarySum Sum2" $ testSerialization Sum2
it "UnarySum2 UnitSum1" $ testSerialization (UnitSum1 Unit)
it "UnarySum2 UnitSum2" $ testSerialization (UnitSum2 Unit)
it "Single" $ testSerialization (Single 2)
it "Product2" $ testSerialization (Product2 2 'b')
it "SumOfProducts SOP0" $ testSerialization SOP0
it "SumOfProducts SOP1" $ testSerialization (SOP1 1)
it "SumOfProducts SOP2" $ testSerialization (SOP2 1 'a')
it "SumOfProducts SOP3" $ testSerialization (SOP3 1 2 3)
CHECK_SIZE(Unit, 1)
CHECK_SIZE(Unit1, 1)
CHECK_SIZE(Unit2, 2)
CHECK_SIZE(Unit3, 17)
CHECK_SIZE(Unit4, 17)
CHECK_SIZE(UnarySum, 1)
CHECK_SIZE(UnarySum2, 2)
CHECK_SIZE(Single, 8)
CHECK_SIZE(Product2, 12)
CHECK_SIZE(SumOfProducts, 25)
CHECK_SIZE(NestedSOP, 26)
it "Bool" $ testSerialization True
it "Complex Int" $ testSerialization (5 :+ 3 :: Complex Int)
it "Ratio Int" $ testSerialization (5 :% 3 :: Ratio Int)
it "Const Float Int" $ testSerialization (Const 333.5678 :: Const Float Int)
it "Identity Int" $ testSerialization (Identity 56760 :: Identity Int)
it "GenericConsistency Bool" $ testGenericConsistency True
it "GenericConsistency (Complex Int)"
$ testGenericConsistency (5 :+ 3 :: Complex Int)
it "GenericConsistency (Ratio Int)"
$ testGenericConsistency (5 :% 3 :: Ratio Int)
it "GenericConsistency (Const Float Int)"
$ testGenericConsistency (Const 333.5678 :: Const Float Int)
it "GenericConsistency (Identity Int)"
$ testGenericConsistency (Identity 56760 :: Identity Int)
-- Fingerprint does not work for GHC 8.6.5
-- it "Fingerprint" $ testSerialization (Fingerprint 123456 876588)
-- it "GenericConsistency Fingerprint"
-- $ testGenericConsistency (Fingerprint 123456 876588)
--------------------------------------------------------------------------------
-- Main function
--------------------------------------------------------------------------------
moduleName :: String
moduleName = "Data.Unboxed"
main :: IO ()
main = hspec $ H.parallel $ describe moduleName testCases