packages feed

gedcom-0.2.0.0: test/Spec.hs

{-# LANGUAGE OverloadedStrings #-}

import Data.Gedcom.Internal.CoreTypes
import Data.Gedcom.Internal.LineParser
import Data.Gedcom.Internal.ParseMonads
import Data.Gedcom.Internal.Parser
import Data.Monoid
import Data.Void
import qualified Data.Text.All as T
import Test.Hspec
import Text.Megaparsec

testTagData :: T.Text
testTagData = "some test data"

testTag :: T.Text -> Maybe GDLine
testTag d = do
  (GDRoot [GDTree t []]) <- parseMaybe gdRoot$ "0 TEST " <> d <> "\n"
  return t

emptyTag :: Maybe GDLine
emptyTag = do
  (GDRoot [GDTree t []]) <- parseMaybe gdRoot$ "0 TEST\n"
  return t

ref1 :: GDXRefID
ref1 = GDXRefID "1"

linkTag :: Maybe GDLine
linkTag = do
  (GDRoot [GDTree t []]) <- parseMaybe gdRoot$ "0 TEST @1@\n"
  return t

linkLinkTag :: Maybe GDLine
linkLinkTag = do
  (GDRoot [GDTree t []]) <- parseMaybe gdRoot$ "0 @2@ TEST @1@\n"
  return t

crdTag :: Maybe GDLine
crdTag = do
  (GDRoot [GDTree t []]) <-
    parseMaybe gdRoot$ "0 @1@ TEST " <> testTagData <> "\n"
  return t

data ParseReturn a =
  Error T.Text | NoMatch | Match a deriving (Show, Eq)

isError :: ParseReturn a -> Bool
isError (Error _) = True
isError _ = False

parseStructure :: Maybe GDTree -> StructureParser a -> ParseReturn a
parseStructure Nothing p = Error "Line parse error"
parseStructure (Just v) p = case runStructure (p v) of
    (Right (Left _), _) -> NoMatch
    (Right (Right m), _) -> Match m
    (Left err, _) -> Error$ T.show err

noEscapes :: T.Text -> [(Maybe GDEscape, T.Text)]
noEscapes v = [(Nothing, v)]

main :: IO ()
main = hspec$ do
  describe "gdRoot"$ do
    it "parses a simple line with no children"$
      (testTag testTagData) `shouldBe`
        (Just$ GDLine (GDLevel 0) Nothing (GDTag "TEST")
          (Just . GDLineItemV . GDLineItem . noEscapes$ testTagData))
    it "parses a simple cross referenced line"$
      crdTag `shouldBe` (Just$ GDLine (GDLevel 0) (Just ref1) (GDTag "TEST")
        (Just . GDLineItemV . GDLineItem . noEscapes$ testTagData))
    it "parses a simple line cross referencing line"$
      linkTag `shouldBe` (Just$ GDLine (GDLevel 0) Nothing (GDTag "TEST")
        (Just . GDXRefIDV$  ref1))
    it "parses an empty tag with no children"$
      emptyTag `shouldBe`
        (Just$ GDLine (GDLevel 0) Nothing (GDTag "TEST") Nothing)

  describe "parseNoLinkTag"$ do
    it "matches the specified tag"$
      testLine (testTag testTagData) (parseNoLinkTag (GDTag "TEST") pure)
        `shouldBe` (Match (noEscapes testTagData, []))
    it "matches the specified tag (with a cross reference)" $
      testLine crdTag (parseNoLinkTag (GDTag "TEST") pure)
        `shouldBe` (Match (noEscapes testTagData, []))
    it "doesn't match other tags"$
      testLine (testTag testTagData) (parseNoLinkTag (GDTag "OTHER") pure)
        `shouldBe` NoMatch
    it "doesn't parse links"$
      isError$ testLine linkTag (parseNoLinkTag (GDTag "TEST") pure)

  describe "parseTag"$ do
    it "matches the specified tag"$
      testLine (testTag testTagData) (parseTag (GDTag "TEST") pure)
        `shouldBe` (Match . GDStructure$ (noEscapes testTagData, []))
    it "matches the specified tag (with a cross reference)" $
      testLine crdTag (parseTag (GDTag "TEST") pure)
        `shouldBe` (Match . GDStructure$ (noEscapes testTagData, []))
    it "doesn't match other tags"$
      testLine (testTag testTagData) (parseTag (GDTag "OTHER") pure)
        `shouldBe` NoMatch
    it "parses links"$
      testLine linkTag (parseTag (GDTag "TEST") pure)
        `shouldBe` (Match$ GDXRef ref1)
    it "doesn't parse links to links"$
      isError$ testLine linkLinkTag (parseTag (GDTag "TEST") pure)

  describe "parseLinkTag"$ do
    it "parses link tags"$
      testLine linkTag (parseLinkTag (GDTag "TEST")) `shouldBe`
        (Match$ (GDXRef ref1 :: GDRef Void))
    it "doesn't parse other tags"$
      isError$ testLine (testTag testTagData)
        (parseLinkTag (GDTag "TEST") :: StructureParser (GDRef Void))

  describe "parseTextTag"$ do
    it "matches the specified tag" $
      testLine (testTag testTagData) (parseTextTag (GDTag "TEST"))
        `shouldBe` (Match testTagData)

  describe "parseListTag"$ do
    it "splits lists"$
      testLine (testTag "one,,two,three,four") (parseListTag (GDTag "TEST"))
        `shouldBe` (Match ["one", "", "two", "three", "four"])
    it "handles the empty list"$
      testLine emptyTag (parseListTag (GDTag "TEST")) `shouldBe` (Match [])

  describe "parseWordTag"$ do
    it "parses numbers"$
      testLine (testTag "42") (parseWordTag (GDTag "TEST")) `shouldBe` (Match 42)
    it "doesn't parse negative numbers"$
      isError$ testLine (testTag "-42") (parseWordTag (GDTag "TEST"))
    it "doesn't parse empty strings"$
      isError$ testLine (testTag "") (parseWordTag (GDTag "TEST"))

  describe "parseBoolTag"$ do
    it "parses yes and no"$ do
      testLine (testTag "yes") (parseBoolTag (GDTag "TEST"))
        `shouldBe` (Match True)
      testLine (testTag "no") (parseBoolTag (GDTag "TEST"))
        `shouldBe` (Match False)
    it "parses case insensitive"$ do
      testLine (testTag "Yes") (parseBoolTag (GDTag "TEST"))
        `shouldBe` (Match True)
      testLine (testTag "nO") (parseBoolTag (GDTag "TEST"))
        `shouldBe` (Match False)
    it "doesn't parse anything else"$
      (isError$ testLine (testTag "true") (parseBoolTag (GDTag "TEST")))
      && (isError$ testLine (testTag "false") (parseBoolTag (GDTag "TEST")))

  where
    testLine l = parseStructure (GDTree <$> l <*> pure [])