2{-# LANGUAGE TemplateHaskell #-}
4-- Required for Eq instance declaration of HigherOrderType
5{-# LANGUAGE UndecidableInstances #-}
7-- We are generating an orphan instance of Serialize for Identity.
8{-# OPTIONS_GHC -fno-warn-orphans #-}
11-- Module : Streamly.Test.Data.Serialize
12-- Copyright : (c) 2022 Composewell technologies
13-- License : BSD-3-Clause
14-- Maintainer : streamly@composewell.com
15-- Stability : experimental
18module Streamly.Test.Data.Serialize (main) where
20--------------------------------------------------------------------------------
22--------------------------------------------------------------------------------
24import Data.Foldable (forM_)
25import Data.Word (Word8)
26import System.Random (randomRIO)
27import Streamly.Internal.Data.MutByteArray (MutByteArray)
28import GHC.Generics (Generic)
29import Streamly.Data.MutByteArray (Serialize)
30import Streamly.Test.Data.Serialize.TH (genDatatype)
32import Data.Functor.Identity (Identity (..))
34import qualified Streamly.Internal.Data.Array as Array
35import qualified Streamly.Internal.Data.MutByteArray as Serialize
37import qualified Streamly.Test.Data.Serialize.CompatV0 as CompatV0
38import qualified Streamly.Test.Data.Serialize.CompatV1 as CompatV1
40import Test.Hspec.QuickCheck
44--------------------------------------------------------------------------------
45-- Serialize configuration
46--------------------------------------------------------------------------------
48#ifdef ENABLE_constructorTagAsString
49#define CONF_NAME "ENABLE_constructorTagAsString"
50#define CONF (Serialize.encodeConstrNames True)
52#define CONF_NAME "DEFAULT"
56--------------------------------------------------------------------------------
58--------------------------------------------------------------------------------
63$(Serialize.deriveSerializeWith CONF [d|instance Serialize Unit|])
68$(Serialize.deriveSerializeWith CONF [d|instance Serialize The|])
70--------------------------------------------------------------------------------
72--------------------------------------------------------------------------------
74$(genDatatype "CustomDatatype" 15)
75$(Serialize.deriveSerializeWith CONF [d|instance Serialize CustomDatatype|])
77--------------------------------------------------------------------------------
78-- Types with functional parameters
79--------------------------------------------------------------------------------
81data HigherOrderType f =
87instance (Eq (f Int), Eq (f Char)) => Eq (HigherOrderType f) where
88 (==) a b = (field0 a == field0 b) && (field1 a == field1 b)
90instance (Show (f Int), Show (f Char)) => Show (HigherOrderType f) where
91 show a = "HigherOrderType " ++ show (field0 a) ++ " " ++ show (field1 a)
93$(Serialize.deriveSerializeWith CONF
94 [d|instance Serialize a => Serialize (Identity a)|])
95$(Serialize.deriveSerializeWith CONF
96 [d|instance Serialize (HigherOrderType Identity)|])
98--------------------------------------------------------------------------------
100--------------------------------------------------------------------------------
104 = Tree (BinTree a) (BinTree a)
106 deriving (Show, Read, Eq, Generic)
108$(Serialize.deriveSerializeWith
110 [d|instance Serialize a => Serialize (BinTree a)|])
112-- XXX This may not terminate, or could become really large.
113instance Arbitrary a => Arbitrary (BinTree a) where
114 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
116-- Make a balanced tree of given level
117mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
118mkBinTree = go (generate arbitrary)
123 go r n = Tree <$> go r (n - 1) <*> go r (n - 1)
125--------------------------------------------------------------------------------
127--------------------------------------------------------------------------------
129upgradeRec :: (a -> b) -> CompatV0.Rec a -> CompatV1.Rec b
132 { CompatV1.initialField = CompatV0.initialField val
133 , CompatV1.otherField = f (CompatV0.otherField val)
134 , CompatV1.theLastField = CompatV0.theLastField val
135 , CompatV1.aNewField = Nothing
138upgradeRiver :: CompatV0.River -> CompatV1.River
139upgradeRiver = read . show
141downgradeRec :: (a -> b) -> CompatV1.Rec a -> CompatV0.Rec b
144 { CompatV0.initialField = CompatV1.initialField val
145 , CompatV0.otherField = f (CompatV1.otherField val)
146 , CompatV0.theLastField = CompatV1.theLastField val
149downgradeRiver :: CompatV1.River -> CompatV0.River
150downgradeRiver = read . show
153 CompatV0.Rec (CompatV0.Rec CompatV0.River)
154 -> CompatV1.Rec (CompatV1.Rec CompatV1.River)
156testCompatibility v0 v1 = do
157 let upgradedV0 = upgradeRec (upgradeRec upgradeRiver) v0
158 downgradedV1 = downgradeRec (downgradeRec downgradeRiver) v1
161 peekAndVerify res upgradedV0
164 peekAndVerify res1 downgradedV1
166--------------------------------------------------------------------------------
168--------------------------------------------------------------------------------
171 forall a. Serialize.Serialize a
173 -> IO (MutByteArray, Int, Int)
175 let sz = Serialize.addSizeTo 0 val
178 randomOff <- randomRIO (10, excessSize)
179 -- Use a proper slice to test instead of the array directly. This will catch
180 -- any hardcoded 0 offsets
181 let arrSize = sz + excessSize
182 serStartOff = randomOff
183 serEndOff = randomOff + sz
184 arr <- Serialize.new arrSize
185 arr2 <- Serialize.new arrSize
186 -- Re-initialize the array with random value
187 forM_ [0..(arrSize - 1)] $ \i -> Serialize.pokeAt i arr2 (8 :: Word8)
189 off1 <- Serialize.serializeAt serStartOff arr val
190 off2 <- Serialize.serializeAt 0 arr2 val
192 let slice1 = Array.Array arr serStartOff off1 :: Array.Array Word8
193 slice2 = Array.Array arr2 0 off2 :: Array.Array Word8
194 -- The serialized representation should be the same
195 slice1 `shouldBe` slice2
197 off1 `shouldBe` serEndOff
198 pure (arr, serStartOff, serEndOff)
201 forall a. (Eq a, Show a, Serialize.Serialize a)
202 => (MutByteArray, Int, Int)
205peekAndVerify (arr, serStartOff, serEndOff) val = do
206 (off2, val2) <- Serialize.deserializeAt serStartOff arr serEndOff
208 off2 `shouldBe` serEndOff
209 let slice = Array.Array arr serStartOff serEndOff
210 val `shouldBe` Array.deserialize slice
211 clonedSlice <- Array.clone slice
212 val `shouldBe` Array.deserialize clonedSlice
215 :: forall a. (Eq a, Show a, Serialize.Serialize a)
220 -- For debugging large size generated by arbitrary
221 -- let sz = Serialize.addSizeTo 0 val
222 -- putStrLn $ "Size is: " ++ show sz
224 val `shouldBe` Array.deserialize (Array.pinnedSerialize val)
227 peekAndVerify res val
230 :: forall a. (Eq a, Show a, Serialize.Serialize a)
234testSerializeList sizeOfA val = do
236 let sz = Serialize.addSizeTo 0 val
238 sz `shouldBe` sizeOfA
242--------------------------------------------------------------------------------
244--------------------------------------------------------------------------------
250 $ testSerializeList (8 + 4 * 8) ([1, 2, 3, 4] :: [Int])
251 it "Serialize [[Int]]"
254 ([[1], [1, 2], [1, 2, 3]] :: [[Int]])
256 describe "Edge Cases" $ do
257 it "Unit" $ roundtrip Unit
258 it "The" $ roundtrip $ The Unit 1 'a'
261 $ roundtrip $ HigherOrderType (Identity 5) (Identity 'e')
264 $ \(x :: Integer) -> roundtrip x
266 prop "([Integer], [Int])"
267 $ \(x :: ([Integer], [Int])) -> roundtrip x
270 $ \(x :: [Int]) -> roundtrip (Array.fromList x)
272 prop "Compatible Record"
273 $ \(a :: CompatV1.Rec (CompatV0.Rec CompatV1.River)) -> roundtrip a
276 $ \a b -> testCompatibility a b
279 $ prop "CustomDatatype"
280 $ \(x :: CustomDatatype) -> roundtrip x
283 $ prop "[CustomDatatype]"
284 $ \(x :: [CustomDatatype]) -> roundtrip x
288 $ forAll (elements [1..15])
290 (r :: BinTree Int) <- mkBinTree x
296 limitQC = modifyMaxSize (const 50)
298--------------------------------------------------------------------------------
300--------------------------------------------------------------------------------
303moduleName = "Data.Serialize." ++ CONF_NAME
306main = hspec $ H.parallel $ describe moduleName testCases
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list