camfort-0.905: tests/Camfort/Specification/Stencils/CheckSpec.hs
{-# LANGUAGE ImplicitParams #-}
module Camfort.Specification.Stencils.CheckSpec (spec) where
import qualified Data.ByteString.Internal as BS
import Control.Lens
import Camfort.Analysis hiding (describe)
import Camfort.Analysis.Annotations (unitAnnotation)
import Camfort.Specification.Parser (runParser)
import qualified Camfort.Specification.Stencils.Annotation as SA
import Camfort.Specification.Stencils.CheckBackend
import Camfort.Specification.Stencils.CheckFrontend
(CheckResult, stencilChecking)
import Camfort.Specification.Stencils.Parser (specParser)
import Camfort.Specification.Stencils.Model
import Camfort.Specification.Stencils.Syntax
import qualified Language.Fortran.Analysis as FA
import qualified Language.Fortran.Analysis.BBlocks as FAB
import qualified Language.Fortran.Analysis.Renaming as FAR
import Language.Fortran.Parser.Any (fortranParser)
import Language.Fortran.Util.ModFile (emptyModFiles)
import qualified Language.Fortran.Util.Position as FU
import Test.Hspec
parseAndConvert :: String -> Either SynToAstError (Either RegionDecl SpecDecl)
parseAndConvert x =
let ?renv = []
in case runParser specParser x of
Left _ -> error "received stencil with invalid syntax in test"
Right v -> synToAst v
spec :: Spec
spec =
describe "Stencils - Check" $ do
describe "Parsing comments into internal rep" $ do
it "parse and convert simple exact stencil (1)" $
parseAndConvert "= stencil forward(depth=1, dim=1) :: x"
`shouldBe`
(Right $ Right (["x"], Specification
(Mult $ Exact (Spatial (Sum [Product [Forward 1 1 True]]))) True))
it "parse and convert simple exact stencil (2)" $
parseAndConvert "= stencil forward(depth=1, dim=1) :: x, y, z"
`shouldBe`
(Right $ Right (["x","y","z"], Specification
(Mult $ Exact (Spatial (Sum [Product [Forward 1 1 True]]))) True))
it "parse and convert simple exact access spec (2)" $
parseAndConvert "= access forward(depth=1, dim=1) :: x, y, z"
`shouldBe`
(Right $ Right (["x","y","z"], Specification
(Mult $ Exact (Spatial (Sum [Product [Forward 1 1 True]]))) False))
it "parse and convert simple exact stencil with nonpointed (2a)" $
parseAndConvert "= stencil centered(depth=1, dim=2, nonpointed) :: x, y, z"
`shouldBe`
(Right $ Right (["x","y","z"], Specification
(Mult $ Exact (Spatial (Sum [Product [Centered 1 2 False]]))) True))
it "parse and convert simple upper bounded stencil (3)" $
parseAndConvert "= stencil atmost, forward(depth=1, dim=1) :: x"
`shouldBe`
(Right $ Right (["x"], Specification
(Mult $ Bound Nothing (Just $ Spatial
(Sum [Product [Forward 1 1 True]]))) True))
it "parse and convert simple upper bounded access spec (3)" $
parseAndConvert "= access atmost, forward(depth=1, dim=1) :: x"
`shouldBe`
(Right $ Right (["x"], Specification
(Mult $ Bound Nothing (Just $ Spatial
(Sum [Product [Forward 1 1 True]]))) False))
it "parse and convert simple lower bounded stencil (4)" $
parseAndConvert "= stencil atleast, backward(depth=2, dim=1) :: x"
`shouldBe`
(Right $ Right (["x"], Specification
(Mult $ Bound (Just $ Spatial
(Sum [Product [Backward 2 1 True]])) Nothing) True))
it "parse and convert stencil requiring distribution (5)" $
parseAndConvert "= stencil readonce, atleast, forward(depth=1, dim=1) * (centered(depth=1, dim=2) + backward(depth=3, dim=4)) :: frob"
`shouldBe`
(Right $ Right (["frob"], Specification
(Once $ Bound (Just $ Spatial
(Sum [Product [Forward 1 1 True, Centered 1 2 True],
Product [Forward 1 1 True, Backward 3 4 True]])) Nothing) True))
it "rejects stencils with undefined regions" $
parseAndConvert "= stencil r1 :: a"
`shouldBe` (Left . regionNotInScope $ "r1")
describe "stencils check" $ do
checkTestShow exampleUnusedRegion
"warns about unused regions"
"(2:3)-(2:34) Warning: Unused region 'r1'"
checkTestShow exampleRedefinedRegion
"warns about redefined"
"(4:3)-(4:34) Region 'r1' already defined\n\
\(6:5)-(6:32) Correct."
checkTestShow exampleSimpleInvalidSyntax
"warns about specification parse errors"
"(2:3)-(2:16) Could not parse specification at: \"... \"\n"
checkTestShow exampleSimpleCorrect
"recognises correct stencils"
"(4:5)-(4:63) Correct."
checkTestShow exampleUnusedRegionWithOtherSpecs
"provides reports in correct order"
"(3:3)-(3:34) Warning: Unused region 'r1'\n\
\(5:5)-(5:63) Correct.\n\
\(9:5)-(9:52) Not well specified.\n\
\ Specification is:\n\
\ stencil readOnce, forward(depth=1, dim=1) :: a\n\n\
\ but at (10:5)-(10:17) the code behaves as\n\
\ stencil readOnce, forward(depth=1, dim=1, nonpointed) :: a\n\n\
\(12:3)-(12:16) Could not parse specification at: \"... \"\n"
checkTestShow exampleSpecWrongVar
"validates that a specification is applied to the correct variables"
"(4:5)-(4:44) Not well specified.\n\
\ Specification is:\n\
\ stencil readOnce, pointed(dim=1) :: b\n\n\
\ but at (5:5)-(5:15) the code behaves as\n\
\ stencil readOnce, pointed(dim=1) :: a\n"
checkText :: BS.ByteString -> IO CheckResult
checkText text = do
pf <- either (fail "received test input with invalid syntax") return $
fortranParser text "example"
let pf' = getBlocks . fmap (const unitAnnotation) $ pf
return (runChecking pf' ^?! arResult . _ARSuccess)
where
runChecking = runIdentity . runAnalysisT "example" (logOutputNone True) LogInfo emptyModFiles . stencilChecking
getBlocks = FAB.analyseBBlocks . FAR.analyseRenames . FA.initAnalysis . fmap SA.mkStencilAnnotation
runCheck :: String -> IO CheckResult
runCheck = checkText . BS.packChars
checkTestShow :: String -> String -> String -> SpecWith ()
checkTestShow exampleText testDescription expected =
it testDescription $ do
res <- runCheck exampleText
show res `shouldBe` expected
exampleUnusedRegion :: String
exampleUnusedRegion =
"program example\n\
\ != region :: r1 = pointed(dim=1)\n\
\end program"
exampleRedefinedRegion :: String
exampleRedefinedRegion =
"program example\n\
\ real, dimension(10) :: a\n\
\ != region :: r1 = forward(depth=1,dim=1, nonpointed)\n\
\ != region :: r1 = pointed(dim=1)\n\
\ do i = 1, 10\n\
\ != stencil readOnce, r1 :: a\n\
\ a(i) = a(i+1)\n\
\ end do\n\
\end program"
exampleSimpleCorrect :: String
exampleSimpleCorrect =
"program example\n\
\ real, dimension(10) :: a\n\
\ do i = 1, 10\n\
\ != stencil readOnce, forward(depth=1,dim=1,nonpointed) :: a\n\
\ a(i) = a(i+1)\n\
\ end do\n\
\end program"
exampleSimpleInvalidSyntax :: String
exampleSimpleInvalidSyntax =
"program example\n\
\ != stencil foo\n\
\end program"
exampleUnusedRegionWithOtherSpecs :: String
exampleUnusedRegionWithOtherSpecs =
"program example\n\
\ real, dimension(10) :: a, b\n\
\ != region :: r1 = pointed(dim=1)\n\
\ do i = 1, 10\n\
\ != stencil readOnce, forward(depth=1,dim=1,nonpointed) :: a\n\
\ a(i) = a(i+1)\n\
\ end do\n\
\ do i = 1, 10\n\
\ != stencil readOnce, forward(depth=1,dim=1) :: a\n\
\ a(i) = a(i+1)\n\
\ end do\n\
\ != stencil foo\n\
\end program"
exampleSpecWrongVar :: String
exampleSpecWrongVar =
"program example\n\
\ real, dimension(10) :: a\n\
\ do i = 1, 10\n\
\ != stencil readOnce, pointed(dim=1) :: b\n\
\ a(i) = a(i)\n\
\ end do\n\
\end program"