@@ -10,7 +10,7 @@
1010module Main (main) where
1111
1212import Control.Applicative.Combinators
13−import Control.Exception (catch)
13+import Control.Exception (bracket, catch)
1414import qualified Control.Lens as Lens
1515import Control.Monad
1616import Control.Monad.IO.Class (liftIO)
@@ -28,15 +28,14 @@ import Development.IDE.Test
2828import Development.IDE.Test.Runfiles
2929import Development.IDE.Types.Location
3030import Development.Shake (getDirectoryFilesIO)
31−import qualified Language.Haskell.LSP.Test as LSPTest
32−import Language.Haskell.LSP.Test hiding (openDoc')
31+import Language.Haskell.LSP.Test
3332import Language.Haskell.LSP.Messages
3433import Language.Haskell.LSP.Types
3534import Language.Haskell.LSP.Types.Capabilities
3635import qualified Language.Haskell.LSP.Types.Lens as Lsp (diagnostics, params, message)
3736import Language.Haskell.LSP.VFS (applyChange)
3837import Network.URI
39−import System.Environment.Blank (setEnv)
38+import System.Environment.Blank (getEnv, setEnv, unsetEnv)
4039import System.FilePath
4140import System.IO.Extra
4241import System.Directory
@@ -50,31 +49,35 @@ import Test.Tasty.QuickCheck
5049import Data.Maybe
5150
5251main :: IO ()
53−main = defaultMainWithRerun $ testGroup "HIE"
54− [ testSession "open close" $ do
55− doc <- openDoc' "Testing.hs" "haskell" ""
56− void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
57− void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
58− closeDoc doc
59− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
60− , initializeResponseTests
61− , completionTests
62− , cppTests
63− , diagnosticTests
64− , codeActionTests
65− , codeLensesTests
66− , outlineTests
67− , findDefinitionAndHoverTests
68− , pluginTests
69− , preprocessorTests
70− , thTests
71− , safeTests
72− , unitTests
73− , haddockTests
74− , positionMappingTests
75− , watchedFilesTests
76− , cradleTests
77− ]
52+main = do
53+ -- We mess with env vars so run single-threaded.
54+ setEnv "TASTY_NUM_THREADS" "1" True
55+ defaultMainWithRerun $ testGroup "HIE"
56+ [ testSession "open close" $ do
57+ doc <- createDoc "Testing.hs" "haskell" ""
58+ void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
59+ void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
60+ closeDoc doc
61+ void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
62+ , initializeResponseTests
63+ , completionTests
64+ , cppTests
65+ , diagnosticTests
66+ , codeActionTests
67+ , codeLensesTests
68+ , outlineTests
69+ , findDefinitionAndHoverTests
70+ , pluginTests
71+ , preprocessorTests
72+ , thTests
73+ , safeTests
74+ , unitTests
75+ , haddockTests
76+ , positionMappingTests
77+ , watchedFilesTests
78+ , cradleTests
79+ , dependentFileTest
80+ ]
7881
7982initializeResponseTests :: TestTree
8083initializeResponseTests = withResource acquire release tests where
@@ -135,7 +138,7 @@ initializeResponseTests = withResource acquire release tests where
135138
136139
137140 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
138− innerCaps (ResponseMessage _ _ (Just (InitializeResponseCapabilities c)) _) = c
141+ innerCaps (ResponseMessage _ _ (Right (InitializeResponseCapabilities c))) = c
139142 innerCaps _ = error "this test only expects inner capabilities"
140143
141144 acquire :: IO InitializeResponse
@@ -149,7 +152,7 @@ diagnosticTests :: TestTree
149152diagnosticTests = testGroup "diagnostics"
150153 [ testSessionWait "fix syntax error" $ do
151154 let content = T.unlines [ "module Testing wher" ]
152− doc <- openDoc' "Testing.hs" "haskell" content
155+ doc <- createDoc "Testing.hs" "haskell" content
153156 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
154157 let change = TextDocumentContentChangeEvent
155158 { _range = Just (Range (Position 0 15) (Position 0 19))
@@ -160,7 +163,7 @@ diagnosticTests = testGroup "diagnostics"
160163 expectDiagnostics [("Testing.hs", [])]
161164 , testSessionWait "introduce syntax error" $ do
162165 let content = T.unlines [ "module Testing where" ]
163− doc <- openDoc' "Testing.hs" "haskell" content
166+ doc <- createDoc "Testing.hs" "haskell" content
164167 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
165168 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
166169 let change = TextDocumentContentChangeEvent
@@ -178,7 +181,7 @@ diagnosticTests = testGroup "diagnostics"
178181 , "bar :: Int -> Int -> Int"
179182 , "bar a b = cd + b"
180183 ]
181− _ <- openDoc' "Testing.hs" "haskell" content
184+ _ <- createDoc "Testing.hs" "haskell" content
182185 expectDiagnostics
183186 [ ( "Testing.hs"
184187 , [ (DsError, (2, 14), "Variable not in scope: ab")
@@ -192,7 +195,7 @@ diagnosticTests = testGroup "diagnostics"
192195 , "foo :: Int -> String -> Int"
193196 , "foo a b = a + b"
194197 ]
195− _ <- openDoc' "Testing.hs" "haskell" content
198+ _ <- createDoc "Testing.hs" "haskell" content
196199 expectDiagnostics
197200 [ ( "Testing.hs"
198201 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
@@ -204,7 +207,7 @@ diagnosticTests = testGroup "diagnostics"
204207 , "foo :: Int -> String"
205208 , "foo a = _ a"
206209 ]
207− _ <- openDoc' "Testing.hs" "haskell" content
210+ _ <- createDoc "Testing.hs" "haskell" content
208211 expectDiagnostics
209212 [ ( "Testing.hs"
210213 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
@@ -226,8 +229,8 @@ diagnosticTests = testGroup "diagnostics"
226229 [ ("A.hs", [(DsError, (2,4), aMessage)])
227230 , ("B.hs", [(DsError, (3,4), bMessage)])]
228231 deferralTest title binding msg = testSessionWait title $ do
229− _ <- openDoc' "A.hs" "haskell" $ sourceA binding
230− _ <- openDoc' "B.hs" "haskell" sourceB
232+ _ <- createDoc "A.hs" "haskell" $ sourceA binding
233+ _ <- createDoc "B.hs" "haskell" sourceB
231234 expectDiagnostics $ expectedDs msg
232235 in
233236 [ deferralTest "type error" "True" "Couldn't match expected type"
@@ -237,12 +240,12 @@ diagnosticTests = testGroup "diagnostics"
237240
238241 , testSessionWait "remove required module" $ do
239242 let contentA = T.unlines [ "module ModuleA where" ]
240− docA <- openDoc' "ModuleA.hs" "haskell" contentA
243+ docA <- createDoc "ModuleA.hs" "haskell" contentA
241244 let contentB = T.unlines
242245 [ "module ModuleB where"
243246 , "import ModuleA"
244247 ]
245− _ <- openDoc' "ModuleB.hs" "haskell" contentB
248+ _ <- createDoc "ModuleB.hs" "haskell" contentB
246249 let change = TextDocumentContentChangeEvent
247250 { _range = Just (Range (Position 0 0) (Position 0 20))
248251 , _rangeLength = Nothing
@@ -255,20 +258,20 @@ diagnosticTests = testGroup "diagnostics"
255258 [ "module ModuleB where"
256259 , "import ModuleA"
257260 ]
258− _ <- openDoc' "ModuleB.hs" "haskell" contentB
261+ _ <- createDoc "ModuleB.hs" "haskell" contentB
259262 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
260263 let contentA = T.unlines [ "module ModuleA where" ]
261− _ <- openDoc' "ModuleA.hs" "haskell" contentA
264+ _ <- createDoc "ModuleA.hs" "haskell" contentA
262265 expectDiagnostics [("ModuleB.hs", [])]
263266 , testSessionWait "add missing module (non workspace)" $ do
264267 let contentB = T.unlines
265268 [ "module ModuleB where"
266269 , "import ModuleA"
267270 ]
268− _ <- openDoc'' "/tmp/ModuleB.hs" "haskell" contentB
271+ _ <- createDoc "/tmp/ModuleB.hs" "haskell" contentB
269272 expectDiagnostics [("/tmp/ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
270273 let contentA = T.unlines [ "module ModuleA where" ]
271− _ <- openDoc'' "/tmp/ModuleA.hs" "haskell" contentA
274+ _ <- createDoc "/tmp/ModuleA.hs" "haskell" contentA
272275 expectDiagnostics [("/tmp/ModuleB.hs", [])]
273276 , testSessionWait "cyclic module dependency" $ do
274277 let contentA = T.unlines
@@ -279,8 +282,8 @@ diagnosticTests = testGroup "diagnostics"
279282 [ "module ModuleB where"
280283 , "import ModuleA"
281284 ]
282− _ <- openDoc' "ModuleA.hs" "haskell" contentA
283− _ <- openDoc' "ModuleB.hs" "haskell" contentB
285+ _ <- createDoc "ModuleA.hs" "haskell" contentA
286+ _ <- createDoc "ModuleB.hs" "haskell" contentB
284287 expectDiagnostics
285288 [ ( "ModuleA.hs"
286289 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
@@ -301,9 +304,9 @@ diagnosticTests = testGroup "diagnostics"
301304 let contentBboot = T.unlines
302305 [ "module ModuleB where"
303306 ]
304− _ <- openDoc' "ModuleA.hs" "haskell" contentA
305− _ <- openDoc' "ModuleB.hs" "haskell" contentB
306− _ <- openDoc' "ModuleB.hs-boot" "haskell" contentBboot
307+ _ <- createDoc "ModuleA.hs" "haskell" contentA
308+ _ <- createDoc "ModuleB.hs" "haskell" contentB
309+ _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot
307310 expectDiagnostics []
308311 , testSessionWait "correct reference used with hs-boot" $ do
309312 let contentB = T.unlines
@@ -325,10 +328,10 @@ diagnosticTests = testGroup "diagnostics"
325328 -- resolved to the hs-boot file
326329 , "y = x"
327330 ]
328− _ <- openDoc' "ModuleB.hs" "haskell" contentB
329− _ <- openDoc' "ModuleA.hs" "haskell" contentA
330− _ <- openDoc' "ModuleA.hs-boot" "haskell" contentAboot
331− _ <- openDoc' "ModuleC.hs" "haskell" contentC
331+ _ <- createDoc "ModuleB.hs" "haskell" contentB
332+ _ <- createDoc "ModuleA.hs" "haskell" contentA
333+ _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot
334+ _ <- createDoc "ModuleC.hs" "haskell" contentC
332335 expectDiagnostics []
333336 , testSessionWait "redundant import" $ do
334337 let contentA = T.unlines ["module ModuleA where"]
@@ -337,8 +340,8 @@ diagnosticTests = testGroup "diagnostics"
337340 , "module ModuleB where"
338341 , "import ModuleA"
339342 ]
340− _ <- openDoc' "ModuleA.hs" "haskell" contentA
341− _ <- openDoc' "ModuleB.hs" "haskell" contentB
343+ _ <- createDoc "ModuleA.hs" "haskell" contentA
344+ _ <- createDoc "ModuleB.hs" "haskell" contentB
342345 expectDiagnostics
343346 [ ( "ModuleB.hs"
344347 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
@@ -360,8 +363,8 @@ diagnosticTests = testGroup "diagnostics"
360363 , "wrong1 = ThisList.map"
361364 , "wrong2 = BaseList.x"
362365 ]
363− _ <- openDoc' "Data/List.hs" "haskell" thisDataListContent
364− _ <- openDoc' "Main.hs" "haskell" mainContent
366+ _ <- createDoc "Data/List.hs" "haskell" thisDataListContent
367+ _ <- createDoc "Main.hs" "haskell" mainContent
365368 expectDiagnostics
366369 [ ( "Main.hs"
367370 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
@@ -376,7 +379,7 @@ diagnosticTests = testGroup "diagnostics"
376379 , "foo :: Ord a => a -> Int"
377380 , "foo a = 1"
378381 ]
379− _ <- openDoc' "Foo.hs" "haskell" fooContent
382+ _ <- createDoc "Foo.hs" "haskell" fooContent
380383 expectDiagnostics
381384 [ ( "Foo.hs"
382385 -- The test is to make sure that warnings contain unqualified names
@@ -427,7 +430,7 @@ diagnosticTests = testGroup "diagnostics"
427430 , "foo :: Int"
428431 , "foo = 1 {-|-}"
429432 ]
430− _ <- openDoc' "Foo.hs" "haskell" fooContent
433+ _ <- createDoc "Foo.hs" "haskell" fooContent
431434 expectDiagnostics
432435 [ ( "Foo.hs"
433436 , [(DsError, (2, 8), "Parse error on input")
@@ -442,7 +445,7 @@ diagnosticTests = testGroup "diagnostics"
442445 , "value :: Maybe ()"
443446 , "value = [()]"
444447 ]
445− _ <- openDoc' (T.unpack name <> ".hs") "haskell" content
448+ _ <- createDoc (T.unpack name <> ".hs") "haskell" content
446449 notification <- skipManyTill anyMessage diagnostic
447450 let
448451 offenders =
@@ -479,29 +482,25 @@ watchedFilesTests :: TestTree
479482watchedFilesTests = testGroup "watched files"
480483 [ testSession' "workspace files" $ \sessionDir -> do
481484 liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\"]}}"
482− _doc <- openDoc' "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport WatchedFilesMissingModule"
485+ _doc <- createDoc "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport WatchedFilesMissingModule"
483486 watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
484487
485− -- Expect 6 subscriptions (A does not get any because it's VFS):
488+ -- Expect 4 subscriptions (A does not get any because it's VFS):
486489 -- - /path-to-workspace/WatchedFilesMissingModule.hs
487490 -- - /path-to-workspace/WatchedFilesMissingModule.lhs
488− -- - WatchedFilesMissingModule.hs
489− -- - WatchedFilesMissingModule.lhs
490− -- - src/WatchedFilesMissingModule.hs
491− -- - src/WatchedFilesMissingModule.lhs
492− liftIO $ length watchedFileRegs @?= 6
491+ -- - /path-to-workspace/src/WatchedFilesMissingModule.hs
492+ -- - /path-to-workspace/src/WatchedFilesMissingModule.lhs
493+ liftIO $ length watchedFileRegs @?= 4
493494
494495 , testSession' "non workspace file" $ \sessionDir -> do
495496 liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-i/tmp\"]}}"
496− _doc <- openDoc' "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport WatchedFilesMissingModule"
497+ _doc <- createDoc "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport WatchedFilesMissingModule"
497498 watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
498499
499− -- Expect 4 subscriptions (/tmp does not get any as it is out of the workspace):
500+ -- Expect 2 subscriptions (/tmp does not get any as it is out of the workspace):
500501 -- - /path-to-workspace/WatchedFilesMissingModule.hs
501502 -- - /path-to-workspace/WatchedFilesMissingModule.lhs
502− -- - WatchedFilesMissingModule.hs
503− -- - WatchedFilesMissingModule.lhs
504− liftIO $ length watchedFileRegs @?= 4
503+ liftIO $ length watchedFileRegs @?= 2
505504
506505 -- TODO add a test for didChangeWorkspaceFolder
507506 ]
@@ -514,7 +513,7 @@ renameActionTests = testGroup "rename actions"
514513 , "foo :: Int -> Int"
515514 , "foo argName = argNme"
516515 ]
517− doc <- openDoc' "Testing.hs" "haskell" content
516+ doc <- createDoc "Testing.hs" "haskell" content
518517 _ <- waitForDiagnostics
519518 action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’"
520519 executeCodeAction action
@@ -532,7 +531,7 @@ renameActionTests = testGroup "rename actions"
532531 , "foo :: Maybe a -> [a]"
533532 , "foo = maybToList"
534533 ]
535− doc <- openDoc' "Testing.hs" "haskell" content
534+ doc <- createDoc "Testing.hs" "haskell" content
536535 _ <- waitForDiagnostics
537536 action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’"
538537 executeCodeAction action
@@ -550,7 +549,7 @@ renameActionTests = testGroup "rename actions"
550549 , "foo :: Char -> Char -> Char -> Char"
551550 , "foo argument1 argument2 argument3 = argumentX"
552551 ]
553− doc <- openDoc' "Testing.hs" "haskell" content
552+ doc <- createDoc "Testing.hs" "haskell" content
554553 _ <- waitForDiagnostics
555554 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
556555 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
@@ -562,7 +561,7 @@ renameActionTests = testGroup "rename actions"
562561 , "monus x y = max 0 (x - y)"
563562 , "foo x y = x `monnus` y"
564563 ]
565− doc <- openDoc' "Testing.hs" "haskell" content
564+ doc <- createDoc "Testing.hs" "haskell" content
566565 _ <- waitForDiagnostics
567566 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
568567 [fixTypo] <- pure [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ]
@@ -585,7 +584,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
585584 , "func :: _"
586585 , "func x = x"
587586 ]
588− doc <- openDoc' "Testing.hs" "haskell" content
587+ doc <- createDoc "Testing.hs" "haskell" content
589588 _ <- waitForDiagnostics
590589 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
591590 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -605,7 +604,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
605604 , "func :: _"
606605 , "func x y = x + y"
607606 ]
608− doc <- openDoc' "Testing.hs" "haskell" content
607+ doc <- createDoc "Testing.hs" "haskell" content
609608 _ <- waitForDiagnostics
610609 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
611610 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -628,7 +627,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
628627 , " y = x * 2"
629628 , " in y"
630629 ]
631− doc <- openDoc' "Testing.hs" "haskell" content
630+ doc <- createDoc "Testing.hs" "haskell" content
632631 _ <- waitForDiagnostics
633632 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
634633 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -653,7 +652,7 @@ removeImportTests = testGroup "remove import actions"
653652 let contentA = T.unlines
654653 [ "module ModuleA where"
655654 ]
656− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
655+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
657656 let contentB = T.unlines
658657 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
659658 , "module ModuleB where"
@@ -661,7 +660,7 @@ removeImportTests = testGroup "remove import actions"
661660 , "stuffB :: Integer"
662661 , "stuffB = 123"
663662 ]
664− docB <- openDoc' "ModuleB.hs" "haskell" contentB
663+ docB <- createDoc "ModuleB.hs" "haskell" contentB
665664 _ <- waitForDiagnostics
666665 [CACodeAction action@CodeAction { _title = actionTitle }]
667666 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -679,7 +678,7 @@ removeImportTests = testGroup "remove import actions"
679678 let contentA = T.unlines
680679 [ "module ModuleA where"
681680 ]
682− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
681+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
683682 let contentB = T.unlines
684683 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
685684 , "module ModuleB where"
@@ -687,7 +686,7 @@ removeImportTests = testGroup "remove import actions"
687686 , "stuffB :: Integer"
688687 , "stuffB = 123"
689688 ]
690− docB <- openDoc' "ModuleB.hs" "haskell" contentB
689+ docB <- createDoc "ModuleB.hs" "haskell" contentB
691690 _ <- waitForDiagnostics
692691 [CACodeAction action@CodeAction { _title = actionTitle }]
693692 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -709,14 +708,14 @@ removeImportTests = testGroup "remove import actions"
709708 , "stuffB = 123"
710709 , "stuffC = ()"
711710 ]
712− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
711+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
713712 let contentB = T.unlines
714713 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
715714 , "module ModuleB where"
716715 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
717716 , "main = print stuffB"
718717 ]
719− docB <- openDoc' "ModuleB.hs" "haskell" contentB
718+ docB <- createDoc "ModuleB.hs" "haskell" contentB
720719 _ <- waitForDiagnostics
721720 [CACodeAction action@CodeAction { _title = actionTitle }]
722721 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -738,14 +737,14 @@ removeImportTests = testGroup "remove import actions"
738737 , "stuffB :: Integer"
739738 , "stuffB = 123"
740739 ]
741− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
740+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
742741 let contentB = T.unlines
743742 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
744743 , "module ModuleB where"
745744 , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
746745 , "main = print A.stuffB"
747746 ]
748− docB <- openDoc' "ModuleB.hs" "haskell" contentB
747+ docB <- createDoc "ModuleB.hs" "haskell" contentB
749748 _ <- waitForDiagnostics
750749 [CACodeAction action@CodeAction { _title = actionTitle }]
751750 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -766,14 +765,14 @@ removeImportTests = testGroup "remove import actions"
766765 , "stuffB :: Integer"
767766 , "stuffB = 123"
768767 ]
769− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
768+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
770769 let contentB = T.unlines
771770 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
772771 , "module ModuleB where"
773772 , "import ModuleA (A(..), stuffB)"
774773 , "main = print stuffB"
775774 ]
776− docB <- openDoc' "ModuleB.hs" "haskell" contentB
775+ docB <- createDoc "ModuleB.hs" "haskell" contentB
777776 _ <- waitForDiagnostics
778777 [CACodeAction action@CodeAction { _title = actionTitle }]
779778 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -793,14 +792,14 @@ removeImportTests = testGroup "remove import actions"
793792 , "data D = A | B"
794793 , "data E = F"
795794 ]
796− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
795+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
797796 let contentB = T.unlines
798797 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
799798 , "module ModuleB where"
800799 , "import ModuleA (D(A,B), E(F))"
801800 , "main = B"
802801 ]
803− docB <- openDoc' "ModuleB.hs" "haskell" contentB
802+ docB <- createDoc "ModuleB.hs" "haskell" contentB
804803 _ <- waitForDiagnostics
805804 [CACodeAction action@CodeAction { _title = actionTitle }]
806805 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -941,8 +940,8 @@ extendImportTests = testGroup "extend import actions"
941940 ]
942941 where
943942 template contentA contentB range expectedAction expectedContentB = do
944− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
945− docB <- openDoc' "ModuleB.hs" "haskell" contentB
943+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
944+ docB <- createDoc "ModuleB.hs" "haskell" contentB
946945 _ <- waitForDiagnostics
947946 CACodeAction action@CodeAction { _title = actionTitle } : _
948947 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -992,7 +991,7 @@ suggestImportTests = testGroup "suggest import actions"
992991 after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other
993992 cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -]}}"
994993 liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle
995− doc <- openDoc' "Test.hs" "haskell" before
994+ doc <- createDoc "Test.hs" "haskell" before
996995 _diags <- waitForDiagnostics
997996 let defLine = length imps + 1
998997 range = Range (Position defLine 0) (Position defLine maxBound)
@@ -1052,7 +1051,7 @@ addExtensionTests = testGroup "add language extension actions"
10521051 ]
10531052 where
10541053 template initialContent range expectedAction expectedContents = do
1055− doc <- openDoc' "Module.hs" "haskell" initialContent
1054+ doc <- createDoc "Module.hs" "haskell" initialContent
10561055 _ <- waitForDiagnostics
10571056 CACodeAction action@CodeAction { _title = actionTitle } : _
10581057 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1075,7 +1074,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
10751074 [""
10761075 ,"someOtherCode = ()"
10771076 ]
1078− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1077+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
10791078 _ <- waitForDiagnostics
10801079 CACodeAction action@CodeAction { _title = actionTitle } : _
10811080 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1099,7 +1098,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
10991098 [""
11001099 ,"someOtherCode = ()"
11011100 ]
1102− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1101+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
11031102 _ <- waitForDiagnostics
11041103 CACodeAction action@CodeAction { _title = actionTitle } : _
11051104 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1138,8 +1137,8 @@ fixConstructorImportTests = testGroup "fix import actions"
11381137 ]
11391138 where
11401139 template contentA contentB range expectedAction expectedContentB = do
1141− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
1142− docB <- openDoc' "ModuleB.hs" "haskell" contentB
1140+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
1141+ docB <- createDoc "ModuleB.hs" "haskell" contentB
11431142 _diags <- waitForDiagnostics
11441143 CACodeAction action@CodeAction { _title = actionTitle } : _
11451144 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1158,7 +1157,7 @@ importRenameActionTests = testGroup "import rename actions"
11581157 [ "module Testing where"
11591158 , "import Data.Mape"
11601159 ]
1161− doc <- openDoc' "Testing.hs" "haskell" content
1160+ doc <- createDoc "Testing.hs" "haskell" content
11621161 _ <- waitForDiagnostics
11631162 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
11641163 let [changeToMap] = [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]
@@ -1195,7 +1194,7 @@ fillTypedHoleTests = let
11951194 newA newB newC = testSession (T.unpack actionTitle) $ do
11961195 let originalCode = sourceCode oldA oldB oldC
11971196 let expectedCode = sourceCode newA newB newC
1198− doc <- openDoc' "Testing.hs" "haskell" originalCode
1197+ doc <- createDoc "Testing.hs" "haskell" originalCode
11991198 _ <- waitForDiagnostics
12001199 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
12011200 chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
@@ -1243,7 +1242,7 @@ addSigActionTests = let
12431242 def >:: sig = testSession (T.unpack def) $ do
12441243 let originalCode = before def
12451244 let expectedCode = after' def sig
1246− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1245+ doc <- createDoc "Sigs.hs" "haskell" originalCode
12471246 _ <- waitForDiagnostics
12481247 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
12491248 chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
@@ -1275,7 +1274,7 @@ addSigLensesTests = let
12751274 sigSession withMissing def sig = testSession (T.unpack def) $ do
12761275 let originalCode = before withMissing def
12771276 let expectedCode = after' withMissing def sig
1278− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1277+ doc <- createDoc "Sigs.hs" "haskell" originalCode
12791278 [CodeLens {_command = Just c}] <- getCodeLenses doc
12801279 executeCommand c
12811280 modifiedCode <- getDocumentEdit doc
@@ -1297,27 +1296,19 @@ addSigLensesTests = let
12971296 ]
12981297 ]
12991298
1300−findDefinitionAndHoverTests :: TestTree
1301−findDefinitionAndHoverTests = let
1299+checkDefs :: [Location] -> Session [Expect] -> Session ()
1300+checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
13021301
1303− tst (get, check) pos targetRange title = testSession title $ do
1304− doc <- openTestDataDoc sourceFilePath
1305− found <- get doc pos
1306− check found targetRange
1307−
1308− checkDefs :: [Location] -> Session [Expect] -> Session ()
1309− checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
1310−
1311− check (ExpectRange expectedRange) = do
1312− assertNDefinitionsFound 1 defs
1313− assertRangeCorrect (head defs) expectedRange
1314− check (ExpectLocation expectedLocation) = do
1315− assertNDefinitionsFound 1 defs
1316− liftIO $ head defs @?= expectedLocation
1317− check ExpectNoDefinitions = do
1318− assertNDefinitionsFound 0 defs
1319− check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1320− check _ = pure () -- all other expectations not relevant to getDefinition
1302+ check (ExpectRange expectedRange) = do
1303+ assertNDefinitionsFound 1 defs
1304+ assertRangeCorrect (head defs) expectedRange
1305+ check (ExpectLocation expectedLocation) = do
1306+ assertNDefinitionsFound 1 defs
1307+ liftIO $ head defs @?= expectedLocation
1308+ check ExpectNoDefinitions = do
1309+ assertNDefinitionsFound 0 defs
1310+ check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1311+ check _ = pure () -- all other expectations not relevant to getDefinition
13211312
13221313 assertNDefinitionsFound :: Int -> [a] -> Session ()
13231314 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
@@ -1325,6 +1316,17 @@ findDefinitionAndHoverTests = let
13251316 assertRangeCorrect Location{_range = foundRange} expectedRange =
13261317 liftIO $ expectedRange @=? foundRange
13271318
1319+
1320+findDefinitionAndHoverTests :: TestTree
1321+findDefinitionAndHoverTests = let
1322+
1323+ tst (get, check) pos targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do
1324+ doc <- openTestDataDoc (dir </> sourceFilePath)
1325+ found <- get doc pos
1326+ check found targetRange
1327+
1328+
1329+
13281330 checkHover :: Maybe Hover -> Session [Expect] -> Session ()
13291331 checkHover hover expectations = traverse_ check =<< expectations where
13301332
@@ -1369,7 +1371,8 @@ findDefinitionAndHoverTests = let
13691371
13701372 mkFindTests tests = testGroup "get"
13711373 [ testGroup "definition" $ mapMaybe fst tests
1372− , testGroup "hover" $ mapMaybe snd tests ]
1374+ , testGroup "hover" $ mapMaybe snd tests
1375+ , checkFileCompiles sourceFilePath ]
13731376
13741377 test runDef runHover look expect = testM runDef runHover look (return expect)
13751378
@@ -1464,6 +1467,14 @@ findDefinitionAndHoverTests = let
14641467 broken = Just . (`xfail` "known broken")
14651468 no = const Nothing -- don't run this test at all
14661469
1470+checkFileCompiles :: FilePath -> TestTree
1471+checkFileCompiles fp =
1472+ testSessionWithExtraFiles "hover" ("Does " ++ fp ++ " compile") $ \dir -> do
1473+ void (openTestDataDoc (dir </> fp))
1474+ expectNoMoreDiagnostics 0.5
1475+
1476+
1477+
14671478pluginTests :: TestTree
14681479pluginTests = (`xfail8101` "known broken (#556)")
14691480 $ testSessionWait "plugins" $ do
@@ -1480,7 +1491,7 @@ pluginTests = (`xfail8101` "known broken (#556)")
14801491 , "foo :: Int -> Int -> Int"
14811492 , "foo a b = a + c"
14821493 ]
1483− _ <- openDoc' "Testing.hs" "haskell" content
1494+ _ <- createDoc "Testing.hs" "haskell" content
14841495 expectDiagnostics
14851496 [ ( "Testing.hs",
14861497 [(DsError, (8, 14), "Variable not in scope: c")]
@@ -1507,7 +1518,7 @@ cppTests =
15071518 run $ expectError content (2, 1)
15081519 )
15091520 , testSessionWait "cpp-ghcide" $ do
1510− _ <- openDoc' "A.hs" "haskell" $ T.unlines
1521+ _ <- createDoc "A.hs" "haskell" $ T.unlines
15111522 ["{-# LANGUAGE CPP #-}"
15121523 ,"main ="
15131524 ,"#ifdef __GHCIDE__"
@@ -1521,7 +1532,7 @@ cppTests =
15211532 where
15221533 expectError :: T.Text -> Cursor -> Session ()
15231534 expectError content cursor = do
1524− _ <- openDoc' "Testing.hs" "haskell" content
1535+ _ <- createDoc "Testing.hs" "haskell" content
15251536 expectDiagnostics
15261537 [ ( "Testing.hs",
15271538 [(DsError, cursor, "error: unterminated")]
@@ -1537,7 +1548,7 @@ preprocessorTests = testSessionWait "preprocessor" $ do
15371548 , "module Testing where"
15381549 , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
15391550 ]
1540− _ <- openDoc' "Testing.hs" "haskell" content
1551+ _ <- createDoc "Testing.hs" "haskell" content
15411552 expectDiagnostics
15421553 [ ( "Testing.hs",
15431554 [(DsError, (2, 8), "Variable not in scope: z")]
@@ -1570,8 +1581,8 @@ safeTests =
15701581 ,"safeId = trustWorthyId"
15711582 ]
15721583
1573− _ <- openDoc' "A.hs" "haskell" sourceA
1574− _ <- openDoc' "B.hs" "haskell" sourceB
1584+ _ <- createDoc "A.hs" "haskell" sourceA
1585+ _ <- createDoc "B.hs" "haskell" sourceB
15751586 expectNoMoreDiagnostics 1 ]
15761587
15771588thTests :: TestTree
@@ -1599,8 +1610,8 @@ thTests =
15991610 "b :: Integer",
16001611 "b = $(litE $ IntegerL $ a) + n"
16011612 ]
1602− _ <- openDoc' "A.hs" "haskell" sourceA
1603− _ <- openDoc' "B.hs" "haskell" sourceB
1613+ _ <- createDoc "A.hs" "haskell" sourceA
1614+ _ <- createDoc "B.hs" "haskell" sourceB
16041615 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
16051616 , testSessionWait "newtype-closure" $ do
16061617 let sourceA =
@@ -1620,8 +1631,8 @@ thTests =
16201631 ,"import A"
16211632 ,"b :: Int"
16221633 ,"b = $( a )" ]
1623− _ <- openDoc' "A.hs" "haskell" sourceA
1624− _ <- openDoc' "B.hs" "haskell" sourceB
1634+ _ <- createDoc "A.hs" "haskell" sourceA
1635+ _ <- createDoc "B.hs" "haskell" sourceB
16251636 return ()
16261637 ]
16271638
@@ -1630,7 +1641,7 @@ completionTests
16301641 = testGroup "completion"
16311642 [ testSessionWait "variable" $ do
16321643 let source = T.unlines ["module A where", "f = hea"]
1633− docId <- openDoc' "A.hs" "haskell" source
1644+ docId <- createDoc "A.hs" "haskell" source
16341645 compls <- getCompletions docId (Position 1 7)
16351646 liftIO $ map dropDocs compls @?=
16361647 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
@@ -1642,7 +1653,7 @@ completionTests
16421653 ]
16431654 , testSessionWait "constructor" $ do
16441655 let source = T.unlines ["module A where", "f = Tru"]
1645− docId <- openDoc' "A.hs" "haskell" source
1656+ docId <- createDoc "A.hs" "haskell" source
16461657 compls <- getCompletions docId (Position 1 7)
16471658 liftIO $ map dropDocs compls @?=
16481659 [ complItem "True" (Just CiConstructor) (Just "Bool")
@@ -1654,7 +1665,7 @@ completionTests
16541665 ]
16551666 , testSessionWait "type" $ do
16561667 let source = T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: ()", "f = ()"]
1657− docId <- openDoc' "A.hs" "haskell" source
1668+ docId <- createDoc "A.hs" "haskell" source
16581669 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
16591670 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]]
16601671 compls <- getCompletions docId (Position 2 7)
@@ -1671,7 +1682,7 @@ completionTests
16711682 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
16721683 , testSessionWait "qualified" $ do
16731684 let source = T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = ()"]
1674− docId <- openDoc' "A.hs" "haskell" source
1685+ docId <- createDoc "A.hs" "haskell" source
16751686 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
16761687 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]]
16771688 compls <- getCompletions docId (Position 2 15)
@@ -1685,7 +1696,7 @@ completionTests
16851696 ]
16861697 , testSessionWait "keyword" $ do
16871698 let source = T.unlines ["module A where", "f = newty"]
1688− docId <- openDoc' "A.hs" "haskell" source
1699+ docId <- createDoc "A.hs" "haskell" source
16891700 compls <- getCompletions docId (Position 1 9)
16901701 liftIO $ compls @?= [keywordItem "newtype"]
16911702 , testSessionWait "type context" $ do
@@ -1694,7 +1705,7 @@ completionTests
16941705 , "module A () where"
16951706 , "f = f"
16961707 ]
1697− docId <- openDoc' "A.hs" "haskell" source
1708+ docId <- createDoc "A.hs" "haskell" source
16981709 expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
16991710 changeDoc docId
17001711 [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
@@ -1762,7 +1773,7 @@ outlineTests = testGroup
17621773 "outline"
17631774 [ testSessionWait "type class" $ do
17641775 let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]
1765− docId <- openDoc' "A.hs" "haskell" source
1776+ docId <- createDoc "A.hs" "haskell" source
17661777 symbols <- getDocumentSymbols docId
17671778 liftIO $ symbols @?= Left
17681779 [ moduleSymbol
@@ -1775,7 +1786,7 @@ outlineTests = testGroup
17751786 ]
17761787 , testSessionWait "type class instance " $ do
17771788 let source = T.unlines ["class A a where", "instance A () where"]
1778− docId <- openDoc' "A.hs" "haskell" source
1789+ docId <- createDoc "A.hs" "haskell" source
17791790 symbols <- getDocumentSymbols docId
17801791 liftIO $ symbols @?= Left
17811792 [ classSymbol "A a" (R 0 0 0 15) []
@@ -1783,7 +1794,7 @@ outlineTests = testGroup
17831794 ]
17841795 , testSessionWait "type family" $ do
17851796 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1786− docId <- openDoc' "A.hs" "haskell" source
1797+ docId <- createDoc "A.hs" "haskell" source
17871798 symbols <- getDocumentSymbols docId
17881799 liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]
17891800 , testSessionWait "type family instance " $ do
@@ -1792,7 +1803,7 @@ outlineTests = testGroup
17921803 , "type family A a"
17931804 , "type instance A () = ()"
17941805 ]
1795− docId <- openDoc' "A.hs" "haskell" source
1806+ docId <- createDoc "A.hs" "haskell" source
17961807 symbols <- getDocumentSymbols docId
17971808 liftIO $ symbols @?= Left
17981809 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
@@ -1800,7 +1811,7 @@ outlineTests = testGroup
18001811 ]
18011812 , testSessionWait "data family" $ do
18021813 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1803− docId <- openDoc' "A.hs" "haskell" source
1814+ docId <- createDoc "A.hs" "haskell" source
18041815 symbols <- getDocumentSymbols docId
18051816 liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]
18061817 , testSessionWait "data family instance " $ do
@@ -1809,7 +1820,7 @@ outlineTests = testGroup
18091820 , "data family A a"
18101821 , "data instance A () = A ()"
18111822 ]
1812− docId <- openDoc' "A.hs" "haskell" source
1823+ docId <- createDoc "A.hs" "haskell" source
18131824 symbols <- getDocumentSymbols docId
18141825 liftIO $ symbols @?= Left
18151826 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
@@ -1817,36 +1828,36 @@ outlineTests = testGroup
18171828 ]
18181829 , testSessionWait "constant" $ do
18191830 let source = T.unlines ["a = ()"]
1820− docId <- openDoc' "A.hs" "haskell" source
1831+ docId <- createDoc "A.hs" "haskell" source
18211832 symbols <- getDocumentSymbols docId
18221833 liftIO $ symbols @?= Left
18231834 [docSymbol "a" SkFunction (R 0 0 0 6)]
18241835 , testSessionWait "pattern" $ do
18251836 let source = T.unlines ["Just foo = Just 21"]
1826− docId <- openDoc' "A.hs" "haskell" source
1837+ docId <- createDoc "A.hs" "haskell" source
18271838 symbols <- getDocumentSymbols docId
18281839 liftIO $ symbols @?= Left
18291840 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
18301841 , testSessionWait "pattern with type signature" $ do
18311842 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1832− docId <- openDoc' "A.hs" "haskell" source
1843+ docId <- createDoc "A.hs" "haskell" source
18331844 symbols <- getDocumentSymbols docId
18341845 liftIO $ symbols @?= Left
18351846 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
18361847 , testSessionWait "function" $ do
18371848 let source = T.unlines ["a x = ()"]
1838− docId <- openDoc' "A.hs" "haskell" source
1849+ docId <- createDoc "A.hs" "haskell" source
18391850 symbols <- getDocumentSymbols docId
18401851 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
18411852 , testSessionWait "type synonym" $ do
18421853 let source = T.unlines ["type A = Bool"]
1843− docId <- openDoc' "A.hs" "haskell" source
1854+ docId <- createDoc "A.hs" "haskell" source
18441855 symbols <- getDocumentSymbols docId
18451856 liftIO $ symbols @?= Left
18461857 [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]
18471858 , testSessionWait "datatype" $ do
18481859 let source = T.unlines ["data A = C"]
1849− docId <- openDoc' "A.hs" "haskell" source
1860+ docId <- createDoc "A.hs" "haskell" source
18501861 symbols <- getDocumentSymbols docId
18511862 liftIO $ symbols @?= Left
18521863 [ docSymbolWithChildren "A"
@@ -1854,9 +1865,21 @@ outlineTests = testGroup
18541865 (R 0 0 0 10)
18551866 [docSymbol "C" SkConstructor (R 0 9 0 10)]
18561867 ]
1868+ , testSessionWait "record fields" $ do
1869+ let source = T.unlines ["data A = B {", " x :: Int", " , y :: Int}"]
1870+ docId <- createDoc "A.hs" "haskell" source
1871+ symbols <- getDocumentSymbols docId
1872+ liftIO $ symbols @=? Left
1873+ [ docSymbolWithChildren "A" SkStruct (R 0 0 2 13)
1874+ [ docSymbolWithChildren' "B" SkConstructor (R 0 9 2 13) (R 0 9 0 10)
1875+ [ docSymbol "x" SkField (R 1 2 1 3)
1876+ , docSymbol "y" SkField (R 2 4 2 5)
1877+ ]
1878+ ]
1879+ ]
18571880 , testSessionWait "import" $ do
18581881 let source = T.unlines ["import Data.Maybe"]
1859− docId <- openDoc' "A.hs" "haskell" source
1882+ docId <- createDoc "A.hs" "haskell" source
18601883 symbols <- getDocumentSymbols docId
18611884 liftIO $ symbols @?= Left
18621885 [docSymbolWithChildren "imports"
@@ -1867,7 +1890,7 @@ outlineTests = testGroup
18671890 ]
18681891 , testSessionWait "multiple import" $ do
18691892 let source = T.unlines ["", "import Data.Maybe", "", "import Control.Exception", ""]
1870− docId <- openDoc' "A.hs" "haskell" source
1893+ docId <- createDoc "A.hs" "haskell" source
18711894 symbols <- getDocumentSymbols docId
18721895 liftIO $ symbols @?= Left
18731896 [docSymbolWithChildren "imports"
@@ -1882,7 +1905,7 @@ outlineTests = testGroup
18821905 [ "{-# language ForeignFunctionInterface #-}"
18831906 , "foreign import ccall \"a\" a :: Int"
18841907 ]
1885− docId <- openDoc' "A.hs" "haskell" source
1908+ docId <- createDoc "A.hs" "haskell" source
18861909 symbols <- getDocumentSymbols docId
18871910 liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]
18881911 , testSessionWait "foreign export" $ do
@@ -1890,7 +1913,7 @@ outlineTests = testGroup
18901913 [ "{-# language ForeignFunctionInterface #-}"
18911914 , "foreign export ccall odd :: Int -> Bool"
18921915 ]
1893− docId <- openDoc' "A.hs" "haskell" source
1916+ docId <- createDoc "A.hs" "haskell" source
18941917 symbols <- getDocumentSymbols docId
18951918 liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]
18961919 ]
@@ -1903,6 +1926,8 @@ outlineTests = testGroup
19031926 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
19041927 docSymbolWithChildren name kind loc cc =
19051928 DocumentSymbol name Nothing kind Nothing loc loc (Just $ List cc)
1929+ docSymbolWithChildren' name kind loc selectionLoc cc =
1930+ DocumentSymbol name Nothing kind Nothing loc selectionLoc (Just $ List cc)
19061931 moduleSymbol name loc cc = DocumentSymbol name
19071932 Nothing
19081933 SkFile
@@ -2008,6 +2033,7 @@ cradleTests :: TestTree
20082033cradleTests = testGroup "cradle"
20092034 [testGroup "dependencies" [sessionDepsArePickedUp]
20102035 ,testGroup "loading" [loadCradleOnlyonce]
2036+ ,testGroup "multi" [simpleMultiTest, simpleMultiTest2]
20112037 ]
20122038
20132039loadCradleOnlyonce :: TestTree
@@ -2022,17 +2048,53 @@ loadCradleOnlyonce = testGroup "load cradle only once"
20222048 test dir
20232049 implicit dir = test dir
20242050 test _dir = do
2025− doc <- openDoc' "B.hs" "haskell" "module B where\nimport Data.Foo"
2051+ doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"
20262052 msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
20272053 liftIO $ length msgs @?= 1
20282054 changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]
20292055 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
20302056 liftIO $ length msgs @?= 0
2031− _ <- openDoc' "A.hs" "haskell" "module A where\nimport LoadCradleBar"
2057+ _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"
20322058 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
20332059 liftIO $ length msgs @?= 0
20342060
20352061
2062+dependentFileTest :: TestTree
2063+dependentFileTest = testGroup "addDependentFile"
2064+ [testGroup "file-changed" [testSession' "test" test]
2065+ ]
2066+ where
2067+ test dir = do
2068+ -- If the file contains B then no type error
2069+ -- otherwise type error
2070+ liftIO $ writeFile (dir </> "dep-file.txt") "A"
2071+ let fooContent = T.unlines
2072+ [ "{-# LANGUAGE TemplateHaskell #-}"
2073+ , "module Foo where"
2074+ , "import Language.Haskell.TH.Syntax"
2075+ , "foo :: Int"
2076+ , "foo = 1 + $(do"
2077+ , " qAddDependentFile \"dep-file.txt\""
2078+ , " f <- qRunIO (readFile \"dep-file.txt\")"
2079+ , " if f == \"B\" then [| 1 |] else lift f)"
2080+ ]
2081+ let bazContent = T.unlines ["module Baz where", "import Foo"]
2082+ _ <-createDoc "Foo.hs" "haskell" fooContent
2083+ doc <- createDoc "Baz.hs" "haskell" bazContent
2084+ expectDiagnostics
2085+ [("Foo.hs", [(DsError, (4, 6), "Couldn't match expected type")])]
2086+ -- Now modify the dependent file
2087+ liftIO $ writeFile (dir </> "dep-file.txt") "B"
2088+ let change = TextDocumentContentChangeEvent
2089+ { _range = Just (Range (Position 2 0) (Position 2 6))
2090+ , _rangeLength = Nothing
2091+ , _text = "f = ()"
2092+ }
2093+ -- Modifying Baz will now trigger Foo to be rebuilt as well
2094+ changeDoc doc [change]
2095+ expectDiagnostics [("Foo.hs", [])]
2096+
2097+
20362098cradleLoadedMessage :: Session FromServerMessage
20372099cradleLoadedMessage = satisfy $ \case
20382100 NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
@@ -2041,6 +2103,56 @@ cradleLoadedMessage = satisfy $ \case
20412103cradleLoadedMethod :: T.Text
20422104cradleLoadedMethod = "ghcide/cradle/loaded"
20432105
2106+-- Stack sets this which trips up cabal in the multi-component tests.
2107+-- However, our plugin tests rely on those env vars so we unset it locally.
2108+withoutStackEnv :: IO a -> IO a
2109+withoutStackEnv s =
2110+ bracket
2111+ (mapM getEnv vars >>= \prevState -> mapM_ unsetEnv vars >> pure prevState)
2112+ (\prevState -> mapM_ (\(var, value) -> restore var value) (zip vars prevState))
2113+ (const s)
2114+ where vars =
2115+ [ "GHC_PACKAGE_PATH"
2116+ , "GHC_ENVIRONMENT"
2117+ , "HASKELL_DIST_DIR"
2118+ , "HASKELL_PACKAGE_SANDBOX"
2119+ , "HASKELL_PACKAGE_SANDBOXES"
2120+ ]
2121+ restore var Nothing = unsetEnv var
2122+ restore var (Just val) = setEnv var val True
2123+
2124+simpleMultiTest :: TestTree
2125+simpleMultiTest = testCase "simple-multi-test" $ withoutStackEnv $ runWithExtraFiles "multi" $ \dir -> do
2126+ let aPath = dir </> "a/A.hs"
2127+ bPath = dir </> "b/B.hs"
2128+ aSource <- liftIO $ readFileUtf8 aPath
2129+ (TextDocumentIdentifier adoc) <- createDoc aPath "haskell" aSource
2130+ expectNoMoreDiagnostics 0.5
2131+ bSource <- liftIO $ readFileUtf8 bPath
2132+ bdoc <- createDoc bPath "haskell" bSource
2133+ expectNoMoreDiagnostics 0.5
2134+ locs <- getDefinitions bdoc (Position 2 7)
2135+ let fooL = mkL adoc 2 0 2 3
2136+ checkDefs locs (pure [fooL])
2137+ expectNoMoreDiagnostics 0.5
2138+
2139+-- Like simpleMultiTest but open the files in the other order
2140+simpleMultiTest2 :: TestTree
2141+simpleMultiTest2 = testCase "simple-multi-test2" $ withoutStackEnv $ runWithExtraFiles "multi" $ \dir -> do
2142+ let aPath = dir </> "a/A.hs"
2143+ bPath = dir </> "b/B.hs"
2144+ bSource <- liftIO $ readFileUtf8 bPath
2145+ bdoc <- createDoc bPath "haskell" bSource
2146+ expectNoMoreDiagnostics 5
2147+ aSource <- liftIO $ readFileUtf8 aPath
2148+ (TextDocumentIdentifier adoc) <- createDoc aPath "haskell" aSource
2149+ -- Need to have some delay here or the test fails
2150+ expectNoMoreDiagnostics 5
2151+ locs <- getDefinitions bdoc (Position 2 7)
2152+ let fooL = mkL adoc 2 0 2 3
2153+ checkDefs locs (pure [fooL])
2154+ expectNoMoreDiagnostics 0.5
2155+
20442156sessionDepsArePickedUp :: TestTree
20452157sessionDepsArePickedUp = testSession'
20462158 "session-deps-are-picked-up"
@@ -2050,7 +2162,7 @@ sessionDepsArePickedUp = testSession'
20502162 (dir </> "hie.yaml")
20512163 "cradle: {direct: {arguments: []}}"
20522164 -- Open without OverloadedStrings and expect an error.
2053− doc <- openDoc' "Foo.hs" "haskell" fooContent
2165+ doc <- createDoc "Foo.hs" "haskell" fooContent
20542166 expectDiagnostics
20552167 [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
20562168 -- Update hie.yaml to enable OverloadedStrings.
@@ -2085,6 +2197,9 @@ sessionDepsArePickedUp = testSession'
20852197testSession :: String -> Session () -> TestTree
20862198testSession name = testCase name . run
20872199
2200+testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree
2201+testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix
2202+
20882203testSession' :: String -> (FilePath -> Session ()) -> TestTree
20892204testSession' name = testCase name . run'
20902205
@@ -2119,6 +2234,19 @@ mkRange a b c d = Range (Position a b) (Position c d)
21192234run :: Session a -> IO a
21202235run s = withTempDir $ \dir -> runInDir dir s
21212236
2237+runWithExtraFiles :: FilePath -> (FilePath -> Session a) -> IO a
2238+runWithExtraFiles prefix s = withTempDir $ \dir -> do
2239+ copyTestDataFiles dir prefix
2240+ runInDir dir (s dir)
2241+
2242+copyTestDataFiles :: FilePath -> FilePath -> IO ()
2243+copyTestDataFiles dir prefix = do
2244+ -- Copy all the test data files to the temporary workspace
2245+ testDataFiles <- getDirectoryFilesIO ("test/data" </> prefix) ["//*"]
2246+ for_ testDataFiles $ \f -> do
2247+ createDirectoryIfMissing True $ dir </> takeDirectory f
2248+ copyFile ("test/data" </> prefix </> f) (dir </> f)
2249+
21222250run' :: (FilePath -> Session a) -> IO a
21232251run' s = withTempDir $ \dir -> runInDir dir (s dir)
21242252
@@ -2130,11 +2258,6 @@ runInDir dir s = do
21302258 -- since the package import test creates "Data/List.hs", which otherwise has no physical home
21312259 createDirectoryIfMissing True $ dir ++ "/Data"
21322260
2133− -- Copy all the test data files to the temporary workspace
2134− testDataFiles <- getDirectoryFilesIO "test/data" ["//*"]
2135− for_ testDataFiles $ \f -> do
2136− createDirectoryIfMissing True $ dir </> takeDirectory f
2137− copyFile ("test/data" </> f) (dir </> f)
21382261
21392262 let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
21402263 -- HIE calls getXgdDirectory which assumes that HOME is set.
@@ -2153,7 +2276,7 @@ runInDir dir s = do
21532276openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
21542277openTestDataDoc path = do
21552278 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2156− openDoc' path "haskell" source
2279+ createDoc path "haskell" source
21572280
21582281findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
21592282findCodeActions doc range expectedTitles = do
@@ -2194,20 +2317,6 @@ unitTests = do
21942317 uriToFilePath' uri @?= Just ""
21952318 ]
21962319
2197−-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
2198−openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2199−openDoc' fp name contents = do
2200− res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
2201− -- Needed as ghcide sets up and relies on WatchedFiles but lsp-test does not track them
2202− sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
2203− return res
2204−
2205−-- | Version of 'LSPTest.openDoc'' that does not send WatchedFiles events for files outside the workspace
2206−openDoc'' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2207−-- At the moment this is just LSPTest.openDoc' but it may change in the future
2208−-- when/if lsp-test implements WatchedFiles
2209−openDoc'' = LSPTest.openDoc'
2210−
22112320positionMappingTests :: TestTree
22122321positionMappingTests =
22132322 testGroup "position mapping"