forked from maltem/zlib-enum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test.hs
206 lines (168 loc) · 6.25 KB
/
test.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-------------------------------------------------------------------------------
-- |
-- Module : zlib-enum-test
-- Copyright : (c) 2011 Malte Sommerkorn, Michael Snoyman, Paulo Tanimoto
-- License : BSD3
--
-- Maintainer : Malte Sommerkorn <malte.sommerkorn@googlemail.com>
--
-------------------------------------------------------------------------------
module Main where
-------------------------------------------------------------------------------
-- Imports
-------------------------------------------------------------------------------
import Prelude
-- import Data.Word (Word8)
import Data.String (fromString)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy as L
import Data.Enumerator
( Stream (..), Step (..), Iteratee (..), Enumerator, Enumeratee
, ($$), returnI, yield, continue, joinI, run, run_ )
import qualified Data.Enumerator as E
import qualified Data.Enumerator.List as EL
import qualified Data.Enumerator.Text as ET
import qualified Data.Enumerator.Binary as EB
import Control.Exception (bracket)
import Control.Monad.Trans (MonadIO (..), liftIO)
import Codec.Zlib (WindowBits (..))
import qualified Codec.Zlib.Enum as Z
import Test.QuickCheck
import Test.QuickCheck.Monadic
import qualified Test.QuickCheck.Monadic as Q
-- import System.Environment (getArgs)
import System.IO (IOMode (..), openFile, hClose)
-------------------------------------------------------------------------------
-- Instances
-------------------------------------------------------------------------------
-- | QuickCheck wants a Show instance for WindowBits
instance Show WindowBits where
show (WindowBits n) = "WindowBits " ++ show n
-- | Random values for WindowBits: 15 or 31
instance Arbitrary WindowBits where
arbitrary = elements [WindowBits 15, WindowBits 31]
-- | Generate relatively large ByteStrings
instance Arbitrary B.ByteString where
arbitrary = (B.pack . concat . replicate 1000) `fmap` arbitrary
-- | Generate relatively large Lazy ByteStrings
instance Arbitrary L.ByteString where
arbitrary = (L.fromChunks . concat . replicate 100) `fmap` arbitrary
-------------------------------------------------------------------------------
-- Utility Functions
-------------------------------------------------------------------------------
-- | Concatenate an Iteratee and a list of Enumeratees into an Iteratee.
concatE :: Monad m => Iteratee a m b -> [Enumeratee a a m b] -> Iteratee a m b
concatE = foldr f
where
f iter enums = joinI $ iter $$ enums
-- | Iteratee that consumes the entire Stream as a strict ByteString.
consume :: Monad m => Iteratee ByteString m ByteString
consume = do
xs <- EL.consume
return $ B.concat xs
-- | Enumeratee that consumes only a few bytes.
unconsumed :: Monad m => Enumeratee ByteString ByteString m a
unconsumed = E.sequence $ do
xs <- EB.take 100
return $ B.concat $ L.toChunks xs
-- | Compress and decompress without doing anything else.
compressDecompress
:: MonadIO m
=> WindowBits
-> [ByteString]
-> m ByteString
compressDecompress win xs =
E.run_ $ E.enumList 1 xs
$$ joinI $ Z.compress 7 win
$$ joinI $ Z.decompress win
$$ consume
-- | Compress and decompress a ByteString with given WindowBits,
-- piping the stream with an Enumeratee.
compressDecompressWith
:: MonadIO m
=> Enumeratee ByteString ByteString m ByteString
-> WindowBits
-> [ByteString]
-> m ByteString
compressDecompressWith enum win xs =
E.run_ $ E.enumList 1 xs
$$ joinI $ Z.compress 7 win
$$ joinI $ enum
$$ joinI $ Z.decompress win
$$ consume
-- | Compress a ByteString 'n' times and then decompress it 'n' times
compressDecompressMany
:: MonadIO m
=> WindowBits
-> Int
-> [ByteString]
-> m ByteString
compressDecompressMany win n xs =
E.run_ $ E.enumList 1 xs
$$ concatE consume es
where
es = replicate m (Z.compress 7 win) ++ replicate m (Z.decompress win)
m = 1 + (abs n `rem` 20) -- restrict n to [1, 20]
-- | Decompress a file with 'unconsumed'
decompressUnconsumed :: FilePath -> IO ByteString
decompressUnconsumed file =
E.run_ $ EB.enumFile file
$$ joinI $ Z.decompress (WindowBits 31)
$$ joinI $ unconsumed
$$ consume
-- | Create uncompressed and compressed files for testing.
createFiles :: FilePath -> IO ()
createFiles file = bracket
(do hDeco <- openFile file WriteMode
hComp <- openFile (file ++ ".gz") WriteMode
return (hDeco, hComp)
)
( \(hDeco, hComp) -> do
mapM_ hClose [hDeco, hComp]
)
$ \(hDeco, hComp) -> do
-- list of random ByteStrings
xs <- sample' (arbitrary :: Gen ByteString)
-- create uncompresssed file
run_ $ E.enumList 1 xs
$$ EB.iterHandle hDeco
-- create compressed file
run_ $ E.enumList 1 xs
$$ joinI $ Z.compress 7 (WindowBits 31)
$$ EB.iterHandle hComp
-------------------------------------------------------------------------------
-- Properties
-------------------------------------------------------------------------------
prop_compress_decompress :: WindowBits -> [ByteString] -> Property
prop_compress_decompress win xs = monadicIO $ do
ys <- Q.run $ compressDecompress win xs
assert (B.concat xs == ys)
prop_unconsumed :: WindowBits -> [ByteString] -> Property
prop_unconsumed win xs = monadicIO $ do
ys <- Q.run $ compressDecompressWith unconsumed win xs
assert (B.concat xs == ys)
prop_many :: WindowBits -> Int -> [ByteString] -> Property
prop_many win n xs = monadicIO $ do
ys <- Q.run $ compressDecompressMany win n xs
assert (B.concat xs == ys)
prop_files :: FilePath -> Property
prop_files file = monadicIO $ do
xs <- Q.run $ B.readFile file
ys <- Q.run $ decompressUnconsumed (file ++ ".gz")
assert (xs == ys)
-------------------------------------------------------------------------------
-- IO
-------------------------------------------------------------------------------
main :: IO ()
main = do
quickCheck prop_compress_decompress
quickCheck prop_unconsumed
quickCheck prop_many
let testFile = "zlib-enum-test-file"
createFiles testFile
quickCheck (prop_files testFile)