content: module Camfort.Specification.DerivedDataTypeSpec (spec) where
  
  import           Camfort.Analysis hiding (describe)
  import           Camfort.Analysis.ModFile (genModFiles)
  import           Camfort.Input (readParseSrcDir)
  import           Camfort.Specification.DerivedDataType
  import           Control.Lens
  import qualified Data.Map.Strict as M
  import qualified Data.Set as S
  import           Language.Fortran.Util.ModFile (ModFile, emptyModFiles)
  import           System.FilePath ((</>))
  import qualified Test.Hspec as Test
  import           Test.Hspec hiding (Spec)
  
  fixturesDir :: String
  fixturesDir = "tests" </> "fixtures" </> "Specification" </> "DerivedDataType"
  
  ddtInferReport :: [String] -> String -> IO DerivedDataTypeReport
  ddtInferReport modNames fileName = do
    let file = fixturesDir </> fileName
        modPaths = fmap (fixturesDir </>) modNames
    modFiles <- mapM mkTestModFile modPaths
    [(pf,_)] <- readParseSrcDir Nothing modFiles file []
  
    let r = runIdentity $ runAnalysisT file (logOutputNone True) LogError modFiles (infer pf)
    return $ r ^?! arResult . _ARSuccess
  
  -- | Helper for producing a basic ModFile from a (terminal) module file.
  mkTestModFile :: String -> IO ModFile
  mkTestModFile file = head <$> genModFiles Nothing emptyModFiles compile () file []
  
  spec :: Test.Spec
  spec = do
    describe "consistent" $ do
      describe "across modfiles" $ do
        it "consistent where labels overlap" $ do
          r <- ddtInferReport ["consistSpec1b.f90"] "consistSpec1a.f90"
          successful r `shouldBe` True
        it "starred spec overrides unstarred spec" $ do
          r <- ddtInferReport ["consistSpec2b.f90"] "consistSpec2a.f90"
          successful r `shouldBe` True
    describe "error-finding" $ do
      describe "bad dimensions" $ do
        it "dim violates min bound" $ do
          r <- ddtInferReport [] "badDim1.f90"
          map (fmap (fst . head . S.toList)) (M.toList (ddtrBDE r)) `shouldBe` [(("example_d_common", 0), 0)]
        it "dim violates max bound" $ do
          r <- ddtInferReport [] "badDim2.f90"
          map (fmap (fst . head . S.toList)) (M.toList (ddtrBDE r)) `shouldBe` [(("example_d_common", 3), 2)]
      describe "bad labelling" $ do
        it "dupped index" $ do
          r <- ddtInferReport [] "dupIndex1.f90"
          S.size (ddtrIDE r) `shouldBe` 1
        it "out-of-bounds index" $ do
          r <- ddtInferReport [] "oobIndex1.f90"
          S.size (ddtrIDE r) `shouldBe` 1
        it "dupped label" $ do
          r <- ddtInferReport [] "badLabel1.f90"
          map (fmap (fst . head . S.toList)) (M.toList (ddtrBLE r)) `shouldBe` [(("example_d_common", 2), "label1")]
      describe "inconsistent specs" $ do
        it "differing type names" $ do
          r <- ddtInferReport ["inconsistSpec1b.f90"] "inconsistSpec1a.f90"
          map (fmap S.size) (M.toList (ddtrCE r)) `shouldBe` [(("inconsist1_d_common", 2), 2)]
        it "clashing label names" $ do
          r <- ddtInferReport ["inconsistSpec2b.f90"] "inconsistSpec2a.f90"
          map (fmap S.size) (M.toList (ddtrCE r)) `shouldBe` [(("inconsist2_d_common", 2), 2)]
        it "disagreeing on label names" $ do
          r <- ddtInferReport ["inconsistSpec3b.f90"] "inconsistSpec3a.f90"
          map (fmap S.size) (M.toList (ddtrCE r)) `shouldBe` [(("inconsist3_d_common", 2), 2)]
        it "when starred specs disagree" $ do
          r <- ddtInferReport ["inconsistSpec4b.f90"] "inconsistSpec4a.f90"
          map (fmap S.size) (M.toList (ddtrCE r)) `shouldBe` [(("inconsist4_d_common", 2), 2)]
  