Skip to content

Commit 1fdf592

Browse files
committed
WIP
1 parent ccaa346 commit 1fdf592

File tree

2 files changed

+113
-66
lines changed

2 files changed

+113
-66
lines changed

tests/Tests/Properties/Builder.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ tb_formatRealFloat :: (RealFloat a, Show a) =>
9090
tb_formatRealFloat a fmt prec = cond ==>
9191
TB.formatRealFloat fmt p a ===
9292
TB.fromString (showFloat fmt p a "")
93-
where p = precision a prec
93+
where p = unPrecision prec
9494
cond = case (p,fmt) of
9595
#if MIN_VERSION_base(4,12,0)
9696
(Just 0, TB.Generic) -> False -- skipping due to gh-231

tests/Tests/QuickCheckUtils.hs

Lines changed: 112 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
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

3133
import Control.Arrow ((***))
3234
import Control.DeepSeq (NFData (..), deepseq)
33-
import Control.Exception (bracket)
3435
import Data.Char (isSpace)
36+
import Data.Coerce (coerce)
3537
import Data.Text.Foreign (I8)
3638
import Data.Text.Lazy.Builder.RealFloat (FPFormat(..))
3739
import Data.Word (Word8, Word16)
@@ -47,6 +49,8 @@ import qualified Data.Text.Internal.Lazy as TL
4749
import qualified Data.Text.Internal.Lazy.Fusion as TLF
4850
import qualified Data.Text.Lazy as TL
4951
import qualified System.IO as IO
52+
import Control.Applicative (liftA2)
53+
import Data.Bits (shiftR, shiftL, countLeadingZeros, finiteBitSize)
5054

5155
genWord8 :: Gen Word8
5256
genWord8 = chooseAny
@@ -79,39 +83,63 @@ newtype Sqrt a = Sqrt { unSqrt :: a }
7983
deriving (Eq, Show)
8084

8185
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)
8698

8799
instance 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

91103
instance 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

95107
newtype BigInt = Big Integer
96108
deriving (Eq, Show)
97109

98110
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
102121

103122
newtype 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+
106135
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
110138

111139
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+
115143

116144
data 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?
168196
eqP :: (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

183216
eqPSqrt :: (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

187220
instance 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
193227
precision :: a -> Precision a -> Maybe Int
194-
precision _ (Precision prec) = prec
228+
precision _ = coerce
229+
{-# DEPRECATED precision "Use @coerce@ or @unPrecision@ with types instead." #-}
195230

196231
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+
]
202238

203239
instance Arbitrary (Precision Float) where
204240
arbitrary = arbitraryPrecision 11
205-
shrink = map Precision . shrink . precision undefined
241+
shrink = coerce (shrink @(Maybe Int))
206242

207243
instance Arbitrary (Precision Double) where
208244
arbitrary = arbitraryPrecision 22
209-
shrink = map Precision . shrink . precision undefined
245+
shrink = coerce (shrink @(Maybe Int))
210246

211247
instance Arbitrary IO.Newline where
212-
arbitrary = oneof [return IO.LF, return IO.CRLF]
248+
arbitrary = oneof [pure IO.LF, pure IO.CRLF]
213249

214250
instance Arbitrary IO.NewlineMode where
215-
arbitrary = IO.NewlineMode <$> arbitrary <*> arbitrary
251+
arbitrary =
252+
liftA2 IO.NewlineMode
253+
arbitrary
254+
arbitrary
216255

217256
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+
]
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.
235276
write_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
245286
write_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
262309
arbitrarySpacyChar :: Gen Char
@@ -269,5 +316,5 @@ newtype SpacyString = SpacyString { getSpacyString :: String }
269316
deriving (Eq, Ord, Show, Read)
270317

271318
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

Comments
 (0)