aboutsummaryrefslogblamecommitdiff
path: root/test/Main.hs
blob: d0801357c5906733f91cfd93f3b9766e0289e0c1 (plain) (tree)



















                                                 
                                 

































                                                                            
                                                             



















































                                                                                
 




























                                                                              
                                        


                                                                    

                                                                                                         
                                                 




                                                                                                                           
         
                                                                                            
      


                                                                      
                        
                                        
                 


                                                
                                                

                                                

                                                 

                                                 

























                                                                              
                                                                           













                                                                                             
 
















































                                                                                                 
                                                                                                 



                                                                                                   
                                                                                                   




































































































































































































                                                                                                    
                                                                 








































































































































































































                                                                              
 













                                                     
                                                          











































                                                                               
                                                          






































                                                                          
                                                          





























                                                                               
                                                          












































                                                                             
                                                          





























                                                                               
                                                          































                                                                             
                                                          





































                                                                                
                                                          





























                                                                          
                                                           





























                                                                          
                                                          







































                                                                               
                                                           
























































                                                                            
                                                          





























                                                                           
                                                          





























                                                                          
                                                          

























                                                                            

 
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}

module Main where

import Control.Exception (try)
import Control.Monad.Logger (runLoggingT)
import qualified Data.ByteString as BS
import qualified Data.Generics.Uniplate.Data as U
import qualified Data.HashMap.Strict as HM
import qualified Data.IntMap as IM
import qualified Data.IntervalMap.Strict as IVM
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Vector as V
import Data.Version (makeVersion)
import HaskellCodeExplorer.AST.TypecheckedSource (removeOverlappingInterval)
import HaskellCodeExplorer.PackageInfo
import HaskellCodeExplorer.Preprocessor
import HaskellCodeExplorer.Types
import qualified HaskellCodeExplorer.Types as HCE
import System.Directory
  ( findExecutable
  , getCurrentDirectory
  , withCurrentDirectory
  )
import System.FilePath ((</>))
import System.Process (readProcess)
import Test.Hspec

testPackageDir :: FilePath
testPackageDir = "test/test-package"

main :: IO ()
main = do
  currentDir <- getCurrentDirectory
  hspec $ do
    packageInfoSpec currentDir
    sourceCodeTransformationSpec currentDir
    tokenizeSpec
    removeOverlappingIntervalSpec

packageInfoSpec :: FilePath -> Spec
packageInfoSpec currentDir = do
  eitherPackageInfo <- runIO $ buildAndIndexTestPackage currentDir
  case eitherPackageInfo of
    Right packageInfo -> do
      describe "createPackageInfo" $ do
        it "returns valid package id" $
          HCE.id (packageInfo :: PackageInfo ModuleInfo) `shouldBe`
          PackageId "test-package" (makeVersion [0, 1, 0, 0])
        it "returns valid list of module paths" $ do
          let paths =
                HM.fromList
                  [ ( HaskellModulePath {getHaskellModulePath = "app/Main.hs"}
                    , ())
                  , ( HaskellModulePath {getHaskellModulePath = "src/Types.hs"}
                    , ())
                  , ( HaskellModulePath {getHaskellModulePath = "test/Spec.hs"}
                    , ())
                  , ( HaskellModulePath {getHaskellModulePath = "src/Lib.hs"}
                    , ())
                  ]
          (HM.map (const ()))
            (HCE.moduleMap (packageInfo :: PackageInfo ModuleInfo)) `shouldBe`
            paths
        it "returns valid list of module names" $ do
          let names =
                HM.fromList
                  [ ( HaskellModuleName {getHaskellModuleName = "Types"}
                    , HM.fromList
                        [ ( ComponentId {getComponentId = "lib"}
                          , HaskellModulePath
                              {getHaskellModulePath = "src/Types.hs"})
                        ])
                  , ( HaskellModuleName {getHaskellModuleName = "Main"}
                    , HM.fromList
                        [ ( ComponentId
                              {getComponentId = "exe-test-package-exe"}
                          , HaskellModulePath
                              {getHaskellModulePath = "app/Main.hs"})
                        , ( ComponentId
                              {getComponentId = "test-test-package-test"}
                          , HaskellModulePath
                              {getHaskellModulePath = "test/Spec.hs"})
                        ])
                  , ( HaskellModuleName {getHaskellModuleName = "Lib"}
                    , HM.fromList
                        [ ( ComponentId {getComponentId = "lib"}
                          , HaskellModulePath
                              {getHaskellModulePath = "src/Lib.hs"})
                        ])
                  ]
          (HCE.moduleNameMap (packageInfo :: PackageInfo ModuleInfo)) `shouldBe`
            names
      let mbModuleInfo =
            HM.lookup
              (HCE.HaskellModulePath "src/Lib.hs")
              (moduleMap (packageInfo :: HCE.PackageInfo HCE.ModuleInfo))
      case mbModuleInfo of
        Just modInfo -> moduleInfoSpec modInfo
        Nothing -> return ()
    Left e -> runIO $ putStrLn e >> return ()

moduleInfoSpec :: ModuleInfo -> Spec
moduleInfoSpec modInfo =
  describe "createModuleInfo" $ do
    it "returns valid module name" $
      HCE.name (modInfo :: HCE.ModuleInfo) `shouldBe`
      HCE.HaskellModuleName "Lib"
    it "returns valid list of declarations " $
      HCE.declarations (modInfo :: HCE.ModuleInfo) `shouldBe` testDeclarations
    it "returns valid source code " $ do
      let sourceCodeLines =
            V.fromList
              [ "module Lib"
              , "    ( someFunc"
              , "    ) where"
              , ""
              , "import Types(Test(..))"
              , ""
              , "-- | someFunc documentation"
              , "someFunc :: IO ()"
              , "someFunc = putStrLn \"someFunc\""
              , ""
              , "-- | mkTest documentation"
              , "mkTest :: Int -> Test"
              , "mkTest i = Test i"
              , ""
              ]
      HCE.source (modInfo :: HCE.ModuleInfo) `shouldBe` sourceCodeLines
    it "returns valid map of expressions" $
      HCE.exprInfoMap (modInfo :: HCE.ModuleInfo) `shouldBe` testExprInfoMap
#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)
    it "returns valid map of identifiers " $
      let removeLocationInfo :: HCE.LocationInfo -> HCE.LocationInfo
          removeLocationInfo _ = HCE.UnknownLocation ""
          removePackageVersionFromExternalId :: HCE.ExternalId -> HCE.ExternalId
          removePackageVersionFromExternalId extId@(HCE.ExternalId textId) = case T.splitOn "|" textId of
            pId:rest -> case T.splitOn "-" pId of
              packageIdParts@(_:_) -> HCE.ExternalId $ T.intercalate "|" ((T.intercalate "-" (init packageIdParts)) : rest)
              _ -> extId
            _ ->  extId
          cleanup :: HCE.IdentifierInfoMap -> HCE.IdentifierInfoMap
          cleanup = U.transformBi removeLocationInfo . U.transformBi removePackageVersionFromExternalId
       in
        cleanup (HCE.idInfoMap (modInfo :: HCE.ModuleInfo)) `shouldBe` cleanup testIdInfoMap
#endif
    it "returns valid map of identifier occurrences" $
      HCE.idOccMap (modInfo :: HCE.ModuleInfo) `shouldBe` testIdOccMap

stackYamlArg :: [String]
#if MIN_VERSION_GLASGOW_HASKELL(8,6,5,0)
stackYamlArg = []
#elif MIN_VERSION_GLASGOW_HASKELL(8,6,4,0)
stackYamlArg = ["--stack-yaml=stack-8.6.4.yaml"]
#elif MIN_VERSION_GLASGOW_HASKELL(8,6,3,0)
stackYamlArg = ["--stack-yaml=stack-8.6.3.yaml"]
#elif MIN_VERSION_GLASGOW_HASKELL(8,4,4,0)
stackYamlArg = ["--stack-yaml=stack-8.4.4.yaml"]
#elif MIN_VERSION_GLASGOW_HASKELL(8,4,3,0)
stackYamlArg = ["--stack-yaml=stack-8.4.3.yaml" ]
#elif MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)
stackYamlArg = ["--stack-yaml=stack-8.2.2.yaml" ]
#else
stackYamlArg = ["--stack-yaml=stack-8.0.2.yaml" ]
#endif

buildAndIndexTestPackage ::
     FilePath -> IO (Either String (PackageInfo ModuleInfo))
buildAndIndexTestPackage currentDir = do
  mbStackExecutable <- findExecutable "stack"
  case mbStackExecutable of
    Just stackExecutable -> do
      let removeEndOfLine str
            | null str = str
            | otherwise = init str
      (eitherDistDir :: Either IOError String) <-
        try .
        fmap removeEndOfLine .
        readProcess stackExecutable (["path", "--dist-dir"] ++ stackYamlArg) $
        ""
      case eitherDistDir of
        Right distDir -> do
          packageInfo <-
            let testPackageDirectoryPath = currentDir </> testPackageDir
             in withCurrentDirectory testPackageDirectoryPath $ do
                  _ <-
                    readProcess
                      stackExecutable
                      (["build", "--test","--force-dirty"] ++ stackYamlArg)
                      ""
                  runLoggingT
                    (createPackageInfo
                       testPackageDirectoryPath
                       (Just distDir)
                       HCE.BeforePreprocessing
                       []
                       [])
                    (\_ _ _ _ -> return ())
          return . Right $ packageInfo
        Left ex -> return . Left $ show ex
    Nothing ->
      return . Left $
      "Cannot find stack executable. Stack executable is required to build the test package."

sourceCodeTransformationSpec :: FilePath -> Spec
sourceCodeTransformationSpec currentDir = do
  sourceCodeAfterPreprocessor <-
    runIO $ BS.readFile $ currentDir </> "test/data/FileAfterPreprocessor.hs"
  let sourceCodeTransformation =
        fst $
        createSourceCodeTransformation
          (HaskellModulePath "File.hs")
          (TE.decodeUtf8 sourceCodeAfterPreprocessor)
          (TE.decodeUtf8 sourceCodeAfterPreprocessor)
  describe "createSourceCodeTransformation" $ do
    it "returns valid list of pragmas" $ do
      let pragmas =
            S.fromList
              [ LinePragma (HaskellFilePath "File1.hs") 8 1
              , LinePragma (HaskellFilePath "File1.hs") 11 14
              , LinePragma (HaskellFilePath "File2.hs") 12 1
              , LinePragma (HaskellFilePath "File1.hs") 18 15
              , LinePragma (HaskellFilePath "File.hs") 21 9
              , LinePragma (HaskellFilePath "File.hs") 23 18
              , LinePragma (HaskellFilePath "File3.hs") 26 1
              , LinePragma (HaskellFilePath "File.hs") 30 21
              ]
      linePragmas sourceCodeTransformation `shouldBe` pragmas
    it "returns valid file index" $
      let index =
            HM.fromList
              [ ( HaskellFilePath {getHaskellFilePath = "File1.hs"}
                , S.fromList
                    [ FileLocation {lineStart = 1, lineEnd = 2, offset = 8}
                    , FileLocation {lineStart = 15, lineEnd = 16, offset = 4}
                    ])
              , ( HaskellFilePath {getHaskellFilePath = "File2.hs"}
                , S.fromList
                    [FileLocation {lineStart = 1, lineEnd = 5, offset = 12}])
              , ( HaskellFilePath {getHaskellFilePath = "File3.hs"}
                , S.fromList
                    [FileLocation {lineStart = 1, lineEnd = 3, offset = 26}])
              , ( HaskellFilePath {getHaskellFilePath = "File.hs"}
                , S.fromList
                    [ FileLocation {lineStart = 1, lineEnd = 8, offset = 0}
                    , FileLocation {lineStart = 9, lineEnd = 9, offset = 13}
                    , FileLocation {lineStart = 18, lineEnd = 19, offset = 6}
                    , FileLocation {lineStart = 21, lineEnd = 21, offset = 10}
                    ])
              ]
       in fileIndex sourceCodeTransformation `shouldBe` index
  describe "fromOriginalLineNumber" $ do
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File.hs", 1) (Right 1)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File.hs", 4) (Right 4)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File1.hs", 1) (Right 9)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File1.hs", 2) (Right 10)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File2.hs", 1) (Right 13)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File2.hs", 3) (Right 15)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File2.hs", 5) (Right 17)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File1.hs", 15) (Right 19)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File1.hs", 16) (Right 20)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File3.hs", 1) (Right 27)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File3.hs", 2) (Right 28)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File3.hs", 3) (Right 29)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File.hs", 9) (Right 22)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File.hs", 18) (Right 24)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File.hs", 19) (Right 25)
    test fromOriginalLineNumber sourceCodeTransformation (HaskellFilePath "File.hs", 21) (Right 31)

test ::
     forall a t t1. (Show t1, Show t, Show a, Eq a)
  => (t1 -> t -> a)
  -> t1
  -> t
  -> a
  -> Spec
test fun arg1 arg2 result =
  it (show arg1 ++ " " ++ show arg2) $ fun arg1 arg2 `shouldBe` result

tokenizeSpec :: Spec
tokenizeSpec =
  describe "tokenize" $ do
    test tokenize "" ([] :: [((Int, Int), String)]) []
    test
      tokenize
      "--comment"
      ([] :: [((Int, Int), String)])
      [("--comment", (1, 10), Nothing)]
    test
      tokenize
      ("abcd" :: T.Text)
      [((1, 5), "abcdIdentifier")]
      [("abcd", (1, 5), Just "abcdIdentifier")]
    test
      tokenize
      ("data Test" :: T.Text)
      [((6, 10), "TestIdentifier")]
      [("data ", (1, 6), Nothing), ("Test", (6, 10), Just "TestIdentifier")]
    test
      tokenize
      ("abc = xyz" :: T.Text)
      [((1, 4), "abcIdentifier"), ((7, 10), "xyzIdentifier")]
      [ ("abc", (1, 4), Just "abcIdentifier")
      , (" = ", (4, 7), Nothing)
      , ("xyz", (7, 10), Just "xyzIdentifier")
      ]
    test
      tokenize
      ("abc = xyz --comment" :: T.Text)
      [((1, 4), "abcIdentifier"), ((7, 10), "xyzIdentifier")]
      [ ("abc", (1, 4), Just "abcIdentifier")
      , (" = ", (4, 7), Nothing)
      , ("xyz", (7, 10), Just "xyzIdentifier")
      , (" --comment", (10, 20), Nothing)
      ]
    test
      tokenize
      ("   abc = xyz --comment" :: T.Text)
      [((4, 7), "abcIdentifier"), ((10, 13), "xyzIdentifier")]
      [ ("   ", (1, 4), Nothing)
      , ("abc", (4, 7), Just "abcIdentifier")
      , (" = ", (7, 10), Nothing)
      , ("xyz", (10, 13), Just "xyzIdentifier")
      , (" --comment", (13, 23), Nothing)
      ]

removeOverlappingIntervalSpec :: Spec
removeOverlappingIntervalSpec =
  describe "removeOverlappingInterval" $ do
    test removeOverlappingInterval [((1, 2), "a")] [] [((1, 2), "a")]
    test
      removeOverlappingInterval
      [((3, 4), "b")]
      [((1, 2), "a")]
      [((1, 2), "a"), ((3, 4), "b")]
    test
      removeOverlappingInterval
      [((2, 3), "b")]
      [((1, 5), "a")]
      [((2, 3), "b")]
    test
      removeOverlappingInterval
      [((1, 5), "b")]
      [((2, 3), "a")]
      [((2, 3), "a")]
    test
      removeOverlappingInterval
      [((1, 5), "b")]
      [((2, 7), "a")]
      [((1, 5), "b")]
    test
      removeOverlappingInterval
      [((1, 3), "b")]
      [((1, 3), "a")]
      [((1, 3), "a")]

testDeclarations :: [Declaration]
testDeclarations =
  [ Declaration
      { sort = ValD
      , name = "someFunc"
      , declType =
          Just
            (Type
               { components =
                   [ TyCon {internalId = HCE.InternalId "0", name = "IO"}
                   , Text " ()"
                   ]
               , componentsExpanded = Nothing
               })
      , isExported = True
      , lineNumber = 9
      }
  , Declaration
      { sort = ValD
      , name = "mkTest"
      , declType =
          Just
            (Type
               { components =
                   [ TyCon {internalId = HCE.InternalId "9", name = "Int"}
                   , Text " -> "
                   , TyCon {internalId = HCE.InternalId "1", name = "Test"}
                   ]
               , componentsExpanded = Nothing
               })
      , isExported = False
      , lineNumber = 13
      }
  ]

testExprInfoMap :: IVM.IntervalMap (Int,Int) HCE.ExpressionInfo
testExprInfoMap =
  IVM.fromList
    [ ( IVM.OpenInterval (9, 12) (9, 31)
      , ExpressionInfo
          { description = "HsApp"
          , exprType =
              Just
                (Type
                   { components =
                       [ TyCon {internalId = InternalId {getInternalId = "0"}, name = "IO"}
                       , Text " ()"
                       ]
                   , componentsExpanded = Nothing
                   })
          })
    , ( IVM.OpenInterval (13, 12) (13, 18)
      , ExpressionInfo
          { description = "HsApp"
          , exprType =
              Just
                (Type
                   { components =
                       [ TyCon
                           {internalId = InternalId {getInternalId = "1"}, name = "Test"}
                       ]
                   , componentsExpanded = Nothing
                   })
          })
    ]


testIdOccMap :: IM.IntMap [((Int, Int), IdentifierOccurrence)]
testIdOccMap =
  IM.fromList
    [ ( 2
      , [ ( (7, 15)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "2"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "3"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "IE"
              , sort = ValueId
              })
        ])
    , ( 5
      , [ ( (8, 13)
          , IdentifierOccurrence
              { internalId = Nothing
              , internalIdFromRenamedSource = Nothing
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "ImportDecl"
              , sort =
                  ModuleId
                    (ExactLocation
                       { packageId =
                           PackageId
                             { name = "test-package"
                             , version = makeVersion [0, 1, 0, 0]
                             }
                       , modulePath =
                           HaskellModulePath
                             {getHaskellModulePath = "src/Types.hs"}
                       , moduleName =
                           HaskellModuleName {getHaskellModuleName = "Types"}
                       , startLine = 1
                       , endLine = 1
                       , startColumn = 1
                       , endColumn = 1
                       })
              })
        , ( (14, 18)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "1"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "1"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "IE"
              , sort = TypeId
              })
        ])
    , ( 8
      , [ ( (1, 9)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "2"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "3"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "TypeSig"
              , sort = ValueId
              })
        , ( (13, 15)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "0"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "0"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsTyVar"
              , sort = TypeId
              })
        , ( (16, 18)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "4"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "4"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsTupleTy"
              , sort = TypeId
              })
        ])
    , ( 9
      , [ ( (1, 9)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "2"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "3"})
              , isBinder = True
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "Match"
              , sort = ValueId
              })
        , ( (12, 20)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "5"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "5"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsVar"
              , sort = ValueId
              })
        , ( (21, 31)
          , IdentifierOccurrence
              { internalId = Nothing
              , internalIdFromRenamedSource = Nothing
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType =
                  Just
                    (Type
                       { components =
                           [ Text "["
                           , TyCon
                               { internalId = InternalId {getInternalId = "6"}
                               , name = "Char"
                               }
                           , Text "]"
                           ]
                       , componentsExpanded = Nothing
                       })
              , typeArguments = Nothing
              , description = "HsLit"
              , sort = ValueId
              })
        ])
    , ( 12
      , [ ( (1, 7)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "7"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "8"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "TypeSig"
              , sort = ValueId
              })
        , ( (11, 14)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "9"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "9"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsTyVar"
              , sort = TypeId
              })
        , ( (18, 22)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "1"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "1"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsTyVar"
              , sort = TypeId
              })
        ])
    , ( 13
      , [ ( (1, 7)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "7"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "8"})
              , isBinder = True
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "Match"
              , sort = ValueId
              })
        , ( (8, 9)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "10"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "11"})
              , isBinder = True
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "VarPat"
              , sort = ValueId
              })
        , ( (12, 16)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "12"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "13"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsVar"
              , sort = ValueId
              })
        , ( (17, 18)
          , IdentifierOccurrence
              { internalId = Just (InternalId {getInternalId = "10"})
              , internalIdFromRenamedSource =
                  Just (InternalId {getInternalId = "11"})
              , isBinder = False
              , instanceResolution = Nothing
              , idOccType = Nothing
              , typeArguments = Nothing
              , description = "HsVar"
              , sort = ValueId
              })
        ])
    ]

testIdInfoMap :: HM.HashMap InternalId IdentifierInfo
testIdInfoMap =
  HM.fromList
    [ ( InternalId {getInternalId = "7"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "mkTest"}
          , demangledOccName = "mkTest"
          , nameSpace = VarName
          , locationInfo =
              ExactLocation
                { packageId =
                    PackageId
                      { name = "test-package"
                      , version = makeVersion [0, 1, 0, 0]
                      }
                , modulePath =
                    HaskellModulePath {getHaskellModulePath = "src/Lib.hs"}
                , moduleName = HaskellModuleName {getHaskellModuleName = "Lib"}
                , startLine = 13
                , endLine = 13
                , startColumn = 1
                , endColumn = 7
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "9"}
                        , name = "Int"
                        }
                    , Text " -> "
                    , TyCon
                        { internalId = InternalId {getInternalId = "1"}
                        , name = "Test"
                        }
                    ]
                , componentsExpanded = Nothing
                }
          , details = Just VanillaId
          , doc = Just "<p><span>mkTest documentation</span></p>"
          , internalId = InternalId {getInternalId = "7"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "test-package-0.1.0.0|Lib|Val|mkTest"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "14"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "TYPE"}
          , demangledOccName = "TYPE"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Prim"}
                , entity = Typ
                , name = "TYPE"
                , haddockAnchorId = Just "TYPE"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "15"}
                        , name = "RuntimeRep"
                        }
                    , Text " -> *"
                    ]
                , componentsExpanded = Nothing
                }
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "14"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Prim|Typ|TYPE"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "0"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "IO"}
          , demangledOccName = "IO"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Types"}
                , entity = Typ
                , name = "IO"
                , haddockAnchorId = Just "IO"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type {components = [Text "* -> *"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "0"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Types|Typ|IO"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "12"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "Test"}
          , demangledOccName = "Test"
          , nameSpace = DataName
          , locationInfo =
              ExactLocation
                { packageId =
                    PackageId
                      { name = "test-package"
                      , version = makeVersion [0, 1, 0, 0]
                      }
                , modulePath =
                    HaskellModulePath {getHaskellModulePath = "src/Types.hs"}
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "Types"}
                , startLine = 5
                , endLine = 5
                , startColumn = 13
                , endColumn = 21
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "9"}
                        , name = "Int"
                        }
                    , Text " -> "
                    , TyCon
                        { internalId = InternalId {getInternalId = "1"}
                        , name = "Test"
                        }
                    ]
                , componentsExpanded = Nothing
                }
          , details = Just DataConWorkId
          , doc = Nothing
          , internalId = InternalId {getInternalId = "12"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "test-package-0.1.0.0|Types|Val|Test"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "17"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "[]"}
          , demangledOccName = "[]"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Types"}
                , entity = Typ
                , name = "[]"
                , haddockAnchorId = Just "-91--93-"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type {components = [Text "* -> *"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "17"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Types|Typ|[]"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "1"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "Test"}
          , demangledOccName = "Test"
          , nameSpace = TcClsName
          , locationInfo =
              ExactLocation
                { packageId =
                    PackageId
                      { name = "test-package"
                      , version = makeVersion [0, 1, 0, 0]
                      }
                , modulePath =
                    HaskellModulePath {getHaskellModulePath = "src/Types.hs"}
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "Types"}
                , startLine = 5
                , endLine = 5
                , startColumn = 1
                , endColumn = 21
                }
          , idType =
              Type {components = [Text "*"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "1"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "test-package-0.1.0.0|Types|Typ|Test"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "18"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "LiftedRep"}
          , demangledOccName = "LiftedRep"
          , nameSpace = DataName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Types"}
                , entity = Val
                , name = "LiftedRep"
                , haddockAnchorId = Just "LiftedRep"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "15"}
                        , name = "RuntimeRep"
                        }
                    ]
                , componentsExpanded = Nothing
                }
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "18"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Types|Val|LiftedRep"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "4"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "()"}
          , demangledOccName = "()"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Tuple"}
                , entity = Typ
                , name = "()"
                , haddockAnchorId = Just "-40--41-"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type {components = [Text "*"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "4"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Tuple|Typ|()"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "16"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "String"}
          , demangledOccName = "String"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "base"
                      , version = makeVersion [4, 10, 1, 0]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Base"}
                , entity = Typ
                , name = "String"
                , haddockAnchorId = Just "String"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type {components = [Text "*"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "16"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "base-4.10.1.0|GHC.Base|Typ|String"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "2"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "someFunc"}
          , demangledOccName = "someFunc"
          , nameSpace = VarName
          , locationInfo =
              ExactLocation
                { packageId =
                    PackageId
                      { name = "test-package"
                      , version = makeVersion [0, 1, 0, 0]
                      }
                , modulePath =
                    HaskellModulePath {getHaskellModulePath = "src/Lib.hs"}
                , moduleName = HaskellModuleName {getHaskellModuleName = "Lib"}
                , startLine = 9
                , endLine = 9
                , startColumn = 1
                , endColumn = 9
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "0"}
                        , name = "IO"
                        }
                    , Text " ()"
                    ]
                , componentsExpanded = Nothing
                }
          , details = Just VanillaId
          , doc = Just "<p><span>someFunc documentation</span></p>"
          , internalId = InternalId {getInternalId = "2"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "test-package-0.1.0.0|Lib|Val|someFunc"})
          , isExported = True
          })
    , ( InternalId {getInternalId = "5"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "putStrLn"}
          , demangledOccName = "putStrLn"
          , nameSpace = VarName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "base"
                      , version = makeVersion [4, 10, 1, 0]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "System.IO"}
                , entity = Val
                , name = "putStrLn"
                , haddockAnchorId = Just "putStrLn"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "16"}
                        , name = "String"
                        }
                    , Text " -> "
                    , TyCon
                        { internalId = InternalId {getInternalId = "0"}
                        , name = "IO"
                        }
                    , Text " ()"
                    ]
                , componentsExpanded =
                    Just
                      [ Text "["
                      , TyCon
                          { internalId = InternalId {getInternalId = "6"}
                          , name = "Char"
                          }
                      , Text "] -> "
                      , TyCon
                          { internalId = InternalId {getInternalId = "0"}
                          , name = "IO"
                          }
                      , Text " ()"
                      ]
                }
          , details = Just VanillaId
          , doc = Nothing
          , internalId = InternalId {getInternalId = "5"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "base-4.10.1.0|System.IO|Val|putStrLn"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "6"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "Char"}
          , demangledOccName = "Char"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Types"}
                , entity = Typ
                , name = "Char"
                , haddockAnchorId = Just "Char"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type {components = [Text "*"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "6"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Types|Typ|Char"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "9"}
      , IdentifierInfo
          { sort = External
          , occName = OccName {getOccName = "Int"}
          , demangledOccName = "Int"
          , nameSpace = TcClsName
          , locationInfo =
              ApproximateLocation
                { packageId =
                    PackageId
                      { name = "ghc-prim"
                      , version = makeVersion [0, 5, 1, 1]
                      }
                , moduleName =
                    HaskellModuleName {getHaskellModuleName = "GHC.Types"}
                , entity = Typ
                , name = "Int"
                , haddockAnchorId = Just "Int"
                , componentId = ComponentId {getComponentId = "lib"}
                }
          , idType =
              Type {components = [Text "*"], componentsExpanded = Nothing}
          , details = Nothing
          , doc = Nothing
          , internalId = InternalId {getInternalId = "9"}
          , externalId =
              Just
                (ExternalId
                   {getExternalId = "ghc-prim-0.5.1.1|GHC.Types|Typ|Int"})
          , isExported = False
          })
    , ( InternalId {getInternalId = "10"}
      , IdentifierInfo
          { sort = Internal
          , occName = OccName {getOccName = "i"}
          , demangledOccName = "i"
          , nameSpace = VarName
          , locationInfo =
              ExactLocation
                { packageId =
                    PackageId
                      { name = "test-package"
                      , version = makeVersion [0, 1, 0, 0]
                      }
                , modulePath =
                    HaskellModulePath {getHaskellModulePath = "src/Lib.hs"}
                , moduleName = HaskellModuleName {getHaskellModuleName = ""}
                , startLine = 13
                , endLine = 13
                , startColumn = 8
                , endColumn = 9
                }
          , idType =
              Type
                { components =
                    [ TyCon
                        { internalId = InternalId {getInternalId = "9"}
                        , name = "Int"
                        }
                    ]
                , componentsExpanded = Nothing
                }
          , details = Just VanillaId
          , doc = Nothing
          , internalId = InternalId {getInternalId = "10"}
          , externalId = Nothing
          , isExported = False
          })
    ]