@@ -1,34 +1,32 @@
1−{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
1+{-# LANGUAGE CPP, FlexibleInstances, FlexibleContexts, TupleSections #-}
3−module Main where
3+module Main ( main ) where
5−import Data.Binary ( encode, Binary(..) )
5+import Data.Binary ( Binary(..) )
6−import Data.Binary.Get ( runGet )
6+import Data.Binary.Get ( runGet, runGetIncremental, pushChunks, Decoder(..) )
7import Data.Binary.Put ( runPut )
7import Data.Binary.Put ( runPut )
9−import qualified Data.Binary.Get as BG ( getWord8, getWord16be, getWord32be, getWord64be )
9+import Data.Binary.Bits
10−import qualified Data.Binary.Put as BP ( putWord8, putWord16be, putWord32be, putWord64be )
10+import Data.Binary.Bits.Get
11+import Data.Binary.Bits.Put
12+import Data.Binary.Bits.BitOrder
12−import Bits
14+import qualified Data.ByteString as B
15+import qualified Data.ByteString.Lazy as L
14import Control.Applicative
17import Control.Applicative
15import Data.Bits
18import Data.Bits
17import Data.Word
19import Data.Word
18import Foreign.Storable
20import Foreign.Storable
19−import System.Random
21+import Data.Traversable (traverse)
20−import System
22+import Data.Foldable (traverse_)
25−import Test.Framework.Options ( TestOptions'(..) )
26import Test.Framework.Providers.QuickCheck2 ( testProperty )
24import Test.Framework.Providers.QuickCheck2 ( testProperty )
27import Test.Framework.Runners.Console ( defaultMain )
25import Test.Framework.Runners.Console ( defaultMain )
28−import Test.Framework.Runners.Options ( RunnerOptions'(..) )
29import Test.Framework ( Test, testGroup )
26import Test.Framework ( Test, testGroup )
30import Test.QuickCheck
27import Test.QuickCheck
32main = defaultMain tests
30main = defaultMain tests
34tests :: [Test]
32tests :: [Test]
@@ -39,6 +37,29 @@ tests =
39 , testGroup "Custom test cases"
37 , testGroup "Custom test cases"
40 [ testProperty "prop_composite_case" prop_composite_case ]
38 [ testProperty "prop_composite_case" prop_composite_case ]
40+ , testGroup "getByteString"
41+ [ testProperty "prop_getByteString_negative" prop_getByteString_negative
42+ , testProperty "prop_putByteString_getByteString" (prop_putByteString_getByteString :: BitOrder -> B.ByteString -> Property)
43+ , testProperty "prop_putByteString_getByteString_many" (prop_putByteString_getByteString_many :: BitOrder -> [B.ByteString] -> Property)
46+ , testGroup "getLazyByteString"
47+ [ testProperty "getLazyByteString == getByteString"
48+ prop_getLazyByteString_equal_to_ByteString
49+ , testProperty "getLazyByteString == getByteString (with shift)"
50+ prop_getLazyByteString_equal_to_ByteString2
53+ , testGroup "isEmpty"
54+ [ testProperty "prop_isEmptyOfEmptyEmpty" prop_isEmptyOfEmptyEmpty
55+ , testProperty "prop_isEmptyOfNonEmptyEmpty" prop_isEmptyOfNonEmptyEmpty
56+ , testProperty "prop_isEmptyOfConsumedEmpty" prop_isEmptyOfConsumedEmpty
57+ , testProperty "prop_isEmptyOfNotConsumedNotEmpty" prop_isEmptyOfNotConsumedNotEmpty
61+ [ testProperty "monadic fail" prop_fail ]
42 , testGroup "prop_bitput_with_get_from_binary"
63 , testGroup "prop_bitput_with_get_from_binary"
43 [ testProperty "Word8" (prop_bitput_with_get_from_binary :: W [Word8] -> Property)
64 [ testProperty "Word8" (prop_bitput_with_get_from_binary :: W [Word8] -> Property)
44 , testProperty "Word16" (prop_bitput_with_get_from_binary :: W [Word16] -> Property)
65 , testProperty "Word16" (prop_bitput_with_get_from_binary :: W [Word16] -> Property)
@@ -68,10 +89,10 @@ tests =
70 , testGroup "prop_put_with_bitreq"
91 , testGroup "prop_put_with_bitreq"
71− [ testProperty "Word8" (prop_putget_with_bitreq :: W Word8 -> Property)
92+ [ testProperty "Word8" (prop_putget_with_bitreq :: BitOrder -> W Word8 -> Property)
72− , testProperty "Word16" (prop_putget_with_bitreq :: W Word16 -> Property)
93+ , testProperty "Word16" (prop_putget_with_bitreq :: BitOrder -> W Word16 -> Property)
73− , testProperty "Word32" (prop_putget_with_bitreq :: W Word32 -> Property)
94+ , testProperty "Word32" (prop_putget_with_bitreq :: BitOrder -> W Word32 -> Property)
74− , testProperty "Word64" (prop_putget_with_bitreq :: W Word64 -> Property)
95+ , testProperty "Word64" (prop_putget_with_bitreq :: BitOrder -> W Word64 -> Property)
77 , testGroup "prop_putget_list_simple"
98 , testGroup "prop_putget_list_simple"
@@ -82,28 +103,88 @@ tests =
82 , testProperty "Word64" (prop_putget_list_simple :: W [Word64] -> Property)
103 , testProperty "Word64" (prop_putget_list_simple :: W [Word64] -> Property)
106+ , testGroup "prop_putget_applicative_with_bitreq"
107+ [ testProperty "Word8" (prop_putget_applicative_with_bitreq :: W [(Word8,Word8,Word8)] -> Property)
108+ , testProperty "Word16" (prop_putget_applicative_with_bitreq :: W [(Word16,Word16,Word16)] -> Property)
109+ , testProperty "Word32" (prop_putget_applicative_with_bitreq :: W [(Word32,Word32,Word32)] -> Property)
110+ , testProperty "Word64" (prop_putget_applicative_with_bitreq :: W [(Word64,Word64,Word64)] -> Property)
85 , testGroup "prop_putget_list_with_bitreq"
113 , testGroup "prop_putget_list_with_bitreq"
86 [ testProperty "Word8" (prop_putget_list_with_bitreq :: W [Word8] -> Property)
114 [ testProperty "Word8" (prop_putget_list_with_bitreq :: W [Word8] -> Property)
87 , testProperty "Word16" (prop_putget_list_with_bitreq :: W [Word16] -> Property)
115 , testProperty "Word16" (prop_putget_list_with_bitreq :: W [Word16] -> Property)
88 , testProperty "Word32" (prop_putget_list_with_bitreq :: W [Word32] -> Property)
116 , testProperty "Word32" (prop_putget_list_with_bitreq :: W [Word32] -> Property)
89− , testProperty "Word64" (prop_putget_list_with_bitreq :: W [Word64] -> Property)
117+ , testProperty "Word64" (prop_putget_list_with_bitreq :: W [Word64] -> Property)
119+ , testGroup "prop_bitget_bytestring_interspersed"
120+ [ testProperty "Word8" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word8 -> [B.ByteString] -> Property)
121+ , testProperty "Word16" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word16 -> [B.ByteString] -> Property)
122+ , testProperty "Word32" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word32 -> [B.ByteString] -> Property)
123+ , testProperty "Word64" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word64 -> [B.ByteString] -> Property)
125+ , testGroup "Simulate programs"
126+ [ testProperty "primitive" prop_primitive
127+ , testProperty "many primitives in sequence" prop_program
93−{-
131+prop_isEmptyOfEmptyEmpty :: Bool
94− -- these tests use the R structure
132+prop_isEmptyOfEmptyEmpty = runGet (runBitGet isEmpty) L.empty
96− -- quickCheck prop_Word32_from_2_Word16
134+prop_isEmptyOfNonEmptyEmpty :: L.ByteString -> Property
97− -- quickCheck prop_Word32_from_Word8_and_Word16
135+prop_isEmptyOfNonEmptyEmpty bs =
98−-}
136+ not (L.null bs) ==> not (runGet (runBitGet isEmpty) bs)
138+prop_isEmptyOfConsumedEmpty :: L.ByteString -> Property
139+prop_isEmptyOfConsumedEmpty bs =
141+ runGet (runBitGet (getByteString n >> isEmpty)) bs
142+ where n = fromIntegral $ L.length bs
144+prop_isEmptyOfNotConsumedNotEmpty :: L.ByteString -> Int -> Property
145+prop_isEmptyOfNotConsumedNotEmpty bs n =
146+ (fromIntegral n) < L.length bs && not (L.null bs) ==>
147+ not (runGet (runBitGet (getByteString n >> isEmpty)) bs)
149+prop_getLazyByteString_equal_to_ByteString :: L.ByteString -> Int -> Property
150+prop_getLazyByteString_equal_to_ByteString bs n =
151+ (fromIntegral n) <= L.length bs ==>
152+ runGet (runBitGet (getLazyByteString (fromIntegral n))) bs ==
153+ (L.fromChunks . (:[]) $ runGet (runBitGet (getByteString n)) bs)
155+prop_getLazyByteString_equal_to_ByteString2 :: L.ByteString -> Int -> Property
156+prop_getLazyByteString_equal_to_ByteString2 bs n =
157+ (L.length bs > 1) && (fromIntegral n) < L.length bs ==>
158+ runGet (runBitGet (getWord8 2 >> getLazyByteString (fromIntegral n))) bs ==
159+ (L.fromChunks . (:[]) $ runGet (runBitGet (getWord8 2 >> getByteString n)) bs)
161+prop_getByteString_negative :: Int -> Property
162+prop_getByteString_negative n =
164+ runGet (runBitGet (getByteString n)) L.empty == B.empty
166+prop_putByteString_getByteString :: BitOrder -> B.ByteString -> Property
167+prop_putByteString_getByteString bo bs = property $ bs' == bs
170+ w = runPut (runBitPut (withBitOrder bo (putByteString bs)))
171+ bs' = runGet (runBitGet (withBitOrder bo (getByteString n))) w
173+prop_putByteString_getByteString_many :: BitOrder -> [B.ByteString] -> Property
174+prop_putByteString_getByteString_many bo bs = property $ bs' == bs
176+ n = fmap B.length bs
177+ w = runPut (runBitPut (withBitOrder bo (traverse_ putByteString bs)))
178+ bs' = runGet (runBitGet (withBitOrder bo (traverse getByteString n))) w
100−prop_putget_with_bitreq :: (BinaryBit a, Num a, Bits a, Ord a) => W a -> Property
181+prop_putget_with_bitreq :: (BinaryBit a, Num a, Bits a, Ord a) => BitOrder -> W a -> Property
101−prop_putget_with_bitreq (W w) = property $
182+prop_putget_with_bitreq bo (W w) = property $
102 -- write all words with as many bits as it's required
183 -- write all words with as many bits as it's required
103 let p = putBits (bitreq w) w
184 let p = putBits (bitreq w) w
104 g = getBits (bitreq w)
185 g = getBits (bitreq w)
105− lbs = runPut (runBitPut p)
186+ lbs = runPut (runBitPut (withBitOrder bo p))
106− w' = runGet (runBitGetSimple g) lbs
187+ w' = runGet (runBitGet (withBitOrder bo g)) lbs
107 in w == w'
188 in w == w'
109-- | Write a list of items. Each item is written with the maximum amount of
190-- | Write a list of items. Each item is written with the maximum amount of
@@ -111,10 +192,10 @@ prop_putget_with_bitreq (W w) = property $
111prop_putget_list_simple :: (BinaryBit a, Eq a, Storable a) => W [a] -> Property
192prop_putget_list_simple :: (BinaryBit a, Eq a, Storable a) => W [a] -> Property
112prop_putget_list_simple (W ws) = property $
193prop_putget_list_simple (W ws) = property $
113 let s = sizeOf (head ws) * 8
194 let s = sizeOf (head ws) * 8
114− p = mapM_ (\v -> putBits s v) ws
195+ p = mapM_ (putBits s) ws
115 g = mapM (const (getBits s)) ws
196 g = mapM (const (getBits s)) ws
116 lbs = runPut (runBitPut p)
197 lbs = runPut (runBitPut p)
117− ws' = runGet (runBitGetSimple g) lbs
198+ ws' = runGet (runBitGet g) lbs
118 in ws == ws'
199 in ws == ws'
120-- | Write a list of items. Each item is written with exactly as many bits
201-- | Write a list of items. Each item is written with exactly as many bits
@@ -125,11 +206,23 @@ prop_putget_list_with_bitreq (W ws) = property $
125 let p = mapM_ (\v -> putBits (bitreq v) v) ws
206 let p = mapM_ (\v -> putBits (bitreq v) v) ws
126 g = mapM getBits bitlist
207 g = mapM getBits bitlist
127 lbs = runPut (runBitPut p)
208 lbs = runPut (runBitPut p)
128− ws' = runGet (runBitGetSimple g) lbs
209+ ws' = runGet (runBitGet g) lbs
129 in ws == ws'
210 in ws == ws'
131 bitlist = map bitreq ws
212 bitlist = map bitreq ws
214+prop_putget_applicative_with_bitreq :: (BinaryBit a, Num a, Bits a, Ord a) => W [(a,a,a)] -> Property
215+prop_putget_applicative_with_bitreq (W ts) = property $
216+ let p = mapM_ (\(a,b,c) -> do putBits (bitreq a) a
217+ putBits (bitreq b) b
218+ putBits (bitreq c) c) ts
219+ g = mapM (\(a,b,c) -> (,,) <$> getBits a <*> getBits b <*> getBits c) bitlist
220+ lbs = runPut (runBitPut p)
221+ ts' = runGet (runBitGet g) lbs
224+ bitlist = map (\(a,b,c) -> (bitreq a, bitreq b, bitreq c)) ts
133-- | Write bits using this library, and read them back using the binary
226-- | Write bits using this library, and read them back using the binary
134-- library.
227-- library.
135prop_bitput_with_get_from_binary :: (BinaryBit a, Binary a, Storable a, Eq a) => W [a] -> Property
228prop_bitput_with_get_from_binary :: (BinaryBit a, Binary a, Storable a, Eq a) => W [a] -> Property
@@ -149,38 +242,65 @@ prop_bitget_with_put_from_binary (W ws) = property $
149 p = mapM_ put ws
242 p = mapM_ put ws
150 g = mapM (const (getBits s)) ws
243 g = mapM (const (getBits s)) ws
151 lbs = runPut p
244 lbs = runPut p
152− ws' = runGet (runBitGetSimple g) lbs
245+ ws' = runGet (runBitGet g) lbs
153 in ws == ws'
246 in ws == ws'
248+-- | Write each 'ByteString' with a variable sized value as a separator.
249+prop_bitget_bytestring_interspersed :: (BinaryBit a, Binary a, Num a, Ord a, Bits a) => BitOrder -> W a -> [B.ByteString] -> Property
250+prop_bitget_bytestring_interspersed bo (W ws) bss = property $
251+ let p = mapM_ (\bs -> putBits (bitreq ws) ws >> putByteString bs) bss
252+ g = mapM (\bs -> (,) <$> getBits (bitreq ws) <*> getByteString (B.length bs)) bss
253+ lbs = runPut (runBitPut (withBitOrder bo p))
254+ r = runGet (runBitGet (withBitOrder bo g)) lbs
255+ in map (ws,) bss == r
258+prop_fail :: L.ByteString -> String -> Property
259+prop_fail lbs errMsg0 = forAll (choose (0, 8 * L.length lbs)) $ \len ->
260+ let (bytes,bits) = len `divMod` 8
261+ expectedBytesConsumed
263+ | otherwise = bytes + 1
264+ p = do _ <- getByteString (fromIntegral bytes)
265+ _ <- getBits (fromIntegral bits) :: BitGet Word8
267+ r = runGetIncremental (runBitGet p) `pushChunks` lbs
269+ Fail remainingBS pos errMsg ->
270+ and [ L.fromChunks [remainingBS] == L.drop expectedBytesConsumed lbs
271+ , pos == expectedBytesConsumed
155−-- number of bits required to write 'v'
276+-- | number of bits required to write @v@
156−bitreq :: (Num b, Bits a, Ord a) => a -> b
277+bitreq :: (Num b, Num a, Bits a, Ord a) => a -> b
157bitreq v = fromIntegral . head $ [ req | (req, top) <- bittable, v <= top ]
278bitreq v = fromIntegral . head $ [ req | (req, top) <- bittable, v <= top ]
159−bittable :: Bits a => [(Integer, a)]
280+bittable :: (Bits a, Num a) => [(Integer, a)]
160bittable = [ (fromIntegral x, (1 `shiftL` x) - 1) | x <- [1..64] ]
281bittable = [ (fromIntegral x, (1 `shiftL` x) - 1) | x <- [1..64] ]
162prop_bitreq :: W Word64 -> Property
283prop_bitreq :: W Word64 -> Property
163prop_bitreq (W w) = property $
284prop_bitreq (W w) = property $
164 ( w == 0 && bitreq w == 1 )
285 ( w == 0 && bitreq w == 1 )
165− || bitreq w == (bitreq (w `shiftR` 1)) + 1
286+ || bitreq w == bitreq (w `shiftR` 1) + 1
167prop_composite_case :: Bool -> W Word16 -> Property
288prop_composite_case :: Bool -> W Word16 -> Property
168prop_composite_case b (W w) = w < 0x8000 ==>
289prop_composite_case b (W w) = w < 0x8000 ==>
169 let p = do putBool b
290 let p = do putBool b
170− putWord16be 15 w
291+ putWord16 15 w
171 g = do v <- getBool
292 g = do v <- getBool
172 case v of
293 case v of
173− True -> getWord16be 15
294+ True -> getWord16 15
174 False -> do
295 False -> do
175 msb <- getWord8 7
296 msb <- getWord8 7
176 lsb <- getWord8 8
297 lsb <- getWord8 8
177 return ((fromIntegral msb `shiftL` 8) .|. fromIntegral lsb)
298 return ((fromIntegral msb `shiftL` 8) .|. fromIntegral lsb)
178 lbs = runPut (runBitPut p)
299 lbs = runPut (runBitPut p)
179− w' = runGet (runBitGetSimple g) lbs
300+ w' = runGet (runBitGet g) lbs
180 in w == w'
301 in w == w'
183−prop_compare_put_with_naive :: (Bits a, BinaryBit a, Ord a) => W [a] -> Property
303+prop_compare_put_with_naive :: (Bits a, BinaryBit a, Ord a, Num a) => W [a] -> Property
184prop_compare_put_with_naive (W ws) = property $
304prop_compare_put_with_naive (W ws) = property $
185 let pn = mapM_ (\v -> naive_put (bitreq v) v) ws
305 let pn = mapM_ (\v -> naive_put (bitreq v) v) ws
186 p = mapM_ (\v -> putBits (bitreq v) v) ws
306 p = mapM_ (\v -> putBits (bitreq v) v) ws
@@ -194,10 +314,10 @@ prop_compare_get_with_naive (W ws) = property $
194 g = mapM (\v -> getBits (bitreq v)) ws
314 g = mapM (\v -> getBits (bitreq v)) ws
195 p = mapM_ (\v -> naive_put (bitreq v) v) ws
315 p = mapM_ (\v -> naive_put (bitreq v) v) ws
196 lbs = runPut (runBitPut p)
316 lbs = runPut (runBitPut p)
197− rn = runGet (runBitGetSimple gn) lbs
317+ rn = runGet (runBitGet gn) lbs
198− r = runGet (runBitGetSimple g ) lbs
318+ r = runGet (runBitGet g ) lbs
199 -- we must help our compiler to resolve the types of 'gn' and 'g'
319 -- we must help our compiler to resolve the types of 'gn' and 'g'
200− types = rn == ws && r == ws
320+ _types = rn == ws && r == ws
201 in rn == r
321 in rn == r
203-- | Write one bit at a time until the full word has been written
323-- | Write one bit at a time until the full word has been written
@@ -215,27 +335,6 @@ naive_get n0 =
215 True -> loop (n-1) ((acc `shiftL` 1) + 1)
335 True -> loop (n-1) ((acc `shiftL` 1) + 1)
216 in loop n0 0
336 in loop n0 0
219−prop_Word32_from_Word8_and_Word16 :: Word8 -> Word16 -> Property
220−prop_Word32_from_Word8_and_Word16 w8 w16 = property $
221− let p = RWord32be 24
222− w' = runGet (get p) lbs
225− lbs = runPut (putWord8 w8 >> putWord16be w16)
226− w0 = ((fromIntegral w8) `shiftL` 16) .|. fromIntegral w16
228−prop_Word32_from_2_Word16 :: Word16 -> Word16 -> Property
229−prop_Word32_from_2_Word16 w1 w2 = property $
230− let p = RWord32be 32
231− w' = runGet (get p) lbs
235− w0 = ((fromIntegral w1) `shiftL` 16) .|. fromIntegral w2
239shrinker :: (Num a, Ord a, Bits a) => a -> [a]
338shrinker :: (Num a, Ord a, Bits a) => a -> [a]
240shrinker 0 = []
339shrinker 0 = []
241shrinker w = [ w `shiftR` 1 -- try to make everything roughly half size
340shrinker w = [ w `shiftR` 1 -- try to make everything roughly half size
@@ -249,14 +348,16 @@ shrinker w = [ w `shiftR` 1 -- try to make everything roughly half size
250data W a = W { unW :: a } deriving (Show, Eq, Ord)
349data W a = W { unW :: a } deriving (Show, Eq, Ord)
351+arbitraryW :: (Arbitrary (W a)) => Gen a
352+arbitraryW = unW <$> arbitrary
354+shrinkW :: (Arbitrary (W a)) => a -> [a]
355+shrinkW x = unW <$> shrink (W x)
252instance Arbitrary (W Bool) where
357instance Arbitrary (W Bool) where
253 arbitrary = W <$> arbitrary
358 arbitrary = W <$> arbitrary
254 shrink = map W <$> shrink . unW
359 shrink = map W <$> shrink . unW
256−instance (Arbitrary (W a)) => Arbitrary (W [a]) where
257− arbitrary = W . map unW <$> arbitrary
258− shrink = map (W . map unW) <$> mapM shrink . map W . unW
260instance Arbitrary (W Word8) where
361instance Arbitrary (W Word8) where
261 arbitrary = W <$> choose (minBound, maxBound)
362 arbitrary = W <$> choose (minBound, maxBound)
262 shrink = map W . shrinker . unW
363 shrink = map W . shrinker . unW
@@ -273,11 +374,159 @@ instance Arbitrary (W Word64) where
273 arbitrary = W <$> choose (minBound, maxBound)
374 arbitrary = W <$> choose (minBound, maxBound)
274 shrink = map W . shrinker . unW
375 shrink = map W . shrinker . unW
276−integralRandomR :: (Integral a, RandomGen g) => (a,a) -> g -> (a,g)
377+instance Arbitrary B.ByteString where
277−integralRandomR (a,b) g = case randomR (fromIntegral a :: Integer,
378+ arbitrary = B.pack <$> arbitrary
278− fromIntegral b :: Integer) g of
379+ shrink bs = B.pack <$> shrink (B.unpack bs)
279− (x,g) -> (fromIntegral x, g)
380+
381+instance Arbitrary L.ByteString where
382+ arbitrary = L.fromChunks <$> arbitrary
383+ shrink bs = L.fromChunks <$> shrink (L.toChunks bs)
385+instance (Arbitrary (W a)) => Arbitrary (W [a]) where
386+ arbitrary = W . map unW <$> arbitrary
387+ shrink = map (W . map unW) <$> mapM shrink . map W . unW
389+instance (Arbitrary (W a), Arbitrary (W b)) => Arbitrary (W (a,b)) where
390+ arbitrary = (W .) . (,) <$> arbitraryW <*> arbitraryW
391+ shrink (W (a,b)) = (W .) . (,) <$> shrinkW a <*> shrinkW b
393+instance (Arbitrary (W a), Arbitrary (W b), Arbitrary (W c)) => Arbitrary (W (a,b,c)) where
394+ arbitrary = ((W .) .) . (,,) <$> arbitraryW <*> arbitraryW <*> arbitraryW
395+ shrink (W (a,b,c)) = ((W .) .) . (,,) <$> shrinkW a <*> shrinkW b <*> shrinkW c
403+ | BS Int B.ByteString
404+ | LBS Int L.ByteString
409+type Program = [Primitive]
411+instance Arbitrary Primitive where
414+ let (maxBits, _) = (\w -> (finiteBitSize w, c undefined w)) undefined
415+ bits <- choose (0, maxBits)
416+ n <- choose (0, fromIntegral (2^bits-1))
419+ [ Bool <$> arbitrary
424+ , Skip <$> choose (0, 3000)
425+ , do n <- choose (0,10)
427+ return (BS n (B.pack cs))
428+ , do n <- choose (0,10)
430+ return (LBS n (L.pack cs))
434+ let snk c x = map (\x' -> c (bitreq x') x') (shrinker x) in
436+ Bool b -> if b then [Bool False] else []
438+ W16 _ x -> snk W16 x
439+ W32 _ x -> snk W32 x
440+ W64 _ x -> snk W64 x
441+ Skip x -> Skip <$> shrink x
442+ BS _ bs -> let ws = B.unpack bs in map (\ws' -> BS (length ws') (B.pack ws')) (shrink ws)
443+ LBS _ lbs -> let ws = L.unpack lbs in map (\ws' -> LBS (length ws') (L.pack ws')) (shrink ws)
446+instance Arbitrary BitOrder where
447+ arbitrary = elements [BB, LB, LL, BL]
448+ shrink LL = [BB,LB,BL]
454+prop_primitive :: Primitive -> Property
455+prop_primitive prim = property $
456+ let p = putPrimitive prim
457+ g = getPrimitive prim
458+ lbs = runPut (runBitPut p)
459+ r = runGet (runBitGet g) lbs
462+prop_program :: Program -> Property
463+prop_program program = property $
464+ let p = mapM_ putPrimitive program
465+ g = verifyProgram (8 * fromIntegral (L.length lbs)) program
466+ lbs = runPut (runBitPut p)
467+ r = runGet (runBitGet g) lbs
470+putPrimitive :: Primitive -> BitPut ()
474+ W8 n x -> putWord8 n x
475+ W16 n x -> putWord16 n x
476+ W32 n x -> putWord32 n x
477+ W64 n x -> putWord64 n x
478+ Skip n -> skipBits n
479+ BS _ bs -> putByteString bs
480+ LBS _ lbs -> mapM_ putByteString (L.toChunks lbs)
481+ IsEmpty -> return ()
483+getPrimitive :: Primitive -> BitGet Primitive
486+ Bool _ -> Bool <$> getBool
487+ W8 n _ -> W8 n <$> getWord8 n
488+ W16 n _ -> W16 n <$> getWord16 n
489+ W32 n _ -> W32 n <$> getWord32 n
490+ W64 n _ -> W64 n <$> getWord64 n
491+ Skip n -> skipBits n >> return (Skip n)
492+ BS n _ -> BS n <$> getByteString n
493+ LBS n _ -> LBS n <$> getLazyByteString n
494+ IsEmpty -> isEmpty >> return IsEmpty
496+getPrimitiveSize :: Primitive -> Int
497+getPrimitiveSize p = case p of
508+verifyProgram :: Int -> Program -> BitGet Bool
509+verifyProgram totalLength ps0 = go 0 ps0
511+ go _ [] = return True
515+ let expected = pos == totalLength
517+ if expected == actual
519+ else error $ "isEmpty returned wrong value, expected "
520+ ++ show expected ++ " but got " ++ show actual
521+ _ -> check p (getPrimitive p) >> go (pos + getPrimitiveSize p) ps
526+ else error $ "Roundtrip error: Expected "
527+ ++ show x ++ " but got " ++ show y
281instance Random Word where
530instance Random Word where
282 randomR = integralRandomR
531 randomR = integralRandomR
283 random = randomR (minBound,maxBound)
532 random = randomR (minBound,maxBound)
@@ -297,3 +546,4 @@ instance Random Word32 where
297instance Random Word64 where
546instance Random Word64 where
298 randomR = integralRandomR
547 randomR = integralRandomR
299 random = randomR (minBound,maxBound)
548 random = randomR (minBound,maxBound)