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"
+ ]