packages feed

derive-storable-0.1.1.0: test/Spec/Foreign/Storable/Generic/InternalSpec.hs

{-#LANGUAGE ScopedTypeVariables #-}
{-#LANGUAGE DeriveGeneric       #-}
{-#LANGUAGE DataKinds           #-}
{-#LANGUAGE GADTs               #-}
module Foreign.Storable.Generic.InternalSpec where


-- Test tools
import Test.Hspec
import Test.QuickCheck
import GenericType 

-- Tested modules
import Foreign.Storable.Generic.Internal 

-- Additional data
import Foreign.Storable.Generic.Tools
import Foreign.Storable.Generic.Instances
import GHC.Generics
import Foreign.Marshal.Alloc (malloc, mallocBytes, free)
import Foreign.Marshal.Array (peekArray, pokeArray)
import Foreign.Ptr (Ptr, plusPtr)
import Data.Word



spec :: Spec
spec = do 
    describe "internalSizeOf" $ do
        it "is equal to: calcSize $ zip (glistSizeOf' a) (glistAlignment' a)" $ do
            property (\((NestedToType (GenericType val)) :: NestedToType 4) -> 
                internalSizeOf val `shouldBe` (calcSize $ zip (glistSizeOf' val) (glistAlignment' val) ) )
    describe "internalAlignment" $ do
        it "is equal to: maximum (glistAlignment' a)" $ do
            property (\((NestedToType (GenericType val)) :: NestedToType 4) -> 
                internalAlignment val `shouldBe` (maximum $ glistAlignment' val) )
    describe "internalOffsets" $ do
        it "is equal to: calcOffsets $ zip (glistSizeOf' a) (glistAlignment' a)" $ do
            property (\((NestedToType (GenericType val)) :: NestedToType 4) -> 
                internalOffsets val `shouldBe` (calcOffsets $ zip (glistSizeOf' val) (glistAlignment' val) ) )
    describe "internalPeekByteOff" $ do
        it "is equal to: gpeekByteOff' (internalOffsets a) ptr off" $ do
            property (\((NestedToType (GenericType (val :: f p))) :: NestedToType 4) -> do
                let size      = internalSizeOf val
                    no_fields = gnumberOf' (undefined :: f p)
                off <- generate $ suchThat arbitrary (\x -> x>=0 && x < 100)
                
                -- Area in memory to peek
                bytes <- generate $ ok_vector (off + size)
                ptr <- mallocBytes (off + size)
                pokeArray ptr bytes
                
                -- first peek
                v1 <- internalPeekByteOff ptr off :: IO (f p)
                v2 <- gpeekByteOff' (internalOffsets val) (no_fields - 1) ptr off :: IO (f p)
                
                free ptr
                v1 `shouldBe` v2
                )
        it "it reads only specified area of the memory" $ do
            property $ (\((NestedToType (GenericType (test_type1 :: f p))) :: NestedToType 4) -> do    
                let size      = internalSizeOf test_type1 
                    pokeBytes = pokeArray :: (Ptr Word8 -> [Word8] -> IO ())
                -- The memory area
                ptr <- mallocBytes (size+16)
                
                -- Beginning state.
                bytes1_beginning <- generate $ ok_vector 8
                bytes1_middle    <- generate $ ok_vector size
                bytes1_end       <- generate $ ok_vector 8
                
                pokeBytes  ptr                   bytes1_beginning
                pokeBytes (plusPtr ptr 8)        bytes1_middle
                pokeBytes (plusPtr ptr (size+8)) bytes1_end
            
                v1 <- internalPeekByteOff ptr 8 :: IO (f p)
                
                -- Changed state 
                bytes2_beginning <- generate $ suchThat (ok_vector 8) (/=bytes1_beginning)
                bytes2_end       <- generate $ suchThat (ok_vector 8) (/=bytes1_end)
                
                pokeBytes  ptr               bytes2_beginning
                pokeBytes (plusPtr ptr (size + 8)) bytes2_end
                
                v2 <- internalPeekByteOff ptr 8 :: IO (f p) 
 
                v1 `shouldBe` v2
                )
    describe "internalPokeByteOff" $ do
        it "is equal to: gpokeByteOff' (internalOffsets a) ptr off v" $ do
            property (\((NestedToType (GenericType (val :: f p))) :: NestedToType 4)  -> do
                let size = internalSizeOf val
                    no_fields = gnumberOf' (undefined :: f p)
                off <- generate $ suchThat arbitrary (\x -> x>=0 && x < 100)
                
                -- Area in memory to poke
                ptr <- mallocBytes (off + size)
                
                -- first poke
                internalPokeByteOff ptr off val
                bytes1 <- peekArray (off + size) ptr :: IO [Word8]
                
                -- second poke
                gpokeByteOff' (internalOffsets val) (no_fields - 1) ptr off val
                bytes2 <- peekArray (off + size) ptr :: IO [Word8]
 
                free ptr
                
                bytes1 `shouldBe` bytes2
                )
        it "it modifies only specified area of the memory" $ do
            property $ (\((NestedToType (GenericType test_type1)) ::NestedToType 4)-> do    
                test_type2 <- generate $ suchThat arbitrary (/=test_type1)
                -- if test_type1 is different from test_type2, then 
                -- the memory state has to change when poking both of them
                let size = internalSizeOf test_type1 
                    peekBytes = peekArray :: (Int -> Ptr Word8 -> IO [Word8])
                -- The memory area
                ptr <- mallocBytes (size+16)
               
                -- Beginning state.
                mem_state1_beginning <- peekBytes 8     ptr                   
                mem_state1_middle    <- peekBytes size (plusPtr ptr 8)              
                mem_state1_end       <- peekBytes 8    (plusPtr ptr (size+8))
            
                internalPokeByteOff ptr 8 test_type1
                -- Poked first variable
                mem_state2_beginning <- peekBytes 8     ptr
                mem_state2_middle    <- peekBytes size (plusPtr ptr 8)
                mem_state2_end       <- peekBytes 8    (plusPtr ptr (size+8))
            
                internalPokeByteOff ptr 8 test_type2
                -- Poked second state
                mem_state3_beginning <- peekBytes 8     ptr
                mem_state3_middle    <- peekBytes size (plusPtr ptr 8)
                mem_state3_end       <- peekBytes 8    (plusPtr ptr (size+8))
                
  
                -- Beginnings and ends should stay the same. The middle one should be different from
                -- the one at the beginning.
                sequence_ [mem_state1_beginning `shouldBe` mem_state2_beginning
                          ,mem_state2_beginning `shouldBe` mem_state3_beginning
                          ,mem_state1_end       `shouldBe` mem_state2_end                  
                          ,mem_state2_end       `shouldBe` mem_state3_end
                          ,(mem_state1_middle /= mem_state2_middle) || (mem_state1_middle /= mem_state3_middle) `shouldBe` True]
                )
    describe "other" $ do
        it "poke, then peek: receive the poked value" $ do
            property $ (\((NestedToType (GenericType (val :: f p) )) :: NestedToType 4) -> do
                let size = internalSizeOf val
                off <- generate $ suchThat arbitrary (\x -> x>=0 && x < 100)
                
                ptr <- mallocBytes (size + off)

                internalPokeByteOff ptr off val
                p_val <- internalPeekByteOff ptr off :: IO (f p) 
                val `shouldBe` p_val
                )