Skip to content

Commit a25cec9

Browse files
committed
Introduce shrinkUTCRange and shrinkIntRange
1 parent 3638d0a commit a25cec9

4 files changed

Lines changed: 67 additions & 22 deletions

File tree

src/Data/API/API/Gen.hs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
{-# LANGUAGE DeriveDataTypeable #-}
2-
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
31
{-# LANGUAGE OverloadedStrings #-}
4-
{-# LANGUAGE StandaloneDeriving #-}
5-
{-# LANGUAGE DeriveGeneric #-}
62
{-# LANGUAGE TemplateHaskell #-}
7-
{-# LANGUAGE LambdaCase #-}
8-
{-# LANGUAGE RecordWildCards #-}
9-
{-# OPTIONS_GHC -Wno-orphans #-}
103

114
-- | This module contains datatypes generated from the DSL description
125
-- of the api-tools API; they thus correspond to the types in

src/Data/API/Tools/Combinators.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ data ToolSettings = ToolSettings
4747
-- ^ Rename the constructors of filtered newtypes and generate
4848
-- smart constructors that enforce the invariants
4949
, defaultDerivedClasses :: APINode -> [Name]
50-
-- ^ The classes which are derived automatically by api-tools.
50+
-- ^ The classes which are derived automatically for datatypes created by 'datatypesTool'.
5151
}
5252

5353
-- | Default settings designed to be overridden.

src/Data/API/Tools/QuickCheck.hs

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,77 @@ gen_sn_ab = mkTool $ \ ts (an, sn) -> case snFilter sn of
5050
Nothing | snType sn == BTint -> mk_instance ts an sn [e| QC.arbitraryBoundedIntegral |] (shrinkNewtype ts an sn)
5151
| otherwise -> mk_instance ts an sn [e| arbitrary |] (shrinkNewtype ts an sn)
5252
Just (FtrIntg ir) ->
53-
mk_instance ts an sn [e| arbitraryIntRange ir |] (shrinkNewtype ts an sn)
53+
mk_instance ts an sn [e| arbitraryIntRange ir |] (shrinkIntRange ir ts an sn)
5454
Just (FtrUTC ur) ->
55-
mk_instance ts an sn [e| arbitraryUTCRange ur |] (shrinkNewtype ts an sn)
55+
mk_instance ts an sn [e| arbitraryUTCRange ur |] (shrinkUTCRange ur ts an sn)
5656
Just (FtrStrg _) -> return []
5757
where
5858
mk_instance ts an sn arb =
5959
mkArbitraryInstance ts (nodeRepT an) [e| fmap $(nodeNewtypeConE ts an sn) $arb |]
6060

61-
-- shrinking a newtype means calling shrink and repack the newtype.
62-
-- Example:
63-
-- shrink = \x -> case x of { Foo y -> map Foo (shrink y) }
64-
shrinkNewtype ts an sn = do
65-
x <- newName "x"
66-
y <- newName "y"
67-
lamE [varP x] $
68-
caseE (varE x) [
69-
match (nodeNewtypeConP ts an sn [varP y])
70-
(normalB [| map $(nodeNewtypeConE ts an sn) (QC.shrink $(varE y)) |])
71-
[]
72-
]
61+
-- shrinking a newtype means calling shrink and repack the newtype.
62+
-- Example:
63+
-- shrink = \x -> case x of { Foo y -> map Foo (shrink y) }
64+
shrinkNewtype :: ToolSettings -> APINode -> SpecNewtype -> Q Exp
65+
shrinkNewtype ts an sn = do
66+
x <- newName "x"
67+
y <- newName "y"
68+
lamE [varP x] $
69+
caseE (varE x) [
70+
match (nodeNewtypeConP ts an sn [varP y])
71+
(normalB [| map $(nodeNewtypeConE ts an sn) (QC.shrink $(varE y)) |])
72+
[]
73+
]
74+
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+
]
101+
102+
noShrink :: ExpQ
103+
noShrink = [e| \_ -> [] |]
104+
105+
-- | Attempts to shrink an input 'APINode' within the given 'UTCRange', i.e. if the 'UTCRange'
106+
-- specifies an 'ur_lo', then we shrink such that the resulting shrunk values still satisfies
107+
-- the min constrain of the range (i.e. we never generate values /smaller/ than 'ur_lo').
108+
-- 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+
]
73124

74125
-- | Generate an 'Arbitrary' instance for a record:
75126
--

src/Data/API/Types.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module Data.API.Types
3535
, inIntRange
3636
, inUTCRange
3737
, base64ToBinary
38+
, liftUTC
3839
) where
3940

4041
import Data.API.Time

0 commit comments

Comments
 (0)