2{-# LANGUAGE ScopedTypeVariables #-}
3module Test.Compiler (compilerTests) where
5import Control.Exception (try, IOException)
7import qualified Data.Map as Map
8import Data.Maybe (isJust)
10import System.Directory (createDirectoryIfMissing)
11import System.Environment (lookupEnv)
12import System.FilePath ((</>), (<.>), dropFileName, joinPath, splitDirectories)
13import System.FilePath.Find (find, (==?), extension)
16import Test.Framework.Providers.HUnit (testCase)
17import Test.HUnit (Assertion, assertFailure, assertBool, assertEqual)
19import qualified Elm.Compiler as Compiler
20import qualified Elm.Package as Package
21import qualified AST.Module as Module
22import qualified AST.Module.Name as ModuleName
23import qualified AST.Variable as Variable
24import qualified AST.Type as Type
27writeExpectedJsEnvVarName :: String
28writeExpectedJsEnvVarName =
29 "ELM_WRITE_NEW_EXPECTED"
35 do goods <- testIf isSuccess =<< getElms "good"
36 bads <- testIf isFailure =<< getElms "bad"
37 expectedJsTests <- testMatchesExpected "good" "good-expected-js"
39 testGroup "Compile Tests"
40 [ testGroup "Good Tests" goods
41 , testGroup "Bad Tests" bads
42 , testGroup "Expected JS Tests" expectedJsTests
50getElms :: FilePath -> IO [FilePath]
54 (extension ==? ".elm")
55 (testsDir </> filePath)
60 "tests" </> "test-files"
62convertToExpectedJsFilePath :: String -> FilePath -> FilePath
63convertToExpectedJsFilePath expectedJsDir filePath =
66 </> joinPath (drop 3 (splitDirectories filePath))
74essentialInterfaces :: Module.Interfaces
78 (ModuleName.inCore ["Debug"],
81 ("crash", Type.Lambda (Type.Var "String") (Type.Var "a"))
87buildCoreInterface :: [(String, Type.Canonical)] -> Module.Interface
88buildCoreInterface members =
90 exports = map (Variable.Value . fst) members
92 types = Map.fromList members
97 Module.Interface Compiler.version Package.coreName exports types imports adts aliases fixities
100compileString :: FilePath -> String -> Either String Compiler.Result
101compileString filePath source =
102 let dependentModuleNames =
103 Map.keys essentialInterfaces
105 Compiler.Context Package.coreName False dependentModuleNames
106 (dealiaser, _warnings, result) =
107 Compiler.compile context source essentialInterfaces
108 formatErrors errors =
109 concatMap (Compiler.errorToString dealiaser filePath source) errors
111 either (Left . formatErrors) Right result
114readFileOrErrorStr :: FilePath -> IO String
115readFileOrErrorStr filePath =
116 do strOrExc <- try $ readFile filePath
121 Left (e :: IOException) ->
130 :: (Either String Compiler.Result -> Assertion)
133testIf handleResult filePaths =
134 let doTest filePath =
135 do source <- readFile filePath
136 let formattedResult = compileString filePath source
137 return $ testCase filePath (handleResult formattedResult)
139 traverse doTest filePaths
142doMatchesExpectedTest :: String -> FilePath -> IO Test
143doMatchesExpectedTest expectedJsDir filePath =
144 do source <- readFile filePath
145 expectedJs <- readFileOrErrorStr (convertToExpectedJsFilePath expectedJsDir filePath)
147 let formattedResult = compileString filePath source
148 let assertion = matchesExpected expectedJs formattedResult
150 return $ testCase filePath assertion
153doWriteNewExpectedTest :: String -> FilePath -> IO Test
154doWriteNewExpectedTest expectedJsDir filePath =
155 do source <- readFile filePath
156 let formattedResult = compileString filePath source
157 let expectedFilePath = convertToExpectedJsFilePath expectedJsDir filePath
159 case formattedResult of
160 Right (Compiler.Result _ _ js) ->
161 do createDirectoryIfMissing True (dropFileName expectedFilePath)
162 -- Force the evaluation of `js` before `writeFile`
163 -- so that if an error is raised we do not unintentionally write an empty file.
164 -- NB: `js` can be an empty string in the case of NoExpressions.elm
165 assertBool "" (length js >= 0)
166 writeFile expectedFilePath js
167 assertFailure ("Wrote new expected js: " ++ expectedFilePath)
170 assertFailure "Compile failed. Could not write new expected js."
171 return $ testCase filePath assertion
174testMatchesExpected :: String -> String -> IO [Test]
175testMatchesExpected elmDir expectedJsDir =
176 do elmFiles <- getElms elmDir
177 envVar <- lookupEnv writeExpectedJsEnvVarName
180 traverse (doWriteNewExpectedTest expectedJsDir) elmFiles
182 traverse (doMatchesExpectedTest expectedJsDir) elmFiles
189isSuccess :: Either String a -> Assertion
195 Left errorMessages ->
196 assertFailure errorMessages
199isFailure :: Either a b -> Assertion
203 assertFailure "Compilation succeeded but should have failed"
209matchesExpected :: String -> Either String Compiler.Result -> Assertion
210matchesExpected expectedJs result =
212 Right (Compiler.Result _ _ js) ->
213 assertEqual matchFailureMessage expectedJs js
215 Left errorMessages ->
216 assertFailure $ "Compile failed:\n" ++ errorMessages
219matchFailureMessage :: String
221 "Compiled JS did not match expected JS." ++
222 "\n If the change is intentional, rerun " ++
223 "the tests with environment variable: " ++ writeExpectedJsEnvVarName ++ "=1"
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