Skip to content
This repository has been archived by the owner on Oct 18, 2021. It is now read-only.

Commit

Permalink
add Hedgehog tests for the hole-filling machinery
Browse files Browse the repository at this point in the history
proposal: "the hole filling machinery" is too damn long. let's call it
"amuse", short for "amulet synthesiser" instead.
  • Loading branch information
Matheus Magalhães de Alcantara committed Nov 11, 2019
1 parent 741f59c commit 620c539
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ install:

script:
- stack --no-terminal build --fast --test --no-run-tests --ghc-options "-Werror -fmax-pmcheck-iterations=5000000"
- env AMC_LIBRARY_PATH=$PWD/lib/ stack --no-terminal test --fast --ghc-options "-Werror -fmax-pmcheck-iterations=5000000" --test-arguments "--xml junit.xml --display t --hedgehog-tests 10000 --timeout 60"
- env AMC_LIBRARY_PATH=$PWD/lib/ stack --no-terminal test --fast --ghc-options "-Werror -fmax-pmcheck-iterations=5000000" --test-arguments "--xml junit.xml --display t --hedgehog-tests 1000 --timeout 80"
- stack --no-terminal exec --package=hlint -- hlint --git

notifications:
Expand Down
1 change: 1 addition & 0 deletions amuletml.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ test-suite tests
, Test.Syntax.Verify

, Test.Types.Check
, Test.Types.Holes
, Test.Types.Unify
, Test.Types.Util

Expand Down
11 changes: 7 additions & 4 deletions tests/driver/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Test.Tasty.Reporter
import Test.Tasty.Rerun
import Test.Tasty

-- import qualified Test.Types.Unify as Solver
import qualified Test.Types.Unify as Solver
import qualified Test.Types.Holes as Holes
import qualified Test.Core.Lint as Lint
import qualified Test.Parser.Lexer as Lexer
import qualified Test.Parser.Parser as Parser
Expand All @@ -20,10 +21,12 @@ import qualified Test.Frontend.Amc as Amc

tests :: IO TestTree
tests = testGroup "Tests" <$> sequence
-- [ pure Solver.tests Has a tendency to timeout. TODO: Get better CI
-- machines
-- These two will timeout if you run 10000 of them.
-- TODO: Get better CI machines
[ pure Solver.tests
, pure Holes.tests

[ Amc.tests
, Amc.tests
, Lint.tests
, Lexer.tests
, Parser.tests
Expand Down
55 changes: 46 additions & 9 deletions tests/driver/Test/Syntax/Gen.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}
module Test.Syntax.Gen ( genType, genLit ) where
module Test.Syntax.Gen ( genType, genSimpleType, genFormula, genLit ) where

import Syntax.Builtin
import Syntax.Var
Expand Down Expand Up @@ -28,6 +28,12 @@ referToVar = do
scope <- asks namesInScope
Gen.element scope

-- | Generate a type using most of Amulet's type system features. Types
-- generated by 'genType' are most likely nonsense.
genType :: MonadGen m => m (Type Typed)
genType = runReaderT gen_type emptyScope


gen_type :: (MonadGen m, MonadReader InScopeSet m) => m (Type Typed)
gen_type = do
scope <- asks namesInScope
Expand All @@ -36,10 +42,7 @@ gen_type = do
[ Gen.element [ tyUnit, tyBool, tyInt, tyString ]
, Gen.element [ TyPromotedCon nILName, TyPromotedCon cONSName ]
]
++
case scope of
[] -> []
_ -> [TyVar <$> referToVar]
++ [ TyVar <$> referToVar | not (null scope) ]

Gen.recursive Gen.choice
base_case
Expand Down Expand Up @@ -72,8 +75,44 @@ gen_type = do
(,) (alpha !! n) <$> genType
]

genType :: MonadGen m => m (Type Typed)
genType = runReaderT gen_type emptyScope
-- | Generate an inhabited type of kind Type
genSimpleType :: MonadGen m => m (Type Typed)
genSimpleType =
Gen.recursive Gen.choice
[ Gen.element [ tyUnit, tyBool, tyInt, tyString ]
]
[ TyArr <$> genSimpleType <*> genSimpleType
, TyTuple <$> genSimpleType <*> genSimpleType

, TyApp tyLazy <$> genSimpleType
, TyApp tyList <$> genSimpleType
]

-- | Generate a type that looks somewhat like a formula in first-order
-- logic, using implication and conjunction. This always starts with a
-- (random) number of foralls introducting logical variables /and/
-- assumptions of those variables. This is to insure that the types are
-- inhabited by a proof more often than they are not.
genFormula :: MonadGen m => m (Type Typed)
genFormula = do
vars <- Gen.int (Range.linear 1 5)
runReaderT (intros vars gen_formula) emptyScope

gen_formula :: (MonadGen m, MonadReader InScopeSet m) => m (Type Typed)
gen_formula =
Gen.recursive Gen.choice
[ TyVar <$> referToVar ]
[ TyArr <$> gen_formula <*> gen_formula
, TyTuple <$> gen_formula <*> gen_formula
]

intro :: MonadReader InScopeSet m => m (Type Typed) -> m (Type Typed)
intro k = withVar $ \v ->
TyPi (Invisible v Nothing Spec) . TyArr (TyVar v) <$> k

intros :: MonadReader InScopeSet m => Int -> m (Type Typed) -> m (Type Typed)
intros 0 k = k
intros n k = intro (intros (n - 1) k)

genLit :: MonadGen m => m Lit
genLit =
Expand All @@ -95,5 +134,3 @@ emptyScope :: InScopeSet
emptyScope = InScopeSet alpha mempty where
alpha = zipWith toName [1..] ([1..] >>= flip replicateM ['a'..'z'])
toName id n = TgName (T.pack n) id

-- pah orphans
122 changes: 122 additions & 0 deletions tests/driver/Test/Types/Holes.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{-# LANGUAGE TemplateHaskell #-}
module Test.Types.Holes where

import Control.Monad.Namey
import Control.Monad.Infer (firstName)
import Control.Arrow hiding ((<+>))
import Control.Lens hiding (Lazy)

import Test.Syntax.Gen
import Test.Types.Util
import Test.Util

import Syntax.Transform
import Syntax.Builtin
import Syntax.Desugar
import Syntax.Pretty
import Syntax

import Types.Holes

import Data.Foldable
import Data.Span

import Text.Pretty.Semantic

import Unsafe.Coerce

import Hedgehog

prop_amuseMakesTermsOfType :: Property
prop_amuseMakesTermsOfType = property $ testAmuse genSimpleType

prop_amuseProvesFormulas :: Property
prop_amuseProvesFormulas = property $ testAmuse genFormula

testAmuse :: Gen (Type Typed) -> PropertyT IO ()
testAmuse gen = do
aty <- forAllWith (displayS . displayType) gen
let (terms, nm) =
runNamey (findHoleCandidate mempty internal builtinEnv aty) firstName

for_ terms $ \term -> do
let term' = forgetTypes term
([LetStmt _ _ [Binding _ term'' _ _]], _) =
runNamey (desugarProgram
[LetStmt NonRecursive Private [Binding firstName term' False internal]])
nm

case checkExpr term'' aty of
Left e -> do
footnote . displayS $
keyword "Proof term:" <+> pretty term''
footnote . displayS . pretty . toList $ e
failure
Right () -> success

tests :: TestTree
tests = hedgehog $ $$(discover)

forgetTypes :: Expr Typed -> Expr Resolved
forgetTypes (VarRef v (an, _)) = VarRef v an
forgetTypes (Let re bs bd (a, _)) = Let re (forgetBinds <$> bs) (forgetTypes bd) a where
forgetBinds (Binding v e b (a, _)) = Binding v (forgetTypes e) b a
forgetBinds (Matching p e (a, _)) = Matching (forgetPat p) (forgetTypes e) a
forgetBinds (TypedMatching p e (a, _) _) = Matching (forgetPat p) (forgetTypes e) a
forgetTypes (If c t e (a, _)) = If (forgetTypes c) (forgetTypes t) (forgetTypes e) a
forgetTypes (App f x (a, _)) = App (forgetTypes f) (forgetTypes x) a
forgetTypes (Fun p bd (a, _)) = Fun (forgetP p) (forgetTypes bd) a where
forgetP (PatParam p) = PatParam $ forgetPat p
forgetP (EvParam p) = EvParam $ forgetPat p
forgetTypes (Begin es (a, _)) = Begin (forgetTypes <$> es) a
forgetTypes (Literal l (a, _)) = Literal l a
forgetTypes (Match e as (a, _)) = Match (forgetTypes e) (forgetArm <$> as) a where
forgetArm (Arm p g e) = Arm (forgetPat p) (forgetTypes <$> g) (forgetTypes e)
forgetTypes (Function as (a, _)) = Function (forgetArm <$> as) a where
forgetArm (Arm p g e) = Arm (forgetPat p) (forgetTypes <$> g) (forgetTypes e)
forgetTypes (BinOp l o r (a, _)) = BinOp (forgetTypes l) (forgetTypes o) (forgetTypes r) a
forgetTypes (Hole v (a, _)) = Hole v a
forgetTypes (Ascription e t (a, _)) = Ascription (forgetTypes e) (forgetKind t) a
forgetTypes (Record fs (a, _)) = Record (forgetField <$> fs) a
forgetTypes (RecordExt e fs (a, _)) = RecordExt (forgetTypes e) (forgetField <$> fs) a
forgetTypes (Access r t (a, _)) = Access (forgetTypes r) t a

forgetTypes LeftSection{} = undefined
forgetTypes RightSection{} = undefined
forgetTypes BothSection{} = undefined
forgetTypes AccessSection{} = undefined
forgetTypes Parens{} = undefined

forgetTypes (Tuple es (a, _)) = Tuple (forgetTypes <$> es) a
forgetTypes TupleSection{} = undefined
forgetTypes OpenIn{} = undefined

forgetTypes (Lazy e (a, _)) = Lazy (forgetTypes e) a
forgetTypes (Vta e t (a, _)) = Vta (forgetTypes e) (forgetKind t) a
forgetTypes (ListExp es (a, _)) = ListExp (forgetTypes <$> es) a

forgetTypes ListComp{} = undefined
forgetTypes DoExpr{} = undefined
forgetTypes Idiom{} = undefined

forgetTypes (ExprWrapper _ e _) = forgetTypes e

forgetField :: Field Typed -> Field Resolved
forgetField (Field t e (a, _)) = Field t (forgetTypes e) a

forgetPat :: Pattern Typed -> Pattern Resolved
forgetPat (Wildcard (a, _)) = Wildcard a
forgetPat (Capture v (a, _)) = Capture v a
forgetPat (Destructure v p (a, _)) = Destructure v (forgetPat <$> p) a
forgetPat (PAs p v (a, _)) = PAs (forgetPat p) v a
forgetPat (PType p t (a, _)) = PType (forgetPat p) (forgetKind t) a
forgetPat (PRecord rs (a, _)) = PRecord (map (second forgetPat) rs) a
forgetPat (PTuple p (a, _)) = PTuple (forgetPat <$> p) a
forgetPat (PList p (a, _)) = PTuple (forgetPat <$> p) a
forgetPat (PLiteral l (a, _)) = PLiteral l a
forgetPat PGadtCon{} = undefined

forgetKind :: Type Typed -> Type Resolved
forgetKind = unsafeCoerce . transformType go where
go (TySkol v) = TyVar (v ^. skolVar)
go x = x

0 comments on commit 620c539

Please sign in to comment.