mirror of
https://github.com/haskell/ghcide.git
synced 2025-01-07 10:39:40 +03:00
Add tests for find-definition and hover (#139)
* Add find definition tests * Add tests for hovers
This commit is contained in:
parent
9d45eee21c
commit
2779dbb2e1
@ -188,6 +188,7 @@ test-suite ghcide-tests
|
|||||||
parser-combinators,
|
parser-combinators,
|
||||||
tasty,
|
tasty,
|
||||||
tasty-hunit,
|
tasty-hunit,
|
||||||
|
tasty-expected-failure,
|
||||||
text
|
text
|
||||||
hs-source-dirs: test/cabal test/exe test/src
|
hs-source-dirs: test/cabal test/exe test/src
|
||||||
include-dirs: include
|
include-dirs: include
|
||||||
|
135
test/exe/Main.hs
135
test/exe/Main.hs
@ -20,6 +20,7 @@ import System.IO.Extra
|
|||||||
import System.Directory
|
import System.Directory
|
||||||
import Test.Tasty
|
import Test.Tasty
|
||||||
import Test.Tasty.HUnit
|
import Test.Tasty.HUnit
|
||||||
|
import Test.Tasty.ExpectedFailure
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
@ -31,9 +32,9 @@ main = defaultMain $ testGroup "HIE"
|
|||||||
void (message :: Session ProgressDoneNotification)
|
void (message :: Session ProgressDoneNotification)
|
||||||
, diagnosticTests
|
, diagnosticTests
|
||||||
, codeActionTests
|
, codeActionTests
|
||||||
|
, findDefinitionTests
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
diagnosticTests :: TestTree
|
diagnosticTests :: TestTree
|
||||||
diagnosticTests = testGroup "diagnostics"
|
diagnosticTests = testGroup "diagnostics"
|
||||||
[ testSession "fix syntax error" $ do
|
[ testSession "fix syntax error" $ do
|
||||||
@ -113,10 +114,10 @@ diagnosticTests = testGroup "diagnostics"
|
|||||||
expectedDs aMessage =
|
expectedDs aMessage =
|
||||||
[ ("A.hs", [(DsError, (2,4), aMessage)])
|
[ ("A.hs", [(DsError, (2,4), aMessage)])
|
||||||
, ("B.hs", [(DsError, (3,4), bMessage)])]
|
, ("B.hs", [(DsError, (3,4), bMessage)])]
|
||||||
deferralTest title binding message = testSession title $ do
|
deferralTest title binding msg = testSession title $ do
|
||||||
_ <- openDoc' "A.hs" "haskell" $ sourceA binding
|
_ <- openDoc' "A.hs" "haskell" $ sourceA binding
|
||||||
_ <- openDoc' "B.hs" "haskell" sourceB
|
_ <- openDoc' "B.hs" "haskell" sourceB
|
||||||
expectDiagnostics $ expectedDs message
|
expectDiagnostics $ expectedDs msg
|
||||||
in
|
in
|
||||||
[ deferralTest "type error" "True" "Couldn't match expected type"
|
[ deferralTest "type error" "True" "Couldn't match expected type"
|
||||||
, deferralTest "typed hole" "_" "Found hole"
|
, deferralTest "typed hole" "_" "Found hole"
|
||||||
@ -561,14 +562,14 @@ fillTypedHoleTests = let
|
|||||||
|
|
||||||
addSigActionTests :: TestTree
|
addSigActionTests :: TestTree
|
||||||
addSigActionTests = let
|
addSigActionTests = let
|
||||||
head = T.unlines [ "{-# OPTIONS_GHC -Wmissing-signatures #-}"
|
header = T.unlines [ "{-# OPTIONS_GHC -Wmissing-signatures #-}"
|
||||||
, "module Sigs where"]
|
, "module Sigs where"]
|
||||||
before def = T.unlines [head, def]
|
before def = T.unlines [header, def]
|
||||||
after def sig = T.unlines [head, sig, def]
|
after' def sig = T.unlines [header, sig, def]
|
||||||
|
|
||||||
def >:: sig = testSession (T.unpack def) $ do
|
def >:: sig = testSession (T.unpack def) $ do
|
||||||
let originalCode = before def
|
let originalCode = before def
|
||||||
let expectedCode = after def sig
|
let expectedCode = after' def sig
|
||||||
doc <- openDoc' "Sigs.hs" "haskell" originalCode
|
doc <- openDoc' "Sigs.hs" "haskell" originalCode
|
||||||
_ <- waitForDiagnostics
|
_ <- waitForDiagnostics
|
||||||
actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
|
actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
|
||||||
@ -586,6 +587,123 @@ addSigActionTests = let
|
|||||||
, "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2"
|
, "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
findDefinitionTests :: TestTree
|
||||||
|
findDefinitionTests = let
|
||||||
|
|
||||||
|
tst (get, check) pos targetRange title = testSession title $ do
|
||||||
|
doc <- openDoc' "Testing.hs" "haskell" source
|
||||||
|
found <- get doc pos
|
||||||
|
check found targetRange
|
||||||
|
|
||||||
|
checkDefs defs expected = do
|
||||||
|
|
||||||
|
let ndef = length defs
|
||||||
|
if ndef /= 1
|
||||||
|
then let dfound n = "definitions found: " <> show n in
|
||||||
|
liftIO $ dfound (1 :: Int) @=? dfound (length defs)
|
||||||
|
else do
|
||||||
|
let [Location{_range = foundRange}] = defs
|
||||||
|
liftIO $ expected @=? foundRange
|
||||||
|
|
||||||
|
checkHover hover expected = do
|
||||||
|
case hover of
|
||||||
|
Nothing -> liftIO $ "hover found" @=? ("no hover found" :: T.Text)
|
||||||
|
Just Hover{_contents = (HoverContents MarkupContent{_value = v})} ->
|
||||||
|
liftIO $ adjust expected @=? Position l c where
|
||||||
|
found = T.splitOn ":" $ head $ T.splitOn "**" $ last $ T.splitOn "Testing.hs:" v
|
||||||
|
[l,c] = map (read . T.unpack) found
|
||||||
|
-- looks like hovers use 1-based numbering while definitions use 0-based
|
||||||
|
adjust Range{_start = Position{_line = l, _character = c}} =
|
||||||
|
Position{_line = l + 1, _character = c + 1}
|
||||||
|
_ -> error "test not expecting this kind of hover info"
|
||||||
|
|
||||||
|
source = T.unlines
|
||||||
|
-- 0123456789 123456789 123456789 123456789
|
||||||
|
[ "{-# OPTIONS_GHC -Wmissing-signatures #-}" -- 0
|
||||||
|
, "module Testing where" -- 1
|
||||||
|
, "data TypeConstructor = DataConstructor" -- 2
|
||||||
|
, " { fff :: String" -- 3
|
||||||
|
, " , ggg :: Int }" -- 4
|
||||||
|
, "aaa :: TypeConstructor" -- 5
|
||||||
|
, "aaa = DataConstructor" -- 6
|
||||||
|
, " { fff = \"\"" -- 7
|
||||||
|
, " , ggg = 0" -- 8
|
||||||
|
, " }" -- 9
|
||||||
|
-- 0123456789 123456789 123456789 123456789
|
||||||
|
, "bbb :: TypeConstructor" -- 10
|
||||||
|
, "bbb = DataConstructor \"\" 0" -- 11
|
||||||
|
, "ccc :: (String, Int)" -- 12
|
||||||
|
, "ccc = (fff bbb, ggg aaa)" -- 13
|
||||||
|
, "ddd :: Num a => a -> a -> a" -- 14
|
||||||
|
, "ddd vv ww = vv +! ww" -- 15
|
||||||
|
, "a +! b = a - b" -- 16
|
||||||
|
, "hhh (Just a) (><) = a >< a" -- 17
|
||||||
|
, "iii a b = a `b` a" -- 18
|
||||||
|
-- 0123456789 123456789 123456789 123456789
|
||||||
|
]
|
||||||
|
|
||||||
|
-- definition locations
|
||||||
|
tcData = mkRange 2 0 4 16
|
||||||
|
tcDC = mkRange 2 23 4 16
|
||||||
|
fff = mkRange 3 4 3 7
|
||||||
|
aaa = mkRange 6 0 6 3
|
||||||
|
vv = mkRange 15 4 15 6
|
||||||
|
op = mkRange 16 2 16 4
|
||||||
|
opp = mkRange 17 13 17 17
|
||||||
|
apmp = mkRange 17 10 17 11
|
||||||
|
bp = mkRange 18 6 18 7
|
||||||
|
-- search locations
|
||||||
|
fffL3 = _start fff
|
||||||
|
fffL7 = Position 7 4
|
||||||
|
fffL13 = Position 13 7
|
||||||
|
aaaL13 = Position 13 20
|
||||||
|
dcL6 = Position 6 11
|
||||||
|
dcL11 = Position 11 11
|
||||||
|
tcL5 = Position 5 11
|
||||||
|
vvL15 = Position 15 12
|
||||||
|
opL15 = Position 15 15
|
||||||
|
opL17 = Position 17 22
|
||||||
|
aL17 = Position 17 20
|
||||||
|
b'L18 = Position 18 13
|
||||||
|
|
||||||
|
--t = (getTypeDefinitions, checkTDefs) -- getTypeDefinitions always times out
|
||||||
|
d = (getDefinitions, checkDefs)
|
||||||
|
h = (getHover, checkHover)
|
||||||
|
in
|
||||||
|
testGroup "get"
|
||||||
|
[ testGroup "definition"
|
||||||
|
[ tst d fffL3 fff "field in record definition"
|
||||||
|
, tst d fffL7 fff "field in record construction" `xfail` "known broken"
|
||||||
|
, tst d fffL13 fff "field name used as accessor" -- 120 in Calculate.hs
|
||||||
|
, tst d aaaL13 aaa "top-level name" -- 120
|
||||||
|
, tst d dcL6 tcDC "record data constructor" `xfail` "known broken"
|
||||||
|
, tst d dcL11 tcDC "plain data constructor" -- 121
|
||||||
|
, tst d tcL5 tcData "type constructor" -- 147
|
||||||
|
, tst d vvL15 vv "plain parameter"
|
||||||
|
, tst d aL17 apmp "pattern match name"
|
||||||
|
, tst d opL15 op "top-level operator" -- 123
|
||||||
|
, tst d opL17 opp "parameter operator"
|
||||||
|
, tst d b'L18 bp "name in backticks"
|
||||||
|
]
|
||||||
|
, testGroup "hover"
|
||||||
|
[ tst h fffL3 fff "field in record definition"
|
||||||
|
, tst h fffL7 fff "field in record construction" `xfail` "known broken"
|
||||||
|
, tst h fffL13 fff "field name used as accessor" -- 120
|
||||||
|
, tst h aaaL13 aaa "top-level name" -- 120
|
||||||
|
, tst h dcL6 tcDC "record data constructor" `xfail` "known broken"
|
||||||
|
, tst h dcL11 tcDC "plain data constructor" -- 121
|
||||||
|
, tst h tcL5 tcData "type constructor" `xfail` "known broken"
|
||||||
|
, tst h vvL15 vv "plain parameter"
|
||||||
|
, tst h aL17 apmp "pattern match name"
|
||||||
|
, tst h opL15 op "top-level operator" -- 123
|
||||||
|
, tst d opL17 opp "parameter operator"
|
||||||
|
, tst h b'L18 bp "name in backticks"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
xfail :: TestTree -> String -> TestTree
|
||||||
|
xfail = flip expectFailBecause
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- Utils
|
-- Utils
|
||||||
|
|
||||||
@ -607,6 +725,9 @@ pickActionWithTitle title actions = head
|
|||||||
| CACodeAction action@CodeAction{ _title = actionTitle } <- actions
|
| CACodeAction action@CodeAction{ _title = actionTitle } <- actions
|
||||||
, title == actionTitle ]
|
, title == actionTitle ]
|
||||||
|
|
||||||
|
mkRange :: Int -> Int -> Int -> Int -> Range
|
||||||
|
mkRange a b c d = Range (Position a b) (Position c d)
|
||||||
|
|
||||||
run :: Session a -> IO a
|
run :: Session a -> IO a
|
||||||
run s = withTempDir $ \dir -> do
|
run s = withTempDir $ \dir -> do
|
||||||
ghcideExe <- locateGhcideExecutable
|
ghcideExe <- locateGhcideExecutable
|
||||||
|
Loading…
Reference in New Issue
Block a user