@@ -9,6 +9,7 @@ import Data.Binary.Put ( runPut )
9import Data.Binary.Bits
9import Data.Binary.Bits
10import Data.Binary.Bits.Get
10import Data.Binary.Bits.Get
11import Data.Binary.Bits.Put
11import Data.Binary.Bits.Put
12+import Data.Binary.Bits.BitOrder
13import qualified Data.ByteString as B
14import qualified Data.ByteString as B
14import qualified Data.ByteString.Lazy as L
15import qualified Data.ByteString.Lazy as L
@@ -17,7 +18,8 @@ import Control.Applicative
17import Data.Bits
18import Data.Bits
18import Data.Word
19import Data.Word
19import Foreign.Storable
20import Foreign.Storable
20−import System.Random
21+import Data.Traversable (traverse)
22+import Data.Foldable (traverse_)
22import Test.Framework.Providers.QuickCheck2 ( testProperty )
24import Test.Framework.Providers.QuickCheck2 ( testProperty )
23import Test.Framework.Runners.Console ( defaultMain )
25import Test.Framework.Runners.Console ( defaultMain )
@@ -36,7 +38,10 @@ tests =
36 [ testProperty "prop_composite_case" prop_composite_case ]
38 [ testProperty "prop_composite_case" prop_composite_case ]
38 , testGroup "getByteString"
40 , testGroup "getByteString"
39− [ testProperty "prop_getByteString_negative" prop_getByteString_negative ]
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)
41 , testGroup "getLazyByteString"
46 , testGroup "getLazyByteString"
42 [ testProperty "getLazyByteString == getByteString"
47 [ testProperty "getLazyByteString == getByteString"
@@ -84,10 +89,10 @@ tests =
86 , testGroup "prop_put_with_bitreq"
91 , testGroup "prop_put_with_bitreq"
87− [ testProperty "Word8" (prop_putget_with_bitreq :: W Word8 -> Property)
92+ [ testProperty "Word8" (prop_putget_with_bitreq :: BitOrder -> W Word8 -> Property)
88− , testProperty "Word16" (prop_putget_with_bitreq :: W Word16 -> Property)
93+ , testProperty "Word16" (prop_putget_with_bitreq :: BitOrder -> W Word16 -> Property)
89− , testProperty "Word32" (prop_putget_with_bitreq :: W Word32 -> Property)
94+ , testProperty "Word32" (prop_putget_with_bitreq :: BitOrder -> W Word32 -> Property)
90− , testProperty "Word64" (prop_putget_with_bitreq :: W Word64 -> Property)
95+ , testProperty "Word64" (prop_putget_with_bitreq :: BitOrder -> W Word64 -> Property)
93 , testGroup "prop_putget_list_simple"
98 , testGroup "prop_putget_list_simple"
@@ -112,10 +117,10 @@ tests =
112 , testProperty "Word64" (prop_putget_list_with_bitreq :: W [Word64] -> Property)
117 , testProperty "Word64" (prop_putget_list_with_bitreq :: W [Word64] -> Property)
114 , testGroup "prop_bitget_bytestring_interspersed"
119 , testGroup "prop_bitget_bytestring_interspersed"
115− [ testProperty "Word8" (prop_bitget_bytestring_interspersed :: W Word8 -> [B.ByteString] -> Property)
120+ [ testProperty "Word8" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word8 -> [B.ByteString] -> Property)
116− , testProperty "Word16" (prop_bitget_bytestring_interspersed :: W Word16 -> [B.ByteString] -> Property)
121+ , testProperty "Word16" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word16 -> [B.ByteString] -> Property)
117− , testProperty "Word32" (prop_bitget_bytestring_interspersed :: W Word32 -> [B.ByteString] -> Property)
122+ , testProperty "Word32" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word32 -> [B.ByteString] -> Property)
118− , testProperty "Word64" (prop_bitget_bytestring_interspersed :: W Word64 -> [B.ByteString] -> Property)
123+ , testProperty "Word64" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word64 -> [B.ByteString] -> Property)
120 , testGroup "Simulate programs"
125 , testGroup "Simulate programs"
121 [ testProperty "primitive" prop_primitive
126 [ testProperty "primitive" prop_primitive
@@ -158,13 +163,28 @@ prop_getByteString_negative n =
158 n < 1 ==>
163 n < 1 ==>
159 runGet (runBitGet (getByteString n)) L.empty == B.empty
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
161−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
162−prop_putget_with_bitreq (W w) = property $
182+prop_putget_with_bitreq bo (W w) = property $
163 -- write all words with as many bits as it's required
183 -- write all words with as many bits as it's required
164 let p = putBits (bitreq w) w
184 let p = putBits (bitreq w) w
165 g = getBits (bitreq w)
185 g = getBits (bitreq w)
166− lbs = runPut (runBitPut p)
186+ lbs = runPut (runBitPut (withBitOrder bo p))
167− w' = runGet (runBitGet g) lbs
187+ w' = runGet (runBitGet (withBitOrder bo g)) lbs
168 in w == w'
188 in w == w'
170-- | 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
@@ -226,12 +246,12 @@ prop_bitget_with_put_from_binary (W ws) = property $
226 in ws == ws'
246 in ws == ws'
228-- | Write each 'ByteString' with a variable sized value as a separator.
248-- | Write each 'ByteString' with a variable sized value as a separator.
229−prop_bitget_bytestring_interspersed :: (BinaryBit a, Binary a, Num a, Ord a, Bits a) => W a -> [B.ByteString] -> Property
249+prop_bitget_bytestring_interspersed :: (BinaryBit a, Binary a, Num a, Ord a, Bits a) => BitOrder -> W a -> [B.ByteString] -> Property
230−prop_bitget_bytestring_interspersed (W ws) bss = property $
250+prop_bitget_bytestring_interspersed bo (W ws) bss = property $
231 let p = mapM_ (\bs -> putBits (bitreq ws) ws >> putByteString bs) bss
251 let p = mapM_ (\bs -> putBits (bitreq ws) ws >> putByteString bs) bss
232 g = mapM (\bs -> (,) <$> getBits (bitreq ws) <*> getByteString (B.length bs)) bss
252 g = mapM (\bs -> (,) <$> getBits (bitreq ws) <*> getByteString (B.length bs)) bss
233− lbs = runPut (runBitPut p)
253+ lbs = runPut (runBitPut (withBitOrder bo p))
234− r = runGet (runBitGet g) lbs
254+ r = runGet (runBitGet (withBitOrder bo g)) lbs
235 in map (ws,) bss == r
255 in map (ws,) bss == r
237-- | Test failing.
257-- | Test failing.
@@ -241,8 +261,8 @@ prop_fail lbs errMsg0 = forAll (choose (0, 8 * L.length lbs)) $ \len ->
241 expectedBytesConsumed
261 expectedBytesConsumed
242 | bits == 0 = bytes
262 | bits == 0 = bytes
243 | otherwise = bytes + 1
263 | otherwise = bytes + 1
244− p = do getByteString (fromIntegral bytes)
264+ p = do _ <- getByteString (fromIntegral bytes)
245− getBits (fromIntegral bits) :: BitGet Word8
265+ _ <- getBits (fromIntegral bits) :: BitGet Word8
246 fail errMsg0
266 fail errMsg0
247 r = runGetIncremental (runBitGet p) `pushChunks` lbs
267 r = runGetIncremental (runBitGet p) `pushChunks` lbs
248 in case r of
268 in case r of
@@ -268,7 +288,7 @@ prop_bitreq (W w) = property $
268prop_composite_case :: Bool -> W Word16 -> Property
288prop_composite_case :: Bool -> W Word16 -> Property
269prop_composite_case b (W w) = w < 0x8000 ==>
289prop_composite_case b (W w) = w < 0x8000 ==>
270 let p = do putBool b
290 let p = do putBool b
271− putWord16be 15 w
291+ putWord16 15 w
272 g = do v <- getBool
292 g = do v <- getBool
273 case v of
293 case v of
274 True -> getWord16 15
294 True -> getWord16 15
@@ -374,11 +394,6 @@ instance (Arbitrary (W a), Arbitrary (W b), Arbitrary (W c)) => Arbitrary (W (a,
374 arbitrary = ((W .) .) . (,,) <$> arbitraryW <*> arbitraryW <*> arbitraryW
394 arbitrary = ((W .) .) . (,,) <$> arbitraryW <*> arbitraryW <*> arbitraryW
375 shrink (W (a,b,c)) = ((W .) .) . (,,) <$> shrinkW a <*> shrinkW b <*> shrinkW c
395 shrink (W (a,b,c)) = ((W .) .) . (,,) <$> shrinkW a <*> shrinkW b <*> shrinkW c
377−integralRandomR :: (Integral a, RandomGen g) => (a,a) -> g -> (a,g)
378−integralRandomR (a,b) g = case randomR (fromIntegral a :: Integer,
379− fromIntegral b :: Integer) g of
380− (x,g) -> (fromIntegral x, g)
382data Primitive
397data Primitive
383 = Bool Bool
398 = Bool Bool
384 | W8 Int Word8
399 | W8 Int Word8
@@ -387,6 +402,7 @@ data Primitive
387 | W64 Int Word64
402 | W64 Int Word64
388 | BS Int B.ByteString
403 | BS Int B.ByteString
389 | LBS Int L.ByteString
404 | LBS Int L.ByteString
390 | IsEmpty
406 | IsEmpty
391 deriving (Eq, Show)
407 deriving (Eq, Show)
@@ -405,6 +421,7 @@ instance Arbitrary Primitive where
405 , gen W16
421 , gen W16
406 , gen W32
422 , gen W32
407 , gen W64
423 , gen W64
424+ , Skip <$> choose (0, 3000)
408 , do n <- choose (0,10)
425 , do n <- choose (0,10)
409 cs <- vector n
426 cs <- vector n
410 return (BS n (B.pack cs))
427 return (BS n (B.pack cs))
@@ -421,10 +438,19 @@ instance Arbitrary Primitive where
421 W16 _ x -> snk W16 x
438 W16 _ x -> snk W16 x
422 W32 _ x -> snk W32 x
439 W32 _ x -> snk W32 x
423 W64 _ x -> snk W64 x
440 W64 _ x -> snk W64 x
441+ Skip x -> Skip <$> shrink x
424 BS _ bs -> let ws = B.unpack bs in map (\ws' -> BS (length ws') (B.pack ws')) (shrink ws)
442 BS _ bs -> let ws = B.unpack bs in map (\ws' -> BS (length ws') (B.pack ws')) (shrink ws)
425 LBS _ lbs -> let ws = L.unpack lbs in map (\ws' -> LBS (length ws') (L.pack ws')) (shrink ws)
443 LBS _ lbs -> let ws = L.unpack lbs in map (\ws' -> LBS (length ws') (L.pack ws')) (shrink ws)
426 IsEmpty -> []
444 IsEmpty -> []
446+instance Arbitrary BitOrder where
447+ arbitrary = elements [BB, LB, LL, BL]
448+ shrink LL = [BB,LB,BL]
428prop_primitive :: Primitive -> Property
454prop_primitive :: Primitive -> Property
429prop_primitive prim = property $
455prop_primitive prim = property $
430 let p = putPrimitive prim
456 let p = putPrimitive prim
@@ -446,9 +472,10 @@ putPrimitive p =
446 case p of
472 case p of
447 Bool b -> putBool b
473 Bool b -> putBool b
448 W8 n x -> putWord8 n x
474 W8 n x -> putWord8 n x
449− W16 n x -> putWord16be n x
475+ W16 n x -> putWord16 n x
450− W32 n x -> putWord32be n x
476+ W32 n x -> putWord32 n x
451− W64 n x -> putWord64be n x
477+ W64 n x -> putWord64 n x
478+ Skip n -> skipBits n
452 BS _ bs -> putByteString bs
479 BS _ bs -> putByteString bs
453 LBS _ lbs -> mapM_ putByteString (L.toChunks lbs)
480 LBS _ lbs -> mapM_ putByteString (L.toChunks lbs)
454 IsEmpty -> return ()
481 IsEmpty -> return ()
@@ -461,10 +488,22 @@ getPrimitive p =
461 W16 n _ -> W16 n <$> getWord16 n
488 W16 n _ -> W16 n <$> getWord16 n
462 W32 n _ -> W32 n <$> getWord32 n
489 W32 n _ -> W32 n <$> getWord32 n
463 W64 n _ -> W64 n <$> getWord64 n
490 W64 n _ -> W64 n <$> getWord64 n
491+ Skip n -> skipBits n >> return (Skip n)
464 BS n _ -> BS n <$> getByteString n
492 BS n _ -> BS n <$> getByteString n
465 LBS n _ -> LBS n <$> getLazyByteString n
493 LBS n _ -> LBS n <$> getLazyByteString n
466 IsEmpty -> isEmpty >> return IsEmpty
494 IsEmpty -> isEmpty >> return IsEmpty
496+getPrimitiveSize :: Primitive -> Int
497+getPrimitiveSize p = case p of
469verifyProgram :: Int -> Program -> BitGet Bool
508verifyProgram :: Int -> Program -> BitGet Bool
470verifyProgram totalLength ps0 = go 0 ps0
509verifyProgram totalLength ps0 = go 0 ps0
@@ -472,13 +511,6 @@ verifyProgram totalLength ps0 = go 0 ps0
472 go _ [] = return True
511 go _ [] = return True
473 go pos (p:ps) =
512 go pos (p:ps) =
474 case p of
513 case p of
475− Bool x -> check x getBool >> go (pos+1) ps
476− W8 n x -> check x (getWord8 n) >> go (pos+n) ps
477− W16 n x -> check x (getWord16 n) >> go (pos+n) ps
478− W32 n x -> check x (getWord32 n) >> go (pos+n) ps
479− W64 n x -> check x (getWord64 n) >> go (pos+n) ps
480− BS n x -> check x (getByteString n) >> go (pos+(8*n)) ps
481− LBS n x -> check x (getLazyByteString n) >> go (pos+(8*n)) ps
482 IsEmpty -> do
514 IsEmpty -> do
483 let expected = pos == totalLength
515 let expected = pos == totalLength
484 actual <- isEmpty
516 actual <- isEmpty
@@ -486,6 +518,7 @@ verifyProgram totalLength ps0 = go 0 ps0
486 then go pos ps
518 then go pos ps
487 else error $ "isEmpty returned wrong value, expected "
519 else error $ "isEmpty returned wrong value, expected "
488 ++ show expected ++ " but got " ++ show actual
520 ++ show expected ++ " but got " ++ show actual
521+ _ -> check p (getPrimitive p) >> go (pos + getPrimitiveSize p) ps
489 check x g = do
522 check x g = do
491 if x == y
524 if x == y