{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module RoundTrips.Basics (tests, roundTrip) where
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
import Test.QuickCheck hiding (Result, Success)
import Test.QuickCheck.Instances ()
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck hiding (Success)
import qualified Text.XML as XML (Element)
import Text.XML.TyDom.Conduit
tests :: TestTree
tests = testGroup "Basics"
[ testSingletonToElem
, testSingletonFromElem
--
, testAttrToElem
, testAttrFromElem
, testAttrRoundTrip
, testAttrMaybeToElem
, testAttrMaybeFromElem
, testAttrMaybeRoundTrip
--
, testChildToElem
, testChildFromElem
, testChildRoundTrip
, testChildMaybeToElem
, testChildMaybeFromElem
, testChildMaybeRoundTrip
, testChildListToElem
, testChildListFromElem
, testChildListRoundTrip
--
, testContentToElem
, testContentFromElem
, testContentRoundTrip
, testContentMaybeToElem
, testContentMaybeFromElem
, testContentMaybeRoundTrip
--
, testElementToElem
, testElementFromElem
, testElementRoundTrip
, testElementMaybeToElem
, testElementMaybeFromElem
, testElementMaybeRoundTrip
, testElementListToElem
, testElementListFromElem
, testElementListRoundTrip
--
, testMultiConstructorsToElem
, testMultiConstructorsFromElem
, testMultiConstructorsRoundTrip
--
, testNewtypeToElem
, testNewtypeFromElem
, testNewtypeRoundTrip
]
xmlHeader :: Text
xmlHeader = ""
-------------------------------------------------------------------------------
opt :: OptionsElement
opt = defaultOptionsElement { optReadLeftovers = LeftoversError }
optAll :: OptionsElement
optAll = defaultOptionsElement
{ optReadLeftovers = LeftoversError
, optReadNodeOrdering = All }
toElemTest :: ToElem a => a -> Text -> Assertion
toElemTest a expected = expected @=? (render (toElem a))
fromElemTest :: (Eq a, Show a, FromElem a) => Text -> a -> Assertion
fromElemTest text expected = (Success expected) @=? (parse text >>= fromElem)
fromElemAll :: (Eq a, Show a)
=> (XML.Element -> Result a) -> Text -> a -> Assertion
fromElemAll from text expected = (Success expected) @=? (parse text >>= from)
roundTrip :: (Eq a, ToElem a, FromElem a) => a -> Bool
roundTrip a = (parse (render $ toElem a) >>= fromElem) == Success a
-------------------------------------------------------------------------------
-- Raw singleton element
data TSingleton = TSingleton deriving (Eq, Show, Generic)
instance ToElem TSingleton where
toElem = genericToElem opt
instance FromElem TSingleton where
fromElem = genericFromElem opt
testSingletonToElem :: TestTree
testSingletonToElem = testCase "TSingleton toElem" $
toElemTest
TSingleton
(T.concat [ xmlHeader, "" ])
testSingletonFromElem :: TestTree
testSingletonFromElem = testCase "TSingleton fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "" ])
TSingleton)
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
TSingleton)
-------------------------------------------------------------------------------
-- Element with an attribute
data TAttr
= TAttr
{ attr :: Attr Text
} deriving (Eq, Show, Generic)
instance ToElem TAttr where
toElem = genericToElem opt
instance FromElem TAttr where
fromElem = genericFromElem opt
instance Arbitrary TAttr where
arbitrary = TAttr <$> arbitrary
testAttrToElem :: TestTree
testAttrToElem = testCase "TAttr toElem" $
toElemTest
(TAttr (Attr "attrTxt"))
(T.concat [ xmlHeader, ""])
testAttrFromElem :: TestTree
testAttrFromElem = testCase "TAttr fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TAttr (Attr "attrTxt")))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TAttr (Attr "attrTxt")))
testAttrRoundTrip :: TestTree
testAttrRoundTrip = testProperty "TAttr roundtrip" (roundTrip :: TAttr -> Bool)
-------------------------------------------------------------------------------
-- Element with an optional attribute
data TAttrMaybe
= TAttrMaybe
{ attrMaybe :: Attr (Maybe Text)
} deriving (Eq, Show, Generic)
instance ToElem TAttrMaybe where
toElem = genericToElem opt
instance FromElem TAttrMaybe where
fromElem = genericFromElem opt
instance Arbitrary TAttrMaybe where
arbitrary = TAttrMaybe <$> arbitrary
testAttrMaybeToElem :: TestTree
testAttrMaybeToElem = testCase "TAttrMaybe toElem" $ do
(toElemTest
(TAttrMaybe (Attr (Just "attrTxt")))
(T.concat [ xmlHeader, "" ]))
(toElemTest
(TAttrMaybe (Attr Nothing))
(T.concat [ xmlHeader, "" ]))
testAttrMaybeFromElem :: TestTree
testAttrMaybeFromElem = testCase "TAttrMaybe fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TAttrMaybe (Attr (Just "attrTxt"))))
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TAttrMaybe (Attr Nothing)))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TAttrMaybe (Attr (Just "attrTxt"))))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TAttrMaybe (Attr Nothing)))
testAttrMaybeRoundTrip :: TestTree
testAttrMaybeRoundTrip
= testProperty "TAttrMaybe roundtrip" (roundTrip :: TAttrMaybe -> Bool)
-------------------------------------------------------------------------------
-- Element with a child containing text
data TChild
= TChild
{ child :: Child Text
} deriving (Eq, Show, Generic)
instance ToElem TChild where
toElem = genericToElem opt
instance FromElem TChild where
fromElem = genericFromElem opt
instance Arbitrary TChild where
arbitrary = TChild <$> arbitrary
testChildToElem :: TestTree
testChildToElem = testCase "TChild toElem" $
toElemTest
(TChild (Child "child text"))
(T.concat [ xmlHeader, "child text" ])
testChildFromElem :: TestTree
testChildFromElem = testCase "TChild fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "child text" ])
(TChild (Child "child text")))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "child text" ])
(TChild (Child "child text")))
testChildRoundTrip :: TestTree
testChildRoundTrip
= testProperty "TChild roundtrip" (roundTrip :: TChild -> Bool)
-------------------------------------------------------------------------------
-- Element with an optional child containing text
data TChildMaybe
= TChildMaybe
{ childMaybe :: Child (Maybe Text)
} deriving (Eq, Show, Generic)
instance ToElem TChildMaybe where
toElem = genericToElem opt
instance FromElem TChildMaybe where
fromElem = genericFromElem opt
instance Arbitrary TChildMaybe where
arbitrary = TChildMaybe <$> arbitrary
testChildMaybeToElem :: TestTree
testChildMaybeToElem = testCase "TChildMaybe toElem" $ do
(toElemTest
(TChildMaybe (Child (Just "maybe child")))
(T.concat [ xmlHeader
, ""
, "maybe child"
, "" ]))
(toElemTest
(TChildMaybe (Child Nothing))
(T.concat [ xmlHeader, ""]))
testChildMaybeFromElem :: TestTree
testChildMaybeFromElem = testCase "TChildMaybe fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader
, ""
, "maybe child"
, "" ])
(TChildMaybe (Child (Just "maybe child"))))
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TChildMaybe (Child Nothing)))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader
, ""
, "maybe child"
, "" ])
(TChildMaybe (Child (Just "maybe child"))))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TChildMaybe (Child Nothing)))
testChildMaybeRoundTrip :: TestTree
testChildMaybeRoundTrip
= testProperty "TChildMaybe roundtrip" (roundTrip :: TChildMaybe -> Bool)
-------------------------------------------------------------------------------
-- Element with a list of children
data TChildList
= TChildList
{ childList :: Child [Text]
} deriving (Eq, Show, Generic)
instance ToElem TChildList where
toElem = genericToElem opt
instance FromElem TChildList where
fromElem = genericFromElem opt
instance Arbitrary TChildList where
arbitrary = TChildList <$> arbitrary
testChildListToElem :: TestTree
testChildListToElem = testCase "TChildList toElem" $
toElemTest
(TChildList (Child ["a item", "b item"]))
(T.concat [ xmlHeader
, ""
, "a item"
, "b item"
, ""])
testChildListFromElem :: TestTree
testChildListFromElem = testCase "TChildList fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader
, ""
, "a item"
, "b item"
, "" ])
(TChildList (Child ["a item", "b item"])))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader
, ""
, "a item"
, "b item"
, "" ])
(TChildList (Child ["a item", "b item"])))
testChildListRoundTrip :: TestTree
testChildListRoundTrip
= testProperty "TChildList roundtrip" (roundTrip :: TChildList -> Bool)
-------------------------------------------------------------------------------
-- Element with content
data TContent
= TContent
{ content :: Content Text
} deriving (Eq, Show, Generic)
instance ToElem TContent where
toElem = genericToElem opt
instance FromElem TContent where
fromElem = genericFromElem opt
instance Arbitrary TContent where
arbitrary = TContent <$> arbitrary
testContentToElem :: TestTree
testContentToElem = testCase "TContent toElem" $ do
(toElemTest
(TContent (Content "some content"))
(T.concat [ xmlHeader, "some content" ]))
(toElemTest
(TContent (Content ""))
(T.concat [ xmlHeader, "" ]))
testContentFromElem :: TestTree
testContentFromElem = testCase "TContent fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "some content" ])
(TContent (Content "some content")))
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TContent (Content "")))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "some content" ])
(TContent (Content "some content")))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TContent (Content "")))
testContentRoundTrip :: TestTree
testContentRoundTrip
= testProperty "TContent roundtrip" (roundTrip :: TContent -> Bool)
-------------------------------------------------------------------------------
-- Element with optional content
data TContentMaybe
= TContentMaybe
{ contentMaybe :: Content (Maybe Text)
} deriving (Eq, Show, Generic)
instance ToElem TContentMaybe where
toElem = genericToElem opt
instance FromElem TContentMaybe where
fromElem = genericFromElem opt
instance Arbitrary TContentMaybe where
arbitrary = TContentMaybe <$> arbitrary
testContentMaybeToElem :: TestTree
testContentMaybeToElem = testCase "TContentMaybe toElem" $ do
(toElemTest
(TContentMaybe (Content (Just "content")))
(T.concat [ xmlHeader
, ""
, "content"
, "" ]))
(toElemTest
(TContentMaybe (Content Nothing))
(T.concat [ xmlHeader, "" ]))
(toElemTest
(TContentMaybe (Content (Just "")))
(T.concat [ xmlHeader, "" ]))
testContentMaybeFromElem :: TestTree
testContentMaybeFromElem = testCase "TContentMaybe fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader
, ""
, "content"
, "" ])
(TContentMaybe (Content (Just "content"))))
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TContentMaybe (Content Nothing)))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader
, ""
, "content"
, "" ])
(TContentMaybe (Content (Just "content"))))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TContentMaybe (Content Nothing)))
newtype NonEmptyText = NonEmptyText { unNonEmptyText :: Text }
deriving (Eq, Show, ToXText, FromXText)
instance Arbitrary NonEmptyText where
arbitrary = do
genText <- arbitrary
return $ NonEmptyText (T.concat ["_", genText])
data TContentMaybeNonEmpty
= TContentMaybeNonEmpty
{ contentMaybeNonEmpty :: Content (Maybe NonEmptyText)
} deriving (Eq, Show, Generic)
instance ToElem TContentMaybeNonEmpty where
toElem = genericToElem opt
instance FromElem TContentMaybeNonEmpty where
fromElem = genericFromElem opt
instance Arbitrary TContentMaybeNonEmpty where
arbitrary = TContentMaybeNonEmpty <$> arbitrary
testContentMaybeRoundTrip :: TestTree
testContentMaybeRoundTrip = testProperty "TContentMaybeNonEmpty roundtrip"
(roundTrip :: TContentMaybeNonEmpty -> Bool )
-------------------------------------------------------------------------------
-- Element with element content
data TElement
= TElement
{ element :: TAttr
} deriving (Eq, Show, Generic)
instance ToElem TElement where
toElem = genericToElem opt
instance FromElem TElement where
fromElem = genericFromElem opt
instance Arbitrary TElement where
arbitrary = TElement <$> arbitrary
testElementToElem :: TestTree
testElementToElem = testCase "TElement toElem" $
toElemTest
(TElement (TAttr (Attr "value")))
(T.concat [ xmlHeader
, ""
, ""
, "" ])
testElementFromElem :: TestTree
testElementFromElem = testCase "TElement fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader
, ""
, ""
, "" ])
(TElement (TAttr (Attr "value"))))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader
, ""
, ""
, "" ])
(TElement (TAttr (Attr "value"))))
testElementRoundTrip :: TestTree
testElementRoundTrip
= testProperty "TElement roundtrip" (roundTrip :: TElement -> Bool)
-------------------------------------------------------------------------------
-- Element with optional element content
data TElementMaybe
= TElementMaybe
{ elementMaybe :: Maybe TAttr
} deriving (Eq, Show, Generic)
instance ToElem TElementMaybe where
toElem = genericToElem opt
instance FromElem TElementMaybe where
fromElem = genericFromElem opt
instance Arbitrary TElementMaybe where
arbitrary = TElementMaybe <$> arbitrary
testElementMaybeToElem :: TestTree
testElementMaybeToElem = testCase "TElementMaybe toElem" $ do
(toElemTest
(TElementMaybe (Just (TAttr (Attr "value"))))
(T.concat [ xmlHeader
, ""
, ""
, "" ]))
(toElemTest
(TElementMaybe Nothing)
(T.concat [ xmlHeader, "" ]))
testElementMaybeFromElem :: TestTree
testElementMaybeFromElem = testCase "TElementMaybe fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader
, ""
, ""
, "" ])
(TElementMaybe (Just (TAttr (Attr "value")))))
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TElementMaybe Nothing))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader
, ""
, ""
, "" ])
(TElementMaybe (Just (TAttr (Attr "value")))))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TElementMaybe Nothing))
testElementMaybeRoundTrip :: TestTree
testElementMaybeRoundTrip
= testProperty "TElementMaybe roundtrip"
(roundTrip :: TElementMaybe -> Bool)
-------------------------------------------------------------------------------
-- Element with element list content
data TElementList
= TElementList
{ elementList :: [TAttr]
} deriving (Eq, Show, Generic)
instance ToElem TElementList where
toElem = genericToElem opt
instance FromElem TElementList where
fromElem = genericFromElem opt
instance Arbitrary TElementList where
arbitrary = TElementList <$> arbitrary
testElementListToElem :: TestTree
testElementListToElem = testCase "TElementList toElem" $
toElemTest
(TElementList [(TAttr (Attr "attr 1")), (TAttr (Attr "attr 2"))])
(T.concat [ xmlHeader
, ""
, ""
, ""
, "" ])
testElementListFromElem :: TestTree
testElementListFromElem = testCase "TElementList fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader
, ""
, ""
, ""
, "" ])
(TElementList [(TAttr (Attr "attr 1")), (TAttr (Attr "attr 2"))]))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader
, ""
, ""
, ""
, "" ])
(TElementList [(TAttr (Attr "attr 1")), (TAttr (Attr "attr 2"))]))
testElementListRoundTrip :: TestTree
testElementListRoundTrip
= testProperty "TestElementList roundtrip"
(roundTrip :: TElementList -> Bool)
-------------------------------------------------------------------------------
-- Multiple constructors
data MultiConstructor
= ElemA { aAttr :: Attr Text }
| ElemB { bAttr :: Attr Text }
deriving (Eq, Show, Generic)
instance ToElem MultiConstructor where
toElem = genericToElem opt
instance FromElem MultiConstructor where
fromElem = genericFromElem opt
instance Arbitrary MultiConstructor where
arbitrary = oneof
[ ElemA <$> arbitrary
, ElemB <$> arbitrary ]
testMultiConstructorsToElem :: TestTree
testMultiConstructorsToElem = testCase "MultiConstructor toElem" $ do
(toElemTest
(ElemA (Attr "a value"))
(T.concat [ xmlHeader, "" ]))
(toElemTest
(ElemB (Attr "b value"))
(T.concat [ xmlHeader, "" ]))
testMultiConstructorsFromElem :: TestTree
testMultiConstructorsFromElem = testCase "MultiConstructor fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "" ])
(ElemA (Attr "a value")))
(fromElemTest
(T.concat [ xmlHeader, "" ])
(ElemB (Attr "b value")))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(ElemA (Attr "a value")))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(ElemB (Attr "b value")))
testMultiConstructorsRoundTrip :: TestTree
testMultiConstructorsRoundTrip = testProperty "MultiConstructor roundtrip"
(roundTrip :: MultiConstructor -> Bool)
-------------------------------------------------------------------------------
-- Newtype (alias another element)
newtype TNewtype = TNewtype { unTNewType :: TAttr } deriving (Eq, Show, Generic)
instance ToElem TNewtype where
toElem = genericToElem opt
instance FromElem TNewtype where
fromElem = genericFromElem opt
instance Arbitrary TNewtype where
arbitrary = TNewtype <$> arbitrary
testNewtypeToElem :: TestTree
testNewtypeToElem = testCase "TNewtype toElem" $
toElemTest
(TNewtype (TAttr (Attr "attr txt")))
(T.concat [ xmlHeader, "" ])
testNewtypeFromElem :: TestTree
testNewtypeFromElem = testCase "TNewtype fromElem" $ do
(fromElemTest
(T.concat [ xmlHeader, "" ])
(TNewtype (TAttr (Attr "attr txt"))))
(fromElemAll
(genericFromElem optAll)
(T.concat [ xmlHeader, "" ])
(TNewtype (TAttr (Attr "attr txt"))))
testNewtypeRoundTrip :: TestTree
testNewtypeRoundTrip = testProperty "TNewtype roundtrip"
(roundTrip :: TNewtype -> Bool)