44--
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE DeriveFunctor #-}
7+ {-# LANGUAGE TypeApplications #-}
8+ {-# LANGUAGE ScopedTypeVariables #-}
79
810{-# OPTIONS_GHC -fno-warn-orphans #-}
911
@@ -29,13 +31,14 @@ module Tests.QuickCheckUtils
2931 ) where
3032
3133import Control.Arrow ((***) )
32- import Control.DeepSeq (NFData (.. ), deepseq )
33- import Control.Exception (bracket )
34+ import Control.DeepSeq (NFData (.. ), deepseq )
3435import Data.Char (isSpace )
36+ import Data.Coerce (coerce )
3537import Data.Text.Foreign (I8 )
3638import Data.Text.Lazy.Builder.RealFloat (FPFormat (.. ))
3739import Data.Word (Word8 , Word16 )
38- import Test.QuickCheck hiding (Fixed (.. ), Small (.. ), (.&.) )
40+ import GHC.Num (integerLog2 )
41+ import Test.QuickCheck hiding (Fixed (.. ), Small (.. ), (.&.) )
3942import Tests.Utils
4043import qualified Data.ByteString as B
4144import qualified Data.ByteString.Lazy as BL
@@ -47,6 +50,7 @@ import qualified Data.Text.Internal.Lazy as TL
4750import qualified Data.Text.Internal.Lazy.Fusion as TLF
4851import qualified Data.Text.Lazy as TL
4952import qualified System.IO as IO
53+ import Control.Applicative (liftA2 , liftA3 )
5054
5155genWord8 :: Gen Word8
5256genWord8 = chooseAny
@@ -56,7 +60,7 @@ instance Arbitrary I8 where
5660 shrink = shrinkIntegral
5761
5862instance Arbitrary B. ByteString where
59- arbitrary = B. pack `fmap` listOf genWord8
63+ arbitrary = B. pack <$> listOf genWord8
6064 shrink = map B. pack . shrink . B. unpack
6165
6266instance Arbitrary BL. ByteString where
@@ -66,64 +70,84 @@ instance Arbitrary BL.ByteString where
6670 , BL. fromChunks . map B. singleton <$> listOf genWord8
6771 -- so that a code point with 4 byte long utf8 representation
6872 -- 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
7380 ]
7481 shrink xs = BL. fromChunks <$> shrink (BL. toChunks xs)
7582
7683-- | For tests that have O(n^2) running times or input sizes, resize
7784-- their inputs to the square root of the originals.
7885newtype Sqrt a = Sqrt { unSqrt :: a }
79- deriving (Eq , Show )
86+ deriving (Eq , Show )
8087
8188instance 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 )
8693
8794instance Arbitrary T. Text where
88- arbitrary = ( T. pack . getUnicodeString) `fmap` arbitrary
95+ arbitrary = T. pack <$> listOf arbitraryUnicodeChar -- without surrogates
8996 shrink = map T. pack . shrink . T. unpack
9097
9198instance Arbitrary TL. Text where
92- arbitrary = ( TL. fromChunks . map notEmpty . unSqrt) `fmap` arbitrary
99+ arbitrary = TL. fromChunks <$> coerce (arbitrary @ ( Sqrt [ NotEmpty T. Text ]))
93100 shrink = map TL. pack . shrink . TL. unpack
94101
95102newtype BigInt = Big Integer
96- deriving (Eq , Show )
103+ deriving (Eq , Show )
97104
98105instance 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
102116
103117newtype 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
105129
106130instance 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
110133
111134instance 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+
115138
116139data DecodeErr = Lenient | Ignore | Strict | Replace
117- deriving (Show , Eq , Bounded , Enum )
140+ deriving (Show , Eq , Bounded , Enum )
118141
119142genDecodeErr :: 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+ ]
127151
128152instance Arbitrary DecodeErr where
129153 arbitrary = arbitraryBoundedEnum
@@ -167,71 +191,84 @@ eq a b s = a s =^= b s
167191-- What about with the RHS packed?
168192eqP :: (Eq a , Show a , Stringy s ) =>
169193 (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
182211
183212eqPSqrt :: (Eq a , Show a , Stringy s ) =>
184213 (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
186215
187216instance Arbitrary FPFormat where
188217 arbitrary = arbitraryBoundedEnum
189218
190- newtype Precision a = Precision ( Maybe Int )
191- deriving (Eq , Show )
219+ newtype Precision a = Precision { unPrecision :: Maybe Int }
220+ deriving (Eq , Show )
192221
222+ -- Deprecated on 2021-10-05
193223precision :: a -> Precision a -> Maybe Int
194- precision _ (Precision prec) = prec
224+ precision _ = coerce
225+ {-# DEPRECATED precision "Use @coerce@ with types instead." #-}
195226
196227arbitraryPrecision :: 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+ ]
202234
203235instance Arbitrary (Precision Float ) where
204236 arbitrary = arbitraryPrecision 11
205- shrink = map Precision . shrink . precision undefined
237+ shrink = coerce ( shrink @ ( Maybe Int ))
206238
207239instance Arbitrary (Precision Double ) where
208240 arbitrary = arbitraryPrecision 22
209- shrink = map Precision . shrink . precision undefined
241+ shrink = coerce ( shrink @ ( Maybe Int ))
210242
211243instance Arbitrary IO. Newline where
212- arbitrary = oneof [return IO. LF , return IO. CRLF ]
244+ arbitrary = oneof [pure IO. LF , pure IO. CRLF ]
213245
214246instance Arbitrary IO. NewlineMode where
215- arbitrary = IO. NewlineMode <$> arbitrary <*> arbitrary
247+ arbitrary =
248+ liftA2 IO. NewlineMode
249+ arbitrary
250+ arbitrary
216251
217252instance 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+ ]
223260
224261-- This test harness is complex! What property are we checking?
225262--
226263-- Reading after writing a multi-line file should give the same
227264-- results as were written.
228265--
229266-- 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.
235272write_read :: (NFData a , Eq a , Show a )
236273 => ([b ] -> a )
237274 -> ((Char -> Bool ) -> a -> b )
@@ -245,18 +282,26 @@ write_read _ _ _ _ (IO.NewlineMode IO.LF IO.CRLF) _ _ = discard
245282write_read unline filt writer reader nl buf ts = ioProperty $
246283 (=== t) <$> act
247284 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
260305
261306-- Generate various Unicode space characters with high probability
262307arbitrarySpacyChar :: Gen Char
@@ -269,5 +314,5 @@ newtype SpacyString = SpacyString { getSpacyString :: String }
269314 deriving (Eq , Ord , Show , Read )
270315
271316instance 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