Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 332 lines (284 sloc) 10.942 kb
db1395d @tanimoto Started testing code
tanimoto authored
1 {-# LANGUAGE NoImplicitPrelude #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# OPTIONS_GHC -fno-warn-orphans #-}
4
5 -------------------------------------------------------------------------------
6 -- |
7 -- Module : zlib-enum-test
8 -- Copyright : (c) 2011 Malte Sommerkorn, Michael Snoyman, Paulo Tanimoto
9 -- License : BSD3
10 --
11 -- Maintainer : Malte Sommerkorn <malte.sommerkorn@googlemail.com>
12 --
13 -------------------------------------------------------------------------------
14
15 module Main where
16
17 -------------------------------------------------------------------------------
18 -- Imports
19 -------------------------------------------------------------------------------
20
21 import Prelude
22
3dd0430 @maltem -Wall
authored
23 import Data.String ()
db1395d @tanimoto Started testing code
tanimoto authored
24
25 import Data.ByteString (ByteString)
26 import qualified Data.ByteString as B
27 import qualified Data.ByteString.Lazy as L
28
29 import Data.Enumerator
3dd0430 @maltem -Wall
authored
30 ( Iteratee (..), Enumeratee
31 , ($$), joinI, run_ )
db1395d @tanimoto Started testing code
tanimoto authored
32 import qualified Data.Enumerator as E
33 import qualified Data.Enumerator.List as EL
34 import qualified Data.Enumerator.Binary as EB
35
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
36 import Control.Monad (foldM)
f7590c5 @maltem Directly depend on transformers instead of mtl
authored
37 import Control.Monad.IO.Class (MonadIO (..))
db1395d @tanimoto Started testing code
tanimoto authored
38 import Control.Exception (bracket)
39
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
40 import Codec.Zlib
db1395d @tanimoto Started testing code
tanimoto authored
41 import qualified Codec.Zlib.Enum as Z
42
43 import Test.QuickCheck
44 import Test.QuickCheck.Monadic
45 import qualified Test.QuickCheck.Monadic as Q
46
ca5e1b4 @tanimoto Changed 'openFile' -> 'openBinaryFile'
tanimoto authored
47 import System.IO (IOMode (..), openBinaryFile, hClose)
db1395d @tanimoto Started testing code
tanimoto authored
48
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
49 import Test.Framework (Test, defaultMain, testGroup)
50 import Test.Framework.Providers.QuickCheck2 (testProperty)
51
db1395d @tanimoto Started testing code
tanimoto authored
52 -------------------------------------------------------------------------------
53 -- Instances
54 -------------------------------------------------------------------------------
55
56 -- | QuickCheck wants a Show instance for WindowBits
57 instance Show WindowBits where
58 show (WindowBits n) = "WindowBits " ++ show n
3dd0430 @maltem -Wall
authored
59 show _ = "DefaultWindowBits" -- not exported from zlib
db1395d @tanimoto Started testing code
tanimoto authored
60
61 -- | Random values for WindowBits: 15 or 31
62 instance Arbitrary WindowBits where
63 arbitrary = elements [WindowBits 15, WindowBits 31]
64
65 -- | Generate relatively large ByteStrings
66 instance Arbitrary B.ByteString where
67 arbitrary = (B.pack . concat . replicate 1000) `fmap` arbitrary
68
69 -- | Generate relatively large Lazy ByteStrings
70 instance Arbitrary L.ByteString where
71 arbitrary = (L.fromChunks . concat . replicate 100) `fmap` arbitrary
72
73 -------------------------------------------------------------------------------
74 -- Utility Functions
75 -------------------------------------------------------------------------------
76
77 -- | Iteratee that consumes the entire Stream as a strict ByteString.
78 consume :: Monad m => Iteratee ByteString m ByteString
79 consume = do
80 xs <- EL.consume
81 return $ B.concat xs
82
83 -- | Enumeratee that consumes only a few bytes.
84 unconsumed :: Monad m => Enumeratee ByteString ByteString m a
85 unconsumed = E.sequence $ do
86 xs <- EB.take 100
87 return $ B.concat $ L.toChunks xs
88
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
89 -- | Concatenate an Iteratee and a list of Enumeratees into an Iteratee.
90 concatWith :: Monad m => Iteratee a m b -> [Enumeratee a a m b] -> Iteratee a m b
91 concatWith = foldr f
92 where
93 f iter enums = joinI $ iter $$ enums
94
95 -- | Compress a list of ByteStrings
96 compress
97 :: MonadIO m
98 => WindowBits
99 -> [ByteString]
100 -> m ByteString
101 compress win xs =
102 E.run_ $ E.enumList 1 xs
9db9966 @tanimoto Revert to original names
tanimoto authored
103 $$ joinI $ Z.compress 7 win
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
104 $$ consume
105
106 -- | Decompress a list of ByteStrings
107 decompress
108 :: MonadIO m
109 => WindowBits
110 -> [ByteString]
111 -> m ByteString
112 decompress win xs =
113 E.run_ $ E.enumList 1 xs
9db9966 @tanimoto Revert to original names
tanimoto authored
114 $$ joinI $ Z.decompress win
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
115 $$ consume
116
b0af09e @tanimoto Add more tests
tanimoto authored
117 -- | Compress and decompress without doing anything else.
118 compressDecompress
119 :: MonadIO m
120 => WindowBits
121 -> [ByteString]
122 -> m ByteString
123 compressDecompress win xs =
124 E.run_ $ E.enumList 1 xs
9db9966 @tanimoto Revert to original names
tanimoto authored
125 $$ joinI $ Z.compress 7 win
126 $$ joinI $ Z.decompress win
b0af09e @tanimoto Add more tests
tanimoto authored
127 $$ consume
128
c0d092f @maltem gzip/ungzip shorthand. Factor out callback.
authored
129 -- | The same, but for 'gzip', 'ungzip'
130 gzipUngzip :: MonadIO m => [ByteString] -> m ByteString
131 gzipUngzip xs =
132 E.run_ $ E.enumList 1 xs
133 $$ joinI $ Z.gzip
134 $$ joinI $ Z.ungzip
135 $$ consume
136
db1395d @tanimoto Started testing code
tanimoto authored
137 -- | Compress and decompress a ByteString with given WindowBits,
138 -- piping the stream with an Enumeratee.
b0af09e @tanimoto Add more tests
tanimoto authored
139 compressDecompressWith
db1395d @tanimoto Started testing code
tanimoto authored
140 :: MonadIO m
141 => Enumeratee ByteString ByteString m ByteString
142 -> WindowBits
143 -> [ByteString]
144 -> m ByteString
b0af09e @tanimoto Add more tests
tanimoto authored
145 compressDecompressWith enum win xs =
db1395d @tanimoto Started testing code
tanimoto authored
146 E.run_ $ E.enumList 1 xs
9db9966 @tanimoto Revert to original names
tanimoto authored
147 $$ joinI $ Z.compress 7 win
db1395d @tanimoto Started testing code
tanimoto authored
148 $$ joinI $ enum
9db9966 @tanimoto Revert to original names
tanimoto authored
149 $$ joinI $ Z.decompress win
db1395d @tanimoto Started testing code
tanimoto authored
150 $$ consume
151
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
152 -- | Compress a ByteString 'n' times and then decompress it 'n' times
b0af09e @tanimoto Add more tests
tanimoto authored
153 compressDecompressMany
154 :: MonadIO m
155 => WindowBits
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
156 -> Int
b0af09e @tanimoto Add more tests
tanimoto authored
157 -> [ByteString]
158 -> m ByteString
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
159 compressDecompressMany win n xs =
b0af09e @tanimoto Add more tests
tanimoto authored
160 E.run_ $ E.enumList 1 xs
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
161 $$ concatWith consume es
162 where
9db9966 @tanimoto Revert to original names
tanimoto authored
163 es = replicate m (Z.compress 7 win) ++ replicate m (Z.decompress win)
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
164 m = 1 + (abs n `rem` 10) -- restrict n to [1, 10]
db1395d @tanimoto Started testing code
tanimoto authored
165
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
166 -- | Compress a [ByteString] to a file with an Enumeratee
167 compressFileWith
168 :: Enumeratee ByteString ByteString IO ()
169 -> WindowBits -> FilePath -> [ByteString] -> IO ()
170 compressFileWith enum win file xs = bracket
ca5e1b4 @tanimoto Changed 'openFile' -> 'openBinaryFile'
tanimoto authored
171 (openBinaryFile file WriteMode)
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
172 (hClose)
173 $ \ h -> do
db1395d @tanimoto Started testing code
tanimoto authored
174 run_ $ E.enumList 1 xs
9db9966 @tanimoto Revert to original names
tanimoto authored
175 $$ joinI $ Z.compress 7 win
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
176 $$ joinI $ enum
177 $$ EB.iterHandle h
178
179 -- | Decompress from a file with an Enumeratee
180 decompressFileWith
181 :: Enumeratee ByteString ByteString IO ByteString
182 -> WindowBits -> FilePath -> IO ByteString
183 decompressFileWith enum win file =
184 run_ $ EB.enumFile file
9db9966 @tanimoto Revert to original names
tanimoto authored
185 $$ joinI $ Z.decompress win
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
186 $$ joinI $ enum
187 $$ consume
db1395d @tanimoto Started testing code
tanimoto authored
188
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
189 -- | Alternative implementation of compress for comparison
190 compressChunks :: WindowBits -> [ByteString] -> IO [ByteString]
191 compressChunks win xs = do
192 def <- initDeflate 7 win
193 gziped <- foldM (go' def) id xs
aaf7baf @def-lkb Bumped version number and updated test suite
def-lkb authored
194 gziped' <- go gziped (finishDeflate def)
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
195 return $ gziped' []
ca5e1b4 @tanimoto Changed 'openFile' -> 'openBinaryFile'
tanimoto authored
196 where
aaf7baf @def-lkb Bumped version number and updated test suite
def-lkb authored
197 go' def front bs = feedDeflate def bs >>= go front
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
198 go front x = do
199 y <- x
200 case y of
201 Nothing -> return front
202 Just z -> go (front . (:) z) x
203
204 -- | Alternative implementation of decompress for comparison
205 decompressChunks :: WindowBits -> [ByteString] -> IO [ByteString]
206 decompressChunks win xs = do
207 inf <- initInflate win
208 ungziped <- foldM (go' inf) id xs
209 final <- finishInflate inf
210 return $ ungziped [final]
ca5e1b4 @tanimoto Changed 'openFile' -> 'openBinaryFile'
tanimoto authored
211 where
aaf7baf @def-lkb Bumped version number and updated test suite
def-lkb authored
212 go' inf front bs = feedInflate inf bs >>= go front
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
213 go front x = do
214 y <- x
215 case y of
216 Nothing -> return front
217 Just z -> go (front . (:) z) x
218
db1395d @tanimoto Started testing code
tanimoto authored
219 -------------------------------------------------------------------------------
220 -- Properties
221 -------------------------------------------------------------------------------
222
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
223 -- | Compare compresssion via lists and enumerator
224 prop_compress_compare :: WindowBits -> [ByteString] -> Property
225 prop_compress_compare win xs = monadicIO $ do
226 chks <- Q.run $ B.concat `fmap` compressChunks win xs
227 enum <- Q.run $ compress win xs
228 assert $ enum == chks
229
230 -- | Compare decompression via lists and enumerator
231 prop_decompress_compare :: WindowBits -> [ByteString] -> Property
232 prop_decompress_compare win xs = monadicIO $ do
233 comp <- Q.run $ compressChunks win xs
234 chks <- Q.run $ B.concat `fmap` decompressChunks win comp
235 enum <- Q.run $ decompress win comp
236 assert $ enum == chks
237
238 -- | Check: bs == decompress (compress bs)
878e235 @tanimoto Improved comments
tanimoto authored
239 -- (That is, with separate Enumeratees)
b0af09e @tanimoto Add more tests
tanimoto authored
240 prop_compress_decompress :: WindowBits -> [ByteString] -> Property
241 prop_compress_decompress win xs = monadicIO $ do
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
242 cs <- Q.run $ compress win xs
243 ys <- Q.run $ decompress win [cs]
244 assert (B.concat xs == ys)
245
246 -- | Check: bs == compressDecompress bs
878e235 @tanimoto Improved comments
tanimoto authored
247 -- (That is, with a single Enumeratee)
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
248 prop_compress_decompress' :: WindowBits -> [ByteString] -> Property
249 prop_compress_decompress' win xs = monadicIO $ do
b0af09e @tanimoto Add more tests
tanimoto authored
250 ys <- Q.run $ compressDecompress win xs
251 assert (B.concat xs == ys)
252
c0d092f @maltem gzip/ungzip shorthand. Factor out callback.
authored
253 -- | Check: bs == gzipUngzip bs
254 -- (just to see if the default parameters aren't broken)
255 prop_gzip_ungzip :: [ByteString] -> Property
256 prop_gzip_ungzip xs = monadicIO $ do
257 ys <- Q.run $ gzipUngzip xs
258 assert (B.concat xs == ys)
259
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
260 -- | Check if using an Iteratee that consumes only a few bytes works
b0af09e @tanimoto Add more tests
tanimoto authored
261 prop_unconsumed :: WindowBits -> [ByteString] -> Property
262 prop_unconsumed win xs = monadicIO $ do
263 ys <- Q.run $ compressDecompressWith unconsumed win xs
db1395d @tanimoto Started testing code
tanimoto authored
264 assert (B.concat xs == ys)
265
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
266 -- | Check if mapping the identity function doesn't affect anything
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
267 prop_map_id :: WindowBits -> [ByteString] -> Property
268 prop_map_id win xs = monadicIO $ do
4d2135f @maltem Re-export ZlibException (fixes #2) and fix a warning
authored
269 ys <- Q.run $ compressDecompressWith (EL.map id) win xs
db1395d @tanimoto Started testing code
tanimoto authored
270 assert (B.concat xs == ys)
271
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
272 -- | Check if mapping 'reverse . reverse' doesn't affect anything
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
273 prop_map_revrev :: WindowBits -> [ByteString] -> Property
274 prop_map_revrev win xs = monadicIO $ do
4d2135f @maltem Re-export ZlibException (fixes #2) and fix a warning
authored
275 ys <- Q.run $ compressDecompressWith (EL.map $ B.reverse . B.reverse) win xs
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
276 assert (B.concat xs == ys)
db1395d @tanimoto Started testing code
tanimoto authored
277
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
278 -- | Check if compressing and decompressing multiple times works
279 prop_many :: WindowBits -> Int -> [ByteString] -> Property
280 prop_many win n xs = monadicIO $ do
281 ys <- Q.run $ compressDecompressMany win n xs
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
282 assert (B.concat xs == ys)
283
878e235 @tanimoto Improved comments
tanimoto authored
284 -- | Check compressing and decompressing a file
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
285 prop_files_map_id :: FilePath -> WindowBits -> [ByteString] -> Property
286 prop_files_map_id file win xs = monadicIO $ do
287 Q.run $ compressFileWith enum win file xs
288 ys <- Q.run $ decompressFileWith enum win file
289 assert (B.concat xs == ys)
290 where
4d2135f @maltem Re-export ZlibException (fixes #2) and fix a warning
authored
291 enum = EL.map id
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
292
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
293 -- | Check compressing and decompressing a file with an Iteratee that
294 -- consumes only a few bytes
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
295 prop_files_unconsumed :: FilePath -> WindowBits -> [ByteString] -> Property
296 prop_files_unconsumed file win xs = monadicIO $ do
297 Q.run $ compressFileWith unconsumed win file xs
298 ys <- Q.run $ decompressFileWith unconsumed win file
299 assert (B.concat xs == ys)
db1395d @tanimoto Started testing code
tanimoto authored
300
301 -------------------------------------------------------------------------------
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
302 -- Tests
db1395d @tanimoto Started testing code
tanimoto authored
303 -------------------------------------------------------------------------------
304
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
305 tests :: [Test]
306 tests = let testFile = "zlib-enum-test-file" in
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
307 [ testGroup "compare"
308 [ testProperty "compress_compressChunks" prop_compress_compare
309 , testProperty "decompress_decompressChunks" prop_decompress_compare
310 ]
311 , testGroup "enumList"
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
312 [ testProperty "compress_decompress" prop_compress_decompress
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
313 , testProperty "compress_decompress'" prop_compress_decompress'
c0d092f @maltem gzip/ungzip shorthand. Factor out callback.
authored
314 , testProperty "gzip_ungzip" prop_gzip_ungzip
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
315 , testProperty "unconsumed" prop_unconsumed
316 , testProperty "map_id" prop_map_id
317 , testProperty "map_revrev" prop_map_revrev
c9e17e3 @tanimoto Compare against alternative implementation using lists
tanimoto authored
318 , testProperty "many" prop_many
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
319 ]
320 , testGroup "enumFile"
321 [ testProperty "files_map_id" (prop_files_map_id testFile)
322 , testProperty "files_unconsumed" (prop_files_unconsumed testFile)
323 ]
324 ]
db1395d @tanimoto Started testing code
tanimoto authored
325
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
326 -------------------------------------------------------------------------------
327 -- IO
328 -------------------------------------------------------------------------------
b0af09e @tanimoto Add more tests
tanimoto authored
329
f7bf0d8 @tanimoto Added more properties, using test-framework
tanimoto authored
330 main :: IO ()
331 main = defaultMain tests
Something went wrong with that request. Please try again.