content: {-# LANGUAGE TemplateHaskell #-}
  
  -- |
  -- Module      : Streamly.Test.Data.Unbox.TH
  -- Copyright   : (c) 2022 Composewell technologies
  -- License     : BSD-3-Clause
  -- Maintainer  : streamly@composewell.com
  -- Stability   : experimental
  -- Portability : GHC
  
  module Streamly.Test.Data.Unbox.TH (main) where
  
  --------------------------------------------------------------------------------
  -- Imports
  --------------------------------------------------------------------------------
  
  import Data.Proxy (Proxy(..))
  import Streamly.Internal.Data.MutByteArray
  import qualified Streamly.Internal.Data.MutByteArray as MBA
  
  import Test.Hspec as H
  
  --------------------------------------------------------------------------------
  -- Test helpers
  --------------------------------------------------------------------------------
  
  testSerialization ::
         forall a. (Eq a, Show a, Unbox a)
      => a
      -> IO ()
  testSerialization val = do
      arr <- MBA.new (sizeOf (Proxy :: Proxy a))
      pokeAt 0 arr val
      peekAt 0 arr `shouldReturn` val
  
  -- Size is also implicitly tested while serializing and deserializing.
  checkSizeOf :: forall a. Unbox a => Proxy a -> Int -> IO ()
  checkSizeOf _ sz = sizeOf (Proxy :: Proxy a) `shouldBe` sz
  
  checkSizeOfNew :: forall a. Unbox a => String -> Proxy a -> Int -> Spec
  checkSizeOfNew tag proxy expectation =
      it ("checkSizeOf " ++ tag) $ checkSizeOf proxy expectation
  
  data CustomDataType1 =
      CustomDataType1
      deriving (Show, Eq)
  
  data CustomDataType2
      = CDT2Constructor1
      | CDT2Constructor2
      | CDT2Constructor3
      deriving (Show, Eq)
  
  data CustomDataType3 a b c
      = CDT3Constructor1 a
      | CDT3Constructor2 a b
      | CDT3Constructor3 a b c
      deriving (Show, Eq)
  
  data CustomDataType4 a b
      = CDT4Constructor1
      | CDT4Constructor2 Bool
      | CDT4Constructor3 Bool b
      deriving (Show, Eq)
  
  $(deriveUnbox [d|instance Unbox CustomDataType1|])
  $(deriveUnbox [d|instance Unbox CustomDataType2|])
  $(deriveUnbox
      [d|instance (Unbox a, Unbox b, Unbox c) => Unbox (CustomDataType3 a b c)|])
  $(deriveUnbox [d|instance Unbox b => Unbox (CustomDataType4 a b)|])
  
  --------------------------------------------------------------------------------
  -- CPP helpers
  --------------------------------------------------------------------------------
  
  #define CHECK_SIZE(type, expectation) \
   it "checkSizeOf type" $ checkSizeOf (Proxy :: Proxy type) expectation
  
  --------------------------------------------------------------------------------
  -- Tests
  --------------------------------------------------------------------------------
  
  testCustomDatatype1TH :: Spec
  testCustomDatatype1TH = do
      it "CustomDataType1" $ testSerialization CustomDataType1
  
      CHECK_SIZE(CustomDataType1, 1)
  
  testCustomDatatype2TH :: Spec
  testCustomDatatype2TH = do
      it "CustomDataType2 1" $ testSerialization CDT2Constructor1
      it "CustomDataType2 2" $ testSerialization CDT2Constructor2
      it "CustomDataType2 3" $ testSerialization CDT2Constructor3
  
      CHECK_SIZE(CustomDataType2, 1)
  
  testCustomDatatype3TH :: Spec
  testCustomDatatype3TH = do
      it "CustomDataType3 1"
             $ testSerialization
                   (CDT3Constructor1 3 :: CustomDataType3 Int Bool Double)
      it "CustomDataType3 2"
             $ testSerialization
                   (CDT3Constructor2 3 False :: CustomDataType3 Int Bool Double)
      it "CustomDataType3 3"
             $ testSerialization (CDT3Constructor3 (3 :: Int) False (5 :: Double))
  
      checkSizeOfNew
          "CustomDataType3"
          (Proxy :: Proxy (CustomDataType3 Int Bool Double))
          (1 + sizeOf (Proxy :: Proxy Int)
             + sizeOf (Proxy :: Proxy Bool)
             + sizeOf (Proxy :: Proxy Double))
  
  testCustomDatatype4TH :: Spec
  testCustomDatatype4TH = do
      it "CustomDataType4 1"
             $ testSerialization (CDT4Constructor1 :: CustomDataType4 a Int)
      it "CustomDataType4 2"
             $ testSerialization
                   (CDT4Constructor2 True :: CustomDataType4 a Int)
      it "CustomDataType4 3"
             $ testSerialization (CDT4Constructor3 False (5 :: Int))
  
      checkSizeOfNew
          "CustomDataType4"
          (Proxy :: Proxy (CustomDataType4 a Int))
          (1 + sizeOf (Proxy :: Proxy Bool) + sizeOf (Proxy :: Proxy Int))
  
  testCases :: Spec
  testCases = do
  
      testCustomDatatype1TH
      testCustomDatatype2TH
      testCustomDatatype3TH
      testCustomDatatype4TH
  
  --------------------------------------------------------------------------------
  -- Main function
  --------------------------------------------------------------------------------
  
  moduleName :: String
  moduleName = "Data.Unboxed.TH"
  
  main :: IO ()
  main = hspec $ H.parallel $ describe moduleName testCases
  