File: QC.hs

package info (click to toggle)
haskell-binary 0.4.1-3
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 292 kB
  • ctags: 11
  • sloc: haskell: 4,054; makefile: 88; ansic: 39
file content (244 lines) | stat: -rw-r--r-- 8,753 bytes parent folder | download
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
{-# OPTIONS_GHC -fglasgow-exts #-}
module Main where

import Data.Binary
import Data.Binary.Put
import Data.Binary.Get

import Parallel

import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Internal as L
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.IntMap as IntMap
import qualified Data.IntSet as IntSet

import Data.Array (Array)
import Data.Array.IArray
import Data.Array.Unboxed (UArray)

import qualified Control.Exception as C (catch,evaluate)
import Control.Monad
import Foreign
import System.Environment
import System.IO
import System.IO.Unsafe

import Test.QuickCheck hiding (test)
import QuickCheckUtils
import Text.Printf

-- import qualified Data.Sequence as Seq

------------------------------------------------------------------------

roundTrip :: (Eq a, Binary a) => a -> (L.ByteString -> L.ByteString) -> Bool
roundTrip a f = a ==
    {-# SCC "decode.refragment.encode" #-} decode (f (encode a))

roundTripWith put get x =
    forAll positiveList $ \xs ->
    x == runGet get (refragment xs (runPut (put x)))

-- make sure that a test fails
errorish :: B a
errorish a = unsafePerformIO $
    C.catch (do C.evaluate a
                return False)
            (\_ -> return True)

-- low level ones:

prop_Word16be = roundTripWith putWord16be getWord16be
prop_Word16le = roundTripWith putWord16le getWord16le
prop_Word16host = roundTripWith putWord16host getWord16host

prop_Word32be = roundTripWith putWord32be getWord32be
prop_Word32le = roundTripWith putWord32le getWord32le
prop_Word32host = roundTripWith putWord32host getWord32host

prop_Word64be = roundTripWith putWord64be getWord64be
prop_Word64le = roundTripWith putWord64le getWord64le
prop_Word64host = roundTripWith putWord64host getWord64host

prop_Wordhost = roundTripWith putWordhost getWordhost

-- read too much:

prop_bookworm x = errorish $ x == a && x /= b
  where
    (a,b) = decode (encode x)

-- sanity:

invariant_lbs :: L.ByteString -> Bool
invariant_lbs (L.Empty)      = True
invariant_lbs (L.Chunk x xs) = not (B.null x) && invariant_lbs xs

prop_invariant :: (Binary a) => a -> Bool
prop_invariant = invariant_lbs . encode

-- be lazy!

-- doesn't do fair testing of lazy put/get.
-- tons of untested cases

-- lazyTrip :: (Binary a, Eq a) => a -> Property
-- lazyTrip a = forAll positiveList $ \xs ->
--     a == (runGet lazyGet . refragment xs . runPut . lazyPut $ a)

-- refragment a lazy bytestring's chunks
refragment :: [Int] -> L.ByteString -> L.ByteString
refragment [] lps = lps
refragment (x:xs) lps =
    let x' = fromIntegral . (+1) . abs $ x
        rest = refragment xs (L.drop x' lps) in
    L.append (L.fromChunks [B.concat . L.toChunks . L.take x' $ lps]) rest

-- check identity of refragmentation
prop_refragment lps xs = lps == refragment xs lps

-- check that refragmention still hold invariant
prop_refragment_inv lps xs = invariant_lbs $ refragment xs lps

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering
    s <- getArgs
    let x = if null s then 100 else read (head s)
    pRun 2 x tests

{-
run :: [(String, Int -> IO ())] -> IO ()
run tests = do
    x <- getArgs
    let n = if null x then 100 else read . head $ x
    mapM_ (\(s,a) -> printf "%-50s" s >> a n) tests
-}

------------------------------------------------------------------------

type T a = a -> Property
type B a = a -> Bool

p       :: Testable a => a -> Int -> IO String
p       = pNon

test    :: (Eq a, Binary a) => a -> Property
test a  = forAll positiveList (roundTrip a . refragment)

positiveList :: Gen [Int]
positiveList = fmap (filter (/=0) . map abs) $ arbitrary

-- tests :: [(String, Int -> IO String)]
tests =
-- utils
        [ ("refragment id",        p prop_refragment     )
        , ("refragment invariant", p prop_refragment_inv )

-- boundaries
        , ("read to much",  p (prop_bookworm :: B Word8     ))

-- Primitives
        , ("Word16be",      p prop_Word16be)
        , ("Word16le",      p prop_Word16le)
        , ("Word16host",    p prop_Word16host)
        , ("Word32be",      p prop_Word32be)
        , ("Word32le",      p prop_Word32le)
        , ("Word32host",    p prop_Word32host)
        , ("Word64be",      p prop_Word64be)
        , ("Word64le",      p prop_Word64le)
        , ("Word64host",    p prop_Word64host)
        , ("Wordhost",      p prop_Wordhost)

-- higher level ones using the Binary class
        ,("()",         p (test :: T ()                     ))
        ,("Bool",       p (test :: T Bool                   ))
        ,("Ordering",   p (test :: T Ordering               ))

        ,("Word8",      p (test :: T Word8                  ))
        ,("Word16",     p (test :: T Word16                 ))
        ,("Word32",     p (test :: T Word32                 ))
        ,("Word64",     p (test :: T Word64                 ))

        ,("Int8",       p (test :: T Int8                   ))
        ,("Int16",      p (test :: T Int16                  ))
        ,("Int32",      p (test :: T Int32                  ))
        ,("Int64",      p (test :: T Int64                  ))

        ,("Word",       p (test :: T Word                   ))
        ,("Int",        p (test :: T Int                    ))
        ,("Integer",    p (test :: T Integer                ))

        ,("Float",      p (test :: T Float                  ))
        ,("Double",     p (test :: T Double                 ))

        ,("Char",       p (test :: T Char                   ))

        ,("[()]",       p (test :: T [()]                  ))
        ,("[Word8]",    p (test :: T [Word8]               ))
        ,("[Word32]",   p (test :: T [Word32]              ))
        ,("[Word64]",   p (test :: T [Word64]              ))
        ,("[Word]",     p (test :: T [Word]                ))
        ,("[Int]",      p (test :: T [Int]                 ))
        ,("[Integer]",  p (test :: T [Integer]             ))
        ,("String",     p (test :: T String                ))

        ,("((), ())",           p (test :: T ((), ())        ))
        ,("(Word8, Word32)",    p (test :: T (Word8, Word32) ))
        ,("(Int8, Int32)",      p (test :: T (Int8,  Int32)  ))
        ,("(Int32, [Int])",     p (test :: T (Int32, [Int])  ))

        ,("Maybe Int8",         p (test :: T (Maybe Int8)        ))
        ,("Either Int8 Int16",  p (test :: T (Either Int8 Int16) ))

        ,("(Maybe Word8, Bool, [Int], Either Bool Word8)",
                p (test :: T (Maybe Word8, Bool, [Int], Either Bool Word8) ))

        ,("(Int, ByteString)",        p (test     :: T (Int, B.ByteString)   ))
--      ,("Lazy (Int, ByteString)",   p (lazyTrip :: T (Int, B.ByteString)   ))
        ,("[(Int, ByteString)]",      p (test     :: T [(Int, B.ByteString)] ))
--      ,("Lazy [(Int, ByteString)]", p (lazyTrip :: T [(Int, B.ByteString)] ))


--      ,("Lazy IntMap",       p (lazyTrip  :: T IntSet.IntSet          ))
        ,("IntSet",            p (test      :: T IntSet.IntSet          ))
        ,("IntMap ByteString", p (test      :: T (IntMap.IntMap B.ByteString) ))

        ,("B.ByteString",  p (test :: T B.ByteString        ))
        ,("L.ByteString",  p (test :: T L.ByteString        ))

        ,("B.ByteString invariant",   p (prop_invariant :: B B.ByteString                 ))
        ,("[B.ByteString] invariant", p (prop_invariant :: B [B.ByteString]               ))
        ,("L.ByteString invariant",   p (prop_invariant :: B L.ByteString                 ))
        ,("[L.ByteString] invariant", p (prop_invariant :: B [L.ByteString]               ))
        ,("IntMap invariant",         p (prop_invariant :: B (IntMap.IntMap B.ByteString) ))

        ,("Set Word32",      p (test :: T (Set.Set Word32)      ))
        ,("Map Word16 Int",  p (test :: T (Map.Map Word16 Int)  ))

        ,("(Maybe Int64, Bool, [Int])", p (test :: T (Maybe Int64, Bool, [Int])))

{-
--
-- Big tuples lack an Arbitrary instance in Hugs/QuickCheck
--

        ,("(Maybe Word16, Bool, [Int], Either Bool Word16, Int)",
            p (test :: T (Maybe Word16, Bool, [Int], Either Bool Word16, Int) ))

        ,("(Maybe Word32, Bool, [Int], Either Bool Word32, Int, Int)", p (roundTrip :: (Maybe Word32, Bool, [Int], Either Bool Word32, Int, Int) -> Bool))

        ,("(Maybe Word64, Bool, [Int], Either Bool Word64, Int, Int, Int)", p (roundTrip :: (Maybe Word64, Bool, [Int], Either Bool Word64, Int, Int, Int) -> Bool))
-}

-- GHC only:
--      ,("Sequence", p (roundTrip :: Seq.Seq Int64 -> Bool))

-- Obsolete
--      ,("ensureLeft/Fail", mytest (shouldFail (decode L.empty :: Either ParseError Int)))
        ]