copilot-core 3.5 → 3.6
raw patch · 28 files changed
+1501/−291 lines, 28 filesdep +HUnitdep +QuickCheckdep +copilot-coredep ~basedep ~prettyPVP ok
version bump matches the API change (PVP)
Dependencies added: HUnit, QuickCheck, copilot-core, test-framework, test-framework-hunit, test-framework-quickcheck2
Dependency ranges changed: base, pretty
API changes (from Hackage documentation)
- Copilot.Core.MakeTags: makeTags :: Spec -> Spec
+ Copilot.Core.Operators: [Atan2] :: RealFloat a => Type a -> Op2 a a a
+ Copilot.Core.Operators: [Ceiling] :: RealFrac a => Type a -> Op1 a a
+ Copilot.Core.Operators: [Floor] :: RealFrac a => Type a -> Op1 a a
Files
- CHANGELOG +12/−0
- README.md +2/−2
- copilot-core.cabal +43/−2
- src/Copilot/Core.hs +0/−2
- src/Copilot/Core/Error.hs +6/−7
- src/Copilot/Core/ErrorInternal.hs +26/−0
- src/Copilot/Core/Expr.hs +1/−0
- src/Copilot/Core/Interpret.hs +1/−1
- src/Copilot/Core/Interpret/Eval.hs +33/−23
- src/Copilot/Core/Interpret/Render.hs +2/−117
- src/Copilot/Core/Interpret/RenderInternal.hs +130/−0
- src/Copilot/Core/MakeTags.hs +0/−113
- src/Copilot/Core/Operators.hs +5/−0
- src/Copilot/Core/PrettyDot.hs +24/−21
- src/Copilot/Core/PrettyPrint.hs +5/−1
- src/Copilot/Core/Type/Dynamic.hs +2/−0
- src/Copilot/Core/Type/Eq.hs +1/−1
- src/Copilot/Core/Type/Read.hs +1/−1
- src/Copilot/Core/Type/Uninitialized.hs +1/−0
- tests/Main.hs +31/−0
- tests/Test/Copilot/Core/Error.hs +35/−0
- tests/Test/Copilot/Core/External.hs +43/−0
- tests/Test/Copilot/Core/Interpret/Eval.hs +770/−0
- tests/Test/Copilot/Core/Type.hs +69/−0
- tests/Test/Copilot/Core/Type/Array.hs +132/−0
- tests/Test/Copilot/Core/Type/Dynamic.hs +48/−0
- tests/Test/Copilot/Core/Type/Show.hs +34/−0
- tests/Test/Extra.hs +44/−0
CHANGELOG view
@@ -1,3 +1,15 @@+2021-11-07+ * Version bump (3.6). (#264)+ * Deprecate Copilot.Core.Type.Dynamic.toDynF and fromDynF. (#269)+ * Deprecate copilot-core:Copilot.Core.Type.Uninitialized. (#270)+ * Deprecate export of copilot-core:Copilot.Core.Interpret.Render. (#268)+ * Replace uses of copilot-core's error reporting functions. (#267)+ * Introduce new ops atan2, ceiling, floor. (#246)+ * Add initial support for unit testing. (#256)+ * Deprecate unused type. (#260)+ * Remove deprecated module. (#250)+ * Fix outdated/broken links. (#252)+ 2021-08-19 * Version bump (3.5). (#247) * Update travis domain in README. (#222)
README.md view
@@ -1,4 +1,4 @@-[](https://travis-ci.com/Copilot-Language/copilot-core)+[](https://app.travis-ci.com/github/Copilot-Language/copilot) # Copilot: a stream DSL The core language, which efficiently represents Copilot expressions. The core@@ -30,4 +30,4 @@ ## License Copilot is distributed under the BSD-3-Clause license, which can be found-[here](https://raw.githubusercontent.com/Copilot-Language/copilot-core/master/LICENSE).+[here](https://raw.githubusercontent.com/Copilot-Language/copilot/master/copilot-core/LICENSE).
copilot-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core-version: 3.5+version: 3.6 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot.@@ -55,7 +55,6 @@ Copilot.Core.Interpret Copilot.Core.Interpret.Eval Copilot.Core.Interpret.Render- Copilot.Core.MakeTags Copilot.Core.Operators Copilot.Core.Spec Copilot.Core.Locals@@ -69,3 +68,45 @@ Copilot.Core.Type.Uninitialized Copilot.Core.PrettyPrint Copilot.Core.PrettyDot++ other-modules:++ Copilot.Core.ErrorInternal+ Copilot.Core.Interpret.RenderInternal++test-suite unit-tests+ type:+ exitcode-stdio-1.0++ main-is:+ Main.hs++ other-modules:+ Test.Extra+ Test.Copilot.Core.Error+ Test.Copilot.Core.External+ Test.Copilot.Core.Interpret.Eval+ Test.Copilot.Core.Type+ Test.Copilot.Core.Type.Array+ Test.Copilot.Core.Type.Dynamic+ Test.Copilot.Core.Type.Show++ build-depends:+ base+ , HUnit+ , QuickCheck+ , pretty+ , test-framework+ , test-framework-hunit+ , test-framework-quickcheck2++ , copilot-core++ hs-source-dirs:+ tests++ default-language:+ Haskell2010++ ghc-options:+ -Wall
src/Copilot/Core.hs view
@@ -26,7 +26,6 @@ ( module Copilot.Core.Error , module Copilot.Core.Expr , module Copilot.Core.External- , module Copilot.Core.MakeTags , module Copilot.Core.Operators , module Copilot.Core.Spec , module Copilot.Core.Type@@ -39,7 +38,6 @@ import Copilot.Core.Error import Copilot.Core.Expr import Copilot.Core.External-import Copilot.Core.MakeTags import Copilot.Core.Operators import Copilot.Core.Spec import Copilot.Core.Type
src/Copilot/Core/Error.hs view
@@ -9,18 +9,17 @@ ( impossible , badUsage ) where +import qualified Copilot.Core.ErrorInternal as Err+ -- | Report an error due to a bug in Copilot.+{-# DEPRECATED impossible "This function is deprecated in Copilot 3.6." #-} impossible :: String -- ^ Name of the function in which the error was detected. -> String -- ^ Name of the package in which the function is located. -> a-impossible function package =- error $ "\"Impossible\" error in function "- ++ function ++ ", in package " ++ package- ++ ". Please file an issue at "- ++ "https://github.com/Copilot-Language/copilot/issues"- ++ "or email the maintainers at <ivan.perezdominguez@nasa.gov>"+impossible = Err.impossible -- | Report an error due to an error detected by Copilot (e.g., user error).+{-# DEPRECATED badUsage "This function is deprecated in Copilot 3.6." #-} badUsage :: String -- ^ Description of the error. -> a-badUsage msg = error $ "Copilot error: " ++ msg+badUsage = Err.badUsage
+ src/Copilot/Core/ErrorInternal.hs view
@@ -0,0 +1,26 @@+--------------------------------------------------------------------------------+-- Copyright © 2011 National Institute of Aerospace / Galois, Inc.+--------------------------------------------------------------------------------++{-# LANGUAGE Safe #-}++-- | Custom functions to report error messages to users.+module Copilot.Core.ErrorInternal+ ( impossible+ , badUsage ) where++-- | Report an error due to a bug in Copilot.+impossible :: String -- ^ Name of the function in which the error was detected.+ -> String -- ^ Name of the package in which the function is located.+ -> a+impossible function package =+ error $ "\"Impossible\" error in function "+ ++ function ++ ", in package " ++ package+ ++ ". Please file an issue at "+ ++ "https://github.com/Copilot-Language/copilot/issues"+ ++ "or email the maintainers at <ivan.perezdominguez@nasa.gov>"++-- | Report an error due to an error detected by Copilot (e.g., user error).+badUsage :: String -- ^ Description of the error.+ -> a+badUsage msg = error $ "Copilot error: " ++ msg
src/Copilot/Core/Expr.hs view
@@ -39,6 +39,7 @@ -------------------------------------------------------------------------------- -- | A unique tag for external arrays/function calls.+{-# DEPRECATED Tag "The type Tag is deprecated in Copilot 3.6." #-} type Tag = Int --------------------------------------------------------------------------------
src/Copilot/Core/Interpret.hs view
@@ -13,7 +13,7 @@ import Copilot.Core import Copilot.Core.Interpret.Eval-import Copilot.Core.Interpret.Render+import Copilot.Core.Interpret.RenderInternal import Copilot.Core.Type.Show (ShowType(..)) -- | Output format for the results of a Copilot spec interpretation.
src/Copilot/Core/Interpret/Eval.hs view
@@ -14,7 +14,8 @@ , eval ) where -import Copilot.Core+import Copilot.Core hiding (badUsage)+import Copilot.Core.ErrorInternal (badUsage) import Copilot.Core.Type.Show (showWithType, ShowType) import Prelude hiding (id)@@ -196,28 +197,36 @@ -- 'Copilot.Core.Operators.Op1'. evalOp1 :: Op1 a b -> (a -> b) evalOp1 op = case op of- Not -> P.not- Abs _ -> P.abs- Sign _ -> P.signum- Recip _ -> P.recip- Exp _ -> P.exp- Sqrt _ -> P.sqrt- Log _ -> P.log- Sin _ -> P.sin- Tan _ -> P.tan- Cos _ -> P.cos- Asin _ -> P.asin- Atan _ -> P.atan- Acos _ -> P.acos- Sinh _ -> P.sinh- Tanh _ -> P.tanh- Cosh _ -> P.cosh- Asinh _ -> P.asinh- Atanh _ -> P.atanh- Acosh _ -> P.acosh- BwNot _ -> complement- Cast _ _ -> P.fromIntegral- GetField (Struct _) _ f -> unfield . f where+ Not -> P.not+ Abs _ -> P.abs+ Sign _ -> P.signum+ Recip _ -> P.recip+ Exp _ -> P.exp+ Sqrt _ -> P.sqrt+ Log _ -> P.log+ Sin _ -> P.sin+ Tan _ -> P.tan+ Cos _ -> P.cos+ Asin _ -> P.asin+ Atan _ -> P.atan+ Acos _ -> P.acos+ Sinh _ -> P.sinh+ Tanh _ -> P.tanh+ Cosh _ -> P.cosh+ Asinh _ -> P.asinh+ Atanh _ -> P.atanh+ Acosh _ -> P.acosh+ Ceiling _ -> P.fromIntegral . idI . P.ceiling+ Floor _ -> P.fromIntegral . idI . P.floor+ BwNot _ -> complement+ Cast _ _ -> P.fromIntegral+ GetField (Struct _) _ f -> unfield . f+ where+ -- Used to help GHC pick a return type for ceiling/floor+ idI :: Integer -> Integer+ idI = P.id++ -- Extract value from field unfield (Field v) = v --------------------------------------------------------------------------------@@ -237,6 +246,7 @@ Fdiv _ -> (P./) Pow _ -> (P.**) Logb _ -> P.logBase+ Atan2 _ -> P.atan2 Eq _ -> (==) Ne _ -> (/=) Le _ -> (<=)
src/Copilot/Core/Interpret/Render.hs view
@@ -7,124 +7,9 @@ {-# LANGUAGE Safe #-} module Copilot.Core.Interpret.Render+ {-# DEPRECATED "This module is deprecated in Copilot 3.6." #-} ( renderAsTable , renderAsCSV ) where -import Data.List (intersperse, transpose, foldl')-import Data.Maybe (catMaybes)-import Copilot.Core.Interpret.Eval (Output, ExecTrace (..))-import Text.PrettyPrint--import Prelude hiding ((<>))-------------------------------------------------------------------------------------- | Render an execution trace as a table, formatted to faciliate readability.-renderAsTable :: ExecTrace -> String-renderAsTable- ExecTrace- { interpTriggers = trigs- , interpObservers = obsvs } = ( render- . asColumns- . transpose- . (:) (ppTriggerNames ++ ppObserverNames)- . transpose- ) (ppTriggerOutputs ++ ppObserverOutputs)- where-- ppTriggerNames :: [Doc]- ppTriggerNames = map (text . (++ ":")) (map fst trigs)-- ppObserverNames :: [Doc]- ppObserverNames = map (text . (++ ":")) (map fst obsvs)-- ppTriggerOutputs :: [[Doc]]- ppTriggerOutputs = map (map ppTriggerOutput) (map snd trigs)-- ppTriggerOutput :: Maybe [Output] -> Doc- ppTriggerOutput (Just vs) = text $ "(" ++ concat (intersperse "," vs) ++ ")"- ppTriggerOutput Nothing = text "--"-- ppObserverOutputs :: [[Doc]]- ppObserverOutputs = map (map text) (map snd obsvs)-------------------------------------------------------------------------------------- | Render an execution trace as using comma-separate value (CSV) format.-renderAsCSV :: ExecTrace -> String-renderAsCSV = render . unfold---- | Pretty print all the steps of the execution trace and concatenate the--- results.-unfold :: ExecTrace -> Doc-unfold r =- case step r of- (cs, Nothing) -> cs- (cs, Just r') -> cs $$ unfold r'---- | Pretty print the state of the triggers, and provide a continuation--- for the execution trace at the next point in time.-step :: ExecTrace -> (Doc, Maybe ExecTrace)-step ExecTrace- { interpTriggers = trigs- } =- if null trigs then (empty, Nothing)- else (foldl' ($$) empty (text "#" : ppTriggerOutputs), tails)-- where-- ppTriggerOutputs :: [Doc]- ppTriggerOutputs =- catMaybes- . fmap ppTriggerOutput- . map (fmap head)- $ trigs-- ppTriggerOutput :: (String, Maybe [Output]) -> Maybe Doc- ppTriggerOutput (_, Nothing) = Nothing- ppTriggerOutput (cs, Just xs) = Just $- text cs <> text "," <>- (foldr (<>) empty . map text . intersperse ",") xs-- tails :: Maybe ExecTrace- tails =- if any null (fmap (tail.snd) trigs)- then Nothing- else Just- ExecTrace- { interpTriggers = map (fmap tail) trigs- , interpObservers = []- }---------------------------------------------------------------------------------------- Copied from pretty-ncols because of incompatibility with newer GHC versions.-asColumns :: [[Doc]] -> Doc-asColumns = flip asColumnsWithBuff $ 1--asColumnsWithBuff :: [[Doc]] -> Int -> Doc-asColumnsWithBuff lls q = normalize- where normalize = vcat $ map hsep- $ map (\x -> pad (length x) longColumnLen empty x)- $ pad' longEntryLen q- $ transpose lls -- normalize column height- longColumnLen = maximum (map length lls)- longEntryLen = maximum $ map docLen (concat lls)--docLen d = length $ render d---- | Pad a string on the right to reach an expected length.-pad :: Int -> Int -> a -> [a] -> [a]-pad lx max b ls = ls ++ replicate (max - lx) b---- | Pad a list of strings on the right with spaces.-pad' :: Int -- ^ Mininum number of spaces to add- -> Int -- ^ Maximum number of spaces to add- -> [[Doc]] -- ^ List of documents to pad- -> [[Doc]]-pad' _ _ [] = []-pad' mx q (ls:xs) = map buf ls : pad' mx q xs- where buf x = x <> (hcat $ replicate q space) <> (hcat $ replicate (mx - (docLen x)) space)+import Copilot.Core.Interpret.RenderInternal
+ src/Copilot/Core/Interpret/RenderInternal.hs view
@@ -0,0 +1,130 @@+--------------------------------------------------------------------------------+-- Copyright © 2011 National Institute of Aerospace / Galois, Inc.+--------------------------------------------------------------------------------++-- | Pretty-print the results of a simulation.++{-# LANGUAGE Safe #-}++module Copilot.Core.Interpret.RenderInternal+ ( renderAsTable+ , renderAsCSV+ ) where++import Data.List (intersperse, transpose, foldl')+import Data.Maybe (catMaybes)+import Copilot.Core.Interpret.Eval (Output, ExecTrace (..))+import Text.PrettyPrint++import Prelude hiding ((<>))++--------------------------------------------------------------------------------++-- | Render an execution trace as a table, formatted to faciliate readability.+renderAsTable :: ExecTrace -> String+renderAsTable+ ExecTrace+ { interpTriggers = trigs+ , interpObservers = obsvs } = ( render+ . asColumns+ . transpose+ . (:) (ppTriggerNames ++ ppObserverNames)+ . transpose+ ) (ppTriggerOutputs ++ ppObserverOutputs)+ where++ ppTriggerNames :: [Doc]+ ppTriggerNames = map (text . (++ ":")) (map fst trigs)++ ppObserverNames :: [Doc]+ ppObserverNames = map (text . (++ ":")) (map fst obsvs)++ ppTriggerOutputs :: [[Doc]]+ ppTriggerOutputs = map (map ppTriggerOutput) (map snd trigs)++ ppTriggerOutput :: Maybe [Output] -> Doc+ ppTriggerOutput (Just vs) = text $ "(" ++ concat (intersperse "," vs) ++ ")"+ ppTriggerOutput Nothing = text "--"++ ppObserverOutputs :: [[Doc]]+ ppObserverOutputs = map (map text) (map snd obsvs)++--------------------------------------------------------------------------------++-- | Render an execution trace as using comma-separate value (CSV) format.+renderAsCSV :: ExecTrace -> String+renderAsCSV = render . unfold++-- | Pretty print all the steps of the execution trace and concatenate the+-- results.+unfold :: ExecTrace -> Doc+unfold r =+ case step r of+ (cs, Nothing) -> cs+ (cs, Just r') -> cs $$ unfold r'++-- | Pretty print the state of the triggers, and provide a continuation+-- for the execution trace at the next point in time.+step :: ExecTrace -> (Doc, Maybe ExecTrace)+step ExecTrace+ { interpTriggers = trigs+ } =+ if null trigs then (empty, Nothing)+ else (foldl' ($$) empty (text "#" : ppTriggerOutputs), tails)++ where++ ppTriggerOutputs :: [Doc]+ ppTriggerOutputs =+ catMaybes+ . fmap ppTriggerOutput+ . map (fmap head)+ $ trigs++ ppTriggerOutput :: (String, Maybe [Output]) -> Maybe Doc+ ppTriggerOutput (_, Nothing) = Nothing+ ppTriggerOutput (cs, Just xs) = Just $+ text cs <> text "," <>+ (foldr (<>) empty . map text . intersperse ",") xs++ tails :: Maybe ExecTrace+ tails =+ if any null (fmap (tail.snd) trigs)+ then Nothing+ else Just+ ExecTrace+ { interpTriggers = map (fmap tail) trigs+ , interpObservers = []+ }++--------------------------------------------------------------------------------++++-- Copied from pretty-ncols because of incompatibility with newer GHC versions.+asColumns :: [[Doc]] -> Doc+asColumns = flip asColumnsWithBuff $ 1++asColumnsWithBuff :: [[Doc]] -> Int -> Doc+asColumnsWithBuff lls q = normalize+ where normalize = vcat $ map hsep+ $ map (\x -> pad (length x) longColumnLen empty x)+ $ pad' longEntryLen q+ $ transpose lls -- normalize column height+ longColumnLen = maximum (map length lls)+ longEntryLen = maximum $ map docLen (concat lls)++docLen d = length $ render d++-- | Pad a string on the right to reach an expected length.+pad :: Int -> Int -> a -> [a] -> [a]+pad lx max b ls = ls ++ replicate (max - lx) b++-- | Pad a list of strings on the right with spaces.+pad' :: Int -- ^ Mininum number of spaces to add+ -> Int -- ^ Maximum number of spaces to add+ -> [[Doc]] -- ^ List of documents to pad+ -> [[Doc]]+pad' _ _ [] = []+pad' mx q (ls:xs) = map buf ls : pad' mx q xs+ where buf x = x <> (hcat $ replicate q space) <> (hcat $ replicate (mx - (docLen x)) space)
− src/Copilot/Core/MakeTags.hs
@@ -1,113 +0,0 @@------------------------------------------------------------------------------------ Copyright © 2011 National Institute of Aerospace / Galois, Inc.------------------------------------------------------------------------------------- | Sets a unique tags for each external array\/function\/struct call.--{-# LANGUAGE Safe #-}--module Copilot.Core.MakeTags- {-# DEPRECATED "This module is deprecated in Copilot 3.3." #-}- (makeTags)- where--import Copilot.Core.Expr-import Copilot.Core.Spec-import Control.Monad.State-import Prelude hiding (id)--next :: State Int Int-next = do- k <- get- put (succ k)- return k--makeTags :: Spec -> Spec-makeTags spec = evalState (mkTagsSpec spec) 0--mkTagsSpec :: Spec -> State Int Spec-mkTagsSpec- Spec- { specStreams = strms- , specObservers = obsvs- , specTriggers = trigs- , specProperties = props- } =- liftM4 Spec- (mkTagsStrms strms)- (mkTagsObsvs obsvs)- (mkTagsTrigs trigs)- (mkTagsProps props)--mkTagsStrms :: [Stream] -> State Int [Stream]-mkTagsStrms = mapM mkTagsStrm-- where- mkTagsStrm Stream- { streamId = id- , streamBuffer = xs- , streamExpr = e- , streamExprType = t } =- do- e' <- mkTagsExpr e- return $ Stream- { streamId = id- , streamBuffer = xs- , streamExpr = e'- , streamExprType = t }--mkTagsObsvs :: [Observer] -> State Int [Observer]-mkTagsObsvs = mapM mkTagsObsv-- where- mkTagsObsv Observer- { observerName = name- , observerExpr = e- , observerExprType = t } =- do- e' <- mkTagsExpr e- return $ Observer- { observerName = name- , observerExpr = e'- , observerExprType = t }--mkTagsTrigs :: [Trigger] -> State Int [Trigger]-mkTagsTrigs = mapM mkTagsTrig-- where- mkTagsTrig Trigger- { triggerName = name- , triggerGuard = g- , triggerArgs = args } =- do- g' <- mkTagsExpr g- args' <- mapM mkTagsUExpr args- return $ Trigger- { triggerName = name- , triggerGuard = g'- , triggerArgs = args' }--mkTagsProps :: [Property] -> State Int [Property]-mkTagsProps = mapM mkTagsProp-- where mkTagsProp p = do- e' <- mkTagsExpr (propertyExpr p)- return $ p { propertyExpr = e' }--mkTagsUExpr :: UExpr -> State Int UExpr-mkTagsUExpr UExpr { uExprExpr = e, uExprType = t } =- do- e' <- mkTagsExpr e- return $ UExpr { uExprExpr = e', uExprType = t }--mkTagsExpr :: Expr a -> State Int (Expr a)-mkTagsExpr e0 = case e0 of- Const t x -> return $ Const t x- Drop t k id -> return $ Drop t k id- Local t1 t2 name e1 e2 -> liftM2 (Local t1 t2 name) (mkTagsExpr e1) (mkTagsExpr e2)- Var t name -> return $ Var t name- ExternVar t name e -> return $ ExternVar t name e- Op1 op e -> liftM (Op1 op) (mkTagsExpr e)- Op2 op e1 e2 -> liftM2 (Op2 op) (mkTagsExpr e1) (mkTagsExpr e2)- Op3 op e1 e2 e3 -> liftM3 (Op3 op) (mkTagsExpr e1) (mkTagsExpr e2) (mkTagsExpr e3)- Label t s e -> liftM (Label t s) (mkTagsExpr e)
src/Copilot/Core/Operators.hs view
@@ -46,6 +46,9 @@ Asinh :: Floating a => Type a -> Op1 a a Atanh :: Floating a => Type a -> Op1 a a Acosh :: Floating a => Type a -> Op1 a a+ -- RealFrac operators+ Ceiling :: RealFrac a => Type a -> Op1 a a+ Floor :: RealFrac a => Type a -> Op1 a a -- Bitwise operators. BwNot :: Bits a => Type a -> Op1 a a -- Casting operator.@@ -73,6 +76,8 @@ -- Floating operators. Pow :: Floating a => Type a -> Op2 a a a Logb :: Floating a => Type a -> Op2 a a a+ -- RealFloat operators.+ Atan2 :: RealFloat a => Type a -> Op2 a a a -- Equality operators. Eq :: Eq a => Type a -> Op2 a a Bool Ne :: Eq a => Type a -> Op2 a a Bool
src/Copilot/Core/PrettyDot.hs view
@@ -96,27 +96,29 @@ -- | Pretty print a unary operator. ppOp1 :: Op1 a b -> String ppOp1 op = case op of- Not -> "not"- Abs _ -> "abs"- Sign _ -> "signum"- Recip _ -> "recip"- Exp _ -> "exp"- Sqrt _ -> "sqrt"- Log _ -> "log"- Sin _ -> "sin"- Tan _ -> "tan"- Cos _ -> "cos"- Asin _ -> "asin"- Atan _ -> "atan"- Acos _ -> "acos"- Sinh _ -> "sinh"- Tanh _ -> "tanh"- Cosh _ -> "cosh"- Asinh _ -> "asinh"- Atanh _ -> "atanh"- Acosh _ -> "acosh"- BwNot _ -> "~"- Cast _ _ -> "(cast)"+ Not -> "not"+ Abs _ -> "abs"+ Sign _ -> "signum"+ Recip _ -> "recip"+ Exp _ -> "exp"+ Sqrt _ -> "sqrt"+ Log _ -> "log"+ Sin _ -> "sin"+ Tan _ -> "tan"+ Cos _ -> "cos"+ Asin _ -> "asin"+ Atan _ -> "atan"+ Acos _ -> "acos"+ Sinh _ -> "sinh"+ Tanh _ -> "tanh"+ Cosh _ -> "cosh"+ Asinh _ -> "asinh"+ Atanh _ -> "atanh"+ Acosh _ -> "acosh"+ Ceiling _ -> "ceiling"+ Floor _ -> "floor"+ BwNot _ -> "~"+ Cast _ _ -> "(cast)" -- | Pretty print a binary operator. ppOp2 :: Op2 a b c -> String@@ -131,6 +133,7 @@ Fdiv _ -> "/" Pow _ -> "**" Logb _ -> "logBase"+ Atan2 _ -> "atan2" Eq _ -> "==" Ne _ -> "/=" Le _ -> "<="
src/Copilot/Core/PrettyPrint.hs view
@@ -12,7 +12,8 @@ , ppExpr ) where -import Copilot.Core+import Copilot.Core hiding (impossible)+import Copilot.Core.ErrorInternal (impossible) import Copilot.Core.Type.Show (showWithType, ShowType(..), showType) import Prelude hiding (id, (<>)) import Text.PrettyPrint.HughesPJ@@ -72,6 +73,8 @@ Asinh _ -> ppPrefix "asinh" Atanh _ -> ppPrefix "atanh" Acosh _ -> ppPrefix "acosh"+ Ceiling _ -> ppPrefix "ceiling"+ Floor _ -> ppPrefix "floor" BwNot _ -> ppPrefix "~" Cast _ _ -> ppPrefix "(cast)" GetField (Struct _) _ f -> \e -> ppInfix "#" e (text $ accessorname f)@@ -90,6 +93,7 @@ Fdiv _ -> ppInfix "/" Pow _ -> ppInfix "**" Logb _ -> ppInfix "logBase"+ Atan2 _ -> ppInfix "atan2" Eq _ -> ppInfix "==" Ne _ -> ppInfix "/=" Le _ -> ppInfix "<="
src/Copilot/Core/Type/Dynamic.hs view
@@ -49,11 +49,13 @@ Nothing -> Nothing -- | Enclose a function and its type in a container.+{-# DEPRECATED toDynF "This function is deprecated in Copilot 3.6." #-} toDynF :: t a -> f a -> DynamicF f t toDynF t fx = DynamicF fx t -- | Extract a value from a dynamic function container. Return 'Nothing' if -- the value is not of the given type.+{-# DEPRECATED fromDynF "This function is deprecated in Copilot 3.6." #-} fromDynF :: EqualType t => t a -> DynamicF f t -> Maybe (f a) fromDynF t1 (DynamicF fx t2) = case t1 =~= t2 of
src/Copilot/Core/Type/Eq.hs view
@@ -12,7 +12,7 @@ , UVal (..) ) where -import Copilot.Core.Error (impossible)+import Copilot.Core.ErrorInternal (impossible) import Copilot.Core.Type import Copilot.Core.Type.Dynamic (fromDyn, toDyn)
src/Copilot/Core/Type/Read.hs view
@@ -13,7 +13,7 @@ ) where import Copilot.Core.Type-import Copilot.Core.Error (badUsage)+import Copilot.Core.ErrorInternal (badUsage) --------------------------------------------------------------------------------
src/Copilot/Core/Type/Uninitialized.hs view
@@ -18,6 +18,7 @@ -- | Initial value for a given type. -- -- Does not support structs or arrays.+{-# DEPRECATED uninitialized "This function is deprecated in Copilot 3.6" #-} uninitialized :: Type a -> a uninitialized t = case t of
+ tests/Main.hs view
@@ -0,0 +1,31 @@+-- | Test copilot-core.+module Main where++-- External imports+import Data.Monoid (mempty)+import Test.Framework (Test, defaultMainWithOpts)++-- Internal library modules being tested+import qualified Test.Copilot.Core.Error+import qualified Test.Copilot.Core.External+import qualified Test.Copilot.Core.Interpret.Eval+import qualified Test.Copilot.Core.Type+import qualified Test.Copilot.Core.Type.Array+import qualified Test.Copilot.Core.Type.Dynamic+import qualified Test.Copilot.Core.Type.Show++-- | Run all unit tests on copilot-core.+main :: IO ()+main = defaultMainWithOpts tests mempty++-- | All unit tests in copilot-core.+tests :: [Test.Framework.Test]+tests =+ [ Test.Copilot.Core.Error.tests+ , Test.Copilot.Core.External.tests+ , Test.Copilot.Core.Interpret.Eval.tests+ , Test.Copilot.Core.Type.tests+ , Test.Copilot.Core.Type.Array.tests+ , Test.Copilot.Core.Type.Dynamic.tests+ , Test.Copilot.Core.Type.Show.tests+ ]
+ tests/Test/Copilot/Core/Error.hs view
@@ -0,0 +1,35 @@+-- | Test copilot-core:Copilot.Core.Error.+module Test.Copilot.Core.Error where++-- External imports+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit (assertBool)++-- Internal imports: auxiliary testing facilities+import Test.Extra (withoutException)++-- Internal imports: library modules being tested+import Copilot.Core.Error (badUsage, impossible)++-- | All unit tests for copilot-core:Copilot.Core.Error.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.Error"+ [ testCase "impossible" testErrorImpossible+ , testCase "badUsage" testErrorBadUsage+ ]++-- * Individual tests++-- | Test that 'Error.impossible' always throws an exception of some kind.+testErrorImpossible :: IO ()+testErrorImpossible = do+ res <- not <$> withoutException (impossible "func1" "pkg1")+ assertBool "The function impossible should fail with an exception" res++-- | Test that 'Error.badUsage' always throws an exception of some kind.+testErrorBadUsage :: IO ()+testErrorBadUsage = do+ res <- not <$> withoutException (badUsage "func1")+ assertBool "The function badUsage should fail with an exception" res
+ tests/Test/Copilot/Core/External.hs view
@@ -0,0 +1,43 @@+-- | Test copilot-core:Copilot.Core.External.+module Test.Copilot.Core.External where++-- External imports+import Data.List (sort)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit (assertBool)++-- Internal imports: library modules being tested+import Copilot.Core.Expr (Expr (ExternVar, Op1))+import Copilot.Core.External (externVarName, externVars)+import Copilot.Core.Operators (Op1 (Abs, Sign))+import Copilot.Core.Spec (Spec (..), Stream (..))+import Copilot.Core.Type (Type (Int64))++-- | All unit tests for copilot-core:Copilot.Core.External.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.External"+ [ testCase "externVars" testExternVarsFind+ ]++-- * Individual tests++-- | Test that 'externVars' will find an extern in a spec.+testExternVarsFind :: IO ()+testExternVarsFind = do+ -- A simple stream and a nested stream+ let s1 = Stream 1 [] (ExternVar Int64 "z" Nothing) Int64+ s2 = Stream 2 [] s2Expr Int64+ s2Expr = Op1 (Abs Int64)+ $ Op1 (Sign Int64)+ $ ExternVar Int64 "y" Nothing++ -- Calculate the expected external vars in a spec+ let res = sort $ map externVarName $ externVars $ Spec [s1, s2] [] [] []++ -- Compare result with the expectation+ let success = [ "y", "z" ] == res+ assertBool+ "The function externVars could not find the expected externs"+ success
+ tests/Test/Copilot/Core/Interpret/Eval.hs view
@@ -0,0 +1,770 @@+{-# LANGUAGE ExistentialQuantification #-}+-- | Test copilot-core:Copilot.Core.Interpret.Eval.+--+-- The gist of this evaluation is in 'SemanticsP' and 'checkSemanticsP' which+-- evaluates an expression using Copilot's evaluator and compares it against+-- its expected meaning.+module Test.Copilot.Core.Interpret.Eval where++-- External imports+import Data.Bits (Bits, complement, shiftL, shiftR,+ xor, (.&.), (.|.))+import Data.Int (Int16, Int32, Int64, Int8)+import Data.List (lookup)+import Data.Maybe (fromMaybe)+import Data.Typeable (Typeable)+import Data.Word (Word16, Word32, Word64, Word8)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Arbitrary, Gen, Property,+ arbitrary, chooseInt, elements,+ forAll, forAllShow, frequency,+ getPositive, oneof, suchThat,+ vectorOf)+import Text.PrettyPrint.HughesPJ (render)++-- Internal imports: library modules being tested+import Copilot.Core.Expr (Expr (Const, Drop, Op1, Op2, Op3),+ UExpr (UExpr))+import Copilot.Core.Interpret.Eval (ExecTrace (interpObservers), eval)+import Copilot.Core.Operators (Op1 (..), Op2 (..), Op3 (..))+import Copilot.Core.PrettyPrint (ppExpr)+import Copilot.Core.Spec (Observer (..), Spec (..), Stream (Stream))+import Copilot.Core.Type (Typed (typeOf))+import Copilot.Core.Type.Show (ShowType (Haskell), showType)++-- Internal imports: auxiliary functions+import Test.Extra (apply1, apply2, apply3)++-- * Constants++-- | Max length of the traces being tested.+maxTraceLength :: Int+maxTraceLength = 200++-- | All unit tests for copilot-core:Copilot.Core.Interpret.Eval.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.Interpret.Eval"+ [ testProperty "eval Expr" testEvalExpr+ , testProperty "eval Expr with Drop" testEvalExprWithDrop+ ]++-- * Individual tests++-- | Test for expression evaluation.+testEvalExpr :: Property+testEvalExpr =+ forAll (chooseInt (0, maxTraceLength)) $ \steps ->+ forAllShow arbitrarySemanticsP (semanticsShowK steps) $ \pair ->+ checkSemanticsP steps [] pair++-- | Test for expression evaluation with a drop.+testEvalExprWithDrop :: Property+testEvalExprWithDrop =+ forAll (chooseInt (0, maxTraceLength)) $ \steps ->+ forAllShow arbitrarySemanticsP (semanticsShowK steps) $ \pair ->+ forAllShow (arbitraryDrop pair) (semanticsShowK steps . snd) $ \(str, sem) ->+ checkSemanticsP steps [str] sem++-- * Random generators++-- ** Random SemanticsP generators++-- | An arbitrary expression, paired with its expected meaning.+--+-- See the function 'checkSemanticsP' to evaluate the pair.+arbitrarySemanticsP :: Gen SemanticsP+arbitrarySemanticsP = oneof+ [ SemanticsP <$> (arbitraryBoolExpr :: Gen (Semantics Bool))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Int8))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Int16))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Int32))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Int64))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Word8))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Word16))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Word32))+ , SemanticsP <$> (arbitraryNumExpr :: Gen (Semantics Word64))+ , SemanticsP <$> (arbitraryFloatingExpr :: Gen (Semantics Float))+ , SemanticsP <$> (arbitraryFloatingExpr :: Gen (Semantics Double))+ , SemanticsP <$> (arbitraryRealFracExpr :: Gen (Semantics Float))+ , SemanticsP <$> (arbitraryRealFracExpr :: Gen (Semantics Double))+ , SemanticsP <$> (arbitraryRealFloatExpr :: Gen (Semantics Float))+ , SemanticsP <$> (arbitraryRealFloatExpr :: Gen (Semantics Double))+ , SemanticsP <$> (arbitraryFractionalExpr :: Gen (Semantics Float))+ , SemanticsP <$> (arbitraryFractionalExpr :: Gen (Semantics Double))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Int8))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Int16))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Int32))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Int64))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Word8))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Word16))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Word32))+ , SemanticsP <$> (arbitraryIntegralExpr :: Gen (Semantics Word64))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Bool))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Int8))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Int16))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Int32))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Int64))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Word8))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Word16))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Word32))+ , SemanticsP <$> (arbitraryBitsExpr :: Gen (Semantics Word64))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Int8))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Int16))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Int32))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Int64))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Word8))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Word16))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Word32))+ , SemanticsP <$> (arbitraryBitsIntegralExpr :: Gen (Semantics Word64))+ ]++-- | Generate an arbitrary drop by taking an expression, adding a number of+-- elements to it, and then dropping some.+arbitraryDrop :: SemanticsP -> Gen (Stream, SemanticsP)+arbitraryDrop (SemanticsP (expr, meaning)) = do+ -- Randomly generate a list of elements+ prependLength <- getPositive <$> arbitrary+ buffer <- vectorOf prependLength arbitrary++ -- Build the stream with the buffer+ let streamId = 0+ stream = Stream streamId buffer expr typeOf++ -- Select how many elements to drop from the stream (up to the length of the+ -- buffer)+ dropLength <- chooseInt (0, prependLength)++ -- Build a drop expression that drops those many elements, paired with its+ -- meaning.+ let expr' = Drop typeOf (fromIntegral dropLength) streamId+ meaning' = drop dropLength buffer ++ meaning++ return (stream, SemanticsP (expr', meaning'))++-- ** Random Expr generators++-- | An arbitrary constant expression of any type, paired with its expected+-- meaning.+arbitraryConst :: (Arbitrary t, Typed t)+ => Gen (Expr t, [t])+arbitraryConst = (\v -> (Const typeOf v, repeat v)) <$> arbitrary++-- | An arbitrary boolean expression, paired with its expected meaning.+arbitraryBoolExpr :: Gen (Expr Bool, [Bool])+arbitraryBoolExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (5, apply1 <$> arbitraryBoolOp1 <*> arbitraryBoolExpr)++ , (1, apply2 <$> arbitraryBoolOp2+ <*> arbitraryBoolExpr+ <*> arbitraryBoolExpr)++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBoolExpr+ <*> arbitraryBoolExpr)++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Int8, [Int8])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Int16, [Int16])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Int32, [Int32])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Int64, [Int64])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Word8, [Word8])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Word16, [Word16])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Word32, [Word32])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryBitsExpr+ <*> (arbitraryBitsExpr :: Gen (Expr Word64, [Word64])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int8, [Int8])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int16, [Int16])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int32, [Int32])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int64, [Int64])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word8, [Word8])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word16, [Word16])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word32, [Word32])))++ , (1, apply2 <$> arbitraryEqOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word64, [Word64])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int8, [Int8])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int16, [Int16])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int32, [Int32])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Int64, [Int64])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word8, [Word8])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word16, [Word16])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word32, [Word32])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryNumExpr+ <*> (arbitraryNumExpr :: Gen (Expr Word64, [Word64])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryFloatingExpr+ <*> (arbitraryFloatingExpr :: Gen (Expr Float, [Float])))++ , (1, apply2 <$> arbitraryOrdOp2+ <*> arbitraryFloatingExpr+ <*> (arbitraryFloatingExpr :: Gen (Expr Double, [Double])))++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryBoolExpr+ <*> arbitraryBoolExpr)+ ]++-- | An arbitrary numeric expression, paired with its expected meaning.+arbitraryNumExpr :: (Arbitrary t, Typed t, Num t)+ => Gen (Expr t, [t])+arbitraryNumExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (5, apply1 <$> arbitraryNumOp1 <*> arbitraryNumExpr)++ , (2, apply2 <$> arbitraryNumOp2 <*> arbitraryNumExpr <*> arbitraryNumExpr)++ , (2, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryNumExpr+ <*> arbitraryNumExpr)+ ]++-- | An arbitrary floating point expression, paired with its expected meaning.+arbitraryFloatingExpr :: (Arbitrary t, Typed t, Floating t)+ => Gen (Expr t, [t])+arbitraryFloatingExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (5, apply1 <$> arbitraryFloatingOp1 <*> arbitraryFloatingExpr)++ , (5, apply1 <$> arbitraryNumOp1 <*> arbitraryFloatingExpr)++ , (2, apply2 <$> arbitraryFloatingOp2+ <*> arbitraryFloatingExpr+ <*> arbitraryFloatingExpr)++ , (2, apply2 <$> arbitraryNumOp2+ <*> arbitraryFloatingExpr+ <*> arbitraryFloatingExpr)++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryFloatingExpr+ <*> arbitraryFloatingExpr)+ ]++-- | An arbitrary realfrac expression, paired with its expected meaning.+arbitraryRealFracExpr :: (Arbitrary t, Typed t, RealFrac t)+ => Gen (Expr t, [t])+arbitraryRealFracExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (2, apply1 <$> arbitraryRealFracOp1 <*> arbitraryRealFracExpr)++ , (5, apply1 <$> arbitraryNumOp1 <*> arbitraryRealFracExpr)++ , (1, apply2 <$> arbitraryNumOp2+ <*> arbitraryRealFracExpr+ <*> arbitraryRealFracExpr)++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryRealFracExpr+ <*> arbitraryRealFracExpr)+ ]++-- | An arbitrary realfloat expression, paired with its expected meaning.+arbitraryRealFloatExpr :: (Arbitrary t, Typed t, RealFloat t)+ => Gen (Expr t, [t])+arbitraryRealFloatExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (2, apply1 <$> arbitraryNumOp1 <*> arbitraryRealFloatExpr)++ , (5, apply2 <$> arbitraryRealFloatOp2+ <*> arbitraryRealFloatExpr+ <*> arbitraryRealFloatExpr)++ , (1, apply2 <$> arbitraryNumOp2+ <*> arbitraryRealFloatExpr+ <*> arbitraryRealFloatExpr)++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryRealFloatExpr+ <*> arbitraryRealFloatExpr)+ ]++-- | An arbitrary fractional expression, paired with its expected meaning.+--+-- We add the constraint Eq because we sometimes need to make sure numbers are+-- not zero.+arbitraryFractionalExpr :: (Arbitrary t, Typed t, Fractional t, Eq t)+ => Gen (Expr t, [t])+arbitraryFractionalExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (5, apply1 <$> arbitraryFractionalOp1 <*> arbitraryFractionalExpr)++ , (5, apply1 <$> arbitraryNumOp1 <*> arbitraryFractionalExpr)++ , (2, apply2 <$> arbitraryFractionalOp2+ <*> arbitraryFractionalExpr+ <*> arbitraryFractionalExprNonZero)++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryFractionalExpr+ <*> arbitraryFractionalExpr)+ ]+ where++ -- Generator for fractional expressions that are never zero.+ --+ -- The list is infinite, so this generator checks up to maxTraceLength+ -- elements.+ arbitraryFractionalExprNonZero = arbitraryFractionalExpr+ `suchThat` (notElem 0 . take maxTraceLength . snd)++-- | An arbitrary integral expression, paired with its expected meaning.+--+-- We add the constraint Eq because we sometimes need to make sure numbers are+-- not zero.+arbitraryIntegralExpr :: (Arbitrary t, Typed t, Integral t, Eq t)+ => Gen (Expr t, [t])+arbitraryIntegralExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (5, apply1 <$> arbitraryNumOp1 <*> arbitraryIntegralExpr)++ , (2, apply2 <$> arbitraryNumOp2+ <*> arbitraryIntegralExpr+ <*> arbitraryIntegralExpr)++ , (2, apply2 <$> arbitraryIntegralOp2+ <*> arbitraryIntegralExpr+ <*> arbitraryIntegralExprNonZero)++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryIntegralExpr+ <*> arbitraryIntegralExpr)+ ]+ where++ -- Generator for integral expressions that are never zero.+ --+ -- The list is infinite, so this generator checks up to maxTraceLength+ -- elements.+ arbitraryIntegralExprNonZero = arbitraryIntegralExpr+ `suchThat` (notElem 0 . take maxTraceLength . snd)++-- | An arbitrary Bits expression, paired with its expected meaning.+arbitraryBitsExpr :: (Arbitrary t, Typed t, Bits t)+ => Gen (Expr t, [t])+arbitraryBitsExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (5, apply1 <$> arbitraryBitsOp1 <*> arbitraryBitsExpr)++ , (2, apply2 <$> arbitraryBitsOp2+ <*> arbitraryBitsExpr+ <*> arbitraryBitsExpr)++ , (2, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryBitsExpr <*> arbitraryBitsExpr)+ ]++-- | An arbitrary expression for types that are instances of Bits and Integral,+-- paired with its expected meaning.+arbitraryBitsIntegralExpr :: (Arbitrary t, Typed t, Bits t, Integral t)+ => Gen (Expr t, [t])+arbitraryBitsIntegralExpr =+ -- We use frequency instead of oneof because the random expression generator+ -- seems to generate expressions that are too large and the test fails due+ -- to running out of memory.+ frequency+ [ (10, arbitraryConst)++ , (2, apply1 <$> arbitraryNumOp1 <*> arbitraryBitsIntegralExpr)++ , (1, apply2 <$> arbitraryNumOp2+ <*> arbitraryBitsIntegralExpr+ <*> arbitraryBitsIntegralExpr)++ , (5, apply2 <$> arbitraryBitsIntegralOp2+ <*> arbitraryBitsIntegralExpr+ <*> arbitraryBitsIntegralExpr)++ , (1, apply3 <$> arbitraryITEOp3+ <*> arbitraryBoolExpr+ <*> arbitraryBitsIntegralExpr+ <*> arbitraryBitsIntegralExpr)+ ]++-- ** Operators++-- *** Op 1++-- | Generator for arbitrary boolean operators with arity 1, paired with their+-- expected meaning.+arbitraryBoolOp1 :: Gen (Expr Bool -> Expr Bool, [Bool] -> [Bool])+arbitraryBoolOp1 = elements+ [ (Op1 Not, fmap not)+ ]++-- | Generator for arbitrary numeric operators with arity 1, paired with their+-- expected meaning.+arbitraryNumOp1 :: (Typed t, Num t)+ => Gen (Expr t -> Expr t, [t] -> [t])+arbitraryNumOp1 = elements+ [ (Op1 (Abs typeOf), fmap abs)+ , (Op1 (Sign typeOf), fmap signum)+ ]++-- | Generator for arbitrary floating point operators with arity 1, paired with+-- their expected meaning.+arbitraryFloatingOp1 :: (Typed t, Floating t)+ => Gen (Expr t -> Expr t, [t] -> [t])+arbitraryFloatingOp1 = elements+ [ (Op1 (Exp typeOf), fmap exp)+ , (Op1 (Sqrt typeOf), fmap sqrt)+ , (Op1 (Log typeOf), fmap log)+ , (Op1 (Sin typeOf), fmap sin)+ , (Op1 (Tan typeOf), fmap tan)+ , (Op1 (Cos typeOf), fmap cos)+ , (Op1 (Asin typeOf), fmap asin)+ , (Op1 (Atan typeOf), fmap atan)+ , (Op1 (Acos typeOf), fmap acos)+ , (Op1 (Sinh typeOf), fmap sinh)+ , (Op1 (Tanh typeOf), fmap tanh)+ , (Op1 (Cosh typeOf), fmap cosh)+ , (Op1 (Asinh typeOf), fmap asinh)+ , (Op1 (Atanh typeOf), fmap atanh)+ , (Op1 (Acosh typeOf), fmap acosh)+ ]++-- | Generator for arbitrary realfrac operators with arity 1, paired with their+-- expected meaning.+arbitraryRealFracOp1 :: (Typed t, RealFrac t)+ => Gen (Expr t -> Expr t, [t] -> [t])+arbitraryRealFracOp1 = elements+ [ (Op1 (Ceiling typeOf), fmap (fromIntegral . idI . ceiling))+ , (Op1 (Floor typeOf), fmap (fromIntegral . idI . floor))+ ]+ where+ -- Auxiliary function to help the compiler determine which integral type+ -- the result of ceiling must be converted to. An Integer ensures that the+ -- result fits and there is no loss of precision due to the intermediate+ -- casting.+ idI :: Integer -> Integer+ idI = id++-- | Generator for arbitrary fractional operators with arity 1, paired with+-- their expected meaning.+arbitraryFractionalOp1 :: (Typed t, Fractional t)+ => Gen (Expr t -> Expr t, [t] -> [t])+arbitraryFractionalOp1 = elements+ [ (Op1 (Recip typeOf), fmap recip)+ ]++-- | Generator for arbitrary bitwise operators with arity 1, paired with their+-- expected meaning.+arbitraryBitsOp1 :: (Typed t, Bits t)+ => Gen (Expr t -> Expr t, [t] -> [t])+arbitraryBitsOp1 = elements+ [ (Op1 (BwNot typeOf), fmap complement)+ ]++-- *** Op 2++-- | Generator for arbitrary boolean operators with arity 2, paired with their+-- expected meaning.+arbitraryBoolOp2 :: Gen ( Expr Bool -> Expr Bool -> Expr Bool+ , [Bool] -> [Bool] -> [Bool]+ )+arbitraryBoolOp2 = elements+ [ (Op2 And, zipWith (&&))+ , (Op2 Or, zipWith (||))+ ]++-- | Generator for arbitrary numeric operators with arity 2, paired with their+-- expected meaning.+arbitraryNumOp2 :: (Typed t, Num t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryNumOp2 = elements+ [ (Op2 (Add typeOf), zipWith (+))+ , (Op2 (Sub typeOf), zipWith (-))+ , (Op2 (Mul typeOf), zipWith (*))+ ]++-- | Generator for arbitrary integral operators with arity 2, paired with their+-- expected meaning.+arbitraryIntegralOp2 :: (Typed t, Integral t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryIntegralOp2 = elements+ [ (Op2 (Mod typeOf), zipWith mod)+ , (Op2 (Div typeOf), zipWith quot)+ ]++-- | Generator for arbitrary fractional operators with arity 2, paired with+-- their expected meaning.+arbitraryFractionalOp2 :: (Typed t, Fractional t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryFractionalOp2 = elements+ [ (Op2 (Fdiv typeOf), zipWith (/))+ ]++-- | Generator for arbitrary floating point operators with arity 2, paired with+-- their expected meaning.+arbitraryFloatingOp2 :: (Typed t, Floating t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryFloatingOp2 = elements+ [ (Op2 (Pow typeOf), zipWith (**))+ , (Op2 (Logb typeOf), zipWith logBase)+ ]++-- | Generator for arbitrary floating point operators with arity 2, paired with+-- their expected meaning.+arbitraryRealFloatOp2 :: (Typed t, RealFloat t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryRealFloatOp2 = elements+ [ (Op2 (Atan2 typeOf), zipWith atan2)+ ]++-- | Generator for arbitrary equality operators with arity 2, paired with their+-- expected meaning.+arbitraryEqOp2 :: (Typed t, Eq t)+ => Gen (Expr t -> Expr t -> Expr Bool, [t] -> [t] -> [Bool])+arbitraryEqOp2 = elements+ [ (Op2 (Eq typeOf), zipWith (==))+ , (Op2 (Ne typeOf), zipWith (/=))+ ]++-- | Generator for arbitrary ordering operators with arity 2, paired with their+-- expected meaning.+arbitraryOrdOp2 :: (Typed t, Ord t)+ => Gen (Expr t -> Expr t -> Expr Bool, [t] -> [t] -> [Bool])+arbitraryOrdOp2 = elements+ [ (Op2 (Le typeOf), zipWith (<=))+ , (Op2 (Lt typeOf), zipWith (<))+ , (Op2 (Ge typeOf), zipWith (>=))+ , (Op2 (Gt typeOf), zipWith (>))+ ]++-- | Generator for arbitrary bitwise operators with arity 2, paired with their+-- expected meaning.+arbitraryBitsOp2 :: (Typed t, Bits t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryBitsOp2 = elements+ [ (Op2 (BwAnd typeOf), zipWith (.&.))+ , (Op2 (BwOr typeOf), zipWith (.|.))+ , (Op2 (BwXor typeOf), zipWith xor)+ ]++-- | Generator for arbitrary bit shifting operators with arity 2, paired with+-- their expected meaning.+--+-- This generator is a bit more strict in its type signature than the+-- underlying bit-shifting operators being tested, since it enforces both the+-- value being manipulated and the value that indicates how much to shift by to+-- have the same type.+arbitraryBitsIntegralOp2 :: (Typed t, Bits t, Integral t)+ => Gen (Expr t -> Expr t -> Expr t, [t] -> [t] -> [t])+arbitraryBitsIntegralOp2 = elements+ [ (Op2 (BwShiftL typeOf typeOf), zipWith (\x y -> shiftL x (fromIntegral y)))+ , (Op2 (BwShiftR typeOf typeOf), zipWith (\x y -> shiftR x (fromIntegral y)))+ ]++-- *** Op 3++-- | Generator for if-then-else operator (with arity 3), paired with its+-- expected meaning.+--+-- Although this is constant and there is nothing arbitrary, we use the same+-- structure and naming convention as with others for simplicity.+arbitraryITEOp3 :: (Arbitrary t, Typed t)+ => Gen ( Expr Bool -> Expr t -> Expr t -> Expr t+ , [Bool] -> [t] -> [t] -> [t]+ )+arbitraryITEOp3 = return+ (Op3 (Mux typeOf), zipWith3 (\x y z -> if x then y else z))++-- * Semantics++-- | Type that pairs an expression with its meaning as an infinite stream.+type Semantics t = (Expr t, [t])++-- | A phantom semantics pair is an existential type that encloses an+-- expression and its expected meaning as an infinite list of values.+--+-- It is needed by the arbitrary expression generator, to create a+-- heterogeneous list.+data SemanticsP = forall t+ . (Typeable t, Read t, Eq t, Show t, Typed t, Arbitrary t)+ => SemanticsP+ { semanticsPair :: (Expr t, [t])+ }++-- | Show function for test triplets that limits the accompanying list+-- to a certain length.+semanticsShowK :: Int -> SemanticsP -> String+semanticsShowK steps (SemanticsP (expr, exprList)) =+ show (showType ty, render $ ppExpr expr, take steps exprList)++ where++ -- Type of the expression. The type is enforced by _u below.+ ty = typeOf++ -- We want to show the type. To help GHC determine that the type t is the+ -- same as the expression's (expr), we use an UExpr, which has an+ -- additional constraint. This definition serves no other purpose than to+ -- help enforce that constraint.+ _u = UExpr ty expr++-- | Check that the expression in the semantics pair is evaluated to the given+-- list, up to a number of steps.+--+-- Some operations will overflow and return NaN. Because comparing any NaN+-- will, as per IEEE 754, always fail (i.e., return False), we handle that+-- specific case by stating that the test succeeds if any expected values+-- is NaN.+checkSemanticsP :: Int -> [Stream] -> SemanticsP -> Bool+checkSemanticsP steps streams (SemanticsP (expr, exprList)) =+ any isNaN' expectation || resultValues == expectation+ where+ -- Limit expectation to the number of evaluation steps.+ expectation = take steps exprList++ -- Obtain the results by looking up the observer in the spec+ -- and parsing the results into Haskell values.+ resultValues = fmap readResult results+ results = lookupWithDefault testObserverName []+ $ interpObservers trace++ -- Spec with just one observer of one expression.+ trace = eval Haskell steps spec+ spec = Spec streams observers [] []+ observers = [Observer testObserverName expr typeOf]++ -- Fixed name for the observer. Used to obtain the result from the+ -- trace. It should be the only observer in the trace.+ testObserverName :: String+ testObserverName = "res"++ -- | Is NaN with Eq requirement only.+ isNaN' :: Eq a => a -> Bool+ isNaN' x = x /= x++-- * Auxiliary++-- | Read a Haskell value from the output of the evaluator.+readResult :: Read a => String -> a+readResult = read . readResult'+ where+ readResult' :: String -> String+ readResult' "false" = "False"+ readResult' "true" = "True"+ readResult' s = s++-- | Variant of 'lookup' with an additional default value returned when the key+-- provided is not found in the map.+lookupWithDefault :: Ord k => k -> v -> [(k, v)] -> v+lookupWithDefault k def = fromMaybe def . lookup k
+ tests/Test/Copilot/Core/Type.hs view
@@ -0,0 +1,69 @@+-- | Test copilot-core:Copilot.Core.Type.+module Test.Copilot.Core.Type where++-- External imports+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Property, elements, forAllBlind,+ shuffle, (==>))++-- Internal imports: library modules being tested+import Copilot.Core.Type (SimpleType (..))++-- | All unit tests for copilot-core:Copilot.Core.Type.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.Type"+ [ testProperty "reflexivity of equality of simple types"+ testSimpleTypesEqualityReflexive+ , testProperty "symmetry of equality of simple types"+ testSimpleTypesEqualitySymmetric+ , testProperty "transitivity of equality of simple types"+ testSimpleTypesEqualityTransitive+ , testProperty "uniqueness of equality of simple types"+ testSimpleTypesEqualityUniqueness+ ]++-- | Test that the equality relation for simple types is reflexive.+testSimpleTypesEqualityReflexive :: Property+testSimpleTypesEqualityReflexive =+ forAllBlind (elements simpleTypes) $ \t ->+ t == t++-- | Test that the equality relation for simple types is symmetric.+testSimpleTypesEqualitySymmetric :: Property+testSimpleTypesEqualitySymmetric =+ forAllBlind (elements simpleTypes) $ \t1 ->+ forAllBlind (elements simpleTypes) $ \t2 ->+ t1 == t2 ==> t2 == t1++-- | Test that the equality relation for simple types is transitive.+testSimpleTypesEqualityTransitive :: Property+testSimpleTypesEqualityTransitive =+ forAllBlind (elements simpleTypes) $ \t1 ->+ forAllBlind (elements simpleTypes) $ \t2 ->+ forAllBlind (elements simpleTypes) $ \t3 ->+ (t1 == t2 && t2 == t3) ==> (t1 == t3)++-- | Test that each type is only equal to itself.+testSimpleTypesEqualityUniqueness :: Property+testSimpleTypesEqualityUniqueness =+ forAllBlind (shuffle simpleTypes) $ \(t:ts) ->+ notElem t ts++-- | Simple types tested.+simpleTypes :: [SimpleType]+simpleTypes =+ [ SBool+ , SInt8+ , SInt16+ , SInt32+ , SInt64+ , SWord8+ , SWord16+ , SWord32+ , SWord64+ , SFloat+ , SDouble+ , SStruct+ ]
+ tests/Test/Copilot/Core/Type/Array.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- | Test copilot-core:Copilot.Core.Type.Array.+module Test.Copilot.Core.Type.Array where++-- External imports+import Data.Int (Int64)+import Data.Proxy (Proxy (..))+import GHC.TypeNats (KnownNat, natVal)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Gen, Property, arbitrary, forAll,+ vectorOf)++-- Internal imports: library modules being tested+import Copilot.Core.Type.Array (Array, array, arrayelems, flatten, size)++-- | All unit tests for copilot-core:Copilot.Core.Array.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.Type.Array"+ [ testProperty "arrayElems . array (identity; 0)"+ (testArrayElemsLeft (Proxy :: Proxy 0))+ , testProperty "arrayElems . array (identity; 5)"+ (testArrayElemsLeft (Proxy :: Proxy 5))+ , testProperty "arrayElems . array (identity; 200)"+ (testArrayElemsLeft (Proxy :: Proxy 200))+ , testProperty "arrayFlatten . array (identity; 0)"+ (testArrayFlattenLeft (Proxy :: Proxy 0))+ , testProperty "arrayFlatten . array (identity; 1)"+ (testArrayFlattenLeft (Proxy :: Proxy 1))+ , testProperty "arrayFlatten . array (identity; 2)"+ (testArrayFlattenLeft (Proxy :: Proxy 2))+ , testProperty "arrayFlatten . array (identity; 22)"+ (testArrayFlattenLeft (Proxy :: Proxy 22))+ , testProperty "arrayFlatten . array (identity; 50)"+ (testArrayFlattenLeft (Proxy :: Proxy 50))+ , testProperty "arrayFlatten . array (identity; nested; nested; 2x6)"+ (testArrayFlattenNestedLeft (Proxy :: Proxy 2) (Proxy :: Proxy 6))+ , testProperty "arrayFlatten . array (identity; nested; nested; 10x2)"+ (testArrayFlattenNestedLeft (Proxy :: Proxy 10) (Proxy :: Proxy 2))+ , testProperty "arrayFlatten . array (identity; nested; nested; 38x20)"+ (testArrayFlattenNestedLeft (Proxy :: Proxy 38) (Proxy :: Proxy 20))+ , testProperty "arrayFlatten . array (identity; nested; nested; 125x100)"+ (testArrayFlattenNestedLeft (Proxy :: Proxy 125) (Proxy :: Proxy 100))+ , testProperty "arraySize . array (identity; 0)"+ (testArraySizeLeft (Proxy :: Proxy 0))+ , testProperty "arraySize . array (identity; 45)"+ (testArraySizeLeft (Proxy :: Proxy 45))+ , testProperty "arraySize . array (identity; 100)"+ (testArraySizeLeft (Proxy :: Proxy 100))+ ]++-- * Individual tests++-- | Test that building an array from a list and extracting the elements with+-- the function 'arrayelems' will result in the same list.+testArrayElemsLeft :: forall n . KnownNat n => Proxy n -> Property+testArrayElemsLeft len =+ forAll xsInt64 $ \ls ->+ let array' :: Array n Int64+ array' = array ls+ in arrayelems array' == ls++ where++ -- Generator for lists of Int64 of known length.+ xsInt64 :: Gen [Int64]+ xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary++-- | Test that building an array from a list and extracting the elements with+-- the function 'flatten' will result in the same list.+--+-- This test tests only plain arrays (no nesting).+testArrayFlattenLeft :: forall n . KnownNat n => Proxy n -> Property+testArrayFlattenLeft len =+ forAll xsInt64 $ \ls ->+ let array' :: Array n Int64+ array' = array ls+ in flatten array' == ls++ where++ -- Generator for lists of Int64 of known length.+ xsInt64 :: Gen [Int64]+ xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary++-- | Test that building a nested array of a known size and extracting the+-- elements with the function 'flatten' will result in a list with a size that+-- is the product of the sizes provided.+testArrayFlattenNestedLeft :: forall n1 n2+ . (KnownNat n1, KnownNat n2)+ => Proxy n1+ -> Proxy n2+ -> Property+testArrayFlattenNestedLeft len1 len2 =+ forAll xsArrays $ \array' ->+ length (flatten array' :: [Int64]) == expectedSize++ where++ -- Expected size of the matrix / array.+ expectedSize :: Int+ expectedSize = fromIntegral $ natVal len1 * natVal len2++ -- Generator for nested matrices of Int64 of known size.+ xsArrays :: Gen (Array n1 (Array n2 Int64))+ xsArrays =+ array <$> vectorOf (fromIntegral (natVal len1)) xsArrayInt64++ -- Generator for arrays of Int64 of known length.+ xsArrayInt64 :: Gen (Array n2 Int64)+ xsArrayInt64 = array <$> xsInt64++ -- Generator for lists of Int64 of known length.+ xsInt64 :: Gen [Int64]+ xsInt64 = vectorOf (fromIntegral (natVal len2)) arbitrary++-- | Test that building an array from a list and calculating the size will+-- return the length of the list.+testArraySizeLeft :: forall n . KnownNat n => Proxy n -> Property+testArraySizeLeft len =+ forAll xsInt64 $ \ls ->+ let array' :: Array n Int64+ array' = array ls+ in size array' == length ls++ where++ -- Generator for lists of Int64 of known length.+ xsInt64 :: Gen [Int64]+ xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary
+ tests/Test/Copilot/Core/Type/Dynamic.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE GADTs #-}+-- | Test copilot-core:Copilot.Core.Type.Dynamic.+module Test.Copilot.Core.Type.Dynamic where++-- External imports+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Arbitrary, Property, property)++-- Internal imports: library modules being tested+import Copilot.Core.Type (Type (..))+import Copilot.Core.Type.Dynamic (fromDyn, toDyn)+import Copilot.Core.Type.Equality (Equal (Refl), EqualType ((=~=)))++-- | All unit tests for copilot-core:Copilot.Core.Type.Dynamic.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.Type.Dynamic"+ [ testProperty "fromDyn . toDyn == identity (Int64)"+ (testDynamicIdentity Int64 Int64)+ , testProperty "fromDyn . toDyn == identity (Bool)"+ (testDynamicIdentity Bool Bool)+ , testProperty "fromDyn . toDyn == identity (Float)"+ (testDynamicIdentity Float Float)+ , testProperty "fromDyn . toDyn == identity (Double)"+ (testDynamicIdentity Double Double)+ , testProperty "fromDyn . toDyn /= identity (Int64 vs Int32)"+ (testDynamicIdentity Int64 Int32)+ , testProperty "fromDyn . toDyn /= identity (Int64 vs Int16)"+ (testDynamicIdentity Int64 Int16)+ , testProperty "fromDyn . toDyn /= identity (Bool vs Int16)"+ (testDynamicIdentity Bool Int16)+ , testProperty "fromDyn . toDyn /= identity (Float vs Double)"+ (testDynamicIdentity Float Double)+ ]++-- | Test that wrapping in and unwrapping from a dynamic gets the original+-- value if the types match, and Nothing otherwise.+testDynamicIdentity :: (Eq a, Arbitrary b)+ => Type a -- ^ Type to unwrap to+ -> Type b -- ^ Type to wrap to+ -> b -- ^ Value to wrap+ -> Property+testDynamicIdentity t1 t2 ls = property $+ let unwrapped = fromDyn t1 (toDyn t2 ls)+ in case t1 =~= t2 of+ Nothing -> Nothing == unwrapped+ Just Refl -> Just ls == unwrapped
+ tests/Test/Copilot/Core/Type/Show.hs view
@@ -0,0 +1,34 @@+-- | Test copilot-core:Copilot.Core.Type.Show.+module Test.Copilot.Core.Type.Show where++-- External imports+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Arbitrary, Property, property)+import Text.Read (readMaybe)++-- Internal imports: library modules being tested+import Copilot.Core.Type (Type (Double, Float, Int16, Int32, Int64))+import Copilot.Core.Type.Show (ShowType (Haskell), showWithType)++-- | All unit tests for copilot-core:Copilot.Core.Type.Show.+tests :: Test.Framework.Test+tests =+ testGroup "Copilot.Core.Type.Show"+ [ testProperty "read . showWithType == identity (Int16)"+ (testShowRead Int16)+ , testProperty "read . showWithType == identity (Int32)"+ (testShowRead Int32)+ , testProperty "read . showWithType == identity (Int64)"+ (testShowRead Int64)+ , testProperty "read . showWithType == identity (Float)"+ (testShowRead Float)+ , testProperty "read . showWithType == identity (Double)"+ (testShowRead Double)+ ]++-- | Test that showing a value with 'showWithType' and reading it back results+-- in the same value.+testShowRead :: (Arbitrary a, Eq a, Read a) => Type a -> a -> Property+testShowRead t v = property $+ Just v == readMaybe (showWithType Haskell t v)
+ tests/Test/Extra.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE ScopedTypeVariables #-}+-- | Auxiliary testing helper functions.+module Test.Extra where++-- External imports+import Control.Arrow ((***))+import Control.Exception (SomeException, catch)++-- * Detecting exceptions++-- | Test that a computation terminates without throwing an exception.+--+-- Returns 'True' if the computation can be completed without an exception,+-- and 'False' otherwise.+withoutException :: IO a -> IO Bool+withoutException e =+ catch (e >> return True)+ (\(_ :: SomeException) -> return False)++-- * Function application++-- | Apply a tuple with two functions to a tuple of arguments.+apply1 :: (a1 -> b1, a2 -> b2) -- ^ Pair with functions+ -> (a1, a2) -- ^ Pair with arguments+ -> (b1, b2) -- ^ Pair with results+apply1 = uncurry (***)++-- | Apply a tuple with two functions on two arguments to their tupled+-- arguments.+apply2 :: (a1 -> b1 -> c1, a2 -> b2 -> c2) -- ^ Pair with functions+ -> (a1, a2) -- ^ Pair with first arguments+ -> (b1, b2) -- ^ Pair with second arguments+ -> (c1, c2) -- ^ Pair with results+apply2 fs = apply1 . apply1 fs++-- | Apply a tuple with two functions on three arguments to their tupled+-- arguments.+apply3 :: (a1 -> b1 -> c1 -> d1, a2 -> b2 -> c2 -> d2)+ -- ^ Pair with functions+ -> (a1, a2) -- ^ Pair with first arguments+ -> (b1, b2) -- ^ Pair with second arguments+ -> (c1, c2) -- ^ Pair with third arguments+ -> (d1, d2) -- ^ Pair with results+apply3 fs = apply2 . apply1 fs