Elm-0.11: tests/Tests/Property/Arbitrary.hs
{-# OPTIONS_GHC -W -fno-warn-orphans #-}
module Tests.Property.Arbitrary where
import Control.Applicative ((<$>), (<*>), pure)
import Test.QuickCheck.Arbitrary
import Test.QuickCheck.Gen
import qualified Data.Set as Set
import qualified Parse.Helpers (reserveds)
import SourceSyntax.Literal
import SourceSyntax.Pattern
import SourceSyntax.Type hiding (listOf)
instance Arbitrary Literal where
arbitrary = oneof [ IntNum <$> arbitrary
, FloatNum <$> (arbitrary `suchThat` noE)
, Chr <$> arbitrary
-- This is too permissive
, Str <$> arbitrary
-- Booleans aren't actually source syntax
-- , Boolean <$> arbitrary
]
shrink l = case l of
IntNum n -> IntNum <$> shrink n
FloatNum f -> FloatNum <$> (filter noE . shrink $ f)
Chr c -> Chr <$> shrink c
Str s -> Str <$> shrink s
Boolean b -> Boolean <$> shrink b
noE :: Double -> Bool
noE = notElem 'e' . show
instance Arbitrary Pattern where
arbitrary = sized pat
where pat :: Int -> Gen Pattern
pat n = oneof [ pure PAnything
, PVar <$> lowVar
, PRecord <$> (listOf1 lowVar)
, PLiteral <$> arbitrary
, PAlias <$> lowVar <*> pat (n-1)
, PData <$> capVar <*> sizedPats
]
where sizedPats = do
len <- choose (0,n)
let m = n `div` (len + 1)
vectorOf len $ pat m
shrink pat = case pat of
PAnything -> []
PVar v -> PVar <$> shrinkWHead v
PRecord fs -> PRecord <$> (filter (all $ not . null) . filter (not . null) $ shrink fs)
PLiteral l -> PLiteral <$> shrink l
PAlias s p -> p : (PAlias <$> shrinkWHead s <*> shrink p)
PData s ps -> ps ++ (PData <$> shrinkWHead s <*> shrink ps)
shrinkWHead :: Arbitrary a => [a] -> [[a]]
shrinkWHead [] = error "Should be nonempty"
shrinkWHead (x:xs) = (x:) <$> shrink xs
instance Arbitrary Type where
arbitrary = sized tipe
where tipe :: Int -> Gen Type
tipe n = oneof [ Lambda <$> depthTipe <*> depthTipe
, Var <$> lowVar
, Data <$> capVar <*> depthTipes
, Record <$> fields <*> pure Nothing
, Record <$> fields1 <*> (Just <$> lowVar)
]
where depthTipe = choose (0,n) >>= tipe
depthTipes = do
len <- choose (0,n)
let m = n `div` (len + 1)
vectorOf len $ tipe m
field = (,) <$> lowVar <*> depthTipe
fields = do
len <- choose (0,n)
let m = n `div` (len + 1)
vectorOf len $ (,) <$> lowVar <*> tipe m
fields1 = (:) <$> field <*> fields
shrink tipe = case tipe of
Lambda s t -> s : t : (Lambda <$> shrink s <*> shrink t)
Var _ -> []
Data n ts -> ts ++ (Data <$> shrinkWHead n <*> shrink ts)
Record fs t -> map snd fs ++ case t of
Nothing -> Record <$> shrinkList shrinkField fs <*> pure Nothing
Just _ ->
do
fields <- filter (not . null) $ shrinkList shrinkField fs
return $ Record fields t
where shrinkField (n,t) = (,) <$> shrinkWHead n <*> shrink t
lowVar :: Gen String
lowVar = notReserved $ (:) <$> lower <*> listOf varLetter
where lower = elements ['a'..'z']
capVar :: Gen String
capVar = notReserved $ (:) <$> upper <*> listOf varLetter
where upper = elements ['A'..'Z']
varLetter :: Gen Char
varLetter = elements $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ['\'', '_']
notReserved :: Gen String -> Gen String
notReserved = flip exceptFor Parse.Helpers.reserveds
exceptFor :: (Ord a) => Gen a -> [a] -> Gen a
exceptFor g xs = g `suchThat` notAnX
where notAnX = flip Set.notMember xset
xset = Set.fromList xs