module TestUtils ( compareFiles , parsedFileGhc , parsedFileGhcCd , parseSourceFileTest , runLogTestGhc , runTestGhc , runRefactGhcState , runRefactGhcStateLog , initialState , initialLogOnState , showAnnDataFromState , showAnnDataItemFromState , showAnnsFromState , exactPrintFromState , sourceFromState , annsFromState , defaultTestSettings , logTestSettings , testOptions , catchException , mkTokenCache , hex , unspace , mkTestGhcName , setLogger , cdAndDo , ct , pwd , cd , parseToAnnotated , parseDeclToAnnotated , ss2span , PosToken , getHsDecls , showNameMap ) where import qualified DynFlags as GHC import qualified FastString as GHC import qualified GHC as GHC import qualified Name as GHC import qualified Outputable as GHC import qualified Unique as GHC import Data.Algorithm.Diff import Data.Algorithm.DiffOutput import Data.Data import Exception import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Annotate import Language.Haskell.GHC.ExactPrint.Parsers import Language.Haskell.GHC.ExactPrint.Types import Language.Haskell.GHC.ExactPrint.Utils import qualified Language.Haskell.GhcMod as GM import qualified Language.Haskell.GhcMod.Types as GM import Language.Haskell.Refact.Utils.Monad import Language.Haskell.Refact.Utils.MonadFunctions import Language.Haskell.Refact.Utils.Types import Language.Haskell.Refact.Utils.Utils import Numeric import System.Directory import System.Log.Handler.Simple import System.Log.Logger import qualified Data.Map as Map -- import Control.Monad.IO.Class -- --------------------------------------------------------------------- type PosToken = (GHC.Located GHC.Token, String) pwd :: IO FilePath pwd = getCurrentDirectory cd :: FilePath -> IO () cd = setCurrentDirectory -- --------------------------------------------------------------------- hex :: Int -> String hex v = "0x" ++ showHex v "" -- --------------------------------------------------------------------- compareFiles :: FilePath -> FilePath -> IO String compareFiles fileA fileB = do astr <- readFile fileA bstr <- readFile fileB let diffToString ds = case ppDiff ds of "\n" -> "" s -> s return $ diffToString $ getGroupedDiff (lines astr) (lines bstr) -- --------------------------------------------------------------------- parsedFileGhc :: String -> IO ParseResult parsedFileGhc fileName = do let comp = do -- logm $ "parsedFileGhc:fileName" ++ show fileName res <- parseSourceFileTest fileName -- logm $ "parsedFileGhc:done" return res (parseResult,_s) <- runRefactGhcStateLog comp Normal -- (parseResult,_s) <- runRefactGhcStateLog comp Debug return parseResult -- --------------------------------------------------------------------- parsedFileGhcCd :: FilePath -> FilePath -> IO ParseResult parsedFileGhcCd path fileName = do old <- getCurrentDirectory let comp = do res <- parseSourceFileTest fileName return res newDir = setCurrentDirectory path oldDir _ = setCurrentDirectory old (parseResult,_s) <- GHC.gbracket newDir oldDir $ \_ -> runRefactGhcState comp return parseResult -- --------------------------------------------------------------------- ct :: IO a -> IO a ct = cdAndDo "./test/testdata/" cdAndDo :: FilePath -> IO a -> IO a cdAndDo path fn = do old <- getCurrentDirectory r <- GHC.gbracket (setCurrentDirectory path) (\_ -> setCurrentDirectory old) $ \_ -> fn return r -- --------------------------------------------------------------------- parseSourceFileTest :: FilePath -> RefactGhc ParseResult parseSourceFileTest fileName = do parseSourceFileGhc fileName -- Load the file first getTypecheckedModule -- --------------------------------------------------------------------- initialState :: RefactState initialState = RefSt { rsSettings = defaultTestSettings , rsUniqState = 1 , rsSrcSpanCol = 1 , rsFlags = RefFlags False , rsStorage = StorageNone , rsCurrentTarget = Nothing , rsModule = Nothing } -- --------------------------------------------------------------------- initialLogOnState :: RefactState initialLogOnState = RefSt { rsSettings = logTestSettings , rsUniqState = 1 , rsSrcSpanCol = 1 , rsFlags = RefFlags False , rsStorage = StorageNone , rsCurrentTarget = Nothing , rsModule = Nothing } -- --------------------------------------------------------------------- mkTokenCache :: a -> TokenCache a mkTokenCache forest = TK (Map.fromList [((TId 0),forest)]) (TId 0) -- --------------------------------------------------------------------- runTestInternal :: RefactGhc a -> RefactState -> GM.Options -> IO (a, RefactState) runTestInternal comp st opts = runRefactGhc comp st opts -- --------------------------------------------------------------------- runLogTestGhc :: RefactGhc a -> IO (a, RefactState) runLogTestGhc comp = runTestInternal comp initialLogOnState testOptions -- --------------------------------------------------------------------- runTestGhc :: RefactGhc a -> IO (a, RefactState) runTestGhc comp = do runTestInternal comp initialState testOptions -- --------------------------------------------------------------------- runRefactGhcState :: RefactGhc t -> IO (t, RefactState) runRefactGhcState comp = runRefactGhcStateLog comp Normal -- --------------------------------------------------------------------- runRefactGhcStateLog :: RefactGhc t -> VerboseLevel -> IO (t, RefactState) runRefactGhcStateLog comp logOn = do let initState = RefSt { rsSettings = defaultTestSettings { rsetVerboseLevel = logOn } , rsUniqState = 1 , rsSrcSpanCol = 1 , rsFlags = RefFlags False , rsStorage = StorageNone , rsCurrentTarget = Nothing , rsModule = Nothing } -- putStrLn $ "runRefactGhcStateLog:initState=" ++ show initState -- putStrLn $ "runRefactGhcStateLog:testOptions=" ++ show testOptions runTestInternal comp initState testOptions -- --------------------------------------------------------------------- testOptions :: GM.Options -- testOptions = GM.defaultOptions { GM.ooptLogLevel = GM.GmError } testOptions = GM.defaultOptions { GM.optOutput = GM.OutputOpts { GM.ooptLogLevel = GM.GmError -- GM.ooptLogLevel = GM.GmVomit , GM.ooptStyle = GM.PlainStyle , GM.ooptLineSeparator = GM.LineSeparator "\0" , GM.ooptLinePrefix = Nothing } } -- --------------------------------------------------------------------- defaultTestSettings :: RefactSettings defaultTestSettings = defaultSettings { rsetVerboseLevel = Normal } logTestSettings :: RefactSettings logTestSettings = defaultSettings { rsetVerboseLevel = Debug } -- --------------------------------------------------------------------- catchException :: (IO t) -> IO (Maybe String) catchException f = do res <- handle handler (f >> return Nothing) return res where handler:: SomeException -> IO (Maybe String) handler e = return (Just (stripCallStack $ show e)) -- --------------------------------------------------------------------- showAnnDataFromState :: RefactState -> String showAnnDataFromState st = case rsModule st of Just tm -> r where anns = tkCache (rsTokenCache tm) Map.! mainTid parsed = GHC.pm_parsed_source $ GHC.tm_parsed_module $ rsTypecheckedMod tm r = showAnnData anns 0 parsed Nothing -> [] -- --------------------------------------------------------------------- showAnnDataItemFromState :: (Data a) => RefactState -> a -> String showAnnDataItemFromState st t = case rsModule st of Just tm -> r where anns = tkCache (rsTokenCache tm) Map.! mainTid r = showAnnData anns 0 t Nothing -> [] -- --------------------------------------------------------------------- showAnnsFromState :: RefactState -> String showAnnsFromState st = case rsModule st of Just tm -> r where anns = tkCache (rsTokenCache tm) Map.! mainTid r = showGhc anns Nothing -> [] -- --------------------------------------------------------------------- exactPrintFromState :: (Annotate a) => RefactState -> GHC.Located a -> String exactPrintFromState st ast = case rsModule st of Just tm -> r where -- anns = tkCache (rsTokenCache tm) Map.! mainTid anns = case Map.lookup mainTid (tkCache (rsTokenCache tm)) of Just a -> a Nothing -> error $ "exactPrintFromState:mainTid not found" r = exactPrint ast anns Nothing -> [] -- --------------------------------------------------------------------- sourceFromState :: RefactState -> String sourceFromState st = case rsModule st of Just tm -> r where anns = tkCache (rsTokenCache tm) Map.! mainTid parsed = GHC.pm_parsed_source $ GHC.tm_parsed_module $ rsTypecheckedMod tm r = exactPrint parsed anns Nothing -> [] -- --------------------------------------------------------------------- annsFromState :: RefactState -> Anns annsFromState st = case rsModule st of Just tm -> tkCache (rsTokenCache tm) Map.! mainTid Nothing -> error $ "annsFromState: no rsModule" -- --------------------------------------------------------------------- setLogger :: IO () setLogger = do {- h <- fileHandler "debug.log" DEBUG >>= \lh -> return $ setFormatter lh (simpleLogFormatter "[$time : $loggername : $prio] $msg") updateGlobalLogger "MyApp.BuggyComponent" (addHandler h) -} -- s <- streamHandler stdout DEBUG h <- fileHandler "debug.log" DEBUG updateGlobalLogger rootLoggerName (setHandlers [h]) -- --------------------------------------------------------------------- -- |Convert any sequence of more than one space to a single space unspace :: String -> String unspace str = go [] str where go acc [] = acc go acc [x] = acc ++ [x] go acc (' ':' ':xs) = go acc (' ':xs) go acc (x:xs) = go (acc++[x]) xs -- --------------------------------------------------------------------- mkTestGhcName :: Int -> Maybe GHC.Module -> String -> GHC.Name mkTestGhcName u maybeMod name = n where un = GHC.mkUnique 'H' (u+1) -- H for HaRe :) n = case maybeMod of Nothing -> GHC.localiseName $ GHC.mkSystemName un (GHC.mkVarOcc name) Just modu -> GHC.mkExternalName un modu (GHC.mkVarOcc name) nullSrcSpan nullSrcSpan :: GHC.SrcSpan nullSrcSpan = GHC.UnhelpfulSpan $ GHC.mkFastString "HaRe nullSrcSpan" -- --------------------------------------------------------------------- parseToAnnotated :: (Show a, Annotate ast) => GHC.DynFlags -> FilePath -> (GHC.DynFlags -> FilePath -> String -> Either a (Anns, GHC.Located ast)) -> String -> (GHC.Located ast, Anns) parseToAnnotated df fp parser src = (ast,anns) where (anns, ast) = case (parser df fp src) of Right xs -> xs Left err -> error (show err) -- --------------------------------------------------------------------- parseDeclToAnnotated :: GHC.DynFlags -> FilePath -- -> (GHC.DynFlags -> FilePath -> String -> Either a (Anns, GHC.Located ast)) -> String -> (GHC.LHsDecl GHC.RdrName, Anns) parseDeclToAnnotated df fp src = (ast,anns) where (anns, ast) = case (parseDecl df fp src) of Right xs -> xs Left err -> error (show err) -- --------------------------------------------------------------------- ss2span :: GHC.SrcSpan -> (Pos,Pos) ss2span ss = (ss2pos ss,ss2posEnd ss) -- --------------------------------------------------------------------- -- | call ghc-excactprint hsDecls in a Transform context getHsDecls :: (HasDecls t) => t -> [GHC.LHsDecl GHC.RdrName] getHsDecls t = decls where -- runTransform :: Anns -> Transform a -> (a, (Anns, Int), [String]) (decls,_,_) = runTransform mempty (hsDecls t) -- --------------------------------------------------------------------- showNameMap :: NameMap -> String showNameMap nm = GHC.showSDocDebug GHC.unsafeGlobalDynFlags doc where doc = GHC.text "NameMap" GHC.<+> GHC.vcat (map one $ Map.toList nm) -- one (s,n) = GHC.parens $ GHC.text (showGhc (s,n,GHC.nameUnique n)) GHC.<+> GHC.ppr n one (s,n) = GHC.parens $ GHC.hsep [GHC.ppr s, GHC.comma, GHC.ppr n] -- --------------------------------------------------------------------- -- EOF