git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
[psc-ide] Restructure testing to avoid running the server (#2599)
220e6aeChristoph Hegemann
purescript.cabal+3−6
@@ -496,7 +496,6 @@ executable psc-ide-server
496 aeson >= 0.8 && < 1.0,
496 aeson >= 0.8 && < 1.0,
497 bytestring -any,
497 bytestring -any,
498 purescript -any,
498 purescript -any,
499− base-compat >=0.6.0,
500 directory -any,
499 directory -any,
501 filepath -any,
500 filepath -any,
502 monad-logger -any,
501 monad-logger -any,
@@ -506,8 +505,7 @@ executable psc-ide-server
506 protolude >= 0.1.6,
505 protolude >= 0.1.6,
507 stm -any,
506 stm -any,
508 text -any,
507 text -any,
509− transformers -any,
508+ transformers -any
510− transformers-compat -any
511 ghc-options: -Wall -O2 -threaded
509 ghc-options: -Wall -O2 -threaded
512 hs-source-dirs: psc-ide-server
510 hs-source-dirs: psc-ide-server
513
511
@@ -543,6 +541,7 @@ test-suite tests
543 hspec-discover -any,
541 hspec-discover -any,
544 HUnit -any,
542 HUnit -any,
545 lens -any,
543 lens -any,
544+ monad-logger -any,
546 mtl -any,
545 mtl -any,
547 optparse-applicative -any,
546 optparse-applicative -any,
548 parsec -any,
547 parsec -any,
@@ -567,14 +566,12 @@ test-suite tests
567 TestPsci
566 TestPsci
568 TestPscIde
567 TestPscIde
569 PscIdeSpec
568 PscIdeSpec
569+ Language.PureScript.Ide.Test
570 Language.PureScript.Ide.FilterSpec
570 Language.PureScript.Ide.FilterSpec
571 Language.PureScript.Ide.ImportsSpec
571 Language.PureScript.Ide.ImportsSpec
572− Language.PureScript.Ide.Imports.IntegrationSpec
573− Language.PureScript.Ide.Integration
574 Language.PureScript.Ide.MatcherSpec
572 Language.PureScript.Ide.MatcherSpec
575 Language.PureScript.Ide.RebuildSpec
573 Language.PureScript.Ide.RebuildSpec
576 Language.PureScript.Ide.ReexportsSpec
574 Language.PureScript.Ide.ReexportsSpec
577− Language.PureScript.Ide.SourceFile.IntegrationSpec
578 Language.PureScript.Ide.SourceFileSpec
575 Language.PureScript.Ide.SourceFileSpec
579 Language.PureScript.Ide.StateSpec
576 Language.PureScript.Ide.StateSpec
580 buildable: True
577 buildable: True
src/Language/PureScript/Ide.hs+34−10
@@ -47,9 +47,13 @@ handleCommand :: (Ide m, MonadLogger m, MonadError PscIdeError m) =>
47 Command -> m Success
47 Command -> m Success
48handleCommand c = case c of
48handleCommand c = case c of
49 Load [] ->
49 Load [] ->
50− findAvailableExterns >>= loadModules
50+ findAvailableExterns >>= loadModulesAsync
51 Load modules ->
51 Load modules ->
52− loadModules modules
52+ loadModulesAsync modules
53+ LoadSync [] ->
54+ findAvailableExterns >>= loadModulesSync
55+ LoadSync modules ->
56+ loadModulesSync modules
53 Type search filters currentModule ->
57 Type search filters currentModule ->
54 findType search filters currentModule
58 findType search filters currentModule
55 Complete filters matcher currentModule ->
59 Complete filters matcher currentModule ->
@@ -78,7 +82,9 @@ handleCommand c = case c of
78 Left question ->
82 Left question ->
79 pure (CompletionResult (map (completionFromMatch . map withEmptyAnn) question))
83 pure (CompletionResult (map (completionFromMatch . map withEmptyAnn) question))
80 Rebuild file ->
84 Rebuild file ->
81− rebuildFile file
85+ rebuildFileAsync file
86+ RebuildSync file ->
87+ rebuildFileSync file
82 Cwd ->
88 Cwd ->
83 TextResult . toS <$> liftIO getCurrentDirectory
89 TextResult . toS <$> liftIO getCurrentDirectory
84 Reset ->
90 Reset ->
@@ -162,6 +168,31 @@ findAllSourceFiles = do
162-- server state. Then proceeds to parse all the specified sourcefiles and
168-- server state. Then proceeds to parse all the specified sourcefiles and
163-- inserts their ASTs into the state. Finally kicks off an async worker, which
169-- inserts their ASTs into the state. Finally kicks off an async worker, which
164-- populates Stage 2 and 3 of the state.
170-- populates Stage 2 and 3 of the state.
171+loadModulesAsync
172+ :: (Ide m, MonadError PscIdeError m, MonadLogger m)
173+ => [P.ModuleName]
174+ -> m Success
175+loadModulesAsync moduleNames = do
176+ tr <- loadModules moduleNames
177+
178+ -- Finally we kick off the worker with @async@ and return the number of
179+ -- successfully parsed modules.
180+ env <- ask
181+ let ll = confLogLevel (ideConfiguration env)
182+ -- populateStage2 and 3 return Unit for now, so it's fine to discard this
183+ -- result. We might want to block on this in a benchmarking situation.
184+ _ <- liftIO (async (runLogger ll (runReaderT (populateStage2 *> populateStage3) env)))
185+ pure tr
186+
187+loadModulesSync
188+ :: (Ide m, MonadError PscIdeError m, MonadLogger m)
189+ => [P.ModuleName]
190+ -> m Success
191+loadModulesSync moduleNames = do
192+ tr <- loadModules moduleNames
193+ populateStage2 *> populateStage3
194+ pure tr
195+
165loadModules
196loadModules
166 :: (Ide m, MonadError PscIdeError m, MonadLogger m)
197 :: (Ide m, MonadError PscIdeError m, MonadLogger m)
167 => [P.ModuleName]
198 => [P.ModuleName]
@@ -182,12 +213,5 @@ loadModules moduleNames = do
182 $(logWarn) ("Failed to parse: " <> show failures)
213 $(logWarn) ("Failed to parse: " <> show failures)
183 traverse_ insertModule allModules
214 traverse_ insertModule allModules
184
215
185− -- Finally we kick off the worker with @async@ and return the number of
186− -- successfully parsed modules.
187− env <- ask
188− let ll = confLogLevel (ideConfiguration env)
189− -- populateStage2 and 3 return Unit for now, so it's fine to discard this
190− -- result. We might want to block on this in a benchmarking situation.
191− _ <- liftIO (async (runLogger ll (runReaderT (populateStage2 *> populateStage3) env)))
192 pure (TextResult ("Loaded " <> show (length efiles) <> " modules and "
216 pure (TextResult ("Loaded " <> show (length efiles) <> " modules and "
193 <> show (length allModules) <> " source files."))
217 <> show (length allModules) <> " source files."))
src/Language/PureScript/Ide/Command.hs+4
@@ -25,6 +25,7 @@ import Language.PureScript.Ide.Types
25
25
26data Command
26data Command
27 = Load [P.ModuleName]
27 = Load [P.ModuleName]
28+ | LoadSync [P.ModuleName] -- used in tests
28 | Type
29 | Type
29 { typeSearch :: Text
30 { typeSearch :: Text
30 , typeFilters :: [Filter]
31 , typeFilters :: [Filter]
@@ -54,6 +55,7 @@ data Command
54 | Import FilePath (Maybe FilePath) [Filter] ImportCommand
55 | Import FilePath (Maybe FilePath) [Filter] ImportCommand
55 | List { listType :: ListType }
56 | List { listType :: ListType }
56 | Rebuild FilePath -- ^ Rebuild the specified file using the loaded externs
57 | Rebuild FilePath -- ^ Rebuild the specified file using the loaded externs
58+ | RebuildSync FilePath -- ^ Rebuild the specified file using the loaded externs
57 | Cwd
59 | Cwd
58 | Reset
60 | Reset
59 | Quit
61 | Quit
@@ -61,6 +63,7 @@ data Command
61commandName :: Command -> Text
63commandName :: Command -> Text
62commandName c = case c of
64commandName c = case c of
63 Load{} -> "Load"
65 Load{} -> "Load"
66+ LoadSync{} -> "LoadSync"
64 Type{} -> "Type"
67 Type{} -> "Type"
65 Complete{} -> "Complete"
68 Complete{} -> "Complete"
66 Pursuit{} -> "Pursuit"
69 Pursuit{} -> "Pursuit"
@@ -69,6 +72,7 @@ commandName c = case c of
69 Import{} -> "Import"
72 Import{} -> "Import"
70 List{} -> "List"
73 List{} -> "List"
71 Rebuild{} -> "Rebuild"
74 Rebuild{} -> "Rebuild"
75+ RebuildSync{} -> "RebuildSync"
72 Cwd{} -> "Cwd"
76 Cwd{} -> "Cwd"
73 Reset{} -> "Reset"
77 Reset{} -> "Reset"
74 Quit{} -> "Quit"
78 Quit{} -> "Quit"
src/Language/PureScript/Ide/Error.hs+1
@@ -29,6 +29,7 @@ data PscIdeError
29 | ModuleFileNotFound ModuleIdent
29 | ModuleFileNotFound ModuleIdent
30 | ParseError P.ParseError Text
30 | ParseError P.ParseError Text
31 | RebuildError [JSONError]
31 | RebuildError [JSONError]
32+ deriving (Show, Eq)
32
33
33instance ToJSON PscIdeError where
34instance ToJSON PscIdeError where
34 toJSON (RebuildError errs) = object
35 toJSON (RebuildError errs) = object
src/Language/PureScript/Ide/Externs.hs+1−1
@@ -90,7 +90,7 @@ convertDecl :: P.ExternsDeclaration -> Maybe IdeDeclaration
90convertDecl P.EDType{..} = Just $ IdeDeclType $
90convertDecl P.EDType{..} = Just $ IdeDeclType $
91 IdeType edTypeName edTypeKind
91 IdeType edTypeName edTypeKind
92convertDecl P.EDTypeSynonym{..} = Just $ IdeDeclTypeSynonym
92convertDecl P.EDTypeSynonym{..} = Just $ IdeDeclTypeSynonym
93− (IdeSynonym edTypeSynonymName edTypeSynonymType)
93+ (IdeTypeSynonym edTypeSynonymName edTypeSynonymType)
94convertDecl P.EDDataConstructor{..} = Just $ IdeDeclDataConstructor $
94convertDecl P.EDDataConstructor{..} = Just $ IdeDeclDataConstructor $
95 IdeDataConstructor edDataCtorName edDataCtorTypeCtor edDataCtorType
95 IdeDataConstructor edDataCtorName edDataCtorTypeCtor edDataCtorType
96convertDecl P.EDValue{..} = Just $ IdeDeclValue $
96convertDecl P.EDValue{..} = Just $ IdeDeclValue $
src/Language/PureScript/Ide/Rebuild.hs+31−5
@@ -2,7 +2,9 @@
2{-# LANGUAGE TemplateHaskell #-}
2{-# LANGUAGE TemplateHaskell #-}
3
3
4module Language.PureScript.Ide.Rebuild
4module Language.PureScript.Ide.Rebuild
5+ ( rebuildFileSync
6+ , rebuildFileAsync
5− ( rebuildFile
7+ , rebuildFile
6 ) where
8 ) where
7
9
8import Protolude
10import Protolude
@@ -38,8 +40,11 @@ import System.IO.UTF8 (readUTF8FileT)
38rebuildFile
40rebuildFile
39 :: (Ide m, MonadLogger m, MonadError PscIdeError m)
41 :: (Ide m, MonadLogger m, MonadError PscIdeError m)
40 => FilePath
42 => FilePath
43+ -- ^ The file to rebuild
44+ -> (ReaderT IdeEnvironment (LoggingT IO) () -> m ())
45+ -- ^ A runner for the second build with open exports
41 -> m Success
46 -> m Success
42−rebuildFile path = do
47+rebuildFile path runOpenBuild = do
43
48
44 input <- liftIO (readUTF8FileT path)
49 input <- liftIO (readUTF8FileT path)
45
50
@@ -71,11 +76,32 @@ rebuildFile path = do
71 case result of
76 case result of
72 Left errors -> throwError (RebuildError (toJSONErrors False P.Error errors))
77 Left errors -> throwError (RebuildError (toJSONErrors False P.Error errors))
73 Right _ -> do
78 Right _ -> do
74− env <- ask
75− let ll = confLogLevel (ideConfiguration env)
76− _ <- liftIO (async (runLogger ll (runReaderT (rebuildModuleOpen makeEnv externs m) env)))
79+ runOpenBuild (rebuildModuleOpen makeEnv externs m)
77 pure (RebuildSuccess (toJSONErrors False P.Warning warnings))
80 pure (RebuildSuccess (toJSONErrors False P.Warning warnings))
78
81
82+rebuildFileAsync
83+ :: forall m. (Ide m, MonadLogger m, MonadError PscIdeError m)
84+ => FilePath -> m Success
85+rebuildFileAsync fp = rebuildFile fp asyncRun
86+ where
87+ asyncRun :: ReaderT IdeEnvironment (LoggingT IO) () -> m ()
88+ asyncRun action = do
89+ env <- ask
90+ let ll = confLogLevel (ideConfiguration env)
91+ void (liftIO (async (runLogger ll (runReaderT action env))))
92+
93+rebuildFileSync
94+ :: forall m. (Ide m, MonadLogger m, MonadError PscIdeError m)
95+ => FilePath -> m Success
96+rebuildFileSync fp = rebuildFile fp syncRun
97+ where
98+ syncRun :: ReaderT IdeEnvironment (LoggingT IO) () -> m ()
99+ syncRun action = do
100+ env <- ask
101+ let ll = confLogLevel (ideConfiguration env)
102+ void (liftIO (runLogger ll (runReaderT action env)))
103+
104+
79-- | Rebuilds a module but opens up its export list first and stores the result
105-- | Rebuilds a module but opens up its export list first and stores the result
80-- inside the rebuild cache
106-- inside the rebuild cache
81rebuildModuleOpen
107rebuildModuleOpen
src/Language/PureScript/Ide/Reexports.hs+8−7
@@ -24,6 +24,7 @@ module Language.PureScript.Ide.Reexports
24
24
25import Protolude
25import Protolude
26
26
27+import Control.Lens hiding ((&))
27import qualified Data.Map as Map
28import qualified Data.Map as Map
28import qualified Language.PureScript as P
29import qualified Language.PureScript as P
29import Language.PureScript.Ide.Types
30import Language.PureScript.Ide.Types
@@ -89,7 +90,7 @@ resolveRef
89 -> Either P.DeclarationRef [IdeDeclarationAnn]
90 -> Either P.DeclarationRef [IdeDeclarationAnn]
90resolveRef decls ref = case ref of
91resolveRef decls ref = case ref of
91 P.TypeRef tn mdtors ->
92 P.TypeRef tn mdtors ->
92− case findRef (lensSatisfies (_IdeDeclType . ideTypeName) (== tn)) of
93+ case findRef (anyOf (_IdeDeclType . ideTypeName) (== tn)) of
93 Nothing -> Left ref
94 Nothing -> Left ref
94 Just d -> Right $ d : case mdtors of
95 Just d -> Right $ d : case mdtors of
95 Nothing ->
96 Nothing ->
@@ -99,13 +100,13 @@ resolveRef decls ref = case ref of
99 findDtors tn
100 findDtors tn
100 Just dtors -> mapMaybe lookupDtor dtors
101 Just dtors -> mapMaybe lookupDtor dtors
101 P.ValueRef i ->
102 P.ValueRef i ->
102− findWrapped (lensSatisfies (_IdeDeclValue . ideValueIdent) (== i))
103+ findWrapped (anyOf (_IdeDeclValue . ideValueIdent) (== i))
103 P.ValueOpRef name ->
104 P.ValueOpRef name ->
104− findWrapped (lensSatisfies (_IdeDeclValueOperator . ideValueOpName) (== name))
105+ findWrapped (anyOf (_IdeDeclValueOperator . ideValueOpName) (== name))
105 P.TypeOpRef name ->
106 P.TypeOpRef name ->
106− findWrapped (lensSatisfies (_IdeDeclTypeOperator . ideTypeOpName) (== name))
107+ findWrapped (anyOf (_IdeDeclTypeOperator . ideTypeOpName) (== name))
107 P.TypeClassRef name ->
108 P.TypeClassRef name ->
108− findWrapped (lensSatisfies (_IdeDeclTypeClass . ideTCName) (== name))
109+ findWrapped (anyOf (_IdeDeclTypeClass . ideTCName) (== name))
109 _ ->
110 _ ->
110 Left ref
111 Left ref
111 where
112 where
@@ -113,9 +114,9 @@ resolveRef decls ref = case ref of
113 findRef f = find (f . discardAnn) decls
114 findRef f = find (f . discardAnn) decls
114
115
115 lookupDtor name =
116 lookupDtor name =
116− findRef (lensSatisfies (_IdeDeclDataConstructor . ideDtorName) (== name))
117+ findRef (anyOf (_IdeDeclDataConstructor . ideDtorName) (== name))
117
118
118− findDtors tn = filter (lensSatisfies
119+ findDtors tn = filter (anyOf
119 (idaDeclaration
120 (idaDeclaration
120 . _IdeDeclDataConstructor
121 . _IdeDeclDataConstructor
121 . ideDtorTypeName) (== tn)) decls
122 . ideDtorTypeName) (== tn)) decls
src/Language/PureScript/Ide/State.hs+4−4
@@ -250,7 +250,7 @@ resolveInstances externs declarations =
250 go (ideInstance, classModule, className) acc' =
250 go (ideInstance, classModule, className) acc' =
251 let
251 let
252 matchTC =
252 matchTC =
253− lensSatisfies (idaDeclaration . _IdeDeclTypeClass . ideTCName) (== className)
253+ anyOf (idaDeclaration . _IdeDeclTypeClass . ideTCName) (== className)
254 updateDeclaration =
254 updateDeclaration =
255 mapIf matchTC (idaDeclaration
255 mapIf matchTC (idaDeclaration
256 . _IdeDeclTypeClass
256 . _IdeDeclTypeClass
@@ -283,14 +283,14 @@ resolveOperatorsForModule modules = map (idaDeclaration %~ resolveOperator)
283 | (P.Qualified (Just mn) (Left ident)) <- op ^. ideValueOpAlias =
283 | (P.Qualified (Just mn) (Left ident)) <- op ^. ideValueOpAlias =
284 let t = getDeclarations mn
284 let t = getDeclarations mn
285 & mapMaybe (preview _IdeDeclValue)
285 & mapMaybe (preview _IdeDeclValue)
286− & filter (lensSatisfies ideValueIdent (== ident))
286+ & filter (anyOf ideValueIdent (== ident))
287 & map (view ideValueType)
287 & map (view ideValueType)
288 & listToMaybe
288 & listToMaybe
289 in IdeDeclValueOperator (op & ideValueOpType .~ t)
289 in IdeDeclValueOperator (op & ideValueOpType .~ t)
290 | (P.Qualified (Just mn) (Right dtor)) <- op ^. ideValueOpAlias =
290 | (P.Qualified (Just mn) (Right dtor)) <- op ^. ideValueOpAlias =
291 let t = getDeclarations mn
291 let t = getDeclarations mn
292 & mapMaybe (preview _IdeDeclDataConstructor)
292 & mapMaybe (preview _IdeDeclDataConstructor)
293− & filter (lensSatisfies ideDtorName (== dtor))
293+ & filter (anyOf ideDtorName (== dtor))
294 & map (view ideDtorType)
294 & map (view ideDtorType)
295 & listToMaybe
295 & listToMaybe
296 in IdeDeclValueOperator (op & ideValueOpType .~ t)
296 in IdeDeclValueOperator (op & ideValueOpType .~ t)
@@ -298,7 +298,7 @@ resolveOperatorsForModule modules = map (idaDeclaration %~ resolveOperator)
298 | P.Qualified (Just mn) properName <- op ^. ideTypeOpAlias =
298 | P.Qualified (Just mn) properName <- op ^. ideTypeOpAlias =
299 let k = getDeclarations mn
299 let k = getDeclarations mn
300 & mapMaybe (preview _IdeDeclType)
300 & mapMaybe (preview _IdeDeclType)
301− & filter (lensSatisfies ideTypeName (== properName))
301+ & filter (anyOf ideTypeName (== properName))
302 & map (view ideTypeKind)
302 & map (view ideTypeKind)
303 & listToMaybe
303 & listToMaybe
304 in IdeDeclTypeOperator (op & ideTypeOpKind .~ k)
304 in IdeDeclTypeOperator (op & ideTypeOpKind .~ k)
src/Language/PureScript/Ide/Types.hs+3−3
@@ -32,7 +32,7 @@ type ModuleMap a = Map P.ModuleName a
32data IdeDeclaration
32data IdeDeclaration
33 = IdeDeclValue IdeValue
33 = IdeDeclValue IdeValue
34 | IdeDeclType IdeType
34 | IdeDeclType IdeType
35− | IdeDeclTypeSynonym IdeSynonym
35+ | IdeDeclTypeSynonym IdeTypeSynonym
36 | IdeDeclDataConstructor IdeDataConstructor
36 | IdeDeclDataConstructor IdeDataConstructor
37 | IdeDeclTypeClass IdeTypeClass
37 | IdeDeclTypeClass IdeTypeClass
38 | IdeDeclValueOperator IdeValueOperator
38 | IdeDeclValueOperator IdeValueOperator
@@ -50,7 +50,7 @@ data IdeType = IdeType
50 , _ideTypeKind :: P.Kind
50 , _ideTypeKind :: P.Kind
51 } deriving (Show, Eq, Ord)
51 } deriving (Show, Eq, Ord)
52
52
53−data IdeSynonym = IdeSynonym
53+data IdeTypeSynonym = IdeTypeSynonym
54 { _ideSynonymName :: P.ProperName 'P.TypeName
54 { _ideSynonymName :: P.ProperName 'P.TypeName
55 , _ideSynonymType :: P.Type
55 , _ideSynonymType :: P.Type
56 } deriving (Show, Eq, Ord)
56 } deriving (Show, Eq, Ord)
@@ -92,7 +92,7 @@ data IdeTypeOperator = IdeTypeOperator
92makePrisms ''IdeDeclaration
92makePrisms ''IdeDeclaration
93makeLenses ''IdeValue
93makeLenses ''IdeValue
94makeLenses ''IdeType
94makeLenses ''IdeType
95−makeLenses ''IdeSynonym
95+makeLenses ''IdeTypeSynonym
96makeLenses ''IdeDataConstructor
96makeLenses ''IdeDataConstructor
97makeLenses ''IdeTypeClass
97makeLenses ''IdeTypeClass
98makeLenses ''IdeInstance
98makeLenses ''IdeInstance
src/Language/PureScript/Ide/Util.hs+1−5
@@ -27,14 +27,13 @@ module Language.PureScript.Ide.Util
27 , prettyTypeT
27 , prettyTypeT
28 , properNameT
28 , properNameT
29 , identT
29 , identT
30− , lensSatisfies
31 , module Language.PureScript.Ide.Logging
30 , module Language.PureScript.Ide.Logging
32 ) where
31 ) where
33
32
34import Protolude hiding (decodeUtf8,
33import Protolude hiding (decodeUtf8,
35 encodeUtf8)
34 encodeUtf8)
36
35
37−import Control.Lens ((^.), (^?), Iso', iso, Getting, (<&>))
36+import Control.Lens hiding ((&), op)
38import Data.Aeson
37import Data.Aeson
39import qualified Data.Text as T
38import qualified Data.Text as T
40import qualified Data.Text.Lazy as TL
39import qualified Data.Text.Lazy as TL
@@ -131,6 +130,3 @@ prettyTypeT =
131 . T.lines
130 . T.lines
132 . T.pack
131 . T.pack
133 . P.prettyPrintTypeWithUnicode
132 . P.prettyPrintTypeWithUnicode
134−
135−lensSatisfies :: forall a s. Getting (First a) s a -> (a -> Bool) -> s -> Bool
136−lensSatisfies getter predicate value = value ^? getter <&> predicate & fromMaybe False
tests/Language/PureScript/Ide/Imports/IntegrationSpec.hsdeleted−83
@@ -1,83 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3−module Language.PureScript.Ide.Imports.IntegrationSpec where
4−
5−
6−import Protolude
7−
8−import qualified Data.Text as T
9−import qualified Language.PureScript.Ide.Integration as Integration
10−import Test.Hspec
11−
12−import System.Directory
13−import System.FilePath
14−import System.IO.UTF8 (readUTF8FileT)
15−
16−setup :: IO ()
17−setup = void (Integration.reset *> Integration.loadAll)
18−
19−withSupportFiles :: (FilePath -> FilePath -> IO a) -> IO ()
20−withSupportFiles test = do
21− pdir <- Integration.projectDirectory
22− let sourceFp = pdir </> "src" </> "ImportsSpec.purs"
23− outFp = pdir </> "src" </> "ImportsSpecOut.tmp"
24− Integration.deleteFileIfExists outFp
25− void $ test sourceFp outFp
26−
27−outputFileShouldBe :: [Text] -> IO ()
28−outputFileShouldBe expectation = do
29− outFp <- (</> "src" </> "ImportsSpecOut.tmp") <$> Integration.projectDirectory
30− outRes <- readUTF8FileT outFp
31− shouldBe (T.strip <$> T.lines outRes) expectation
32−
33−spec :: Spec
34−spec = beforeAll_ setup . describe "Adding imports" $ do
35− let
36− sourceFileSkeleton :: [Text] -> [Text]
37− sourceFileSkeleton importSection =
38− [ "module ImportsSpec where" , ""] ++ importSection ++ [ "" , "myId x = x"]
39− it "adds an implicit import" $ do
40− withSupportFiles (Integration.addImplicitImport "ImportsSpec1")
41− outputFileShouldBe (sourceFileSkeleton
42− [ "import ImportsSpec1"
43− ])
44− it "adds an explicit unqualified import" $ do
45− withSupportFiles (Integration.addImport "exportedFunction")
46− outputFileShouldBe (sourceFileSkeleton
47− [ "import ImportsSpec1 (exportedFunction)"
48− ])
49− it "adds an explicit unqualified import (type)" $ do
50− withSupportFiles (Integration.addImport "MyType")
51− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyType)"])
52− it "adds an explicit unqualified import (parameterized type)" $ do
53− withSupportFiles (Integration.addImport "MyParamType")
54− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyParamType)"])
55− it "adds an explicit unqualified import (typeclass)" $ do
56− withSupportFiles (Integration.addImport "ATypeClass")
57− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (class ATypeClass)"])
58− it "adds an explicit unqualified import (dataconstructor)" $ do
59− withSupportFiles (Integration.addImport "MyJust")
60− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyMaybe(..))"])
61− it "adds an explicit unqualified import (newtype)" $ do
62− withSupportFiles (Integration.addImport "MyNewtype")
63− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyNewtype(..))"])
64− it "adds an explicit unqualified import (typeclass member function)" $ do
65− withSupportFiles (Integration.addImport "typeClassFun")
66− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (typeClassFun)"])
67− it "doesn't add a newtypes constructor if only the type is exported" $ do
68− withSupportFiles (Integration.addImport "OnlyTypeExported")
69− outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (OnlyTypeExported)"])
70− it "doesn't add an import if the identifier is defined in the module itself" $ do
71− withSupportFiles (Integration.addImport "myId")
72− outputFileShouldBe (sourceFileSkeleton [])
73− it "responds with an error if it's undecidable whether we want a type or constructor" $
74− withSupportFiles (\sourceFp outFp -> do
75− r <- Integration.addImport "SpecialCase" sourceFp outFp
76− shouldBe False (Integration.resultIsSuccess r)
77− shouldBe False =<< doesFileExist outFp)
78− it "responds with an error if the identifier cannot be found and doesn't \
79− \write to the output file" $
80− withSupportFiles (\sourceFp outFp -> do
81− r <- Integration.addImport "doesntExist" sourceFp outFp
82− shouldBe False (Integration.resultIsSuccess r)
83− shouldBe False =<< doesFileExist outFp)
tests/Language/PureScript/Ide/ImportsSpec.hs+65
@@ -6,8 +6,12 @@ import Protolude
6import Data.Maybe (fromJust)
6import Data.Maybe (fromJust)
7
7
8import qualified Language.PureScript as P
8import qualified Language.PureScript as P
9+import Language.PureScript.Ide.Command as Command
10+import Language.PureScript.Ide.Error
9import Language.PureScript.Ide.Imports
11import Language.PureScript.Ide.Imports
12+import qualified Language.PureScript.Ide.Test as Test
10import Language.PureScript.Ide.Types
13import Language.PureScript.Ide.Types
14+import System.FilePath
11import Test.Hspec
15import Test.Hspec
12
16
13simpleFile :: [Text]
17simpleFile :: [Text]
@@ -22,6 +26,7 @@ splitSimpleFile :: (P.ModuleName, [Text], [Import], [Text])
22splitSimpleFile = fromRight (sliceImportSection simpleFile)
26splitSimpleFile = fromRight (sliceImportSection simpleFile)
23 where
27 where
24 fromRight = fromJust . rightToMaybe
28 fromRight = fromJust . rightToMaybe
29+
25withImports :: [Text] -> [Text]
30withImports :: [Text] -> [Text]
26withImports is =
31withImports is =
27 take 2 simpleFile ++ is ++ drop 2 simpleFile
32 take 2 simpleFile ++ is ++ drop 2 simpleFile
@@ -171,3 +176,63 @@ spec = do
171 -- the imported names don't actually have to exist!
176 -- the imported names don't actually have to exist!
172 (map (uncurry dtorImport) [("Just", "Maybe"), ("Nothing", "Maybe"), ("SomeOtherConstructor", "SomeDataType")])
177 (map (uncurry dtorImport) [("Just", "Maybe"), ("Nothing", "Maybe"), ("SomeOtherConstructor", "SomeDataType")])
173 ["import Prelude", "import Control.Monad (Maybe(..), SomeDataType(..), ap)"]
178 ["import Prelude", "import Control.Monad (Maybe(..), SomeDataType(..), ap)"]
179+ describe "importing from a loaded IdeState" importFromIdeState
180+
181+implImport :: Text -> Command
182+implImport mn =
183+ Command.Import ("src" </> "ImportsSpec.purs") Nothing [] (Command.AddImplicitImport (Test.mn mn))
184+
185+addExplicitImport :: Text -> Command
186+addExplicitImport i =
187+ Command.Import ("src" </> "ImportsSpec.purs") Nothing [] (Command.AddImportForIdentifier i)
188+
189+importShouldBe :: [Text] -> [Text] -> Expectation
190+importShouldBe res importSection =
191+ res `shouldBe` [ "module ImportsSpec where" , ""] ++ importSection ++ [ "" , "myId x = x"]
192+
193+runIdeLoaded :: Command -> IO (Either PscIdeError Success)
194+runIdeLoaded c = do
195+ ([_, result], _) <- Test.inProject $ Test.runIde [Command.LoadSync [] , c]
196+ pure result
197+
198+importFromIdeState :: Spec
199+importFromIdeState = do
200+ it "adds an implicit import" $ do
201+ Right (MultilineTextResult result) <-
202+ runIdeLoaded (implImport "ImportsSpec1")
203+ result `importShouldBe` [ "import ImportsSpec1" ]
204+ it "adds an explicit unqualified import" $ do
205+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "exportedFunction")
206+ result `importShouldBe` [ "import ImportsSpec1 (exportedFunction)" ]
207+ it "adds an explicit unqualified import (type)" $ do
208+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "MyType")
209+ result `importShouldBe` [ "import ImportsSpec1 (MyType)" ]
210+ it "adds an explicit unqualified import (parameterized type)" $ do
211+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "MyParamType")
212+ result `importShouldBe` [ "import ImportsSpec1 (MyParamType)" ]
213+ it "adds an explicit unqualified import (typeclass)" $ do
214+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "ATypeClass")
215+ result `importShouldBe` [ "import ImportsSpec1 (class ATypeClass)" ]
216+ it "adds an explicit unqualified import (dataconstructor)" $ do
217+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "MyJust")
218+ result `importShouldBe` [ "import ImportsSpec1 (MyMaybe(..))" ]
219+ it "adds an explicit unqualified import (newtype)" $ do
220+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "MyNewtype")
221+ result `importShouldBe` [ "import ImportsSpec1 (MyNewtype(..))" ]
222+ it "adds an explicit unqualified import (typeclass member function)" $ do
223+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "typeClassFun")
224+ result `importShouldBe` [ "import ImportsSpec1 (typeClassFun)" ]
225+ it "doesn't add a newtypes constructor if only the type is exported" $ do
226+ Right (MultilineTextResult result) <-
227+ runIdeLoaded (addExplicitImport "OnlyTypeExported")
228+ result `importShouldBe` [ "import ImportsSpec1 (OnlyTypeExported)" ]
229+ it "doesn't add an import if the identifier is defined in the module itself" $ do
230+ Right (MultilineTextResult result) <- runIdeLoaded (addExplicitImport "myId")
231+ result `importShouldBe` []
232+ it "responds with an error if it's undecidable whether we want a type or constructor" $ do
233+ result <- runIdeLoaded (addExplicitImport "SpecialCase")
234+ result `shouldSatisfy` isLeft
235+ it "responds with an error if the identifier cannot be found and doesn't \
236+ \write to the output file" $ do
237+ result <- runIdeLoaded (addExplicitImport "doesnExist")
238+ result `shouldSatisfy` isLeft
tests/Language/PureScript/Ide/Integration.hsdeleted−280
@@ -1,280 +0,0 @@
1−-----------------------------------------------------------------------------
2−--
3−-- Module : Language.PureScript.Ide.Integration
4−-- Description : A psc-ide client for use in integration tests
5−-- Copyright : Christoph Hegemann 2016
6−-- License : MIT (http://opensource.org/licenses/MIT)
7−--
8−-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9−-- Stability : experimental
10−--
11−-- |
12−-- A psc-ide client for use in integration tests
13−-----------------------------------------------------------------------------
14−
15−{-# LANGUAGE OverloadedStrings #-}
16−{-# LANGUAGE ScopedTypeVariables #-}
17−{-# LANGUAGE NoImplicitPrelude #-}
18−module Language.PureScript.Ide.Integration
19− (
20− -- managing the server process
21− startServer
22− , withServer
23− , stopServer
24− , quitServer
25− -- util
26− , compileTestProject
27− , deleteOutputFolder
28− , projectDirectory
29− , deleteFileIfExists
30− -- sending commands
31− , addImport
32− , addImplicitImport
33− , loadAll
34− , loadModule
35− , loadModules
36− , getCwd
37− , getFlexCompletions
38− , getFlexCompletionsInModule
39− , getType
40− , rebuildModule
41− , reset
42− -- checking results
43− , resultIsSuccess
44− , parseCompletions
45− , parseTextResult
46− ) where
47−
48−import Protolude
49−import Data.Maybe (fromJust)
50−
51−import Data.Aeson
52−import Data.Aeson.Types
53−import qualified Data.Text.Encoding as T
54−import qualified Data.ByteString.Char8 as BS8
55−import qualified Data.ByteString.Lazy.Char8 as BSL8
56−import qualified Data.Vector as V
57−import qualified Language.PureScript as P
58−import System.Directory
59−import System.FilePath
60−import System.IO
61−import System.IO.Error (mkIOError, userErrorType)
62−import System.Process
63−
64−projectDirectory :: IO FilePath
65−projectDirectory = do
66− cd <- getCurrentDirectory
67− return $ cd </> "tests" </> "support" </> "pscide"
68−
69−startServer :: IO ProcessHandle
70−startServer = do
71− pdir <- projectDirectory
72− -- Turn off filewatching since it creates race condition in a testing environment
73− (_, _, _, procHandle) <- createProcess $
74− (shell "psc-ide-server --no-watch src/*.purs") {cwd = Just pdir}
75− threadDelay 2000000 -- give the server 2s to start up
76− return procHandle
77−
78−stopServer :: ProcessHandle -> IO ()
79−stopServer = terminateProcess
80−
81−withServer :: IO a -> IO a
82−withServer s = do
83− _ <- startServer
84− started <- tryNTimes 5 (rightToMaybe <$> (try getCwd :: IO (Either SomeException Text)))
85− when (isNothing started) $
86− throwIO (mkIOError userErrorType "psc-ide-server didn't start in time" Nothing Nothing)
87− r <- s
88− quitServer
89− pure r
90−
91−-- project management utils
92−
93−compileTestProject :: IO Bool
94−compileTestProject = do
95− pdir <- projectDirectory
96− (_, _, _, procHandle) <- createProcess $
97− (shell . toS $ "psc " <> fileGlob) { cwd = Just pdir }
98− r <- tryNTimes 10 (getProcessExitCode procHandle)
99− pure (fromMaybe False (isSuccess <$> r))
100−
101−tryNTimes :: Int -> IO (Maybe a) -> IO (Maybe a)
102−tryNTimes 0 _ = pure Nothing
103−tryNTimes n action = do
104− r <- action
105− case r of
106− Nothing -> do
107− threadDelay 500000
108− tryNTimes (n - 1) action
109− Just a -> pure (Just a)
110−
111−deleteOutputFolder :: IO ()
112−deleteOutputFolder = do
113− odir <- fmap (</> "output") projectDirectory
114− whenM (doesDirectoryExist odir) (removeDirectoryRecursive odir)
115−
116−deleteFileIfExists :: FilePath -> IO ()
117−deleteFileIfExists fp = whenM (doesFileExist fp) (removeFile fp)
118−
119−isSuccess :: ExitCode -> Bool
120−isSuccess ExitSuccess = True
121−isSuccess (ExitFailure _) = False
122−
123−fileGlob :: Text
124−fileGlob = "\"src/**/*.purs\""
125−
126−-- Integration Testing API
127−
128−sendCommand :: Value -> IO Text
129−sendCommand v = do
130− (Just hin, Just hout, _, _) <-
131− createProcess ((proc "psc-ide-client" []) {std_in=CreatePipe, std_out=CreatePipe})
132−
133− hSetEncoding hin utf8
134− hSetEncoding hout utf8
135−
136− BS8.hPutStrLn hin (BSL8.toStrict (encode v))
137− hFlush hin
138− T.decodeUtf8 <$> BS8.hGetLine hout
139−
140−quitServer :: IO ()
141−quitServer = do
142− let quitCommand = object ["command" .= ("quit" :: Text)]
143− _ <- try $ sendCommand quitCommand :: IO (Either SomeException Text)
144− return ()
145−
146−reset :: IO ()
147−reset = do
148− let resetCommand = object ["command" .= ("reset" :: Text)]
149− _ <- try $ sendCommand resetCommand :: IO (Either SomeException Text)
150− return ()
151−
152−getCwd :: IO Text
153−getCwd = do
154− let cwdCommand = object ["command" .= ("cwd" :: Text)]
155− sendCommand cwdCommand
156−
157−loadModule :: Text -> IO Text
158−loadModule m = loadModules [m]
159−
160−loadModules :: [Text] -> IO Text
161−loadModules = sendCommand . load
162−
163−loadAll :: IO Text
164−loadAll = sendCommand (load [])
165−
166−getFlexCompletions :: Text -> IO [(Text, Text, Text, Maybe P.SourceSpan)]
167−getFlexCompletions q = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)) Nothing)
168−
169−getFlexCompletionsInModule :: Text -> Text -> IO [(Text, Text, Text, Maybe P.SourceSpan)]
170−getFlexCompletionsInModule q m = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)) (Just m))
171−
172−getType :: Text -> IO [(Text, Text, Text, Maybe P.SourceSpan)]
173−getType q = parseCompletions <$> sendCommand (typeC q [])
174−
175−addImport :: Text -> FilePath -> FilePath -> IO Text
176−addImport identifier fp outfp = sendCommand (addImportC identifier fp outfp)
177−
178−addImplicitImport :: Text -> FilePath -> FilePath -> IO Text
179−addImplicitImport mn fp outfp = sendCommand (addImplicitImportC mn fp outfp)
180−
181−rebuildModule :: FilePath -> IO Text
182−rebuildModule m = sendCommand (rebuildC m Nothing)
183−
184−-- Command Encoding
185−
186−commandWrapper :: Text -> Value -> Value
187−commandWrapper c p = object ["command" .= c, "params" .= p]
188−
189−load :: [Text] -> Value
190−load ms = commandWrapper "load" (object ["modules" .= ms])
191−
192−typeC :: Text -> [Value] -> Value
193−typeC q filters = commandWrapper "type" (object ["search" .= q, "filters" .= filters])
194−
195−addImportC :: Text -> FilePath -> FilePath -> Value
196−addImportC identifier = addImportW $
197− object [ "importCommand" .= ("addImport" :: Text)
198− , "identifier" .= identifier
199− ]
200−
201−addImplicitImportC :: Text -> FilePath -> FilePath -> Value
202−addImplicitImportC mn = addImportW $
203− object [ "importCommand" .= ("addImplicitImport" :: Text)
204− , "module" .= mn
205− ]
206−
207−rebuildC :: FilePath -> Maybe FilePath -> Value
208−rebuildC file outFile =
209− commandWrapper "rebuild" (object [ "file" .= file
210− , "outfile" .= outFile
211− ])
212−
213−addImportW :: Value -> FilePath -> FilePath -> Value
214−addImportW importCommand fp outfp =
215− commandWrapper "import" (object [ "file" .= fp
216− , "outfile" .= outfp
217− , "importCommand" .= importCommand
218− ])
219−
220−
221−completion :: [Value] -> Maybe Value -> Maybe Text -> Value
222−completion filters matcher currentModule =
223− let
224− matcher' = case matcher of
225− Nothing -> []
226− Just m -> ["matcher" .= m]
227− currentModule' = case currentModule of
228− Nothing -> []
229− Just cm -> ["currentModule" .= cm]
230− in
231− commandWrapper "complete" (object $ "filters" .= filters : matcher' ++ currentModule' )
232−
233−flexMatcher :: Text -> Value
234−flexMatcher q = object [ "matcher" .= ("flex" :: Text)
235− , "params" .= object ["search" .= q]
236− ]
237−
238−-- Result parsing
239−
240−unwrapResult :: Value -> Parser (Either Text Value)
241−unwrapResult = withObject "result" $ \o -> do
242− (rt :: Text) <- o .: "resultType"
243− case rt of
244− "error" -> do
245− res <- o .: "result"
246− withArray "errors" (fmap (Left . fold) . traverse (withObject "error" (.: "message"))) res
247− "success" -> do
248− res <- o .: "result"
249− pure (Right res)
250− _ -> mzero
251−
252−withResult :: (Value -> Parser a) -> Value -> Parser (Either Text a)
253−withResult p v = do
254− r <- unwrapResult v
255− case r of
256− Left err -> pure (Left err)
257− Right res -> Right <$> p res
258−
259−completionParser :: Value -> Parser [(Text, Text, Text, Maybe P.SourceSpan)]
260−completionParser = withArray "res" $ \cs ->
261− mapM (withObject "completion" $ \o -> do
262− ident <- o .: "identifier"
263− module' <- o .: "module"
264− ty <- o .: "type"
265− ss <- o .: "definedAt"
266− pure (module', ident, ty, ss)) (V.toList cs)
267−
268−valueFromText :: Text -> Value
269−valueFromText = fromJust . decode . toS
270−
271−resultIsSuccess :: Text -> Bool
272−resultIsSuccess = isRight . join . first toS . parseEither unwrapResult . valueFromText
273−
274−parseCompletions :: Text -> [(Text, Text, Text, Maybe P.SourceSpan)]
275−parseCompletions s =
276− fromJust $ join (rightToMaybe <$> parseMaybe (withResult completionParser) (valueFromText s))
277−
278−parseTextResult :: Text -> Text
279−parseTextResult s =
280− fromJust $ join (rightToMaybe <$> parseMaybe (withResult (withText "tr" pure)) (valueFromText s))
tests/Language/PureScript/Ide/MatcherSpec.hs−13
@@ -6,7 +6,6 @@ module Language.PureScript.Ide.MatcherSpec where
6import Protolude
6import Protolude
7
7
8import qualified Language.PureScript as P
8import qualified Language.PureScript as P
9−import Language.PureScript.Ide.Integration
10import Language.PureScript.Ide.Matcher
9import Language.PureScript.Ide.Matcher
11import Language.PureScript.Ide.Types
10import Language.PureScript.Ide.Types
12import Language.PureScript.Ide.Util
11import Language.PureScript.Ide.Util
@@ -26,9 +25,6 @@ completions = [firstResult, secondResult, fiult]
26runFlex :: Text -> [Match IdeDeclarationAnn]
25runFlex :: Text -> [Match IdeDeclarationAnn]
27runFlex s = runMatcher (flexMatcher s) completions
26runFlex s = runMatcher (flexMatcher s) completions
28
27
29−setup :: IO ()
30−setup = reset *> void loadAll
31−
32spec :: Spec
28spec :: Spec
33spec = do
29spec = do
34 describe "Flex Matcher" $ do
30 describe "Flex Matcher" $ do
@@ -38,12 +34,3 @@ spec = do
38 runFlex "firstResult" `shouldBe` [firstResult]
34 runFlex "firstResult" `shouldBe` [firstResult]
39 it "scores short matches higher and sorts accordingly" $
35 it "scores short matches higher and sorts accordingly" $
40 runFlex "filt" `shouldBe` [fiult, firstResult]
36 runFlex "filt" `shouldBe` [fiult, firstResult]
41−
42− beforeAll_ setup . describe "Integration Tests: Flex Matcher" $ do
43− it "doesn't match on an empty string" $ do
44− cs <- getFlexCompletions ""
45− cs `shouldBe` []
46− it "matches on equality" $ do
47− -- ignore any position information
48− (m, i, t, _) : _ <- getFlexCompletions "const"
49− (m, i, t) `shouldBe` ("MatcherSpec", "const", "∀ a b. a → b → a")
tests/Language/PureScript/Ide/RebuildSpec.hs+41−37
@@ -4,54 +4,58 @@ module Language.PureScript.Ide.RebuildSpec where
4
4
5import Protolude
5import Protolude
6
6
7+import Language.PureScript.Ide.Command
8+import Language.PureScript.Ide.Matcher
9+import Language.PureScript.Ide.Types
7−import qualified Language.PureScript.Ide.Integration as Integration
10+import qualified Language.PureScript.Ide.Test as Test
8import System.FilePath
11import System.FilePath
9import Test.Hspec
12import Test.Hspec
10
13
11−shouldBeSuccess :: Text -> IO ()
14+load :: [Text] -> Command
12−shouldBeSuccess = shouldBe True . Integration.resultIsSuccess
15+load = LoadSync . map Test.mn
13
16
14−shouldBeFailure :: Text -> IO ()
17+rebuild :: FilePath -> Command
15−shouldBeFailure = shouldBe False . Integration.resultIsSuccess
18+rebuild fp = Rebuild ("src" </> fp)
19+
20+rebuildSync :: FilePath -> Command
21+rebuildSync fp = RebuildSync ("src" </> fp)
16
22
17spec :: Spec
23spec :: Spec
18−spec = before_ Integration.reset . describe "Rebuilding single modules" $ do
24+spec = describe "Rebuilding single modules" $ do
19 it "rebuilds a correct module without dependencies successfully" $ do
25 it "rebuilds a correct module without dependencies successfully" $ do
20− _ <- Integration.loadModule "RebuildSpecSingleModule"
26+ ([_, result], _) <- Test.inProject $
21− pdir <- Integration.projectDirectory
27+ Test.runIde [ load ["RebuildSpecSingleModule"]
22− let file = pdir </> "src" </> "RebuildSpecSingleModule.purs"
28+ , rebuild "RebuildSpecSingleModule.purs"
23− Integration.rebuildModule file >>= shouldBeSuccess
29+ ]
30+ result `shouldSatisfy` isRight
24 it "fails to rebuild an incorrect module without dependencies and returns the errors" $ do
31 it "fails to rebuild an incorrect module without dependencies and returns the errors" $ do
25− pdir <- Integration.projectDirectory
32+ ([result], _) <- Test.inProject $
26− let file = pdir </> "src" </> "RebuildSpecSingleModule.fail"
33+ Test.runIde [ rebuild "RebuildSpecSingleModule.fail" ]
27− Integration.rebuildModule file >>= shouldBeFailure
34+ result `shouldSatisfy` isLeft
28 it "rebuilds a correct module with its dependencies successfully" $ do
35 it "rebuilds a correct module with its dependencies successfully" $ do
36+ ([_, result], _) <- Test.inProject $
29− _ <- Integration.loadModules ["RebuildSpecWithDeps", "RebuildSpecDep"]
37+ Test.runIde [ load ["RebuildSpecWithDeps", "RebuildSpecDep"]
30− pdir <- Integration.projectDirectory
31− let file = pdir </> "src" </> "RebuildSpecWithDeps.purs"
38+ , rebuild "RebuildSpecWithDeps.purs"
32− Integration.rebuildModule file >>= shouldBeSuccess
39+ ]
40+ result `shouldSatisfy` isRight
33 it "rebuilds a correct module that has reverse dependencies" $ do
41 it "rebuilds a correct module that has reverse dependencies" $ do
34− _ <- Integration.loadModule "RebuildSpecWithDeps"
42+ ([_, result], _) <- Test.inProject $
35− pdir <- Integration.projectDirectory
36− let file = pdir </> "src" </> "RebuildSpecDep.purs"
43+ Test.runIde [ load ["RebuildSpecWithDeps"], rebuild "RebuildSpecDep.purs" ]
37− Integration.rebuildModule file >>= shouldBeSuccess
44+ result `shouldSatisfy` isRight
38 it "fails to rebuild a module if its dependencies are not loaded" $ do
45 it "fails to rebuild a module if its dependencies are not loaded" $ do
39− _ <- Integration.loadModule "RebuildSpecWithDeps"
46+ ([_, result], _) <- Test.inProject $
40− pdir <- Integration.projectDirectory
41− let file = pdir </> "src" </> "RebuildSpecWithDeps.purs"
47+ Test.runIde [ load ["RebuildSpecWithDeps"], rebuild "RebuildSpecWithDeps.purs" ]
42− Integration.rebuildModule file >>= shouldBeFailure
48+ result `shouldSatisfy` isLeft
43 it "rebuilds a correct module with a foreign file" $ do
49 it "rebuilds a correct module with a foreign file" $ do
44− _ <- Integration.loadModule "RebuildSpecWithForeign"
50+ ([_, result], _) <- Test.inProject $
45− pdir <- Integration.projectDirectory
46− let file = pdir </> "src" </> "RebuildSpecWithForeign.purs"
51+ Test.runIde [ load ["RebuildSpecWithForeign"], rebuild "RebuildSpecWithForeign.purs" ]
47− Integration.rebuildModule file >>= shouldBeSuccess
52+ result `shouldSatisfy` isRight
48 it "fails to rebuild a module with a foreign import but no file" $ do
53 it "fails to rebuild a module with a foreign import but no file" $ do
49− pdir <- Integration.projectDirectory
54+ ([result], _) <- Test.inProject $
50− let file = pdir </> "src" </> "RebuildSpecWithMissingForeign.fail"
55+ Test.runIde [ rebuild "RebuildSpecWithMissingForeign.fail" ]
51− Integration.rebuildModule file >>= shouldBeFailure
56+ result `shouldSatisfy` isLeft
52 it "completes a hidden identifier after rebuilding" $ do
57 it "completes a hidden identifier after rebuilding" $ do
53− pdir <- Integration.projectDirectory
58+ ([_, (Right (CompletionResult [ result ]))], _) <- Test.inProject $
54− let file = pdir </> "src" </> "RebuildSpecWithHiddenIdent.purs"
59+ Test.runIde [ rebuildSync "RebuildSpecWithHiddenIdent.purs"
55− Integration.rebuildModule file >>= shouldBeSuccess
56− res <- Integration.getFlexCompletionsInModule "hid" "RebuildSpecWithHiddenIdent"
60+ , Complete [] (flexMatcher "hid") (Just (Test.mn "RebuildSpecWithHiddenIdent"))]
57− shouldBe False (null res)
61+ complIdentifier result `shouldBe` "hidden"
tests/Language/PureScript/Ide/SourceFile/IntegrationSpec.hsdeleted−41
@@ -1,41 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3−module Language.PureScript.Ide.SourceFile.IntegrationSpec where
4−
5−
6−import Protolude
7−
8−import qualified Data.Text as T
9−import qualified Language.PureScript.Ide.Integration as Integration
10−import qualified Language.PureScript as P
11−import Test.Hspec
12−
13−setup :: IO ()
14−setup = void (Integration.reset *> Integration.loadAll)
15−
16−spec :: Spec
17−spec = beforeAll_ setup $
18− describe "Sourcefile Integration" $ do
19− it "finds a value declaration" $
20− testCase "sfValue" (3, 1)
21− it "finds a type declaration" $
22− testCase "SFType" (5, 1)
23− it "finds a data declaration" $
24− testCase "SFData" (7, 1)
25− it "finds a data constructor" $
26− testCase "SFOne" (7, 1)
27− it "finds a typeclass" $
28− testCase "SFClass" (9, 1)
29− it "finds a typeclass member" $
30− testCase "sfShow" (10, 3)
31−
32−testCase :: Text -> (Int, Int) -> IO ()
33−testCase s (x, y) = do
34− P.SourceSpan f (P.SourcePos l c) _ <- getLocation s
35− toS f `shouldSatisfy` T.isSuffixOf "SourceFileSpec.purs"
36− (l, c) `shouldBe` (x, y)
37−
38−getLocation :: Text -> IO P.SourceSpan
39−getLocation s = do
40− (_, _, _, Just location) : _ <- Integration.getType s
41− pure location
tests/Language/PureScript/Ide/SourceFileSpec.hs+42
@@ -5,8 +5,10 @@ module Language.PureScript.Ide.SourceFileSpec where
5import Protolude
5import Protolude
6
6
7import qualified Language.PureScript as P
7import qualified Language.PureScript as P
8+import Language.PureScript.Ide.Command
8import Language.PureScript.Ide.SourceFile
9import Language.PureScript.Ide.SourceFile
9import Language.PureScript.Ide.Types
10import Language.PureScript.Ide.Types
11+import Language.PureScript.Ide.Test
10import Test.Hspec
12import Test.Hspec
11
13
12span0, span1, span2 :: P.SourceSpan
14span0, span1, span2 :: P.SourceSpan
@@ -52,3 +54,43 @@ spec = do
52 describe "Type annotations" $ do
54 describe "Type annotations" $ do
53 it "extracts a type annotation" $
55 it "extracts a type annotation" $
54 extractTypeAnnotations [typeAnnotation1] `shouldBe` [(P.Ident "value1", P.REmpty)]
56 extractTypeAnnotations [typeAnnotation1] `shouldBe` [(P.Ident "value1", P.REmpty)]
57+ describe "Finding Source Spans for identifiers" $ do
58+ it "finds a value declaration" $ do
59+ Just r <- getLocation "sfValue"
60+ r `shouldBe` valueSS
61+ it "finds a synonym declaration" $ do
62+ Just r <- getLocation "SFType"
63+ r `shouldBe` synonymSS
64+ it "finds a data declaration and its constructors" $ do
65+ rs <- traverse getLocation ["SFData", "SFOne", "SFTwo", "SFThree"]
66+ traverse_ (`shouldBe` (Just typeSS)) rs
67+ it "finds a class declaration" $ do
68+ Just r <- getLocation "SFClass"
69+ r `shouldBe` classSS
70+
71+getLocation :: Text -> IO (Maybe P.SourceSpan)
72+getLocation s = do
73+ ([Right (CompletionResult [c])], _) <-
74+ runIde' defConfig ideState [Type s [] Nothing]
75+ pure (complLocation c)
76+ where
77+ ideState = emptyIdeState `s3`
78+ [ ("Test",
79+ [ ideValue "sfValue" Nothing `annLoc` valueSS
80+ , ideSynonym "SFType" P.tyString `annLoc` synonymSS
81+ , ideType "SFData" Nothing `annLoc` typeSS
82+ , ideDtor "SFOne" "SFData" Nothing `annLoc` typeSS
83+ , ideDtor "SFTwo" "SFData" Nothing `annLoc` typeSS
84+ , ideDtor "SFThree" "SFData" Nothing `annLoc` typeSS
85+ , ideTypeClass "SFClass" [] `annLoc` classSS
86+ ])
87+ ]
88+
89+valueSS, synonymSS, typeSS, classSS :: P.SourceSpan
90+valueSS = ss 3 1
91+synonymSS = ss 5 1
92+typeSS = ss 7 1
93+classSS = ss 8 1
94+
95+ss :: Int -> Int -> P.SourceSpan
96+ss x y = P.SourceSpan "Test.purs" (P.SourcePos x y) (P.SourcePos x y)
tests/Language/PureScript/Ide/Test.hsnew file+135
@@ -0,0 +1,135 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE PackageImports #-}
4+module Language.PureScript.Ide.Test where
5+
6+import Control.Concurrent.STM
7+import "monad-logger" Control.Monad.Logger
8+import qualified Data.Map as Map
9+import Language.PureScript.Ide
10+import Language.PureScript.Ide.Command
11+import Language.PureScript.Ide.Error
12+import Language.PureScript.Ide.Types
13+import Protolude
14+import System.Directory
15+import System.FilePath
16+import System.Process
17+
18+import qualified Language.PureScript as P
19+
20+defConfig :: Configuration
21+defConfig =
22+ Configuration { confLogLevel = LogNone
23+ , confOutputPath = "output/"
24+ , confGlobs = ["src/*.purs"]
25+ }
26+
27+runIde' :: Configuration -> IdeState -> [Command] -> IO ([Either PscIdeError Success], IdeState)
28+runIde' conf s cs = do
29+ stateVar <- newTVarIO s
30+ let env' = IdeEnvironment {ideStateVar = stateVar, ideConfiguration = conf}
31+ r <- runNoLoggingT (runReaderT (traverse (runExceptT . handleCommand) cs) env')
32+ newState <- readTVarIO stateVar
33+ pure (r, newState)
34+
35+runIde :: [Command] -> IO ([Either PscIdeError Success], IdeState)
36+runIde = runIde' defConfig emptyIdeState
37+
38+s3 :: IdeState -> [(Text, [IdeDeclarationAnn])] -> IdeState
39+s3 s ds =
40+ s {ideStage3 = stage3}
41+ where
42+ stage3 = Stage3 (Map.fromList decls) Nothing
43+ decls = map (first P.moduleNameFromString) ds
44+
45+-- | Adding Annotations to IdeDeclarations
46+ann :: IdeDeclarationAnn -> Annotation -> IdeDeclarationAnn
47+ann (IdeDeclarationAnn _ d) a = IdeDeclarationAnn a d
48+
49+annLoc :: IdeDeclarationAnn -> P.SourceSpan -> IdeDeclarationAnn
50+annLoc (IdeDeclarationAnn a d) loc = IdeDeclarationAnn a {annLocation = Just loc} d
51+
52+annExp :: IdeDeclarationAnn -> P.ModuleName -> IdeDeclarationAnn
53+annExp (IdeDeclarationAnn a d) e = IdeDeclarationAnn a {annExportedFrom = Just e} d
54+
55+annTyp :: IdeDeclarationAnn -> P.Type -> IdeDeclarationAnn
56+annTyp (IdeDeclarationAnn a d) ta = IdeDeclarationAnn a {annTypeAnnotation = Just ta} d
57+
58+
59+ida :: IdeDeclaration -> IdeDeclarationAnn
60+ida = IdeDeclarationAnn emptyAnn
61+
62+-- | Builders for Ide declarations
63+ideValue :: Text -> Maybe P.Type -> IdeDeclarationAnn
64+ideValue i ty = ida (IdeDeclValue (IdeValue (P.Ident i) (fromMaybe P.tyString ty)))
65+
66+ideType :: Text -> Maybe P.Kind -> IdeDeclarationAnn
67+ideType pn ki = ida (IdeDeclType (IdeType (P.ProperName pn) (fromMaybe P.kindType ki)))
68+
69+ideSynonym :: Text -> P.Type -> IdeDeclarationAnn
70+ideSynonym pn ty = ida (IdeDeclTypeSynonym (IdeTypeSynonym (P.ProperName pn) ty))
71+
72+ideTypeClass :: Text -> [IdeInstance] -> IdeDeclarationAnn
73+ideTypeClass pn instances = ida (IdeDeclTypeClass (IdeTypeClass (P.ProperName pn) instances))
74+
75+ideDtor :: Text -> Text -> Maybe P.Type -> IdeDeclarationAnn
76+ideDtor pn tn ty = ida (IdeDeclDataConstructor (IdeDataConstructor (P.ProperName pn) (P.ProperName tn) (fromMaybe P.tyString ty)))
77+
78+ideValueOp :: Text -> P.Qualified (Either Text Text) -> Integer -> Maybe P.Associativity -> Maybe P.Type -> IdeDeclarationAnn
79+ideValueOp opName ident precedence assoc t =
80+ ida (IdeDeclValueOperator
81+ (IdeValueOperator
82+ (P.OpName opName)
83+ (bimap P.Ident P.ProperName <$> ident)
84+ (precedence)
85+ (fromMaybe P.Infix assoc)
86+ t))
87+
88+ideTypeOp :: Text -> P.Qualified Text -> Integer -> Maybe P.Associativity -> Maybe P.Kind -> IdeDeclarationAnn
89+ideTypeOp opName ident precedence assoc k =
90+ ida (IdeDeclTypeOperator
91+ (IdeTypeOperator
92+ (P.OpName opName)
93+ (P.ProperName <$> ident)
94+ (precedence)
95+ (fromMaybe P.Infix assoc)
96+ k))
97+
98+ideKind :: Text -> IdeDeclarationAnn
99+ideKind pn = ida (IdeDeclKind (P.ProperName pn))
100+
101+mn :: Text -> P.ModuleName
102+mn = P.moduleNameFromString
103+
104+inProject :: IO a -> IO a
105+inProject f = do
106+ cwd' <- getCurrentDirectory
107+ setCurrentDirectory ("." </> "tests" </> "support" </> "pscide")
108+ a <- f
109+ setCurrentDirectory cwd'
110+ pure a
111+
112+compileTestProject :: IO Bool
113+compileTestProject = inProject $ do
114+ (_, _, _, procHandle) <-
115+ createProcess $ (shell $ "psc \"src/**/*.purs\"")
116+ r <- tryNTimes 10 (getProcessExitCode procHandle)
117+ pure (fromMaybe False (isSuccess <$> r))
118+
119+isSuccess :: ExitCode -> Bool
120+isSuccess ExitSuccess = True
121+isSuccess (ExitFailure _) = False
122+
123+tryNTimes :: Int -> IO (Maybe a) -> IO (Maybe a)
124+tryNTimes 0 _ = pure Nothing
125+tryNTimes n action = do
126+ r <- action
127+ case r of
128+ Nothing -> do
129+ threadDelay 500000
130+ tryNTimes (n - 1) action
131+ Just a -> pure (Just a)
132+
133+deleteOutputFolder :: IO ()
134+deleteOutputFolder = inProject $
135+ whenM (doesDirectoryExist "output") (removeDirectoryRecursive "output")
tests/TestPscIde.hs+3−5
@@ -1,15 +1,13 @@
1module TestPscIde where
1module TestPscIde where
2
2
3import Control.Monad (unless)
3import Control.Monad (unless)
4−import Language.PureScript.Ide.Integration
5import qualified PscIdeSpec
4import qualified PscIdeSpec
5+import Language.PureScript.Ide.Test
6import Test.Hspec
6import Test.Hspec
7
7
8main :: IO ()
8main :: IO ()
9main = do
9main = do
10 deleteOutputFolder
10 deleteOutputFolder
11 s <- compileTestProject
11 s <- compileTestProject
12− unless s $ fail "Failed to compile .purs sources"
12+ unless s (fail "Failed to compile .purs sources")
13−
14− quitServer -- shuts down any left over server (primarily happens during development)
15− withServer (hspec PscIdeSpec.spec)
13+ hspec PscIdeSpec.spec