packages feed

pandoc-plot-0.1.0.0: tests/Common.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedStrings #-}

module Common where

import           Control.Monad                    (unless)
import           Control.Monad.Reader

import           Data.Default.Class               (def)
import           Data.List                        (isInfixOf, isSuffixOf)
import           Data.Monoid                      ((<>))
import           Data.Text                        (Text, pack, unpack)

import           Test.Tasty
import           Test.Tasty.HUnit

import           Text.Pandoc.Filter.Plot
import           Text.Pandoc.Filter.Plot.Internal

import qualified Text.Pandoc.Builder              as B
import qualified Text.Pandoc.Definition           as B
import           Text.Pandoc.JSON

import           System.Directory                 (createDirectory,
                                                   createDirectoryIfMissing,
                                                   doesDirectoryExist,
                                                   doesFileExist, listDirectory,
                                                   removeDirectoryRecursive,
                                                   removePathForcibly)
import           System.FilePath                  (takeExtensions, (</>))
import           System.IO.Temp                   (getCanonicalTemporaryDirectory)


-------------------------------------------------------------------------------
-- Test that plot files and source files are created when the filter is run
testFileCreation :: Toolkit -> TestTree
testFileCreation tk =
    testCase "writes output files in appropriate directory" $ do
        tempDir <- (</> "test-file-creation") <$> getCanonicalTemporaryDirectory
        ensureDirectoryExistsAndEmpty tempDir

        let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk))
        _ <- (make tk) def cb
        filesCreated <- length <$> listDirectory tempDir
        assertEqual "" 2 filesCreated

-------------------------------------------------------------------------------
-- Test that included files are found within the source
testFileInclusion :: Toolkit -> TestTree
testFileInclusion tk =
    testCase "includes plot inclusions" $ do
        tempDir <- (</> "test-file-inclusion") <$> getCanonicalTemporaryDirectory
        ensureDirectoryExistsAndEmpty tempDir

        let cb = (addInclusion (include tk) $
                    addDirectory tempDir $ codeBlock tk (trivialContent tk))
        _ <- (make tk) def cb
        inclusion <- readFile (include tk)
        sourcePath <- head . filter (isExtensionOf "txt") <$> listDirectory tempDir
        src <- readFile (tempDir </> sourcePath)
        assertIsInfix inclusion src
    where
        include Matplotlib   = "tests/includes/matplotlib.py"
        include PlotlyPython = "tests/includes/plotly-python.py"
        include Matlab       = "tests/includes/matlabplot.m"
        include Mathematica  = "tests/includes/mathplot.m"
        include Octave       = "tests/includes/octave.m"
        include GGPlot2      = "tests/includes/ggplot2.r"

-------------------------------------------------------------------------------
-- Test that the files are saved in the appropriate format
testSaveFormat :: Toolkit -> TestTree
testSaveFormat tk =
    testCase "saves in the appropriate format" $ do
        tempDir <- (</> "test-safe-format") <$> getCanonicalTemporaryDirectory
        ensureDirectoryExistsAndEmpty tempDir
        let fmt = head (supportedSaveFormats tk)
            cb = (addSaveFormat fmt $
                 addDirectory tempDir $ codeBlock tk (trivialContent tk))
        _ <- (make tk) def cb
        numberjpgFiles <-
            length <$> filter (isExtensionOf (extension fmt)) <$>
            listDirectory tempDir
        assertEqual "" numberjpgFiles 1



codeBlock :: Toolkit -> Script -> Block
codeBlock tk script = CodeBlock (mempty, [cls tk], mempty) script


trivialContent :: Toolkit -> Script
trivialContent Matplotlib   = "import matplotlib.pyplot as plt\n"
trivialContent PlotlyPython = "import plotly.graph_objects as go; fit = go.Figure()\n"
trivialContent Matlab       = "figure('visible', 'off')\n"
trivialContent Mathematica  = "\n"
trivialContent Octave       = "figure('visible', 'off')\nplot (-10:0.1:10);"
trivialContent GGPlot2      = "library(ggplot2)\nggplot()\n"


addCaption :: String -> Block -> Block
addCaption caption (CodeBlock (id', cls, attrs) script) =
    CodeBlock (id', cls, attrs ++ [(tshow CaptionK, pack caption)]) script


addDirectory :: FilePath -> Block -> Block
addDirectory dir (CodeBlock (id', cls, attrs) script) =
    CodeBlock (id', cls, attrs ++ [(tshow DirectoryK, pack dir)]) script


addInclusion :: FilePath -> Block -> Block
addInclusion inclusionPath (CodeBlock (id', cls, attrs) script) =
    CodeBlock (id', cls, attrs ++ [(tshow PreambleK, pack inclusionPath)]) script


addSaveFormat :: SaveFormat -> Block -> Block
addSaveFormat saveFormat (CodeBlock (id', cls, attrs) script) =
    CodeBlock (id', cls, attrs ++ [(tshow SaveFormatK, pack . extension $ saveFormat)]) script


addDPI :: Int -> Block -> Block
addDPI dpi (CodeBlock (id', cls, attrs) script) =
    CodeBlock (id', cls, attrs ++ [(tshow DpiK, pack . show $ dpi)]) script


addWithSource :: Bool -> Block -> Block
addWithSource yn (CodeBlock (id', cls, attrs) script) =
    CodeBlock (id', cls, attrs ++ [(tshow WithSourceK, pack . show $ yn)]) script


-- | Assert that a file exists
assertFileExists :: HasCallStack => FilePath -> Assertion
assertFileExists filepath = do
    fileExists <- doesFileExist filepath
    unless fileExists (assertFailure msg)
  where
    msg = mconcat ["File ", filepath, " does not exist."]


-- | Not available with GHC < 8.4
-- since this function was added in filepath-1.4.2
-- but GHC 8.2.2 comes with filepath-1.4.1.2
isExtensionOf :: String -> FilePath -> Bool
isExtensionOf ext@('.':_) = isSuffixOf ext . takeExtensions
isExtensionOf ext         = isSuffixOf ('.':ext) . takeExtensions


-- | Assert that the first list is contained,
-- wholly and intact, anywhere within the second.
assertIsInfix :: (Eq a, Show a, HasCallStack) => [a] -> [a] -> Assertion
assertIsInfix xs ys = unless (xs `isInfixOf` ys) (assertFailure msg)
  where
    msg = mconcat ["Expected ", show xs, " to be an infix of ", show ys]

-- Ensure a directory is empty but exists.
ensureDirectoryExistsAndEmpty :: FilePath -> IO ()
ensureDirectoryExistsAndEmpty dir = do
    exists <- doesDirectoryExist dir
    if exists
        then removePathForcibly dir
        else return ()
    createDirectory dir

tshow :: Show a => a -> Text
tshow = pack . show