@@ -10,7 +10,8 @@
1010module Main (main) where
1111
1212import Control.Applicative.Combinators
13−import Control.Exception (catch)
13+import Control.Exception (bracket, catch)
14+import qualified Control.Lens as Lens
1415import Control.Monad
1516import Control.Monad.IO.Class (liftIO)
1617import Data.Aeson (FromJSON, Value)
@@ -27,14 +28,14 @@ import Development.IDE.Test
2728import Development.IDE.Test.Runfiles
2829import Development.IDE.Types.Location
2930import Development.Shake (getDirectoryFilesIO)
30−import qualified Language.Haskell.LSP.Test as LSPTest
31−import Language.Haskell.LSP.Test hiding (openDoc')
31+import Language.Haskell.LSP.Test
3232import Language.Haskell.LSP.Messages
3333import Language.Haskell.LSP.Types
3434import Language.Haskell.LSP.Types.Capabilities
35+import qualified Language.Haskell.LSP.Types.Lens as Lsp (diagnostics, params, message)
3536import Language.Haskell.LSP.VFS (applyChange)
3637import Network.URI
37−import System.Environment.Blank (setEnv)
38+import System.Environment.Blank (getEnv, setEnv, unsetEnv)
3839import System.FilePath
3940import System.IO.Extra
4041import System.Directory
@@ -48,31 +49,35 @@ import Test.Tasty.QuickCheck
4849import Data.Maybe
4950
5051main :: IO ()
51−main = defaultMainWithRerun $ testGroup "HIE"
52− [ testSession "open close" $ do
53− doc <- openDoc' "Testing.hs" "haskell" ""
54− void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
55− void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
56− closeDoc doc
57− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
58− , initializeResponseTests
59− , completionTests
60− , cppTests
61− , diagnosticTests
62− , codeActionTests
63− , codeLensesTests
64− , outlineTests
65− , findDefinitionAndHoverTests
66− , pluginTests
67− , preprocessorTests
68− , thTests
69− , safeTests
70− , unitTests
71− , haddockTests
72− , positionMappingTests
73− , watchedFilesTests
74− , cradleTests
75− ]
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+ ]
7681
7782initializeResponseTests :: TestTree
7883initializeResponseTests = withResource acquire release tests where
@@ -133,7 +138,7 @@ initializeResponseTests = withResource acquire release tests where
133138
134139
135140 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
136− innerCaps (ResponseMessage _ _ (Just (InitializeResponseCapabilities c)) _) = c
141+ innerCaps (ResponseMessage _ _ (Right (InitializeResponseCapabilities c))) = c
137142 innerCaps _ = error "this test only expects inner capabilities"
138143
139144 acquire :: IO InitializeResponse
@@ -147,7 +152,7 @@ diagnosticTests :: TestTree
147152diagnosticTests = testGroup "diagnostics"
148153 [ testSessionWait "fix syntax error" $ do
149154 let content = T.unlines [ "module Testing wher" ]
150− doc <- openDoc' "Testing.hs" "haskell" content
155+ doc <- createDoc "Testing.hs" "haskell" content
151156 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
152157 let change = TextDocumentContentChangeEvent
153158 { _range = Just (Range (Position 0 15) (Position 0 19))
@@ -158,7 +163,7 @@ diagnosticTests = testGroup "diagnostics"
158163 expectDiagnostics [("Testing.hs", [])]
159164 , testSessionWait "introduce syntax error" $ do
160165 let content = T.unlines [ "module Testing where" ]
161− doc <- openDoc' "Testing.hs" "haskell" content
166+ doc <- createDoc "Testing.hs" "haskell" content
162167 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
163168 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
164169 let change = TextDocumentContentChangeEvent
@@ -176,7 +181,7 @@ diagnosticTests = testGroup "diagnostics"
176181 , "bar :: Int -> Int -> Int"
177182 , "bar a b = cd + b"
178183 ]
179− _ <- openDoc' "Testing.hs" "haskell" content
184+ _ <- createDoc "Testing.hs" "haskell" content
180185 expectDiagnostics
181186 [ ( "Testing.hs"
182187 , [ (DsError, (2, 14), "Variable not in scope: ab")
@@ -190,7 +195,7 @@ diagnosticTests = testGroup "diagnostics"
190195 , "foo :: Int -> String -> Int"
191196 , "foo a b = a + b"
192197 ]
193− _ <- openDoc' "Testing.hs" "haskell" content
198+ _ <- createDoc "Testing.hs" "haskell" content
194199 expectDiagnostics
195200 [ ( "Testing.hs"
196201 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
@@ -202,7 +207,7 @@ diagnosticTests = testGroup "diagnostics"
202207 , "foo :: Int -> String"
203208 , "foo a = _ a"
204209 ]
205− _ <- openDoc' "Testing.hs" "haskell" content
210+ _ <- createDoc "Testing.hs" "haskell" content
206211 expectDiagnostics
207212 [ ( "Testing.hs"
208213 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
@@ -224,24 +229,23 @@ diagnosticTests = testGroup "diagnostics"
224229 [ ("A.hs", [(DsError, (2,4), aMessage)])
225230 , ("B.hs", [(DsError, (3,4), bMessage)])]
226231 deferralTest title binding msg = testSessionWait title $ do
227− _ <- openDoc' "A.hs" "haskell" $ sourceA binding
228− _ <- openDoc' "B.hs" "haskell" sourceB
232+ _ <- createDoc "A.hs" "haskell" $ sourceA binding
233+ _ <- createDoc "B.hs" "haskell" sourceB
229234 expectDiagnostics $ expectedDs msg
230235 in
231236 [ deferralTest "type error" "True" "Couldn't match expected type"
232237 , deferralTest "typed hole" "_" "Found hole"
233238 , deferralTest "out of scope var" "unbound" "Variable not in scope"
234− , deferralTest "message shows error" "True" "A.hs:3:5: error:"
235239 ]
236240
237241 , testSessionWait "remove required module" $ do
238242 let contentA = T.unlines [ "module ModuleA where" ]
239− docA <- openDoc' "ModuleA.hs" "haskell" contentA
243+ docA <- createDoc "ModuleA.hs" "haskell" contentA
240244 let contentB = T.unlines
241245 [ "module ModuleB where"
242246 , "import ModuleA"
243247 ]
244− _ <- openDoc' "ModuleB.hs" "haskell" contentB
248+ _ <- createDoc "ModuleB.hs" "haskell" contentB
245249 let change = TextDocumentContentChangeEvent
246250 { _range = Just (Range (Position 0 0) (Position 0 20))
247251 , _rangeLength = Nothing
@@ -254,20 +258,20 @@ diagnosticTests = testGroup "diagnostics"
254258 [ "module ModuleB where"
255259 , "import ModuleA"
256260 ]
257− _ <- openDoc' "ModuleB.hs" "haskell" contentB
261+ _ <- createDoc "ModuleB.hs" "haskell" contentB
258262 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
259263 let contentA = T.unlines [ "module ModuleA where" ]
260− _ <- openDoc' "ModuleA.hs" "haskell" contentA
264+ _ <- createDoc "ModuleA.hs" "haskell" contentA
261265 expectDiagnostics [("ModuleB.hs", [])]
262266 , testSessionWait "add missing module (non workspace)" $ do
263267 let contentB = T.unlines
264268 [ "module ModuleB where"
265269 , "import ModuleA"
266270 ]
267− _ <- openDoc'' "/tmp/ModuleB.hs" "haskell" contentB
271+ _ <- createDoc "/tmp/ModuleB.hs" "haskell" contentB
268272 expectDiagnostics [("/tmp/ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
269273 let contentA = T.unlines [ "module ModuleA where" ]
270− _ <- openDoc'' "/tmp/ModuleA.hs" "haskell" contentA
274+ _ <- createDoc "/tmp/ModuleA.hs" "haskell" contentA
271275 expectDiagnostics [("/tmp/ModuleB.hs", [])]
272276 , testSessionWait "cyclic module dependency" $ do
273277 let contentA = T.unlines
@@ -278,8 +282,8 @@ diagnosticTests = testGroup "diagnostics"
278282 [ "module ModuleB where"
279283 , "import ModuleA"
280284 ]
281− _ <- openDoc' "ModuleA.hs" "haskell" contentA
282− _ <- openDoc' "ModuleB.hs" "haskell" contentB
285+ _ <- createDoc "ModuleA.hs" "haskell" contentA
286+ _ <- createDoc "ModuleB.hs" "haskell" contentB
283287 expectDiagnostics
284288 [ ( "ModuleA.hs"
285289 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
@@ -300,9 +304,9 @@ diagnosticTests = testGroup "diagnostics"
300304 let contentBboot = T.unlines
301305 [ "module ModuleB where"
302306 ]
303− _ <- openDoc' "ModuleA.hs" "haskell" contentA
304− _ <- openDoc' "ModuleB.hs" "haskell" contentB
305− _ <- 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
306310 expectDiagnostics []
307311 , testSessionWait "correct reference used with hs-boot" $ do
308312 let contentB = T.unlines
@@ -324,10 +328,10 @@ diagnosticTests = testGroup "diagnostics"
324328 -- resolved to the hs-boot file
325329 , "y = x"
326330 ]
327− _ <- openDoc' "ModuleB.hs" "haskell" contentB
328− _ <- openDoc' "ModuleA.hs" "haskell" contentA
329− _ <- openDoc' "ModuleA.hs-boot" "haskell" contentAboot
330− _ <- 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
331335 expectDiagnostics []
332336 , testSessionWait "redundant import" $ do
333337 let contentA = T.unlines ["module ModuleA where"]
@@ -336,8 +340,8 @@ diagnosticTests = testGroup "diagnostics"
336340 , "module ModuleB where"
337341 , "import ModuleA"
338342 ]
339− _ <- openDoc' "ModuleA.hs" "haskell" contentA
340− _ <- openDoc' "ModuleB.hs" "haskell" contentB
343+ _ <- createDoc "ModuleA.hs" "haskell" contentA
344+ _ <- createDoc "ModuleB.hs" "haskell" contentB
341345 expectDiagnostics
342346 [ ( "ModuleB.hs"
343347 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
@@ -359,8 +363,8 @@ diagnosticTests = testGroup "diagnostics"
359363 , "wrong1 = ThisList.map"
360364 , "wrong2 = BaseList.x"
361365 ]
362− _ <- openDoc' "Data/List.hs" "haskell" thisDataListContent
363− _ <- openDoc' "Main.hs" "haskell" mainContent
366+ _ <- createDoc "Data/List.hs" "haskell" thisDataListContent
367+ _ <- createDoc "Main.hs" "haskell" mainContent
364368 expectDiagnostics
365369 [ ( "Main.hs"
366370 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
@@ -375,7 +379,7 @@ diagnosticTests = testGroup "diagnostics"
375379 , "foo :: Ord a => a -> Int"
376380 , "foo a = 1"
377381 ]
378− _ <- openDoc' "Foo.hs" "haskell" fooContent
382+ _ <- createDoc "Foo.hs" "haskell" fooContent
379383 expectDiagnostics
380384 [ ( "Foo.hs"
381385 -- The test is to make sure that warnings contain unqualified names
@@ -426,13 +430,32 @@ diagnosticTests = testGroup "diagnostics"
426430 , "foo :: Int"
427431 , "foo = 1 {-|-}"
428432 ]
429− _ <- openDoc' "Foo.hs" "haskell" fooContent
433+ _ <- createDoc "Foo.hs" "haskell" fooContent
430434 expectDiagnostics
431435 [ ( "Foo.hs"
432436 , [(DsError, (2, 8), "Parse error on input")
433437 ]
434438 )
435439 ]
440+ , testSessionWait "strip file path" $ do
441+ let
442+ name = "Testing"
443+ content = T.unlines
444+ [ "module " <> name <> " where"
445+ , "value :: Maybe ()"
446+ , "value = [()]"
447+ ]
448+ _ <- createDoc (T.unpack name <> ".hs") "haskell" content
449+ notification <- skipManyTill anyMessage diagnostic
450+ let
451+ offenders =
452+ Lsp.params .
453+ Lsp.diagnostics .
454+ Lens.folded .
455+ Lsp.message .
456+ Lens.filtered (T.isInfixOf ("/" <> name <> ".hs:"))
457+ failure msg = liftIO $ assertFailure $ "Expected file path to be stripped but got " <> T.unpack msg
458+ Lens.mapMOf_ offenders failure notification
436459 ]
437460
438461codeActionTests :: TestTree
@@ -459,29 +482,25 @@ watchedFilesTests :: TestTree
459482watchedFilesTests = testGroup "watched files"
460483 [ testSession' "workspace files" $ \sessionDir -> do
461484 liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\"]}}"
462− _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"
463486 watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
464487
465− -- 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):
466489 -- - /path-to-workspace/WatchedFilesMissingModule.hs
467490 -- - /path-to-workspace/WatchedFilesMissingModule.lhs
468− -- - WatchedFilesMissingModule.hs
469− -- - WatchedFilesMissingModule.lhs
470− -- - src/WatchedFilesMissingModule.hs
471− -- - src/WatchedFilesMissingModule.lhs
472− liftIO $ length watchedFileRegs @?= 6
491+ -- - /path-to-workspace/src/WatchedFilesMissingModule.hs
492+ -- - /path-to-workspace/src/WatchedFilesMissingModule.lhs
493+ liftIO $ length watchedFileRegs @?= 4
473494
474495 , testSession' "non workspace file" $ \sessionDir -> do
475496 liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-i/tmp\"]}}"
476− _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"
477498 watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
478499
479− -- 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):
480501 -- - /path-to-workspace/WatchedFilesMissingModule.hs
481502 -- - /path-to-workspace/WatchedFilesMissingModule.lhs
482− -- - WatchedFilesMissingModule.hs
483− -- - WatchedFilesMissingModule.lhs
484− liftIO $ length watchedFileRegs @?= 4
503+ liftIO $ length watchedFileRegs @?= 2
485504
486505 -- TODO add a test for didChangeWorkspaceFolder
487506 ]
@@ -494,7 +513,7 @@ renameActionTests = testGroup "rename actions"
494513 , "foo :: Int -> Int"
495514 , "foo argName = argNme"
496515 ]
497− doc <- openDoc' "Testing.hs" "haskell" content
516+ doc <- createDoc "Testing.hs" "haskell" content
498517 _ <- waitForDiagnostics
499518 action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’"
500519 executeCodeAction action
@@ -512,7 +531,7 @@ renameActionTests = testGroup "rename actions"
512531 , "foo :: Maybe a -> [a]"
513532 , "foo = maybToList"
514533 ]
515− doc <- openDoc' "Testing.hs" "haskell" content
534+ doc <- createDoc "Testing.hs" "haskell" content
516535 _ <- waitForDiagnostics
517536 action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’"
518537 executeCodeAction action
@@ -530,7 +549,7 @@ renameActionTests = testGroup "rename actions"
530549 , "foo :: Char -> Char -> Char -> Char"
531550 , "foo argument1 argument2 argument3 = argumentX"
532551 ]
533− doc <- openDoc' "Testing.hs" "haskell" content
552+ doc <- createDoc "Testing.hs" "haskell" content
534553 _ <- waitForDiagnostics
535554 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
536555 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
@@ -542,7 +561,7 @@ renameActionTests = testGroup "rename actions"
542561 , "monus x y = max 0 (x - y)"
543562 , "foo x y = x `monnus` y"
544563 ]
545− doc <- openDoc' "Testing.hs" "haskell" content
564+ doc <- createDoc "Testing.hs" "haskell" content
546565 _ <- waitForDiagnostics
547566 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
548567 [fixTypo] <- pure [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ]
@@ -565,7 +584,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
565584 , "func :: _"
566585 , "func x = x"
567586 ]
568− doc <- openDoc' "Testing.hs" "haskell" content
587+ doc <- createDoc "Testing.hs" "haskell" content
569588 _ <- waitForDiagnostics
570589 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
571590 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -585,7 +604,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
585604 , "func :: _"
586605 , "func x y = x + y"
587606 ]
588− doc <- openDoc' "Testing.hs" "haskell" content
607+ doc <- createDoc "Testing.hs" "haskell" content
589608 _ <- waitForDiagnostics
590609 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
591610 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -608,7 +627,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
608627 , " y = x * 2"
609628 , " in y"
610629 ]
611− doc <- openDoc' "Testing.hs" "haskell" content
630+ doc <- createDoc "Testing.hs" "haskell" content
612631 _ <- waitForDiagnostics
613632 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
614633 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -633,7 +652,7 @@ removeImportTests = testGroup "remove import actions"
633652 let contentA = T.unlines
634653 [ "module ModuleA where"
635654 ]
636− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
655+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
637656 let contentB = T.unlines
638657 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
639658 , "module ModuleB where"
@@ -641,7 +660,7 @@ removeImportTests = testGroup "remove import actions"
641660 , "stuffB :: Integer"
642661 , "stuffB = 123"
643662 ]
644− docB <- openDoc' "ModuleB.hs" "haskell" contentB
663+ docB <- createDoc "ModuleB.hs" "haskell" contentB
645664 _ <- waitForDiagnostics
646665 [CACodeAction action@CodeAction { _title = actionTitle }]
647666 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -659,7 +678,7 @@ removeImportTests = testGroup "remove import actions"
659678 let contentA = T.unlines
660679 [ "module ModuleA where"
661680 ]
662− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
681+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
663682 let contentB = T.unlines
664683 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
665684 , "module ModuleB where"
@@ -667,7 +686,7 @@ removeImportTests = testGroup "remove import actions"
667686 , "stuffB :: Integer"
668687 , "stuffB = 123"
669688 ]
670− docB <- openDoc' "ModuleB.hs" "haskell" contentB
689+ docB <- createDoc "ModuleB.hs" "haskell" contentB
671690 _ <- waitForDiagnostics
672691 [CACodeAction action@CodeAction { _title = actionTitle }]
673692 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -689,14 +708,14 @@ removeImportTests = testGroup "remove import actions"
689708 , "stuffB = 123"
690709 , "stuffC = ()"
691710 ]
692− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
711+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
693712 let contentB = T.unlines
694713 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
695714 , "module ModuleB where"
696715 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
697716 , "main = print stuffB"
698717 ]
699− docB <- openDoc' "ModuleB.hs" "haskell" contentB
718+ docB <- createDoc "ModuleB.hs" "haskell" contentB
700719 _ <- waitForDiagnostics
701720 [CACodeAction action@CodeAction { _title = actionTitle }]
702721 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -718,22 +737,18 @@ removeImportTests = testGroup "remove import actions"
718737 , "stuffB :: Integer"
719738 , "stuffB = 123"
720739 ]
721− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
740+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
722741 let contentB = T.unlines
723742 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
724743 , "module ModuleB where"
725744 , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
726745 , "main = print A.stuffB"
727746 ]
728− docB <- openDoc' "ModuleB.hs" "haskell" contentB
747+ docB <- createDoc "ModuleB.hs" "haskell" contentB
729748 _ <- waitForDiagnostics
730749 [CACodeAction action@CodeAction { _title = actionTitle }]
731750 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
732−#if MIN_GHC_API_VERSION(8,6,0)
733751 liftIO $ "Remove !!, <?> from import" @=? actionTitle
734−#else
735− liftIO $ "Remove A.!!, A.<?> from import" @=? actionTitle
736−#endif
737752 executeCodeAction action
738753 contentAfterAction <- documentContents docB
739754 let expectedContentAfterAction = T.unlines
@@ -750,14 +765,14 @@ removeImportTests = testGroup "remove import actions"
750765 , "stuffB :: Integer"
751766 , "stuffB = 123"
752767 ]
753− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
768+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
754769 let contentB = T.unlines
755770 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
756771 , "module ModuleB where"
757772 , "import ModuleA (A(..), stuffB)"
758773 , "main = print stuffB"
759774 ]
760− docB <- openDoc' "ModuleB.hs" "haskell" contentB
775+ docB <- createDoc "ModuleB.hs" "haskell" contentB
761776 _ <- waitForDiagnostics
762777 [CACodeAction action@CodeAction { _title = actionTitle }]
763778 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -777,14 +792,14 @@ removeImportTests = testGroup "remove import actions"
777792 , "data D = A | B"
778793 , "data E = F"
779794 ]
780− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
795+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
781796 let contentB = T.unlines
782797 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
783798 , "module ModuleB where"
784799 , "import ModuleA (D(A,B), E(F))"
785800 , "main = B"
786801 ]
787− docB <- openDoc' "ModuleB.hs" "haskell" contentB
802+ docB <- createDoc "ModuleB.hs" "haskell" contentB
788803 _ <- waitForDiagnostics
789804 [CACodeAction action@CodeAction { _title = actionTitle }]
790805 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -925,8 +940,8 @@ extendImportTests = testGroup "extend import actions"
925940 ]
926941 where
927942 template contentA contentB range expectedAction expectedContentB = do
928− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
929− docB <- openDoc' "ModuleB.hs" "haskell" contentB
943+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
944+ docB <- createDoc "ModuleB.hs" "haskell" contentB
930945 _ <- waitForDiagnostics
931946 CACodeAction action@CodeAction { _title = actionTitle } : _
932947 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -976,7 +991,7 @@ suggestImportTests = testGroup "suggest import actions"
976991 after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other
977992 cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -]}}"
978993 liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle
979− doc <- openDoc' "Test.hs" "haskell" before
994+ doc <- createDoc "Test.hs" "haskell" before
980995 _diags <- waitForDiagnostics
981996 let defLine = length imps + 1
982997 range = Range (Position defLine 0) (Position defLine maxBound)
@@ -1036,7 +1051,7 @@ addExtensionTests = testGroup "add language extension actions"
10361051 ]
10371052 where
10381053 template initialContent range expectedAction expectedContents = do
1039− doc <- openDoc' "Module.hs" "haskell" initialContent
1054+ doc <- createDoc "Module.hs" "haskell" initialContent
10401055 _ <- waitForDiagnostics
10411056 CACodeAction action@CodeAction { _title = actionTitle } : _
10421057 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1059,7 +1074,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
10591074 [""
10601075 ,"someOtherCode = ()"
10611076 ]
1062− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1077+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
10631078 _ <- waitForDiagnostics
10641079 CACodeAction action@CodeAction { _title = actionTitle } : _
10651080 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1083,7 +1098,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
10831098 [""
10841099 ,"someOtherCode = ()"
10851100 ]
1086− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1101+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
10871102 _ <- waitForDiagnostics
10881103 CACodeAction action@CodeAction { _title = actionTitle } : _
10891104 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1122,8 +1137,8 @@ fixConstructorImportTests = testGroup "fix import actions"
11221137 ]
11231138 where
11241139 template contentA contentB range expectedAction expectedContentB = do
1125− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
1126− docB <- openDoc' "ModuleB.hs" "haskell" contentB
1140+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
1141+ docB <- createDoc "ModuleB.hs" "haskell" contentB
11271142 _diags <- waitForDiagnostics
11281143 CACodeAction action@CodeAction { _title = actionTitle } : _
11291144 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1142,7 +1157,7 @@ importRenameActionTests = testGroup "import rename actions"
11421157 [ "module Testing where"
11431158 , "import Data.Mape"
11441159 ]
1145− doc <- openDoc' "Testing.hs" "haskell" content
1160+ doc <- createDoc "Testing.hs" "haskell" content
11461161 _ <- waitForDiagnostics
11471162 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
11481163 let [changeToMap] = [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]
@@ -1179,7 +1194,7 @@ fillTypedHoleTests = let
11791194 newA newB newC = testSession (T.unpack actionTitle) $ do
11801195 let originalCode = sourceCode oldA oldB oldC
11811196 let expectedCode = sourceCode newA newB newC
1182− doc <- openDoc' "Testing.hs" "haskell" originalCode
1197+ doc <- createDoc "Testing.hs" "haskell" originalCode
11831198 _ <- waitForDiagnostics
11841199 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
11851200 chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
@@ -1227,7 +1242,7 @@ addSigActionTests = let
12271242 def >:: sig = testSession (T.unpack def) $ do
12281243 let originalCode = before def
12291244 let expectedCode = after' def sig
1230− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1245+ doc <- createDoc "Sigs.hs" "haskell" originalCode
12311246 _ <- waitForDiagnostics
12321247 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
12331248 chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
@@ -1259,7 +1274,7 @@ addSigLensesTests = let
12591274 sigSession withMissing def sig = testSession (T.unpack def) $ do
12601275 let originalCode = before withMissing def
12611276 let expectedCode = after' withMissing def sig
1262− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1277+ doc <- createDoc "Sigs.hs" "haskell" originalCode
12631278 [CodeLens {_command = Just c}] <- getCodeLenses doc
12641279 executeCommand c
12651280 modifiedCode <- getDocumentEdit doc
@@ -1281,27 +1296,19 @@ addSigLensesTests = let
12811296 ]
12821297 ]
12831298
1284−findDefinitionAndHoverTests :: TestTree
1285−findDefinitionAndHoverTests = let
1286−
1287− tst (get, check) pos targetRange title = testSession title $ do
1288− doc <- openTestDataDoc sourceFilePath
1289− found <- get doc pos
1290− check found targetRange
1291−
1292− checkDefs :: [Location] -> Session [Expect] -> Session ()
1293− checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
1299+checkDefs :: [Location] -> Session [Expect] -> Session ()
1300+checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
12941301
1295− check (ExpectRange expectedRange) = do
1296− assertNDefinitionsFound 1 defs
1297− assertRangeCorrect (head defs) expectedRange
1298− check (ExpectLocation expectedLocation) = do
1299− assertNDefinitionsFound 1 defs
1300− liftIO $ head defs @?= expectedLocation
1301− check ExpectNoDefinitions = do
1302− assertNDefinitionsFound 0 defs
1303− check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1304− 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
13051312
13061313 assertNDefinitionsFound :: Int -> [a] -> Session ()
13071314 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
@@ -1309,6 +1316,17 @@ findDefinitionAndHoverTests = let
13091316 assertRangeCorrect Location{_range = foundRange} expectedRange =
13101317 liftIO $ expectedRange @=? foundRange
13111318
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+
13121330 checkHover :: Maybe Hover -> Session [Expect] -> Session ()
13131331 checkHover hover expectations = traverse_ check =<< expectations where
13141332
@@ -1353,7 +1371,8 @@ findDefinitionAndHoverTests = let
13531371
13541372 mkFindTests tests = testGroup "get"
13551373 [ testGroup "definition" $ mapMaybe fst tests
1356− , testGroup "hover" $ mapMaybe snd tests ]
1374+ , testGroup "hover" $ mapMaybe snd tests
1375+ , checkFileCompiles sourceFilePath ]
13571376
13581377 test runDef runHover look expect = testM runDef runHover look (return expect)
13591378
@@ -1448,6 +1467,14 @@ findDefinitionAndHoverTests = let
14481467 broken = Just . (`xfail` "known broken")
14491468 no = const Nothing -- don't run this test at all
14501469
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+
14511478pluginTests :: TestTree
14521479pluginTests = (`xfail8101` "known broken (#556)")
14531480 $ testSessionWait "plugins" $ do
@@ -1464,7 +1491,7 @@ pluginTests = (`xfail8101` "known broken (#556)")
14641491 , "foo :: Int -> Int -> Int"
14651492 , "foo a b = a + c"
14661493 ]
1467− _ <- openDoc' "Testing.hs" "haskell" content
1494+ _ <- createDoc "Testing.hs" "haskell" content
14681495 expectDiagnostics
14691496 [ ( "Testing.hs",
14701497 [(DsError, (8, 14), "Variable not in scope: c")]
@@ -1491,7 +1518,7 @@ cppTests =
14911518 run $ expectError content (2, 1)
14921519 )
14931520 , testSessionWait "cpp-ghcide" $ do
1494− _ <- openDoc' "A.hs" "haskell" $ T.unlines
1521+ _ <- createDoc "A.hs" "haskell" $ T.unlines
14951522 ["{-# LANGUAGE CPP #-}"
14961523 ,"main ="
14971524 ,"#ifdef __GHCIDE__"
@@ -1505,7 +1532,7 @@ cppTests =
15051532 where
15061533 expectError :: T.Text -> Cursor -> Session ()
15071534 expectError content cursor = do
1508− _ <- openDoc' "Testing.hs" "haskell" content
1535+ _ <- createDoc "Testing.hs" "haskell" content
15091536 expectDiagnostics
15101537 [ ( "Testing.hs",
15111538 [(DsError, cursor, "error: unterminated")]
@@ -1521,7 +1548,7 @@ preprocessorTests = testSessionWait "preprocessor" $ do
15211548 , "module Testing where"
15221549 , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
15231550 ]
1524− _ <- openDoc' "Testing.hs" "haskell" content
1551+ _ <- createDoc "Testing.hs" "haskell" content
15251552 expectDiagnostics
15261553 [ ( "Testing.hs",
15271554 [(DsError, (2, 8), "Variable not in scope: z")]
@@ -1554,8 +1581,8 @@ safeTests =
15541581 ,"safeId = trustWorthyId"
15551582 ]
15561583
1557− _ <- openDoc' "A.hs" "haskell" sourceA
1558− _ <- openDoc' "B.hs" "haskell" sourceB
1584+ _ <- createDoc "A.hs" "haskell" sourceA
1585+ _ <- createDoc "B.hs" "haskell" sourceB
15591586 expectNoMoreDiagnostics 1 ]
15601587
15611588thTests :: TestTree
@@ -1583,8 +1610,8 @@ thTests =
15831610 "b :: Integer",
15841611 "b = $(litE $ IntegerL $ a) + n"
15851612 ]
1586− _ <- openDoc' "A.hs" "haskell" sourceA
1587− _ <- openDoc' "B.hs" "haskell" sourceB
1613+ _ <- createDoc "A.hs" "haskell" sourceA
1614+ _ <- createDoc "B.hs" "haskell" sourceB
15881615 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
15891616 , testSessionWait "newtype-closure" $ do
15901617 let sourceA =
@@ -1604,8 +1631,8 @@ thTests =
16041631 ,"import A"
16051632 ,"b :: Int"
16061633 ,"b = $( a )" ]
1607− _ <- openDoc' "A.hs" "haskell" sourceA
1608− _ <- openDoc' "B.hs" "haskell" sourceB
1634+ _ <- createDoc "A.hs" "haskell" sourceA
1635+ _ <- createDoc "B.hs" "haskell" sourceB
16091636 return ()
16101637 ]
16111638
@@ -1614,7 +1641,7 @@ completionTests
16141641 = testGroup "completion"
16151642 [ testSessionWait "variable" $ do
16161643 let source = T.unlines ["module A where", "f = hea"]
1617− docId <- openDoc' "A.hs" "haskell" source
1644+ docId <- createDoc "A.hs" "haskell" source
16181645 compls <- getCompletions docId (Position 1 7)
16191646 liftIO $ map dropDocs compls @?=
16201647 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
@@ -1626,7 +1653,7 @@ completionTests
16261653 ]
16271654 , testSessionWait "constructor" $ do
16281655 let source = T.unlines ["module A where", "f = Tru"]
1629− docId <- openDoc' "A.hs" "haskell" source
1656+ docId <- createDoc "A.hs" "haskell" source
16301657 compls <- getCompletions docId (Position 1 7)
16311658 liftIO $ map dropDocs compls @?=
16321659 [ complItem "True" (Just CiConstructor) (Just "Bool")
@@ -1638,7 +1665,7 @@ completionTests
16381665 ]
16391666 , testSessionWait "type" $ do
16401667 let source = T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: ()", "f = ()"]
1641− docId <- openDoc' "A.hs" "haskell" source
1668+ docId <- createDoc "A.hs" "haskell" source
16421669 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
16431670 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]]
16441671 compls <- getCompletions docId (Position 2 7)
@@ -1655,7 +1682,7 @@ completionTests
16551682 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
16561683 , testSessionWait "qualified" $ do
16571684 let source = T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = ()"]
1658− docId <- openDoc' "A.hs" "haskell" source
1685+ docId <- createDoc "A.hs" "haskell" source
16591686 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
16601687 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]]
16611688 compls <- getCompletions docId (Position 2 15)
@@ -1669,7 +1696,7 @@ completionTests
16691696 ]
16701697 , testSessionWait "keyword" $ do
16711698 let source = T.unlines ["module A where", "f = newty"]
1672− docId <- openDoc' "A.hs" "haskell" source
1699+ docId <- createDoc "A.hs" "haskell" source
16731700 compls <- getCompletions docId (Position 1 9)
16741701 liftIO $ compls @?= [keywordItem "newtype"]
16751702 , testSessionWait "type context" $ do
@@ -1678,7 +1705,7 @@ completionTests
16781705 , "module A () where"
16791706 , "f = f"
16801707 ]
1681− docId <- openDoc' "A.hs" "haskell" source
1708+ docId <- createDoc "A.hs" "haskell" source
16821709 expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
16831710 changeDoc docId
16841711 [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
@@ -1746,7 +1773,7 @@ outlineTests = testGroup
17461773 "outline"
17471774 [ testSessionWait "type class" $ do
17481775 let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]
1749− docId <- openDoc' "A.hs" "haskell" source
1776+ docId <- createDoc "A.hs" "haskell" source
17501777 symbols <- getDocumentSymbols docId
17511778 liftIO $ symbols @?= Left
17521779 [ moduleSymbol
@@ -1759,7 +1786,7 @@ outlineTests = testGroup
17591786 ]
17601787 , testSessionWait "type class instance " $ do
17611788 let source = T.unlines ["class A a where", "instance A () where"]
1762− docId <- openDoc' "A.hs" "haskell" source
1789+ docId <- createDoc "A.hs" "haskell" source
17631790 symbols <- getDocumentSymbols docId
17641791 liftIO $ symbols @?= Left
17651792 [ classSymbol "A a" (R 0 0 0 15) []
@@ -1767,7 +1794,7 @@ outlineTests = testGroup
17671794 ]
17681795 , testSessionWait "type family" $ do
17691796 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1770− docId <- openDoc' "A.hs" "haskell" source
1797+ docId <- createDoc "A.hs" "haskell" source
17711798 symbols <- getDocumentSymbols docId
17721799 liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]
17731800 , testSessionWait "type family instance " $ do
@@ -1776,7 +1803,7 @@ outlineTests = testGroup
17761803 , "type family A a"
17771804 , "type instance A () = ()"
17781805 ]
1779− docId <- openDoc' "A.hs" "haskell" source
1806+ docId <- createDoc "A.hs" "haskell" source
17801807 symbols <- getDocumentSymbols docId
17811808 liftIO $ symbols @?= Left
17821809 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
@@ -1784,7 +1811,7 @@ outlineTests = testGroup
17841811 ]
17851812 , testSessionWait "data family" $ do
17861813 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1787− docId <- openDoc' "A.hs" "haskell" source
1814+ docId <- createDoc "A.hs" "haskell" source
17881815 symbols <- getDocumentSymbols docId
17891816 liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]
17901817 , testSessionWait "data family instance " $ do
@@ -1793,7 +1820,7 @@ outlineTests = testGroup
17931820 , "data family A a"
17941821 , "data instance A () = A ()"
17951822 ]
1796− docId <- openDoc' "A.hs" "haskell" source
1823+ docId <- createDoc "A.hs" "haskell" source
17971824 symbols <- getDocumentSymbols docId
17981825 liftIO $ symbols @?= Left
17991826 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
@@ -1801,36 +1828,36 @@ outlineTests = testGroup
18011828 ]
18021829 , testSessionWait "constant" $ do
18031830 let source = T.unlines ["a = ()"]
1804− docId <- openDoc' "A.hs" "haskell" source
1831+ docId <- createDoc "A.hs" "haskell" source
18051832 symbols <- getDocumentSymbols docId
18061833 liftIO $ symbols @?= Left
18071834 [docSymbol "a" SkFunction (R 0 0 0 6)]
18081835 , testSessionWait "pattern" $ do
18091836 let source = T.unlines ["Just foo = Just 21"]
1810− docId <- openDoc' "A.hs" "haskell" source
1837+ docId <- createDoc "A.hs" "haskell" source
18111838 symbols <- getDocumentSymbols docId
18121839 liftIO $ symbols @?= Left
18131840 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
18141841 , testSessionWait "pattern with type signature" $ do
18151842 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1816− docId <- openDoc' "A.hs" "haskell" source
1843+ docId <- createDoc "A.hs" "haskell" source
18171844 symbols <- getDocumentSymbols docId
18181845 liftIO $ symbols @?= Left
18191846 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
18201847 , testSessionWait "function" $ do
18211848 let source = T.unlines ["a x = ()"]
1822− docId <- openDoc' "A.hs" "haskell" source
1849+ docId <- createDoc "A.hs" "haskell" source
18231850 symbols <- getDocumentSymbols docId
18241851 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
18251852 , testSessionWait "type synonym" $ do
18261853 let source = T.unlines ["type A = Bool"]
1827− docId <- openDoc' "A.hs" "haskell" source
1854+ docId <- createDoc "A.hs" "haskell" source
18281855 symbols <- getDocumentSymbols docId
18291856 liftIO $ symbols @?= Left
18301857 [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]
18311858 , testSessionWait "datatype" $ do
18321859 let source = T.unlines ["data A = C"]
1833− docId <- openDoc' "A.hs" "haskell" source
1860+ docId <- createDoc "A.hs" "haskell" source
18341861 symbols <- getDocumentSymbols docId
18351862 liftIO $ symbols @?= Left
18361863 [ docSymbolWithChildren "A"
@@ -1838,9 +1865,21 @@ outlineTests = testGroup
18381865 (R 0 0 0 10)
18391866 [docSymbol "C" SkConstructor (R 0 9 0 10)]
18401867 ]
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+ ]
18411880 , testSessionWait "import" $ do
18421881 let source = T.unlines ["import Data.Maybe"]
1843− docId <- openDoc' "A.hs" "haskell" source
1882+ docId <- createDoc "A.hs" "haskell" source
18441883 symbols <- getDocumentSymbols docId
18451884 liftIO $ symbols @?= Left
18461885 [docSymbolWithChildren "imports"
@@ -1851,7 +1890,7 @@ outlineTests = testGroup
18511890 ]
18521891 , testSessionWait "multiple import" $ do
18531892 let source = T.unlines ["", "import Data.Maybe", "", "import Control.Exception", ""]
1854− docId <- openDoc' "A.hs" "haskell" source
1893+ docId <- createDoc "A.hs" "haskell" source
18551894 symbols <- getDocumentSymbols docId
18561895 liftIO $ symbols @?= Left
18571896 [docSymbolWithChildren "imports"
@@ -1866,7 +1905,7 @@ outlineTests = testGroup
18661905 [ "{-# language ForeignFunctionInterface #-}"
18671906 , "foreign import ccall \"a\" a :: Int"
18681907 ]
1869− docId <- openDoc' "A.hs" "haskell" source
1908+ docId <- createDoc "A.hs" "haskell" source
18701909 symbols <- getDocumentSymbols docId
18711910 liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]
18721911 , testSessionWait "foreign export" $ do
@@ -1874,7 +1913,7 @@ outlineTests = testGroup
18741913 [ "{-# language ForeignFunctionInterface #-}"
18751914 , "foreign export ccall odd :: Int -> Bool"
18761915 ]
1877− docId <- openDoc' "A.hs" "haskell" source
1916+ docId <- createDoc "A.hs" "haskell" source
18781917 symbols <- getDocumentSymbols docId
18791918 liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]
18801919 ]
@@ -1887,6 +1926,8 @@ outlineTests = testGroup
18871926 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
18881927 docSymbolWithChildren name kind loc cc =
18891928 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)
18901931 moduleSymbol name loc cc = DocumentSymbol name
18911932 Nothing
18921933 SkFile
@@ -1992,6 +2033,7 @@ cradleTests :: TestTree
19922033cradleTests = testGroup "cradle"
19932034 [testGroup "dependencies" [sessionDepsArePickedUp]
19942035 ,testGroup "loading" [loadCradleOnlyonce]
2036+ ,testGroup "multi" [simpleMultiTest, simpleMultiTest2]
19952037 ]
19962038
19972039loadCradleOnlyonce :: TestTree
@@ -2006,17 +2048,53 @@ loadCradleOnlyonce = testGroup "load cradle only once"
20062048 test dir
20072049 implicit dir = test dir
20082050 test _dir = do
2009− doc <- openDoc' "B.hs" "haskell" "module B where\nimport Data.Foo"
2051+ doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"
20102052 msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
20112053 liftIO $ length msgs @?= 1
20122054 changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]
20132055 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
20142056 liftIO $ length msgs @?= 0
2015− _ <- openDoc' "A.hs" "haskell" "module A where\nimport LoadCradleBar"
2057+ _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"
20162058 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
20172059 liftIO $ length msgs @?= 0
20182060
20192061
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+
20202098cradleLoadedMessage :: Session FromServerMessage
20212099cradleLoadedMessage = satisfy $ \case
20222100 NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
@@ -2025,6 +2103,56 @@ cradleLoadedMessage = satisfy $ \case
20252103cradleLoadedMethod :: T.Text
20262104cradleLoadedMethod = "ghcide/cradle/loaded"
20272105
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+
20282156sessionDepsArePickedUp :: TestTree
20292157sessionDepsArePickedUp = testSession'
20302158 "session-deps-are-picked-up"
@@ -2034,7 +2162,7 @@ sessionDepsArePickedUp = testSession'
20342162 (dir </> "hie.yaml")
20352163 "cradle: {direct: {arguments: []}}"
20362164 -- Open without OverloadedStrings and expect an error.
2037− doc <- openDoc' "Foo.hs" "haskell" fooContent
2165+ doc <- createDoc "Foo.hs" "haskell" fooContent
20382166 expectDiagnostics
20392167 [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
20402168 -- Update hie.yaml to enable OverloadedStrings.
@@ -2069,6 +2197,9 @@ sessionDepsArePickedUp = testSession'
20692197testSession :: String -> Session () -> TestTree
20702198testSession name = testCase name . run
20712199
2200+testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree
2201+testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix
2202+
20722203testSession' :: String -> (FilePath -> Session ()) -> TestTree
20732204testSession' name = testCase name . run'
20742205
@@ -2103,6 +2234,19 @@ mkRange a b c d = Range (Position a b) (Position c d)
21032234run :: Session a -> IO a
21042235run s = withTempDir $ \dir -> runInDir dir s
21052236
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+
21062250run' :: (FilePath -> Session a) -> IO a
21072251run' s = withTempDir $ \dir -> runInDir dir (s dir)
21082252
@@ -2114,11 +2258,6 @@ runInDir dir s = do
21142258 -- since the package import test creates "Data/List.hs", which otherwise has no physical home
21152259 createDirectoryIfMissing True $ dir ++ "/Data"
21162260
2117− -- Copy all the test data files to the temporary workspace
2118− testDataFiles <- getDirectoryFilesIO "test/data" ["//*"]
2119− for_ testDataFiles $ \f -> do
2120− createDirectoryIfMissing True $ dir </> takeDirectory f
2121− copyFile ("test/data" </> f) (dir </> f)
21222261
21232262 let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
21242263 -- HIE calls getXgdDirectory which assumes that HOME is set.
@@ -2137,7 +2276,7 @@ runInDir dir s = do
21372276openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
21382277openTestDataDoc path = do
21392278 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2140− openDoc' path "haskell" source
2279+ createDoc path "haskell" source
21412280
21422281findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
21432282findCodeActions doc range expectedTitles = do
@@ -2178,20 +2317,6 @@ unitTests = do
21782317 uriToFilePath' uri @?= Just ""
21792318 ]
21802319
2181−-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
2182−openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2183−openDoc' fp name contents = do
2184− res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
2185− -- Needed as ghcide sets up and relies on WatchedFiles but lsp-test does not track them
2186− sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
2187− return res
2188−
2189−-- | Version of 'LSPTest.openDoc'' that does not send WatchedFiles events for files outside the workspace
2190−openDoc'' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2191−-- At the moment this is just LSPTest.openDoc' but it may change in the future
2192−-- when/if lsp-test implements WatchedFiles
2193−openDoc'' = LSPTest.openDoc'
2194−
21952320positionMappingTests :: TestTree
21962321positionMappingTests =
21972322 testGroup "position mapping"