packages feed

checkmate-0.4.0: test/Checkmate/Parser/CheckFileSpec.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Checkmate.Parser.CheckFileSpec (spec) where

import Control.Monad
import System.IO (hClose)

import Data.Text
import Data.Text.IO
import Test.Hspec
import Test.Hspec.Megaparsec
import Text.InterpolatedString.Perl6
import Text.Megaparsec hiding (ParseError)
import System.FilePath
import System.IO.Temp

import Checkmate.Check
import Checkmate.Parser.CheckFile

spec :: Spec
spec = do
    parserSpec "parser" $ \ text ->
        return (".", parse (parser ".") ("." </> "CHECK") text)
    parserSpec "parseCheckFileText" $ \ text ->
        return (".", parseCheckFileText ("." </> "CHECK") text)
    parserSpec "parseCheckFileText" $ \ text ->
        withSystemTempFile "checkmate-test" $ \ filePath handle -> do
            hPutStr handle text
            hClose handle
            result <- parseCheckFile filePath
            return (takeDirectory filePath, result)

parserSpec :: String
           -> ( Text -> IO (FilePath, Either ParseError Checklist)
              )
           -> Spec
parserSpec specName parseFn =
    describe specName $ do
        it "returns [] if a given text is empty" $ do
            (_, parsed) <- parse' ""
            parsed `shouldParse` []
        let bulletStyles =
                [ ("CHECK", "CHECK", "CHECK", "CHECK")
                , ("CHECK:", "CHECK:", "CHECK:", "CHECK:")
                , ("bullet \"*\"", "*", "*", "*")
                , ("bullet \"-\"", "-", "-", "-")
                , ("bullet \"+\"", "+", "+", "+")
                , ("digit \"1.\"", "1.", "2.", "3.")
                , ("digit \"1)\"", "1)", "2)", "3)")
                ] :: [(Text, Text, Text, Text)]
        forM_ bulletStyles $ \ (styleName, b1, b2, b3) -> do
            it [qq|interprets $styleName line as a Check|] $ do
                (dir, parsed) <- parse' [qq|$b1 foo|]
                parsed `shouldParse` [Check dir 1 "foo"]
            let nl = "\n" :: Text
            it [qq|interprets multiple $styleName lines as [Check]|] $ do
                (ad, a) <- parse' [qq|$b1 foo$nl$b2 bar$nl|]
                a `shouldParse` [Check ad 1 "foo", Check ad 2 "bar"]
                (bd, b) <- parse' [qq|$b1 foo$nl$b2 bar$nl$b3 baz|]
                b `shouldParse` [ Check bd 1 "foo"
                                , Check bd 2 "bar"
                                , Check bd 3 "baz"
                                ]
            it [qq|interprets multiple lines as a Check until it's followed 
                   by another $styleName|] $ do
                (ad, a) <- parse' [qq|$b1  foo{nl}bar{nl}qux$nl|]
                a `shouldParse` [Check ad 1 "foo\nbar\nqux"]
                (bd, b) <- parse' [qq|$b1 foo{nl}bar$nl$b2 qux$nl|]
                b `shouldParse` [Check bd 1 "foo\nbar", Check bd 2 "qux"]
            it "ignores the leading spaces/empty lines" $ do
                (_, a) <- parse' "    "
                a `shouldParse` []
                (_, b) <- parse' "\t"
                b `shouldParse` []
                (_, c) <- parse' "\n"
                c `shouldParse` []
                (_, d) <- parse' "\r\n"
                d `shouldParse` []
                (_, e) <- parse' "\n\n"
                e `shouldParse` []
                (_, f) <- parse' "  \t  \n\n"
                f `shouldParse` []
                (dir, g) <- parse' (mconcat ["  \t  \n\n", b1, " foo"])
                g `shouldParse` [Check dir 1 "foo"]
        it "parses well even if bullet styles are mixed" $ do
            (dir, parsed) <- parse'
                "CHECK foo\nbar\n- baz\nqux\n* quz\n+ a\n1. b\n2) c\nCHECK: d"
            parsed `shouldParse`
                [ Check dir 1 "foo\nbar"
                , Check dir 2 "baz\nqux"
                , Check dir 3 "quz"
                , Check dir 4 "a"
                , Check dir 5 "b"
                , Check dir 6 "c"
                , Check dir 7 "d"
                ]
  where
    parse' :: Text -> IO (Scope, Either ParseError Checklist)
    parse' text = do
        (dir, result) <- parseFn text
        return (Directory dir, result)