fortran-src-0.4.2: test/Language/Fortran/RewriterSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Language.Fortran.RewriterSpec
( spec
)
where
import Test.Hspec
import Control.Exception.Base ( try
, throw
)
import Control.Monad ( foldM
, unless
)
import qualified Data.Map as M
import System.Directory ( copyFile
, createDirectory
, getCurrentDirectory
, doesDirectoryExist
)
import Language.Fortran.Rewriter
import qualified Data.ByteString.Lazy.Char8 as BC
spec :: Spec
spec = do
describe "ReplacementMap" $ do
it "Initialize ReplacementMap" $ do
let
rm = M.fromList
[ ( "foo.f" :: String
, [ Replacement
(SourceRange (SourceLocation 0 0) (SourceLocation 1 1))
""
]
)
, ("bar.f", [])
]
rm `shouldBe` M.fromList
[ ( "foo.f"
, [ Replacement
(SourceRange (SourceLocation 0 0) (SourceLocation 1 1))
""
]
)
, ("bar.f", [])
]
it "Process ReplacementMap (empty; should not die)" $ do
let rm = M.empty
processReplacements rm
it "Process ReplacementMap (simple replacements)" $ do
base <- getCurrentDirectory
let
body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 1 12) (SourceLocation 1 16))
"cccc"
]
)
, ( workDir ++ "002_foo.f"
, [ Replacement
(SourceRange (SourceLocation 1 7) (SourceLocation 1 16))
""
]
)
, ( workDir ++ "003_foo.f"
, [ Replacement
(SourceRange (SourceLocation 1 12) (SourceLocation 1 17))
"cccc1"
, Replacement
(SourceRange (SourceLocation 3 12) (SourceLocation 3 17))
"cccc2"
]
)
, ( workDir ++ "004_unicode.f"
, [ Replacement
(SourceRange (SourceLocation 3 17) (SourceLocation 3 28))
"int(z'deadbeef')"
]
)
, ( workDir ++ "005_unicode.f"
, [ Replacement
(SourceRange (SourceLocation 3 21) (SourceLocation 3 21))
""
]
)
]
wrapReplacementsMapInvocationTestHelper
body
base
Nothing
"replacementsmap-simple"
[ "001_foo.f"
, "002_foo.f"
, "003_foo.f"
, "004_unicode.f"
, "005_unicode.f"
]
it "Process ReplacementMap (insertions)" $ do
base <- getCurrentDirectory
let body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 0 24) (SourceLocation 0 24))
"\n call bbbb"
]
)
]
wrapReplacementsMapInvocationTestHelper body
base
Nothing
"replacementsmap-insertion"
["001_foo.f"]
it "Process ReplacementMap (overlapping)" $ do
base <- getCurrentDirectory
let
r1 = Replacement
(SourceRange (SourceLocation 1 12) (SourceLocation 1 16))
"cccc"
r2 = Replacement
(SourceRange (SourceLocation 1 7) (SourceLocation 1 16))
"call dddd"
body workDir =
processReplacements $ M.fromList [(workDir ++ "001_foo.f", [r1, r2])]
wrapReplacementsMapInvocationTestHelper
body
base
(Just $ OverlappingError [(r2, r1)])
"replacementsmap-overlapping"
["001_foo.f"]
it "Process ReplacementMap (overlapping filtered)" $ do
base <- getCurrentDirectory
let r1 = Replacement
(SourceRange (SourceLocation 1 12) (SourceLocation 1 16))
"cccc"
r2 = Replacement
(SourceRange (SourceLocation 1 7) (SourceLocation 1 16))
"call dddd"
(repls, overlapping) = partitionOverlapping [r1, r2]
body workDir =
processReplacements $ M.fromList [(workDir ++ "001_foo.f", repls)]
repls `shouldBe` [r1]
wrapReplacementsMapInvocationTestHelper
body
base
Nothing
"replacementsmap-overlapping-filtered"
["001_foo.f"]
overlapping `shouldBe` [r2]
it "Process ReplacementMap (invalid range; start line)" $ do
base <- getCurrentDirectory
let
body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 9 12) (SourceLocation 9 15))
""
]
)
]
wrapReplacementsMapInvocationTestHelper body
base
(Just InvalidRangeError)
"replacementsmap-overlapping"
["001_foo.f"]
it "Process ReplacementMap (invalid range; start column)" $ do
base <- getCurrentDirectory
let
body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 1 112) (SourceLocation 1 115))
""
]
)
]
wrapReplacementsMapInvocationTestHelper body
base
(Just InvalidRangeError)
"replacementsmap-overlapping"
["001_foo.f"]
it "Process ReplacementMap (invalid range; end line)" $ do
base <- getCurrentDirectory
let
body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 1 13) (SourceLocation 19 115))
""
]
)
]
wrapReplacementsMapInvocationTestHelper body
base
(Just InvalidRangeError)
"replacementsmap-overlapping"
["001_foo.f"]
it "Process ReplacementMap (invalid range; end column)" $ do
base <- getCurrentDirectory
let
body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 1 13) (SourceLocation 1 115))
""
]
)
]
wrapReplacementsMapInvocationTestHelper body
base
(Just InvalidRangeError)
"replacementsmap-overlapping"
["001_foo.f"]
it "Process ReplacementMap (column limit)" $ do
base <- getCurrentDirectory
let
body workDir = processReplacements $ M.fromList
[ ( workDir ++ "001_foo.f"
, [ Replacement
(SourceRange (SourceLocation 3 42) (SourceLocation 3 43))
"999999999999999999999"
]
)
, ( workDir ++ "002_other.f"
, [ Replacement
(SourceRange (SourceLocation 4 61) (SourceLocation 4 62))
"999999999999"
]
)
, ( workDir ++ "003_multiline.f"
, [ Replacement
(SourceRange (SourceLocation 4 61) (SourceLocation 4 62))
"9 .and. \n + 4 .lt. 4\n + .or. .true."
]
)
, ( workDir ++ "004_comment.f"
, [ Replacement
(SourceRange (SourceLocation 2 18) (SourceLocation 2 19))
"foobar"
, Replacement
(SourceRange (SourceLocation 8 16) (SourceLocation 8 27))
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
]
)
, ( workDir ++ "005_removals.f"
, [ Replacement
(SourceRange (SourceLocation 2 50) (SourceLocation 3 24))
""
, Replacement
(SourceRange (SourceLocation 8 55) (SourceLocation 9 22))
""
]
)
, ( workDir ++ "006_linewrap_heuristic.f"
, [ Replacement
(SourceRange (SourceLocation 3 19) (SourceLocation 3 23))
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, Replacement
(SourceRange (SourceLocation 4 23) (SourceLocation 4 27))
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, Replacement
(SourceRange (SourceLocation 5 28) (SourceLocation 5 32))
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, Replacement
(SourceRange (SourceLocation 6 28) (SourceLocation 6 32))
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, Replacement
(SourceRange (SourceLocation 7 19) (SourceLocation 7 23))
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, Replacement
(SourceRange (SourceLocation 8 6) (SourceLocation 8 24))
"call testcommons_logarray_settori_findex(logical_1_to_4(test), 1)"
, Replacement
(SourceRange (SourceLocation 11 12) (SourceLocation 11 28))
"call testcommons_set_logical2var(logical_2_to_4(l2))"
, Replacement
(SourceRange (SourceLocation 12 12) (SourceLocation 12 12))
"call testcommons_set_logical2var(logical_2_to_4(l2))"
, Replacement
(SourceRange (SourceLocation 13 12) (SourceLocation 13 13))
"call testcommons_set_logical2var(logical_2_to_4(l2))"
]
)
]
wrapReplacementsMapInvocationTestHelper
body
base
Nothing
"replacementsmap-columnlimit"
[ "001_foo.f"
, "002_other.f"
, "003_multiline.f"
, "004_comment.f"
, "005_removals.f"
, "006_linewrap_heuristic.f"
]
describe "Filtering overlapping replacements" $ do
it "Simple overlap" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 0) (SourceLocation 0 2))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"b"
(disjoint, overlapping) = partitionOverlapping [r1, r2]
disjoint `shouldBe` [r1]
overlapping `shouldBe` [r2]
it "Reversed order" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 0) (SourceLocation 0 2))
"b"
(disjoint, overlapping) = partitionOverlapping [r1, r2]
disjoint `shouldBe` [r1]
overlapping `shouldBe` [r2]
it "Middle overlap" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 0) (SourceLocation 0 2))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"b"
r3 = Replacement
(SourceRange (SourceLocation 0 2) (SourceLocation 0 4))
"c"
(disjoint, overlapping) = partitionOverlapping [r1, r2, r3]
disjoint `shouldBe` [r1, r3]
overlapping `shouldBe` [r2]
it "Middle overlap out of order" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 0) (SourceLocation 0 2))
"b"
r3 = Replacement
(SourceRange (SourceLocation 0 2) (SourceLocation 0 4))
"c"
(disjoint, overlapping) = partitionOverlapping [r1, r2, r3]
disjoint `shouldBe` [r1]
overlapping `shouldBe` [r2, r3]
it "Exact overlap" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"b"
(disjoint, overlapping) = partitionOverlapping [r1, r2]
disjoint `shouldBe` [r1]
overlapping `shouldBe` [r2]
it "Overlapping beginning" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 2))
"b"
(disjoint, overlapping) = partitionOverlapping [r1, r2]
disjoint `shouldBe` [r1]
overlapping `shouldBe` [r2]
it "Some overlapping" $ do
let
r1 = Replacement
(SourceRange (SourceLocation 0 1) (SourceLocation 0 3))
"a"
r2 = Replacement
(SourceRange (SourceLocation 0 5) (SourceLocation 0 7))
"b"
r3 = Replacement
(SourceRange (SourceLocation 0 3) (SourceLocation 0 5))
"b"
r4 = Replacement
(SourceRange (SourceLocation 0 6) (SourceLocation 0 8))
"b"
(disjoint, overlapping) = partitionOverlapping [r1, r2, r3, r4]
disjoint `shouldBe` [r1, r2, r3]
overlapping `shouldBe` [r4]
wrapReplacementsMapInvocationTestHelper
:: (String -> IO ())
-> FilePath
-> Maybe ReplacementError
-> String
-> [String]
-> Expectation
wrapReplacementsMapInvocationTestHelper invocation baseDir Nothing ctx files =
do
(inputContextDir, outputContextDir, invokeResult) <-
wrapReplacementsMapInvocationTestHelper_ invocation baseDir ctx files
case invokeResult of
Left e -> error $ "Test failed unexpectedly; should pass: " <> show e
Right _ -> do
let res = foldM
(\acc file -> do
let expected = inputContextDir ++ "/" ++ file ++ ".expected"
let output = outputContextDir ++ "/" ++ file
ok <- compareFile expected output
return (acc && ok)
)
True
files
res `shouldReturn` True
wrapReplacementsMapInvocationTestHelper invocation baseDir (Just expExcp) ctx files
= do
(_, _, invokeResult) <- wrapReplacementsMapInvocationTestHelper_
invocation
baseDir
ctx
files
case invokeResult of
Left ex -> throw ex `shouldThrow` (== expExcp)
Right _ -> error "Test passed unexpectedly; should fail"
wrapReplacementsMapInvocationTestHelper_
:: (String -> IO ())
-> FilePath
-> String
-> [String]
-> IO (String, String, Either ReplacementError ())
wrapReplacementsMapInvocationTestHelper_ invocation baseDir ctx files = do
let inputContextDir = baseDir ++ "/test/Language/Fortran/samples/rewriter/" ++ ctx
outputDir = baseDir ++ "/test/rewriter-test-output"
outputContextDir = outputDir ++ "/" ++ ctx
doesDirectoryExist outputDir
>>= (\x -> unless x $ createDirectory outputDir)
doesDirectoryExist outputContextDir
>>= (\x -> unless x $ createDirectory outputContextDir)
mapM_
(\file -> copyFile (inputContextDir ++ "/" ++ file)
(outputContextDir ++ "/" ++ file)
)
files
result <-
try (invocation $ outputContextDir ++ "/") :: IO
(Either ReplacementError ())
return (inputContextDir, outputContextDir, result)
--------------------------------------------------------------------------------
-- | Utility function to compare file content.
compareFile :: FilePath -> FilePath -> IO Bool
compareFile expected actual = do
c1 <- BC.readFile expected
c2 <- BC.readFile actual
compareByteString c1 c2
compareByteString :: BC.ByteString -> BC.ByteString -> IO Bool
compareByteString expected actual = if expected == actual
then return True
else do
BC.putStrLn ">>>>>>> EXPECTED"
BC.putStrLn expected
BC.putStrLn ">>>>>>> ACTUAL"
BC.putStrLn actual
return False