Skip to content

Commit 334036f

Browse files
committed
Add more test-cases
1 parent 89a6b2b commit 334036f

File tree

7 files changed

+194
-31
lines changed

7 files changed

+194
-31
lines changed

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

+26-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Test.Hls
1616
defaultTestRunner,
1717
goldenGitDiff,
1818
goldenWithHaskellDoc,
19+
goldenWithCabalDoc,
1920
goldenWithHaskellDocFormatter,
2021
def,
2122
runSessionWithServer,
@@ -120,12 +121,35 @@ goldenWithHaskellDoc
120121
-> FilePath
121122
-> (TextDocumentIdentifier -> Session ())
122123
-> TestTree
123-
goldenWithHaskellDoc plugin title testDataDir path desc ext act =
124+
goldenWithHaskellDoc = goldenWithDoc "haskell"
125+
126+
goldenWithCabalDoc
127+
:: PluginDescriptor IdeState
128+
-> TestName
129+
-> FilePath
130+
-> FilePath
131+
-> FilePath
132+
-> FilePath
133+
-> (TextDocumentIdentifier -> Session ())
134+
-> TestTree
135+
goldenWithCabalDoc = goldenWithDoc "cabal"
136+
137+
goldenWithDoc
138+
:: T.Text
139+
-> PluginDescriptor IdeState
140+
-> TestName
141+
-> FilePath
142+
-> FilePath
143+
-> FilePath
144+
-> FilePath
145+
-> (TextDocumentIdentifier -> Session ())
146+
-> TestTree
147+
goldenWithDoc fileType plugin title testDataDir path desc ext act =
124148
goldenGitDiff title (testDataDir </> path <.> desc <.> ext)
125149
$ runSessionWithServer plugin testDataDir
126150
$ TL.encodeUtf8 . TL.fromStrict
127151
<$> do
128-
doc <- openDoc (path <.> ext) "haskell"
152+
doc <- openDoc (path <.> ext) fileType
129153
void waitForBuildQueue
130154
act doc
131155
documentContents doc

plugins/hls-cabal-plugin/hls-cabal-plugin.cabal

+2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ test-suite tests
6464
, filepath
6565
, hls-cabal-plugin
6666
, hls-test-utils ^>=1.3
67+
, lens
6768
, lsp
69+
, ghcide
6870
, lsp-types
6971
, tasty-hunit
7072
, text

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs

+29-15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ data Log
3838
= LogModificationTime NormalizedFilePath (Maybe FileVersion)
3939
| LogDiagnostics NormalizedFilePath [FileDiagnostic]
4040
| LogShake Shake.Log
41+
| LogDocOpened Uri
42+
| LogDocModified Uri
43+
| LogDocSaved Uri
44+
| LogDocClosed Uri
4145
deriving Show
4246

4347
instance Pretty Log where
@@ -47,6 +51,14 @@ instance Pretty Log where
4751
"Modified:" <+> pretty (fromNormalizedFilePath nfp) <+> pretty (show modTime)
4852
LogDiagnostics nfp diags ->
4953
"Diagnostics for " <+> pretty (fromNormalizedFilePath nfp) <> ":" <+> pretty (show diags)
54+
LogDocOpened uri ->
55+
"Opened text document:" <+> pretty (getUri uri)
56+
LogDocModified uri ->
57+
"Modified text document:" <+> pretty (getUri uri)
58+
LogDocSaved uri ->
59+
"Saved text document:" <+> pretty (getUri uri)
60+
LogDocClosed uri ->
61+
"Closed text document:" <+> pretty (getUri uri)
5062

5163
descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
5264
descriptor recorder plId = (defaultCabalPluginDescriptor plId)
@@ -56,39 +68,39 @@ descriptor recorder plId = (defaultCabalPluginDescriptor plId)
5668
[ mkPluginNotificationHandler LSP.STextDocumentDidOpen $
5769
\ide vfs _ (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> liftIO $ do
5870
whenUriFile _uri $ \file -> do
59-
logDebug (ideLogger ide) $ "Opened text document: " <> getUri _uri
71+
log' Debug $ LogDocOpened _uri
6072
join $ atomically $ Shake.recordDirtyKeys (shakeExtras ide) GetModificationTime [file]
6173
restartShakeSession (shakeExtras ide) (VFSModified vfs) (fromNormalizedFilePath file ++ " (opened)") []
6274
join $ Shake.shakeEnqueue (shakeExtras ide) $ Shake.mkDelayedAction "cabal parse modified" Info $ void $ use ParseCabal file
6375

6476
, mkPluginNotificationHandler LSP.STextDocumentDidChange $
6577
\ide vfs _ (DidChangeTextDocumentParams VersionedTextDocumentIdentifier{_uri} _) -> liftIO $ do
6678
whenUriFile _uri $ \file -> do
67-
logDebug (ideLogger ide) $ "Modified text document: " <> getUri _uri
68-
logDebug (ideLogger ide) $ "VFS State: " <> T.pack (show vfs)
79+
log' Debug $ LogDocModified _uri
6980
join $ atomically $ Shake.recordDirtyKeys (shakeExtras ide) GetModificationTime [file]
7081
restartShakeSession (shakeExtras ide) (VFSModified vfs) (fromNormalizedFilePath file ++ " (modified)") []
7182
join $ Shake.shakeEnqueue (shakeExtras ide) $ Shake.mkDelayedAction "cabal parse modified" Info $ void $ use ParseCabal file
7283

7384
, mkPluginNotificationHandler LSP.STextDocumentDidSave $
7485
\ide vfs _ (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> liftIO $ do
7586
whenUriFile _uri $ \file -> do
76-
logDebug (ideLogger ide) $ "Saved text document: " <> getUri _uri
87+
log' Debug $ LogDocSaved _uri
7788
join $ atomically $ Shake.recordDirtyKeys (shakeExtras ide) GetModificationTime [file]
7889
restartShakeSession (shakeExtras ide) (VFSModified vfs) (fromNormalizedFilePath file ++ " (saved)") []
7990
join $ Shake.shakeEnqueue (shakeExtras ide) $ Shake.mkDelayedAction "cabal parse modified" Info $ void $ use ParseCabal file
8091

8192
, mkPluginNotificationHandler LSP.STextDocumentDidClose $
8293
\ide vfs _ (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> liftIO $ do
8394
whenUriFile _uri $ \file -> do
84-
let msg = "Closed text document: " <> getUri _uri
85-
logDebug (ideLogger ide) msg
95+
log' Debug $ LogDocClosed _uri
8696
join $ atomically $ Shake.recordDirtyKeys (shakeExtras ide) GetModificationTime [file]
8797
restartShakeSession (shakeExtras ide) (VFSModified vfs) (fromNormalizedFilePath file ++ " (closed)") []
8898
join $ Shake.shakeEnqueue (shakeExtras ide) $ Shake.mkDelayedAction "cabal parse modified" Info $ void $ use ParseCabal file
8999
]
90100
}
91101
where
102+
log' = logWith recorder
103+
92104
whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
93105
whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath'
94106

@@ -114,15 +126,17 @@ cabalRules recorder = do
114126
Nothing -> do
115127
liftIO $ BS.readFile $ fromNormalizedFilePath file
116128

117-
pm <- liftIO $ Parse.parseCabalFileContents contents
118-
let diagLst = case pm of
119-
(pWarnings, Left (_, pErrorNE)) ->
120-
let warningDiags = fmap (Diag.warningDiag file) pWarnings
121-
errorDiags = NE.toList $ NE.map (Diag.errorDiag file) pErrorNE
122-
in warningDiags <> errorDiags
123-
_ -> []
124-
log' Debug $ LogDiagnostics file diagLst
125-
return (diagLst, Just ())
129+
(pWarnings, pm) <- liftIO $ Parse.parseCabalFileContents contents
130+
let warningDiags = fmap (Diag.warningDiag file) pWarnings
131+
case pm of
132+
Left (_cabalVersion, pErrorNE) -> do
133+
let errorDiags = NE.toList $ NE.map (Diag.errorDiag file) pErrorNE
134+
allDiags = errorDiags <> warningDiags
135+
log' Debug $ LogDiagnostics file allDiags
136+
pure (allDiags, Nothing)
137+
Right _ -> do
138+
log' Debug $ LogDiagnostics file warningDiags
139+
pure (warningDiags, Just ())
126140
where
127141
log' = logWith recorder
128142

plugins/hls-cabal-plugin/test/Main.hs

+120-13
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,128 @@
11
{-# LANGUAGE OverloadedStrings #-}
2+
{-# OPTIONS_GHC -Wno-orphans #-}
3+
{-# LANGUAGE NamedFieldPuns #-}
4+
{-# LANGUAGE ViewPatterns #-}
25
module Main
36
( main
47
) where
58

6-
import qualified Ide.Plugin.Cabal.Parse as Lib
7-
import qualified Data.Text as T
8-
import qualified Language.LSP.Types.Lens as L
9+
import Control.Lens ((^.))
10+
import Data.Function
11+
import qualified Data.Text as Text
12+
import Development.IDE.Types.Logger
913
import Ide.Plugin.Cabal
14+
import qualified Ide.Plugin.Cabal.Parse as Lib
15+
import qualified Language.LSP.Types.Lens as J
1016
import System.FilePath
1117
import Test.Hls
12-
import Test.Hls.Util (onlyWorkForGhcVersions)
13-
import Test.Tasty.HUnit (assertFailure, testCase, (@?=))
18+
import qualified Data.Text.IO as Text
19+
20+
cabalPlugin :: Recorder (WithPriority Log) -> PluginDescriptor IdeState
21+
cabalPlugin recorder = descriptor recorder "cabal"
1422

1523
main :: IO ()
16-
main = defaultTestRunner tests
24+
main = do
25+
recorder <- initialiseRecorder True
26+
defaultTestRunner $
27+
testGroup "Cabal Plugin Tests"
28+
[ unitTests
29+
, pluginTests recorder
30+
]
31+
32+
initialiseRecorder :: Bool -> IO (Recorder (WithPriority Log))
33+
initialiseRecorder True = pure mempty
34+
initialiseRecorder False = do
35+
docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug
1736

18-
pragmasPlugin :: PluginDescriptor IdeState
19-
pragmasPlugin = descriptor mempty "cabal"
37+
let docWithFilteredPriorityRecorder =
38+
docWithPriorityRecorder
39+
& cfilter (\WithPriority{ priority } -> priority >= Debug)
40+
pure $ docWithFilteredPriorityRecorder
41+
& cmapWithPrio pretty
2042

21-
tests :: TestTree
22-
tests =
23-
testGroup "cabal"
43+
-- ------------------------------------------------------------------------
44+
-- Unit Tests
45+
-- ------------------------------------------------------------------------
46+
47+
unitTests :: TestTree
48+
unitTests =
49+
testGroup "Unit Tests"
2450
[ testCase "parsing works" $ do
2551
parseRes <- Lib.parseCabalFile "test/testdata/simple.cabal"
26-
goldenShowStr <- readFile "test/testdata/simple.cabal.golden.txt"
27-
show parseRes @?= goldenShowStr
52+
goldenShowStr <- Text.readFile "test/testdata/simple.cabal.golden.txt"
53+
Text.pack (show parseRes) @?= Text.strip goldenShowStr
2854
]
2955

56+
-- ------------------------------------------------------------------------
57+
-- Integration Tests
58+
-- ------------------------------------------------------------------------
59+
60+
pluginTests :: Recorder (WithPriority Log) -> TestTree
61+
pluginTests recorder = testGroup "Plugin Tests"
62+
[ testGroup "Diagnostics"
63+
[ runCabalTestCaseSession "Publishes Diagnostics on Error" recorder "" $ do
64+
doc <- openDoc "invalid.cabal" "cabal"
65+
diags <- waitForDiagnosticsFromSource doc "parsing"
66+
reduceDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"]
67+
liftIO $ do
68+
length diags @?= 1
69+
reduceDiag ^. J.range @?= Range (Position 3 24) (Position 4 0)
70+
reduceDiag ^. J.severity @?= Just DsError
71+
, runCabalTestCaseSession "Clears diagnostics" recorder "" $ do
72+
doc <- openDoc "invalid.cabal" "cabal"
73+
diags <- waitForDiagnosticsFrom doc
74+
reduceDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"]
75+
liftIO $ do
76+
length diags @?= 1
77+
reduceDiag ^. J.range @?= Range (Position 3 24) (Position 4 0)
78+
reduceDiag ^. J.severity @?= Just DsError
79+
_ <- applyEdit doc $ TextEdit (Range (Position 3 20) (Position 4 0)) "BSD-3-Clause\n"
80+
newDiags <- waitForDiagnosticsFrom doc
81+
liftIO $ newDiags @?= []
82+
]
83+
, testGroup "Code Actions"
84+
[ runCabalTestCaseSession "BSD-3" recorder "" $ do
85+
doc <- openDoc "licenseCodeAction.cabal" "cabal"
86+
diags <- waitForDiagnosticsFromSource doc "parsing"
87+
reduceDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"]
88+
liftIO $ do
89+
length diags @?= 1
90+
reduceDiag ^. J.range @?= Range (Position 3 24) (Position 4 0)
91+
reduceDiag ^. J.severity @?= Just DsError
92+
[InR codeAction] <- getCodeActions doc (Range (Position 3 24) (Position 4 0))
93+
executeCodeAction codeAction
94+
contents <- documentContents doc
95+
liftIO $ contents @?= Text.unlines
96+
[ "cabal-version: 3.0"
97+
, "name: licenseCodeAction"
98+
, "version: 0.1.0.0"
99+
, "license: BSD-3-Clause"
100+
, ""
101+
, "library"
102+
, " build-depends: base"
103+
, " default-language: Haskell2010"
104+
]
105+
]
106+
]
107+
108+
-- ------------------------------------------------------------------------
109+
-- Runner utils
110+
-- ------------------------------------------------------------------------
111+
112+
runCabalTestCaseSession :: TestName -> Recorder (WithPriority Log) -> FilePath -> Session () -> TestTree
113+
runCabalTestCaseSession title recorder subdir act = testCase title $ runCabalSession recorder subdir act
114+
115+
runCabalSession :: Recorder (WithPriority Log) -> FilePath -> Session a -> IO a
116+
runCabalSession recorder subdir =
117+
failIfSessionTimeout . runSessionWithServer (cabalPlugin recorder) (testDataDir </> subdir)
118+
119+
testDataDir :: FilePath
120+
testDataDir = "test" </> "testdata"
121+
122+
-- ------------------------------------------------------------------------
123+
-- Test utils for lib:Cabal
124+
-- ------------------------------------------------------------------------
125+
30126
-- Orphans
31127
instance Eq Lib.PWarning where
32128
Lib.PWarning pWarnType1 pos1 str1 == Lib.PWarning pWarnType2 pos2 str2 =
@@ -35,3 +131,14 @@ instance Eq Lib.PWarning where
35131
instance Eq Lib.PError where
36132
Lib.PError pos1 str1 == Lib.PError pos2 str2 =
37133
pos1 == pos2 && str1 == str2
134+
135+
136+
-- ------------------------------------------------------------------------
137+
-- Test utils
138+
-- ------------------------------------------------------------------------
139+
140+
pointRange :: Int -> Int -> Range
141+
pointRange
142+
(subtract 1 -> fromIntegral -> line)
143+
(subtract 1 -> fromIntegral -> col) =
144+
Range (Position line col) (Position line $ col + 1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
cabal-version: 3.0
2+
name: invalid
3+
version: 0.1.0.0
4+
license: BSD3
5+
6+
library
7+
build-depends: base
8+
default-language: Haskell2010
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
cabal-version: 3.0
2+
name: licenseCodeAction
3+
version: 0.1.0.0
4+
license: BSD3
5+
6+
library
7+
build-depends: base
8+
default-language: Haskell2010

0 commit comments

Comments
 (0)