Commit 989625b8 authored by Andrew Gibiansky's avatar Andrew Gibiansky

Travis fixes 10

parent 654369fd
...@@ -26,6 +26,7 @@ addons: ...@@ -26,6 +26,7 @@ addons:
packages: packages:
- libmagic-dev - libmagic-dev
- libgmp-dev - libgmp-dev
- python3
before_install: before_install:
# Download and unpack the stack executable # Download and unpack the stack executable
...@@ -58,3 +59,4 @@ script: ...@@ -58,3 +59,4 @@ script:
- export LD_LIBRARY_PATH=$HOME/zeromq/lib - export LD_LIBRARY_PATH=$HOME/zeromq/lib
- stack build --resolver=$RESOLVER - stack build --resolver=$RESOLVER
- stack test --resolver=$RESOLVER - stack test --resolver=$RESOLVER
- ./verify_formatting.py
...@@ -147,7 +147,6 @@ ihaskellGlobalImports = ...@@ -147,7 +147,6 @@ ihaskellGlobalImports =
, "import qualified IHaskell.Eval.Widgets" , "import qualified IHaskell.Eval.Widgets"
] ]
-- | Evaluation function for testing. -- | Evaluation function for testing.
testInterpret :: Interpreter a -> IO a testInterpret :: Interpreter a -> IO a
testInterpret val = interpret GHC.Paths.libdir False (const val) testInterpret val = interpret GHC.Paths.libdir False (const val)
......
{-# LANGUAGE QuasiQuotes, OverloadedStrings, ExtendedDefaultRules, CPP #-} {-# LANGUAGE QuasiQuotes, OverloadedStrings, ExtendedDefaultRules, CPP #-}
-- Keep all the language pragmas here so it can be compiled separately. -- Keep all the language pragmas here so it can be compiled separately.
module Main where module Main where
...@@ -48,7 +49,7 @@ replace :: String -> String -> String -> String ...@@ -48,7 +49,7 @@ replace :: String -> String -> String -> String
replace needle replacement haystack = replace needle replacement haystack =
T.unpack $ T.replace (T.pack needle) (T.pack replacement) (T.pack haystack) T.unpack $ T.replace (T.pack needle) (T.pack replacement) (T.pack haystack)
traceShowId x = traceShow x x traceShowId x = traceShow x x
doGhc = runGhc (Just libdir) doGhc = runGhc (Just libdir)
...@@ -65,11 +66,12 @@ is string blockType = do ...@@ -65,11 +66,12 @@ is string blockType = do
eval string = do eval string = do
outputAccum <- newIORef [] outputAccum <- newIORef []
pagerAccum <- newIORef [] pagerAccum <- newIORef []
let publish evalResult = case evalResult of let publish evalResult =
IntermediateResult {} -> return () case evalResult of
FinalResult outs page [] -> do IntermediateResult{} -> return ()
modifyIORef outputAccum (outs :) FinalResult outs page [] -> do
modifyIORef pagerAccum (page :) modifyIORef outputAccum (outs :)
modifyIORef pagerAccum (page :)
noWidgetHandling s _ = return s noWidgetHandling s _ = return s
getTemporaryDirectory >>= setCurrentDirectory getTemporaryDirectory >>= setCurrentDirectory
...@@ -80,13 +82,13 @@ eval string = do ...@@ -80,13 +82,13 @@ eval string = do
return (reverse out, unlines . map extractPlain . reverse $ pagerOut) return (reverse out, unlines . map extractPlain . reverse $ pagerOut)
evaluationComparing comparison string = do evaluationComparing comparison string = do
let indent (' ':x) = 1 + indent x let indent (' ':x) = 1 + indent x
indent _ = 0 indent _ = 0
empty = null . strip empty = null . strip
stringLines = filter (not . empty) $ lines string stringLines = filter (not . empty) $ lines string
minIndent = minimum (map indent stringLines) minIndent = minimum (map indent stringLines)
newString = unlines $ map (drop minIndent) stringLines newString = unlines $ map (drop minIndent) stringLines
eval newString >>= comparison eval newString >>= comparison
becomes string expected = evaluationComparing comparison string becomes string expected = evaluationComparing comparison string
where where
...@@ -94,12 +96,11 @@ becomes string expected = evaluationComparing comparison string ...@@ -94,12 +96,11 @@ becomes string expected = evaluationComparing comparison string
comparison (results, pageOut) = do comparison (results, pageOut) = do
when (length results /= length expected) $ when (length results /= length expected) $
expectationFailure $ "Expected result to have " ++ show (length expected) expectationFailure $ "Expected result to have " ++ show (length expected)
++ " results. Got " ++ show results ++ " results. Got " ++ show results
forM_ (zip results expected) $ \(ManyDisplay [Display result], expected) -> forM_ (zip results expected) $ \(ManyDisplay [Display result], expected) -> case extractPlain result of
case extractPlain result of "" -> expectationFailure $ "No plain-text output in " ++ show result ++ "\nExpected: " ++ expected
"" -> expectationFailure $ "No plain-text output in " ++ show result ++ "\nExpected: " ++ expected str -> str `shouldBe` expected
str -> str `shouldBe` expected
pages string expected = evaluationComparing comparison string pages string expected = evaluationComparing comparison string
where where
...@@ -109,9 +110,10 @@ pages string expected = evaluationComparing comparison string ...@@ -109,9 +110,10 @@ pages string expected = evaluationComparing comparison string
-- A very, very hacky method for removing HTML -- A very, very hacky method for removing HTML
stripHtml str = go str stripHtml str = go str
where where
go ('<':str) = case stripPrefix "script" str of go ('<':str) =
Nothing -> go' str case stripPrefix "script" str of
Just str -> dropScriptTag str Nothing -> go' str
Just str -> dropScriptTag str
go (x:xs) = x : go xs go (x:xs) = x : go xs
go [] = [] go [] = []
...@@ -119,83 +121,94 @@ pages string expected = evaluationComparing comparison string ...@@ -119,83 +121,94 @@ pages string expected = evaluationComparing comparison string
go' (x:xs) = go' xs go' (x:xs) = go' xs
go' [] = error $ "Unending bracket html tag in string " ++ str go' [] = error $ "Unending bracket html tag in string " ++ str
dropScriptTag str = case stripPrefix "</script>" str of dropScriptTag str =
Just str -> go str case stripPrefix "</script>" str of
Nothing -> dropScriptTag $ tail str Just str -> go str
Nothing -> dropScriptTag $ tail str
readCompletePrompt :: String -> (String, Int) readCompletePrompt :: String -> (String, Int)
-- | @readCompletePrompt "xs*ys"@ return @(xs, i)@ where i is the location of -- | @readCompletePrompt "xs*ys"@ return @(xs, i)@ where i is the location of
-- @'*'@ in the input string. -- @'*'@ in the input string.
readCompletePrompt string = case elemIndex '*' string of readCompletePrompt string =
Nothing -> error "Expected cursor written as '*'." case elemIndex '*' string of
Just idx -> (replace "*" "" string, idx) Nothing -> error "Expected cursor written as '*'."
Just idx -> (replace "*" "" string, idx)
completes string expected = completionTarget newString cursorloc `shouldBe` expected completes string expected = completionTarget newString cursorloc `shouldBe` expected
where (newString, cursorloc) = readCompletePrompt string where
(newString, cursorloc) = readCompletePrompt string
completionEvent :: String -> Interpreter (String, [String]) completionEvent :: String -> Interpreter (String, [String])
completionEvent string = complete newString cursorloc completionEvent string = complete newString cursorloc
where (newString, cursorloc) = case elemIndex '*' string of where
Nothing -> error "Expected cursor written as '*'." (newString, cursorloc) =
Just idx -> (replace "*" "" string, idx) case elemIndex '*' string of
Nothing -> error "Expected cursor written as '*'."
Just idx -> (replace "*" "" string, idx)
completionEventInDirectory :: String -> IO (String, [String]) completionEventInDirectory :: String -> IO (String, [String])
completionEventInDirectory string completionEventInDirectory string = withHsDirectory $ const $ completionEvent string
= withHsDirectory $ const $ completionEvent string
shouldHaveCompletionsInDirectory :: String -> [String] -> IO () shouldHaveCompletionsInDirectory :: String -> [String] -> IO ()
shouldHaveCompletionsInDirectory string expected = do shouldHaveCompletionsInDirectory string expected = do
(matched, completions) <- completionEventInDirectory string (matched, completions) <- completionEventInDirectory string
let existsInCompletion = (`elem` completions) let existsInCompletion = (`elem` completions)
unmatched = filter (not . existsInCompletion) expected unmatched = filter (not . existsInCompletion) expected
expected `shouldBeAmong` completions expected `shouldBeAmong` completions
completionHas string expected completionHas string expected = do
= do (matched, completions) <- doGhc $ do initCompleter (matched, completions) <- doGhc $ do
completionEvent string initCompleter
let existsInCompletion = (`elem` completions) completionEvent string
unmatched = filter (not . existsInCompletion) expected let existsInCompletion = (`elem` completions)
expected `shouldBeAmong` completions unmatched = filter (not . existsInCompletion) expected
expected `shouldBeAmong` completions
initCompleter :: Interpreter () initCompleter :: Interpreter ()
initCompleter = do initCompleter = do
flags <- getSessionDynFlags flags <- getSessionDynFlags
setSessionDynFlags $ flags { hscTarget = HscInterpreted, ghcLink = LinkInMemory } setSessionDynFlags $ flags { hscTarget = HscInterpreted, ghcLink = LinkInMemory }
-- Import modules. -- Import modules.
imports <- mapM parseImportDecl ["import Prelude", imports <- mapM parseImportDecl
"import qualified Control.Monad", [ "import Prelude"
"import qualified Data.List as List", , "import qualified Control.Monad"
"import IHaskell.Display", , "import qualified Data.List as List"
"import Data.Maybe as Maybe"] , "import IHaskell.Display"
, "import Data.Maybe as Maybe"
]
setContext $ map IIDecl imports setContext $ map IIDecl imports
inDirectory :: [Shelly.FilePath] -- ^ directories relative to temporary directory inDirectory :: [Shelly.FilePath] -- ^ directories relative to temporary directory
-> [Shelly.FilePath] -- ^ files relative to temporary directory -> [Shelly.FilePath] -- ^ files relative to temporary directory
-> (Shelly.FilePath -> Interpreter a) -> (Shelly.FilePath -> Interpreter a)
-> IO a -> IO a
-- | Run an Interpreter action, but first make a temporary directory -- | Run an Interpreter action, but first make a temporary directory
-- with some files and folder and cd to it. -- with some files and folder and cd to it.
inDirectory dirs files action = shelly $ withTmpDir $ \dirPath -> inDirectory dirs files action = shelly $ withTmpDir $ \dirPath -> do
do cd dirPath cd dirPath
mapM_ mkdir_p dirs mapM_ mkdir_p dirs
mapM_ touchfile files mapM_ touchfile files
liftIO $ doGhc $ wrap (T.unpack $ toTextIgnore dirPath) (action dirPath) liftIO $ doGhc $ wrap (T.unpack $ toTextIgnore dirPath) (action dirPath)
where cdEvent path = liftIO $ setCurrentDirectory path --Eval.evaluate defaultKernelState (":! cd " ++ path) noPublish where
wrap :: FilePath -> Interpreter a -> Interpreter a cdEvent path = liftIO $ setCurrentDirectory path
wrap path action = wrap :: FilePath -> Interpreter a -> Interpreter a
do initCompleter wrap path action =
pwd <- Eval.liftIO getCurrentDirectory do
cdEvent path -- change to the temporary directory initCompleter
out <- action -- run action pwd <- Eval.liftIO getCurrentDirectory
cdEvent pwd -- change back to the original directory cdEvent path -- change to the temporary directory
return out out <- action -- run action
cdEvent pwd -- change back to the original directory
withHsDirectory :: (Shelly.FilePath -> Interpreter a) -> IO a return out
withHsDirectory :: (Shelly.FilePath -> Interpreter a) -> IO a
withHsDirectory = inDirectory [p "" </> p "dir", p "dir" </> p "dir1"] withHsDirectory = inDirectory [p "" </> p "dir", p "dir" </> p "dir1"]
[p ""</> p "file1.hs", p "dir" </> p "file2.hs", [ p "" </> p "file1.hs"
p "" </> p "file1.lhs", p "dir" </> p "file2.lhs"] , p "dir" </> p "file2.hs"
, p "" </> p "file1.lhs"
, p "dir" </> p "file2.lhs"
]
where where
p :: FilePath -> FilePath p :: FilePath -> FilePath
p = id p = id
...@@ -210,77 +223,89 @@ completionTests = do ...@@ -210,77 +223,89 @@ completionTests = do
parseShellTests parseShellTests
describe "Completion" $ do describe "Completion" $ do
it "correctly gets the completion identifier without dots" $ do it "correctly gets the completion identifier without dots" $ do
"hello*" `completes` ["hello"] "hello*" `completes` ["hello"]
"hello aa*bb goodbye" `completes` ["aa"] "hello aa*bb goodbye" `completes` ["aa"]
"hello aabb* goodbye" `completes` ["aabb"] "hello aabb* goodbye" `completes` ["aabb"]
"aacc* goodbye" `completes` ["aacc"] "aacc* goodbye" `completes` ["aacc"]
"hello *aabb goodbye" `completes` [] "hello *aabb goodbye" `completes` []
"*aabb goodbye" `completes` [] "*aabb goodbye" `completes` []
it "correctly gets the completion identifier with dots" $ do it "correctly gets the completion identifier with dots" $ do
"hello test.aa*bb goodbye" `completes` ["test", "aa"] "hello test.aa*bb goodbye" `completes` ["test", "aa"]
"Test.*" `completes` ["Test", ""] "Test.*" `completes` ["Test", ""]
"Test.Thing*" `completes` ["Test", "Thing"] "Test.Thing*" `completes` ["Test", "Thing"]
"Test.Thing.*" `completes` ["Test", "Thing", ""] "Test.Thing.*" `completes` ["Test", "Thing", ""]
"Test.Thing.*nope" `completes` ["Test", "Thing", ""] "Test.Thing.*nope" `completes` ["Test", "Thing", ""]
it "correctly gets the completion type" $ do it "correctly gets the completion type" $ do
completionType "import Data." 12 ["Data", ""] `shouldBe` ModuleName "Data" "" completionType "import Data." 12 ["Data", ""] `shouldBe` ModuleName "Data" ""
completionType "import Prel" 11 ["Prel"] `shouldBe` ModuleName "" "Prel" completionType "import Prel" 11 ["Prel"] `shouldBe` ModuleName "" "Prel"
completionType "import D.B.M" 12 ["D", "B", "M"] `shouldBe` ModuleName "D.B" "M" completionType "import D.B.M" 12 ["D", "B", "M"] `shouldBe` ModuleName "D.B" "M"
completionType " import A." 10 ["A", ""] `shouldBe` ModuleName "A" "" completionType " import A." 10 ["A", ""] `shouldBe` ModuleName "A" ""
completionType "import a.x" 10 ["a", "x"] `shouldBe` Identifier "x" completionType "import a.x" 10 ["a", "x"] `shouldBe` Identifier "x"
completionType "A.x" 3 ["A", "x"] `shouldBe` Qualified "A" "x" completionType "A.x" 3 ["A", "x"] `shouldBe` Qualified "A" "x"
completionType "a.x" 3 ["a", "x"] `shouldBe` Identifier "x" completionType "a.x" 3 ["a", "x"] `shouldBe` Identifier "x"
completionType "pri" 3 ["pri"] `shouldBe` Identifier "pri" completionType "pri" 3 ["pri"] `shouldBe` Identifier "pri"
completionType ":load A" 7 ["A"] `shouldBe` HsFilePath ":load A" completionType ":load A" 7 ["A"] `shouldBe` HsFilePath ":load A" "A"
"A" completionType ":! cd " 6 [""] `shouldBe` FilePath ":! cd " ""
completionType ":! cd " 6 [""] `shouldBe` FilePath ":! cd " ""
it "properly completes identifiers" $ do it "properly completes identifiers" $ do
"pri*" `completionHas` ["print"] "pri*" `completionHas` ["print"]
"ma*" `completionHas` ["map"] "ma*" `completionHas` ["map"]
"hello ma*" `completionHas` ["map"] "hello ma*" `completionHas` ["map"]
"print $ catMa*" `completionHas` ["catMaybes"] "print $ catMa*" `completionHas` ["catMaybes"]
it "properly completes qualified identifiers" $ do it "properly completes qualified identifiers" $ do
"Control.Monad.liftM*" `completionHas` [ "Control.Monad.liftM" "Control.Monad.liftM*" `completionHas` [ "Control.Monad.liftM"
, "Control.Monad.liftM2" , "Control.Monad.liftM2"
, "Control.Monad.liftM5"] , "Control.Monad.liftM5"
"print $ List.intercal*" `completionHas` ["List.intercalate"] ]
"print $ Data.Maybe.cat*" `completionHas` [] "print $ List.intercal*" `completionHas` ["List.intercalate"]
"print $ Maybe.catM*" `completionHas` ["Maybe.catMaybes"] "print $ Data.Maybe.cat*" `completionHas` []
"print $ Maybe.catM*" `completionHas` ["Maybe.catMaybes"]
it "properly completes imports" $ do it "properly completes imports" $ do
"import Data.*" `completionHas` ["Data.Maybe", "Data.List"] "import Data.*" `completionHas` ["Data.Maybe", "Data.List"]
"import Data.M*" `completionHas` ["Data.Maybe"] "import Data.M*" `completionHas` ["Data.Maybe"]
"import Prel*" `completionHas` ["Prelude"] "import Prel*" `completionHas` ["Prelude"]
it "properly completes haskell file paths on :load directive" $ it "properly completes haskell file paths on :load directive" $
let loading xs = ":load " ++ T.unpack (toTextIgnore xs) let loading xs = ":load " ++ T.unpack (toTextIgnore xs)
paths = map (T.unpack . toTextIgnore) paths = map (T.unpack . toTextIgnore)
in do in do
loading ("dir" </> "file*") `shouldHaveCompletionsInDirectory` paths ["dir" </> "file2.hs", loading ("dir" </> "file*") `shouldHaveCompletionsInDirectory` paths
"dir" </> "file2.lhs"] [ "dir" </> "file2.hs"
loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths ["" </> "file1.hs", , "dir" </> "file2.lhs"
"" </> "file1.lhs"] ]
loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths ["" </> "file1.hs", loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths
"" </> "file1.lhs"] [ "" </> "file1.hs"
loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths ["./" </> "dir/" , "" </> "file1.lhs"
, "./" </> "file1.hs" ]
, "./" </> "file1.lhs"] loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths
loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths ["./" </> "dir/" [ "" </> "file1.hs"
, "./" </> "file1.hs" , "" </> "file1.lhs"
, "./" </> "file1.lhs"] ]
loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths
[ "./" </> "dir/"
, "./" </> "file1.hs"
, "./" </> "file1.lhs"
]
loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths
[ "./" </> "dir/"
, "./" </> "file1.hs"
, "./" </> "file1.lhs"
]
it "provides path completions on empty shell cmds " $ it "provides path completions on empty shell cmds " $
":! cd *" `shouldHaveCompletionsInDirectory` map (T.unpack . toTextIgnore) ["" </> "dir/" ":! cd *" `shouldHaveCompletionsInDirectory` map (T.unpack . toTextIgnore)
, "" </> "file1.hs" [ "" </> "dir/"
, "" </> "file1.lhs"] , "" </> "file1.hs"
, "" </> "file1.lhs"
]
let withHsHome action = withHsDirectory $ \dirPath-> do let withHsHome action = withHsDirectory $ \dirPath -> do
home <- shelly $ Shelly.get_env_text "HOME" home <- shelly $ Shelly.get_env_text "HOME"
setHomeEvent dirPath setHomeEvent dirPath
result <- action result <- action
...@@ -288,39 +313,44 @@ completionTests = do ...@@ -288,39 +313,44 @@ completionTests = do
return result return result
setHomeEvent path = liftIO $ setEnv "HOME" (T.unpack $ toTextIgnore path) setHomeEvent path = liftIO $ setEnv "HOME" (T.unpack $ toTextIgnore path)
it "correctly interprets ~ as the environment HOME variable" $ it "correctly interprets ~ as the environment HOME variable" $
let shouldHaveCompletions :: String -> [String] -> IO () let shouldHaveCompletions :: String -> [String] -> IO ()
shouldHaveCompletions string expected = do shouldHaveCompletions string expected = do
(matched, completions) <- withHsHome $ completionEvent string (matched, completions) <- withHsHome $ completionEvent
let existsInCompletion = (`elem` completions) string
unmatched = filter (not . existsInCompletion) expected let existsInCompletion = (`elem` completions)
expected `shouldBeAmong` completions unmatched = filter
(not . existsInCompletion)
in do expected
":! cd ~/*" `shouldHaveCompletions` ["~/dir/"] expected `shouldBeAmong` completions
":! ~/*" `shouldHaveCompletions` ["~/dir/"] in do
":load ~/*" `shouldHaveCompletions` ["~/dir/"] ":! cd ~/*" `shouldHaveCompletions` ["~/dir/"]
":l ~/*" `shouldHaveCompletions` ["~/dir/"] ":! ~/*" `shouldHaveCompletions` ["~/dir/"]
":load ~/*" `shouldHaveCompletions` ["~/dir/"]
":l ~/*" `shouldHaveCompletions` ["~/dir/"]
let shouldHaveMatchingText :: String -> String -> IO () let shouldHaveMatchingText :: String -> String -> IO ()
shouldHaveMatchingText string expected = do shouldHaveMatchingText string expected = do
matchText <- withHsHome $ fst <$> uncurry complete (readCompletePrompt string) matchText <- withHsHome $ fst <$> uncurry complete (readCompletePrompt string)
matchText `shouldBe` expected matchText `shouldBe` expected
setHomeEvent path = liftIO $ setEnv "HOME" (T.unpack $ toTextIgnore path) setHomeEvent path = liftIO $ setEnv "HOME" (T.unpack $ toTextIgnore path)
it "generates the correct matchingText on `:! cd ~/*` " $ it "generates the correct matchingText on `:! cd ~/*` " $
do ":! cd ~/*" `shouldHaveMatchingText` ("~/" :: String) do
":! cd ~/*" `shouldHaveMatchingText` ("~/" :: String)
it "generates the correct matchingText on `:load ~/*` " $ it "generates the correct matchingText on `:load ~/*` " $
do ":load ~/*" `shouldHaveMatchingText` ("~/" :: String) do
":load ~/*" `shouldHaveMatchingText` ("~/" :: String)
it "generates the correct matchingText on `:l ~/*` " $ it "generates the correct matchingText on `:l ~/*` " $
do ":l ~/*" `shouldHaveMatchingText` ("~/" :: String) do
":l ~/*" `shouldHaveMatchingText` ("~/" :: String)
evalTests = do evalTests = do
describe "Code Evaluation" $ do describe "Code Evaluation" $ do
it "evaluates expressions" $ do it "evaluates expressions" $ do
"3" `becomes` ["3"] "3" `becomes` ["3"]
"3+5" `becomes` ["8"] "3+5" `becomes` ["8"]
"print 3" `becomes` ["3"] "print 3" `becomes` ["3"]
...@@ -330,11 +360,11 @@ evalTests = do ...@@ -330,11 +360,11 @@ evalTests = do
x+z x+z
|] `becomes` ["21"] |] `becomes` ["21"]
it "evaluates flags" $ do it "evaluates flags" $ do
":set -package hello" `becomes` ["Warning: -package not supported yet"] ":set -package hello" `becomes` ["Warning: -package not supported yet"]
":set -XNoImplicitPrelude" `becomes` [] ":set -XNoImplicitPrelude" `becomes` []
it "evaluates multiline expressions" $ do it "evaluates multiline expressions" $ do
[hereLit| [hereLit|
import Control.Monad import Control.Monad
forM_ [1, 2, 3] $ \x -> forM_ [1, 2, 3] $ \x ->
...@@ -380,7 +410,6 @@ evalTests = do ...@@ -380,7 +410,6 @@ evalTests = do
#else #else
":in String" `pages` ["type String = [Char] \t-- Defined in `GHC.Base'"] ":in String" `pages` ["type String = [Char] \t-- Defined in `GHC.Base'"]
#endif #endif
parserTests = do parserTests = do
layoutChunkerTests layoutChunkerTests
moduleNameTests moduleNameTests
...@@ -394,10 +423,10 @@ layoutChunkerTests = describe "Layout Chunk" $ do ...@@ -394,10 +423,10 @@ layoutChunkerTests = describe "Layout Chunk" $ do
map unloc (layoutChunks "a\n string") `shouldBe` ["a\n string"] map unloc (layoutChunks "a\n string") `shouldBe` ["a\n string"]
it "chunks 'a\\n string\\nextra'" $ it "chunks 'a\\n string\\nextra'" $
map unloc (layoutChunks "a\n string\nextra") `shouldBe` ["a\n string","extra"] map unloc (layoutChunks "a\n string\nextra") `shouldBe` ["a\n string", "extra"]
it "chunks strings with too many lines" $ it "chunks strings with too many lines" $
map unloc (layoutChunks "a\n\nstring") `shouldBe` ["a","string"] map unloc (layoutChunks "a\n\nstring") `shouldBe` ["a", "string"]
it "parses multiple exprs" $ do it "parses multiple exprs" $ do
let text = [hereLit| let text = [hereLit|
...@@ -408,11 +437,11 @@ layoutChunkerTests = describe "Layout Chunk" $ do ...@@ -408,11 +437,11 @@ layoutChunkerTests = describe "Layout Chunk" $ do
fourth fourth
|] |]
layoutChunks text `shouldBe` layoutChunks text `shouldBe` [ Located 2 "first"
[Located 2 "first", , Located 4 "second"
Located 4 "second", , Located 5 "third"
Located 5 "third", , Located 7 "fourth"
Located 7 "fourth"] ]
moduleNameTests = describe "Get Module Name" $ do moduleNameTests = describe "Get Module Name" $ do
it "parses simple module names" $ it "parses simple module names" $
...@@ -439,44 +468,25 @@ parseStringTests = describe "Parser" $ do ...@@ -439,44 +468,25 @@ parseStringTests = describe "Parser" $ do
"3 + 5" `is` Expression "3 + 5" `is` Expression
it "parses :type" $ it "parses :type" $
parses ":type x\n:ty x" `like` [ parses ":type x\n:ty x" `like` [Directive GetType "x", Directive GetType "x"]
Directive GetType "x",
Directive GetType "x"
]
it "parses :info" $ it "parses :info" $
parses ":info x\n:in x" `like` [ parses ":info x\n:in x" `like` [Directive GetInfo "x", Directive GetInfo "x"]
Directive GetInfo "x",
Directive GetInfo "x"
]
it "parses :help and :?" $ it "parses :help and :?" $
parses ":? x\n:help x" `like` [ parses ":? x\n:help x" `like` [Directive GetHelp "x", Directive GetHelp "x"]
Directive GetHelp "x",
Directive GetHelp "x"
]
it "parses :set x" $ it "parses :set x" $
parses ":set x" `like` [ parses ":set x" `like` [Directive SetDynFlag "x"]
Directive SetDynFlag "x"
]
it "parses :extension x" $ it "parses :extension x" $
parses ":ex x\n:extension x" `like` [ parses ":ex x\n:extension x" `like` [Directive SetExtension "x", Directive SetExtension "x"]
Directive SetExtension "x",
Directive SetExtension "x"
]
it "fails to parse :nope" $ it "fails to parse :nope" $
parses ":nope goodbye" `like` [ parses ":nope goodbye" `like` [ParseError (Loc 1 1) "Unknown directive: 'nope'."]
ParseError (Loc 1 1) "Unknown directive: 'nope'."
]
it "parses number followed by let stmt" $ it "parses number followed by let stmt" $
parses "3\nlet x = expr" `like` [ parses "3\nlet x = expr" `like` [Expression "3", Statement "let x = expr"]
Expression "3",
Statement "let x = expr"
]
it "parses let x in y" $ it "parses let x in y" $
"let x = 3 in x + 3" `is` Expression "let x = 3 in x + 3" `is` Expression
...@@ -485,41 +495,30 @@ parseStringTests = describe "Parser" $ do ...@@ -485,41 +495,30 @@ parseStringTests = describe "Parser" $ do
"data X = Y Int" `is` Declaration "data X = Y Int" `is` Declaration
it "parses number followed by type directive" $ it "parses number followed by type directive" $
parses "3\n:t expr" `like` [ parses "3\n:t expr" `like` [Expression "3", Directive GetType "expr"]
Expression "3",
Directive GetType "expr"
]
it "parses a <- statement" $ it "parses a <- statement" $
"y <- print 'no'" `is` Statement "y <- print 'no'" `is` Statement
it "parses a <- stmt followed by let stmt" $ it "parses a <- stmt followed by let stmt" $
parses "y <- do print 'no'\nlet x = expr" `like` [ parses "y <- do print 'no'\nlet x = expr" `like` [ Statement "y <- do print 'no'"
Statement "y <- do print 'no'", , Statement "let x = expr"
Statement "let x = expr" ]
]
it "parses <- followed by let followed by expr" $ it "parses <- followed by let followed by expr" $
parses "y <- do print 'no'\nlet x = expr\nexpression" `like` [ parses "y <- do print 'no'\nlet x = expr\nexpression" `like` [ Statement "y <- do print 'no'"
Statement "y <- do print 'no'", , Statement "let x = expr"
Statement "let x = expr", , Expression "expression"
Expression "expression" ]
]
it "parses two print statements" $ it "parses two print statements" $
parses "print yes\nprint no" `like` [ parses "print yes\nprint no" `like` [Expression "print yes", Expression "print no"]
Expression "print yes",
Expression "print no"
]
it "parses a pattern-maching function declaration" $ it "parses a pattern-maching function declaration" $
"fun [] = 10" `is` Declaration "fun [] = 10" `is` Declaration
it "parses a function decl followed by an expression" $ it "parses a function decl followed by an expression" $
parses "fun [] = 10\nprint 'h'" `like` [ parses "fun [] = 10\nprint 'h'" `like` [Declaration "fun [] = 10", Expression "print 'h'"]
Declaration "fun [] = 10",
Expression "print 'h'"
]
it "parses list pattern matching fun decl" $ it "parses list pattern matching fun decl" $
"fun (x : xs) = 100" `is` Declaration "fun (x : xs) = 100" `is` Declaration
...@@ -537,30 +536,16 @@ parseStringTests = describe "Parser" $ do ...@@ -537,30 +536,16 @@ parseStringTests = describe "Parser" $ do
"module B (x) where x = 3" `is` Module "module B (x) where x = 3" `is` Module
it "breaks when a let is incomplete" $ it "breaks when a let is incomplete" $
parses "let x = 3 in" `like` [ parses "let x = 3 in" `like` [ ParseError (Loc 1 13)
ParseError (Loc 1 13) "parse error (possibly incorrect indentation or mismatched brackets)" "parse error (possibly incorrect indentation or mismatched brackets)"
] ]
it "breaks without data kinds" $ it "breaks without data kinds" $
parses "data X = 3" `like` [ parses "data X = 3" `like` [dataKindsError]
#if MIN_VERSION_ghc(7, 10, 0)
ParseError (Loc 1 10) "Cannot parse data constructor in a data/newtype declaration: 3"
#elif MIN_VERSION_ghc(7, 8, 0)
ParseError (Loc 1 10) "Illegal literal in type (use DataKinds to enable): 3"
#else
ParseError (Loc 1 10) "Illegal literal in type (use -XDataKinds to enable): 3"
#endif
]
it "parses statements after imports" $ do it "parses statements after imports" $ do
parses "import X\nprint 3" `like` [ parses "import X\nprint 3" `like` [Import "import X", Expression "print 3"]
Import "import X", parses "import X\n\nprint 3" `like` [Import "import X", Expression "print 3"]
Expression "print 3"
]
parses "import X\n\nprint 3" `like` [
Import "import X",
Expression "print 3"
]
it "ignores blank lines properly" $ it "ignores blank lines properly" $
[hereLit| [hereLit|
test arg = hello test arg = hello
...@@ -582,40 +567,44 @@ parseStringTests = describe "Parser" $ do ...@@ -582,40 +567,44 @@ parseStringTests = describe "Parser" $ do
|] `is` Expression |] `is` Expression
it "correctly locates parsed items" $ do it "correctly locates parsed items" $ do
let go = doGhc . parseString let go = doGhc . parseString
go [hereLit| go
[hereLit|
first first
second second
|] >>= (`shouldBe` [Located 2 (Expression "first"), |] >>= (`shouldBe` [Located 2 (Expression "first"), Located 4 (Expression "second")])
Located 4 (Expression "second")]) where
dataKindsError = ParseError (Loc 1 10) msg
#if MIN_VERSION_ghc(7, 10, 0)
parseShellTests = msg = "Cannot parse data constructor in a data/newtype declaration: 3"
#elif MIN_VERSION_ghc(7, 8, 0)
msg = "Illegal literal in type (use DataKinds to enable): 3"
#else
msg = "Illegal literal in type (use -XDataKinds to enable): 3"
#endif
parseShellTests =
describe "Parsing Shell Commands" $ do describe "Parsing Shell Commands" $ do
test "A" ["A"] test "A" ["A"]
test ":load A" [":load", "A"] test ":load A" [":load", "A"]
test ":!l ~/Downloads/MyFile\\ Has\\ Spaces.txt" test ":!l ~/Downloads/MyFile\\ Has\\ Spaces.txt"
[":!l", "~/Downloads/MyFile\\ Has\\ Spaces.txt"] [":!l", "~/Downloads/MyFile\\ Has\\ Spaces.txt"]
test ":!l \"~/Downloads/MyFile Has Spaces.txt\" /Another/File\\ WithSpaces.doc" test ":!l \"~/Downloads/MyFile Has Spaces.txt\" /Another/File\\ WithSpaces.doc"
[":!l", "~/Downloads/MyFile Has Spaces.txt", "/Another/File\\ WithSpaces.doc" ] [":!l", "~/Downloads/MyFile Has Spaces.txt", "/Another/File\\ WithSpaces.doc"]
where where
test string expected = test string expected =
it ("parses " ++ string ++ " correctly") $ it ("parses " ++ string ++ " correctly") $
string `shouldParseTo` expected string `shouldParseTo` expected
shouldParseTo xs ys = fun ys (parseShell xs) shouldParseTo xs ys = fun ys (parseShell xs)
where fun ys (Right xs') = xs' `shouldBe` ys where
fun ys (Left e) = assertFailure $ "parseShell returned error: \n" ++ show e fun ys (Right xs') = xs' `shouldBe` ys
fun ys (Left e) = assertFailure $ "parseShell returned error: \n" ++ show e
-- Useful HSpec expectations ----
---------------------------------
-- Useful HSpec expectations ---- -------------------------------
shouldBeAmong :: (Show a, Eq a) => [a] -> [a] -> Expectation shouldBeAmong :: (Show a, Eq a) => [a] -> [a] -> Expectation
-- | -- |
-- @sublist \`shouldbeAmong\` list@ sets the expectation that @sublist@ elements are -- @sublist \`shouldbeAmong\` list@ sets the expectation that @sublist@ elements are
-- among those in @list@. -- among those in @list@.
sublist `shouldBeAmong` list = assertBool errorMsg sublist `shouldBeAmong` list = assertBool errorMsg $ and [x `elem` list | x <- sublist]
$ and [x `elem` list | x <- sublist]
where where
errorMsg = show list ++ " doesn't contain " ++ show sublist errorMsg = show list ++ " doesn't contain " ++ show sublist
...@@ -4,3 +4,5 @@ packages: ...@@ -4,3 +4,5 @@ packages:
- './ipython-kernel' - './ipython-kernel'
- './ghc-parser' - './ghc-parser'
resolver: lts-6.2 resolver: lts-6.2
extra-deps:
- system-argv0-0.1.1 # Necessary for LTS 2.22 (GHC 7.8)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment