@@ -50,7 +50,13 @@ import Development.IDE.Test (Cursor,
50
50
expectNoMoreDiagnostics ,
51
51
flushMessages ,
52
52
standardizeQuotes ,
53
- waitForAction )
53
+ waitForAction ,
54
+ garbageCollectDirtyKeys ,
55
+ getStoredKeys ,
56
+ waitForTypecheck ,
57
+ getFilesOfInterest ,
58
+ waitForBuildQueue ,
59
+ garbageCollectNotVisitedKeys )
54
60
import Development.IDE.Test.Runfiles
55
61
import qualified Development.IDE.Types.Diagnostics as Diagnostics
56
62
import Development.IDE.Types.Location
@@ -172,6 +178,7 @@ main = do
172
178
, clientSettingsTest
173
179
, codeActionHelperFunctionTests
174
180
, referenceTests
181
+ , garbageCollectionTests
175
182
]
176
183
177
184
initializeResponseTests :: TestTree
@@ -718,7 +725,7 @@ cancellationTemplate (edit, undoEdit) mbKey = testCase (maybe "-" fst mbKey) $ r
718
725
-- Now we edit the document and wait for the given key (if any)
719
726
changeDoc doc [edit]
720
727
whenJust mbKey $ \ (key, expectedResult) -> do
721
- Right WaitForIdeRuleResult {ideResultSuccess} <- waitForAction key doc
728
+ WaitForIdeRuleResult {ideResultSuccess} <- waitForAction key doc
722
729
liftIO $ ideResultSuccess @?= expectedResult
723
730
724
731
-- The 2nd edit cancels the active session and unbreaks the file
@@ -732,7 +739,7 @@ cancellationTemplate (edit, undoEdit) mbKey = testCase (maybe "-" fst mbKey) $ r
732
739
runTestNoKick s = withTempDir $ \ dir -> runInDir' dir " ." " ." [" --test-no-kick" ] s
733
740
734
741
typeCheck doc = do
735
- Right WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" doc
742
+ WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" doc
736
743
liftIO $ assertBool " The file should typecheck" ideResultSuccess
737
744
-- wait for the debouncer to publish diagnostics if the rule runs
738
745
liftIO $ sleep 0.2
@@ -5035,7 +5042,7 @@ retryFailedCradle = testSession' "retry failed" $ \dir -> do
5035
5042
liftIO $ writeFile hiePath hieContents
5036
5043
let aPath = dir </> " A.hs"
5037
5044
doc <- createDoc aPath " haskell" " main = return ()"
5038
- Right WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" doc
5045
+ WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" doc
5039
5046
liftIO $ " Test assumption failed: cradle should error out" `assertBool` not ideResultSuccess
5040
5047
5041
5048
-- Fix the cradle and typecheck again
@@ -5044,7 +5051,7 @@ retryFailedCradle = testSession' "retry failed" $ \dir -> do
5044
5051
sendNotification SWorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $
5045
5052
List [FileEvent (filePathToUri $ dir </> " hie.yaml" ) FcChanged ]
5046
5053
5047
- Right WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" doc
5054
+ WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" doc
5048
5055
liftIO $ " No joy after fixing the cradle" `assertBool` ideResultSuccess
5049
5056
5050
5057
@@ -5123,11 +5130,11 @@ simpleMultiTest = testCase "simple-multi-test" $ withLongTimeout $ runWithExtraF
5123
5130
bPath = dir </> " b/B.hs"
5124
5131
aSource <- liftIO $ readFileUtf8 aPath
5125
5132
adoc <- createDoc aPath " haskell" aSource
5126
- Right WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" adoc
5133
+ WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" adoc
5127
5134
liftIO $ assertBool " A should typecheck" ideResultSuccess
5128
5135
bSource <- liftIO $ readFileUtf8 bPath
5129
5136
bdoc <- createDoc bPath " haskell" bSource
5130
- Right WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" bdoc
5137
+ WaitForIdeRuleResult {.. } <- waitForAction " TypeCheck" bdoc
5131
5138
liftIO $ assertBool " B should typecheck" ideResultSuccess
5132
5139
locs <- getDefinitions bdoc (Position 2 7 )
5133
5140
let fooL = mkL (adoc ^. L. uri) 2 0 2 3
@@ -5837,6 +5844,86 @@ unitTests = do
5837
5844
, Progress. tests
5838
5845
]
5839
5846
5847
+ garbageCollectionTests :: TestTree
5848
+ garbageCollectionTests = testGroup " garbage collection"
5849
+ [ testGroup " dirty keys" (sharedGCtests garbageCollectDirtyKeys)
5850
+ , testGroup " unvisited keys" (sharedGCtests garbageCollectNotVisitedKeys)
5851
+ ]
5852
+ where
5853
+ sharedGCtests gc =
5854
+ [ testSession' " are collected" $ \ dir -> do
5855
+ liftIO $ writeFile (dir </> " hie.yaml" ) " cradle: {direct: {arguments: [A]}}"
5856
+ void $ generateGarbage " A" dir
5857
+ garbage <- gc 0
5858
+ liftIO $ assertBool " no garbage was found" $ not $ null garbage
5859
+
5860
+ , testSession' " are deleted from the state" $ \ dir -> do
5861
+ liftIO $ writeFile (dir </> " hie.yaml" ) " cradle: {direct: {arguments: [A]}}"
5862
+ void $ generateGarbage " A" dir
5863
+ keys0 <- getStoredKeys
5864
+ garbage <- gc 0
5865
+ liftIO $ assertBool " something is wrong with this test - no garbage found" $ not $ null garbage
5866
+ keys1 <- getStoredKeys
5867
+ liftIO $ assertBool " keys were not deleted from the state" (length keys1 < length keys0)
5868
+
5869
+ , testSession' " are not regenerated unless needed" $ \ dir -> do
5870
+ liftIO $ writeFile (dir </> " hie.yaml" ) " cradle: {direct: {arguments: [A.hs, B.hs]}}"
5871
+ void $ generateGarbage " A" dir
5872
+
5873
+ keysA <- getStoredKeys
5874
+
5875
+ reopenB <- generateGarbage " B" dir
5876
+ -- garbage collect A keys
5877
+ garbage <- gc 1
5878
+ liftIO $ assertBool " something is wrong with this test - no garbage found" $ not $ null garbage
5879
+ keysB <- getStoredKeys
5880
+ liftIO $ assertBool " something is wrong with this test - keys were not deleted from the state" (length keysB < length keysA)
5881
+ ff <- getFilesOfInterest
5882
+ liftIO $ assertBool (" something is wrong with this test - files of interest is " <> show ff) (null ff)
5883
+
5884
+ -- typecheck B again
5885
+ _ <- reopenB
5886
+
5887
+ -- review the keys in store now to validate that A keys have not been regenerated
5888
+ keysB' <- getStoredKeys
5889
+ let regeneratedKeys = Set. filter (not . isExpected) $
5890
+ Set. intersection (Set. fromList garbage) (Set. fromList keysB')
5891
+ liftIO $ regeneratedKeys @?= mempty
5892
+
5893
+ , testSession' " regenerate successfully" $ \ dir -> do
5894
+ liftIO $ writeFile (dir </> " hie.yaml" ) " cradle: {direct: {arguments: [A]}}"
5895
+ reopenA <- generateGarbage " A" dir
5896
+ garbage <- gc 0
5897
+ liftIO $ assertBool " no garbage was found" $ not $ null garbage
5898
+ let edit = T. unlines
5899
+ [ " module A where"
5900
+ , " a :: Bool"
5901
+ , " a = ()"
5902
+ ]
5903
+ doc <- reopenA
5904
+ changeDoc doc ([TextDocumentContentChangeEvent Nothing Nothing edit])
5905
+ builds <- waitForTypecheck doc
5906
+ liftIO $ assertBool " it still builds" builds
5907
+ expectCurrentDiagnostics doc ([(DsError , (2 ,4 ), " Couldn't match expected type" )])
5908
+ ]
5909
+
5910
+ isExpected k = any (`isPrefixOf` k) [" GhcSessionIO" ]
5911
+
5912
+ generateGarbage :: String -> FilePath -> Session (Session TextDocumentIdentifier )
5913
+ generateGarbage modName dir = do
5914
+ let fp = modName <> " .hs"
5915
+ body = printf " module %s where" modName
5916
+ doc <- createDoc fp " haskell" (T. pack body)
5917
+ liftIO $ writeFile (dir </> fp) body
5918
+ builds <- waitForTypecheck doc
5919
+ liftIO $ assertBool " something is wrong with this test" builds
5920
+ closeDoc doc
5921
+ waitForBuildQueue
5922
+ -- dirty the garbage
5923
+ sendNotification SWorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $
5924
+ List [FileEvent (filePathToUri $ dir </> modName <> " .hs" ) FcChanged ]
5925
+ return $ openDoc (modName <> " .hs" ) " haskell"
5926
+
5840
5927
findResolution_us :: Int -> IO Int
5841
5928
findResolution_us delay_us | delay_us >= 1000000 = error " Unable to compute timestamp resolution"
5842
5929
findResolution_us delay_us = withTempFile $ \ f -> withTempFile $ \ f' -> do
0 commit comments