Tests.hs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. {-# LANGUAGE DoAndIfThenElse #-}
  2. module LaTeXAndHTML.Tests where
  3. import Control.Monad
  4. import Data.Char
  5. import qualified Data.List as List
  6. import Data.Maybe
  7. import Data.Text.Encoding
  8. import qualified Data.ByteString as BS
  9. import qualified Data.Text as T
  10. import qualified Network.URI.Encode
  11. import System.Directory
  12. import System.Exit
  13. import System.FilePath
  14. import System.IO.Temp
  15. import System.Process
  16. import qualified System.Process.Text as PT
  17. import qualified System.Process.ByteString as PB
  18. import Text.Read (readMaybe)
  19. import Test.Tasty
  20. import Test.Tasty.Silver
  21. import Test.Tasty.Silver.Advanced (readFileMaybe)
  22. import Test.Tasty.Silver.Filter
  23. import UserManual.Tests (examplesInUserManual)
  24. import Utils
  25. import Agda.Utils.Three
  26. type LaTeXProg = String
  27. allLaTeXProgs :: [LaTeXProg]
  28. allLaTeXProgs = ["pdflatex", "xelatex", "lualatex"]
  29. testDirPrefix :: FilePath -> FilePath
  30. testDirPrefix x = "test" </> "LaTeXAndHTML" </> x
  31. -- Andreas, 2021-01-30
  32. -- See issue #5140 for the split into these two directories.
  33. testDirs :: [FilePath]
  34. testDirs = map testDirPrefix
  35. [ "fail"
  36. , "succeed"
  37. ]
  38. userManualTestDir :: FilePath
  39. userManualTestDir = testDirPrefix "user-manual"
  40. disabledTests :: [RegexFilter]
  41. disabledTests = []
  42. -- | Filtering out tests using Text.ICU.
  43. icuTests :: [RegexFilter]
  44. icuTests = [ disable "LaTeXAndHTML/.*/Grapheme.*" ]
  45. where disable = RFInclude
  46. -- | Filtering out tests using latex.
  47. latexTests :: [RegexFilter]
  48. latexTests = [ disable "LaTeXAndHTML/.*LaTeX/.*" ]
  49. where disable = RFInclude
  50. -- | Test group with subgroups
  51. --
  52. -- @
  53. -- "LaTeXAndHTML" / [ "HTML" , "LaTeX" , "QuickLaTeX" ]
  54. -- @.
  55. --
  56. tests :: IO TestTree
  57. tests = do
  58. agdaBin <- getAgdaBin
  59. suiteTests <- concat <$> mapM (taggedListOfAllTests agdaBin) testDirs
  60. let allTests = suiteTests ++ userManualTests agdaBin
  61. let (html, latex, quicklatex) = (\ f -> partition3 (f . fst) allTests) $ \case
  62. HTML -> One
  63. LaTeX -> Two
  64. QuickLaTeX -> Three
  65. return $
  66. testGroup "LaTeXAndHTML"
  67. [ testGroup "HTML" $ map snd html
  68. , testGroup "LaTeX" $ map snd latex
  69. , testGroup "QuickLaTeX" $ map snd quicklatex
  70. ]
  71. taggedListOfAllTests :: FilePath -> FilePath -> IO [(Kind, TestTree)]
  72. taggedListOfAllTests agdaBin testDir = do
  73. inpFiles <- getAgdaFilesInDir NonRec testDir
  74. return $
  75. [ (k, mkLaTeXOrHTMLTest k False agdaBin testDir f)
  76. | f <- inpFiles
  77. -- Note that the LaTeX backends are only tested on the @.lagda@
  78. -- and @.lagda.tex@ files.
  79. , k <- HTML : concat [ [ LaTeX, QuickLaTeX ]
  80. | any (`List.isSuffixOf` takeExtensions f)
  81. [".lagda",".lagda.tex"]
  82. ]
  83. ]
  84. -- See issue #3372 for the origin of these tests.
  85. -- | These tests do not have a @.lagda[.tex]@ source.
  86. userManualTests :: FilePath -> [(Kind, TestTree)]
  87. userManualTests agdaBin =
  88. [ (k, mkLaTeXOrHTMLTest k True agdaBin userManualTestDir f)
  89. | f <- examplesInUserManual
  90. , k <- [LaTeX, QuickLaTeX]
  91. ]
  92. data LaTeXResult
  93. = AgdaFailed ProgramResult
  94. | LaTeXFailed [LaTeXProg]
  95. | Success T.Text -- ^ The resulting LaTeX or HTML file.
  96. data Kind = LaTeX | QuickLaTeX | HTML
  97. deriving Show
  98. -- The test output may not be very informative for failing tests. One
  99. -- can perhaps improve the user experience by switching from
  100. -- goldenVsAction to something else (see test/Fail/Tests.hs for one
  101. -- example).
  102. mkLaTeXOrHTMLTest
  103. :: Kind
  104. -> Bool -- ^ Should the file be copied to the temporary test
  105. -- directory before the test is run?
  106. -> FilePath -- ^ Agda binary.
  107. -> FilePath -- ^ Test directory in which input file resides.
  108. -> FilePath -- ^ Input file.
  109. -> TestTree
  110. mkLaTeXOrHTMLTest k copy agdaBin testDir inp =
  111. goldenVsAction'
  112. testName
  113. goldenFile
  114. (liftM2 (,) getCurrentDirectory doRun)
  115. (uncurry printLaTeXResult)
  116. where
  117. extension = case k of
  118. LaTeX -> "tex"
  119. QuickLaTeX -> "quick.tex"
  120. HTML -> if "MdHighlight" `List.isPrefixOf` inFileName
  121. then "md"
  122. else if "RsTHighlight" `List.isPrefixOf` inFileName
  123. then "rst"
  124. else if "OrgHighlight" `List.isPrefixOf` inFileName
  125. then "org"
  126. else "html"
  127. flags :: FilePath -> [String]
  128. flags dir = case k of
  129. LaTeX -> latexFlags
  130. QuickLaTeX -> latexFlags ++ ["--only-scope-checking"]
  131. HTML -> ["--html", "--html-dir=" ++ dir]
  132. where
  133. latexFlags = ["--latex", "--latex-dir=" ++ dir]
  134. inFileName = takeFileName inp
  135. testName = asTestName testDir inp ++ "_" ++ show k
  136. baseName = if copy
  137. then testDir </> dropAgdaExtension inFileName
  138. else dropAgdaExtension inp
  139. flagFile = baseName <.> "flags"
  140. goldenFile = baseName <.> extension
  141. -- For removing a LaTeX compiler when testing @Foo.lagda@, you can
  142. -- create a file @Foo.compile@ with the list of the LaTeX compilers
  143. -- that you want to use (e.g. ["xelatex", "lualatex"]).
  144. compFile = baseName <.> ".compile"
  145. outFileName = case k of
  146. LaTeX -> golden
  147. HTML -> Network.URI.Encode.encode golden
  148. QuickLaTeX -> replaceExtension (dropExtension golden) "tex"
  149. where
  150. golden = takeFileName goldenFile
  151. -- Andreas, 2017-04-14, issue #2317
  152. -- Create temporary files in system temp directory.
  153. -- This has the advantage that upon Ctrl-C no junk is left behind
  154. -- in the Agda directory.
  155. -- doRun = withTempDirectory "." testName $ \outDir -> do
  156. doRun = withSystemTempDirectory testName $ \outDir -> do
  157. -- One can give extra options in .flags files (one per line).
  158. flagFileExists <- doesFileExist flagFile
  159. extraFlags <- if flagFileExists
  160. then lines <$> readFile flagFile
  161. else return []
  162. let newFile = outDir </> inFileName
  163. agdaArgs = flags outDir ++
  164. [ "-i" ++ if copy then outDir else testDir
  165. , if copy then newFile else inp
  166. , "--ignore-interfaces"
  167. , "--no-libraries"
  168. ] ++ extraFlags
  169. when copy $ copyFile inp newFile
  170. (exitcode, out, err) <- PT.readProcessWithExitCode agdaBin agdaArgs T.empty
  171. if exitcode /= ExitSuccess then
  172. AgdaFailed <$> do
  173. ProgramResult exitcode
  174. <$> cleanOutput out
  175. <*> cleanOutput err
  176. else do
  177. output <- decodeUtf8 <$> BS.readFile (outDir </> outFileName)
  178. let done = return $ Success output
  179. compile = do
  180. rl <- doesEnvContain "DONT_RUN_LATEX"
  181. if rl
  182. then done
  183. else do
  184. -- read compile options
  185. doCompile <- readFileMaybe compFile
  186. case doCompile of
  187. -- there is no compile file, so we run all the LaTeX compilers
  188. Nothing -> foldl (runLaTeX outFileName outDir) done allLaTeXProgs
  189. -- there is a compile file, check it's content
  190. Just content -> do
  191. let latexProgs =
  192. fromMaybe allLaTeXProgs
  193. (readMaybe $ T.unpack $ decodeUtf8 content)
  194. -- run the selected LaTeX compilers
  195. foldl (runLaTeX outFileName outDir) done latexProgs
  196. case k of
  197. HTML -> done
  198. LaTeX -> compile
  199. QuickLaTeX -> compile
  200. runLaTeX :: FilePath -- tex file
  201. -> FilePath -- working dir
  202. -> IO LaTeXResult -- continuation
  203. -> LaTeXProg
  204. -> IO LaTeXResult
  205. runLaTeX texFile wd cont prog = do
  206. let proc' = (proc prog ["-interaction=errorstopmode", texFile]) { cwd = Just wd }
  207. (ret, _, _) <- PB.readCreateProcessWithExitCode proc' BS.empty
  208. if ret == ExitSuccess then
  209. cont
  210. else do
  211. r <- cont
  212. return $ case r of
  213. LaTeXFailed progs -> LaTeXFailed (prog : progs)
  214. _ -> LaTeXFailed [prog]
  215. printLaTeXResult
  216. :: FilePath -- ^ The current working directory.
  217. -> LaTeXResult
  218. -> T.Text
  219. printLaTeXResult dir r = case r of
  220. Success t -> t
  221. AgdaFailed p -> "AGDA_COMPILE_FAILED\n\n"
  222. `T.append`
  223. mangle (printProgramResult p)
  224. LaTeXFailed progs -> "LATEX_COMPILE_FAILED with "
  225. `T.append`
  226. T.intercalate ", " (map T.pack progs)
  227. where
  228. -- Tries to make the resulting string more platform-independent.
  229. mangle = T.unwords . T.words . removeCWD
  230. removeCWD
  231. | null dir = id
  232. | otherwise = T.concat . T.splitOn (T.pack dir)