bond-0.11.0.0: tests/Tests/Syntax.hs
-- Copyright (c) Microsoft. All rights reserved.
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
{-# LANGUAGE OverloadedStrings, RecordWildCards, TemplateHaskell, ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Tests.Syntax
( roundtripAST
, compareAST
, failBadSyntax
, aliasResolution
, verifySchemaDef
) where
import Control.Exception
import Data.Maybe
import Data.List
import Data.Aeson (encode, decode)
import Data.Aeson.Encode.Pretty (Config(..), encodePretty')
import Data.DeriveTH
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import System.FilePath
import Test.QuickCheck
import Test.HUnit
import Test.Tasty
import Test.Tasty.Golden
import Language.Bond.Syntax.Types
import Language.Bond.Syntax.Util
import Language.Bond.Syntax.SchemaDef
import IO
derive makeArbitrary ''Attribute
derive makeArbitrary ''Bond
derive makeArbitrary ''Constant
derive makeArbitrary ''Constraint
derive makeArbitrary ''Declaration
derive makeArbitrary ''Default
derive makeArbitrary ''Field
derive makeArbitrary ''Import
derive makeArbitrary ''Language
derive makeArbitrary ''Modifier
derive makeArbitrary ''Namespace
derive makeArbitrary ''Type
derive makeArbitrary ''TypeParam
derive makeArbitrary ''MethodType
instance Arbitrary Method where
arbitrary = oneof
[ Function <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
, Event <$> arbitrary <*> arbitrary <*> eventInput]
where
-- events cannot have streaming input, so we need to customize the
-- arbitrary input to omit Streaming
eventInput = oneof [return Void, Unary <$> arbitrary]
roundtripAST :: Bond -> Property
roundtripAST x = (decode . encode) x === Just x
assertException :: String -> IO a -> Assertion
assertException errMsg action = do
e <- try action
case e of
Left (_ :: SomeException) -> return ()
Right _ -> assertFailure errMsg
failBadSyntax :: String -> FilePath -> Assertion
failBadSyntax errMsg file = assertException errMsg (parseBondFile [] $ "tests" </> "schema" </> "error" </> file <.> "bond")
compareAST :: FilePath -> Assertion
compareAST file = do
bond <- parseBondFile [] $ "tests" </> "schema" </> file <.> "bond"
json <- parseASTFile $ "tests" </> "schema" </> file <.> "json"
assertEqual "" bond json
aliasResolution :: Assertion
aliasResolution = do
let p1 = TypeParam "T" Nothing
let p2 = TypeParam "U" Nothing
let a1 = Alias [] "" [] BT_Bool
let a2 = Alias [] "" [p1] $ BT_TypeParam p1
let a3 = Alias [] "" [p1] $ BT_List $ BT_TypeParam p1
let a4 = Alias [] "" [p1, p2] $ BT_Nullable $ BT_Map (BT_TypeParam p2) (BT_Nullable $ BT_TypeParam p1)
let a5 = Alias [] "" [p1] $ BT_List $ BT_UserDefined a3 [BT_TypeParam p1]
let a6 = Alias [] "" [p1, p2] $ BT_Vector $ BT_Vector $ BT_TypeParam p2
assertEqual "" BT_Bool $
resolveAlias a1 []
assertEqual "" BT_Int32 $
resolveAlias a2 [BT_Int32]
assertEqual "" (BT_Set BT_Int32) $
resolveAlias a2 [BT_Set BT_Int32]
assertEqual "" (BT_TypeParam p2) $
resolveAlias a2 [BT_TypeParam p2]
assertEqual "" (BT_TypeParam p1) $
resolveAlias a2 [BT_TypeParam p1]
assertEqual "" (BT_List BT_Int32) $
resolveAlias a3 [BT_Int32]
assertEqual "" (BT_List $ BT_UserDefined a1 []) $
resolveAlias a3 [BT_UserDefined a1 []]
assertEqual "" (BT_Nullable $ BT_Map BT_Bool $ BT_Nullable BT_String) $
resolveAlias a4 [BT_String, BT_Bool]
assertEqual "" (BT_List $ BT_UserDefined a3 [BT_Set BT_Bool]) $
resolveAlias a5 [BT_Set BT_Bool]
assertEqual "" (BT_Vector $ BT_Vector $ BT_List BT_Double) $
resolveAlias a6 [BT_IntTypeArg 10, BT_List BT_Double]
verifySchemaDef :: FilePath -> String -> TestTree
verifySchemaDef baseName schemaName =
goldenVsString
schemaName
("tests" </> "generated" </> "schemadef" </> baseName <.> schemaName <.> "json")
schemaDef
where
schemaDef = do
(Bond _ _ declarations) <- parseBondFile [] $ "tests" </> "schema" </> baseName <.> "bond"
let schema = fromJust $ find ((schemaName ==) . declName) declarations
return $
-- some versions aeson encode angle brackets
BL.fromStrict $
replace "\\u003c" "<" $
replace "\\u003e" ">" $
BL.toStrict $ prettyEncode $ makeSchemaDef $ BT_UserDefined schema []
where
prettyEncode = encodePretty' (Config indentSpaces compare)
where
indentSpaces = 2
replace s r bs = if B.null t then h else
B.append h (B.append r $ replace s r (B.drop (B.length s) t))
where
(h, t) = B.breakSubstring s bs