Skip to content

Commit 22ec256

Browse files
committed
WIP on introducing tasty test suite
1 parent d1a232f commit 22ec256

File tree

6 files changed

+628
-11
lines changed

6 files changed

+628
-11
lines changed

haskell-language-server.cabal

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,84 @@ executable haskell-language-server-wrapper
173173
default-language: Haskell2010
174174

175175

176-
test-suite test
176+
test-suite hls-tests
177177
type: exitcode-stdio-1.0
178-
main-is: Spec.hs
178+
default-language: Haskell2010
179+
build-tool-depends:
180+
haskell-language-server:haskell-language-server,
181+
ghcide:ghcide-test-preprocessor
182+
build-depends:
183+
base >=4.7 && <5
184+
, haskell-language-server
185+
, aeson
186+
, base
187+
, bytestring
188+
, containers
189+
, directory
190+
, extra
191+
, filepath
192+
--------------------------------------------------------------
193+
-- The MIN_GHC_API_VERSION macro relies on MIN_VERSION pragmas
194+
-- which require depending on ghc. So the tests need to depend
195+
-- on ghc if they need to use MIN_GHC_API_VERSION. Maybe a
196+
-- better solution can be found, but this is a quick solution
197+
-- which works for now.
198+
, ghc
199+
--------------------------------------------------------------
200+
, ghcide
201+
, ghc-typelits-knownnat
202+
, haddock-library
203+
, haskell-lsp
204+
, haskell-lsp-types
205+
, hls-test-utils
206+
, lens
207+
, lsp-test >= 0.8
208+
, parser-combinators
209+
, QuickCheck
210+
, quickcheck-instances
211+
, rope-utf16-splay
212+
, tasty
213+
, tasty-expected-failure
214+
, tasty-hunit
215+
, tasty-quickcheck
216+
, text
179217
other-modules:
180218
Paths_haskell_language_server
181219
hs-source-dirs:
182-
test
220+
test/exe
183221
ghc-options:
184222
-Wall
185223
-Wredundant-constraints
186224
-Wno-name-shadowing
187225
-threaded -rtsopts -with-rtsopts=-N
188226
if flag(pedantic)
189227
ghc-options: -Werror
190-
build-depends:
191-
base >=4.7 && <5
192-
, haskell-language-server
193-
default-language: Haskell2010
228+
main-is: Main.hs
229+
-- other-modules:
230+
-- Development.IDE.Test
231+
-- Development.IDE.Test.Runfiles
232+
233+
library hls-test-utils
234+
hs-source-dirs: test/utils
235+
exposed-modules: TestUtils
236+
build-depends: base
237+
, haskell-language-server
238+
, haskell-lsp
239+
, hie-bios
240+
, aeson
241+
, blaze-markup
242+
, containers
243+
, data-default
244+
, directory
245+
, filepath
246+
, hslogger
247+
, hspec
248+
, hspec-core
249+
, stm
250+
, text
251+
, unordered-containers
252+
, yaml
253+
ghc-options: -Wall -Wredundant-constraints
254+
if flag(pedantic)
255+
ghc-options: -Werror
256+
default-language: Haskell2010

hie.yaml.cbl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ cradle:
99
cabal:
1010

1111
- path: "./test"
12-
component: "haskell-language-server:test"
12+
component: "haskell-language-server:hls-tests"
13+
14+
- path: "./test/utils/"
15+
component: "haskell-language-server:hls-test-utils"
1316

1417
- path: "./exe/Main.hs"
1518
component: "haskell-language-server:exe:haskell-language-server"

hie.yaml.stack

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
cradle:
1313
stack:
1414
- path: "./test"
15-
component: "haskell-language-server:test"
15+
component: "haskell-language-server:hls-tests"
1616

1717
- path: "./exe/Main.hs"
1818
component: "haskell-language-server:exe:haskell-language-server"

test/Spec.hs

Lines changed: 0 additions & 2 deletions
This file was deleted.

test/exe/Main.hs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
-- Copyright (c) 2019-2020 The DAML and HLS Authors. All rights reserved.
2+
-- SPDX-License-Identifier: Apache-2.0
3+
4+
{-# LANGUAGE DuplicateRecordFields #-}
5+
{-# LANGUAGE OverloadedStrings #-}
6+
{-# LANGUAGE PatternSynonyms #-}
7+
{-# LANGUAGE CPP #-}
8+
9+
module Main (main) where
10+
11+
import Control.Applicative.Combinators
12+
import Control.Exception (catch)
13+
import Control.Monad
14+
import Control.Monad.IO.Class (liftIO)
15+
import Data.Char (toLower)
16+
import Data.Foldable
17+
import Data.List
18+
import Data.Rope.UTF16 (Rope)
19+
import qualified Data.Rope.UTF16 as Rope
20+
import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent)
21+
import Development.IDE.GHC.Util
22+
import qualified Data.Text as T
23+
import Development.IDE.Spans.Common
24+
-- import Development.IDE.Test
25+
-- import Development.IDE.Test.Runfiles
26+
import Development.IDE.Types.Location
27+
import qualified Language.Haskell.LSP.Test as LSPTest
28+
import Language.Haskell.LSP.Test hiding (openDoc')
29+
import Language.Haskell.LSP.Types
30+
import Language.Haskell.LSP.Types.Capabilities
31+
import Language.Haskell.LSP.VFS (applyChange)
32+
import System.Environment.Blank (setEnv)
33+
import System.FilePath
34+
import System.IO.Extra
35+
import System.Directory
36+
import Test.QuickCheck
37+
import Test.QuickCheck.Instances ()
38+
import Test.Tasty
39+
import Test.Tasty.ExpectedFailure
40+
import Test.Tasty.HUnit
41+
import Test.Tasty.QuickCheck
42+
import Data.Maybe
43+
44+
import TestUtils
45+
46+
-- ---------------------------------------------------------------------
47+
48+
main :: IO ()
49+
main = defaultMain $ testGroup "HLS"
50+
[ testSession "open close" $ do
51+
doc <- openDoc' "Testing.hs" "haskell" ""
52+
void (message :: Session WorkDoneProgressCreateRequest)
53+
void (message :: Session WorkDoneProgressBeginNotification)
54+
closeDoc doc
55+
void (message :: Session WorkDoneProgressEndNotification)
56+
]
57+
58+
----------------------------------------------------------------------
59+
-- Utils
60+
61+
62+
testSession :: String -> Session () -> TestTree
63+
testSession name = testCase name . run
64+
65+
{-
66+
testSessionWait :: String -> Session () -> TestTree
67+
testSessionWait name = testSession name .
68+
-- Check that any diagnostics produced were already consumed by the test case.
69+
--
70+
-- If in future we add test cases where we don't care about checking the diagnostics,
71+
-- this could move elsewhere.
72+
--
73+
-- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear.
74+
( >> expectNoMoreDiagnostics 0.5)
75+
76+
pickActionWithTitle :: T.Text -> [CAResult] -> CodeAction
77+
pickActionWithTitle title actions = head
78+
[ action
79+
| CACodeAction action@CodeAction{ _title = actionTitle } <- actions
80+
, title == actionTitle ]
81+
-}
82+
83+
mkRange :: Int -> Int -> Int -> Int -> Range
84+
mkRange a b c d = Range (Position a b) (Position c d)
85+
86+
run :: Session a -> IO a
87+
run s = withTempDir $ \dir -> do
88+
let ghcideExe = hieCommand
89+
90+
-- Temporarily hack around https://github.com/mpickering/hie-bios/pull/56
91+
-- since the package import test creates "Data/List.hs", which otherwise has no physical home
92+
createDirectoryIfMissing True $ dir ++ "/Data"
93+
94+
let cmd = unwords [ghcideExe, "--lsp", "--cwd", dir]
95+
-- HIE calls getXgdDirectory which assumes that HOME is set.
96+
-- Only sets HOME if it wasn't already set.
97+
setEnv "HOME" "/homeless-shelter" False
98+
let lspTestCaps = fullCaps { _window = Just $ WindowClientCapabilities $ Just True }
99+
runSessionWithConfig conf cmd lspTestCaps dir s
100+
where
101+
conf = defaultConfig
102+
-- If you uncomment this you can see all logging
103+
-- which can be quite useful for debugging.
104+
-- { logStdErr = True, logColor = False }
105+
-- If you really want to, you can also see all messages
106+
-- { logMessages = True, logColor = False }
107+
108+
openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
109+
openTestDataDoc path = do
110+
source <- liftIO $ readFileUtf8 $ "test/data" </> path
111+
openDoc' path "haskell" source
112+
113+
findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
114+
findCodeActions doc range expectedTitles = do
115+
actions <- getCodeActions doc range
116+
let matches = sequence
117+
[ listToMaybe
118+
[ action
119+
| CACodeAction action@CodeAction { _title = actionTitle } <- actions
120+
, actionTitle == expectedTitle ]
121+
| expectedTitle <- expectedTitles]
122+
let msg = show
123+
[ actionTitle
124+
| CACodeAction CodeAction { _title = actionTitle } <- actions
125+
]
126+
++ "is not a superset of "
127+
++ show expectedTitles
128+
liftIO $ case matches of
129+
Nothing -> assertFailure msg
130+
Just _ -> pure ()
131+
return (fromJust matches)
132+
133+
findCodeAction :: TextDocumentIdentifier -> Range -> T.Text -> Session CodeAction
134+
findCodeAction doc range t = head <$> findCodeActions doc range [t]
135+
136+
-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
137+
openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
138+
openDoc' fp name contents = do
139+
res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
140+
sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
141+
return res

0 commit comments

Comments
 (0)