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
@@ -30,8 +32,8 @@ module Tests.QuickCheckUtils
30
32
31
33
import Control.Arrow ((***) )
32
34
import Control.DeepSeq (NFData (.. ), deepseq )
33
- import Control.Exception (bracket )
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 )
@@ -47,6 +49,8 @@ import qualified Data.Text.Internal.Lazy as TL
47
49
import qualified Data.Text.Internal.Lazy.Fusion as TLF
48
50
import qualified Data.Text.Lazy as TL
49
51
import qualified System.IO as IO
52
+ import Control.Applicative (liftA2 )
53
+ import Data.Bits (shiftR , shiftL , countLeadingZeros , finiteBitSize )
50
54
51
55
genWord8 :: Gen Word8
52
56
genWord8 = chooseAny
@@ -79,39 +83,63 @@ newtype Sqrt a = Sqrt { unSqrt :: a }
79
83
deriving (Eq , Show )
80
84
81
85
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
86
+ arbitrary = coerce $ sized $ \ n -> resize (smallish n) $ arbitrary @ a
87
+ where
88
+ smallish = intSqrt . abs
89
+ -- | Simple implementation of square root for integers.
90
+ intSqrt :: Int -> Int
91
+ intSqrt n =
92
+ if n < 2
93
+ then n
94
+ else
95
+ let b2 = shiftR (finiteBitSize n - countLeadingZeros n) 1 in
96
+ shiftR (shiftL 1 b2 + shiftR n b2) 1
97
+ shrink = coerce (shrink @ a )
86
98
87
99
instance Arbitrary T. Text where
88
- arbitrary = ( T. pack . getUnicodeString) `fmap` arbitrary
100
+ arbitrary = T. pack <$> listOf arbitraryUnicodeChar -- without surrogates
89
101
shrink = map T. pack . shrink . T. unpack
90
102
91
103
instance Arbitrary TL. Text where
92
- arbitrary = ( TL. fromChunks . map notEmpty . unSqrt) `fmap` arbitrary
104
+ arbitrary = TL. fromChunks <$> coerce (arbitrary @ ( Sqrt [ NotEmpty T. Text ]))
93
105
shrink = map TL. pack . shrink . TL. unpack
94
106
95
107
newtype BigInt = Big Integer
96
108
deriving (Eq , Show )
97
109
98
110
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
111
+ arbitrary = do
112
+ e <- choose @ Int (1 ,200 )
113
+ coerce $ choose @ Integer (10 ^ (e- 1 ),10 ^ e)
114
+
115
+ shrink ba = [coerce (a `div` 2 ^ (l- e)) | e <- shrink l]
116
+ where
117
+ a :: Integer
118
+ a = coerce ba
119
+ l :: Word
120
+ l = integerLog2 a
102
121
103
122
newtype NotEmpty a = NotEmpty { notEmpty :: a }
104
123
deriving (Eq , Ord , Show )
105
124
125
+ toNotEmptyBy :: Functor m => ([Char ] -> a ) -> m (NonEmptyList Char ) -> m (NotEmpty a )
126
+ toNotEmptyBy f = fmap (coerce f)
127
+
128
+ arbitraryNotEmptyBy :: ([Char ] -> a ) -> Gen (NotEmpty a )
129
+ arbitraryNotEmptyBy f = toNotEmptyBy f arbitrary
130
+
131
+ shrinkNotEmptyBy :: ([Char ] -> a ) -> (a -> [Char ]) -> NotEmpty a -> [NotEmpty a ]
132
+ shrinkNotEmptyBy g f =
133
+ toNotEmptyBy g . shrink . coerce f
134
+
106
135
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
136
+ arbitrary = arbitraryNotEmptyBy T. pack
137
+ shrink = shrinkNotEmptyBy T. pack T. unpack
110
138
111
139
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
140
+ arbitrary = arbitraryNotEmptyBy TL. pack
141
+ shrink = shrinkNotEmptyBy TL. pack TL. unpack
142
+
115
143
116
144
data DecodeErr = Lenient | Ignore | Strict | Replace
117
145
deriving (Show , Eq , Bounded , Enum )
@@ -167,71 +195,84 @@ eq a b s = a s =^= b s
167
195
-- What about with the RHS packed?
168
196
eqP :: (Eq a , Show a , Stringy s ) =>
169
197
(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
198
+ eqP f g s w =
199
+ testCounterExamples
200
+ [ (" orig" , s , t )
201
+ , (" mini" , s , mini)
202
+ , (" head" , sa, ta )
203
+ , (" tail" , sb, tb )
204
+ ]
205
+ where
206
+ testCounterExamples :: Property
207
+ testCounterExamples = foldr (.&&.) mempty $ fmap $ uncurry3 testCounterExample
208
+ uncurry3 fun (a, b, c) = fun a b c
209
+ testCounterExample txt a b = counterexample txt $ f a =^= g b
210
+ t = packS s
211
+ mini = packSChunkSize 10 s
212
+ (sa,sb) = splitAt m s
213
+ (ta,tb) = splitAtS m t
214
+ m = (if null s then id else (`mod` length s)) $ fromIntegral w
182
215
183
216
eqPSqrt :: (Eq a , Show a , Stringy s ) =>
184
217
(String -> a ) -> (s -> a ) -> Sqrt String -> Word8 -> Property
185
- eqPSqrt f g s = eqP f g (unSqrt s)
218
+ eqPSqrt f g s = eqP f g $ coerce s
186
219
187
220
instance Arbitrary FPFormat where
188
221
arbitrary = arbitraryBoundedEnum
189
222
190
- newtype Precision a = Precision ( Maybe Int )
191
- deriving (Eq , Show )
223
+ newtype Precision a = Precision { unPrecision :: Maybe Int }
224
+ deriving (Eq , Show )
192
225
226
+ -- Deprecated on 2021-10-05
193
227
precision :: a -> Precision a -> Maybe Int
194
- precision _ (Precision prec) = prec
228
+ precision _ = coerce
229
+ {-# DEPRECATED precision "Use @coerce@ or @unPrecision@ with types instead." #-}
195
230
196
231
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
232
+ arbitraryPrecision maxDigits = do
233
+ n <- choose (0 ,maxDigits)
234
+ frequency
235
+ [ (1 , pure $ coerce $ Nothing @ Int )
236
+ , (n, pure $ coerce $ Just n)
237
+ ]
202
238
203
239
instance Arbitrary (Precision Float ) where
204
240
arbitrary = arbitraryPrecision 11
205
- shrink = map Precision . shrink . precision undefined
241
+ shrink = coerce ( shrink @ ( Maybe Int ))
206
242
207
243
instance Arbitrary (Precision Double ) where
208
244
arbitrary = arbitraryPrecision 22
209
- shrink = map Precision . shrink . precision undefined
245
+ shrink = coerce ( shrink @ ( Maybe Int ))
210
246
211
247
instance Arbitrary IO. Newline where
212
- arbitrary = oneof [return IO. LF , return IO. CRLF ]
248
+ arbitrary = oneof [pure IO. LF , pure IO. CRLF ]
213
249
214
250
instance Arbitrary IO. NewlineMode where
215
- arbitrary = IO. NewlineMode <$> arbitrary <*> arbitrary
251
+ arbitrary =
252
+ liftA2 IO. NewlineMode
253
+ arbitrary
254
+ arbitrary
216
255
217
256
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 ) ]
257
+ arbitrary =
258
+ oneof
259
+ [ pure IO. NoBuffering
260
+ , pure IO. LineBuffering
261
+ , pure (IO. BlockBuffering Nothing )
262
+ , IO. BlockBuffering . pure . succ . fromIntegral <$> arbitrary @ Word16
263
+ ]
223
264
224
265
-- This test harness is complex! What property are we checking?
225
266
--
226
267
-- Reading after writing a multi-line file should give the same
227
268
-- results as were written.
228
269
--
229
270
-- 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.
271
+ -- * The lines themselves, scrubbed to contain neither CR nor LF. (By
272
+ -- working with a list of lines, we ensure that the data will
273
+ -- sometimes contain line endings.)
274
+ -- * Newline translation mode.
275
+ -- * Buffering.
235
276
write_read :: (NFData a , Eq a , Show a )
236
277
=> ([b ] -> a )
237
278
-> ((Char -> Bool ) -> a -> b )
@@ -245,18 +286,24 @@ write_read _ _ _ _ (IO.NewlineMode IO.LF IO.CRLF) _ _ = discard
245
286
write_read unline filt writer reader nl buf ts = ioProperty $
246
287
(=== t) <$> act
247
288
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
289
+ t = unline . map (filt (`notElem` " \r\n " )) $ ts
290
+
291
+ act =
292
+ withTempFile roundTrip
293
+ where
294
+
295
+ roundTrip path h = do
296
+ IO. hSetNewlineMode h nl
297
+ IO. hSetBuffering h buf
298
+ () <- writer h t
299
+ IO. hClose h
300
+ let
301
+ readBack h' = do
302
+ IO. hSetNewlineMode h' nl
303
+ IO. hSetBuffering h' buf
304
+ r <- reader h'
305
+ r `deepseq` pure r
306
+ IO. withFile path IO. ReadMode readBack
260
307
261
308
-- Generate various Unicode space characters with high probability
262
309
arbitrarySpacyChar :: Gen Char
@@ -269,5 +316,5 @@ newtype SpacyString = SpacyString { getSpacyString :: String }
269
316
deriving (Eq , Ord , Show , Read )
270
317
271
318
instance Arbitrary SpacyString where
272
- arbitrary = SpacyString `fmap` listOf arbitrarySpacyChar
273
- shrink ( SpacyString xs) = SpacyString `fmap` shrink xs
319
+ arbitrary = coerce $ listOf arbitrarySpacyChar
320
+ shrink = coerce ( shrink @ [ Char ])
0 commit comments