11baeb8850
Otherwise everything is on one line and the diff is uninformative.
217 lines
7.8 KiB
Haskell
217 lines
7.8 KiB
Haskell
{-# LANGUAGE PatternGuards #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Tests.Writers.OOXML (ooxmlTest) where
|
|
|
|
import Text.Pandoc hiding (Attr)
|
|
import Test.Tasty
|
|
import Test.Tasty.Golden.Advanced
|
|
import Control.Applicative ((<|>))
|
|
import Codec.Archive.Zip
|
|
import Text.XML.Light
|
|
import qualified Data.ByteString as BS
|
|
import qualified Data.ByteString.Lazy as BL
|
|
import Data.Foldable (asum)
|
|
import qualified Data.Text.IO as T
|
|
import Data.List (isSuffixOf, sort, (\\), intercalate, union)
|
|
import Data.Maybe (catMaybes, mapMaybe)
|
|
import Tests.Helpers
|
|
import Data.Algorithm.Diff
|
|
import System.FilePath.Glob (compile, match)
|
|
|
|
compareXML :: Content -> Content -> Maybe XMLDifference
|
|
-- We make a special exception for times at the moment, and just pass
|
|
-- them because we can't control the utctime when running IO. Besides,
|
|
-- so long as we have two times, we're okay.
|
|
compareXML (Elem goodElem) (Elem myElem)
|
|
| (QName "created" _ (Just "dcterms")) <- elName myElem
|
|
, (QName "created" _ (Just "dcterms")) <- elName goodElem =
|
|
Nothing
|
|
compareXML (Elem goodElem) (Elem myElem)
|
|
| (QName "modified" _ (Just "dcterms")) <- elName myElem
|
|
, (QName "modified" _ (Just "dcterms")) <- elName goodElem =
|
|
Nothing
|
|
compareXML (Elem goodElem) (Elem myElem) =
|
|
(if elName myElem == elName goodElem
|
|
then Nothing
|
|
else Just
|
|
(ElemNamesDiffer
|
|
(Comparison {mine = elName myElem, good = elName goodElem}))
|
|
)
|
|
<|> (if sort (elAttribs myElem) == sort (elAttribs goodElem)
|
|
then Nothing
|
|
else Just
|
|
(ElemAttributesDiffer
|
|
(Comparison { mine = sort (elAttribs myElem)
|
|
, good = sort (elAttribs goodElem)
|
|
})))
|
|
<|> asum (zipWith compareXML (elContent myElem) (elContent goodElem))
|
|
compareXML (Text goodCData) (Text myCData) =
|
|
(if cdVerbatim myCData == cdVerbatim goodCData
|
|
&& cdData myCData == cdData goodCData
|
|
then Nothing
|
|
else Just (CDatasDiffer (Comparison { mine = myCData, good = goodCData })))
|
|
compareXML (CRef goodStr) (CRef myStr) =
|
|
if myStr == goodStr
|
|
then Nothing
|
|
else Just (CRefsDiffer (Comparison { mine = myStr, good = goodStr }))
|
|
compareXML g m = Just (OtherContentsDiffer (Comparison {mine = m, good = g}))
|
|
|
|
data XMLDifference
|
|
= ElemNamesDiffer (Comparison QName)
|
|
| ElemAttributesDiffer (Comparison [Attr])
|
|
| CDatasDiffer (Comparison CData)
|
|
| CRefsDiffer (Comparison String)
|
|
| OtherContentsDiffer (Comparison Content)
|
|
deriving (Show)
|
|
|
|
data Comparison a = Comparison { good :: a, mine :: a }
|
|
deriving (Show)
|
|
|
|
displayDiff :: Element -> Element -> String
|
|
displayDiff elemA elemB =
|
|
showDiff (1,1)
|
|
(getDiff (lines $ ppElement elemA) (lines $ ppElement elemB))
|
|
|
|
goldenArchive :: FilePath -> IO Archive
|
|
goldenArchive fp = toArchive . BL.fromStrict <$> BS.readFile fp
|
|
|
|
testArchive :: (WriterOptions -> Pandoc -> PandocIO BL.ByteString)
|
|
-> WriterOptions
|
|
-> FilePath
|
|
-> IO Archive
|
|
testArchive writerFn opts fp = do
|
|
txt <- T.readFile fp
|
|
bs <- runIOorExplode $ do
|
|
setTranslations "en-US"
|
|
setVerbosity ERROR -- otherwise test output is confusingly noisy
|
|
readNative def txt >>= writerFn opts
|
|
return $ toArchive bs
|
|
|
|
compareFileList :: FilePath -> Archive -> Archive -> Maybe String
|
|
compareFileList goldenFP goldenArch testArch =
|
|
let testFiles = filesInArchive testArch
|
|
goldenFiles = filesInArchive goldenArch
|
|
diffTestGolden = testFiles \\ goldenFiles
|
|
diffGoldenTest = goldenFiles \\ testFiles
|
|
|
|
results =
|
|
[ if null diffGoldenTest
|
|
then Nothing
|
|
else Just $
|
|
"Files in " ++ goldenFP ++ " but not in generated archive:\n" ++
|
|
intercalate ", " diffGoldenTest
|
|
, if null diffTestGolden
|
|
then Nothing
|
|
else Just $
|
|
"Files in generated archive but not in " ++ goldenFP ++ ":\n" ++
|
|
intercalate ", " diffTestGolden
|
|
]
|
|
in
|
|
if null $ catMaybes results
|
|
then Nothing
|
|
else Just $ intercalate "\n" $ catMaybes results
|
|
|
|
compareXMLFile' :: FilePath -> Archive -> Archive -> Either String ()
|
|
compareXMLFile' fp goldenArch testArch = do
|
|
testEntry <- case findEntryByPath fp testArch of
|
|
Just entry -> Right entry
|
|
Nothing -> Left $
|
|
"Can't extract " ++ fp ++ " from generated archive"
|
|
testXMLDoc <- case parseXMLDoc $ fromEntry testEntry of
|
|
Just doc -> Right doc
|
|
Nothing -> Left $
|
|
"Can't parse xml in " ++ fp ++ " from generated archive"
|
|
|
|
goldenEntry <- case findEntryByPath fp goldenArch of
|
|
Just entry -> Right entry
|
|
Nothing -> Left $
|
|
"Can't extract " ++ fp ++ " from archive in stored file"
|
|
goldenXMLDoc <- case parseXMLDoc $ fromEntry goldenEntry of
|
|
Just doc -> Right doc
|
|
Nothing -> Left $
|
|
"Can't parse xml in " ++ fp ++ " from archive in stored file"
|
|
|
|
let testContent = Elem testXMLDoc
|
|
goldenContent = Elem goldenXMLDoc
|
|
display difference = "Non-matching xml in "
|
|
++ fp ++ ":\n"
|
|
++ "* " ++ show difference ++ "\n"
|
|
++ displayDiff testXMLDoc goldenXMLDoc
|
|
|
|
|
|
maybe (Right ()) (Left . display) (compareXML goldenContent testContent)
|
|
|
|
compareXMLFile :: FilePath -> Archive -> Archive -> Maybe String
|
|
compareXMLFile fp goldenArch testArch =
|
|
case compareXMLFile' fp goldenArch testArch of
|
|
Right _ -> Nothing
|
|
Left s -> Just s
|
|
|
|
compareAllXMLFiles :: Archive -> Archive -> Maybe String
|
|
compareAllXMLFiles goldenArch testArch =
|
|
let allFiles = filesInArchive goldenArch `union` filesInArchive testArch
|
|
allXMLFiles = sort $
|
|
filter
|
|
(\fp -> ".xml" `isSuffixOf` fp || ".rels" `isSuffixOf` fp)
|
|
allFiles
|
|
results =
|
|
mapMaybe (\fp -> compareXMLFile fp goldenArch testArch) allXMLFiles
|
|
in
|
|
if null results
|
|
then Nothing
|
|
else Just $ unlines results
|
|
|
|
compareMediaFile' :: FilePath -> Archive -> Archive -> Either String ()
|
|
compareMediaFile' fp goldenArch testArch = do
|
|
testEntry <- case findEntryByPath fp testArch of
|
|
Just entry -> Right entry
|
|
Nothing -> Left $
|
|
"Can't extract " ++ fp ++ " from generated archive"
|
|
goldenEntry <- case findEntryByPath fp goldenArch of
|
|
Just entry -> Right entry
|
|
Nothing -> Left $
|
|
"Can't extract " ++ fp ++ " from archive in stored file"
|
|
|
|
if fromEntry testEntry == fromEntry goldenEntry
|
|
then Right ()
|
|
else Left $
|
|
"Non-matching binary file: " ++ fp
|
|
|
|
compareMediaFile :: FilePath -> Archive -> Archive -> Maybe String
|
|
compareMediaFile fp goldenArch testArch =
|
|
case compareMediaFile' fp goldenArch testArch of
|
|
Right _ -> Nothing
|
|
Left s -> Just s
|
|
|
|
compareAllMediaFiles :: Archive -> Archive -> Maybe String
|
|
compareAllMediaFiles goldenArch testArch =
|
|
let allFiles = filesInArchive goldenArch `union` filesInArchive testArch
|
|
mediaPattern = compile "*/media/*"
|
|
allMediaFiles = sort $
|
|
filter (match mediaPattern) allFiles
|
|
results =
|
|
mapMaybe (\fp -> compareMediaFile fp goldenArch testArch) allMediaFiles
|
|
in
|
|
if null results
|
|
then Nothing
|
|
else Just $ unlines results
|
|
|
|
ooxmlTest :: (WriterOptions -> Pandoc -> PandocIO BL.ByteString)
|
|
-> String
|
|
-> WriterOptions
|
|
-> FilePath
|
|
-> FilePath
|
|
-> TestTree
|
|
ooxmlTest writerFn testName opts nativeFP goldenFP =
|
|
goldenTest
|
|
testName
|
|
(goldenArchive goldenFP)
|
|
(testArchive writerFn opts nativeFP)
|
|
(\goldenArch testArch ->
|
|
let res = catMaybes [ compareFileList goldenFP goldenArch testArch
|
|
, compareAllXMLFiles goldenArch testArch
|
|
, compareAllMediaFiles goldenArch testArch
|
|
]
|
|
in return $ if null res then Nothing else Just $ unlines res)
|
|
(\a -> BL.writeFile goldenFP $ fromArchive a)
|