1{-# LANGUAGE TupleSections #-}
4 Copyright : © 2006-2021 John MacFarlane
5 License : GNU GPL, version 2 or above
7 Maintainer : John MacFarlane <jgm@berkeley@edu>
11Run commands, and test results, defined in markdown files.
13module Tests.Command (runTest, tests)
16import Data.Algorithm.Diff
17import System.Environment.Executable (getExecutablePath)
18import qualified Data.ByteString as BS
19import qualified Data.Text as T
20import Data.List (isSuffixOf)
21import System.Directory
22import qualified System.Environment as Env
24import System.FilePath (takeDirectory, (</>))
25import System.IO (hPutStr, stderr)
26import System.IO.Unsafe (unsafePerformIO)
29import Test.Tasty.HUnit
30import Test.Tasty.Golden.Advanced (goldenTest)
33import qualified Text.Pandoc.UTF8 as UTF8
35-- | Run a test with and return output.
36execTest :: String -- ^ Path to test executable
37 -> String -- ^ Shell command
38 -> String -- ^ Input text
39 -> IO (ExitCode, String) -- ^ Exit code and actual output
40execTest testExePath cmd inp = do
41 mldpath <- Env.lookupEnv "LD_LIBRARY_PATH"
42 mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH"
43 let env' = ("PATH",takeDirectory testExePath) :
45 ("LANG","en_US.UTF-8") :
47 ("pandoc_datadir", "..") :
48 maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++
49 maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath
50 let pr = (shell (pandocToEmulate True cmd)){ env = Just env' }
51 (ec, out', err') <- readCreateProcessWithExitCode pr inp
52 -- filter \r so the tests will work on Windows machines
53 let out = filter (/= '\r') $ err' ++ out'
55 ExitFailure _ -> hPutStr stderr err'
56 ExitSuccess -> return ()
59pandocToEmulate :: Bool -> String -> String
60pandocToEmulate True ('p':'a':'n':'d':'o':'c':cs) =
61 "test-pandoc --emulate" ++ pandocToEmulate False cs
62pandocToEmulate False ('|':' ':'p':'a':'n':'d':'o':'c':cs) =
63 "| " ++ "test-pandoc --emulate" ++ pandocToEmulate False cs
64pandocToEmulate _ (c:cs) = c : pandocToEmulate False cs
65pandocToEmulate _ [] = []
67-- | Run a test, return True if test passed.
68runTest :: String -- ^ Path to test executable
69 -> String -- ^ Title of test
70 -> String -- ^ Shell command
71 -> String -- ^ Input text
72 -> String -- ^ Expected output
74runTest testExePath testname cmd inp norm = testCase testname $ do
75 (ec, out) <- execTest testExePath cmd inp
76 result <- if ec == ExitSuccess
79 then return TestPassed
81 $ TestFailed cmd "expected"
82 $ getDiff (lines out) (lines norm)
83 else return $ TestError ec
84 assertBool (show result) (result == TestPassed)
88tests = unsafePerformIO $ do
89 files <- filter (".md" `isSuffixOf`) <$>
90 getDirectoryContents "command"
91 testExePath <- getExecutablePath
92 let cmds = map (extractCommandTest testExePath) files
93 return $ testGroup "Command:" cmds
95isCodeBlock :: Block -> Bool
96isCodeBlock (CodeBlock _ _) = True
99extractCode :: Block -> String
100extractCode (CodeBlock _ code) = T.unpack code
103dropPercent :: String -> String
104dropPercent ('%':xs) = dropWhile (== ' ') xs
107runCommandTest :: FilePath -> FilePath -> Int -> String -> TestTree
108runCommandTest testExePath fp num code =
109 goldenTest testname getExpected getActual compareValues updateGolden
111 testname = "#" <> show num
112 codelines = lines code
113 (continuations, r1) = span ("\\" `isSuffixOf`) codelines
114 cmd = dropPercent (unwords (map init continuations ++ take 1 r1))
116 (inplines, r3) = break (=="^D") r2
117 normlines = takeWhile (/=".") (drop 1 r3)
118 input = unlines inplines
119 norm = unlines normlines
120 getExpected = return norm
121 getActual = snd <$> execTest testExePath cmd input
122 compareValues expected actual
123 | actual == expected = return Nothing
124 | otherwise = return $ Just $ "--- test/command/" ++ fp ++ "\n+++ " ++
125 cmd ++ "\n" ++ showDiff (1,1)
126 (getDiff (lines actual) (lines expected))
127 updateGolden newnorm = do
128 let fp' = "command" </> fp
129 raw <- UTF8.readFile fp'
130 let cmdline = "% " <> cmd
131 let x = cmdline <> "\n" <> input <> "^D\n" <> norm
132 let y = cmdline <> "\n" <> input <> "^D\n" <> newnorm
133 let updated = T.replace (T.pack x) (T.pack y) raw
134 UTF8.writeFile fp' updated
136extractCommandTest :: FilePath -> FilePath -> TestTree
137extractCommandTest testExePath fp = unsafePerformIO $ do
138 contents <- UTF8.toText <$> BS.readFile ("command" </> fp)
139 Pandoc _ blocks <- runIOorExplode (readMarkdown
140 def{ readerExtensions = pandocExtensions } contents)
141 let codeblocks = map extractCode $ filter isCodeBlock blocks
142 let cases = zipWith (runCommandTest testExePath fp) [1..] codeblocks
143 return $ testGroup fp cases
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