camfort-0.906: tests/Camfort/Specification/Units/Analysis/InferSpec.hs
module Camfort.Specification.Units.Analysis.InferSpec (spec) where
import System.FilePath ((</>))
import Control.Lens
import Test.Hspec hiding (Spec)
import qualified Test.Hspec as Test
import Language.Fortran.Util.ModFile (ModFile, emptyModFiles)
import Camfort.Analysis hiding (describe)
import Camfort.Analysis.ModFile (genModFiles, readParseSrcDir)
import Camfort.Specification.Units.Analysis (compileUnits)
import Camfort.Specification.Units.Analysis.Infer (inferUnits)
import Camfort.Specification.Units.Monad
(LiteralsOpt(..), unitOpts0, uoLiterals, runUnitAnalysis, UnitEnv(..))
spec :: Test.Spec
spec =
describe "fixtures integration tests" $ do
it "infers correctly based on simple addition" $
"example-simple-1.f90" `unitsInferReportIs` exampleInferSimple1Report
describe "Polymorphic functions" $
it "squarePoly1" $
"squarePoly1.f90" `unitsInferReportIs` squarePoly1Report
describe "Recursive functions" $
it "Recursive Addition is OK" $
"recursive1.f90" `unitsInferReportIs` recursive1Report
describe "Explicitly annotated parametric polymorphic unit variables" $ do
it "inside-outside" $
"insideOutside.f90" `unitsInferReportIs` insideOutsideReport
it "eapVarScope" $
"eapVarScope.f90" `unitsInferReportIs` eapVarScopeReport
it "eapVarApp" $
"eapVarApp.f90" `unitsInferReportIs` eapVarAppReport
describe "Implicit parametric polymorphic unit variables" $
it "inferPoly1" $
"inferPoly1.f90" `unitsInferReportIs` inferPoly1Report
describe "Intrinsic functions" $
it "sqrtPoly" $
"sqrtPoly.f90" `unitsInferReportIs` sqrtPolyReport
describe "Intrinsic function transfer (explicit cast)" $
it "transfer" $
"transfer.f90" `unitsInferReportIs` transferReport
describe "GCD of powers" $
it "gcd1" $
"gcd1.f90" `unitsInferReportIs` gcd1Report
describe "literals" $ do
it "literal-zero" $
"literal-zero.f90" `unitsInferReportIs` literalZeroReport
it "literal-nonzero" $
"literal-nonzero.f90" `unitsInferReportIs` literalNonZeroReport
it "literal-nonzero" $
"literal-nonzero2.f90" `unitsInferReportIs` literalNonZero2Report
it "do-loop1" $
"do-loop1.f90" `unitsInferReportIs` doLoop1Report
it "do-loop2" $
"do-loop2.f90" `unitsInferReportIs` doLoop2Report
describe "cross module analysis" $ do
it "with literals" $
unitsInferReportWithMod ["cross-module-b/cross-module-b1.f90"] "cross-module-b/cross-module-b2.f90"
crossModuleBReport
fixturesDir :: String
fixturesDir = "tests" </> "fixtures" </> "Specification" </> "Units"
-- | Assert that the report of performing units inference on a file is as expected.
unitsInferReportIs :: String -> String -> Expectation
unitsInferReportIs fileName expectedReport = do
unitsInferReportWithMod [] fileName expectedReport
-- | Assert that the report of performing units inference on a file is as expected (with mod files).
unitsInferReportWithMod :: [String] -> String -> String -> Expectation
unitsInferReportWithMod modNames fileName expectedReport = do
let file = fixturesDir </> fileName
modPaths = fmap (fixturesDir </>) modNames
modFiles <- mapM mkTestModFile modPaths
[(pf,_)] <- readParseSrcDir Nothing modFiles file []
let uEnv = UnitEnv { unitOpts = uOpts, unitProgramFile = pf }
report <- runAnalysisT file (logOutputNone True) LogError modFiles $ runUnitAnalysis uEnv $ inferUnits
let res = report ^?! arResult . _ARSuccess
show res `shouldBe` expectedReport
where uOpts = unitOpts0 { uoLiterals = LitMixed }
-- | Helper for producing a basic ModFile from a (terminal) module file.
mkTestModFile :: String -> IO ModFile
mkTestModFile file = head <$> genModFiles Nothing emptyModFiles compileUnits unitOpts0 file []
exampleInferSimple1Report :: String
exampleInferSimple1Report =
"\ntests/fixtures/Specification/Units/example-simple-1.f90:\n\
\ 3:14 unit s :: x\n\
\ 3:17 unit s :: y\n"
inferReport :: String -> String -> String
inferReport fname res = concat ["\n", fixturesDir </> fname, ":\n", res]
squarePoly1Report :: String
squarePoly1Report = inferReport "squarePoly1.f90"
" 4:11 unit m**2 :: x\n\
\ 5:11 unit s**2 :: y\n\
\ 7:11 unit m :: a\n\
\ 9:11 unit s :: b\n\
\ 13:3 unit ('b)**2 :: square\n\
\ 14:13 unit 'b :: n\n\
\ 17:3 unit ('a)**2 :: squarep\n\
\ 18:13 unit 'a :: m\n"
recursive1Report :: String
recursive1Report = inferReport "recursive1.f90"
" 3:14 unit 1 :: x\n\
\ 3:21 unit m :: y\n\
\ 3:28 unit m :: z\n\
\ 7:3 unit 'a :: r\n\
\ 8:16 unit 1 :: n\n\
\ 8:19 unit 'a :: b\n"
insideOutsideReport :: String
insideOutsideReport = inferReport "insideOutside.f90"
" 5:13 unit 'a :: x\n\
\ 5:16 unit 'a :: k\n\
\ 5:19 unit ('a)**2 :: m\n\
\ 5:22 unit ('a)**2 :: outside\n\
\ 12:15 unit 'a :: y\n\
\ 12:18 unit ('a)**2 :: inside\n"
eapVarScopeReport :: String
eapVarScopeReport = inferReport "eapVarScope.f90"
" 5:13 unit 'a :: x\n\
\ 5:16 unit ('a)**3 :: k\n\
\ 5:19 unit ('a)**3 :: f\n\
\ 11:13 unit 'a :: y\n\
\ 11:16 unit 'a :: j\n\
\ 11:19 unit 'a :: g\n"
eapVarAppReport :: String
eapVarAppReport = inferReport "eapVarApp.f90"
" 5:13 unit 'a :: fx\n\
\ 5:17 unit 'a :: fj\n\
\ 5:21 unit ('a)**2 :: fk\n\
\ 5:25 unit ('a)**4 :: fl\n\
\ 5:29 unit ('a)**2 :: f\n\
\ 13:13 unit 'b :: gx\n\
\ 13:17 unit 'b :: gn\n\
\ 13:21 unit 'b :: gm\n\
\ 13:25 unit 'b :: g\n\
\ 20:13 unit m :: hx\n\
\ 20:17 unit m**2 :: h\n\
\ 20:20 unit m**2 :: hy\n"
inferPoly1Report :: String
inferPoly1Report = inferReport "inferPoly1.f90"
" 4:13 unit 'c :: x1\n\
\ 4:17 unit 'c :: id\n\
\ 8:13 unit 'f :: x2\n\
\ 8:17 unit ('f)**2 :: sqr\n\
\ 12:13 unit 'a :: x3\n\
\ 12:17 unit 'b :: y3\n\
\ 12:21 unit 'a :: fst\n\
\ 16:13 unit 'e :: x4\n\
\ 16:17 unit 'd :: y4\n\
\ 16:21 unit 'd :: snd\n"
sqrtPolyReport :: String
sqrtPolyReport = inferReport "sqrtPoly.f90"
" 4:11 unit m :: x\n\
\ 6:11 unit s :: y\n\
\ 8:11 unit j :: z\n\
\ 9:14 unit m**2 :: a\n\
\ 10:14 unit s**4 :: b\n\
\ 11:14 unit j**2 :: c\n\
\ 16:3 unit ('a)**2 :: square\n\
\ 17:13 unit 'a :: n\n"
transferReport :: String
transferReport = inferReport "transfer.f90"
" 4:11 unit m :: x\n\
\ 6:11 unit s :: y\n"
gcd1Report :: String
gcd1Report = inferReport "gcd1.f90"
" 3:3 unit ('a)**12 :: g\n\
\ 4:13 unit ('a)**2 :: x\n\
\ 4:16 unit ('a)**3 :: y\n"
literalZeroReport :: String
literalZeroReport = inferReport "literal-zero.f90"
" 3:11 unit m :: a\n\
\ 3:14 unit m :: b\n\
\ 9:3 unit 'a :: f\n\
\ 11:13 unit 'a :: x\n"
literalNonZeroReport :: String
literalNonZeroReport = inferReport "literal-nonzero.f90"
" 2:11 unit m s :: a\n\
\ 2:14 unit m s :: b\n\
\ 8:3 unit m s :: f\n\
\ 10:13 unit m s :: x\n"
literalNonZero2Report :: String
literalNonZero2Report = inferReport "literal-nonzero2.f90"
" 3:11 unit m :: a\n\
\ 3:14 unit m :: b\n\
\ 3:17 unit m :: c\n\
\ 3:20 unit m :: d\n\
\ 10:3 unit m :: f\n\
\ 11:13 unit m :: x\n"
doLoop1Report :: String
doLoop1Report = inferReport "do-loop1.f90"
" 3:11 unit m :: x\n\
\ 3:14 unit m :: y\n\
\ 4:14 unit m :: i\n\
\ 10:3 unit 1 :: f\n\
\ 11:13 unit 1 :: x\n\
\ 11:16 unit 1 :: y\n\
\ 12:16 unit 1 :: i\n"
doLoop2Report :: String
doLoop2Report = inferReport "do-loop2.f90"
" 3:11 unit m :: x\n\
\ 3:14 unit m :: y\n\
\ 4:14 unit m :: i\n\
\ 10:3 unit 1 :: f\n\
\ 11:13 unit 1 :: x\n\
\ 11:16 unit 1 :: y\n\
\ 12:16 unit 1 :: i\n\
\ 19:3 unit 1 :: g\n\
\ 20:13 unit 1 :: x\n\
\ 20:16 unit 1 :: y\n\
\ 21:16 unit 1 :: i\n\
\ 28:3 unit 'a :: h\n\
\ 29:13 unit 'a :: x\n\
\ 29:16 unit 'a :: y\n\
\ 30:16 unit 'a :: i\n"
crossModuleBReport :: String
crossModuleBReport =
"\ntests/fixtures/Specification/Units/cross-module-b/cross-module-b2.f90:\n\
\ 6:24 unit c :: foo\n\
\ 9:13 unit c :: tc\n\
\ 9:17 unit k :: t\n"