packages feed

prosidy-1.6.0.2: test/Prosidy/Test/Parse.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Prosidy.Test.Parse (tests) where

import qualified Prosidy

import           Test.Tasty
import           Test.Tasty.Golden.Advanced     ( goldenTest )
import qualified Data.ByteString               as BS
import qualified Data.ByteString.Lazy          as BS.Lazy
import qualified System.Directory              as Dir
import           System.FilePath                ( (</>) )
import           Control.Exception              ( handle
                                                , displayException
                                                )

import qualified Paths_prosidy                 as Paths
import qualified Data.Aeson                    as Aeson
import qualified Data.Aeson.Diff               as Aeson.Diff
import qualified Data.Aeson.Encode.Pretty      as Aeson.Pretty

import qualified Data.Text.Lazy                as Text.Lazy
import           Data.Text.Lazy.Encoding        ( decodeUtf8With )

tests :: IO TestTree
tests = do
    goldenDir   <- (</> "golden") <$> Paths.getDataDir
    goldenTests <- Dir.listDirectory goldenDir
    pure . testGroup "parse" $ makeTest goldenDir <$> goldenTests

makeTest :: FilePath -> String -> TestTree
makeTest goldenDir name = goldenTest name
                                     (catchErrors getJSON)
                                     (catchErrors parsePro)
                                     compareDocs
                                     writeGolden
  where
    getJSON :: IO Aeson.Value
    getJSON = do
        bytes <- BS.readFile $ goldenDir </> name </> "output.json"
        case Aeson.eitherDecode' . BS.Lazy.fromStrict $ bytes of
            Left  e  -> fail $ "Failed to parse JSON: " <> e
            Right ok -> pure ok

    parsePro :: IO Aeson.Value
    parsePro = Aeson.toJSON
        <$> Prosidy.readDocument (goldenDir </> name </> "input.pro")

    compareDocs :: Aeson.Value -> Aeson.Value -> IO (Maybe String)
    compareDocs gold test | gold == test = pure Nothing
    compareDocs gold test =
        let diff   = Aeson.Diff.diff (Aeson.toJSON gold) (Aeson.toJSON test)
            pretty = Aeson.Pretty.encodePretty diff
        in  pure
                .  Just
                .  Text.Lazy.unpack
                $  "Golden test failed. The diff is included below:\n"
                <> decodeUtf8With (\_ _ -> Just '\65533') pretty

    writeGolden :: Aeson.Value -> IO ()
    writeGolden doc =
        BS.Lazy.writeFile (goldenDir </> name </> "output.json")
            $ Aeson.Pretty.encodePretty doc

catchErrors :: IO a -> IO a
catchErrors = handle $ \(e :: Prosidy.Failure) -> fail $ displayException e