44--
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE DeriveFunctor #-}
7+ {-# LANGUAGE TypeApplications #-}
8+ {-# LANGUAGE ScopedTypeVariables #-}
79
810{-# OPTIONS_GHC -fno-warn-orphans #-}
911
@@ -30,8 +32,8 @@ module Tests.QuickCheckUtils
3032
3133import Control.Arrow ((***) )
3234import Control.DeepSeq (NFData (.. ), deepseq )
33- import Control.Exception (bracket )
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 )
@@ -47,6 +49,8 @@ import qualified Data.Text.Internal.Lazy as TL
4749import qualified Data.Text.Internal.Lazy.Fusion as TLF
4850import qualified Data.Text.Lazy as TL
4951import qualified System.IO as IO
52+ import Control.Applicative (liftA2 )
53+ import Data.Bits (shiftR , shiftL , countLeadingZeros , finiteBitSize )
5054
5155genWord8 :: Gen Word8
5256genWord8 = chooseAny
@@ -79,39 +83,63 @@ newtype Sqrt a = Sqrt { unSqrt :: a }
7983 deriving (Eq , Show )
8084
8185instance 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 )
8698
8799instance Arbitrary T. Text where
88- arbitrary = ( T. pack . getUnicodeString) `fmap` arbitrary
100+ arbitrary = T. pack <$> listOf arbitraryUnicodeChar -- without surrogates
89101 shrink = map T. pack . shrink . T. unpack
90102
91103instance Arbitrary TL. Text where
92- arbitrary = ( TL. fromChunks . map notEmpty . unSqrt) `fmap` arbitrary
104+ arbitrary = TL. fromChunks <$> coerce (arbitrary @ ( Sqrt [ NotEmpty T. Text ]))
93105 shrink = map TL. pack . shrink . TL. unpack
94106
95107newtype BigInt = Big Integer
96108 deriving (Eq , Show )
97109
98110instance 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
102121
103122newtype NotEmpty a = NotEmpty { notEmpty :: a }
104123 deriving (Eq , Ord , Show )
105124
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+
106135instance 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
110138
111139instance 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+
115143
116144data DecodeErr = Lenient | Ignore | Strict | Replace
117145 deriving (Show , Eq , Bounded , Enum )
@@ -167,71 +195,84 @@ eq a b s = a s =^= b s
167195-- What about with the RHS packed?
168196eqP :: (Eq a , Show a , Stringy s ) =>
169197 (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
182215
183216eqPSqrt :: (Eq a , Show a , Stringy s ) =>
184217 (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
186219
187220instance Arbitrary FPFormat where
188221 arbitrary = arbitraryBoundedEnum
189222
190- newtype Precision a = Precision ( Maybe Int )
191- deriving (Eq , Show )
223+ newtype Precision a = Precision { unPrecision :: Maybe Int }
224+ deriving (Eq , Show )
192225
226+ -- Deprecated on 2021-10-05
193227precision :: a -> Precision a -> Maybe Int
194- precision _ (Precision prec) = prec
228+ precision _ = coerce
229+ {-# DEPRECATED precision "Use @coerce@ or @unPrecision@ with types instead." #-}
195230
196231arbitraryPrecision :: 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+ ]
202238
203239instance Arbitrary (Precision Float ) where
204240 arbitrary = arbitraryPrecision 11
205- shrink = map Precision . shrink . precision undefined
241+ shrink = coerce ( shrink @ ( Maybe Int ))
206242
207243instance Arbitrary (Precision Double ) where
208244 arbitrary = arbitraryPrecision 22
209- shrink = map Precision . shrink . precision undefined
245+ shrink = coerce ( shrink @ ( Maybe Int ))
210246
211247instance Arbitrary IO. Newline where
212- arbitrary = oneof [return IO. LF , return IO. CRLF ]
248+ arbitrary = oneof [pure IO. LF , pure IO. CRLF ]
213249
214250instance Arbitrary IO. NewlineMode where
215- arbitrary = IO. NewlineMode <$> arbitrary <*> arbitrary
251+ arbitrary =
252+ liftA2 IO. NewlineMode
253+ arbitrary
254+ arbitrary
216255
217256instance 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+ ]
223264
224265-- This test harness is complex! What property are we checking?
225266--
226267-- Reading after writing a multi-line file should give the same
227268-- results as were written.
228269--
229270-- 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.
235276write_read :: (NFData a , Eq a , Show a )
236277 => ([b ] -> a )
237278 -> ((Char -> Bool ) -> a -> b )
@@ -245,18 +286,24 @@ write_read _ _ _ _ (IO.NewlineMode IO.LF IO.CRLF) _ _ = discard
245286write_read unline filt writer reader nl buf ts = ioProperty $
246287 (=== t) <$> act
247288 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
260307
261308-- Generate various Unicode space characters with high probability
262309arbitrarySpacyChar :: Gen Char
@@ -269,5 +316,5 @@ newtype SpacyString = SpacyString { getSpacyString :: String }
269316 deriving (Eq , Ord , Show , Read )
270317
271318instance 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