1-----------------------------------------------------------------------------
4-- License : MIT (http://opensource.org/licenses/MIT)
6-- Maintainer : Phil Freeman <paf31@cantab.net>
7-- Stability : experimental
12-----------------------------------------------------------------------------
14{-# LANGUAGE DataKinds #-}
15{-# LANGUAGE DoAndIfThenElse #-}
16{-# LANGUAGE TupleSections #-}
17{-# LANGUAGE GeneralizedNewtypeDeriving #-}
18{-# LANGUAGE FlexibleInstances #-}
21-- Failing tests can specify the kind of error that should be thrown with a
22-- @shouldFailWith declaration. For example:
24-- "-- @shouldFailWith TypesDoNotUnify"
26-- will cause the test to fail unless that module fails to compile with exactly
27-- one TypesDoNotUnify error.
29-- If a module is expected to produce multiple type errors, then use multiple
30-- @shouldFailWith lines; for example:
32-- -- @shouldFailWith TypesDoNotUnify
33-- -- @shouldFailWith TypesDoNotUnify
34-- -- @shouldFailWith TransitiveExportError
36module Main (main) where
38import qualified Language.PureScript as P
39import qualified Language.PureScript.CodeGen.JS as J
40import qualified Language.PureScript.CoreFn as CF
42import Data.Char (isSpace)
43import Data.Maybe (mapMaybe, fromMaybe)
44import Data.List (isSuffixOf, sort, stripPrefix)
45#if __GLASGOW_HASKELL__ < 710
46import Data.Traversable (traverse)
48import Data.Time.Clock (UTCTime())
50import qualified Data.Map as M
53import Control.Monad.IO.Class (liftIO)
54#if __GLASGOW_HASKELL__ < 710
55import Control.Applicative
57import Control.Arrow ((>>>))
59import Control.Monad.Reader
60import Control.Monad.Writer.Strict
61import Control.Monad.Trans.Maybe
62import Control.Monad.Trans.Except
63import Control.Monad.Error.Class
68import System.Directory
70import qualified System.Info
71import qualified System.FilePath.Glob as Glob
73import Text.Parsec (ParseError)
78modulesDir = ".test_modules" </> "node_modules"
80makeActions :: M.Map P.ModuleName FilePath -> P.MakeActions P.Make
81makeActions foreigns = (P.buildMakeActions modulesDir (error "makeActions: input file map was read.") foreigns False)
82 { P.getInputTimestamp = getInputTimestamp
83 , P.getOutputTimestamp = getOutputTimestamp
86 getInputTimestamp :: P.ModuleName -> P.Make (Either P.RebuildPolicy (Maybe UTCTime))
88 | isSupportModule (P.runModuleName mn) = return (Left P.RebuildNever)
89 | otherwise = return (Left P.RebuildAlways)
91 isSupportModule = flip elem supportModules
93 getOutputTimestamp :: P.ModuleName -> P.Make (Maybe UTCTime)
94 getOutputTimestamp mn = do
95 let filePath = modulesDir </> P.runModuleName mn
96 exists <- liftIO $ doesDirectoryExist filePath
97 return (if exists then Just (error "getOutputTimestamp: read timestamp") else Nothing)
99readInput :: [FilePath] -> IO [(FilePath, String)]
100readInput inputFiles = forM inputFiles $ \inputFile -> do
101 text <- readUTF8File inputFile
102 return (inputFile, text)
104type TestM = WriterT [(FilePath, String)] IO
106runTest :: P.Make a -> IO (Either P.MultipleErrors a)
107runTest = fmap (fmap fst) . P.runMake P.defaultOptions
109compile :: [FilePath] -> M.Map P.ModuleName FilePath -> IO (Either P.MultipleErrors P.Environment)
110compile inputFiles foreigns = runTest $ do
111 fs <- liftIO $ readInput inputFiles
112 ms <- P.parseModulesFromFiles id fs
113 P.make (makeActions foreigns) (map snd ms)
115assert :: [FilePath] ->
116 M.Map P.ModuleName FilePath ->
117 (Either P.MultipleErrors P.Environment -> IO (Maybe String)) ->
119assert inputFiles foreigns f = do
120 e <- liftIO $ compile inputFiles foreigns
121 maybeErr <- liftIO $ f e
123 Just err -> tell [(last inputFiles, err)]
126assertCompiles :: [FilePath] -> M.Map P.ModuleName FilePath -> TestM ()
127assertCompiles inputFiles foreigns = do
128 liftIO . putStrLn $ "Assert " ++ last inputFiles ++ " compiles successfully"
129 assert inputFiles foreigns $ \e ->
131 Left errs -> return . Just . P.prettyPrintMultipleErrors False $ errs
133 process <- findNodeProcess
134 let entryPoint = modulesDir </> "index.js"
135 writeFile entryPoint "require('Main').main()"
136 result <- traverse (\node -> readProcessWithExitCode node [entryPoint] "") process
138 Just (ExitSuccess, out, _) -> putStrLn out >> return Nothing
139 Just (ExitFailure _, _, err) -> return $ Just err
140 Nothing -> return $ Just "Couldn't find node.js executable"
142assertDoesNotCompile :: [FilePath] -> M.Map P.ModuleName FilePath -> TestM ()
143assertDoesNotCompile inputFiles foreigns = do
144 let testFile = last inputFiles
145 liftIO . putStrLn $ "Assert " ++ testFile ++ " does not compile"
146 shouldFailWith <- getShouldFailWith testFile
147 assert inputFiles foreigns $ \e ->
150 putStrLn (P.prettyPrintMultipleErrors False errs)
151 return $ if null shouldFailWith
152 then Just $ "shouldFailWith declaration is missing (errors were: "
153 ++ show (map P.errorCode (P.runMultipleErrors errs))
155 else checkShouldFailWith shouldFailWith errs
157 return $ Just "Should not have compiled"
164 >>> mapMaybe (stripPrefix "-- @shouldFailWith ")
168 checkShouldFailWith expected errs =
169 let actual = map P.errorCode $ P.runMultipleErrors errs
170 in if sort expected == sort actual
172 else Just $ "Expected these errors: " ++ show expected ++ ", but got these: " ++ show actual
175 dropWhile isSpace >>> reverse >>> dropWhile isSpace >>> reverse
180 cwd <- getCurrentDirectory
182 let supportDir = cwd </> "tests" </> "support" </> "flattened"
183 let supportFiles ext = Glob.globDir1 (Glob.compile ("*." ++ ext)) supportDir
185 supportPurs <- supportFiles "purs"
186 supportJS <- supportFiles "js"
188 foreignFiles <- forM supportJS (\f -> (f,) <$> readUTF8File f)
189 Right (foreigns, _) <- runExceptT $ runWriterT $ P.parseForeignModulesFromFiles foreignFiles
191 let passing = cwd </> "examples" </> "passing"
192 passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passing
193 let failing = cwd </> "examples" </> "failing"
194 failingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents failing
196 failures <- execWriterT $ do
197 forM_ passingTestCases $ \inputFile ->
198 assertCompiles (supportPurs ++ [passing </> inputFile]) foreigns
199 forM_ failingTestCases $ \inputFile ->
200 assertDoesNotCompile (supportPurs ++ [failing </> inputFile]) foreigns
206 forM_ failures $ \(fp, err) ->
207 let fp' = fromMaybe fp $ stripPrefix (failing ++ [pathSeparator]) fp
208 in putStrLn $ fp' ++ ": " ++ err
211supportModules :: [String]
213 [ "Control.Monad.Eff.Class"
214 , "Control.Monad.Eff.Console"
215 , "Control.Monad.Eff"
216 , "Control.Monad.Eff.Unsafe"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list