@@ -27,7 +27,7 @@ import qualified Language.PureScript as P
28import Data.Char (isSpace)
28import Data.Char (isSpace)
29import Data.Function (on)
29import Data.Function (on)
30−import Data.List (sort, stripPrefix, intercalate, groupBy, sortBy)
30+import Data.List (sort, stripPrefix, intercalate, groupBy, sortBy, minimumBy)
31import Data.Maybe (mapMaybe)
31import Data.Maybe (mapMaybe)
32import Data.Time.Clock (UTCTime())
32import Data.Time.Clock (UTCTime())
33import Data.Tuple (swap)
33import Data.Tuple (swap)
@@ -58,31 +58,40 @@ main = hspec spec
58spec :: Spec
58spec :: Spec
61− (supportExterns, passingTestCases, failingTestCases) <- runIO $ do
61+ (supportExterns, passingTestCases, warningTestCases, failingTestCases) <- runIO $ do
62 cwd <- getCurrentDirectory
62 cwd <- getCurrentDirectory
63 let passing = cwd </> "examples" </> "passing"
63 let passing = cwd </> "examples" </> "passing"
64+ let warning = cwd </> "examples" </> "warning"
64 let failing = cwd </> "examples" </> "failing"
65 let failing = cwd </> "examples" </> "failing"
65 let supportDir = cwd </> "tests" </> "support" </> "bower_components"
66 let supportDir = cwd </> "tests" </> "support" </> "bower_components"
66 let supportFiles ext = Glob.globDir1 (Glob.compile ("purescript-*/**/*." ++ ext)) supportDir
67 let supportFiles ext = Glob.globDir1 (Glob.compile ("purescript-*/**/*." ++ ext)) supportDir
67 passingFiles <- getTestFiles passing <$> testGlob passing
68 passingFiles <- getTestFiles passing <$> testGlob passing
69+ warningFiles <- getTestFiles warning <$> testGlob warning
68 failingFiles <- getTestFiles failing <$> testGlob failing
70 failingFiles <- getTestFiles failing <$> testGlob failing
69 supportPurs <- supportFiles "purs"
71 supportPurs <- supportFiles "purs"
70 supportPursFiles <- readInput supportPurs
72 supportPursFiles <- readInput supportPurs
71 supportExterns <- runExceptT $ do
73 supportExterns <- runExceptT $ do
72 modules <- ExceptT . return $ P.parseModulesFromFiles id supportPursFiles
74 modules <- ExceptT . return $ P.parseModulesFromFiles id supportPursFiles
73 foreigns <- inferForeignModules modules
75 foreigns <- inferForeignModules modules
74− externs <- ExceptT . runTest $ P.make (makeActions foreigns) (map snd modules)
76+ externs <- ExceptT . fmap fst . runTest $ P.make (makeActions foreigns) (map snd modules)
75 return (zip (map snd modules) externs)
77 return (zip (map snd modules) externs)
76 case supportExterns of
78 case supportExterns of
77 Left errs -> fail (P.prettyPrintMultipleErrors False errs)
79 Left errs -> fail (P.prettyPrintMultipleErrors False errs)
78− Right externs -> return (externs, passingFiles, failingFiles)
80+ Right externs -> return (externs, passingFiles, warningFiles, failingFiles)
80− context ("Passing examples") $ do
82+ context "Passing examples" $
81 forM_ passingTestCases $ \testPurs ->
83 forM_ passingTestCases $ \testPurs ->
82 it ("'" <> takeFileName (getTestMain testPurs) <> "' should compile and run without error") $
84 it ("'" <> takeFileName (getTestMain testPurs) <> "' should compile and run without error") $
83 assertCompiles supportExterns testPurs
85 assertCompiles supportExterns testPurs
87+ context "Warning examples" $
88+ forM_ warningTestCases $ \testPurs -> do
89+ let mainPath = getTestMain testPurs
90+ expectedWarnings <- runIO $ getShouldWarnWith mainPath
91+ it ("'" <> takeFileName mainPath <> "' should compile with warning(s) '" <> intercalate "', '" expectedWarnings <> "'") $
92+ assertCompilesWithWarnings supportExterns testPurs expectedWarnings
85− context ("Failing examples") $ do
94+ context "Failing examples" $
86 forM_ failingTestCases $ \testPurs -> do
95 forM_ failingTestCases $ \testPurs -> do
87 let mainPath = getTestMain testPurs
96 let mainPath = getTestMain testPurs
88 expectedFailures <- runIO $ getShouldFailWith mainPath
97 expectedFailures <- runIO $ getShouldFailWith mainPath
@@ -100,8 +109,7 @@ spec = do
100 -- .purs files and the .js files for the test case.
109 -- .purs files and the .js files for the test case.
101 getTestFiles :: FilePath -> [FilePath] -> [[FilePath]]
110 getTestFiles :: FilePath -> [FilePath] -> [[FilePath]]
102 getTestFiles baseDir
111 getTestFiles baseDir
103− = map (filter ((== ".purs") . takeExtensions))
112+ = map (filter ((== ".purs") . takeExtensions) . map (baseDir </>))
104− . map (map (baseDir </>))
105 . groupBy ((==) `on` extractPrefix)
113 . groupBy ((==) `on` extractPrefix)
106 . sortBy (compare `on` extractPrefix)
114 . sortBy (compare `on` extractPrefix)
107 . map (makeRelative baseDir)
115 . map (makeRelative baseDir)
@@ -110,7 +118,7 @@ spec = do
110 -- by the file with the shortest path name, as everything but the main file
118 -- by the file with the shortest path name, as everything but the main file
111 -- will be under a subdirectory.
119 -- will be under a subdirectory.
112 getTestMain :: [FilePath] -> FilePath
120 getTestMain :: [FilePath] -> FilePath
113− getTestMain = head . sortBy (compare `on` length)
121+ getTestMain = minimumBy (compare `on` length)
115 -- Extracts the filename part of a .purs file, or if the file is in a
123 -- Extracts the filename part of a .purs file, or if the file is in a
116 -- subdirectory, the first part of that directory path.
124 -- subdirectory, the first part of that directory path.
@@ -125,9 +133,17 @@ spec = do
125 -- Scans a file for @shouldFailWith directives in the comments, used to
133 -- Scans a file for @shouldFailWith directives in the comments, used to
126 -- determine expected failures
134 -- determine expected failures
127 getShouldFailWith :: FilePath -> IO [String]
135 getShouldFailWith :: FilePath -> IO [String]
128− getShouldFailWith = fmap extractFailWiths . readUTF8File
136+ getShouldFailWith = extractPragma "shouldFailWith"
138+ -- Scans a file for @shouldWarnWith directives in the comments, used to
139+ -- determine expected warnings
140+ getShouldWarnWith :: FilePath -> IO [String]
141+ getShouldWarnWith = extractPragma "shouldWarnWith"
143+ extractPragma :: String -> FilePath -> IO [String]
144+ extractPragma pragma = fmap go . readUTF8File
130− extractFailWiths = lines >>> mapMaybe (stripPrefix "-- @shouldFailWith ") >>> map trim
146+ go = lines >>> mapMaybe (stripPrefix ("-- @" ++ pragma ++ " ")) >>> map trim
132inferForeignModules
148inferForeignModules
133 :: MonadIO m
149 :: MonadIO m
@@ -168,16 +184,14 @@ readInput inputFiles = forM inputFiles $ \inputFile -> do
168 text <- readUTF8File inputFile
184 text <- readUTF8File inputFile
169 return (inputFile, text)
185 return (inputFile, text)
171−type TestM = WriterT [(FilePath, String)] IO
173−runTest :: P.Make a -> IO (Either P.MultipleErrors a)
187+runTest :: P.Make a -> IO (Either P.MultipleErrors a, P.MultipleErrors)
174−runTest = fmap fst . P.runMake P.defaultOptions
188+runTest = P.runMake P.defaultOptions
177 :: [(P.Module, P.ExternsFile)]
191 :: [(P.Module, P.ExternsFile)]
178 -> [FilePath]
192 -> [FilePath]
179 -> ([P.Module] -> IO ())
193 -> ([P.Module] -> IO ())
180− -> IO (Either P.MultipleErrors [P.ExternsFile])
194+ -> IO (Either P.MultipleErrors [P.ExternsFile], P.MultipleErrors)
181compile supportExterns inputFiles check = silence $ runTest $ do
195compile supportExterns inputFiles check = silence $ runTest $ do
182 fs <- liftIO $ readInput inputFiles
196 fs <- liftIO $ readInput inputFiles
183 ms <- P.parseModulesFromFiles id fs
197 ms <- P.parseModulesFromFiles id fs
@@ -192,18 +206,30 @@ assert
192 :: [(P.Module, P.ExternsFile)]
206 :: [(P.Module, P.ExternsFile)]
193 -> [FilePath]
207 -> [FilePath]
194 -> ([P.Module] -> IO ())
208 -> ([P.Module] -> IO ())
195− -> (Either P.MultipleErrors [P.ExternsFile] -> IO (Maybe String))
209+ -> (Either P.MultipleErrors P.MultipleErrors -> IO (Maybe String))
196 -> Expectation
210 -> Expectation
197assert supportExterns inputFiles check f = do
211assert supportExterns inputFiles check f = do
198− e <- compile supportExterns inputFiles check
212+ (e, w) <- compile supportExterns inputFiles check
199− maybeErr <- f e
213+ maybeErr <- f (const w <$> e)
200 maybe (return ()) expectationFailure maybeErr
214 maybe (return ()) expectationFailure maybeErr
216+checkMain :: [P.Module] -> IO ()
218+ unless (any ((== P.moduleNameFromString "Main") . P.getModuleName) ms)
219+ (fail "Main module missing")
221+checkShouldFailWith :: [String] -> P.MultipleErrors -> Maybe String
222+checkShouldFailWith expected errs =
223+ let actual = map P.errorCode $ P.runMultipleErrors errs
224+ in if sort expected == sort actual
226+ else Just $ "Expected these errors: " ++ show expected ++ ", but got these: " ++ show actual
202assertCompiles
228assertCompiles
203 :: [(P.Module, P.ExternsFile)]
229 :: [(P.Module, P.ExternsFile)]
204 -> [FilePath]
230 -> [FilePath]
205 -> Expectation
231 -> Expectation
206−assertCompiles supportExterns inputFiles = do
232+assertCompiles supportExterns inputFiles =
207 assert supportExterns inputFiles checkMain $ \e ->
233 assert supportExterns inputFiles checkMain $ \e ->
208 case e of
234 case e of
209 Left errs -> return . Just . P.prettyPrintMultipleErrors False $ errs
235 Left errs -> return . Just . P.prettyPrintMultipleErrors False $ errs
@@ -219,17 +245,32 @@ assertCompiles supportExterns inputFiles = do
219 | otherwise -> return $ Just $ "Test did not finish with 'Done':\n\n" <> out
245 | otherwise -> return $ Just $ "Test did not finish with 'Done':\n\n" <> out
220 Just (ExitFailure _, _, err) -> return $ Just err
246 Just (ExitFailure _, _, err) -> return $ Just err
221 Nothing -> return $ Just "Couldn't find node.js executable"
247 Nothing -> return $ Just "Couldn't find node.js executable"
249+assertCompilesWithWarnings
250+ :: [(P.Module, P.ExternsFile)]
254+assertCompilesWithWarnings supportExterns inputFiles shouldWarnWith =
255+ assert supportExterns inputFiles checkMain $ \e ->
258+ return . Just . P.prettyPrintMultipleErrors False $ errs
261+ . fmap (printAllWarnings warnings)
262+ $ checkShouldFailWith shouldWarnWith warnings
223− checkMain ms =
265+ printAllWarnings warnings =
224− unless (any ((== P.moduleNameFromString "Main") . P.getModuleName) ms)
266+ (<> "\n\n" <> P.prettyPrintMultipleErrors False warnings)
225− (fail "Main module missing")
227assertDoesNotCompile
268assertDoesNotCompile
228 :: [(P.Module, P.ExternsFile)]
269 :: [(P.Module, P.ExternsFile)]
229 -> [FilePath]
270 -> [FilePath]
230 -> [String]
271 -> [String]
231 -> Expectation
272 -> Expectation
232−assertDoesNotCompile supportExterns inputFiles shouldFailWith = do
273+assertDoesNotCompile supportExterns inputFiles shouldFailWith =
233 assert supportExterns inputFiles noPreCheck $ \e ->
274 assert supportExterns inputFiles noPreCheck $ \e ->
234 case e of
275 case e of
235 Left errs ->
276 Left errs ->
@@ -243,9 +284,3 @@ assertDoesNotCompile supportExterns inputFiles shouldFailWith = do
245 noPreCheck = const (return ())
286 noPreCheck = const (return ())
247− checkShouldFailWith expected errs =
248− let actual = map P.errorCode $ P.runMultipleErrors errs
249− in if sort expected == sort actual
251− else Just $ "Expected these errors: " ++ show expected ++ ", but got these: " ++ show actual