File: test-texmath.hs

package info (click to toggle)
haskell-texmath 0.12.8.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 31,344 kB
  • sloc: haskell: 12,645; makefile: 29
file content (153 lines) | stat: -rw-r--r-- 5,910 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
{-# LANGUAGE OverloadedStrings #-}

import System.Directory
import System.FilePath
import Text.XML.Light
import Text.Show.Pretty (ppShow)
import Text.TeXMath
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.ByteString.Char8 as B
import Test.Tasty
import Test.Tasty.Golden (goldenVsStringDiff)
import Test.Tasty.Options
import Data.Text.Lazy.Encoding (encodeUtf8)
import Data.Text.Encoding as TE (decodeUtf8)
import Data.List (sort)
import Data.Typeable (Typeable)
import Data.Proxy

newtype RoundTrip = RoundTrip Bool
  deriving (Eq, Ord, Typeable)

instance IsOption RoundTrip where
  defaultValue = RoundTrip False
  parseValue = fmap RoundTrip . safeRead
  optionName = return "roundtrip"
  optionHelp = return "Do round-trip tests instead of regular"
  optionCLParser = flagCLParser Nothing (RoundTrip True)

main :: IO ()
main = do
  let getFiles dir = map (dir </>) . sort <$> listDirectory dir
  texReaderTests <- getFiles "test/reader/tex"
  mmlReaderTests <- getFiles "test/reader/mml"
  ommlReaderTests <- getFiles "test/reader/omml"
  texWriterTests <- getFiles "test/writer/tex"
  mmlWriterTests <- getFiles "test/writer/mml"
  ommlWriterTests <- getFiles "test/writer/omml"
  eqnWriterTests <- getFiles "test/writer/eqn"
  typstWriterTests <- getFiles "test/writer/typst"
  regressionTests <- getFiles "test/regression"
  roundtripTests <- getFiles "test/roundtrip"
  let ings = includingOptions [Option (Proxy :: Proxy RoundTrip)] :
                defaultIngredients
  defaultMainWithIngredients ings $ askOption $ \(RoundTrip roundTrip) ->
    testGroup "Tests" $
      if roundTrip
         then
           [ testGroup "roundtrip"
             [ testGroup "tex" $ map (toRoundtripTest "tex") roundtripTests
             , testGroup "mml" $ map (toRoundtripTest "mml") roundtripTests
             , testGroup "mml" $ map (toRoundtripTest "omml") roundtripTests
             ]
           ]
         else
           [ testGroup "reader"
             [ testGroup "tex" $ map toGoldenTest texReaderTests
             , testGroup "mml" $ map toGoldenTest mmlReaderTests
             , testGroup "omml" $ map toGoldenTest ommlReaderTests
             ],
             testGroup "writer"
             [ testGroup "tex" $ map toGoldenTest texWriterTests
             , testGroup "mml" $ map toGoldenTest mmlWriterTests
             , testGroup "omml" $ map toGoldenTest ommlWriterTests
             , testGroup "eqn" $ map toGoldenTest eqnWriterTests
             , testGroup "typst" $ map toGoldenTest typstWriterTests
             ],
             testGroup "regression" $ map toGoldenTest regressionTests
           ]

toRoundtripTest :: T.Text -> FilePath -> TestTree
toRoundtripTest format fp =
  goldenVsStringDiff (takeBaseName fp) diff fp getTested
 where
  diff ref new = ["diff", "-u", ref, new]

  getTested = do
    golden <- read <$> readFile fp
    reader <- maybe (error $ "Unknown input format " <> T.unpack format)
               return $ lookup format readers
    writer <- maybe (error $ "Unknown output format " <> T.unpack format)
               return $ lookup format writers
    case reader (writer golden) of
      Left err -> return $ encodeUtf8 $ TL.fromStrict err
      Right result -> return $ encodeUtf8 $ TL.fromStrict
                             $ ensureFinalNewline $ T.pack $ ppShow result

toGoldenTest :: FilePath -> TestTree
toGoldenTest fp =
  goldenVsStringDiff (takeBaseName fp) diff fp getTested
 where
  diff ref new = ["diff", "-u", ref, new]

  constructGoldenTest ((inFormat, inText), (outFormat, outText)) =
    return $ encodeUtf8 $ TL.fromStrict $
      "<<< " <> inFormat <> "\n" <> inText <> ">>> " <> outFormat <>
      "\n" <> ensureFinalNewline outText

  getTested = do
    ((inFormat, inText), (outFormat, _)) <- readGoldenTest fp
    result <- convert inFormat outFormat inText
    constructGoldenTest ((inFormat, inText), (outFormat, result))

  convert inFormat outFormat inText = do
    reader <- maybe (error $ "Unknown input format " <> T.unpack inFormat)
               return $ lookup inFormat readers
    writer <- maybe (error $ "Unknown output format " <> T.unpack outFormat)
               return $ lookup outFormat writers
    case writer <$> reader inText of
      Left err -> return err
      Right result -> return result

  readGoldenTest fp' = do
    lns <- B.lines <$> B.readFile fp'
    case break ("<<<" `B.isPrefixOf`) lns of
      (_, inputSpec:rest) ->
        case break (">>>" `B.isPrefixOf`) rest of
          (inlines, outputSpec:outlines) -> return
              ((T.strip $ T.drop 3 $ T.pack $ B.unpack inputSpec,
                 TE.decodeUtf8 $ B.unlines inlines),
               (T.strip $ T.drop 3 $ T.pack $ B.unpack outputSpec,
                 TE.decodeUtf8 $ B.unlines outlines))
          _ -> error $ fp' <> " contains no >>> output spec"
      _ -> error $ fp' <> " contains no <<< input spec"

ensureFinalNewline :: T.Text -> T.Text
ensureFinalNewline xs = case T.unsnoc xs of
  Nothing        -> xs
  Just (_, '\n') -> xs
  _              -> xs <> "\n"


readers :: [(T.Text, T.Text -> Either T.Text [Exp])]
readers = [ ("tex", readTeX)
          , ("mml", readMathML)
          , ("omml", readOMML)
          , ("native", readEither)
          ]

readEither :: T.Text -> Either T.Text [Exp]
readEither t = case reads (T.unpack t) of
                  [] -> error "Could not read native value"
                  ((x,_):_) -> return x

writers :: [(T.Text, [Exp] -> T.Text)]
writers = [ ("mml", T.pack . ppTopElement . writeMathML DisplayBlock)
          , ("tex", writeTeX)
          , ("omml", T.pack . ppTopElement . writeOMML DisplayBlock)
          , ("eqn", writeEqn DisplayBlock)
          , ("typst", writeTypst DisplayBlock)
          , ("native", T.pack . ppShow)
          , ("pandoc", maybe "" (T.pack . ppShow) . writePandoc DisplayBlock)
          ]