4
4
--
5
5
{-# LANGUAGE FlexibleInstances #-}
6
6
{-# LANGUAGE DeriveFunctor #-}
7
+ {-# LANGUAGE TypeApplications #-}
8
+ {-# LANGUAGE ScopedTypeVariables #-}
7
9
8
10
{-# OPTIONS_GHC -fno-warn-orphans #-}
9
11
@@ -29,13 +31,14 @@ module Tests.QuickCheckUtils
29
31
) where
30
32
31
33
import Control.Arrow ((***) )
32
- import Control.DeepSeq (NFData (.. ), deepseq )
33
- import Control.Exception (bracket )
34
+ import Control.DeepSeq (NFData (.. ), deepseq )
34
35
import Data.Char (isSpace )
36
+ import Data.Coerce (coerce )
35
37
import Data.Text.Foreign (I8 )
36
38
import Data.Text.Lazy.Builder.RealFloat (FPFormat (.. ))
37
39
import Data.Word (Word8 , Word16 )
38
- import Test.QuickCheck hiding (Fixed (.. ), Small (.. ), (.&.) )
40
+ import GHC.Num (integerLog2 )
41
+ import Test.QuickCheck hiding (Fixed (.. ), Small (.. ), (.&.) )
39
42
import Tests.Utils
40
43
import qualified Data.ByteString as B
41
44
import qualified Data.ByteString.Lazy as BL
@@ -47,6 +50,7 @@ import qualified Data.Text.Internal.Lazy as TL
47
50
import qualified Data.Text.Internal.Lazy.Fusion as TLF
48
51
import qualified Data.Text.Lazy as TL
49
52
import qualified System.IO as IO
53
+ import Control.Applicative (liftA2 , liftA3 )
50
54
51
55
genWord8 :: Gen Word8
52
56
genWord8 = chooseAny
@@ -56,7 +60,7 @@ instance Arbitrary I8 where
56
60
shrink = shrinkIntegral
57
61
58
62
instance Arbitrary B. ByteString where
59
- arbitrary = B. pack `fmap` listOf genWord8
63
+ arbitrary = B. pack <$> listOf genWord8
60
64
shrink = map B. pack . shrink . B. unpack
61
65
62
66
instance Arbitrary BL. ByteString where
@@ -66,64 +70,84 @@ instance Arbitrary BL.ByteString where
66
70
, BL. fromChunks . map B. singleton <$> listOf genWord8
67
71
-- so that a code point with 4 byte long utf8 representation
68
72
-- could appear split over 3 non-singleton chunks
69
- , (\ a b c -> BL. fromChunks [a, b, c])
70
- <$> arbitrary
71
- <*> ((\ a b -> B. pack [a, b]) <$> genWord8 <*> genWord8)
72
- <*> arbitrary
73
+ , liftA3 (\ a b c -> BL. fromChunks [a, b, c])
74
+ arbitrary
75
+ (liftA2 (\ a b -> B. pack [a, b])
76
+ genWord8
77
+ genWord8
78
+ )
79
+ arbitrary
73
80
]
74
81
shrink xs = BL. fromChunks <$> shrink (BL. toChunks xs)
75
82
76
83
-- | For tests that have O(n^2) running times or input sizes, resize
77
84
-- their inputs to the square root of the originals.
78
85
newtype Sqrt a = Sqrt { unSqrt :: a }
79
- deriving (Eq , Show )
86
+ deriving (Eq , Show )
80
87
81
88
instance Arbitrary a => Arbitrary (Sqrt a ) where
82
- arbitrary = fmap Sqrt $ sized $ \ n -> resize (smallish n) arbitrary
83
- where
84
- smallish = round . ( sqrt :: Double -> Double ) . fromIntegral . abs
85
- shrink = map Sqrt . shrink . unSqrt
89
+ arbitrary = coerce $ sized $ \ n -> resize (smallish n) $ arbitrary @ a
90
+ where
91
+ smallish = round . sqrt @ Double . fromIntegral . abs
92
+ shrink = coerce ( shrink @ a )
86
93
87
94
instance Arbitrary T. Text where
88
- arbitrary = ( T. pack . getUnicodeString) `fmap` arbitrary
95
+ arbitrary = T. pack <$> listOf arbitraryUnicodeChar -- without surrogates
89
96
shrink = map T. pack . shrink . T. unpack
90
97
91
98
instance Arbitrary TL. Text where
92
- arbitrary = ( TL. fromChunks . map notEmpty . unSqrt) `fmap` arbitrary
99
+ arbitrary = TL. fromChunks <$> coerce (arbitrary @ ( Sqrt [ NotEmpty T. Text ]))
93
100
shrink = map TL. pack . shrink . TL. unpack
94
101
95
102
newtype BigInt = Big Integer
96
- deriving (Eq , Show )
103
+ deriving (Eq , Show )
97
104
98
105
instance Arbitrary BigInt where
99
- arbitrary = choose (1 :: Int ,200 ) >>= \ e -> Big <$> choose (10 ^ (e- 1 ),10 ^ e)
100
- shrink (Big a) = [Big (a `div` 2 ^ (l- e)) | e <- shrink l]
101
- where l = truncate (log (fromIntegral a) / log 2 :: Double ) :: Integer
106
+ arbitrary = do
107
+ e <- choose @ Int (1 ,200 )
108
+ coerce $ choose @ Integer (10 ^ (e- 1 ),10 ^ e)
109
+
110
+ shrink ba = [coerce (a `div` 2 ^ (l- e)) | e <- shrink l]
111
+ where
112
+ a :: Integer
113
+ a = coerce ba
114
+ l :: Word
115
+ l = integerLog2 a
102
116
103
117
newtype NotEmpty a = NotEmpty { notEmpty :: a }
104
- deriving (Eq , Ord , Show )
118
+ deriving (Eq , Ord , Show )
119
+
120
+ toNotEmptyBy :: Functor m => ([Char ] -> a ) -> m (NonEmptyList Char ) -> m (NotEmpty a )
121
+ toNotEmptyBy f = fmap (coerce f)
122
+
123
+ arbitraryNotEmptyBy :: ([Char ] -> a ) -> Gen (NotEmpty a )
124
+ arbitraryNotEmptyBy f = toNotEmptyBy f arbitrary
125
+
126
+ shrinkNotEmptyBy :: ([Char ] -> a ) -> (a -> [Char ]) -> NotEmpty a -> [NotEmpty a ]
127
+ shrinkNotEmptyBy g f =
128
+ toNotEmptyBy g . shrink . coerce f
105
129
106
130
instance Arbitrary (NotEmpty T. Text ) where
107
- arbitrary = fmap (NotEmpty . T. pack . getNonEmpty) arbitrary
108
- shrink = fmap (NotEmpty . T. pack . getNonEmpty)
109
- . shrink . NonEmpty . T. unpack . notEmpty
131
+ arbitrary = arbitraryNotEmptyBy T. pack
132
+ shrink = shrinkNotEmptyBy T. pack T. unpack
110
133
111
134
instance Arbitrary (NotEmpty TL. Text ) where
112
- arbitrary = fmap ( NotEmpty . TL. pack . getNonEmpty) arbitrary
113
- shrink = fmap ( NotEmpty . TL. pack . getNonEmpty)
114
- . shrink . NonEmpty . TL. unpack . notEmpty
135
+ arbitrary = arbitraryNotEmptyBy TL. pack
136
+ shrink = shrinkNotEmptyBy TL. pack TL. unpack
137
+
115
138
116
139
data DecodeErr = Lenient | Ignore | Strict | Replace
117
- deriving (Show , Eq , Bounded , Enum )
140
+ deriving (Show , Eq , Bounded , Enum )
118
141
119
142
genDecodeErr :: DecodeErr -> Gen T. OnDecodeError
120
- genDecodeErr Lenient = return T. lenientDecode
121
- genDecodeErr Ignore = return T. ignore
122
- genDecodeErr Strict = return T. strictDecode
123
- genDecodeErr Replace = (\ c _ _ -> c) <$> frequency
124
- [ (1 , return Nothing )
125
- , (50 , Just <$> arbitraryUnicodeChar)
126
- ]
143
+ genDecodeErr Lenient = pure T. lenientDecode
144
+ genDecodeErr Ignore = pure T. ignore
145
+ genDecodeErr Strict = pure T. strictDecode
146
+ genDecodeErr Replace = (\ c _ _ -> c) <$>
147
+ frequency
148
+ [ (1 , pure Nothing )
149
+ , (50 , pure <$> arbitraryUnicodeChar)
150
+ ]
127
151
128
152
instance Arbitrary DecodeErr where
129
153
arbitrary = arbitraryBoundedEnum
@@ -167,71 +191,84 @@ eq a b s = a s =^= b s
167
191
-- What about with the RHS packed?
168
192
eqP :: (Eq a , Show a , Stringy s ) =>
169
193
(String -> a ) -> (s -> a ) -> String -> Word8 -> Property
170
- eqP f g s w = counterexample " orig" (f s =^= g t) .&&.
171
- counterexample " mini" (f s =^= g mini) .&&.
172
- counterexample " head" (f sa =^= g ta) .&&.
173
- counterexample " tail" (f sb =^= g tb)
174
- where t = packS s
175
- mini = packSChunkSize 10 s
176
- (sa,sb) = splitAt m s
177
- (ta,tb) = splitAtS m t
178
- l = length s
179
- m | l == 0 = n
180
- | otherwise = n `mod` l
181
- n = fromIntegral w
194
+ eqP f g s w =
195
+ testCounterExample " orig" s t .&&.
196
+ testCounterExample " mini" s mini .&&.
197
+ testCounterExample " head" sa ta .&&.
198
+ testCounterExample " tail" sb tb
199
+ where
200
+ testCounterExample txt a b = counterexample txt $ f a =^= g b
201
+
202
+ t = packS s
203
+ mini = packSChunkSize 10 s
204
+ (sa,sb) = splitAt m s
205
+ (ta,tb) = splitAtS m t
206
+
207
+ m = if l == 0 then n else n `mod` l
208
+ where
209
+ l = length s
210
+ n = fromIntegral w
182
211
183
212
eqPSqrt :: (Eq a , Show a , Stringy s ) =>
184
213
(String -> a ) -> (s -> a ) -> Sqrt String -> Word8 -> Property
185
- eqPSqrt f g s = eqP f g (unSqrt s)
214
+ eqPSqrt f g s = eqP f g $ coerce s
186
215
187
216
instance Arbitrary FPFormat where
188
217
arbitrary = arbitraryBoundedEnum
189
218
190
- newtype Precision a = Precision ( Maybe Int )
191
- deriving (Eq , Show )
219
+ newtype Precision a = Precision { unPrecision :: Maybe Int }
220
+ deriving (Eq , Show )
192
221
222
+ -- Deprecated on 2021-10-05
193
223
precision :: a -> Precision a -> Maybe Int
194
- precision _ (Precision prec) = prec
224
+ precision _ = coerce
225
+ {-# DEPRECATED precision "Use @coerce@ with types instead." #-}
195
226
196
227
arbitraryPrecision :: Int -> Gen (Precision a )
197
- arbitraryPrecision maxDigits = Precision <$> do
198
- n <- choose (- 1 ,maxDigits)
199
- return $ if n == - 1
200
- then Nothing
201
- else Just n
228
+ arbitraryPrecision maxDigits = do
229
+ n <- choose (0 ,maxDigits)
230
+ frequency
231
+ [ (1 , pure $ coerce $ Nothing @ Int )
232
+ , (n, pure $ coerce $ Just n)
233
+ ]
202
234
203
235
instance Arbitrary (Precision Float ) where
204
236
arbitrary = arbitraryPrecision 11
205
- shrink = map Precision . shrink . precision undefined
237
+ shrink = coerce ( shrink @ ( Maybe Int ))
206
238
207
239
instance Arbitrary (Precision Double ) where
208
240
arbitrary = arbitraryPrecision 22
209
- shrink = map Precision . shrink . precision undefined
241
+ shrink = coerce ( shrink @ ( Maybe Int ))
210
242
211
243
instance Arbitrary IO. Newline where
212
- arbitrary = oneof [return IO. LF , return IO. CRLF ]
244
+ arbitrary = oneof [pure IO. LF , pure IO. CRLF ]
213
245
214
246
instance Arbitrary IO. NewlineMode where
215
- arbitrary = IO. NewlineMode <$> arbitrary <*> arbitrary
247
+ arbitrary =
248
+ liftA2 IO. NewlineMode
249
+ arbitrary
250
+ arbitrary
216
251
217
252
instance Arbitrary IO. BufferMode where
218
- arbitrary = oneof [ return IO. NoBuffering ,
219
- return IO. LineBuffering ,
220
- return (IO. BlockBuffering Nothing ),
221
- (IO. BlockBuffering . Just . (+ 1 ) . fromIntegral ) `fmap`
222
- (arbitrary :: Gen Word16 ) ]
253
+ arbitrary =
254
+ oneof
255
+ [ pure IO. NoBuffering
256
+ , pure IO. LineBuffering
257
+ , pure (IO. BlockBuffering Nothing )
258
+ , IO. BlockBuffering . pure . succ . fromIntegral <$> arbitrary @ Word16
259
+ ]
223
260
224
261
-- This test harness is complex! What property are we checking?
225
262
--
226
263
-- Reading after writing a multi-line file should give the same
227
264
-- results as were written.
228
265
--
229
266
-- What do we vary while checking this property?
230
- -- * The lines themselves, scrubbed to contain neither CR nor LF. (By
231
- -- working with a list of lines, we ensure that the data will
232
- -- sometimes contain line endings.)
233
- -- * Newline translation mode.
234
- -- * Buffering.
267
+ -- * The lines themselves, scrubbed to contain neither CR nor LF. (By
268
+ -- working with a list of lines, we ensure that the data will
269
+ -- sometimes contain line endings.)
270
+ -- * Newline translation mode.
271
+ -- * Buffering.
235
272
write_read :: (NFData a , Eq a , Show a )
236
273
=> ([b ] -> a )
237
274
-> ((Char -> Bool ) -> a -> b )
@@ -245,18 +282,26 @@ write_read _ _ _ _ (IO.NewlineMode IO.LF IO.CRLF) _ _ = discard
245
282
write_read unline filt writer reader nl buf ts = ioProperty $
246
283
(=== t) <$> act
247
284
where
248
- t = unline . map (filt (not . (`elem` " \r\n " ))) $ ts
249
-
250
- act = withTempFile $ \ path h -> do
251
- IO. hSetNewlineMode h nl
252
- IO. hSetBuffering h buf
253
- () <- writer h t
254
- IO. hClose h
255
- bracket (IO. openFile path IO. ReadMode ) IO. hClose $ \ h' -> do
256
- IO. hSetNewlineMode h' nl
257
- IO. hSetBuffering h' buf
258
- r <- reader h'
259
- r `deepseq` return r
285
+
286
+ t = unline . map (filt (`notElem` " \r\n " )) $ ts
287
+
288
+ act =
289
+ withTempFile roundTrip
290
+ where
291
+
292
+ readBack h' = do
293
+ IO. hSetNewlineMode h' nl
294
+ IO. hSetBuffering h' buf
295
+ r <- reader h'
296
+ r `deepseq` pure r
297
+
298
+ roundTrip path h = do
299
+ IO. hSetNewlineMode h nl
300
+ IO. hSetBuffering h buf
301
+ () <- writer h t
302
+ IO. hClose h
303
+
304
+ IO. withFile path IO. ReadMode readBack
260
305
261
306
-- Generate various Unicode space characters with high probability
262
307
arbitrarySpacyChar :: Gen Char
@@ -269,5 +314,5 @@ newtype SpacyString = SpacyString { getSpacyString :: String }
269
314
deriving (Eq , Ord , Show , Read )
270
315
271
316
instance Arbitrary SpacyString where
272
- arbitrary = SpacyString `fmap` listOf arbitrarySpacyChar
273
- shrink ( SpacyString xs) = SpacyString `fmap` shrink xs
317
+ arbitrary = coerce $ listOf arbitrarySpacyChar
318
+ shrink = coerce ( shrink @ [ Char ])
0 commit comments