Hello community,

here is the log from the commit of package ghc-email-validate for 
openSUSE:Factory checked in at 2017-08-31 20:46:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ghc-email-validate (Old)
 and      /work/SRC/openSUSE:Factory/.ghc-email-validate.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ghc-email-validate"

Thu Aug 31 20:46:52 2017 rev:4 rq:513234 version:2.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/ghc-email-validate/ghc-email-validate.changes    
2017-07-11 08:26:12.156424440 +0200
+++ 
/work/SRC/openSUSE:Factory/.ghc-email-validate.new/ghc-email-validate.changes   
    2017-08-31 20:46:53.372081440 +0200
@@ -1,0 +2,5 @@
+Thu Jul 27 14:07:12 UTC 2017 - [email protected]
+
+- Update to version 2.3.
+
+-------------------------------------------------------------------

Old:
----
  email-validate-2.2.1.1.tar.gz

New:
----
  email-validate-2.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ghc-email-validate.spec ++++++
--- /var/tmp/diff_new_pack.4ua4mI/_old  2017-08-31 20:46:54.183967480 +0200
+++ /var/tmp/diff_new_pack.4ua4mI/_new  2017-08-31 20:46:54.187966919 +0200
@@ -19,7 +19,7 @@
 %global pkg_name email-validate
 %bcond_with tests
 Name:           ghc-%{pkg_name}
-Version:        2.2.1.1
+Version:        2.3
 Release:        0
 Summary:        Email address validation
 License:        BSD-3-Clause
@@ -30,13 +30,12 @@
 BuildRequires:  ghc-attoparsec-devel
 BuildRequires:  ghc-bytestring-devel
 BuildRequires:  ghc-rpm-macros
+BuildRequires:  ghc-template-haskell-devel
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 %if %{with tests}
-BuildRequires:  ghc-HUnit-devel
 BuildRequires:  ghc-QuickCheck-devel
-BuildRequires:  ghc-test-framework-devel
-BuildRequires:  ghc-test-framework-hunit-devel
-BuildRequires:  ghc-test-framework-quickcheck2-devel
+BuildRequires:  ghc-doctest-devel
+BuildRequires:  ghc-hspec-devel
 %endif
 
 %description

++++++ email-validate-2.2.1.1.tar.gz -> email-validate-2.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/email-validate-2.2.1.1/email-validate.cabal 
new/email-validate-2.3/email-validate.cabal
--- old/email-validate-2.2.1.1/email-validate.cabal     2017-06-26 
01:32:54.000000000 +0200
+++ new/email-validate-2.3/email-validate.cabal 2017-06-26 08:49:19.000000000 
+0200
@@ -1,5 +1,5 @@
 name: email-validate
-version: 2.2.1.1
+version: 2.3
 cabal-version: >=1.10
 build-type: Simple
 license: BSD3
@@ -20,17 +20,18 @@
 source-repository this
     type: git
     location: git://github.com/Porges/email-validate-hs.git
-    tag: v2.2.1.1
+    tag: v2.3
 
 library
     exposed-modules:
-        Text.Domain.Parser
+        Text.Email.QuasiQuotation
         Text.Email.Validate
         Text.Email.Parser
     build-depends:
         base >=4.4 && <5,
         attoparsec >=0.10.0 && <0.14,
-        bytestring >=0.9 && <0.11
+        bytestring >=0.9 && <0.11,
+        template-haskell >=2.11.1.0 && <2.12
     default-language: Haskell2010
     hs-source-dirs: src
     ghc-options: -Wall
@@ -39,13 +40,20 @@
     type: exitcode-stdio-1.0
     main-is: Main.hs
     build-depends:
+        email-validate ==2.3.*,
         base ==4.*,
-        HUnit >=1.2 && <2,
-        email-validate >=2.2.1.1 && <2.3,
+        hspec >=2.4.3 && <2.5,
         QuickCheck >=2.4 && <2.11,
-        test-framework >=0.4.1 && <0.9,
-        test-framework-quickcheck2 >=0.3.0.4 && <0.4,
-        test-framework-hunit >=0.3.0.2 && <0.4,
         bytestring >=0.9 && <0.11
     default-language: Haskell2010
     hs-source-dirs: tests
+    ghc-options: -threaded
+test-suite doctests
+    type: exitcode-stdio-1.0
+    main-is: doctests.hs
+    build-depends:
+        base >=4.9.1.0 && <4.10,
+        doctest >=0.8 && <0.12
+    default-language: Haskell2010
+    hs-source-dirs: tests
+    ghc-options: -threaded
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/email-validate-2.2.1.1/src/Text/Domain/Parser.hs 
new/email-validate-2.3/src/Text/Domain/Parser.hs
--- old/email-validate-2.2.1.1/src/Text/Domain/Parser.hs        2017-06-26 
01:25:25.000000000 +0200
+++ new/email-validate-2.3/src/Text/Domain/Parser.hs    1970-01-01 
01:00:00.000000000 +0100
@@ -1,41 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}
-
-module Text.Domain.Parser
-    ( domainParser
-    )
-where
-
-import           Control.Applicative
-import           Control.Monad (guard)
-import           Data.Attoparsec.ByteString.Char8
-import qualified Data.ByteString.Char8 as BS
-import           Data.ByteString (ByteString)
-
-domainParser :: Parser ByteString
-domainParser = do
-    domain <- fst <$> match (label `sepBy1` char '.' >> optional (char '.'))
-
-    -- trim off the excess '.' if it is there
-    let trimmed =
-            case BS.last domain of
-                '.' -> BS.init domain
-                _ -> domain
-
-    -- domain name must be no greater than 253 chars
-    guard (BS.length trimmed <= 253)
-    return trimmed
-
-label :: Parser ByteString
-label = do
-    lbl <- fst <$> match (alphaNum >> skipWhile isAlphaNumHyphen)
-
-    -- label must be no greater than 63 chars and cannot end with '-'
-    guard (BS.length lbl <= 63 && BS.last lbl /= '-')
-    return lbl
-
-alphaNum :: Parser Char
-alphaNum = satisfy isAlphaNum
-    where isAlphaNum x = isDigit x || isAlpha_ascii x
-
-isAlphaNumHyphen :: Char -> Bool
-isAlphaNumHyphen x = isDigit x || isAlpha_ascii x || x == '-'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/email-validate-2.2.1.1/src/Text/Email/Parser.hs 
new/email-validate-2.3/src/Text/Email/Parser.hs
--- old/email-validate-2.2.1.1/src/Text/Email/Parser.hs 2017-06-26 
01:25:25.000000000 +0200
+++ new/email-validate-2.3/src/Text/Email/Parser.hs     2017-06-26 
08:45:38.000000000 +0200
@@ -11,7 +11,7 @@
 where
 
 import           Control.Applicative
-import           Control.Monad (void)
+import           Control.Monad (guard, void, when)
 import           Data.Attoparsec.ByteString.Char8
 import           Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as BS
@@ -19,8 +19,6 @@
 import           GHC.Generics (Generic)
 import qualified Text.Read as Read
 
-import           Text.Domain.Parser (domainParser)
-
 -- | Represents an email address.
 data EmailAddress = EmailAddress ByteString ByteString
     deriving (Eq, Ord, Data, Typeable, Generic)
@@ -57,7 +55,19 @@
 
 -- | A parser for email addresses.
 addrSpec :: Parser EmailAddress
-addrSpec = unsafeEmailAddress <$> local <* char '@' <*> domain
+addrSpec = do
+    l <- local
+
+    -- Maximum length of local-part is 64, per RFC3696
+    when (BS.length l > 64) (fail "local-part of email is too long (more than 
64 octets)")
+
+    _ <- char '@' <?> "at sign"
+    d <- domain
+
+    -- Maximum length is 254, per Erratum 1690 on RFC3696
+    when (BS.length l + BS.length d + 1 > 254) (fail "email address is too 
long (more than 254 octets)")
+
+    return (unsafeEmailAddress l d)
 
 local :: Parser ByteString
 local = dottedAtoms
@@ -67,10 +77,28 @@
 
 domainName :: Parser ByteString
 domainName = do
-    raw <- BS.append <$> dottedAtoms <*> option BS.empty (string (BS.pack "."))
-    case parseOnly (domainParser <* endOfInput) raw of
-        Left err -> fail err
-        Right result -> return result
+    parsedDomain <- BS.intercalate (BS.singleton '.') <$>
+        domainLabel `sepBy1` char '.' <* optional (char '.')
+
+    -- Domain name must be no greater than 253 chars, per RFC1035
+    guard (BS.length parsedDomain <= 253)
+    return parsedDomain
+
+domainLabel :: Parser ByteString
+domainLabel = do
+    content <- between1 (optional cfws) (fst <$> match (alphaNum >> skipWhile 
isAlphaNumHyphen))
+
+    -- Per RFC1035:
+    -- label must be no greater than 63 chars and cannot end with '-'
+    -- (we already enforced that it does not start with '-')
+    guard (BS.length content <= 63 && BS.last content /= '-')
+    return content
+
+alphaNum :: Parser Char
+alphaNum = satisfy isAlphaNum
+
+isAlphaNumHyphen :: Char -> Bool
+isAlphaNumHyphen x = isDigit x || isAlpha_ascii x || x == '-'
 
 dottedAtoms :: Parser ByteString
 dottedAtoms = BS.intercalate (BS.singleton '.') <$>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/email-validate-2.2.1.1/src/Text/Email/QuasiQuotation.hs 
new/email-validate-2.3/src/Text/Email/QuasiQuotation.hs
--- old/email-validate-2.2.1.1/src/Text/Email/QuasiQuotation.hs 1970-01-01 
01:00:00.000000000 +0100
+++ new/email-validate-2.3/src/Text/Email/QuasiQuotation.hs     2017-06-26 
08:45:38.000000000 +0200
@@ -0,0 +1,42 @@
+{-# LANGUAGE CPP #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TemplateHaskellQuotes #-}
+#else
+{-# LANGUAGE TemplateHaskell #-}
+#endif
+
+module Text.Email.QuasiQuotation
+    ( email
+    ) where
+
+import qualified Data.ByteString.Char8 as BS8
+
+import Language.Haskell.TH.Quote (QuasiQuoter(..))
+
+import Text.Email.Validate (validate, localPart, domainPart, 
unsafeEmailAddress)
+
+-- | A QuasiQuoter for email addresses. 
+--
+-- Use it like this:
+-- 
+-- >>> :set -XQuasiQuotes
+-- >>> [email|[email protected]|]
+-- "[email protected]"
+email :: QuasiQuoter
+email = QuasiQuoter
+    { quoteExp = quoteEmail emailToExp
+    , quotePat = error "email is not supported as a pattern"
+    , quoteDec = error "email is not supported at top-level"
+    , quoteType = error "email is not supported as a type"
+    }
+    where
+
+    quoteEmail p s = 
+        case validate (BS8.pack s) of 
+            Left err -> error ("Invalid quasi-quoted email address: " ++ err) 
+            Right e -> p e
+
+    emailToExp e = 
+        let lp = BS8.unpack (localPart e) in
+        let dp = BS8.unpack (domainPart e) in
+        [| unsafeEmailAddress (BS8.pack lp) (BS8.pack dp) |]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/email-validate-2.2.1.1/src/Text/Email/Validate.hs 
new/email-validate-2.3/src/Text/Email/Validate.hs
--- old/email-validate-2.2.1.1/src/Text/Email/Validate.hs       2017-06-26 
01:25:25.000000000 +0200
+++ new/email-validate-2.3/src/Text/Email/Validate.hs   2017-06-26 
08:45:38.000000000 +0200
@@ -1,21 +1,32 @@
 module Text.Email.Validate
-        ( isValid
-        , validate
-        , emailAddress
-        , canonicalizeEmail
-        , EmailAddress -- re-exported
-        , unsafeEmailAddress
-        , localPart
-        , domainPart
-        , toByteString
-        )
+    ( isValid
+    , validate
+    , emailAddress
+    , canonicalizeEmail
+
+    -- Re-exports:
+    , EmailAddress
+    , domainPart
+    , localPart
+    , toByteString
+    , unsafeEmailAddress
+    )
 where
 
-import           Data.Attoparsec.ByteString (endOfInput, parseOnly)
-import           Data.ByteString            (ByteString)
+import Data.Attoparsec.ByteString (endOfInput, parseOnly)
+import Data.ByteString (ByteString)
 
-import           Text.Email.Parser          (EmailAddress, addrSpec, 
domainPart,
-                                             localPart, toByteString, 
unsafeEmailAddress)
+import Text.Email.Parser
+    ( EmailAddress
+    , addrSpec
+    , domainPart
+    , localPart
+    , toByteString
+    , unsafeEmailAddress)
+
+-- $setup
+-- This is required for all examples:
+-- >>> :set -XOverloadedStrings
 
 -- | Smart constructor for an email address
 emailAddress :: ByteString -> Maybe EmailAddress
@@ -23,6 +34,10 @@
 
 -- | Checks that an email is valid and returns a version of it
 --   where comments and whitespace have been removed.
+--
+-- Example:
+-- >>> canonicalizeEmail "spaces. are. [email protected]"
+-- Just "[email protected]"
 canonicalizeEmail :: ByteString -> Maybe ByteString
 canonicalizeEmail = fmap toByteString . emailAddress
 
@@ -33,5 +48,12 @@
 
 -- | If you want to find out *why* a particular string is not
 --   an email address, use this.
+--
+-- Examples:
+-- >>> validate "[email protected]"
+-- Right "[email protected]"
+-- >>> validate "not.good"
+-- Left "at sign > @: not enough input"
 validate :: ByteString -> Either String EmailAddress
 validate = parseOnly (addrSpec >>= \r -> endOfInput >> return r)
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/email-validate-2.2.1.1/tests/Main.hs 
new/email-validate-2.3/tests/Main.hs
--- old/email-validate-2.2.1.1/tests/Main.hs    2017-06-26 01:25:25.000000000 
+0200
+++ new/email-validate-2.3/tests/Main.hs        2017-06-26 08:45:39.000000000 
+0200
@@ -1,19 +1,21 @@
 {-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Main where
 
+import Control.Exception (evaluate)
+import Control.Monad (forM_)
 import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as BS
-import Data.Maybe (Maybe(..), isNothing)
+import Data.List (isInfixOf)
+import Data.Maybe (Maybe(..), isNothing, fromJust)
+import Data.Monoid ((<>))
 
-import Test.Framework as TF (defaultMain, testGroup, Test)
-import Test.Framework.Providers.HUnit (testCase)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-
-import Test.HUnit ((@?=), assert)
-import Test.QuickCheck (Arbitrary(..), suchThat)
+import Test.Hspec (hspec, context, describe, errorCall, it, parallel, 
shouldBe, shouldSatisfy)
+import Test.QuickCheck (Arbitrary(..), suchThat, property)
 
+import Text.Email.QuasiQuotation (email)
 import Text.Email.Validate
     ( EmailAddress
     , canonicalizeEmail
@@ -27,56 +29,78 @@
     )
 
 main :: IO ()
-main = defaultMain testGroups
-
-{- Tests -}
+main = hspec $ parallel $ do
 
-testGroups :: [Test]
-testGroups =
-    [ showAndRead
-    , canonicalization
-    , exampleTests
-    , specificFailures
-    , simpleAccessors
-    ]
+    showAndRead
+    canonicalization
+    exampleTests
+    specificFailures
+    simpleAccessors
+    quasiQuotationTests
 
 canonicalization =
-    testGroup "QuickCheck Text.Email.Validate"
-    [ testProperty "doubleCanonicalize" prop_doubleCanonicalize
-    ]
+    describe "emailAddress" $ do
+        it "is idempotent" $
+            property prop_doubleCanonicalize
 
 exampleTests =
-    testGroup "Unit tests Text.Email.Validate" (concatMap exampleTest examples)
-    where
-    exampleTest Example{example, valid, reason} =
-        if valid
-        then
-            [ testCase ("Ensure valid " ++ name) (assert (isValid example))
-            , testCase ("doubleCanonicalize test " ++ name) (assert (case 
emailAddress example of { Just ok -> prop_doubleCanonicalize ok; Nothing -> 
False }))
-            ]
-        else
-            [ testCase ("Ensure invalid " ++ name) (assert (not (isValid 
example))) ]
-
-        where name = show example ++ (if null reason then "" else " (" ++ 
reason ++ ")")
+    describe "Examples" $ do
+        forM_ examples $ \Example{example, exampleValid, exampleWhy, 
errorContains} -> do
+            context (show example ++ (if null exampleWhy then "" else " (" ++ 
exampleWhy ++ ")")) $ do
+                if exampleValid
+                then do
+                    it "should be valid" $ 
+                        isValid example `shouldBe` True
+
+                    it "passes double-canonicalization test" $
+                        prop_doubleCanonicalize (fromJust (emailAddress 
example))
+
+                else do
+                    it "should be invalid" $
+                        isValid example `shouldBe` False
+
+                    case (errorContains, validate example) of
+                        (Just err, Left errMessage) ->
+                            it "should have correct error message" $
+                                errMessage `shouldSatisfy` (err `isInfixOf`)
+                        (_, _) -> return ()
 
 showAndRead =
-    testGroup "EmailAddress Show/Read instances"
-    [ testProperty "showLikeByteString" prop_showLikeByteString
-    , testProperty "showAndReadBackWithoutQuoteFails" 
prop_showAndReadBackWithoutQuoteFails
-    , testProperty "showAndReadBack" prop_showAndReadBack
-    ]
+    describe "show/read instances" $ do
 
-specificFailures =
-    testGroup "Specifics"
-    [ testCase "Issue #12" (let (Right em) = validate (BS.pack "\"\"@1") in em 
@?= read (show em))
-    , testCase "Check canonicalization of trailing dot" (canonicalizeEmail 
"[email protected]." @?= Just "[email protected]")
-    ]
+        it "can roundtrip" $
+            property prop_showAndReadBack
 
-simpleAccessors = 
-    testGroup "Simple accessors"
-    [ testCase "local-part" (localPart (unsafeEmailAddress "local" undefined) 
@?= "local")
-    , testCase "domain-part" (domainPart (unsafeEmailAddress undefined 
"domain") @?= "domain")
-    ]
+        it "shows in the same way as ByteString" $
+            property prop_showLikeByteString
+
+        it "should fail if read back without a quote" $
+            property prop_showAndReadBackWithoutQuoteFails
+
+specificFailures = do
+    describe "GitHub issue #12" $ do
+        it "is fixed" $
+            let (Right em) = validate (BS.pack "\"\"@1") in
+            em `shouldBe` read (show em)
+
+    describe "Trailing dot" $ do
+        it "is canonicalized" $ 
+            canonicalizeEmail "[email protected]." `shouldBe` Just "[email protected]"
+
+simpleAccessors = do
+    describe "localPart" $
+        it "extracts local part" $
+            localPart (unsafeEmailAddress "local" undefined) `shouldBe` "local"
+
+
+    describe "domainPart" $
+        it "extracts domain part" $
+            domainPart (unsafeEmailAddress undefined "domain") `shouldBe` 
"domain"
+
+quasiQuotationTests =
+    describe "QuasiQuoter" $ do
+        it "works as expected" $
+            [email|[email protected]|] `shouldBe` unsafeEmailAddress "local" 
"domain.com"
 
 instance Arbitrary ByteString where
     arbitrary = fmap BS.pack arbitrary
@@ -115,241 +139,262 @@
 
 {- Examples -}
 
-data Example = Example { example :: ByteString, valid :: Bool, reason :: 
String }
+data Example = Example
+    { example :: ByteString
+    , exampleValid :: Bool
+    , exampleWhy :: String
+    , errorContains :: Maybe String }
+
+valid, invalid :: ByteString -> Example
+valid e = Example e True "" Nothing
+invalid e = Example e False "" Nothing
+
+why :: Example -> String -> Example
+why ex str = ex { exampleWhy = str }
+
+errorShouldContain :: Example -> String -> Example
+errorShouldContain ex str = ex { errorContains = Just str }
+
 
 examples :: [Example]
 examples =
-    map (\(e, v, r) -> Example e v r)
-    [ ("[email protected]", True, "")
-    , ("[email protected].", True, "Dot allowed on end of domain")
-    , ("local@exam_ple.com", False, "Underscore not permitted in domain")
-    , 
("1234567890123456789012345678901234567890123456789012345678901...@example.com",
 True, "")
-    , ("\"first last\"@example.com", True, "")
-    , ("\"first\\\"last\"@example.com", True, "")
-    , ("first\\@[email protected]", False, "Escaping can only happen within a 
quoted string")
-    , ("\"first@last\"@example.com", True, "")
-    , ("\"first\\\\last\"@example.com", True, "")
-    , 
("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23",
 True, "Max length is 253")
-    , 
("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23.",
 True, "Trailing dot doesn't increase length")
-    , 
("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x234",
 False, "Max length is 253")
-    , 
("123456789012345678901234567890123456789012345678901234567...@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.123456789012345678901234567890123456789012345678901234567890123.example.com",
 True, "")
-    , ("first.last@[12.34.56.78]", True, "")
-    , ("first.last@[IPv6:::12.34.56.78]", True, "")
-    , ("first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]", True, "")
-    , ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]", True, "")
-    , ("first.last@[IPv6:::1111:2222:3333:4444:5555:6666]", True, "")
-    , ("first.last@[IPv6:1111:2222:3333::4444:5555:6666]", True, "")
-    , ("first.last@[IPv6:1111:2222:3333:4444:5555:6666::]", True, "")
-    , ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]", True, "")
-    , 
("first.l...@x23456789012345678901234567890123456789012345678901234567890123.example.com",
 True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("first.last", False, "No @")
-    , ("[email protected]", False, "Local part starts with a dot")
-    , ("[email protected]", False, "Local part ends with a dot")
-    , ("[email protected]", False, "Local part has consecutive dots")
-    , ("\"first\"last\"@example.com", False, "Local part contains unescaped 
excluded characters")
-    , ("\"first\\last\"@example.com", True, "Any character can be escaped in a 
quoted string")
-    , ("\"\"\"@example.com", False, "Local part contains unescaped excluded 
characters")
-    , ("\"\\\"@example.com", False, "Local part cannot end with a backslash")
-    , ("first\\\\@[email protected]", False, "Local part contains unescaped 
excluded characters")
-    , ("first.last@", False, "No domain")
-    , ("\"Abc\\@def\"@example.com", True, "")
-    , ("\"Fred\\ Bloggs\"@example.com", True, "")
-    , ("\"Joe.\\\\Blow\"@example.com", True, "")
-    , ("\"Abc@def\"@example.com", True, "")
-    , ("\"Fred Bloggs\"@example.com", True, "")
-    , ("[email protected]", True, "")
-    , ("customer/[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("!def!xyz%[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("abc\\@[email protected]", False, "This example from RFC3696 was 
corrected in an erratum")
-    , ("abc\\\\@example.com", False, "This example from RFC3696 was corrected 
in an erratum")
-    , ("[email protected]", True, "")
-    , ("Doug\\ \\\"Ace\\\"\\ [email protected]", False, "Escaping can only 
happen in a quoted string")
-    , ("\"Doug \\\"Ace\\\" L.\"@example.com", True, "")
-    , ("abc@[email protected]", False, "Doug Lovell says this should fail")
-    , ("abc\\\\@[email protected]", False, "Doug Lovell says this should fail")
-    , ("abc\\@example.com", False, "Doug Lovell says this should fail")
-    , ("@example.com", False, "No local part")
-    , ("doug@", False, "Doug Lovell says this should fail")
-    , ("\"[email protected]", False, "Doug Lovell says this should fail")
-    , ("ote\"@example.com", False, "Doug Lovell says this should fail")
-    , ("[email protected]", False, "Doug Lovell says this should fail")
-    , ("[email protected]", False, "Doug Lovell says this should fail")
-    , ("[email protected]", False, "Doug Lovell says this should fail")
-    , ("\"Doug \"Ace\" L.\"@example.com", False, "Doug Lovell says this should 
fail")
-    , ("Doug\\ \\\"Ace\\\"\\ L\\[email protected]", False, "Doug Lovell says this 
should fail")
-    , ("hello [email protected]", False, "Doug Lovell says this should fail")
-    , ("[email protected].", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("t*[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("{_test_}@example.com", True, "")
-    , ("\"[[ test ]]\"@example.com", True, "")
-    , ("[email protected]", True, "")
-    , ("\"test.test\"@example.com", True, "")
-    , ("test.\"test\"@example.com", True, "Obsolete form, but documented in 
RFC2822")
-    , ("\"test@test\"@example.com", True, "")
-    , ("[email protected]", True, "")
-    , ("test@[123.123.123.123]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("test.example.com", False, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", False, "")
-    , ("test@[email protected]", False, "")
-    , ("test@@example.com", False, "")
-    , ("-- test [email protected]", False, "No spaces allowed in local part")
-    , ("[test]@example.com", False, "Square brackets only allowed within 
quotes")
-    , ("\"test\\test\"@example.com", True, "Any character can be escaped in a 
quoted string")
-    , ("\"test\"test\"@example.com", False, "Quotes cannot be nested")
-    , ("()[]\\;:,><@example.com", False, "Disallowed Characters")
-    , ("test@.", False, "Dave Child says so")
-    , ("test@example.", True, "")
-    , ("[email protected]", False, "Dave Child says so")
-    , ("test@[123.123.123.123", False, "Dave Child says so")
-    , ("[email protected]]", False, "Dave Child says so")
-    , ("NotAnEmail", False, "Phil Haack says so")
-    , ("@NotAnEmail", False, "Phil Haack says so")
-    , ("\"test\\\\blah\"@example.com", True, "")
-    , ("\"test\\blah\"@example.com", True, "Any character can be escaped in a 
quoted string")
-    , ("\"test\\\rblah\"@example.com", True, "Quoted string specifically 
excludes carriage returns unless escaped")
-    , ("\"test\rblah\"@example.com", False, "Quoted string specifically 
excludes carriage returns")
-    , ("\"test\\\"blah\"@example.com", True, "")
-    , ("\"test\"blah\"@example.com", False, "Phil Haack says so")
-    , ("customer/[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", False, "Phil Haack says so")
-    , ("[email protected]", False, "Phil Haack says so")
-    , ("[email protected]", False, "Phil Haack says so")
-    , ("[email protected]", False, "Phil Haack says so")
-    , ("\"Austin@Powers\"@example.com", True, "")
-    , ("[email protected]", True, "")
-    , ("\"Ima.Fool\"@example.com", True, "")
-    , ("\"Ima Fool\"@example.com", True, "")
-    , ("Ima [email protected]", False, "Phil Haack says so")
-    , ("phil.h\\@\\@[email protected]", False, "Escaping can only happen in a 
quoted string")
-    , ("\"first\".\"last\"@example.com", True, "")
-    , ("\"first\".middle.\"last\"@example.com", True, "")
-    , ("\"first\\\\\"last\"@example.com", False, "Contains an unescaped quote")
-    , ("\"first\"[email protected]", True, "obs-local-part form as described 
in RFC 2822")
-    , ("first.\"last\"@example.com", True, "obs-local-part form as described 
in RFC 2822")
-    , ("\"first\".\"middle\".\"last\"@example.com", True, "obs-local-part form 
as described in RFC 2822")
-    , ("\"first.middle\".\"last\"@example.com", True, "obs-local-part form as 
described in RFC 2822")
-    , ("\"first.middle.last\"@example.com", True, "obs-local-part form as 
described in RFC 2822")
-    , ("\"first..last\"@example.com", True, "obs-local-part form as described 
in RFC 2822")
-    , ("foo@[\\1.2.3.4]", False, "RFC 5321 specifies the syntax for 
address-literal and does not allow escaping")
-    , ("\"first\\\\\\\"last\"@example.com", True, "")
-    , ("first.\"mid\\dle\".\"last\"@example.com", True, "Backslash can escape 
anything but must escape something")
-    , ("Test.\r\n Folding.\r\n [email protected]", True, "")
-    , ("first\\[email protected]", False, "Unquoted string must be an atom")
-    , ("Abc\\@[email protected]", False, "Was incorrectly given as a valid 
address in the original RFC3696")
-    , ("Fred\\ [email protected]", False, "Was incorrectly given as a valid 
address in the original RFC3696")
-    , ("Joe.\\\\[email protected]", False, "Was incorrectly given as a valid 
address in the original RFC3696")
-    , ("\"test\\\r\n blah\"@example.com", False, "Folding white space can\'t 
appear within a quoted pair")
-    , ("\"test\r\n blah\"@example.com", True, "This is a valid quoted string 
with folding white space")
-    , ("{^c\\@**Dog^}@cartoon.com", False, "This is a throwaway example from 
Doug Lovell\'s article. Actually it\'s not a valid address.")
-    , ("(foo)cal(bar)@(baz)iamcal.com(quux)", True, "A valid address 
containing comments")
-    , ("cal@iamcal(woo).(yay)com", True, "A valid address containing comments")
-    , ("cal(woo(yay)hoopla)@iamcal.com", True, "A valid address containing 
comments")
-    , ("cal(foo\\@bar)@iamcal.com", True, "A valid address containing 
comments")
-    , ("cal(foo\\)bar)@iamcal.com", True, "A valid address containing comments 
and an escaped parenthesis")
-    , ("cal(foo(bar)@iamcal.com", False, "Unclosed parenthesis in comment")
-    , ("cal(foo)bar)@iamcal.com", False, "Too many closing parentheses")
-    , ("cal(foo\\)@iamcal.com", False, "Backslash at end of comment has 
nothing to escape")
-    , ("first()[email protected]", True, "A valid address containing an empty 
comment")
-    , ("first.(\r\n middle\r\n )[email protected]", True, "Comment with folding 
white space")
-    , 
("first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890)example.com",
 False, "Too long with comments, not too long without")
-    , ("first(Welcome to\r\n the (\"wonderful\" (!)) world\r\n of 
email)@example.com", True, "Silly example from my blog post")
-    , ("pete(his account)@silly.test(his host)", True, "Canonical example from 
RFC5322")
-    , ("c@(Chris\'s host.)public.example", True, "Canonical example from 
RFC5322")
-    , ("jdoe@machine(comment).  example", True, "Canonical example from 
RFC5322")
-    , ("1234   @   local(blah)  .machine .example", True, "Canonical example 
from RFC5322")
-    , ("first(middle)[email protected]", False, "Can\'t have a comment or white 
space except at an element boundary")
-    , ("first(abc.def)[email protected]", True, "Comment can contain a dot")
-    , ("first(a\"bc.def)[email protected]", True, "Comment can contain double 
quote")
-    , ("first.(\")middle.last(\")@example.com", True, "Comment can contain a 
quote")
-    , 
("first(abc(\"def\".ghi).mno)middle(abc(\"def\".ghi).mno).last@(abc(\"def\".ghi).mno)example(abc(\"def\".ghi).mno).(abc(\"def\".ghi).mno)com(abc(\"def\".ghi).mno)",
 False, "Can\'t have comments or white space except at an element boundary")
-    , ("first(abc\\(def)@example.com", True, "Comment can contain quoted-pair")
-    , 
("first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com",
 True, "Label is longer than 63 octets, but not with comment removed")
-    , ("a(a(b(c)d(e(f))g)h(i)j)@example.com", True, "")
-    , ("a(a(b(c)d(e(f))g)(h(i)j)@example.com", False, "Braces are not properly 
matched")
-    , ("[email protected]", True, "")
-    , (".@", False, "")
-    , ("@bar.com", False, "")
-    , ("@@bar.com", False, "")
-    , ("[email protected]", True, "")
-    , ("aaa.com", False, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", False, "")
-    , ("aaa@[123.123.123.123]", True, "")
-    , ("aaa@[123.123.123.123]a", False, "extra data outside ip")
-    , ("[email protected].", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "TLDs can be any length")
-    , ("[email protected]", True, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", True, "")
-    , ("\"hello my name is\"@stutter.com", True, "")
-    , ("\"Test \\\"Fail\\\" Ing\"@example.com", True, "")
-    , ("[email protected]", True, "")
-    , ("[email protected]", True, "Disagree with Paul Gregg 
here")
-    , ("[email protected]", False, "......")
-    , ("\"Joe\\\\Blow\"@example.com", True, "")
-    , ("Invalid \\\n Folding \\\n [email protected]", False, "This isn\'t 
FWS so Dominic Sayers says it\'s invalid")
-    , ("HM2Kinsists@(that comments are allowed)this.is.ok", True, "")
-    , ("user%[email protected]", True, "")
-    , ("\"first(last)\"@example.com", True, "")
-    , (" \r\n (\r\n x \r\n ) \r\n first\r\n ( \r\n x\r\n ) \r\n .\r\n ( \r\n 
x) \r\n last \r\n (  x \r\n ) \r\n @example.com", True, "")
-    , ("test.\r\n \r\n [email protected]", True, "obs-fws allows multiple lines")
-    , ("test. \r\n \r\n [email protected]", True, "obs-fws allows multiple lines 
(test 2: space before break)")
-    , ("test.\r\n\r\n [email protected]", False, "obs-fws must have at least one 
WSP per line")
-    , ("\"null \\\0\"@char.com", True, "can have escaped null character")
-    , ("\"null \0\"@char.com", False, "cannot have unescaped null character")
+    let domain249 = BS.intercalate "." (take 25 (repeat (BS.replicate 9 'x'))) 
in
+    [ valid "[email protected]"
+    , valid "[email protected]." `why` "Dot allowed on end of domain"
+    , invalid "local@exam_ple.com" `why` "Underscore not permitted in domain"
+    , valid 
"1234567890123456789012345678901234567890123456789012345678901...@example.com"
+    , valid "\"first last\"@example.com" `why` "Contains quoted spaces"
+    , valid "\"first\\\"last\"@example.com" `why` "Contains quoted escaped 
quote"
+    , invalid "first\\@[email protected]" `why` "Escaping can only happen 
within a quoted string"
+    , valid "\"first@last\"@example.com" `why` "Contains quoted at-sign"
+    , valid "\"first\\\\last\"@example.com" `why` "Contains quoted escaped 
backslash"
+    , valid ("1234@" <> domain249)
+        `why` "Maximum length is 254, this is 254 exactly"
+    , valid ("1234@" <> domain249 <> ".")
+        `why` "Trailing dot doesn't increase length"
+    , invalid ("12345@" <> domain249)
+        `why` "Maximum length is 254, this is 255"
+        `errorShouldContain` "too long"
+    , valid "first.last@[12.34.56.78]" `why` "IP address"
+    , valid "first.last@[IPv6:::12.34.56.78]" `why` "IPv6 address"
+    , valid "first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]"
+    , valid "first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]"
+    , valid "first.last@[IPv6:::1111:2222:3333:4444:5555:6666]"
+    , valid "first.last@[IPv6:1111:2222:3333::4444:5555:6666]"
+    , valid "first.last@[IPv6:1111:2222:3333:4444:5555:6666::]"
+    , valid "first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]"
+    , valid 
"first.l...@x23456789012345678901234567890123456789012345678901234567890123.example.com"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , invalid "first.last" `why` "no at sign" `errorShouldContain` "at sign"
+    , invalid "[email protected]" `why` "Local part starts with a dot"
+    , invalid "[email protected]" `why` "Local part ends with a dot"
+    , invalid "[email protected]" `why` "Local part has consecutive dots"
+    , invalid "\"first\"last\"@example.com" `why` "Local part contains 
unescaped excluded characters"
+    , valid "\"first\\last\"@example.com" `why` "Any character can be escaped 
in a quoted string"
+    , invalid "\"\"\"@example.com" `why` "Local part contains unescaped 
excluded characters"
+    , invalid "\"\\\"@example.com" `why` "Local part cannot end with a 
backslash"
+    , invalid "first\\\\@[email protected]" `why` "Local part contains 
unescaped excluded characters"
+    , invalid "first.last@" `why` "No domain"
+    , valid "\"Abc\\@def\"@example.com"
+    , valid "\"Fred\\ Bloggs\"@example.com"
+    , valid "\"Joe.\\\\Blow\"@example.com"
+    , valid "\"Abc@def\"@example.com"
+    , valid "\"Fred Bloggs\"@example.com"
+    , valid "[email protected]"
+    , valid "customer/[email protected]"
+    , valid "[email protected]"
+    , valid "!def!xyz%[email protected]"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , invalid "abc\\@[email protected]" `why` "This example from RFC3696 was 
corrected in an erratum"
+    , invalid "abc\\\\@example.com" `why` "This example from RFC3696 was 
corrected in an erratum"
+    , valid "[email protected]"
+    , invalid "Doug\\ \\\"Ace\\\"\\ [email protected]" `why` "Escaping can 
only happen in a quoted string"
+    , valid "\"Doug \\\"Ace\\\" L.\"@example.com"
+    , invalid "abc@[email protected]" `why` "Doug Lovell says this should fail"
+    , invalid "abc\\\\@[email protected]" `why` "Doug Lovell says this should 
fail"
+    , invalid "abc\\@example.com" `why` "Doug Lovell says this should fail"
+    , invalid "@example.com" `why` "no local part"
+    , invalid "doug@" `why` "no domain part"
+    , invalid "\"[email protected]" `why` "Doug Lovell says this should fail"
+    , invalid "ote\"@example.com" `why` "Doug Lovell says this should fail"
+    , invalid "[email protected]" `why` "Doug Lovell says this should fail"
+    , invalid "[email protected]" `why` "Doug Lovell says this should fail"
+    , invalid "[email protected]" `why` "Doug Lovell says this should fail"
+    , invalid "\"Doug \"Ace\" L.\"@example.com" `why` "Doug Lovell says this 
should fail"
+    , invalid "Doug\\ \\\"Ace\\\"\\ L\\[email protected]" `why` "Doug Lovell says 
this should fail"
+    , invalid "hello [email protected]" `why` "Doug Lovell says this should 
fail"
+    , valid "[email protected]."
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , valid "t*[email protected]"
+    , valid "[email protected]"
+    , valid "{_test_}@example.com"
+    , valid "\"[[ test ]]\"@example.com"
+    , valid "[email protected]"
+    , valid "\"test.test\"@example.com"
+    , valid "test.\"test\"@example.com" `why` "Obsolete form, but documented 
in RFC2822"
+    , valid "\"test@test\"@example.com"
+    , valid "[email protected]"
+    , valid "test@[123.123.123.123]"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , invalid "test.example.com"
+    , invalid "[email protected]"
+    , invalid "[email protected]"
+    , invalid "[email protected]"
+    , invalid "test@[email protected]"
+    , invalid "test@@example.com"
+    , invalid "-- test [email protected]" `why` "No spaces allowed in local part"
+    , invalid "[test]@example.com" `why` "Square brackets only allowed within 
quotes"
+    , valid "\"test\\test\"@example.com" `why` "Any character can be escaped 
in a quoted string"
+    , invalid "\"test\"test\"@example.com" `why` "Quotes cannot be nested"
+    , invalid "()[]\\;:,><@example.com" `why` "Disallowed Characters"
+    , invalid "test@." `why` "Dave Child says so"
+    , valid "test@example."
+    , invalid "[email protected]" `why` "Dave Child says so"
+    , invalid "test@[123.123.123.123" `why` "Dave Child says so"
+    , invalid "[email protected]]" `why` "Dave Child says so"
+    , invalid "NotAnEmail" `why` "Phil Haack says so"
+    , invalid "@NotAnEmail" `why` "Phil Haack says so"
+    , valid "\"test\\\\blah\"@example.com"
+    , valid "\"test\\blah\"@example.com" `why` "Any character can be escaped 
in a quoted string"
+    , valid "\"test\\\rblah\"@example.com" `why` "Quoted string specifically 
excludes carriage returns unless escaped"
+    , invalid "\"test\rblah\"@example.com" `why` "Quoted string specifically 
excludes carriage returns"
+    , valid "\"test\\\"blah\"@example.com"
+    , invalid "\"test\"blah\"@example.com" `why` "Phil Haack says so"
+    , valid "customer/[email protected]"
+    , valid "[email protected]"
+    , valid "[email protected]"
+    , invalid "[email protected]" `why` "Phil Haack says so"
+    , invalid "[email protected]" `why` "Phil Haack says so"
+    , invalid "[email protected]" `why` "Phil Haack says so"
+    , invalid "[email protected]" `why` "Phil Haack says so"
+    , valid "\"Austin@Powers\"@example.com"
+    , valid "[email protected]"
+    , valid "\"Ima.Fool\"@example.com"
+    , valid "\"Ima Fool\"@example.com"
+    , invalid "Ima [email protected]" `why` "Phil Haack says so"
+    , invalid "phil.h\\@\\@[email protected]" `why` "Escaping can only happen 
in a quoted string"
+    , valid "\"first\".\"last\"@example.com"
+    , valid "\"first\".middle.\"last\"@example.com"
+    , invalid "\"first\\\\\"last\"@example.com" `why` "Contains an unescaped 
quote"
+    , valid "\"first\"[email protected]" `why` "obs-local-part form as 
described in RFC 2822"
+    , valid "first.\"last\"@example.com" `why` "obs-local-part form as 
described in RFC 2822"
+    , valid "\"first\".\"middle\".\"last\"@example.com" `why` "obs-local-part 
form as described in RFC 2822"
+    , valid "\"first.middle\".\"last\"@example.com" `why` "obs-local-part form 
as described in RFC 2822"
+    , valid "\"first.middle.last\"@example.com" `why` "obs-local-part form as 
described in RFC 2822"
+    , valid "\"first..last\"@example.com" `why` "obs-local-part form as 
described in RFC 2822"
+    , invalid "foo@[\\1.2.3.4]" `why` "RFC 5321 specifies the syntax for 
address-literal and does not allow escaping"
+    , valid "\"first\\\\\\\"last\"@example.com"
+    , valid "first.\"mid\\dle\".\"last\"@example.com" `why` "Backslash can 
escape anything but must escape something"
+    , valid "Test.\r\n Folding.\r\n [email protected]"
+    , invalid "first\\[email protected]" `why` "Unquoted string must be an atom"
+    , invalid "Abc\\@[email protected]" `why` "Was incorrectly given as a valid 
address in the original RFC3696"
+    , invalid "Fred\\ [email protected]" `why` "Was incorrectly given as a 
valid address in the original RFC3696"
+    , invalid "Joe.\\\\[email protected]" `why` "Was incorrectly given as a 
valid address in the original RFC3696"
+    , invalid "\"test\\\r\n blah\"@example.com" `why` "Folding white space 
can\'t appear within a quoted pair"
+    , valid "\"test\r\n blah\"@example.com" `why` "This is a valid quoted 
string with folding white space"
+    , invalid "{^c\\@**Dog^}@cartoon.com" `why` "This is a throwaway example 
from Doug Lovell\'s article. Actually it\'s not a valid address."
+    , valid "(foo)cal(bar)@(baz)iamcal.com(quux)" `why` "A valid address 
containing comments"
+    , valid "cal@iamcal(woo).(yay)com" `why` "A valid address containing 
comments"
+    , valid "cal(woo(yay)hoopla)@iamcal.com" `why` "A valid address containing 
comments"
+    , valid "cal(foo\\@bar)@iamcal.com" `why` "A valid address containing 
comments"
+    , valid "cal(foo\\)bar)@iamcal.com" `why` "A valid address containing 
comments and an escaped parenthesis"
+    , invalid "cal(foo(bar)@iamcal.com" `why` "Unclosed parenthesis in comment"
+    , invalid "cal(foo)bar)@iamcal.com" `why` "Too many closing parentheses"
+    , invalid "cal(foo\\)@iamcal.com" `why` "Backslash at end of comment has 
nothing to escape"
+    , valid "first()[email protected]" `why` "A valid address containing an 
empty comment"
+    , valid "first.(\r\n middle\r\n )[email protected]" `why` "Comment with 
folding white space"
+    , invalid 
"first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890)example.com"
 `why` "Too long with comments, not too long without"
+    , valid "first(Welcome to\r\n the (\"wonderful\" (!)) world\r\n of 
email)@example.com" `why` "Silly example from my blog post"
+    , valid "pete(his account)@silly.test(his host)" `why` "Canonical example 
from RFC5322"
+    , valid "c@(Chris\'s host.)public.example" `why` "Canonical example from 
RFC5322"
+    , valid "jdoe@machine(comment).  example" `why` "Canonical example from 
RFC5322"
+    , valid "1234   @   local(blah)  .machine .example" `why` "Canonical 
example from RFC5322"
+    , invalid "first(middle)[email protected]" `why` "Can\'t have a comment or 
white space except at an element boundary"
+    , valid "first(abc.def)[email protected]" `why` "Comment can contain a dot"
+    , valid "first(a\"bc.def)[email protected]" `why` "Comment can contain 
double quote"
+    , valid "first.(\")middle.last(\")@example.com" `why` "Comment can contain 
a quote"
+    , invalid 
"first(abc(\"def\".ghi).mno)middle(abc(\"def\".ghi).mno).last@(abc(\"def\".ghi).mno)example(abc(\"def\".ghi).mno).(abc(\"def\".ghi).mno)com(abc(\"def\".ghi).mno)"
 `why` "Can\'t have comments or white space except at an element boundary"
+    , valid "first(abc\\(def)@example.com" `why` "Comment can contain 
quoted-pair"
+    , valid 
"first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com"
 `why` "Label is longer than 63 octets, but not with comment removed"
+    , valid "a(a(b(c)d(e(f))g)h(i)j)@example.com"
+    , invalid "a(a(b(c)d(e(f))g)(h(i)j)@example.com" `why` "Braces are not 
properly matched"
+    , valid "[email protected]"
+    , invalid ".@"
+    , invalid "@bar.com"
+    , invalid "@@bar.com"
+    , valid "[email protected]"
+    , invalid "aaa.com"
+    , invalid "[email protected]"
+    , invalid "[email protected]"
+    , valid "aaa@[123.123.123.123]"
+    , invalid "aaa@[123.123.123.123]a" `why` "extra data outside ip"
+    , valid "[email protected]."
+    , valid "[email protected]"
+    , valid "[email protected]" `why` "TLDs can be any length"
+    , valid "[email protected]"
+    , invalid "[email protected]"
+    , invalid "[email protected]"
+    , valid "[email protected]"
+    , valid "\"hello my name is\"@stutter.com"
+    , valid "\"Test \\\"Fail\\\" Ing\"@example.com"
+    , valid "[email protected]"
+    , valid "[email protected]" `why` "Disagree with Paul Gregg 
here"
+    , invalid "[email protected]" `why` "......"
+    , valid "\"Joe\\\\Blow\"@example.com"
+    , invalid "Invalid \\\n Folding \\\n [email protected]" `why` "This 
isn\'t FWS so Dominic Sayers says it\'s invalid"
+    , valid "HM2Kinsists@(that comments are allowed)this.is.ok"
+    , valid "user%[email protected]"
+    , valid "\"first(last)\"@example.com"
+    , valid " \r\n (\r\n x \r\n ) \r\n first\r\n ( \r\n x\r\n ) \r\n .\r\n ( 
\r\n x) \r\n last \r\n (  x \r\n ) \r\n @example.com"
+    , valid "test.\r\n \r\n [email protected]" `why` "obs-fws allows multiple 
lines"
+    , valid "test. \r\n \r\n [email protected]" `why` "obs-fws allows multiple 
lines (test 2: space before break)"
+    , invalid "test.\r\n\r\n [email protected]" `why` "obs-fws must have at 
least one WSP per line"
+    , valid "\"null \\\0\"@char.com" `why` "can have escaped null character"
+    , invalid "\"null \0\"@char.com" `why` "cannot have unescaped null 
character"
     -- items below here are invalid according to other RFCs (or opinions)
-    --, ("\"\"@example.com", False, "Local part is effectively empty")
-    --, ("[email protected]", False, "ip need to be []")
-    --, ("first.last@[.12.34.56.78]", False, "Only char that can precede IPv4 
address is \':\'")
-    --, ("first.last@[12.34.56.789]", False, "Can\'t be interpreted as IPv4 so 
IPv6 tag is missing")
-    --, ("first.last@[::12.34.56.78]", False, "IPv6 tag is missing")
-    --, ("first.last@[IPv5:::12.34.56.78]", False, "IPv6 tag is wrong")
-    --, ("first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]", False, 
"Too many IPv6 groups (4 max)")
-    --, ("first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]", False, "Not 
enough IPv6 groups")
-    --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]", 
False, "Too many IPv6 groups (6 max)")
-    --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]", False, "Not 
enough IPv6 groups")
-    --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]", 
False, "Too many IPv6 groups (8 max)")
-    --, ("first.last@[IPv6:1111:2222::3333::4444:5555:6666]", False, "Too many 
\'::\' (can be none or one)")
-    --, ("first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]", False, "Too 
many IPv6 groups (6 max)")
-    --, ("first.last@[IPv6:1111:2222:333x::4444:5555]", False, "x is not valid 
in an IPv6 address")
-    --, ("first.last@[IPv6:1111:2222:33333::4444:5555]", False, "33333 is not 
a valid group in an IPv6 address")
-    --, ("[email protected]", False, "TLD can\'t be all digits")
-    --, ("aaa@[123.123.123.333]", False, "not a valid IP")
-    --, ("first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]", 
False, "IPv4 part contains an invalid octet")
-    --, ("a@b", False, "")
-    --, ("a@bar", False, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", False, "")
-    , ("[email protected]", False, "")
-    --, ("\"foo\"(yay)@(hoopla)[1.2.3.4]", False, "Address literal can\'t be 
commented (RFC5321)")
-    --, ("first.\"\"[email protected]", False, "Contains a zero-length 
element")
-    --, ("test@example", False, "Dave Child says so")
-    --, 
("12345678901234567890123456789012345678901234567890123456789012...@example.com",
 False, "Local part more than 64 characters")
-    , 
("x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456",
 False, "Domain exceeds 255 chars")
-    , 
("t...@123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012.com",
 False, "255 characters is maximum length for domain. This is 256.")
-    --, 
("123456789012345678901234567890123456789012345678901234567...@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.1234.example.com",
 False, "Entire address is longer than 256 characters")
-    --, ("[email protected]", False, "Top Level Domain won\'t be 
all-numeric (see RFC3696 Section 2). I disagree with Dave Child on this one.")
-    , 
("first.l...@x234567890123456789012345678901234567890123456789012345678901234.example.com",
 False, "Label can\'t be longer than 63 octets")
-    --, ("first.last@com", False, "Mail host must be second- or lower level")
-    , ("[email protected]", False, "Label can\'t begin with a hyphen")
-    , ("[email protected]", False, "Label can\'t end with a hyphen")
+    --, invalid "\"\"@example.com" `why` "Local part is effectively empty"
+    --, invalid "[email protected]" `why` "ip need to be []"
+    --, invalid "first.last@[.12.34.56.78]" `why` "Only char that can precede 
IPv4 address is \':\'"
+    --, invalid "first.last@[12.34.56.789]" `why` "Can\'t be interpreted as 
IPv4 so IPv6 tag is missing"
+    --, invalid "first.last@[::12.34.56.78]" `why` "IPv6 tag is missing"
+    --, invalid "first.last@[IPv5:::12.34.56.78]" `why` "IPv6 tag is wrong"
+    --, invalid "first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]" 
`why` "Too many IPv6 groups (4 max)"
+    --, invalid "first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]" `why` 
"Not enough IPv6 groups"
+    --, invalid 
"first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]" `why` "Too 
many IPv6 groups (6 max)"
+    --, invalid "first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]" `why` 
"Not enough IPv6 groups"
+    --, invalid 
"first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]" `why` "Too 
many IPv6 groups (8 max)"
+    --, invalid "first.last@[IPv6:1111:2222::3333::4444:5555:6666]" `why` "Too 
many \'::\' (can be none or one)"
+    --, invalid "first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]" `why` 
"Too many IPv6 groups (6 max)"
+    --, invalid "first.last@[IPv6:1111:2222:333x::4444:5555]" `why` "x is not 
valid in an IPv6 address"
+    --, invalid "first.last@[IPv6:1111:2222:33333::4444:5555]" `why` "33333 is 
not a valid group in an IPv6 address"
+    --, invalid "[email protected]" `why` "TLD can\'t be all digits"
+    --, invalid "aaa@[123.123.123.333]" `why` "not a valid IP"
+    --, invalid "first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]" 
`why` "IPv4 part contains an invalid octet"
+    , valid "a@b"
+    , valid "a@bar"
+    , invalid "[email protected]" `why` "domain can't end with hyphen"
+    , invalid "[email protected]" `why` "domain can't start with hyphen"
+    , invalid "[email protected]" `why` "domain label can't end with hyphen"
+    --, invalid "\"foo\"(yay)@(hoopla)[1.2.3.4]" `why` "Address literal can\'t 
be commented (RFC5321)"
+    --, invalid "first.\"\"[email protected]" `why` "Contains a zero-length 
element"
+    --, invalid "test@example" `why` "Dave Child says so"
+    , invalid (BS.replicate 65 'x' <> "@x") `why` "local-part longer than 64 
octets" `errorShouldContain` "too long"
+    , invalid 
"x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456"
 `why` "Domain exceeds 255 chars"
+    , invalid 
"t...@123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012.com"
 `why` "255 characters is maximum length for domain. This is 256."
+    , invalid 
"123456789012345678901234567890123456789012345678901234567...@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.1234.example.com"
 `why` "Entire address is longer than 254 characters (this is 257)"
+    , invalid 
"123456789012345678901234567890123456789012345678901234567...@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.123.example.com"
 `why` "Entire address is longer than 254 characters (this is 256)"
+    , invalid 
"123456789012345678901234567890123456789012345678901234567...@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12.example.com"
 `why` "Entire address is longer than 254 characters (this is 255)"
+    , valid 
"123456789012345678901234567890123456789012345678901234567...@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.1.example.com"
 `why` "Entire address is 254 characters"
+    --, invalid "[email protected]" `why` "Top Level Domain won\'t be 
all-numeric (see RFC3696 Section 2). I disagree with Dave Child on this one."
+    , invalid 
"first.l...@x234567890123456789012345678901234567890123456789012345678901234.example.com"
 `why` "Label can\'t be longer than 63 octets"
+    --, invalid "first.last@com" `why` "Mail host must be second- or lower 
level"
+    , invalid "[email protected]" `why` "Label can\'t begin with a 
hyphen"
+    , invalid "[email protected]" `why` "Label can\'t end with a hyphen"
     ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/email-validate-2.2.1.1/tests/doctests.hs 
new/email-validate-2.3/tests/doctests.hs
--- old/email-validate-2.2.1.1/tests/doctests.hs        1970-01-01 
01:00:00.000000000 +0100
+++ new/email-validate-2.3/tests/doctests.hs    2017-06-26 08:45:39.000000000 
+0200
@@ -0,0 +1,7 @@
+import Test.DocTest
+
+main = doctest
+    [ "-isrc"
+    , "src/Text/Email/QuasiQuotation.hs"
+    , "src/Text/Email/Validate.hs"
+    ]


Reply via email to