Skip to content

Commit e100f81

Browse files
committed
Introduce declarative test project definition
Test data is currently often in some 'testdata' directory in 'test/'. It can easily get quite messy with many files. Especially since 'lsp-test' should load only exactly what is needed to speed up tests and avoid test flakiness. A subdirectory per test file is quite overkill and increases the required boilerplate to write tests. Thus, we introduce a declarative test project specification that runs lsp-test in a temporary directory. The first advantage is that we can load only exactly what we need, and create more accurate projects.
1 parent 3ffde0d commit e100f81

File tree

3 files changed

+410
-2
lines changed

3 files changed

+410
-2
lines changed

hls-test-utils/hls-test-utils.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ library
2828
exposed-modules:
2929
Test.Hls
3030
Test.Hls.Util
31+
Test.Hls.FileSystem
3132

3233
hs-source-dirs: src
3334
build-depends:

hls-test-utils/src/Test/Hls.hs

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,26 @@ module Test.Hls
2020
defaultTestRunner,
2121
goldenGitDiff,
2222
goldenWithHaskellDoc,
23+
goldenWithHaskellDocInTmpDir,
2324
goldenWithHaskellAndCaps,
25+
goldenWithHaskellAndCapsInTmpDir,
2426
goldenWithCabalDoc,
2527
goldenWithHaskellDocFormatter,
28+
goldenWithHaskellDocFormatterInTmpDir,
2629
goldenWithCabalDocFormatter,
30+
goldenWithCabalDocFormatterInTmpDir,
2731
def,
2832
-- * Running HLS for integration tests
2933
runSessionWithServer,
3034
runSessionWithServerAndCaps,
3135
runSessionWithServerFormatter,
3236
runSessionWithCabalServerFormatter,
37+
runSessionWithServerInTmpDir,
38+
runSessionWithServerAndCapsInTmpDir,
39+
runSessionWithServerFormatterInTmpDir,
40+
runSessionWithCabalServerFormatterInTmpDir,
3341
runSessionWithServer',
42+
runSessionWithServerInTmpDir',
3443
-- * Helpful re-exports
3544
PluginDescriptor,
3645
IdeState,
@@ -105,9 +114,12 @@ import System.Directory (getCurrentDirectory,
105114
setCurrentDirectory)
106115
import System.Environment (lookupEnv)
107116
import System.FilePath
117+
import System.IO.Extra (withTempDir)
108118
import System.IO.Unsafe (unsafePerformIO)
109119
import System.Process.Extra (createPipe)
110120
import System.Time.Extra
121+
import qualified Test.Hls.FileSystem as FS
122+
import Test.Hls.FileSystem
111123
import Test.Hls.Util
112124
import Test.Tasty hiding (Timeout)
113125
import Test.Tasty.ExpectedFailure
@@ -144,6 +156,18 @@ goldenWithHaskellDoc
144156
-> TestTree
145157
goldenWithHaskellDoc = goldenWithDoc "haskell"
146158

159+
goldenWithHaskellDocInTmpDir
160+
:: Pretty b
161+
=> PluginTestDescriptor b
162+
-> TestName
163+
-> VirtualFileTree
164+
-> FilePath
165+
-> FilePath
166+
-> FilePath
167+
-> (TextDocumentIdentifier -> Session ())
168+
-> TestTree
169+
goldenWithHaskellDocInTmpDir = goldenWithDocInTmpDir "haskell"
170+
147171
goldenWithHaskellAndCaps
148172
:: Pretty b
149173
=> ClientCapabilities
@@ -165,6 +189,27 @@ goldenWithHaskellAndCaps clientCaps plugin title testDataDir path desc ext act =
165189
act doc
166190
documentContents doc
167191

192+
goldenWithHaskellAndCapsInTmpDir
193+
:: Pretty b
194+
=> ClientCapabilities
195+
-> PluginTestDescriptor b
196+
-> TestName
197+
-> VirtualFileTree
198+
-> FilePath
199+
-> FilePath
200+
-> FilePath
201+
-> (TextDocumentIdentifier -> Session ())
202+
-> TestTree
203+
goldenWithHaskellAndCapsInTmpDir clientCaps plugin title tree path desc ext act =
204+
goldenGitDiff title (vftTestDataRoot tree </> path <.> desc <.> ext)
205+
$ runSessionWithServerAndCapsInTmpDir plugin clientCaps tree
206+
$ TL.encodeUtf8 . TL.fromStrict
207+
<$> do
208+
doc <- openDoc (path <.> ext) "haskell"
209+
void waitForBuildQueue
210+
act doc
211+
documentContents doc
212+
168213
goldenWithCabalDoc
169214
:: Pretty b
170215
=> PluginTestDescriptor b
@@ -198,6 +243,27 @@ goldenWithDoc fileType plugin title testDataDir path desc ext act =
198243
act doc
199244
documentContents doc
200245

246+
goldenWithDocInTmpDir
247+
:: Pretty b
248+
=> T.Text
249+
-> PluginTestDescriptor b
250+
-> TestName
251+
-> VirtualFileTree
252+
-> FilePath
253+
-> FilePath
254+
-> FilePath
255+
-> (TextDocumentIdentifier -> Session ())
256+
-> TestTree
257+
goldenWithDocInTmpDir fileType plugin title tree path desc ext act =
258+
goldenGitDiff title (vftTestDataRoot tree </> path <.> desc <.> ext)
259+
$ runSessionWithServerInTmpDir plugin tree
260+
$ TL.encodeUtf8 . TL.fromStrict
261+
<$> do
262+
doc <- openDoc (path <.> ext) fileType
263+
void waitForBuildQueue
264+
act doc
265+
documentContents doc
266+
201267
-- ------------------------------------------------------------
202268
-- Helper function for initialising plugins under test
203269
-- ------------------------------------------------------------
@@ -308,6 +374,51 @@ runSessionWithServerFormatter plugin formatter conf fp act = do
308374
fp
309375
act
310376

377+
runSessionWithServerInTmpDir :: Pretty b => PluginTestDescriptor b -> VirtualFileTree -> Session a -> IO a
378+
runSessionWithServerInTmpDir plugin tree act = do
379+
recorder <- pluginTestRecorder
380+
runSessionWithServerInTmpDir' (plugin recorder) def def fullCaps tree act
381+
382+
runSessionWithServerAndCapsInTmpDir :: Pretty b => PluginTestDescriptor b -> ClientCapabilities -> VirtualFileTree -> Session a -> IO a
383+
runSessionWithServerAndCapsInTmpDir plugin caps tree act = do
384+
recorder <- pluginTestRecorder
385+
runSessionWithServerInTmpDir' (plugin recorder) def def caps tree act
386+
387+
runSessionWithServerFormatterInTmpDir :: Pretty b => PluginTestDescriptor b -> String -> PluginConfig -> VirtualFileTree -> Session a -> IO a
388+
runSessionWithServerFormatterInTmpDir plugin formatter conf tree act = do
389+
recorder <- pluginTestRecorder
390+
runSessionWithServerInTmpDir'
391+
(plugin recorder)
392+
def
393+
{ formattingProvider = T.pack formatter
394+
, plugins = M.singleton (PluginId $ T.pack formatter) conf
395+
}
396+
def
397+
fullCaps
398+
tree
399+
act
400+
401+
-- | Host a server, and run a test session on it
402+
-- Note: cwd will be shifted into a temporary directory in @Session a@
403+
runSessionWithServerInTmpDir' ::
404+
-- | Plugins to load on the server.
405+
--
406+
-- For improved logging, make sure these plugins have been initalised with
407+
-- the recorder produced by @pluginTestRecorder@.
408+
IdePlugins IdeState ->
409+
-- | lsp config for the server
410+
Config ->
411+
-- | config for the test session
412+
SessionConfig ->
413+
ClientCapabilities ->
414+
VirtualFileTree ->
415+
Session a ->
416+
IO a
417+
runSessionWithServerInTmpDir' plugins conf sessConf caps tree act = withLock lock2 $ do
418+
withTempDir $ \tmpDir -> do
419+
_fs <- FS.materialiseVFT tmpDir tree
420+
runSessionWithServer' plugins conf sessConf caps tmpDir act
421+
311422
goldenWithHaskellDocFormatter
312423
:: Pretty b
313424
=> PluginTestDescriptor b -- ^ Formatter plugin to be used
@@ -352,6 +463,50 @@ goldenWithCabalDocFormatter plugin formatter conf title testDataDir path desc ex
352463
act doc
353464
documentContents doc
354465

466+
goldenWithHaskellDocFormatterInTmpDir
467+
:: Pretty b
468+
=> PluginTestDescriptor b -- ^ Formatter plugin to be used
469+
-> String -- ^ Name of the formatter to be used
470+
-> PluginConfig
471+
-> TestName -- ^ Title of the test
472+
-> VirtualFileTree -- ^ Virtual representation of the test project
473+
-> FilePath -- ^ Path to the testdata to be used within the directory
474+
-> FilePath -- ^ Additional suffix to be appended to the output file
475+
-> FilePath -- ^ Extension of the output file
476+
-> (TextDocumentIdentifier -> Session ())
477+
-> TestTree
478+
goldenWithHaskellDocFormatterInTmpDir plugin formatter conf title tree path desc ext act =
479+
goldenGitDiff title (vftTestDataRoot tree </> path <.> desc <.> ext)
480+
$ runSessionWithServerFormatterInTmpDir plugin formatter conf tree
481+
$ TL.encodeUtf8 . TL.fromStrict
482+
<$> do
483+
doc <- openDoc (path <.> ext) "haskell"
484+
void waitForBuildQueue
485+
act doc
486+
documentContents doc
487+
488+
goldenWithCabalDocFormatterInTmpDir
489+
:: Pretty b
490+
=> PluginTestDescriptor b -- ^ Formatter plugin to be used
491+
-> String -- ^ Name of the formatter to be used
492+
-> PluginConfig
493+
-> TestName -- ^ Title of the test
494+
-> VirtualFileTree -- ^ Virtual representation of the test project
495+
-> FilePath -- ^ Path to the testdata to be used within the directory
496+
-> FilePath -- ^ Additional suffix to be appended to the output file
497+
-> FilePath -- ^ Extension of the output file
498+
-> (TextDocumentIdentifier -> Session ())
499+
-> TestTree
500+
goldenWithCabalDocFormatterInTmpDir plugin formatter conf title tree path desc ext act =
501+
goldenGitDiff title (vftTestDataRoot tree </> path <.> desc <.> ext)
502+
$ runSessionWithCabalServerFormatterInTmpDir plugin formatter conf tree
503+
$ TL.encodeUtf8 . TL.fromStrict
504+
<$> do
505+
doc <- openDoc (path <.> ext) "cabal"
506+
void waitForBuildQueue
507+
act doc
508+
documentContents doc
509+
355510
runSessionWithCabalServerFormatter :: Pretty b => PluginTestDescriptor b -> String -> PluginConfig -> FilePath -> Session a -> IO a
356511
runSessionWithCabalServerFormatter plugin formatter conf fp act = do
357512
recorder <- pluginTestRecorder
@@ -363,7 +518,22 @@ runSessionWithCabalServerFormatter plugin formatter conf fp act = do
363518
}
364519
def
365520
fullCaps
366-
fp act
521+
fp
522+
act
523+
524+
runSessionWithCabalServerFormatterInTmpDir :: Pretty b => PluginTestDescriptor b -> String -> PluginConfig -> VirtualFileTree -> Session a -> IO a
525+
runSessionWithCabalServerFormatterInTmpDir plugin formatter conf tree act = do
526+
recorder <- pluginTestRecorder
527+
runSessionWithServerInTmpDir'
528+
(plugin recorder)
529+
def
530+
{ cabalFormattingProvider = T.pack formatter
531+
, plugins = M.singleton (PluginId $ T.pack formatter) conf
532+
}
533+
def
534+
fullCaps
535+
tree
536+
act
367537

368538
-- | Restore cwd after running an action
369539
keepCurrentDirectory :: IO a -> IO a
@@ -374,6 +544,12 @@ keepCurrentDirectory = bracket getCurrentDirectory setCurrentDirectory . const
374544
lock :: Lock
375545
lock = unsafePerformIO newLock
376546

547+
548+
{-# NOINLINE lock2 #-}
549+
-- | Never run in parallel
550+
lock2 :: Lock
551+
lock2 = unsafePerformIO newLock
552+
377553
-- | Host a server, and run a test session on it
378554
-- Note: cwd will be shifted into @root@ in @Session a@
379555
runSessionWithServer' ::
@@ -390,7 +566,7 @@ runSessionWithServer' ::
390566
FilePath ->
391567
Session a ->
392568
IO a
393-
runSessionWithServer' plugins conf sconf caps root s = withLock lock $ keepCurrentDirectory $ do
569+
runSessionWithServer' plugins conf sconf caps root s = withLock lock $ keepCurrentDirectory $ do
394570
(inR, inW) <- createPipe
395571
(outR, outW) <- createPipe
396572

0 commit comments

Comments
 (0)