Skip to content

Commit 3cf8f17

Browse files
committed
Simplify shrinkWithinIntRange and shrinkWithinUTCRange
1 parent a25cec9 commit 3cf8f17

1 file changed

Lines changed: 36 additions & 46 deletions

File tree

src/Data/API/Tools/QuickCheck.hs

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE RecordWildCards #-}
23
{-# LANGUAGE TemplateHaskell #-}
34

45
module Data.API.Tools.QuickCheck
@@ -15,13 +16,13 @@ import Control.Applicative
1516
import Control.Monad
1617
import Data.Monoid
1718
import Data.Time
19+
import Data.Coerce
1820
import Language.Haskell.TH
1921
import Prelude
2022
import Test.QuickCheck as QC
23+
import Language.Haskell.TH.Syntax (lift)
2124

22-
-- | Tool to generate 'Arbitrary' instances for generated types. This tool generates
23-
-- also a stock shrinker via the 'generic-arbitrary' package, which means we require
24-
-- the wrapped type to be an instance of 'Generic'.
25+
-- | Tool to generate 'Arbitrary' instances for generated types.
2526
quickCheckTool :: APITool
2627
quickCheckTool = apiNodeTool $ apiSpecTool gen_sn_ab gen_sr_ab gen_su_ab gen_se_ab mempty
2728

@@ -50,9 +51,9 @@ gen_sn_ab = mkTool $ \ ts (an, sn) -> case snFilter sn of
5051
Nothing | snType sn == BTint -> mk_instance ts an sn [e| QC.arbitraryBoundedIntegral |] (shrinkNewtype ts an sn)
5152
| otherwise -> mk_instance ts an sn [e| arbitrary |] (shrinkNewtype ts an sn)
5253
Just (FtrIntg ir) ->
53-
mk_instance ts an sn [e| arbitraryIntRange ir |] (shrinkIntRange ir ts an sn)
54+
mk_instance ts an sn [e| arbitraryIntRange ir |] (shrinkIntRange ir sn)
5455
Just (FtrUTC ur) ->
55-
mk_instance ts an sn [e| arbitraryUTCRange ur |] (shrinkUTCRange ur ts an sn)
56+
mk_instance ts an sn [e| arbitraryUTCRange ur |] (shrinkUTCRange ur sn)
5657
Just (FtrStrg _) -> return []
5758
where
5859
mk_instance ts an sn arb =
@@ -72,32 +73,29 @@ shrinkNewtype ts an sn = do
7273
[]
7374
]
7475

75-
-- | Attempts to shrink an input 'APINode' within the given 'IntRange', i.e. if the 'IntRange'
76-
-- specifies an 'ir_lo', then we shrink such that the resulting shrunk values still satisfies
77-
-- the min constrain of the range (i.e. we never generate values /smaller/ than 'ir_lo').
78-
--
79-
-- A few observations/remarks:
80-
--
81-
-- * If the 'ir_lo' is 'Nothing', then this because just 'shrinkNewtype', because we don't
82-
-- really care about 'ir_hi' as shrinking by default won't generate value higher than the
83-
-- value being shrunk (it would be a nonsense);
84-
--
85-
-- * We can generate code that typechecks only if we have a 'BTint', otherwise we don't shrink.
86-
shrinkIntRange :: IntRange -> ToolSettings -> APINode -> SpecNewtype -> ExpQ
87-
shrinkIntRange ir ts an sn = case ir_lo ir of
88-
Nothing -> shrinkNewtype ts an sn
89-
Just lowerBound -> do
90-
x <- newName "x"
91-
y <- newName "y"
92-
lamE [varP x] $
93-
caseE (varE x) [
94-
match (nodeNewtypeConP ts an sn [varP y])
95-
(normalB $ do
96-
if snType sn == BTint
97-
then [| map $(nodeNewtypeConE ts an sn) $ filter (>= lowerBound) $ (QC.shrink $(varE y)) |]
98-
else noShrink
99-
) []
100-
]
76+
shrinkWithinIntRange :: IntRange -> Int -> [Int]
77+
shrinkWithinIntRange ir@IntRange{..} x = refine $ QC.shrink x
78+
where
79+
refine = case (ir_lo, ir_hi) of
80+
(Nothing, Nothing) -> id -- avoid filter altogether
81+
_ -> filter (`inIntRange` ir)
82+
83+
shrinkWithinUTCRange :: UTCRange -> UTCTime -> [UTCTime]
84+
shrinkWithinUTCRange ur@UTCRange{..} x = refine $ QC.shrink x
85+
where
86+
refine = case (ur_lo, ur_hi) of
87+
(Nothing, Nothing) -> id -- avoid filter altogether
88+
_ -> filter (`inUTCRange` ur)
89+
90+
-- | Attempts to shrink an input 'APINode' within the given 'IntRange'.
91+
-- We can generate code that typechecks only if we have a 'BTint', otherwise we don't shrink.
92+
shrinkIntRange :: IntRange -> SpecNewtype -> ExpQ
93+
shrinkIntRange ir sn = do
94+
x <- newName "x"
95+
lamE [varP x] $
96+
if snType sn == BTint
97+
then [e| coerce (shrinkWithinIntRange $(lift ir) $ coerce $(varE x)) |]
98+
else noShrink
10199

102100
noShrink :: ExpQ
103101
noShrink = [e| \_ -> [] |]
@@ -106,21 +104,13 @@ noShrink = [e| \_ -> [] |]
106104
-- specifies an 'ur_lo', then we shrink such that the resulting shrunk values still satisfies
107105
-- the min constrain of the range (i.e. we never generate values /smaller/ than 'ur_lo').
108106
-- Same proviso as for 'shrinkIntRange', it makes sense to apply the filter only for 'BTutc'.
109-
shrinkUTCRange :: UTCRange -> ToolSettings -> APINode -> SpecNewtype -> ExpQ
110-
shrinkUTCRange ur ts an sn = case ur_lo ur of
111-
Nothing -> shrinkNewtype ts an sn
112-
Just lowerBound -> do
113-
x <- newName "x"
114-
y <- newName "y"
115-
lamE [varP x] $
116-
caseE (varE x) [
117-
match (nodeNewtypeConP ts an sn [varP y])
118-
(normalB $ do
119-
if snType sn == BTutc
120-
then [| map $(nodeNewtypeConE ts an sn) $ filter (>= $(liftUTC lowerBound)) $ (QC.shrink $(varE y)) |]
121-
else noShrink
122-
) []
123-
]
107+
shrinkUTCRange :: UTCRange -> SpecNewtype -> ExpQ
108+
shrinkUTCRange ur sn = do
109+
x <- newName "x"
110+
lamE [varP x] $
111+
if snType sn == BTutc
112+
then [e| coerce (shrinkWithinUTCRange $(lift ur) $ coerce $(varE x)) |]
113+
else noShrink
124114

125115
-- | Generate an 'Arbitrary' instance for a record:
126116
--

0 commit comments

Comments
 (0)