-- ghc-mod: Making Haskell development *more* fun -- Copyright (C) 2015 Daniel Gröber -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU Affero General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU Affero General Public License for more details. -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . {-# LANGUAGE OverloadedStrings #-} module HomeModuleGraphSpec where import Language.Haskell.GhcMod.HomeModuleGraph import Language.Haskell.GhcMod.LightGhc import TestUtils import GHC import qualified Data.Map as Map import qualified Data.Set as Set import Data.Maybe import Test.Hspec runAGhc :: [GHCOption] -> (HscEnv -> LightGhc a) -> IO a runAGhc opts action = withLightHscEnv opts $ \env -> do runLightGhc env $ getSession >>= action hmGraph :: FilePath -> [String] -> String -> IO GmModuleGraph hmGraph dir opts mn = runAGhc opts $ \env -> liftIO $ do runD' dir $ do smp <- liftIO $ findModulePathSet env [mkModuleName mn] homeModuleGraph env smp uhmGraph :: FilePath -> [String] -> String -> String -> GmModuleGraph -> IO GmModuleGraph uhmGraph dir opts mn umn g = runAGhc opts $ \env -> liftIO $ do runD' dir $ do smp <- liftIO $ findModulePathSet env [mkModuleName mn] usmp <- liftIO $ findModulePathSet env [mkModuleName umn] updateHomeModuleGraph env g smp usmp mapMap :: (Ord k, Ord k') => (k -> k') -> (a -> a') -> Map.Map k a -> Map.Map k' a' mapMap fk fa = Map.mapKeys fk . Map.map fa mapMpFn :: (FilePath -> FilePath) -> ModulePath -> ModulePath mapMpFn f (ModulePath mn fn) = ModulePath mn (f fn) mp :: ModuleName -> ModulePath mp mn = ModulePath mn $ moduleNameString mn ++ ".hs" spec :: Spec spec = do describe "reachable" $ do let smp = Set.fromList [ mp "A" , mp "B" , mp "C" , mp "D" , mp "E" , mp "F" , mp "G" , mp "H" , mp "I" ] moduleMap = mkModuleMap smp completeGraph = Map.map (Set.map lookupMM) . Map.mapKeys lookupMM lookupMM = fromJust . flip Map.lookup moduleMap graph = completeGraph $ Map.fromList [ ("A", Set.fromList ["B"]) , ("B", Set.fromList ["C", "D"]) , ("C", Set.fromList ["F"]) , ("D", Set.fromList ["E"]) , ("E", Set.fromList []) , ("F", Set.fromList []) , ("G", Set.fromList []) , ("H", Set.fromList []) , ("I", Set.fromList []) ] really_reachable = Set.fromList [ mp "A" , mp "B" , mp "C" , mp "D" , mp "E" , mp "F" ] g = GmModuleGraph { gmgGraph = graph } it "reachable Set.empty g == Set.empty" $ do reachable Set.empty g `shouldBe` Set.empty it "lists only reachable nodes" $ do reachable (Set.fromList [mp "A"]) g `shouldBe` really_reachable describe "homeModuleGraph" $ do it "cycles don't break it" $ do let tdir = "test/data/home-module-graph/cycle" g <- hmGraph tdir [] "A" gmgGraph g `shouldBe` Map.fromList [ (mp "A", Set.fromList [mp "B"]) , (mp "B", Set.fromList [mp "A"]) ] it "follows imports" $ do let tdir = "test/data/home-module-graph/indirect" g <- hmGraph tdir [] "A" gmgGraph g `shouldBe` Map.fromList [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"]) , (mp "A1", Set.fromList [mp "B"]) , (mp "A2", Set.fromList [mp "C"]) , (mp "A3", Set.fromList [mp "B"]) , (mp "B", Set.fromList []) , (mp "C", Set.fromList []) ] it "returns partial results on parse errors" $ do let tdir = "test/data/home-module-graph/errors" g <- hmGraph tdir [] "A" gmgGraph g `shouldBe` Map.fromList [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"]) , (mp "A1", Set.fromList []) -- parse error here , (mp "A2", Set.fromList []) , (mp "A3", Set.fromList [mp "B"]) , (mp "B", Set.fromList []) ] it "returns partial results on CPP errors" $ do let tdir = "test/data/home-module-graph/cpp" g <- hmGraph tdir [] "A" gmgGraph g `shouldBe` Map.fromList [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"]) , (mp "A1", Set.fromList []) -- CPP error here , (mp "A2", Set.fromList []) , (mp "A3", Set.fromList [mp "B"]) , (mp "B", Set.fromList []) ] describe "updateHomeModuleGraph" $ do it "removes unreachable nodes" $ do let tdir = "test/data/home-module-graph/indirect" let tdir' = "test/data/home-module-graph/indirect-update" ig <- hmGraph tdir [] "A" g <- uhmGraph tdir' [] "A" "A2" ig gmgGraph g `shouldBe` Map.fromList [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"]) , (mp "A1", Set.fromList [mp "B"]) , (mp "A2", Set.fromList []) , (mp "A3", Set.fromList [mp "B"]) , (mp "B", Set.fromList []) -- C was removed ]