@@ -507,32 +507,73 @@ runSessionWithServerInTmpDir config plugin tree act =
507507 {testLspConfig= config, testPluginDescriptor = plugin, testDirLocation= Right tree}
508508 (const act)
509509
510- runWithLockInTempDir :: VirtualFileTree -> (FileSystem -> IO a ) -> IO a
511- runWithLockInTempDir tree act = withLock lockForTempDirs $ do
510+ -- | Same as 'withTemporaryDataAndCacheDirectory', but materialises the given
511+ -- 'VirtualFileTree' in the temporary directory.
512+ withVfsTestDataDirectory :: VirtualFileTree -> (FileSystem -> IO a ) -> IO a
513+ withVfsTestDataDirectory tree act = do
514+ withTemporaryDataAndCacheDirectory $ \ tmpRoot -> do
515+ fs <- FS. materialiseVFT tmpRoot tree
516+ act fs
517+
518+ -- | Run an action in a temporary directory.
519+ -- Sets the 'XDG_CACHE_HOME' environment variable to a temporary directory as well.
520+ --
521+ -- This sets up a temporary directory for HLS tests to run.
522+ -- Typically, HLS tests copy their test data into the directory and then launch
523+ -- the HLS session in that directory.
524+ -- This makes sure that the tests are run in isolation, which is good for correctness
525+ -- but also important to have fast tests.
526+ --
527+ -- For improved isolation, we also make sure the 'XDG_CACHE_HOME' environment
528+ -- variable points to a temporary directory. So, we never share interface files
529+ -- or the 'hiedb' across tests.
530+ withTemporaryDataAndCacheDirectory :: (FilePath -> IO a ) -> IO a
531+ withTemporaryDataAndCacheDirectory act = withLock lockForTempDirs $ do
512532 testRoot <- setupTestEnvironment
513533 helperRecorder <- hlsHelperTestRecorder
514534 -- Do not clean up the temporary directory if this variable is set to anything but '0'.
515535 -- Aids debugging.
516536 cleanupTempDir <- lookupEnv " HLS_TEST_HARNESS_NO_TESTDIR_CLEANUP"
517537 let runTestInDir action = case cleanupTempDir of
518538 Just val | val /= " 0" -> do
519- (tempDir, _) <- newTempDirWithin testRoot
520- a <- action tempDir
539+ (tempDir, cacheHome, _) <- setupTemporaryTestDirectories testRoot
540+ a <- withTempCacheHome cacheHome ( action tempDir)
521541 logWith helperRecorder Debug LogNoCleanup
522542 pure a
523543
524544 _ -> do
525- (tempDir, cleanup) <- newTempDirWithin testRoot
526- a <- action tempDir `finally` cleanup
545+ (tempDir, cacheHome, cleanup) <- setupTemporaryTestDirectories testRoot
546+ a <- withTempCacheHome cacheHome ( action tempDir) `finally` cleanup
527547 logWith helperRecorder Debug LogCleanup
528548 pure a
529549 runTestInDir $ \ tmpDir' -> do
530550 -- we canonicalize the path, so that we do not need to do
531- -- cannibalization during the test when we compare two paths
551+ -- canonicalization during the test when we compare two paths
532552 tmpDir <- canonicalizePath tmpDir'
533553 logWith helperRecorder Info $ LogTestDir tmpDir
534- fs <- FS. materialiseVFT tmpDir tree
535- act fs
554+ act tmpDir
555+ where
556+ cache_home_var = " XDG_CACHE_HOME"
557+ -- Set the dir for "XDG_CACHE_HOME".
558+ -- When the operation finished, make sure the old value is restored.
559+ withTempCacheHome tempCacheHomeDir act =
560+ bracket
561+ (do
562+ old_cache_home <- lookupEnv cache_home_var
563+ setEnv cache_home_var tempCacheHomeDir
564+ pure old_cache_home)
565+ (\ old_cache_home ->
566+ maybe (pure () ) (setEnv cache_home_var) old_cache_home
567+ )
568+ (\ _ -> act)
569+
570+ -- Set up a temporary directory for the test files and one for the 'XDG_CACHE_HOME'.
571+ -- The 'XDG_CACHE_HOME' is important for independent test runs, i.e. completely empty
572+ -- caches.
573+ setupTemporaryTestDirectories testRoot = do
574+ (tempTestCaseDir, cleanup1) <- newTempDirWithin testRoot
575+ (tempCacheHomeDir, cleanup2) <- newTempDirWithin testRoot
576+ pure (tempTestCaseDir, tempCacheHomeDir, cleanup1 >> cleanup2)
536577
537578runSessionWithServer :: Pretty b => Config -> PluginTestDescriptor b -> FilePath -> Session a -> IO a
538579runSessionWithServer config plugin fp act =
@@ -565,18 +606,18 @@ instance Default (TestConfig b) where
565606-- It returns the root to the testing directory that tests should use.
566607-- This directory is not fully cleaned between reruns.
567608-- However, it is totally safe to delete the directory between runs.
568- --
569- -- Additionally, this overwrites the 'XDG_CACHE_HOME' variable to isolate
570- -- the tests from existing caches. 'hie-bios' and 'ghcide' honour the
571- -- 'XDG_CACHE_HOME' environment variable and generate their caches there.
572609setupTestEnvironment :: IO FilePath
573610setupTestEnvironment = do
574- tmpDirRoot <- getTemporaryDirectory
575- let testRoot = tmpDirRoot </> " hls-test-root"
576- testCacheDir = testRoot </> " .cache"
577- createDirectoryIfMissing True testCacheDir
578- setEnv " XDG_CACHE_HOME" testCacheDir
579- pure testRoot
611+ mRootDir <- lookupEnv " HLS_TEST_ROOTDIR"
612+ case mRootDir of
613+ Nothing -> do
614+ tmpDirRoot <- getTemporaryDirectory
615+ let testRoot = tmpDirRoot </> " hls-test-root"
616+ createDirectoryIfMissing True testRoot
617+ pure testRoot
618+ Just rootDir -> do
619+ createDirectoryIfMissing True rootDir
620+ pure rootDir
580621
581622goldenWithHaskellDocFormatter
582623 :: Pretty b
@@ -692,7 +733,6 @@ lockForTempDirs = unsafePerformIO newLock
692733data TestConfig b = TestConfig
693734 {
694735 testDirLocation :: Either FilePath VirtualFileTree
695- -- ^ Client capabilities
696736 -- ^ The file tree to use for the test, either a directory or a virtual file tree
697737 -- if using a virtual file tree,
698738 -- Creates a temporary directory, and materializes the VirtualFileTree
@@ -747,8 +787,20 @@ wrapClientLogger logger = do
747787 return (lspLogRecorder <> logger, cb1)
748788
749789-- | Host a server, and run a test session on it.
750- -- For setting custom timeout, set the environment variable 'LSP_TIMEOUT'
751- -- * LSP_TIMEOUT=10 cabal test
790+ --
791+ -- Environment variables are used to influence logging verbosity, test cleanup and test execution:
792+ --
793+ -- * @LSP_TIMEOUT@: Set a specific test timeout in seconds.
794+ -- * @LSP_TEST_LOG_MESSAGES@: Log the LSP messages between the client and server.
795+ -- * @LSP_TEST_LOG_STDERR@: Log the stderr of the server to the stderr of this process.
796+ -- * @HLS_TEST_HARNESS_STDERR@: Log test setup messages.
797+ --
798+ -- Test specific environment variables:
799+ --
800+ -- * @HLS_TEST_PLUGIN_LOG_STDERR@: Log all messages of the hls plugin under test to stderr.
801+ -- * @HLS_TEST_LOG_STDERR@: Log all HLS messages to stderr.
802+ -- * @HLS_TEST_HARNESS_NO_TESTDIR_CLEANUP@: Don't remove the test directories after test execution.
803+ --
752804-- For more detail of the test configuration, see 'TestConfig'
753805runSessionWithTestConfig :: Pretty b => TestConfig b -> (FilePath -> Session a ) -> IO a
754806runSessionWithTestConfig TestConfig {.. } session =
@@ -792,8 +844,10 @@ runSessionWithTestConfig TestConfig{..} session =
792844 else f
793845 runSessionInVFS (Left testConfigRoot) act = do
794846 root <- makeAbsolute testConfigRoot
795- act root
796- runSessionInVFS (Right vfs) act = runWithLockInTempDir vfs $ \ fs -> act (fsRoot fs)
847+ withTemporaryDataAndCacheDirectory (const $ act root)
848+ runSessionInVFS (Right vfs) act =
849+ withVfsTestDataDirectory vfs $ \ fs -> do
850+ act (fsRoot fs)
797851 testingArgs prjRoot recorderIde plugins =
798852 let
799853 arguments@ Arguments { argsHlsPlugins, argsIdeOptions, argsLspOptions } = defaultArguments (cmapWithPrio LogIDEMain recorderIde) prjRoot plugins
0 commit comments