Skip to content

Commit 407511e

Browse files
committed
Add test utilities
* expectNoMoreDiagnostics, adapted from ghcide * add knownBroken and ignore by ghc version
1 parent 6a692de commit 407511e

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

haskell-language-server.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ common hls-test-utils
200200
, lens
201201
, lsp-test >=0.11.0.6
202202
, stm
203+
, tasty-expected-failure
203204
, tasty-hunit
204205
, temporary
205206
, transformers
@@ -227,7 +228,6 @@ test-suite func-test
227228
, lens
228229
, tasty
229230
, tasty-ant-xml >=1.1.6
230-
, tasty-expected-failure
231231
, tasty-golden
232232
, tasty-rerun
233233

test/utils/Test/Hls/Util.hs

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Test.Hls.Util
55
, dummyLspFuncs
66
, expectCodeAction
77
, expectDiagnostic
8+
, expectNoMoreDiagnostics
89
, flushStackEnvironment
910
, fromAction
1011
, fromCommand
@@ -13,50 +14,59 @@ module Test.Hls.Util
1314
, hlsCommand
1415
, hlsCommandExamplePlugin
1516
, hlsCommandVomit
17+
, ignoreForGhcVersions
1618
, inspectCodeAction
1719
, inspectCommand
1820
, inspectDiagnostic
21+
, knownBrokenForGhcVersions
1922
, logConfig
2023
, logFilePath
2124
, noLogConfig
2225
, setupBuildToolFiles
2326
, waitForDiagnosticsFrom
2427
, waitForDiagnosticsFromSource
28+
, waitForDiagnosticsFromSourceWithTimeout
2529
, withFileLogging
2630
, withCurrentDirectoryInTmp
2731
)
2832
where
2933

3034
import Control.Monad
31-
import Control.Applicative.Combinators (skipManyTill)
35+
import Control.Monad.IO.Class
36+
import Control.Applicative.Combinators (skipManyTill, (<|>))
3237
import Control.Lens ((^.))
3338
import Data.Default
3439
import Data.List (intercalate)
3540
import Data.List.Extra (find)
3641
import Data.Maybe
3742
import qualified Data.Text as T
3843
import Language.Haskell.LSP.Core
44+
import Language.Haskell.LSP.Messages (FromServerMessage(NotLogMessage))
3945
import Language.Haskell.LSP.Types
40-
import qualified Language.Haskell.LSP.Test as T
46+
import qualified Language.Haskell.LSP.Test as Test
4147
import qualified Language.Haskell.LSP.Types.Lens as L
4248
import qualified Language.Haskell.LSP.Types.Capabilities as C
4349
import System.Directory
4450
import System.Environment
51+
import System.Time.Extra (Seconds, sleep)
4552
import System.FilePath
4653
import qualified System.Log.Logger as L
4754
import System.IO.Temp
4855
import System.IO.Unsafe
4956
import Test.Hspec.Runner
50-
import Test.Hspec.Core.Formatters
57+
import Test.Hspec.Core.Formatters hiding (Seconds)
58+
import Test.Tasty (TestTree)
59+
import Test.Tasty.ExpectedFailure (ignoreTestBecause, expectFailBecause)
60+
import Test.Tasty.HUnit (assertFailure)
5161
import Text.Blaze.Renderer.String (renderMarkup)
5262
import Text.Blaze.Internal hiding (null)
5363

5464

55-
noLogConfig :: T.SessionConfig
56-
noLogConfig = T.defaultConfig { T.logMessages = False }
65+
noLogConfig :: Test.SessionConfig
66+
noLogConfig = Test.defaultConfig { Test.logMessages = False }
5767

58-
logConfig :: T.SessionConfig
59-
logConfig = T.defaultConfig { T.logMessages = True }
68+
logConfig :: Test.SessionConfig
69+
logConfig = Test.defaultConfig { Test.logMessages = True }
6070

6171
codeActionSupportCaps :: C.ClientCapabilities
6272
codeActionSupportCaps = def { C._textDocument = Just textDocumentCaps }
@@ -127,6 +137,16 @@ ghcVersion = GHC86
127137
ghcVersion = GHC84
128138
#endif
129139

140+
knownBrokenForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree
141+
knownBrokenForGhcVersions vers reason
142+
| ghcVersion `elem` vers = expectFailBecause reason
143+
| otherwise = id
144+
145+
ignoreForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree
146+
ignoreForGhcVersions vers reason
147+
| ghcVersion `elem` vers = ignoreTestBecause reason
148+
| otherwise = id
149+
130150
logFilePath :: String
131151
logFilePath = "hls-" ++ show ghcVersion ++ ".log"
132152

@@ -330,17 +350,17 @@ inspectCommand cars s = fromCommand <$> onMatch cars predicate err
330350
predicate _ = False
331351
err = "expected code action matching '" ++ show s ++ "' but did not find one"
332352

333-
waitForDiagnosticsFrom :: TextDocumentIdentifier -> T.Session [Diagnostic]
353+
waitForDiagnosticsFrom :: TextDocumentIdentifier -> Test.Session [Diagnostic]
334354
waitForDiagnosticsFrom doc = do
335-
diagsNot <- skipManyTill T.anyMessage T.message :: T.Session PublishDiagnosticsNotification
355+
diagsNot <- skipManyTill Test.anyMessage Test.message :: Test.Session PublishDiagnosticsNotification
336356
let (List diags) = diagsNot ^. L.params . L.diagnostics
337357
if doc ^. L.uri /= diagsNot ^. L.params . L.uri
338358
then waitForDiagnosticsFrom doc
339359
else return diags
340360

341-
waitForDiagnosticsFromSource :: TextDocumentIdentifier -> String -> T.Session [Diagnostic]
361+
waitForDiagnosticsFromSource :: TextDocumentIdentifier -> String -> Test.Session [Diagnostic]
342362
waitForDiagnosticsFromSource doc src = do
343-
diagsNot <- skipManyTill T.anyMessage T.message :: T.Session PublishDiagnosticsNotification
363+
diagsNot <- skipManyTill Test.anyMessage Test.message :: Test.Session PublishDiagnosticsNotification
344364
let (List diags) = diagsNot ^. L.params . L.diagnostics
345365
let res = filter matches diags
346366
if doc ^. L.uri /= diagsNot ^. L.params . L.uri || null res
@@ -349,3 +369,49 @@ waitForDiagnosticsFromSource doc src = do
349369
where
350370
matches :: Diagnostic -> Bool
351371
matches d = d ^. L.source == Just (T.pack src)
372+
373+
-- | wait for @timeout@ seconds and report an assertion failure
374+
-- if any diagnostic messages arrive in that period
375+
expectNoMoreDiagnostics :: Seconds -> TextDocumentIdentifier -> String -> Test.Session ()
376+
expectNoMoreDiagnostics timeout doc src = do
377+
diags <- waitForDiagnosticsFromSourceWithTimeout timeout doc src
378+
unless (null diags) $
379+
liftIO $ assertFailure $
380+
"Got unexpected diagnostics for " <> show (doc ^. L.uri) <>
381+
" got " <> show diags
382+
383+
-- | wait for @timeout@ seconds and return diagnostics for the given @document and @source.
384+
-- If timeout is 0 it will wait until the session timeout
385+
waitForDiagnosticsFromSourceWithTimeout :: Seconds -> TextDocumentIdentifier -> String -> Test.Session [Diagnostic]
386+
waitForDiagnosticsFromSourceWithTimeout timeout document source = do
387+
when (timeout > 0) $ do
388+
-- Give any further diagnostic messages time to arrive.
389+
liftIO $ sleep timeout
390+
-- Send a dummy message to provoke a response from the server.
391+
-- This guarantees that we have at least one message to
392+
-- process, so message won't block or timeout.
393+
void $ Test.sendRequest (CustomClientMethod "non-existent-method") ()
394+
handleMessages
395+
where
396+
matches :: Diagnostic -> Bool
397+
matches d = d ^. L.source == Just (T.pack source)
398+
399+
handleMessages = handleDiagnostic <|> handleCustomMethodResponse <|> ignoreOthers
400+
handleDiagnostic = do
401+
diagsNot <- Test.message :: Test.Session PublishDiagnosticsNotification
402+
let fileUri = diagsNot ^. L.params . L.uri
403+
(List diags) = diagsNot ^. L.params . L.diagnostics
404+
res = filter matches diags
405+
if fileUri == document ^. L.uri && not (null res)
406+
then return diags else handleMessages
407+
handleCustomMethodResponse =
408+
-- the CustomClientMethod triggers a RspCustomServer
409+
-- handle that and then exit
410+
void (Test.satisfyMaybe responseForNonExistentMethod) >> return []
411+
412+
responseForNonExistentMethod notif
413+
| NotLogMessage logMsg <- notif,
414+
"non-existent-method" `T.isInfixOf` (logMsg ^. L.params . L.message) = Just notif
415+
| otherwise = Nothing
416+
417+
ignoreOthers = void Test.anyMessage >> handleMessages

0 commit comments

Comments
 (0)