diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+## 1.8.0.1
+
+* Use doctest to validate code examples from documentation
+
 ## 1.8.0
 
 * Upgrade to conduit 1.3.0
diff --git a/Text/XML/Stream/Parse.hs b/Text/XML/Stream/Parse.hs
--- a/Text/XML/Stream/Parse.hs
+++ b/Text/XML/Stream/Parse.hs
@@ -9,49 +9,39 @@
 {-# LANGUAGE PatternGuards              #-}
 {-# LANGUAGE RankNTypes                 #-}
 {-# LANGUAGE StandaloneDeriving         #-}
-{-# LANGUAGE TupleSections              #-}
 {-# LANGUAGE TypeFamilies               #-}
 -- | This module provides both a native Haskell solution for parsing XML
 -- documents into a stream of events, and a set of parser combinators for
 -- dealing with a stream of events.
 --
--- As a simple example, if you have the following XML file:
+-- As a simple example:
 --
--- > <?xml version="1.0" encoding="utf-8"?>
--- > <people>
--- >     <person age="25">Michael</person>
--- >     <person age="2">Eliezer</person>
--- > </people>
+-- >>> :set -XOverloadedStrings
+-- >>> import Data.Conduit (runConduit, (.|))
+-- >>> import Data.Text (Text, unpack)
+-- >>> import Data.XML.Types (Event)
+-- >>> data Person = Person Int Text Text deriving Show
+-- >>> :{
+-- let parsePerson :: MonadThrow m => ConduitT Event o m (Maybe Person)
+--     parsePerson = tag' "person" parseAttributes $ \(age, goodAtHaskell) -> do
+--       name <- content
+--       return $ Person (read $ unpack age) name goodAtHaskell
+--       where parseAttributes = (,) <$> requireAttr "age" <*> requireAttr "goodAtHaskell" <* ignoreAttrs
+--     parsePeople :: MonadThrow m => ConduitT Event o m (Maybe [Person])
+--     parsePeople = tagNoAttr "people" $ many parsePerson
+--     inputXml = mconcat
+--       [ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+--       , "<people>"
+--       , "  <person age=\"25\" goodAtHaskell=\"yes\">Michael</person>"
+--       , "  <person age=\"2\" goodAtHaskell=\"might become\">Eliezer</person>"
+--       , "</people>"
+--       ]
+-- :}
 --
--- Then this code:
+-- >>> runConduit $ parseLBS def inputXml .| force "people required" parsePeople
+-- [Person 25 "Michael" "yes",Person 2 "Eliezer" "might become"]
 --
--- > {-# LANGUAGE OverloadedStrings #-}
--- > import Control.Monad.Trans.Resource
--- > import Data.Conduit (Consumer, Sink, ($$))
--- > import Data.Text (Text, unpack)
--- > import Text.XML.Stream.Parse
--- > import Data.XML.Types (Event)
--- >
--- > data Person = Person Int Text
--- >     deriving Show
--- >
--- > parsePerson :: MonadThrow m => ConduitT Event o m (Maybe Person)
--- > parsePerson = tag' "person" (requireAttr "age") $ \age -> do
--- >     name <- content
--- >     return $ Person (read $ unpack age) name
--- >
--- > parsePeople :: MonadThrow m => Sink Event m (Maybe [Person])
--- > parsePeople = tagNoAttr "people" $ many parsePerson
--- >
--- > main = do
--- >     people <- runResourceT $
--- >             parseFile def "people.xml" $$ force "people required" parsePeople
--- >     print people
 --
--- will produce:
---
--- > [Person 25 "Michael",Person 2 "Eliezer"]
---
 -- This module also supports streaming results using 'yield'.
 -- This allows parser results to be processed using conduits
 -- while a particular parser (e.g. 'many') is still running.
@@ -60,29 +50,16 @@
 -- to process by using streaming results.
 -- See http://stackoverflow.com/q/21367423/2597135 for a related discussion.
 --
--- > {-# LANGUAGE OverloadedStrings #-}
--- > import Control.Monad (void)
--- > import Control.Monad.Trans.Class (lift)
--- > import Control.Monad.Trans.Resource
--- > import Data.Conduit
--- > import qualified Data.Conduit.List as CL
--- > import Data.Text (Text, unpack)
--- > import Data.XML.Types (Event)
--- > import Text.XML.Stream.Parse
--- >
--- > data Person = Person Int Text deriving Show
--- >
--- > parsePerson :: MonadThrow m => ConduitT Event o m (Maybe Person)
--- > parsePerson = tag' "person" (requireAttr "age") $ \age -> do
--- >     name <- content
--- >     return $ Person (read $ unpack age) name
--- >
--- > parsePeople :: MonadThrow m => Conduit Event m Person
--- > parsePeople = void $ tagNoAttr "people" $ manyYield parsePerson
--- >
--- > main = runResourceT $
--- >     parseFile def "people.xml" $$ parsePeople .| CL.mapM_ (lift . print)
+-- >>> import Data.Conduit.List as CL
+-- >>> :{
+-- let parsePeople' :: MonadThrow m => ConduitT Event Person m (Maybe ())
+--     parsePeople' = tagNoAttr "people" $ manyYield parsePerson
+-- :}
 --
+-- >>> runConduit $ parseLBS def inputXml .| force "people required" parsePeople' .| CL.mapM_ print
+-- Person 25 "Michael" "yes"
+-- Person 2 "Eliezer" "might become"
+--
 -- Previous versions of this module contained a number of more sophisticated
 -- functions written by Aristid Breitkreuz and Dmitry Olshansky. To keep this
 -- package simpler, those functions are being moved to a separate package. This
@@ -159,6 +136,7 @@
     , PositionRange
     , EventPos
     ) where
+import           Conduit
 import           Control.Applicative          (Alternative (empty, (<|>)),
                                                Applicative (..), (<$>))
 import qualified Control.Applicative          as A
@@ -178,12 +156,10 @@
 import qualified Data.ByteString              as S
 import qualified Data.ByteString.Lazy         as L
 import           Data.Char                    (isSpace)
-import           Conduit
-import qualified Data.Conduit.Text            as CT
 import           Data.Conduit.Attoparsec      (PositionRange, conduitParser)
+import qualified Data.Conduit.Text            as CT
 import           Data.Default.Class           (Default (..))
-import           Data.List                    (intercalate)
-import           Data.List                    (foldl')
+import           Data.List                    (foldl', intercalate)
 import qualified Data.Map                     as Map
 import           Data.Maybe                   (fromMaybe, isNothing)
 import           Data.String                  (IsString (..))
@@ -773,8 +749,8 @@
           _         -> return (x, leftovers')
     runAttrParser' p as =
         case runAttrParser p as of
-            Left e          -> Left e
-            Right ([], x)   -> Right x
+            Left e           -> Left e
+            Right ([], x)    -> Right x
             Right (attr', _) -> Left $ toException $ UnparsedAttributes attr'
 
 -- | A simplified version of 'tag' where the 'NameMatcher' result isn't forwarded to the attributes parser.
@@ -837,6 +813,16 @@
 ignoreTree = ignoreTreeContent
 
 -- | Like 'ignoreTreeContent', but matches any name and also ignores content events.
+--
+-- >>> :set -XOverloadedStrings
+-- >>> import Data.Conduit
+-- >>> import Data.Conduit.List (consume)
+--
+-- >>> runConduit $ parseLBS def "<a>content</a><b></b>" .| (ignoreAnyTreeContent >> consume)
+-- [EventBeginElement (Name {nameLocalName = "b", ...}) [],EventEndElement (Name {nameLocalName = "b", ...}),EventEndDocument]
+--
+-- >>> runConduit $ parseLBS def "text<b></b>" .| (ignoreAnyTreeContent >> consume)
+-- [EventBeginElement (Name {nameLocalName = "b", ...}) [],EventEndElement (Name {nameLocalName = "b", ...}),EventEndDocument]
 ignoreAnyTreeContent :: MonadThrow m => ConduitT Event o m (Maybe ())
 ignoreAnyTreeContent = (void <$> contentMaybe) `orE` ignoreTreeContent anyName
 
@@ -1091,12 +1077,29 @@
     Just e -> if isWhitespace e then yield e >> takeContent else leftover e >> return Nothing
     _ -> return Nothing
 
--- | Stream 'Event's corresponding to a single element that matches given 'NameMatcher' and 'AttrParser', from the opening- to the closing-tag.
+-- | Stream 'Event's corresponding to a single XML element that matches given 'NameMatcher' and 'AttrParser', from the opening- to the closing-tag.
 --
+-- >>> :set -XOverloadedStrings
+-- >>> import Control.Monad (void)
+-- >>> import Data.Conduit
+-- >>> import Data.Conduit.List (consume)
+--
+-- >>> runConduit $ parseLBS def "<a>content</a><b></b>" .| void (takeTree "a" ignoreAttrs) .| consume
+-- [EventBeginDocument,EventBeginElement (Name {nameLocalName = "a", ...}) [],EventContent (ContentText "content"),EventEndElement (Name {nameLocalName = "a", ...})]
+--
+-- >>> runConduit $ parseLBS def "<a>content</a><b></b>" .| void (takeTree "b" ignoreAttrs) .| consume
+-- [EventBeginDocument]
+--
 -- If next 'Event' isn't an element, nothing is consumed.
 --
+-- >>> runConduit $ parseLBS def "text<a></a>" .| void (takeTree "a" ignoreAttrs) .| consume
+-- [EventBeginDocument]
+--
 -- If an opening-tag is consumed but no matching closing-tag is found, an 'XmlException' is thrown.
 --
+-- >>> runConduit $ parseLBS def "<a><b></b>" .| void (takeTree "a" ignoreAttrs) .| consume
+-- *** Exception: InvalidEndElement (Name {nameLocalName = "a", nameNamespace = Nothing, namePrefix = Nothing}) Nothing
+--
 -- This function automatically ignores comments, instructions and whitespace.
 --
 -- Returns @Just ()@ if an element was consumed, 'Nothing' otherwise.
@@ -1134,14 +1137,19 @@
 
 -- | Like 'takeTreeContent', without checking for tag name or attributes.
 --
--- >>> runResourceT $ parseLBS def "text<a></a>" $$ takeAnyTreeContent .| consume
--- Just [ EventContent (ContentText "text") ]
+-- >>> :set -XOverloadedStrings
+-- >>> import Control.Monad (void)
+-- >>> import Data.Conduit ((.|), runConduit)
+-- >>> import Data.Conduit.List (consume)
 --
--- >>> runResourceT $ parseLBS def "</a><b></b>" $$ takeAnyTreeContent .| consume
--- Just [ ]
+-- >>> runConduit $ parseLBS def "text<a></a>" .| void takeAnyTreeContent .| consume
+-- [EventBeginDocument,EventContent (ContentText "text")]
 --
--- >>> runResourceT $ parseLBS def "<b><c></c></b></a>text" $$ takeAnyTreeContent .| consume
--- Just [ EventBeginElement "b" [], EventBeginElement "c" [], EventEndElement "c", EventEndElement "b" ]
+-- >>> runConduit $ parseLBS def "</a><b></b>" .| void takeAnyTreeContent .| consume
+-- [EventBeginDocument]
+--
+-- >>> runConduit $ parseLBS def "<b><c></c></b></a>text" .| void takeAnyTreeContent .| consume
+-- [EventBeginDocument,EventBeginElement (Name {nameLocalName = "b", ...}) [],EventBeginElement (Name {nameLocalName = "c", ...}) [],EventEndElement (Name {nameLocalName = "c", ...}),EventEndElement (Name {nameLocalName = "b", ...})]
 --
 -- Since 1.5.0
 takeAnyTreeContent :: MonadThrow m
diff --git a/test/doctest.hs b/test/doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest.hs
@@ -0,0 +1,3 @@
+import           Test.DocTest
+
+main = doctest ["Text"]
diff --git a/test/main.hs b/test/main.hs
deleted file mode 100644
--- a/test/main.hs
+++ /dev/null
@@ -1,955 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE OverloadedStrings  #-}
-
-import           Control.Exception            (Exception, toException)
-import           Control.Monad.IO.Class       (liftIO)
-import qualified Data.ByteString.Char8        as S
-import qualified Data.ByteString.Lazy.Char8   as L
-import           Data.Typeable                (Typeable)
-import           Data.XML.Types
-import           Test.Hspec
-import           Test.HUnit                   hiding (Test)
-import qualified Text.XML                     as Res
-import qualified Text.XML.Cursor              as Cu
-import           Text.XML.Stream.Parse        (def)
-import qualified Text.XML.Stream.Parse        as P
-import qualified Text.XML.Unresolved          as D
-
-import           Control.Monad
-import qualified Data.Set                     as Set
-import           Data.Text                    (Text)
-import qualified Data.Text                    as T
-import           Text.XML.Cursor              (($.//), ($/), ($//), ($|),
-                                               (&.//), (&/), (&//))
-
-import qualified Control.Monad.Trans.Resource as C
-import           Data.Conduit                 ((.|), runConduit, runConduitRes, ConduitT)
-import qualified Data.Conduit                 as C
-import qualified Data.Conduit.List            as CL
-import qualified Data.Map                     as Map
-import           Text.Blaze                   (toMarkup)
-import           Text.Blaze.Renderer.String   (renderMarkup)
-
-main :: IO ()
-main = hspec $ do
-    describe "XML parsing and rendering" $ do
-        it "is idempotent to parse and render a document" documentParseRender
-        it "has valid parser combinators" combinators
-        context "has working choose function" testChoose
-        it "has working many function" testMany
-        it "has working many' function" testMany'
-        it "has working manyYield function" testManyYield
-        it "has working takeContent function" testTakeContent
-        it "has working takeTree function" testTakeTree
-        it "has working takeAnyTreeContent function" testTakeAnyTreeContent
-        it "has working orE" testOrE
-        it "is idempotent to parse and pretty render a document" documentParsePrettyRender
-        it "ignores the BOM" parseIgnoreBOM
-        it "strips duplicated attributes" stripDuplicateAttributes
-        it "displays comments" testRenderComments
-        it "conduit parser" testConduitParser
-        it "can omit the XML declaration" omitXMLDeclaration
-        context "correctly parses hexadecimal entities" hexEntityParsing
-    describe "XML Cursors" $ do
-        it "has correct parent" cursorParent
-        it "has correct ancestor" cursorAncestor
-        it "has correct orSelf" cursorOrSelf
-        it "has correct preceding" cursorPreceding
-        it "has correct following" cursorFollowing
-        it "has correct precedingSibling" cursorPrecedingSib
-        it "has correct followingSibling" cursorFollowingSib
-        it "has correct descendant" cursorDescendant
-        it "has correct check" cursorCheck
-        it "has correct check with lists" cursorPredicate
-        it "has correct checkNode" cursorCheckNode
-        it "has correct checkElement" cursorCheckElement
-        it "has correct checkName" cursorCheckName
-        it "has correct anyElement" cursorAnyElement
-        it "has correct element" cursorElement
-        it "has correct laxElement" cursorLaxElement
-        it "has correct content" cursorContent
-        it "has correct attribute" cursorAttribute
-        it "has correct laxAttribute" cursorLaxAttribute
-        it "has correct &* and $* operators" cursorDeep
-        it "has correct force" cursorForce
-        it "has correct forceM" cursorForceM
-        it "has correct hasAttribute" cursorHasAttribute
-        it "has correct attributeIs" cursorAttributeIs
-    describe "resolved" $ do
-        it "identifies unresolved entities" resolvedIdentifies
-        it "decodeHtmlEntities" testHtmlEntities
-        it "works for resolvable entities" resolvedAllGood
-        it "merges adjacent content nodes" resolvedMergeContent
-        it "understands inline entity declarations" resolvedInline
-    describe "pretty" $ do
-        it "works" casePretty
-    describe "top level namespaces" $ do
-        it "works" caseTopLevelNamespace
-        it "works with prefix" caseTopLevelNamespacePrefix
-        it "handles conflicts" caseTLNConflict
-    describe "blaze-html instances" $ do
-        it "works" caseBlazeHtml
-    describe "attribute reordering" $ do
-        it "works" caseAttrReorder
-    describe "ordering attributes explicitly" $ do
-        it "works" caseOrderAttrs
-    it "parsing CDATA" caseParseCdata
-    it "retains namespaces when asked" caseRetainNamespaces
-    it "handles iso-8859-1" caseIso8859_1
-    it "renders CDATA when asked" caseRenderCDATA
-    it "escapes CDATA closing tag in CDATA" caseEscapesCDATA
-
-documentParseRender :: IO ()
-documentParseRender =
-    mapM_ go docs
-  where
-    go x = x @=? D.parseLBS_ def (D.renderLBS def x)
-    docs =
-        [ Document (Prologue [] Nothing [])
-                   (Element "foo" [] [])
-                   []
-        , D.parseLBS_ def
-            "<?xml version=\"1.0\"?><!DOCTYPE foo>\n<foo/>"
-        , D.parseLBS_ def
-            "<?xml version=\"1.0\"?><!DOCTYPE foo>\n<foo><nested>&ignore;</nested></foo>"
-        , D.parseLBS_ def
-            "<foo><![CDATA[this is some<CDATA content>]]></foo>"
-        , D.parseLBS_ def
-            "<foo bar='baz&amp;bin'/>"
-        , D.parseLBS_ def
-            "<foo><?instr this is a processing instruction?></foo>"
-        , D.parseLBS_ def
-            "<foo><!-- this is a comment --></foo>"
-        ]
-
-documentParsePrettyRender :: IO ()
-documentParsePrettyRender =
-    L.unpack (D.renderLBS def { D.rsPretty = True } (D.parseLBS_ def doc)) @?= L.unpack doc
-  where
-    doc = L.unlines
-        [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-        , "<foo>"
-        , "    <?bar bar?>"
-        , "    text"
-        , "    <?bin bin?>"
-        , "</foo>"
-        ]
-
-combinators :: Assertion
-combinators = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tag' "hello" (P.requireAttr "world") $ \world -> do
-        liftIO $ world @?= "true"
-        P.force "need child1" $ P.tagNoAttr "{mynamespace}child1" $ return ()
-        P.force "need child2" $ P.tagNoAttr "child2" $ return ()
-        P.force "need child3" $ P.tagNoAttr "child3" $ do
-            x <- P.contentMaybe
-            liftIO $ x @?= Just "combine <all> &content"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello world='true'>"
-        , "<?this should be ignored?>"
-        , "<child1 xmlns='mynamespace'/>"
-        , "<!-- this should be ignored -->"
-        , "<child2>   </child2>"
-        , "<child3>combine &lt;all&gt; <![CDATA[&content]]></child3>\n"
-        , "</hello>"
-        ]
-
-testChoose :: Spec
-testChoose = do
-    it "can choose between elements"
-        testChooseEitherElem
-    it "can choose between elements and text, returning text"
-        testChooseElemOrTextIsText
-    it "can choose between elements and text, returning elements"
-        testChooseElemOrTextIsElem
-    it "can choose between text and elements, returning text"
-        testChooseTextOrElemIsText
-    it "can choose between text and elements, returning elements"
-        testChooseTextOrElemIsElem
-    it "can choose between text and elements, when the text is encoded"
-        testChooseElemOrTextIsEncoded
-    it "can choose between text and elements, when the text is encoded, NBSP"
-        testChooseElemOrTextIsEncodedNBSP
-    it "can choose between elements and text, when the text is whitespace"
-        testChooseElemOrTextIsWhiteSpace
-    it "can choose between text and elements, when the text is whitespace"
-        testChooseTextOrElemIsWhiteSpace
-    it "can choose between text and elements, when the whitespace is both literal and encoded"
-        testChooseElemOrTextIsChunkedText
-    it "can choose between text and elements, when the text is chunked the other way"
-        testChooseElemOrTextIsChunkedText2
-
-testChooseElemOrTextIsText :: Assertion
-testChooseElemOrTextIsText = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return "boom"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just " something "
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , " something "
-        , "</hello>"
-        ]
-
-testChooseElemOrTextIsEncoded :: Assertion
-testChooseElemOrTextIsEncoded = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return "boom"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just "\x20something\x20"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "&#x20;something&#x20;"
-        , "</hello>"
-        ]
-
-testChooseElemOrTextIsEncodedNBSP :: Assertion
-testChooseElemOrTextIsEncodedNBSP = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return "boom"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just "\160something\160"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "&#160;something&#160;"
-        , "</hello>"
-        ]
-
-
-testChooseElemOrTextIsWhiteSpace :: Assertion
-testChooseElemOrTextIsWhiteSpace = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return "boom"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just "\x20\x20\x20"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>   </hello>"
-        ]
-
-testChooseTextOrElemIsWhiteSpace :: Assertion
-testChooseTextOrElemIsWhiteSpace = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.contentMaybe
-            , P.tagNoAttr "failure" $ return "boom"
-            ]
-        liftIO $ x @?= Just "\x20\x20\x20"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>   </hello>"
-        ]
-
-testChooseElemOrTextIsChunkedText :: Assertion
-testChooseElemOrTextIsChunkedText = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return "boom"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just "\x20\x20\x20"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello> &#x20; </hello>"
-        ]
-
-testChooseElemOrTextIsChunkedText2 :: Assertion
-testChooseElemOrTextIsChunkedText2 = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return "boom"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just "\x20\x20\x20"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>&#x20; &#x20;</hello>"
-        ]
-
-testChooseElemOrTextIsElem :: Assertion
-testChooseElemOrTextIsElem = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "success" $ return "success"
-            , P.contentMaybe
-            ]
-        liftIO $ x @?= Just "success"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "</hello>"
-        ]
-
-testChooseTextOrElemIsText :: Assertion
-testChooseTextOrElemIsText = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.contentMaybe
-            , P.tagNoAttr "failure" $ return "boom"
-            ]
-        liftIO $ x @?= Just " something "
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , " something "
-        , "</hello>"
-        ]
-
-testChooseTextOrElemIsElem :: Assertion
-testChooseTextOrElemIsElem = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.contentMaybe
-            , P.tagNoAttr "success" $ return "success"
-            ]
-        liftIO $ x @?= Just "success"
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "</hello>"
-        ]
-
-testChooseEitherElem :: Assertion
-testChooseEitherElem = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.choose
-            [ P.tagNoAttr "failure" $ return 1
-            , P.tagNoAttr "success" $ return 2
-            ]
-        liftIO $ x @?= Just (2 :: Int)
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "</hello>"
-        ]
-
-testManyYield :: Assertion
-testManyYield = do
-    -- Basically the same as testMany, but consume the streamed result
-    result <- runConduitRes $
-        P.parseLBS def input .| helloParser
-        .| CL.consume
-    length result @?= 5
-  where
-    helloParser = void $ P.tagNoAttr "hello" $ P.manyYield successParser
-    successParser = P.tagNoAttr "success" $ return ()
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "</hello>"
-        ]
-
-testTakeContent :: Assertion
-testTakeContent = do
-    result <- runConduitRes $ P.parseLBS def input .| rootParser
-    result @?= Just
-      [ EventContent (ContentText "Hello world !")
-      ]
-  where
-    rootParser = P.tagNoAttr "root" $ void (P.takeContent >> P.takeContent) .| CL.consume
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<root>"
-        , "Hello world !"
-        , "</root>"
-        ]
-
-testTakeTree :: Assertion
-testTakeTree = do
-    result <- runConduitRes $ P.parseLBS def input .| rootParser
-    result @?=
-      [ EventBeginDocument
-      , EventBeginDoctype "foo" Nothing
-      , EventEndDoctype
-      , EventBeginElement "a" []
-      , EventBeginElement "em" []
-      , EventContent (ContentText "Hello world !")
-      , EventEndElement "em"
-      , EventEndElement "a"
-      ]
-  where
-    rootParser = void (P.takeTree "a" P.ignoreAttrs) .| CL.consume
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<a>"
-        , "<em>Hello world !</em>"
-        , "</a>"
-        , "<b>"
-        , "</b>"
-        ]
-
-testTakeAnyTreeContent :: Assertion
-testTakeAnyTreeContent = do
-    result <- runConduitRes $ P.parseLBS def input .| rootParser
-    result @?= Just
-      [ EventBeginElement "b" []
-      , EventContent (ContentText "Hello ")
-      , EventBeginElement "em" []
-      , EventContent (ContentText "world")
-      , EventEndElement "em"
-      , EventContent (ContentText " !")
-      , EventEndElement "b"
-      ]
-  where
-    rootParser = P.tagNoAttr "root" $ (P.takeAnyTreeContent >> void P.ignoreAnyTreeContent) .| CL.consume
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<root>"
-        , "<b>Hello <em>world</em> !</b> Welcome !"
-        , "</root>"
-        ]
-
-
-testMany :: Assertion
-testMany = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.many $ P.tagNoAttr "success" $ return ()
-        liftIO $ length x @?= 5
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "</hello>"
-        ]
-
-testMany' :: Assertion
-testMany' = runConduitRes $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.many' $ P.tagNoAttr "success" $ return ()
-        liftIO $ length x @?= 5
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "<success/>"
-        , "<success/>"
-        , "<foobar/>"
-        , "<success/>"
-        , "<foo><bar attr=\"1\">some content</bar></foo>"
-        , "<success/>"
-        , "</hello>"
-        ]
-
-testOrE :: IO ()
-testOrE = runConduitRes $ runConduit $ P.parseLBS def input .| do
-    P.force "need hello" $ P.tagNoAttr "hello" $ do
-        x <- P.tagNoAttr "failure" (return 1) `P.orE`
-             P.tagNoAttr "success" (return 2)
-        y <- P.tag' "success" (P.requireAttr "failure") (const $ return 1) `P.orE`
-             P.tag' "success" (P.requireAttr "success") (const $ return 2)
-        liftIO $ x @?= Just (2 :: Int)
-        liftIO $ y @?= Just (2 :: Int)
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<success/>"
-        , "<success success=\"0\"/>"
-        , "</hello>"
-        ]
-
-testConduitParser :: Assertion
-testConduitParser = do
-    x <-   runConduitRes
-         $ P.parseLBS def input
-        .| (P.force "need hello" $ P.tagNoAttr "hello" f)
-        .| CL.consume
-    liftIO $ x @?= [1, 1, 1]
-  where
-    input = L.concat
-        [ "<?xml version='1.0'?>"
-        , "<!DOCTYPE foo []>\n"
-        , "<hello>"
-        , "<item/>"
-        , "<item/>"
-        , "<item/>"
-        , "</hello>"
-        ]
-    f :: C.MonadThrow m => ConduitT Event Int m ()
-    f = do
-        ma <- P.tagNoAttr "item" (return 1)
-        maybe (return ()) (\a -> C.yield a >> f) ma
-
-omitXMLDeclaration :: Assertion
-omitXMLDeclaration = Res.renderLBS settings input @?= spec
-  where
-    settings = def { Res.rsXMLDeclaration = False }
-    input = Res.Document (Prologue [] Nothing [])
-              (Res.Element "foo" Map.empty [Res.NodeContent "bar"])
-              []
-    spec = "<foo>bar</foo>"
-
-hexEntityParsing :: Spec
-hexEntityParsing = do
-  it "rejects leading 0x" $
-    go "<foo>&#x0xff;</foo>" @?= Nothing
-  it "rejects leading 0X" $
-    go "<foo>&#x0Xff;</foo>" @?= Nothing
-  it "accepts lowercase hex digits" $
-    go "<foo>&#xff;</foo>" @?= Just (spec "\xff")
-  it "accepts uppercase hex digits" $
-    go "<foo>&#xFF;</foo>" @?= Just (spec "\xff")
-  --Note: this must be rejected, because, according to the XML spec, a
-  --legal EntityRef's entity matches Name, which can't start with a
-  --hash.
-  it "rejects trailing junk" $
-    go "<foo>&#xffhello;</foo>" @?= Nothing
-  --Some of these next tests are XML 1.0 specific (i.e., they would
-  --differ for XML 1.1), but approximately no-one uses XML 1.1.
-  it "rejects illegal character #x0" $
-    go "<foo>&#x0;</foo>" @?= Nothing
-  it "rejects illegal character #xFFFE" $
-    go "<foo>&#xFFFE;</foo>" @?= Nothing
-  it "rejects illegal character #xFFFF" $
-    go "<foo>&#xFFFF;</foo>" @?= Nothing
-  it "rejects illegal character #xD900" $
-    go "<foo>&#xD900;</foo>" @?= Nothing
-  it "rejects illegal character #xC" $
-    go "<foo>&#xC;</foo>" @?= Nothing
-  it "rejects illegal character #x1F" $
-    go "<foo>&#x1F;</foo>" @?= Nothing
-  it "accepts astral plane character" $
-    go "<foo>&#x1006ff;</foo>" @?= Just (spec "\x1006ff")
-  it "accepts custom character references" $
-    go' customSettings "<foo>&#xC;</foo>" @?= Just (spec "\xff")
-  where
-    spec content = Document (Prologue [] Nothing [])
-                    (Element "foo" [] [NodeContent (ContentText content)])
-                    []
-
-    go = either (const Nothing) Just . D.parseLBS def
-    go' settings = either (const Nothing) Just . D.parseLBS settings
-    customSettings = def { P.psDecodeIllegalCharacters = customDecoder }
-    customDecoder 12 = Just '\xff'
-    customDecoder _  = Nothing
-
-name :: [Cu.Cursor] -> [Text]
-name [] = []
-name (c:cs) = ($ name cs) $ case Cu.node c of
-                              Res.NodeElement e -> ((Res.nameLocalName $ Res.elementName e) :)
-                              _ -> id
-
-cursor :: Cu.Cursor
-cursor =
-    Cu.fromDocument $ Res.parseLBS_ def input
-  where
-    input = L.concat
-        [ "<foo attr=\"x\">"
-        ,    "<bar1/>"
-        ,    "<bar2>"
-        ,       "<baz1/>"
-        ,       "<baz2 attr=\"y\"/>"
-        ,       "<baz3>a</baz3>"
-        ,    "</bar2>"
-        ,    "<bar3>"
-        ,       "<bin1/>"
-        ,       "b"
-        ,       "<bin2/>"
-        ,       "<bin3/>"
-        ,    "</bar3>"
-        ,    "<Bar1 xmlns=\"http://example.com\" Attr=\"q\"/>"
-        , "</foo>"
-        ]
-
-bar2, baz2, bar3, bin2 :: Cu.Cursor
-bar2 = Cu.child cursor !! 1
-baz2 = Cu.child bar2 !! 1
-
-bar3 = Cu.child cursor !! 2
-bin2 = Cu.child bar3 !! 1
-
-cursorParent, cursorAncestor, cursorOrSelf, cursorPreceding, cursorFollowing,
-    cursorPrecedingSib, cursorFollowingSib, cursorDescendant, cursorCheck,
-    cursorPredicate, cursorCheckNode, cursorCheckElement, cursorCheckName,
-    cursorAnyElement, cursorElement, cursorLaxElement, cursorContent,
-    cursorAttribute, cursorLaxAttribute, cursorHasAttribute,
-    cursorAttributeIs, cursorDeep, cursorForce, cursorForceM,
-    resolvedIdentifies, resolvedAllGood, resolvedMergeContent,
-    testHtmlEntities
-    :: Assertion
-cursorParent = name (Cu.parent bar2) @?= ["foo"]
-cursorAncestor = name (Cu.ancestor baz2) @?= ["bar2", "foo"]
-cursorOrSelf = name (Cu.orSelf Cu.ancestor baz2) @?= ["baz2", "bar2", "foo"]
-cursorPreceding = do
-  name (Cu.preceding baz2) @?= ["baz1", "bar1"]
-  name (Cu.preceding bin2) @?= ["bin1", "baz3", "baz2", "baz1", "bar2", "bar1"]
-cursorFollowing = do
-  name (Cu.following baz2) @?= ["baz3", "bar3", "bin1", "bin2", "bin3", "Bar1"]
-  name (Cu.following bar2) @?= ["bar3", "bin1", "bin2", "bin3", "Bar1"]
-cursorPrecedingSib = name (Cu.precedingSibling baz2) @?= ["baz1"]
-cursorFollowingSib = name (Cu.followingSibling baz2) @?= ["baz3"]
-cursorDescendant = (name $ Cu.descendant cursor) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3 Bar1"
-cursorCheck = null (cursor $.// Cu.check (const False)) @?= True
-cursorPredicate = (name $ cursor $.// Cu.check Cu.descendant) @?= T.words "foo bar2 baz3 bar3"
-cursorCheckNode = (name $ cursor $// Cu.checkNode f) @?= T.words "bar1 bar2 bar3"
-    where f (Res.NodeElement e) = "bar" `T.isPrefixOf` Res.nameLocalName (Res.elementName e)
-          f _               = False
-cursorCheckElement = (name $ cursor $// Cu.checkElement f) @?= T.words "bar1 bar2 bar3"
-    where f e = "bar" `T.isPrefixOf` Res.nameLocalName (Res.elementName e)
-cursorCheckName = (name $ cursor $// Cu.checkName f) @?= T.words "bar1 bar2 bar3"
-    where f n = "bar" `T.isPrefixOf` nameLocalName n
-cursorAnyElement = (name $ cursor $// Cu.anyElement) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3 Bar1"
-cursorElement = (name $ cursor $// Cu.element "bar1") @?= ["bar1"]
-cursorLaxElement = (name $ cursor $// Cu.laxElement "bar1") @?= ["bar1", "Bar1"]
-cursorContent = do
-  Cu.content cursor @?= []
-  (cursor $.// Cu.content) @?= ["a", "b"]
-cursorAttribute = Cu.attribute "attr" cursor @?= ["x"]
-cursorLaxAttribute = (cursor $.// Cu.laxAttribute "Attr") @?= ["x", "y", "q"]
-
-cursorHasAttribute = (length $ cursor $.// Cu.hasAttribute "attr") @?= 2
-cursorAttributeIs = (length $ cursor $.// Cu.attributeIs "attr" "y") @?= 1
-
-cursorDeep = do
-  (Cu.element "foo" &/ Cu.element "bar2" &// Cu.attribute "attr") cursor @?= ["y"]
-  (return &.// Cu.attribute "attr") cursor @?= ["x", "y"]
-  (cursor $.// Cu.attribute "attr") @?= ["x", "y"]
-  (cursor $/ Cu.element "bar2" &// Cu.attribute "attr") @?= ["y"]
-  (cursor $/ Cu.element "bar2" &/ Cu.element "baz2" >=> Cu.attribute "attr") @?= ["y"]
-  null (cursor $| Cu.element "foo") @?= False
-cursorForce = do
-  Cu.force DummyEx [] @?= (Nothing :: Maybe Integer)
-  Cu.force DummyEx [1] @?= Just (1 :: Int)
-  Cu.force DummyEx [1,2] @?= Just (1 :: Int)
-cursorForceM = do
-  Cu.forceM DummyEx [] @?= (Nothing :: Maybe Integer)
-  Cu.forceM DummyEx [Just 1, Nothing] @?= Just (1 :: Int)
-  Cu.forceM DummyEx [Nothing, Just (1 :: Int)] @?= Nothing
-
-data DummyEx = DummyEx
-    deriving (Show, Typeable)
-instance Exception DummyEx
-
-showEq :: (Show a, Show b) => Either a b -> Either a b -> Assertion
-showEq x y = show x @=? show y
-
-resolvedIdentifies =
-    Left (toException $ Res.UnresolvedEntityException $ Set.fromList ["foo", "bar", "baz"]) `showEq`
-    Res.parseLBS def
-    "<root attr='&bar;'>&foo; --- &baz; &foo;</root>"
-
-testHtmlEntities =
-    Res.parseLBS_ def
-        { P.psDecodeEntities = P.decodeHtmlEntities
-        } xml1 @=? Res.parseLBS_ def xml2
-  where
-    xml1 = "<root>&nbsp;</root>"
-    xml2 = "<root>&#160;</root>"
-
-resolvedAllGood =
-    D.parseLBS_ def xml @=?
-    Res.toXMLDocument (Res.parseLBS_ def xml)
-  where
-    xml = "<foo><bar/><baz/></foo>"
-
-resolvedMergeContent =
-    Res.documentRoot (Res.parseLBS_ def xml) @=?
-    Res.Element "foo" Map.empty [Res.NodeContent "bar&baz"]
-  where
-    xml = "<foo>bar&amp;baz</foo>"
-
-parseIgnoreBOM :: Assertion
-parseIgnoreBOM = do
-    either (const $ Left (1 :: Int)) Right (Res.parseText Res.def "\xfeef<foo/>") @?=
-        either (const $ Left (2 :: Int)) Right (Res.parseText Res.def "<foo/>")
-
-stripDuplicateAttributes :: Assertion
-stripDuplicateAttributes = do
-    "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo bar=\"baz\"/>" @=?
-        D.renderLBS def (Document (Prologue [] Nothing []) (Element "foo" [("bar", [ContentText "baz"]), ("bar", [ContentText "bin"])] []) [])
-    "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo x:bar=\"baz\" xmlns:x=\"namespace\"/>" @=?
-        D.renderLBS def (Document (Prologue [] Nothing []) (Element "foo"
-            [ ("x:bar", [ContentText "baz"])
-            , (Name "bar" (Just "namespace") (Just "x"), [ContentText "bin"])
-            ] []) [])
-
-testRenderComments :: Assertion
-testRenderComments =do
-    "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo><!--comment--></foo>"
-        @=? D.renderLBS def (Document (Prologue [] Nothing [])
-            (Element "foo" [] [NodeComment "comment"]) [])
-
-resolvedInline :: Assertion
-resolvedInline = do
-    Res.Document _ root _ <- return $ Res.parseLBS_ Res.def "<!DOCTYPE foo [<!ENTITY bar \"baz\">]><foo>&bar;</foo>"
-    root @?= Res.Element "foo" Map.empty [Res.NodeContent "baz"]
-    Res.Document _ root2 _ <- return $ Res.parseLBS_ Res.def "<!DOCTYPE foo [<!ENTITY bar \"baz\">]><foo bar='&bar;'/>"
-    root2 @?= Res.Element "foo" (Map.singleton "bar" "baz") []
-
-casePretty :: Assertion
-casePretty = do
-    let pretty = S.unlines
-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-            , "<!DOCTYPE foo>"
-            , "<foo bar=\"bar\" baz=\"baz\">"
-            , "    <foo"
-            , "      bar=\"bar\""
-            , "      baz=\"baz\""
-            , "      bin=\"bin\">"
-            , "        Hello World"
-            , "    </foo>"
-            , "    <foo/>"
-            , "    <?foo bar?>"
-            , "    <!-- foo bar baz bin -->"
-            , "    <bar>"
-            , "        bar content"
-            , "    </bar>"
-            , "</foo>"
-            ]
-        doctype = Res.Doctype "foo" Nothing
-        doc = Res.Document (Res.Prologue [] (Just doctype) []) root []
-        root = Res.Element "foo" (Map.fromList [("bar", "bar"), ("baz", "baz")])
-                [ Res.NodeElement $ Res.Element "foo" (Map.fromList [("bar", "bar"), ("baz", "baz"), ("bin", "bin")])
-                    [ Res.NodeContent "  Hello World\n\n"
-                    , Res.NodeContent "  "
-                    ]
-                , Res.NodeElement $ Res.Element "foo" Map.empty []
-                , Res.NodeInstruction $ Res.Instruction "foo" "bar"
-                , Res.NodeComment "foo bar\n\r\nbaz    \tbin "
-                , Res.NodeElement $ Res.Element "bar" Map.empty [Res.NodeContent "bar content"]
-                ]
-    pretty @=? S.concat (L.toChunks $ Res.renderLBS def { D.rsPretty = True } doc)
-
-caseTopLevelNamespace :: Assertion
-caseTopLevelNamespace = do
-    let lbs = S.concat
-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-            , "<foo xmlns:bar=\"baz\">"
-            , "<subfoo bar:bin=\"\"/>"
-            , "</foo>"
-            ]
-        rs = def { D.rsNamespaces = [("bar", "baz")] }
-        doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "foo" Map.empty
-                    [ Res.NodeElement
-                        $ Res.Element "subfoo" (Map.singleton "{baz}bin" "") []
-                    ])
-                []
-    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
-
-caseTopLevelNamespacePrefix :: Assertion
-caseTopLevelNamespacePrefix = do
-    let lbs = S.concat
-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-            , "<foo xmlns:bar=\"baz\">"
-            , "<subfoo bar:bin=\"\"/>"
-            , "</foo>"
-            ]
-        rs = def { D.rsNamespaces = [("bar", "baz")] }
-        doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "foo" Map.empty
-                    [ Res.NodeElement
-                        $ Res.Element "subfoo" (Map.fromList [(Name "bin" (Just "baz") (Just "bar"), "")]) []
-                    ])
-                []
-    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
-
-caseTLNConflict :: Assertion
-caseTLNConflict = do
-    let lbs = S.concat
-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-            , "<foo xmlns:bar=\"something\" bar:x=\"y\">"
-            , "<subfoo xmlns:bar_=\"baz\" bar_:bin=\"\"/>"
-            , "</foo>"
-            ]
-        rs = def { D.rsNamespaces = [("bar", "baz")] }
-        doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "foo" (Map.fromList [(Name "x" (Just "something") (Just "bar"), "y")])
-                    [ Res.NodeElement
-                        $ Res.Element "subfoo" (Map.fromList [(Name "bin" (Just "baz") (Just "bar"), "")]) []
-                    ])
-                []
-    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
-
-caseBlazeHtml :: Assertion
-caseBlazeHtml =
-    expected @=? str
-  where
-    str = renderMarkup $ toMarkup $ Res.Document (Res.Prologue [] Nothing []) root []
-    root :: Res.Element
-    root = Res.Element "html" Map.empty
-        [ Res.NodeElement $ Res.Element "head" Map.empty
-            [ Res.NodeElement $ Res.Element "title" Map.empty [Res.NodeContent "Test"]
-            , Res.NodeElement $ Res.Element "script" Map.empty
-                [Res.NodeContent "if (5 < 6 || 8 > 9) alert('Hello World!');"]
-            , Res.NodeElement $ Res.Element "{http://www.snoyman.com/xml2html}ie-cond" (Map.singleton "cond" "lt IE 7")
-                [Res.NodeElement $ Res.Element "link" (Map.singleton "href" "ie6.css") []]
-            , Res.NodeElement $ Res.Element "style" Map.empty
-                [Res.NodeContent "body > h1 { color: red }"]
-            ]
-        , Res.NodeElement $ Res.Element "body" Map.empty
-            [ Res.NodeElement $ Res.Element "h1" Map.empty [Res.NodeContent "Hello World!"]
-            ]
-        ]
-    expected :: String
-    expected = concat
-        [ "<!DOCTYPE HTML>\n"
-        , "<html><head><title>Test</title><script>if (5 < 6 || 8 > 9) alert('Hello World!');</script>"
-        , "<!--[if lt IE 7]><link href=\"ie6.css\" /><![endif]-->"
-        , "<style>body > h1 { color: red }</style>"
-        , "</head>"
-        , "<body><h1>Hello World!</h1></body></html>"
-        ]
-
-caseAttrReorder :: Assertion
-caseAttrReorder = do
-    let lbs = S.concat
-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-            , "<foo c=\"c\" b=\"b\" a=\"a\">"
-            , "<bar a=\"a\" b=\"b\" c=\"c\"/>"
-            , "</foo>"
-            ]
-        rs = def { Res.rsAttrOrder = \name' m ->
-                        case name' of
-                            "foo" -> reverse $ Map.toAscList m
-                            _     -> Map.toAscList m
-                 }
-        attrs = Map.fromList [("a", "a"), ("b", "b"), ("c", "c")]
-        doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "foo" attrs
-                    [ Res.NodeElement
-                        $ Res.Element "bar" attrs []
-                    ])
-                []
-    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
-
-caseOrderAttrs :: Assertion
-caseOrderAttrs = do
-    let lbs = S.concat
-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-            , "<foo c=\"c\" b=\"b\" a=\"a\">"
-            , "<bar a=\"a\" b=\"b\" c=\"c\"/>"
-            , "</foo>"
-            ]
-        rs = def { Res.rsAttrOrder = Res.orderAttrs
-                     [("foo", ["c", "b"])]
-                 }
-        attrs = Map.fromList [("a", "a"), ("b", "b"), ("c", "c")]
-        doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "foo" attrs
-                    [ Res.NodeElement
-                        $ Res.Element "bar" attrs []
-                    ])
-                []
-    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
-
-caseParseCdata :: Assertion
-caseParseCdata = do
-    let lbs = "<a><![CDATA[www.google.com]]></a>"
-        doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "a" Map.empty
-                    [ Res.NodeContent "www.google.com"
-                    ])
-                []
-    Res.parseLBS_ def lbs @?= doc
-
-caseRetainNamespaces :: Assertion
-caseRetainNamespaces = do
-    let lbs = "<foo xmlns:bar='baz'><bar:bin/><bin3 xmlns='bin4'></bin3></foo>"
-        doc = Res.parseLBS_ def { Res.psRetainNamespaces = True } lbs
-    doc `shouldBe` Res.Document
-        (Res.Prologue [] Nothing [])
-        (Res.Element
-            "foo"
-            (Map.singleton "xmlns:bar" "baz")
-            [ Res.NodeElement $ Res.Element
-                "{baz}bin"
-                Map.empty
-                []
-            , Res.NodeElement $ Res.Element
-                "{bin4}bin3"
-                (Map.singleton "xmlns" "bin4")
-                []
-            ])
-        []
-
-caseIso8859_1 :: Assertion
-caseIso8859_1 = do
-    let lbs = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><foo>\232</foo>"
-        doc = Res.parseLBS_ def lbs
-    doc `shouldBe` Res.Document
-        (Res.Prologue [] Nothing [])
-        (Res.Element
-            "foo"
-            Map.empty
-            [Res.NodeContent "\232"])
-        []
-
-caseRenderCDATA :: Assertion
-caseRenderCDATA = do
-    let doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "a" Map.empty
-                    [ Res.NodeContent "www.google.com"
-                    ])
-                []
-        withoutCDATA = Res.renderLBS def doc
-        withCDATA = Res.renderLBS (def { Res.rsUseCDATA = const True }) doc
-    withCDATA `shouldBe` "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a><![CDATA[www.google.com]]></a>"
-    withoutCDATA `shouldBe` "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a>www.google.com</a>"
-
-caseEscapesCDATA :: Assertion
-caseEscapesCDATA = do
-    let doc = Res.Document (Res.Prologue [] Nothing [])
-                (Res.Element "a" Map.empty
-                    [ Res.NodeContent "]]>"
-                    ])
-                []
-        result = Res.renderLBS (def { Res.rsUseCDATA = const True }) doc
-    result `shouldBe` "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a><![CDATA[]]]]><![CDATA[>]]></a>"
diff --git a/test/unit.hs b/test/unit.hs
new file mode 100644
--- /dev/null
+++ b/test/unit.hs
@@ -0,0 +1,955 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+
+import           Control.Exception            (Exception, toException)
+import           Control.Monad.IO.Class       (liftIO)
+import qualified Data.ByteString.Char8        as S
+import qualified Data.ByteString.Lazy.Char8   as L
+import           Data.Typeable                (Typeable)
+import           Data.XML.Types
+import           Test.Hspec
+import           Test.HUnit                   hiding (Test)
+import qualified Text.XML                     as Res
+import qualified Text.XML.Cursor              as Cu
+import           Text.XML.Stream.Parse        (def)
+import qualified Text.XML.Stream.Parse        as P
+import qualified Text.XML.Unresolved          as D
+
+import           Control.Monad
+import qualified Data.Set                     as Set
+import           Data.Text                    (Text)
+import qualified Data.Text                    as T
+import           Text.XML.Cursor              (($.//), ($/), ($//), ($|),
+                                               (&.//), (&/), (&//))
+
+import qualified Control.Monad.Trans.Resource as C
+import           Data.Conduit                 ((.|), runConduit, runConduitRes, ConduitT)
+import qualified Data.Conduit                 as C
+import qualified Data.Conduit.List            as CL
+import qualified Data.Map                     as Map
+import           Text.Blaze                   (toMarkup)
+import           Text.Blaze.Renderer.String   (renderMarkup)
+
+main :: IO ()
+main = hspec $ do
+    describe "XML parsing and rendering" $ do
+        it "is idempotent to parse and render a document" documentParseRender
+        it "has valid parser combinators" combinators
+        context "has working choose function" testChoose
+        it "has working many function" testMany
+        it "has working many' function" testMany'
+        it "has working manyYield function" testManyYield
+        it "has working takeContent function" testTakeContent
+        it "has working takeTree function" testTakeTree
+        it "has working takeAnyTreeContent function" testTakeAnyTreeContent
+        it "has working orE" testOrE
+        it "is idempotent to parse and pretty render a document" documentParsePrettyRender
+        it "ignores the BOM" parseIgnoreBOM
+        it "strips duplicated attributes" stripDuplicateAttributes
+        it "displays comments" testRenderComments
+        it "conduit parser" testConduitParser
+        it "can omit the XML declaration" omitXMLDeclaration
+        context "correctly parses hexadecimal entities" hexEntityParsing
+    describe "XML Cursors" $ do
+        it "has correct parent" cursorParent
+        it "has correct ancestor" cursorAncestor
+        it "has correct orSelf" cursorOrSelf
+        it "has correct preceding" cursorPreceding
+        it "has correct following" cursorFollowing
+        it "has correct precedingSibling" cursorPrecedingSib
+        it "has correct followingSibling" cursorFollowingSib
+        it "has correct descendant" cursorDescendant
+        it "has correct check" cursorCheck
+        it "has correct check with lists" cursorPredicate
+        it "has correct checkNode" cursorCheckNode
+        it "has correct checkElement" cursorCheckElement
+        it "has correct checkName" cursorCheckName
+        it "has correct anyElement" cursorAnyElement
+        it "has correct element" cursorElement
+        it "has correct laxElement" cursorLaxElement
+        it "has correct content" cursorContent
+        it "has correct attribute" cursorAttribute
+        it "has correct laxAttribute" cursorLaxAttribute
+        it "has correct &* and $* operators" cursorDeep
+        it "has correct force" cursorForce
+        it "has correct forceM" cursorForceM
+        it "has correct hasAttribute" cursorHasAttribute
+        it "has correct attributeIs" cursorAttributeIs
+    describe "resolved" $ do
+        it "identifies unresolved entities" resolvedIdentifies
+        it "decodeHtmlEntities" testHtmlEntities
+        it "works for resolvable entities" resolvedAllGood
+        it "merges adjacent content nodes" resolvedMergeContent
+        it "understands inline entity declarations" resolvedInline
+    describe "pretty" $ do
+        it "works" casePretty
+    describe "top level namespaces" $ do
+        it "works" caseTopLevelNamespace
+        it "works with prefix" caseTopLevelNamespacePrefix
+        it "handles conflicts" caseTLNConflict
+    describe "blaze-html instances" $ do
+        it "works" caseBlazeHtml
+    describe "attribute reordering" $ do
+        it "works" caseAttrReorder
+    describe "ordering attributes explicitly" $ do
+        it "works" caseOrderAttrs
+    it "parsing CDATA" caseParseCdata
+    it "retains namespaces when asked" caseRetainNamespaces
+    it "handles iso-8859-1" caseIso8859_1
+    it "renders CDATA when asked" caseRenderCDATA
+    it "escapes CDATA closing tag in CDATA" caseEscapesCDATA
+
+documentParseRender :: IO ()
+documentParseRender =
+    mapM_ go docs
+  where
+    go x = x @=? D.parseLBS_ def (D.renderLBS def x)
+    docs =
+        [ Document (Prologue [] Nothing [])
+                   (Element "foo" [] [])
+                   []
+        , D.parseLBS_ def
+            "<?xml version=\"1.0\"?><!DOCTYPE foo>\n<foo/>"
+        , D.parseLBS_ def
+            "<?xml version=\"1.0\"?><!DOCTYPE foo>\n<foo><nested>&ignore;</nested></foo>"
+        , D.parseLBS_ def
+            "<foo><![CDATA[this is some<CDATA content>]]></foo>"
+        , D.parseLBS_ def
+            "<foo bar='baz&amp;bin'/>"
+        , D.parseLBS_ def
+            "<foo><?instr this is a processing instruction?></foo>"
+        , D.parseLBS_ def
+            "<foo><!-- this is a comment --></foo>"
+        ]
+
+documentParsePrettyRender :: IO ()
+documentParsePrettyRender =
+    L.unpack (D.renderLBS def { D.rsPretty = True } (D.parseLBS_ def doc)) @?= L.unpack doc
+  where
+    doc = L.unlines
+        [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        , "<foo>"
+        , "    <?bar bar?>"
+        , "    text"
+        , "    <?bin bin?>"
+        , "</foo>"
+        ]
+
+combinators :: Assertion
+combinators = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tag' "hello" (P.requireAttr "world") $ \world -> do
+        liftIO $ world @?= "true"
+        P.force "need child1" $ P.tagNoAttr "{mynamespace}child1" $ return ()
+        P.force "need child2" $ P.tagNoAttr "child2" $ return ()
+        P.force "need child3" $ P.tagNoAttr "child3" $ do
+            x <- P.contentMaybe
+            liftIO $ x @?= Just "combine <all> &content"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello world='true'>"
+        , "<?this should be ignored?>"
+        , "<child1 xmlns='mynamespace'/>"
+        , "<!-- this should be ignored -->"
+        , "<child2>   </child2>"
+        , "<child3>combine &lt;all&gt; <![CDATA[&content]]></child3>\n"
+        , "</hello>"
+        ]
+
+testChoose :: Spec
+testChoose = do
+    it "can choose between elements"
+        testChooseEitherElem
+    it "can choose between elements and text, returning text"
+        testChooseElemOrTextIsText
+    it "can choose between elements and text, returning elements"
+        testChooseElemOrTextIsElem
+    it "can choose between text and elements, returning text"
+        testChooseTextOrElemIsText
+    it "can choose between text and elements, returning elements"
+        testChooseTextOrElemIsElem
+    it "can choose between text and elements, when the text is encoded"
+        testChooseElemOrTextIsEncoded
+    it "can choose between text and elements, when the text is encoded, NBSP"
+        testChooseElemOrTextIsEncodedNBSP
+    it "can choose between elements and text, when the text is whitespace"
+        testChooseElemOrTextIsWhiteSpace
+    it "can choose between text and elements, when the text is whitespace"
+        testChooseTextOrElemIsWhiteSpace
+    it "can choose between text and elements, when the whitespace is both literal and encoded"
+        testChooseElemOrTextIsChunkedText
+    it "can choose between text and elements, when the text is chunked the other way"
+        testChooseElemOrTextIsChunkedText2
+
+testChooseElemOrTextIsText :: Assertion
+testChooseElemOrTextIsText = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return "boom"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just " something "
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , " something "
+        , "</hello>"
+        ]
+
+testChooseElemOrTextIsEncoded :: Assertion
+testChooseElemOrTextIsEncoded = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return "boom"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just "\x20something\x20"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "&#x20;something&#x20;"
+        , "</hello>"
+        ]
+
+testChooseElemOrTextIsEncodedNBSP :: Assertion
+testChooseElemOrTextIsEncodedNBSP = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return "boom"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just "\160something\160"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "&#160;something&#160;"
+        , "</hello>"
+        ]
+
+
+testChooseElemOrTextIsWhiteSpace :: Assertion
+testChooseElemOrTextIsWhiteSpace = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return "boom"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just "\x20\x20\x20"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>   </hello>"
+        ]
+
+testChooseTextOrElemIsWhiteSpace :: Assertion
+testChooseTextOrElemIsWhiteSpace = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.contentMaybe
+            , P.tagNoAttr "failure" $ return "boom"
+            ]
+        liftIO $ x @?= Just "\x20\x20\x20"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>   </hello>"
+        ]
+
+testChooseElemOrTextIsChunkedText :: Assertion
+testChooseElemOrTextIsChunkedText = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return "boom"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just "\x20\x20\x20"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello> &#x20; </hello>"
+        ]
+
+testChooseElemOrTextIsChunkedText2 :: Assertion
+testChooseElemOrTextIsChunkedText2 = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return "boom"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just "\x20\x20\x20"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>&#x20; &#x20;</hello>"
+        ]
+
+testChooseElemOrTextIsElem :: Assertion
+testChooseElemOrTextIsElem = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "success" $ return "success"
+            , P.contentMaybe
+            ]
+        liftIO $ x @?= Just "success"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "</hello>"
+        ]
+
+testChooseTextOrElemIsText :: Assertion
+testChooseTextOrElemIsText = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.contentMaybe
+            , P.tagNoAttr "failure" $ return "boom"
+            ]
+        liftIO $ x @?= Just " something "
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , " something "
+        , "</hello>"
+        ]
+
+testChooseTextOrElemIsElem :: Assertion
+testChooseTextOrElemIsElem = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.contentMaybe
+            , P.tagNoAttr "success" $ return "success"
+            ]
+        liftIO $ x @?= Just "success"
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "</hello>"
+        ]
+
+testChooseEitherElem :: Assertion
+testChooseEitherElem = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.choose
+            [ P.tagNoAttr "failure" $ return 1
+            , P.tagNoAttr "success" $ return 2
+            ]
+        liftIO $ x @?= Just (2 :: Int)
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "</hello>"
+        ]
+
+testManyYield :: Assertion
+testManyYield = do
+    -- Basically the same as testMany, but consume the streamed result
+    result <- runConduitRes $
+        P.parseLBS def input .| helloParser
+        .| CL.consume
+    length result @?= 5
+  where
+    helloParser = void $ P.tagNoAttr "hello" $ P.manyYield successParser
+    successParser = P.tagNoAttr "success" $ return ()
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "</hello>"
+        ]
+
+testTakeContent :: Assertion
+testTakeContent = do
+    result <- runConduitRes $ P.parseLBS def input .| rootParser
+    result @?= Just
+      [ EventContent (ContentText "Hello world !")
+      ]
+  where
+    rootParser = P.tagNoAttr "root" $ void (P.takeContent >> P.takeContent) .| CL.consume
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<root>"
+        , "Hello world !"
+        , "</root>"
+        ]
+
+testTakeTree :: Assertion
+testTakeTree = do
+    result <- runConduitRes $ P.parseLBS def input .| rootParser
+    result @?=
+      [ EventBeginDocument
+      , EventBeginDoctype "foo" Nothing
+      , EventEndDoctype
+      , EventBeginElement "a" []
+      , EventBeginElement "em" []
+      , EventContent (ContentText "Hello world !")
+      , EventEndElement "em"
+      , EventEndElement "a"
+      ]
+  where
+    rootParser = void (P.takeTree "a" P.ignoreAttrs) .| CL.consume
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<a>"
+        , "<em>Hello world !</em>"
+        , "</a>"
+        , "<b>"
+        , "</b>"
+        ]
+
+testTakeAnyTreeContent :: Assertion
+testTakeAnyTreeContent = do
+    result <- runConduitRes $ P.parseLBS def input .| rootParser
+    result @?= Just
+      [ EventBeginElement "b" []
+      , EventContent (ContentText "Hello ")
+      , EventBeginElement "em" []
+      , EventContent (ContentText "world")
+      , EventEndElement "em"
+      , EventContent (ContentText " !")
+      , EventEndElement "b"
+      ]
+  where
+    rootParser = P.tagNoAttr "root" $ (P.takeAnyTreeContent >> void P.ignoreAnyTreeContent) .| CL.consume
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<root>"
+        , "<b>Hello <em>world</em> !</b> Welcome !"
+        , "</root>"
+        ]
+
+
+testMany :: Assertion
+testMany = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.many $ P.tagNoAttr "success" $ return ()
+        liftIO $ length x @?= 5
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "</hello>"
+        ]
+
+testMany' :: Assertion
+testMany' = runConduitRes $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.many' $ P.tagNoAttr "success" $ return ()
+        liftIO $ length x @?= 5
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "<success/>"
+        , "<success/>"
+        , "<foobar/>"
+        , "<success/>"
+        , "<foo><bar attr=\"1\">some content</bar></foo>"
+        , "<success/>"
+        , "</hello>"
+        ]
+
+testOrE :: IO ()
+testOrE = runConduitRes $ runConduit $ P.parseLBS def input .| do
+    P.force "need hello" $ P.tagNoAttr "hello" $ do
+        x <- P.tagNoAttr "failure" (return 1) `P.orE`
+             P.tagNoAttr "success" (return 2)
+        y <- P.tag' "success" (P.requireAttr "failure") (const $ return 1) `P.orE`
+             P.tag' "success" (P.requireAttr "success") (const $ return 2)
+        liftIO $ x @?= Just (2 :: Int)
+        liftIO $ y @?= Just (2 :: Int)
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<success/>"
+        , "<success success=\"0\"/>"
+        , "</hello>"
+        ]
+
+testConduitParser :: Assertion
+testConduitParser = do
+    x <-   runConduitRes
+         $ P.parseLBS def input
+        .| (P.force "need hello" $ P.tagNoAttr "hello" f)
+        .| CL.consume
+    liftIO $ x @?= [1, 1, 1]
+  where
+    input = L.concat
+        [ "<?xml version='1.0'?>"
+        , "<!DOCTYPE foo []>\n"
+        , "<hello>"
+        , "<item/>"
+        , "<item/>"
+        , "<item/>"
+        , "</hello>"
+        ]
+    f :: C.MonadThrow m => ConduitT Event Int m ()
+    f = do
+        ma <- P.tagNoAttr "item" (return 1)
+        maybe (return ()) (\a -> C.yield a >> f) ma
+
+omitXMLDeclaration :: Assertion
+omitXMLDeclaration = Res.renderLBS settings input @?= spec
+  where
+    settings = def { Res.rsXMLDeclaration = False }
+    input = Res.Document (Prologue [] Nothing [])
+              (Res.Element "foo" Map.empty [Res.NodeContent "bar"])
+              []
+    spec = "<foo>bar</foo>"
+
+hexEntityParsing :: Spec
+hexEntityParsing = do
+  it "rejects leading 0x" $
+    go "<foo>&#x0xff;</foo>" @?= Nothing
+  it "rejects leading 0X" $
+    go "<foo>&#x0Xff;</foo>" @?= Nothing
+  it "accepts lowercase hex digits" $
+    go "<foo>&#xff;</foo>" @?= Just (spec "\xff")
+  it "accepts uppercase hex digits" $
+    go "<foo>&#xFF;</foo>" @?= Just (spec "\xff")
+  --Note: this must be rejected, because, according to the XML spec, a
+  --legal EntityRef's entity matches Name, which can't start with a
+  --hash.
+  it "rejects trailing junk" $
+    go "<foo>&#xffhello;</foo>" @?= Nothing
+  --Some of these next tests are XML 1.0 specific (i.e., they would
+  --differ for XML 1.1), but approximately no-one uses XML 1.1.
+  it "rejects illegal character #x0" $
+    go "<foo>&#x0;</foo>" @?= Nothing
+  it "rejects illegal character #xFFFE" $
+    go "<foo>&#xFFFE;</foo>" @?= Nothing
+  it "rejects illegal character #xFFFF" $
+    go "<foo>&#xFFFF;</foo>" @?= Nothing
+  it "rejects illegal character #xD900" $
+    go "<foo>&#xD900;</foo>" @?= Nothing
+  it "rejects illegal character #xC" $
+    go "<foo>&#xC;</foo>" @?= Nothing
+  it "rejects illegal character #x1F" $
+    go "<foo>&#x1F;</foo>" @?= Nothing
+  it "accepts astral plane character" $
+    go "<foo>&#x1006ff;</foo>" @?= Just (spec "\x1006ff")
+  it "accepts custom character references" $
+    go' customSettings "<foo>&#xC;</foo>" @?= Just (spec "\xff")
+  where
+    spec content = Document (Prologue [] Nothing [])
+                    (Element "foo" [] [NodeContent (ContentText content)])
+                    []
+
+    go = either (const Nothing) Just . D.parseLBS def
+    go' settings = either (const Nothing) Just . D.parseLBS settings
+    customSettings = def { P.psDecodeIllegalCharacters = customDecoder }
+    customDecoder 12 = Just '\xff'
+    customDecoder _  = Nothing
+
+name :: [Cu.Cursor] -> [Text]
+name [] = []
+name (c:cs) = ($ name cs) $ case Cu.node c of
+                              Res.NodeElement e -> ((Res.nameLocalName $ Res.elementName e) :)
+                              _ -> id
+
+cursor :: Cu.Cursor
+cursor =
+    Cu.fromDocument $ Res.parseLBS_ def input
+  where
+    input = L.concat
+        [ "<foo attr=\"x\">"
+        ,    "<bar1/>"
+        ,    "<bar2>"
+        ,       "<baz1/>"
+        ,       "<baz2 attr=\"y\"/>"
+        ,       "<baz3>a</baz3>"
+        ,    "</bar2>"
+        ,    "<bar3>"
+        ,       "<bin1/>"
+        ,       "b"
+        ,       "<bin2/>"
+        ,       "<bin3/>"
+        ,    "</bar3>"
+        ,    "<Bar1 xmlns=\"http://example.com\" Attr=\"q\"/>"
+        , "</foo>"
+        ]
+
+bar2, baz2, bar3, bin2 :: Cu.Cursor
+bar2 = Cu.child cursor !! 1
+baz2 = Cu.child bar2 !! 1
+
+bar3 = Cu.child cursor !! 2
+bin2 = Cu.child bar3 !! 1
+
+cursorParent, cursorAncestor, cursorOrSelf, cursorPreceding, cursorFollowing,
+    cursorPrecedingSib, cursorFollowingSib, cursorDescendant, cursorCheck,
+    cursorPredicate, cursorCheckNode, cursorCheckElement, cursorCheckName,
+    cursorAnyElement, cursorElement, cursorLaxElement, cursorContent,
+    cursorAttribute, cursorLaxAttribute, cursorHasAttribute,
+    cursorAttributeIs, cursorDeep, cursorForce, cursorForceM,
+    resolvedIdentifies, resolvedAllGood, resolvedMergeContent,
+    testHtmlEntities
+    :: Assertion
+cursorParent = name (Cu.parent bar2) @?= ["foo"]
+cursorAncestor = name (Cu.ancestor baz2) @?= ["bar2", "foo"]
+cursorOrSelf = name (Cu.orSelf Cu.ancestor baz2) @?= ["baz2", "bar2", "foo"]
+cursorPreceding = do
+  name (Cu.preceding baz2) @?= ["baz1", "bar1"]
+  name (Cu.preceding bin2) @?= ["bin1", "baz3", "baz2", "baz1", "bar2", "bar1"]
+cursorFollowing = do
+  name (Cu.following baz2) @?= ["baz3", "bar3", "bin1", "bin2", "bin3", "Bar1"]
+  name (Cu.following bar2) @?= ["bar3", "bin1", "bin2", "bin3", "Bar1"]
+cursorPrecedingSib = name (Cu.precedingSibling baz2) @?= ["baz1"]
+cursorFollowingSib = name (Cu.followingSibling baz2) @?= ["baz3"]
+cursorDescendant = (name $ Cu.descendant cursor) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3 Bar1"
+cursorCheck = null (cursor $.// Cu.check (const False)) @?= True
+cursorPredicate = (name $ cursor $.// Cu.check Cu.descendant) @?= T.words "foo bar2 baz3 bar3"
+cursorCheckNode = (name $ cursor $// Cu.checkNode f) @?= T.words "bar1 bar2 bar3"
+    where f (Res.NodeElement e) = "bar" `T.isPrefixOf` Res.nameLocalName (Res.elementName e)
+          f _               = False
+cursorCheckElement = (name $ cursor $// Cu.checkElement f) @?= T.words "bar1 bar2 bar3"
+    where f e = "bar" `T.isPrefixOf` Res.nameLocalName (Res.elementName e)
+cursorCheckName = (name $ cursor $// Cu.checkName f) @?= T.words "bar1 bar2 bar3"
+    where f n = "bar" `T.isPrefixOf` nameLocalName n
+cursorAnyElement = (name $ cursor $// Cu.anyElement) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3 Bar1"
+cursorElement = (name $ cursor $// Cu.element "bar1") @?= ["bar1"]
+cursorLaxElement = (name $ cursor $// Cu.laxElement "bar1") @?= ["bar1", "Bar1"]
+cursorContent = do
+  Cu.content cursor @?= []
+  (cursor $.// Cu.content) @?= ["a", "b"]
+cursorAttribute = Cu.attribute "attr" cursor @?= ["x"]
+cursorLaxAttribute = (cursor $.// Cu.laxAttribute "Attr") @?= ["x", "y", "q"]
+
+cursorHasAttribute = (length $ cursor $.// Cu.hasAttribute "attr") @?= 2
+cursorAttributeIs = (length $ cursor $.// Cu.attributeIs "attr" "y") @?= 1
+
+cursorDeep = do
+  (Cu.element "foo" &/ Cu.element "bar2" &// Cu.attribute "attr") cursor @?= ["y"]
+  (return &.// Cu.attribute "attr") cursor @?= ["x", "y"]
+  (cursor $.// Cu.attribute "attr") @?= ["x", "y"]
+  (cursor $/ Cu.element "bar2" &// Cu.attribute "attr") @?= ["y"]
+  (cursor $/ Cu.element "bar2" &/ Cu.element "baz2" >=> Cu.attribute "attr") @?= ["y"]
+  null (cursor $| Cu.element "foo") @?= False
+cursorForce = do
+  Cu.force DummyEx [] @?= (Nothing :: Maybe Integer)
+  Cu.force DummyEx [1] @?= Just (1 :: Int)
+  Cu.force DummyEx [1,2] @?= Just (1 :: Int)
+cursorForceM = do
+  Cu.forceM DummyEx [] @?= (Nothing :: Maybe Integer)
+  Cu.forceM DummyEx [Just 1, Nothing] @?= Just (1 :: Int)
+  Cu.forceM DummyEx [Nothing, Just (1 :: Int)] @?= Nothing
+
+data DummyEx = DummyEx
+    deriving (Show, Typeable)
+instance Exception DummyEx
+
+showEq :: (Show a, Show b) => Either a b -> Either a b -> Assertion
+showEq x y = show x @=? show y
+
+resolvedIdentifies =
+    Left (toException $ Res.UnresolvedEntityException $ Set.fromList ["foo", "bar", "baz"]) `showEq`
+    Res.parseLBS def
+    "<root attr='&bar;'>&foo; --- &baz; &foo;</root>"
+
+testHtmlEntities =
+    Res.parseLBS_ def
+        { P.psDecodeEntities = P.decodeHtmlEntities
+        } xml1 @=? Res.parseLBS_ def xml2
+  where
+    xml1 = "<root>&nbsp;</root>"
+    xml2 = "<root>&#160;</root>"
+
+resolvedAllGood =
+    D.parseLBS_ def xml @=?
+    Res.toXMLDocument (Res.parseLBS_ def xml)
+  where
+    xml = "<foo><bar/><baz/></foo>"
+
+resolvedMergeContent =
+    Res.documentRoot (Res.parseLBS_ def xml) @=?
+    Res.Element "foo" Map.empty [Res.NodeContent "bar&baz"]
+  where
+    xml = "<foo>bar&amp;baz</foo>"
+
+parseIgnoreBOM :: Assertion
+parseIgnoreBOM = do
+    either (const $ Left (1 :: Int)) Right (Res.parseText Res.def "\xfeef<foo/>") @?=
+        either (const $ Left (2 :: Int)) Right (Res.parseText Res.def "<foo/>")
+
+stripDuplicateAttributes :: Assertion
+stripDuplicateAttributes = do
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo bar=\"baz\"/>" @=?
+        D.renderLBS def (Document (Prologue [] Nothing []) (Element "foo" [("bar", [ContentText "baz"]), ("bar", [ContentText "bin"])] []) [])
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo x:bar=\"baz\" xmlns:x=\"namespace\"/>" @=?
+        D.renderLBS def (Document (Prologue [] Nothing []) (Element "foo"
+            [ ("x:bar", [ContentText "baz"])
+            , (Name "bar" (Just "namespace") (Just "x"), [ContentText "bin"])
+            ] []) [])
+
+testRenderComments :: Assertion
+testRenderComments =do
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo><!--comment--></foo>"
+        @=? D.renderLBS def (Document (Prologue [] Nothing [])
+            (Element "foo" [] [NodeComment "comment"]) [])
+
+resolvedInline :: Assertion
+resolvedInline = do
+    Res.Document _ root _ <- return $ Res.parseLBS_ Res.def "<!DOCTYPE foo [<!ENTITY bar \"baz\">]><foo>&bar;</foo>"
+    root @?= Res.Element "foo" Map.empty [Res.NodeContent "baz"]
+    Res.Document _ root2 _ <- return $ Res.parseLBS_ Res.def "<!DOCTYPE foo [<!ENTITY bar \"baz\">]><foo bar='&bar;'/>"
+    root2 @?= Res.Element "foo" (Map.singleton "bar" "baz") []
+
+casePretty :: Assertion
+casePretty = do
+    let pretty = S.unlines
+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            , "<!DOCTYPE foo>"
+            , "<foo bar=\"bar\" baz=\"baz\">"
+            , "    <foo"
+            , "      bar=\"bar\""
+            , "      baz=\"baz\""
+            , "      bin=\"bin\">"
+            , "        Hello World"
+            , "    </foo>"
+            , "    <foo/>"
+            , "    <?foo bar?>"
+            , "    <!-- foo bar baz bin -->"
+            , "    <bar>"
+            , "        bar content"
+            , "    </bar>"
+            , "</foo>"
+            ]
+        doctype = Res.Doctype "foo" Nothing
+        doc = Res.Document (Res.Prologue [] (Just doctype) []) root []
+        root = Res.Element "foo" (Map.fromList [("bar", "bar"), ("baz", "baz")])
+                [ Res.NodeElement $ Res.Element "foo" (Map.fromList [("bar", "bar"), ("baz", "baz"), ("bin", "bin")])
+                    [ Res.NodeContent "  Hello World\n\n"
+                    , Res.NodeContent "  "
+                    ]
+                , Res.NodeElement $ Res.Element "foo" Map.empty []
+                , Res.NodeInstruction $ Res.Instruction "foo" "bar"
+                , Res.NodeComment "foo bar\n\r\nbaz    \tbin "
+                , Res.NodeElement $ Res.Element "bar" Map.empty [Res.NodeContent "bar content"]
+                ]
+    pretty @=? S.concat (L.toChunks $ Res.renderLBS def { D.rsPretty = True } doc)
+
+caseTopLevelNamespace :: Assertion
+caseTopLevelNamespace = do
+    let lbs = S.concat
+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            , "<foo xmlns:bar=\"baz\">"
+            , "<subfoo bar:bin=\"\"/>"
+            , "</foo>"
+            ]
+        rs = def { D.rsNamespaces = [("bar", "baz")] }
+        doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "foo" Map.empty
+                    [ Res.NodeElement
+                        $ Res.Element "subfoo" (Map.singleton "{baz}bin" "") []
+                    ])
+                []
+    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
+
+caseTopLevelNamespacePrefix :: Assertion
+caseTopLevelNamespacePrefix = do
+    let lbs = S.concat
+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            , "<foo xmlns:bar=\"baz\">"
+            , "<subfoo bar:bin=\"\"/>"
+            , "</foo>"
+            ]
+        rs = def { D.rsNamespaces = [("bar", "baz")] }
+        doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "foo" Map.empty
+                    [ Res.NodeElement
+                        $ Res.Element "subfoo" (Map.fromList [(Name "bin" (Just "baz") (Just "bar"), "")]) []
+                    ])
+                []
+    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
+
+caseTLNConflict :: Assertion
+caseTLNConflict = do
+    let lbs = S.concat
+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            , "<foo xmlns:bar=\"something\" bar:x=\"y\">"
+            , "<subfoo xmlns:bar_=\"baz\" bar_:bin=\"\"/>"
+            , "</foo>"
+            ]
+        rs = def { D.rsNamespaces = [("bar", "baz")] }
+        doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "foo" (Map.fromList [(Name "x" (Just "something") (Just "bar"), "y")])
+                    [ Res.NodeElement
+                        $ Res.Element "subfoo" (Map.fromList [(Name "bin" (Just "baz") (Just "bar"), "")]) []
+                    ])
+                []
+    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
+
+caseBlazeHtml :: Assertion
+caseBlazeHtml =
+    expected @=? str
+  where
+    str = renderMarkup $ toMarkup $ Res.Document (Res.Prologue [] Nothing []) root []
+    root :: Res.Element
+    root = Res.Element "html" Map.empty
+        [ Res.NodeElement $ Res.Element "head" Map.empty
+            [ Res.NodeElement $ Res.Element "title" Map.empty [Res.NodeContent "Test"]
+            , Res.NodeElement $ Res.Element "script" Map.empty
+                [Res.NodeContent "if (5 < 6 || 8 > 9) alert('Hello World!');"]
+            , Res.NodeElement $ Res.Element "{http://www.snoyman.com/xml2html}ie-cond" (Map.singleton "cond" "lt IE 7")
+                [Res.NodeElement $ Res.Element "link" (Map.singleton "href" "ie6.css") []]
+            , Res.NodeElement $ Res.Element "style" Map.empty
+                [Res.NodeContent "body > h1 { color: red }"]
+            ]
+        , Res.NodeElement $ Res.Element "body" Map.empty
+            [ Res.NodeElement $ Res.Element "h1" Map.empty [Res.NodeContent "Hello World!"]
+            ]
+        ]
+    expected :: String
+    expected = concat
+        [ "<!DOCTYPE HTML>\n"
+        , "<html><head><title>Test</title><script>if (5 < 6 || 8 > 9) alert('Hello World!');</script>"
+        , "<!--[if lt IE 7]><link href=\"ie6.css\" /><![endif]-->"
+        , "<style>body > h1 { color: red }</style>"
+        , "</head>"
+        , "<body><h1>Hello World!</h1></body></html>"
+        ]
+
+caseAttrReorder :: Assertion
+caseAttrReorder = do
+    let lbs = S.concat
+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            , "<foo c=\"c\" b=\"b\" a=\"a\">"
+            , "<bar a=\"a\" b=\"b\" c=\"c\"/>"
+            , "</foo>"
+            ]
+        rs = def { Res.rsAttrOrder = \name' m ->
+                        case name' of
+                            "foo" -> reverse $ Map.toAscList m
+                            _     -> Map.toAscList m
+                 }
+        attrs = Map.fromList [("a", "a"), ("b", "b"), ("c", "c")]
+        doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "foo" attrs
+                    [ Res.NodeElement
+                        $ Res.Element "bar" attrs []
+                    ])
+                []
+    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
+
+caseOrderAttrs :: Assertion
+caseOrderAttrs = do
+    let lbs = S.concat
+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            , "<foo c=\"c\" b=\"b\" a=\"a\">"
+            , "<bar a=\"a\" b=\"b\" c=\"c\"/>"
+            , "</foo>"
+            ]
+        rs = def { Res.rsAttrOrder = Res.orderAttrs
+                     [("foo", ["c", "b"])]
+                 }
+        attrs = Map.fromList [("a", "a"), ("b", "b"), ("c", "c")]
+        doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "foo" attrs
+                    [ Res.NodeElement
+                        $ Res.Element "bar" attrs []
+                    ])
+                []
+    lbs @=? S.concat (L.toChunks $ Res.renderLBS rs doc)
+
+caseParseCdata :: Assertion
+caseParseCdata = do
+    let lbs = "<a><![CDATA[www.google.com]]></a>"
+        doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "a" Map.empty
+                    [ Res.NodeContent "www.google.com"
+                    ])
+                []
+    Res.parseLBS_ def lbs @?= doc
+
+caseRetainNamespaces :: Assertion
+caseRetainNamespaces = do
+    let lbs = "<foo xmlns:bar='baz'><bar:bin/><bin3 xmlns='bin4'></bin3></foo>"
+        doc = Res.parseLBS_ def { Res.psRetainNamespaces = True } lbs
+    doc `shouldBe` Res.Document
+        (Res.Prologue [] Nothing [])
+        (Res.Element
+            "foo"
+            (Map.singleton "xmlns:bar" "baz")
+            [ Res.NodeElement $ Res.Element
+                "{baz}bin"
+                Map.empty
+                []
+            , Res.NodeElement $ Res.Element
+                "{bin4}bin3"
+                (Map.singleton "xmlns" "bin4")
+                []
+            ])
+        []
+
+caseIso8859_1 :: Assertion
+caseIso8859_1 = do
+    let lbs = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><foo>\232</foo>"
+        doc = Res.parseLBS_ def lbs
+    doc `shouldBe` Res.Document
+        (Res.Prologue [] Nothing [])
+        (Res.Element
+            "foo"
+            Map.empty
+            [Res.NodeContent "\232"])
+        []
+
+caseRenderCDATA :: Assertion
+caseRenderCDATA = do
+    let doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "a" Map.empty
+                    [ Res.NodeContent "www.google.com"
+                    ])
+                []
+        withoutCDATA = Res.renderLBS def doc
+        withCDATA = Res.renderLBS (def { Res.rsUseCDATA = const True }) doc
+    withCDATA `shouldBe` "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a><![CDATA[www.google.com]]></a>"
+    withoutCDATA `shouldBe` "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a>www.google.com</a>"
+
+caseEscapesCDATA :: Assertion
+caseEscapesCDATA = do
+    let doc = Res.Document (Res.Prologue [] Nothing [])
+                (Res.Element "a" Map.empty
+                    [ Res.NodeContent "]]>"
+                    ])
+                []
+        result = Res.renderLBS (def { Res.rsUseCDATA = const True }) doc
+    result `shouldBe` "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a><![CDATA[]]]]><![CDATA[>]]></a>"
diff --git a/xml-conduit.cabal b/xml-conduit.cabal
--- a/xml-conduit.cabal
+++ b/xml-conduit.cabal
@@ -1,5 +1,5 @@
 name:            xml-conduit
-version:         1.8.0
+version:         1.8.0.1
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>, Aristid Breitkreuz <aristidb@googlemail.com>
@@ -11,8 +11,7 @@
 cabal-version:   >= 1.8
 build-type:      Simple
 homepage:        http://github.com/snoyberg/xml
-extra-source-files: test/main.hs
-                    README.md
+extra-source-files: README.md
                     ChangeLog.md
 
 library
@@ -27,7 +26,6 @@
                    , attoparsec                >= 0.10
                    , transformers              >= 0.2      && < 0.6
                    , data-default-class
-                   , monad-control             >= 0.3      && < 1.1
                    , blaze-markup              >= 0.5
                    , blaze-html                >= 0.5
                    , deepseq                   >= 1.1.0.0
@@ -40,9 +38,9 @@
     other-modules:   Text.XML.Stream.Token
     ghc-options:     -Wall
 
-test-suite test
+test-suite unit
     type: exitcode-stdio-1.0
-    main-is: main.hs
+    main-is: unit.hs
     hs-source-dirs: test
     build-depends:          base
                           , containers
@@ -56,6 +54,13 @@
                           , conduit
                           , blaze-markup
                           , resourcet
+
+test-suite doctest
+    type: exitcode-stdio-1.0
+    main-is: doctest.hs
+    hs-source-dirs: test
+    build-depends:          base
+                          , doctest >= 0.8
 
 source-repository head
   type:     git
