summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_email
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_email')
-rw-r--r--Lib/test/test_email/__init__.py6
-rw-r--r--Lib/test/test_email/test__encoded_words.py187
-rw-r--r--Lib/test/test_email/test__header_value_parser.py2466
-rw-r--r--Lib/test/test_email/test__headerregistry.py717
-rw-r--r--Lib/test/test_email/test_generator.py168
-rw-r--r--Lib/test/test_email/test_pickleable.py57
-rw-r--r--Lib/test/test_email/test_policy.py128
7 files changed, 3624 insertions, 105 deletions
diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py
index b05fb3c..75dc64d 100644
--- a/Lib/test/test_email/__init__.py
+++ b/Lib/test/test_email/__init__.py
@@ -65,3 +65,9 @@ class TestEmailBase(unittest.TestCase):
def assertBytesEqual(self, first, second, msg):
"""Our byte strings are really encoded strings; improve diff output"""
self.assertEqual(self._bytes_repr(first), self._bytes_repr(second))
+
+ def assertDefectsEqual(self, actual, expected):
+ self.assertEqual(len(actual), len(expected), actual)
+ for i in range(len(actual)):
+ self.assertIsInstance(actual[i], expected[i],
+ 'item {}'.format(i))
diff --git a/Lib/test/test_email/test__encoded_words.py b/Lib/test/test_email/test__encoded_words.py
new file mode 100644
index 0000000..14395fe
--- /dev/null
+++ b/Lib/test/test_email/test__encoded_words.py
@@ -0,0 +1,187 @@
+import unittest
+from email import _encoded_words as _ew
+from email import errors
+from test.test_email import TestEmailBase
+
+
+class TestDecodeQ(TestEmailBase):
+
+ def _test(self, source, ex_result, ex_defects=[]):
+ result, defects = _ew.decode_q(source)
+ self.assertEqual(result, ex_result)
+ self.assertDefectsEqual(defects, ex_defects)
+
+ def test_no_encoded(self):
+ self._test(b'foobar', b'foobar')
+
+ def test_spaces(self):
+ self._test(b'foo=20bar=20', b'foo bar ')
+ self._test(b'foo_bar_', b'foo bar ')
+
+ def test_run_of_encoded(self):
+ self._test(b'foo=20=20=21=2Cbar', b'foo !,bar')
+
+
+class TestDecodeB(TestEmailBase):
+
+ def _test(self, source, ex_result, ex_defects=[]):
+ result, defects = _ew.decode_b(source)
+ self.assertEqual(result, ex_result)
+ self.assertDefectsEqual(defects, ex_defects)
+
+ def test_simple(self):
+ self._test(b'Zm9v', b'foo')
+
+ def test_missing_padding(self):
+ self._test(b'dmk', b'vi', [errors.InvalidBase64PaddingDefect])
+
+ def test_invalid_character(self):
+ self._test(b'dm\x01k===', b'vi', [errors.InvalidBase64CharactersDefect])
+
+ def test_invalid_character_and_bad_padding(self):
+ self._test(b'dm\x01k', b'vi', [errors.InvalidBase64CharactersDefect,
+ errors.InvalidBase64PaddingDefect])
+
+
+class TestDecode(TestEmailBase):
+
+ def test_wrong_format_input_raises(self):
+ with self.assertRaises(ValueError):
+ _ew.decode('=?badone?=')
+ with self.assertRaises(ValueError):
+ _ew.decode('=?')
+ with self.assertRaises(ValueError):
+ _ew.decode('')
+
+ def _test(self, source, result, charset='us-ascii', lang='', defects=[]):
+ res, char, l, d = _ew.decode(source)
+ self.assertEqual(res, result)
+ self.assertEqual(char, charset)
+ self.assertEqual(l, lang)
+ self.assertDefectsEqual(d, defects)
+
+ def test_simple_q(self):
+ self._test('=?us-ascii?q?foo?=', 'foo')
+
+ def test_simple_b(self):
+ self._test('=?us-ascii?b?dmk=?=', 'vi')
+
+ def test_q_case_ignored(self):
+ self._test('=?us-ascii?Q?foo?=', 'foo')
+
+ def test_b_case_ignored(self):
+ self._test('=?us-ascii?B?dmk=?=', 'vi')
+
+ def test_non_trivial_q(self):
+ self._test('=?latin-1?q?=20F=fcr=20Elise=20?=', ' Für Elise ', 'latin-1')
+
+ def test_q_escpaed_bytes_preserved(self):
+ self._test(b'=?us-ascii?q?=20\xACfoo?='.decode('us-ascii',
+ 'surrogateescape'),
+ ' \uDCACfoo',
+ defects = [errors.UndecodableBytesDefect])
+
+ def test_b_undecodable_bytes_ignored_with_defect(self):
+ self._test(b'=?us-ascii?b?dm\xACk?='.decode('us-ascii',
+ 'surrogateescape'),
+ 'vi',
+ defects = [
+ errors.InvalidBase64CharactersDefect,
+ errors.InvalidBase64PaddingDefect])
+
+ def test_b_invalid_bytes_ignored_with_defect(self):
+ self._test('=?us-ascii?b?dm\x01k===?=',
+ 'vi',
+ defects = [errors.InvalidBase64CharactersDefect])
+
+ def test_b_invalid_bytes_incorrect_padding(self):
+ self._test('=?us-ascii?b?dm\x01k?=',
+ 'vi',
+ defects = [
+ errors.InvalidBase64CharactersDefect,
+ errors.InvalidBase64PaddingDefect])
+
+ def test_b_padding_defect(self):
+ self._test('=?us-ascii?b?dmk?=',
+ 'vi',
+ defects = [errors.InvalidBase64PaddingDefect])
+
+ def test_nonnull_lang(self):
+ self._test('=?us-ascii*jive?q?test?=', 'test', lang='jive')
+
+ def test_unknown_8bit_charset(self):
+ self._test('=?unknown-8bit?q?foo=ACbar?=',
+ b'foo\xacbar'.decode('ascii', 'surrogateescape'),
+ charset = 'unknown-8bit',
+ defects = [])
+
+ def test_unknown_charset(self):
+ self._test('=?foobar?q?foo=ACbar?=',
+ b'foo\xacbar'.decode('ascii', 'surrogateescape'),
+ charset = 'foobar',
+ # XXX Should this be a new Defect instead?
+ defects = [errors.CharsetError])
+
+
+class TestEncodeQ(TestEmailBase):
+
+ def _test(self, src, expected):
+ self.assertEqual(_ew.encode_q(src), expected)
+
+ def test_all_safe(self):
+ self._test(b'foobar', 'foobar')
+
+ def test_spaces(self):
+ self._test(b'foo bar ', 'foo_bar_')
+
+ def test_run_of_encodables(self):
+ self._test(b'foo ,,bar', 'foo__=2C=2Cbar')
+
+
+class TestEncodeB(TestEmailBase):
+
+ def test_simple(self):
+ self.assertEqual(_ew.encode_b(b'foo'), 'Zm9v')
+
+ def test_padding(self):
+ self.assertEqual(_ew.encode_b(b'vi'), 'dmk=')
+
+
+class TestEncode(TestEmailBase):
+
+ def test_q(self):
+ self.assertEqual(_ew.encode('foo', 'utf-8', 'q'), '=?utf-8?q?foo?=')
+
+ def test_b(self):
+ self.assertEqual(_ew.encode('foo', 'utf-8', 'b'), '=?utf-8?b?Zm9v?=')
+
+ def test_auto_q(self):
+ self.assertEqual(_ew.encode('foo', 'utf-8'), '=?utf-8?q?foo?=')
+
+ def test_auto_q_if_short_mostly_safe(self):
+ self.assertEqual(_ew.encode('vi.', 'utf-8'), '=?utf-8?q?vi=2E?=')
+
+ def test_auto_b_if_enough_unsafe(self):
+ self.assertEqual(_ew.encode('.....', 'utf-8'), '=?utf-8?b?Li4uLi4=?=')
+
+ def test_auto_b_if_long_unsafe(self):
+ self.assertEqual(_ew.encode('vi.vi.vi.vi.vi.', 'utf-8'),
+ '=?utf-8?b?dmkudmkudmkudmkudmku?=')
+
+ def test_auto_q_if_long_mostly_safe(self):
+ self.assertEqual(_ew.encode('vi vi vi.vi ', 'utf-8'),
+ '=?utf-8?q?vi_vi_vi=2Evi_?=')
+
+ def test_utf8_default(self):
+ self.assertEqual(_ew.encode('foo'), '=?utf-8?q?foo?=')
+
+ def test_lang(self):
+ self.assertEqual(_ew.encode('foo', lang='jive'), '=?utf-8*jive?q?foo?=')
+
+ def test_unknown_8bit(self):
+ self.assertEqual(_ew.encode('foo\uDCACbar', charset='unknown-8bit'),
+ '=?unknown-8bit?q?foo=ACbar?=')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py
new file mode 100644
index 0000000..75fe299
--- /dev/null
+++ b/Lib/test/test_email/test__header_value_parser.py
@@ -0,0 +1,2466 @@
+import string
+import unittest
+from email import _header_value_parser as parser
+from email import errors
+from email import policy
+from test.test_email import TestEmailBase
+
+class TestTokens(TestEmailBase):
+
+ # EWWhiteSpaceTerminal
+
+ def test_EWWhiteSpaceTerminal(self):
+ x = parser.EWWhiteSpaceTerminal(' \t', 'fws')
+ self.assertEqual(x, ' \t')
+ self.assertEqual(str(x), '')
+ self.assertEqual(x.value, '')
+ self.assertEqual(x.encoded, ' \t')
+
+ # UnstructuredTokenList
+
+ def test_undecodable_bytes_error_preserved(self):
+ badstr = b"le pouf c\xaflebre".decode('ascii', 'surrogateescape')
+ unst = parser.get_unstructured(badstr)
+ self.assertDefectsEqual(unst.all_defects, [errors.UndecodableBytesDefect])
+ parts = list(unst.parts)
+ self.assertDefectsEqual(parts[0].all_defects, [])
+ self.assertDefectsEqual(parts[1].all_defects, [])
+ self.assertDefectsEqual(parts[2].all_defects, [errors.UndecodableBytesDefect])
+
+
+class TestParser(TestEmailBase):
+
+ # _wsp_splitter
+
+ rfc_printable_ascii = bytes(range(33, 127)).decode('ascii')
+ rfc_atext_chars = (string.ascii_letters + string.digits +
+ "!#$%&\'*+-/=?^_`{}|~")
+ rfc_dtext_chars = rfc_printable_ascii.translate(str.maketrans('','',r'\[]'))
+
+ def test__wsp_splitter_one_word(self):
+ self.assertEqual(parser._wsp_splitter('foo', 1), ['foo'])
+
+ def test__wsp_splitter_two_words(self):
+ self.assertEqual(parser._wsp_splitter('foo def', 1),
+ ['foo', ' ', 'def'])
+
+ def test__wsp_splitter_ws_runs(self):
+ self.assertEqual(parser._wsp_splitter('foo \t def jik', 1),
+ ['foo', ' \t ', 'def jik'])
+
+
+ # test harness
+
+ def _test_get_x(self, method, input, string, value, defects,
+ remainder, comments=None):
+ token, rest = method(input)
+ self.assertEqual(str(token), string)
+ self.assertEqual(token.value, value)
+ self.assertDefectsEqual(token.all_defects, defects)
+ self.assertEqual(rest, remainder)
+ if comments is not None:
+ self.assertEqual(token.comments, comments)
+ return token
+
+ # get_fws
+
+ def test_get_fws_only(self):
+ fws = self._test_get_x(parser.get_fws, ' \t ', ' \t ', ' ', [], '')
+ self.assertEqual(fws.token_type, 'fws')
+
+ def test_get_fws_space(self):
+ self._test_get_x(parser.get_fws, ' foo', ' ', ' ', [], 'foo')
+
+ def test_get_fws_ws_run(self):
+ self._test_get_x(parser.get_fws, ' \t foo ', ' \t ', ' ', [], 'foo ')
+
+ # get_encoded_word
+
+ def test_get_encoded_word_missing_start_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_encoded_word('abc')
+
+ def test_get_encoded_word_missing_end_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_encoded_word('=?abc')
+
+ def test_get_encoded_word_missing_middle_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_encoded_word('=?abc?=')
+
+ def test_get_encoded_word_valid_ew(self):
+ self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?this_is_a_test?= bird',
+ 'this is a test',
+ 'this is a test',
+ [],
+ ' bird')
+
+ def test_get_encoded_word_internal_spaces(self):
+ self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?this is a test?= bird',
+ 'this is a test',
+ 'this is a test',
+ [errors.InvalidHeaderDefect],
+ ' bird')
+
+ def test_get_encoded_word_gets_first(self):
+ self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?first?= =?utf-8?q?second?=',
+ 'first',
+ 'first',
+ [],
+ ' =?utf-8?q?second?=')
+
+ def test_get_encoded_word_gets_first_even_if_no_space(self):
+ self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?first?==?utf-8?q?second?=',
+ 'first',
+ 'first',
+ [],
+ '=?utf-8?q?second?=')
+
+ def test_get_encoded_word_sets_extra_attributes(self):
+ ew = self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii*jive?q?first_second?=',
+ 'first second',
+ 'first second',
+ [],
+ '')
+ self.assertEqual(ew.encoded, '=?us-ascii*jive?q?first_second?=')
+ self.assertEqual(ew.charset, 'us-ascii')
+ self.assertEqual(ew.lang, 'jive')
+
+ def test_get_encoded_word_lang_default_is_blank(self):
+ ew = self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?first_second?=',
+ 'first second',
+ 'first second',
+ [],
+ '')
+ self.assertEqual(ew.encoded, '=?us-ascii?q?first_second?=')
+ self.assertEqual(ew.charset, 'us-ascii')
+ self.assertEqual(ew.lang, '')
+
+ def test_get_encoded_word_non_printable_defect(self):
+ self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?first\x02second?=',
+ 'first\x02second',
+ 'first\x02second',
+ [errors.NonPrintableDefect],
+ '')
+
+ def test_get_encoded_word_leading_internal_space(self):
+ self._test_get_x(parser.get_encoded_word,
+ '=?us-ascii?q?=20foo?=',
+ ' foo',
+ ' foo',
+ [],
+ '')
+
+ # get_unstructured
+
+ def _get_unst(self, value):
+ token = parser.get_unstructured(value)
+ return token, ''
+
+ def test_get_unstructured_null(self):
+ self._test_get_x(self._get_unst, '', '', '', [], '')
+
+ def test_get_unstructured_one_word(self):
+ self._test_get_x(self._get_unst, 'foo', 'foo', 'foo', [], '')
+
+ def test_get_unstructured_normal_phrase(self):
+ self._test_get_x(self._get_unst, 'foo bar bird',
+ 'foo bar bird',
+ 'foo bar bird',
+ [],
+ '')
+
+ def test_get_unstructured_normal_phrase_with_whitespace(self):
+ self._test_get_x(self._get_unst, 'foo \t bar bird',
+ 'foo \t bar bird',
+ 'foo bar bird',
+ [],
+ '')
+
+ def test_get_unstructured_leading_whitespace(self):
+ self._test_get_x(self._get_unst, ' foo bar',
+ ' foo bar',
+ ' foo bar',
+ [],
+ '')
+
+ def test_get_unstructured_trailing_whitespace(self):
+ self._test_get_x(self._get_unst, 'foo bar ',
+ 'foo bar ',
+ 'foo bar ',
+ [],
+ '')
+
+ def test_get_unstructured_leading_and_trailing_whitespace(self):
+ self._test_get_x(self._get_unst, ' foo bar ',
+ ' foo bar ',
+ ' foo bar ',
+ [],
+ '')
+
+ def test_get_unstructured_one_valid_ew_no_ws(self):
+ self._test_get_x(self._get_unst, '=?us-ascii?q?bar?=',
+ 'bar',
+ 'bar',
+ [],
+ '')
+
+ def test_get_unstructured_one_ew_trailing_ws(self):
+ self._test_get_x(self._get_unst, '=?us-ascii?q?bar?= ',
+ 'bar ',
+ 'bar ',
+ [],
+ '')
+
+ def test_get_unstructured_one_valid_ew_trailing_text(self):
+ self._test_get_x(self._get_unst, '=?us-ascii?q?bar?= bird',
+ 'bar bird',
+ 'bar bird',
+ [],
+ '')
+
+ def test_get_unstructured_phrase_with_ew_in_middle_of_text(self):
+ self._test_get_x(self._get_unst, 'foo =?us-ascii?q?bar?= bird',
+ 'foo bar bird',
+ 'foo bar bird',
+ [],
+ '')
+
+ def test_get_unstructured_phrase_with_two_ew(self):
+ self._test_get_x(self._get_unst,
+ 'foo =?us-ascii?q?bar?= =?us-ascii?q?bird?=',
+ 'foo barbird',
+ 'foo barbird',
+ [],
+ '')
+
+ def test_get_unstructured_phrase_with_two_ew_trailing_ws(self):
+ self._test_get_x(self._get_unst,
+ 'foo =?us-ascii?q?bar?= =?us-ascii?q?bird?= ',
+ 'foo barbird ',
+ 'foo barbird ',
+ [],
+ '')
+
+ def test_get_unstructured_phrase_with_ew_with_leading_ws(self):
+ self._test_get_x(self._get_unst,
+ ' =?us-ascii?q?bar?=',
+ ' bar',
+ ' bar',
+ [],
+ '')
+
+ def test_get_unstructured_phrase_with_two_ew_extra_ws(self):
+ self._test_get_x(self._get_unst,
+ 'foo =?us-ascii?q?bar?= \t =?us-ascii?q?bird?=',
+ 'foo barbird',
+ 'foo barbird',
+ [],
+ '')
+
+ def test_get_unstructured_two_ew_extra_ws_trailing_text(self):
+ self._test_get_x(self._get_unst,
+ '=?us-ascii?q?test?= =?us-ascii?q?foo?= val',
+ 'testfoo val',
+ 'testfoo val',
+ [],
+ '')
+
+ def test_get_unstructured_ew_with_internal_ws(self):
+ self._test_get_x(self._get_unst,
+ '=?iso-8859-1?q?hello=20world?=',
+ 'hello world',
+ 'hello world',
+ [],
+ '')
+
+ def test_get_unstructured_ew_with_internal_leading_ws(self):
+ self._test_get_x(self._get_unst,
+ ' =?us-ascii?q?=20test?= =?us-ascii?q?=20foo?= val',
+ ' test foo val',
+ ' test foo val',
+ [],
+ '')
+
+ def test_get_unstructured_invaild_ew(self):
+ self._test_get_x(self._get_unst,
+ '=?test val',
+ '=?test val',
+ '=?test val',
+ [],
+ '')
+
+ def test_get_unstructured_undecodable_bytes(self):
+ self._test_get_x(self._get_unst,
+ b'test \xACfoo val'.decode('ascii', 'surrogateescape'),
+ 'test \uDCACfoo val',
+ 'test \uDCACfoo val',
+ [errors.UndecodableBytesDefect],
+ '')
+
+ def test_get_unstructured_undecodable_bytes_in_EW(self):
+ self._test_get_x(self._get_unst,
+ (b'=?us-ascii?q?=20test?= =?us-ascii?q?=20\xACfoo?='
+ b' val').decode('ascii', 'surrogateescape'),
+ ' test \uDCACfoo val',
+ ' test \uDCACfoo val',
+ [errors.UndecodableBytesDefect]*2,
+ '')
+
+ def test_get_unstructured_missing_base64_padding(self):
+ self._test_get_x(self._get_unst,
+ '=?utf-8?b?dmk?=',
+ 'vi',
+ 'vi',
+ [errors.InvalidBase64PaddingDefect],
+ '')
+
+ def test_get_unstructured_invalid_base64_character(self):
+ self._test_get_x(self._get_unst,
+ '=?utf-8?b?dm\x01k===?=',
+ 'vi',
+ 'vi',
+ [errors.InvalidBase64CharactersDefect],
+ '')
+
+ def test_get_unstructured_invalid_base64_character_and_bad_padding(self):
+ self._test_get_x(self._get_unst,
+ '=?utf-8?b?dm\x01k?=',
+ 'vi',
+ 'vi',
+ [errors.InvalidBase64CharactersDefect,
+ errors.InvalidBase64PaddingDefect],
+ '')
+
+ def test_get_unstructured_no_whitespace_between_ews(self):
+ self._test_get_x(self._get_unst,
+ '=?utf-8?q?foo?==?utf-8?q?bar?=',
+ 'foobar',
+ 'foobar',
+ [errors.InvalidHeaderDefect],
+ '')
+
+ # get_qp_ctext
+
+ def test_get_qp_ctext_only(self):
+ ptext = self._test_get_x(parser.get_qp_ctext,
+ 'foobar', 'foobar', ' ', [], '')
+ self.assertEqual(ptext.token_type, 'ptext')
+
+ def test_get_qp_ctext_all_printables(self):
+ with_qp = self.rfc_printable_ascii.replace('\\', '\\\\')
+ with_qp = with_qp. replace('(', r'\(')
+ with_qp = with_qp.replace(')', r'\)')
+ ptext = self._test_get_x(parser.get_qp_ctext,
+ with_qp, self.rfc_printable_ascii, ' ', [], '')
+
+ def test_get_qp_ctext_two_words_gets_first(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo de', 'foo', ' ', [], ' de')
+
+ def test_get_qp_ctext_following_wsp_preserved(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo \t\tde', 'foo', ' ', [], ' \t\tde')
+
+ def test_get_qp_ctext_up_to_close_paren_only(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo)', 'foo', ' ', [], ')')
+
+ def test_get_qp_ctext_wsp_before_close_paren_preserved(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo )', 'foo', ' ', [], ' )')
+
+ def test_get_qp_ctext_close_paren_mid_word(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo)bar', 'foo', ' ', [], ')bar')
+
+ def test_get_qp_ctext_up_to_open_paren_only(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo(', 'foo', ' ', [], '(')
+
+ def test_get_qp_ctext_wsp_before_open_paren_preserved(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo (', 'foo', ' ', [], ' (')
+
+ def test_get_qp_ctext_open_paren_mid_word(self):
+ self._test_get_x(parser.get_qp_ctext,
+ 'foo(bar', 'foo', ' ', [], '(bar')
+
+ def test_get_qp_ctext_non_printables(self):
+ ptext = self._test_get_x(parser.get_qp_ctext,
+ 'foo\x00bar)', 'foo\x00bar', ' ',
+ [errors.NonPrintableDefect], ')')
+ self.assertEqual(ptext.defects[0].non_printables[0], '\x00')
+
+ # get_qcontent
+
+ def test_get_qcontent_only(self):
+ ptext = self._test_get_x(parser.get_qcontent,
+ 'foobar', 'foobar', 'foobar', [], '')
+ self.assertEqual(ptext.token_type, 'ptext')
+
+ def test_get_qcontent_all_printables(self):
+ with_qp = self.rfc_printable_ascii.replace('\\', '\\\\')
+ with_qp = with_qp. replace('"', r'\"')
+ ptext = self._test_get_x(parser.get_qcontent, with_qp,
+ self.rfc_printable_ascii,
+ self.rfc_printable_ascii, [], '')
+
+ def test_get_qcontent_two_words_gets_first(self):
+ self._test_get_x(parser.get_qcontent,
+ 'foo de', 'foo', 'foo', [], ' de')
+
+ def test_get_qcontent_following_wsp_preserved(self):
+ self._test_get_x(parser.get_qcontent,
+ 'foo \t\tde', 'foo', 'foo', [], ' \t\tde')
+
+ def test_get_qcontent_up_to_dquote_only(self):
+ self._test_get_x(parser.get_qcontent,
+ 'foo"', 'foo', 'foo', [], '"')
+
+ def test_get_qcontent_wsp_before_close_paren_preserved(self):
+ self._test_get_x(parser.get_qcontent,
+ 'foo "', 'foo', 'foo', [], ' "')
+
+ def test_get_qcontent_close_paren_mid_word(self):
+ self._test_get_x(parser.get_qcontent,
+ 'foo"bar', 'foo', 'foo', [], '"bar')
+
+ def test_get_qcontent_non_printables(self):
+ ptext = self._test_get_x(parser.get_qcontent,
+ 'foo\x00fg"', 'foo\x00fg', 'foo\x00fg',
+ [errors.NonPrintableDefect], '"')
+ self.assertEqual(ptext.defects[0].non_printables[0], '\x00')
+
+ # get_atext
+
+ def test_get_atext_only(self):
+ atext = self._test_get_x(parser.get_atext,
+ 'foobar', 'foobar', 'foobar', [], '')
+ self.assertEqual(atext.token_type, 'atext')
+
+ def test_get_atext_all_atext(self):
+ atext = self._test_get_x(parser.get_atext, self.rfc_atext_chars,
+ self.rfc_atext_chars,
+ self.rfc_atext_chars, [], '')
+
+ def test_get_atext_two_words_gets_first(self):
+ self._test_get_x(parser.get_atext,
+ 'foo bar', 'foo', 'foo', [], ' bar')
+
+ def test_get_atext_following_wsp_preserved(self):
+ self._test_get_x(parser.get_atext,
+ 'foo \t\tbar', 'foo', 'foo', [], ' \t\tbar')
+
+ def test_get_atext_up_to_special(self):
+ self._test_get_x(parser.get_atext,
+ 'foo@bar', 'foo', 'foo', [], '@bar')
+
+ def test_get_atext_non_printables(self):
+ atext = self._test_get_x(parser.get_atext,
+ 'foo\x00bar(', 'foo\x00bar', 'foo\x00bar',
+ [errors.NonPrintableDefect], '(')
+ self.assertEqual(atext.defects[0].non_printables[0], '\x00')
+
+ # get_bare_quoted_string
+
+ def test_get_bare_quoted_string_only(self):
+ bqs = self._test_get_x(parser.get_bare_quoted_string,
+ '"foo"', '"foo"', 'foo', [], '')
+ self.assertEqual(bqs.token_type, 'bare-quoted-string')
+
+ def test_get_bare_quoted_string_must_start_with_dquote(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_bare_quoted_string('foo"')
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_bare_quoted_string(' "foo"')
+
+ def test_get_bare_quoted_string_following_wsp_preserved(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '"foo"\t bar', '"foo"', 'foo', [], '\t bar')
+
+ def test_get_bare_quoted_string_multiple_words(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '"foo bar moo"', '"foo bar moo"', 'foo bar moo', [], '')
+
+ def test_get_bare_quoted_string_multiple_words_wsp_preserved(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '" foo moo\t"', '" foo moo\t"', ' foo moo\t', [], '')
+
+ def test_get_bare_quoted_string_end_dquote_mid_word(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '"foo"bar', '"foo"', 'foo', [], 'bar')
+
+ def test_get_bare_quoted_string_quoted_dquote(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ r'"foo\"in"a', r'"foo\"in"', 'foo"in', [], 'a')
+
+ def test_get_bare_quoted_string_non_printables(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '"a\x01a"', '"a\x01a"', 'a\x01a',
+ [errors.NonPrintableDefect], '')
+
+ def test_get_bare_quoted_string_no_end_dquote(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '"foo', '"foo"', 'foo',
+ [errors.InvalidHeaderDefect], '')
+ self._test_get_x(parser.get_bare_quoted_string,
+ '"foo ', '"foo "', 'foo ',
+ [errors.InvalidHeaderDefect], '')
+
+ def test_get_bare_quoted_string_empty_quotes(self):
+ self._test_get_x(parser.get_bare_quoted_string,
+ '""', '""', '', [], '')
+
+ # get_comment
+
+ def test_get_comment_only(self):
+ comment = self._test_get_x(parser.get_comment,
+ '(comment)', '(comment)', ' ', [], '', ['comment'])
+ self.assertEqual(comment.token_type, 'comment')
+
+ def test_get_comment_must_start_with_paren(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_comment('foo"')
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_comment(' (foo"')
+
+ def test_get_comment_following_wsp_preserved(self):
+ self._test_get_x(parser.get_comment,
+ '(comment) \t', '(comment)', ' ', [], ' \t', ['comment'])
+
+ def test_get_comment_multiple_words(self):
+ self._test_get_x(parser.get_comment,
+ '(foo bar) \t', '(foo bar)', ' ', [], ' \t', ['foo bar'])
+
+ def test_get_comment_multiple_words_wsp_preserved(self):
+ self._test_get_x(parser.get_comment,
+ '( foo bar\t ) \t', '( foo bar\t )', ' ', [], ' \t',
+ [' foo bar\t '])
+
+ def test_get_comment_end_paren_mid_word(self):
+ self._test_get_x(parser.get_comment,
+ '(foo)bar', '(foo)', ' ', [], 'bar', ['foo'])
+
+ def test_get_comment_quoted_parens(self):
+ self._test_get_x(parser.get_comment,
+ '(foo\) \(\)bar)', '(foo\) \(\)bar)', ' ', [], '', ['foo) ()bar'])
+
+ def test_get_comment_non_printable(self):
+ self._test_get_x(parser.get_comment,
+ '(foo\x7Fbar)', '(foo\x7Fbar)', ' ',
+ [errors.NonPrintableDefect], '', ['foo\x7Fbar'])
+
+ def test_get_comment_no_end_paren(self):
+ self._test_get_x(parser.get_comment,
+ '(foo bar', '(foo bar)', ' ',
+ [errors.InvalidHeaderDefect], '', ['foo bar'])
+ self._test_get_x(parser.get_comment,
+ '(foo bar ', '(foo bar )', ' ',
+ [errors.InvalidHeaderDefect], '', ['foo bar '])
+
+ def test_get_comment_nested_comment(self):
+ comment = self._test_get_x(parser.get_comment,
+ '(foo(bar))', '(foo(bar))', ' ', [], '', ['foo(bar)'])
+ self.assertEqual(comment[1].content, 'bar')
+
+ def test_get_comment_nested_comment_wsp(self):
+ comment = self._test_get_x(parser.get_comment,
+ '(foo ( bar ) )', '(foo ( bar ) )', ' ', [], '', ['foo ( bar ) '])
+ self.assertEqual(comment[2].content, ' bar ')
+
+ def test_get_comment_empty_comment(self):
+ self._test_get_x(parser.get_comment,
+ '()', '()', ' ', [], '', [''])
+
+ def test_get_comment_multiple_nesting(self):
+ comment = self._test_get_x(parser.get_comment,
+ '(((((foo)))))', '(((((foo)))))', ' ', [], '', ['((((foo))))'])
+ for i in range(4, 0, -1):
+ self.assertEqual(comment[0].content, '('*(i-1)+'foo'+')'*(i-1))
+ comment = comment[0]
+ self.assertEqual(comment.content, 'foo')
+
+ def test_get_comment_missing_end_of_nesting(self):
+ self._test_get_x(parser.get_comment,
+ '(((((foo)))', '(((((foo)))))', ' ',
+ [errors.InvalidHeaderDefect]*2, '', ['((((foo))))'])
+
+ def test_get_comment_qs_in_nested_comment(self):
+ comment = self._test_get_x(parser.get_comment,
+ '(foo (b\)))', '(foo (b\)))', ' ', [], '', ['foo (b\))'])
+ self.assertEqual(comment[2].content, 'b)')
+
+ # get_cfws
+
+ def test_get_cfws_only_ws(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ ' \t \t', ' \t \t', ' ', [], '', [])
+ self.assertEqual(cfws.token_type, 'cfws')
+
+ def test_get_cfws_only_comment(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ '(foo)', '(foo)', ' ', [], '', ['foo'])
+ self.assertEqual(cfws[0].content, 'foo')
+
+ def test_get_cfws_only_mixed(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ ' (foo ) ( bar) ', ' (foo ) ( bar) ', ' ', [], '',
+ ['foo ', ' bar'])
+ self.assertEqual(cfws[1].content, 'foo ')
+ self.assertEqual(cfws[3].content, ' bar')
+
+ def test_get_cfws_ends_at_non_leader(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ '(foo) bar', '(foo) ', ' ', [], 'bar', ['foo'])
+ self.assertEqual(cfws[0].content, 'foo')
+
+ def test_get_cfws_ends_at_non_printable(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ '(foo) \x07', '(foo) ', ' ', [], '\x07', ['foo'])
+ self.assertEqual(cfws[0].content, 'foo')
+
+ def test_get_cfws_non_printable_in_comment(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ '(foo \x07) "test"', '(foo \x07) ', ' ',
+ [errors.NonPrintableDefect], '"test"', ['foo \x07'])
+ self.assertEqual(cfws[0].content, 'foo \x07')
+
+ def test_get_cfws_header_ends_in_comment(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ ' (foo ', ' (foo )', ' ',
+ [errors.InvalidHeaderDefect], '', ['foo '])
+ self.assertEqual(cfws[1].content, 'foo ')
+
+ def test_get_cfws_multiple_nested_comments(self):
+ cfws = self._test_get_x(parser.get_cfws,
+ '(foo (bar)) ((a)(a))', '(foo (bar)) ((a)(a))', ' ', [],
+ '', ['foo (bar)', '(a)(a)'])
+ self.assertEqual(cfws[0].comments, ['foo (bar)'])
+ self.assertEqual(cfws[2].comments, ['(a)(a)'])
+
+ # get_quoted_string
+
+ def test_get_quoted_string_only(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ '"bob"', '"bob"', 'bob', [], '')
+ self.assertEqual(qs.token_type, 'quoted-string')
+ self.assertEqual(qs.quoted_value, '"bob"')
+ self.assertEqual(qs.content, 'bob')
+
+ def test_get_quoted_string_with_wsp(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ '\t "bob" ', '\t "bob" ', ' bob ', [], '')
+ self.assertEqual(qs.quoted_value, ' "bob" ')
+ self.assertEqual(qs.content, 'bob')
+
+ def test_get_quoted_string_with_comments_and_wsp(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (foo) "bob"(bar)', ' (foo) "bob"(bar)', ' bob ', [], '')
+ self.assertEqual(qs[0][1].content, 'foo')
+ self.assertEqual(qs[2][0].content, 'bar')
+ self.assertEqual(qs.content, 'bob')
+ self.assertEqual(qs.quoted_value, ' "bob" ')
+
+ def test_get_quoted_string_with_multiple_comments(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (foo) (bar) "bob"(bird)', ' (foo) (bar) "bob"(bird)', ' bob ',
+ [], '')
+ self.assertEqual(qs[0].comments, ['foo', 'bar'])
+ self.assertEqual(qs[2].comments, ['bird'])
+ self.assertEqual(qs.content, 'bob')
+ self.assertEqual(qs.quoted_value, ' "bob" ')
+
+ def test_get_quoted_string_non_printable_in_comment(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (\x0A) "bob"', ' (\x0A) "bob"', ' bob',
+ [errors.NonPrintableDefect], '')
+ self.assertEqual(qs[0].comments, ['\x0A'])
+ self.assertEqual(qs.content, 'bob')
+ self.assertEqual(qs.quoted_value, ' "bob"')
+
+ def test_get_quoted_string_non_printable_in_qcontent(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (a) "a\x0B"', ' (a) "a\x0B"', ' a\x0B',
+ [errors.NonPrintableDefect], '')
+ self.assertEqual(qs[0].comments, ['a'])
+ self.assertEqual(qs.content, 'a\x0B')
+ self.assertEqual(qs.quoted_value, ' "a\x0B"')
+
+ def test_get_quoted_string_internal_ws(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (a) "foo bar "', ' (a) "foo bar "', ' foo bar ',
+ [], '')
+ self.assertEqual(qs[0].comments, ['a'])
+ self.assertEqual(qs.content, 'foo bar ')
+ self.assertEqual(qs.quoted_value, ' "foo bar "')
+
+ def test_get_quoted_string_header_ends_in_comment(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (a) "bob" (a', ' (a) "bob" (a)', ' bob ',
+ [errors.InvalidHeaderDefect], '')
+ self.assertEqual(qs[0].comments, ['a'])
+ self.assertEqual(qs[2].comments, ['a'])
+ self.assertEqual(qs.content, 'bob')
+ self.assertEqual(qs.quoted_value, ' "bob" ')
+
+ def test_get_quoted_string_header_ends_in_qcontent(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ ' (a) "bob', ' (a) "bob"', ' bob',
+ [errors.InvalidHeaderDefect], '')
+ self.assertEqual(qs[0].comments, ['a'])
+ self.assertEqual(qs.content, 'bob')
+ self.assertEqual(qs.quoted_value, ' "bob"')
+
+ def test_get_quoted_string_no_quoted_string(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_quoted_string(' (ab) xyz')
+
+ def test_get_quoted_string_qs_ends_at_noncfws(self):
+ qs = self._test_get_x(parser.get_quoted_string,
+ '\t "bob" fee', '\t "bob" ', ' bob ', [], 'fee')
+ self.assertEqual(qs.content, 'bob')
+ self.assertEqual(qs.quoted_value, ' "bob" ')
+
+ # get_atom
+
+ def test_get_atom_only(self):
+ atom = self._test_get_x(parser.get_atom,
+ 'bob', 'bob', 'bob', [], '')
+ self.assertEqual(atom.token_type, 'atom')
+
+ def test_get_atom_with_wsp(self):
+ self._test_get_x(parser.get_atom,
+ '\t bob ', '\t bob ', ' bob ', [], '')
+
+ def test_get_atom_with_comments_and_wsp(self):
+ atom = self._test_get_x(parser.get_atom,
+ ' (foo) bob(bar)', ' (foo) bob(bar)', ' bob ', [], '')
+ self.assertEqual(atom[0][1].content, 'foo')
+ self.assertEqual(atom[2][0].content, 'bar')
+
+ def test_get_atom_with_multiple_comments(self):
+ atom = self._test_get_x(parser.get_atom,
+ ' (foo) (bar) bob(bird)', ' (foo) (bar) bob(bird)', ' bob ',
+ [], '')
+ self.assertEqual(atom[0].comments, ['foo', 'bar'])
+ self.assertEqual(atom[2].comments, ['bird'])
+
+ def test_get_atom_non_printable_in_comment(self):
+ atom = self._test_get_x(parser.get_atom,
+ ' (\x0A) bob', ' (\x0A) bob', ' bob',
+ [errors.NonPrintableDefect], '')
+ self.assertEqual(atom[0].comments, ['\x0A'])
+
+ def test_get_atom_non_printable_in_atext(self):
+ atom = self._test_get_x(parser.get_atom,
+ ' (a) a\x0B', ' (a) a\x0B', ' a\x0B',
+ [errors.NonPrintableDefect], '')
+ self.assertEqual(atom[0].comments, ['a'])
+
+ def test_get_atom_header_ends_in_comment(self):
+ atom = self._test_get_x(parser.get_atom,
+ ' (a) bob (a', ' (a) bob (a)', ' bob ',
+ [errors.InvalidHeaderDefect], '')
+ self.assertEqual(atom[0].comments, ['a'])
+ self.assertEqual(atom[2].comments, ['a'])
+
+ def test_get_atom_no_atom(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_atom(' (ab) ')
+
+ def test_get_atom_no_atom_before_special(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_atom(' (ab) @')
+
+ def test_get_atom_atom_ends_at_special(self):
+ atom = self._test_get_x(parser.get_atom,
+ ' (foo) bob(bar) @bang', ' (foo) bob(bar) ', ' bob ', [], '@bang')
+ self.assertEqual(atom[0].comments, ['foo'])
+ self.assertEqual(atom[2].comments, ['bar'])
+
+ def test_get_atom_atom_ends_at_noncfws(self):
+ atom = self._test_get_x(parser.get_atom,
+ 'bob fred', 'bob ', 'bob ', [], 'fred')
+
+ # get_dot_atom_text
+
+ def test_get_dot_atom_text(self):
+ dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
+ 'foo.bar.bang', 'foo.bar.bang', 'foo.bar.bang', [], '')
+ self.assertEqual(dot_atom_text.token_type, 'dot-atom-text')
+ self.assertEqual(len(dot_atom_text), 5)
+
+ def test_get_dot_atom_text_lone_atom_is_valid(self):
+ dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
+ 'foo', 'foo', 'foo', [], '')
+
+ def test_get_dot_atom_text_raises_on_leading_dot(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom_text('.foo.bar')
+
+ def test_get_dot_atom_text_raises_on_trailing_dot(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom_text('foo.bar.')
+
+ def test_get_dot_atom_text_raises_on_leading_non_atext(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom_text(' foo.bar')
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom_text('@foo.bar')
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom_text('"foo.bar"')
+
+ def test_get_dot_atom_text_trailing_text_preserved(self):
+ dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
+ 'foo@bar', 'foo', 'foo', [], '@bar')
+
+ def test_get_dot_atom_text_trailing_ws_preserved(self):
+ dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
+ 'foo .bar', 'foo', 'foo', [], ' .bar')
+
+ # get_dot_atom
+
+ def test_get_dot_atom_only(self):
+ dot_atom = self._test_get_x(parser.get_dot_atom,
+ 'foo.bar.bing', 'foo.bar.bing', 'foo.bar.bing', [], '')
+ self.assertEqual(dot_atom.token_type, 'dot-atom')
+ self.assertEqual(len(dot_atom), 1)
+
+ def test_get_dot_atom_with_wsp(self):
+ self._test_get_x(parser.get_dot_atom,
+ '\t foo.bar.bing ', '\t foo.bar.bing ', ' foo.bar.bing ', [], '')
+
+ def test_get_dot_atom_with_comments_and_wsp(self):
+ self._test_get_x(parser.get_dot_atom,
+ ' (sing) foo.bar.bing (here) ', ' (sing) foo.bar.bing (here) ',
+ ' foo.bar.bing ', [], '')
+
+ def test_get_dot_atom_space_ends_dot_atom(self):
+ self._test_get_x(parser.get_dot_atom,
+ ' (sing) foo.bar .bing (here) ', ' (sing) foo.bar ',
+ ' foo.bar ', [], '.bing (here) ')
+
+ def test_get_dot_atom_no_atom_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom(' (foo) ')
+
+ def test_get_dot_atom_leading_dot_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom(' (foo) .bar')
+
+ def test_get_dot_atom_two_dots_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom('bar..bang')
+
+ def test_get_dot_atom_trailing_dot_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_dot_atom(' (foo) bar.bang. foo')
+
+ # get_word (if this were black box we'd repeat all the qs/atom tests)
+
+ def test_get_word_atom_yields_atom(self):
+ word = self._test_get_x(parser.get_word,
+ ' (foo) bar (bang) :ah', ' (foo) bar (bang) ', ' bar ', [], ':ah')
+ self.assertEqual(word.token_type, 'atom')
+ self.assertEqual(word[0].token_type, 'cfws')
+
+ def test_get_word_qs_yields_qs(self):
+ word = self._test_get_x(parser.get_word,
+ '"bar " (bang) ah', '"bar " (bang) ', 'bar ', [], 'ah')
+ self.assertEqual(word.token_type, 'quoted-string')
+ self.assertEqual(word[0].token_type, 'bare-quoted-string')
+ self.assertEqual(word[0].value, 'bar ')
+ self.assertEqual(word.content, 'bar ')
+
+ def test_get_word_ends_at_dot(self):
+ self._test_get_x(parser.get_word,
+ 'foo.', 'foo', 'foo', [], '.')
+
+ # get_phrase
+
+ def test_get_phrase_simple(self):
+ phrase = self._test_get_x(parser.get_phrase,
+ '"Fred A. Johnson" is his name, oh.',
+ '"Fred A. Johnson" is his name',
+ 'Fred A. Johnson is his name',
+ [],
+ ', oh.')
+ self.assertEqual(phrase.token_type, 'phrase')
+
+ def test_get_phrase_complex(self):
+ phrase = self._test_get_x(parser.get_phrase,
+ ' (A) bird (in (my|your)) "hand " is messy\t<>\t',
+ ' (A) bird (in (my|your)) "hand " is messy\t',
+ ' bird hand is messy ',
+ [],
+ '<>\t')
+ self.assertEqual(phrase[0][0].comments, ['A'])
+ self.assertEqual(phrase[0][2].comments, ['in (my|your)'])
+
+ def test_get_phrase_obsolete(self):
+ phrase = self._test_get_x(parser.get_phrase,
+ 'Fred A.(weird).O Johnson',
+ 'Fred A.(weird).O Johnson',
+ 'Fred A. .O Johnson',
+ [errors.ObsoleteHeaderDefect]*3,
+ '')
+ self.assertEqual(len(phrase), 7)
+ self.assertEqual(phrase[3].comments, ['weird'])
+
+ def test_get_phrase_pharse_must_start_with_word(self):
+ phrase = self._test_get_x(parser.get_phrase,
+ '(even weirder).name',
+ '(even weirder).name',
+ ' .name',
+ [errors.InvalidHeaderDefect] + [errors.ObsoleteHeaderDefect]*2,
+ '')
+ self.assertEqual(len(phrase), 3)
+ self.assertEqual(phrase[0].comments, ['even weirder'])
+
+ def test_get_phrase_ending_with_obsolete(self):
+ phrase = self._test_get_x(parser.get_phrase,
+ 'simple phrase.(with trailing comment):boo',
+ 'simple phrase.(with trailing comment)',
+ 'simple phrase. ',
+ [errors.ObsoleteHeaderDefect]*2,
+ ':boo')
+ self.assertEqual(len(phrase), 4)
+ self.assertEqual(phrase[3].comments, ['with trailing comment'])
+
+ def get_phrase_cfws_only_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_phrase(' (foo) ')
+
+ # get_local_part
+
+ def test_get_local_part_simple(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ 'dinsdale@python.org', 'dinsdale', 'dinsdale', [], '@python.org')
+ self.assertEqual(local_part.token_type, 'local-part')
+ self.assertEqual(local_part.local_part, 'dinsdale')
+
+ def test_get_local_part_with_dot(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ 'Fred.A.Johnson@python.org',
+ 'Fred.A.Johnson',
+ 'Fred.A.Johnson',
+ [],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
+
+ def test_get_local_part_with_whitespace(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' Fred.A.Johnson @python.org',
+ ' Fred.A.Johnson ',
+ ' Fred.A.Johnson ',
+ [],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
+
+ def test_get_local_part_with_cfws(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' (foo) Fred.A.Johnson (bar (bird)) @python.org',
+ ' (foo) Fred.A.Johnson (bar (bird)) ',
+ ' Fred.A.Johnson ',
+ [],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
+ self.assertEqual(local_part[0][0].comments, ['foo'])
+ self.assertEqual(local_part[0][2].comments, ['bar (bird)'])
+
+ def test_get_local_part_simple_quoted(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ '"dinsdale"@python.org', '"dinsdale"', '"dinsdale"', [], '@python.org')
+ self.assertEqual(local_part.token_type, 'local-part')
+ self.assertEqual(local_part.local_part, 'dinsdale')
+
+ def test_get_local_part_with_quoted_dot(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ '"Fred.A.Johnson"@python.org',
+ '"Fred.A.Johnson"',
+ '"Fred.A.Johnson"',
+ [],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
+
+ def test_get_local_part_quoted_with_whitespace(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' "Fred A. Johnson" @python.org',
+ ' "Fred A. Johnson" ',
+ ' "Fred A. Johnson" ',
+ [],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred A. Johnson')
+
+ def test_get_local_part_quoted_with_cfws(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' (foo) " Fred A. Johnson " (bar (bird)) @python.org',
+ ' (foo) " Fred A. Johnson " (bar (bird)) ',
+ ' " Fred A. Johnson " ',
+ [],
+ '@python.org')
+ self.assertEqual(local_part.local_part, ' Fred A. Johnson ')
+ self.assertEqual(local_part[0][0].comments, ['foo'])
+ self.assertEqual(local_part[0][2].comments, ['bar (bird)'])
+
+
+ def test_get_local_part_simple_obsolete(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ 'Fred. A.Johnson@python.org',
+ 'Fred. A.Johnson',
+ 'Fred. A.Johnson',
+ [errors.ObsoleteHeaderDefect],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
+
+ def test_get_local_part_complex_obsolete_1(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' (foo )Fred (bar).(bird) A.(sheep)Johnson."and dogs "@python.org',
+ ' (foo )Fred (bar).(bird) A.(sheep)Johnson."and dogs "',
+ ' Fred . A. Johnson.and dogs ',
+ [errors.ObsoleteHeaderDefect],
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson.and dogs ')
+
+ def test_get_local_part_complex_obsolete_invalid(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' (foo )Fred (bar).(bird) A.(sheep)Johnson "and dogs"@python.org',
+ ' (foo )Fred (bar).(bird) A.(sheep)Johnson "and dogs"',
+ ' Fred . A. Johnson and dogs',
+ [errors.InvalidHeaderDefect]*2,
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'Fred.A.Johnson and dogs')
+
+ def test_get_local_part_no_part_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_local_part(' (foo) ')
+
+ def test_get_local_part_special_instead_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_local_part(' (foo) @python.org')
+
+ def test_get_local_part_trailing_dot(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' borris.@python.org',
+ ' borris.',
+ ' borris.',
+ [errors.InvalidHeaderDefect]*2,
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'borris.')
+
+ def test_get_local_part_trailing_dot_with_ws(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' borris. @python.org',
+ ' borris. ',
+ ' borris. ',
+ [errors.InvalidHeaderDefect]*2,
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'borris.')
+
+ def test_get_local_part_leading_dot(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ '.borris@python.org',
+ '.borris',
+ '.borris',
+ [errors.InvalidHeaderDefect]*2,
+ '@python.org')
+ self.assertEqual(local_part.local_part, '.borris')
+
+ def test_get_local_part_leading_dot_after_ws(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' .borris@python.org',
+ ' .borris',
+ ' .borris',
+ [errors.InvalidHeaderDefect]*2,
+ '@python.org')
+ self.assertEqual(local_part.local_part, '.borris')
+
+ def test_get_local_part_double_dot_raises(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ ' borris.(foo).natasha@python.org',
+ ' borris.(foo).natasha',
+ ' borris. .natasha',
+ [errors.InvalidHeaderDefect]*2,
+ '@python.org')
+ self.assertEqual(local_part.local_part, 'borris..natasha')
+
+ def test_get_local_part_quoted_strings_in_atom_list(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ '""example" example"@example.com',
+ '""example" example"',
+ 'example example',
+ [errors.InvalidHeaderDefect]*3,
+ '@example.com')
+ self.assertEqual(local_part.local_part, 'example example')
+
+ def test_get_local_part_valid_and_invalid_qp_in_atom_list(self):
+ local_part = self._test_get_x(parser.get_local_part,
+ r'"\\"example\\" example"@example.com',
+ r'"\\"example\\" example"',
+ r'\example\\ example',
+ [errors.InvalidHeaderDefect]*5,
+ '@example.com')
+ self.assertEqual(local_part.local_part, r'\example\\ example')
+
+ def test_get_local_part_unicode_defect(self):
+ # Currently this only happens when parsing unicode, not when parsing
+ # stuff that was originally binary.
+ local_part = self._test_get_x(parser.get_local_part,
+ 'exámple@example.com',
+ 'exámple',
+ 'exámple',
+ [errors.NonASCIILocalPartDefect],
+ '@example.com')
+ self.assertEqual(local_part.local_part, 'exámple')
+
+ # get_dtext
+
+ def test_get_dtext_only(self):
+ dtext = self._test_get_x(parser.get_dtext,
+ 'foobar', 'foobar', 'foobar', [], '')
+ self.assertEqual(dtext.token_type, 'ptext')
+
+ def test_get_dtext_all_dtext(self):
+ dtext = self._test_get_x(parser.get_dtext, self.rfc_dtext_chars,
+ self.rfc_dtext_chars,
+ self.rfc_dtext_chars, [], '')
+
+ def test_get_dtext_two_words_gets_first(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo bar', 'foo', 'foo', [], ' bar')
+
+ def test_get_dtext_following_wsp_preserved(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo \t\tbar', 'foo', 'foo', [], ' \t\tbar')
+
+ def test_get_dtext_non_printables(self):
+ dtext = self._test_get_x(parser.get_dtext,
+ 'foo\x00bar]', 'foo\x00bar', 'foo\x00bar',
+ [errors.NonPrintableDefect], ']')
+ self.assertEqual(dtext.defects[0].non_printables[0], '\x00')
+
+ def test_get_dtext_with_qp(self):
+ ptext = self._test_get_x(parser.get_dtext,
+ r'foo\]\[\\bar\b\e\l\l',
+ r'foo][\barbell',
+ r'foo][\barbell',
+ [errors.ObsoleteHeaderDefect],
+ '')
+
+ def test_get_dtext_up_to_close_bracket_only(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo]', 'foo', 'foo', [], ']')
+
+ def test_get_dtext_wsp_before_close_bracket_preserved(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo ]', 'foo', 'foo', [], ' ]')
+
+ def test_get_dtext_close_bracket_mid_word(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo]bar', 'foo', 'foo', [], ']bar')
+
+ def test_get_dtext_up_to_open_bracket_only(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo[', 'foo', 'foo', [], '[')
+
+ def test_get_dtext_wsp_before_open_bracket_preserved(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo [', 'foo', 'foo', [], ' [')
+
+ def test_get_dtext_open_bracket_mid_word(self):
+ self._test_get_x(parser.get_dtext,
+ 'foo[bar', 'foo', 'foo', [], '[bar')
+
+ # get_domain_literal
+
+ def test_get_domain_literal_only(self):
+ domain_literal = domain_literal = self._test_get_x(parser.get_domain_literal,
+ '[127.0.0.1]',
+ '[127.0.0.1]',
+ '[127.0.0.1]',
+ [],
+ '')
+ self.assertEqual(domain_literal.token_type, 'domain-literal')
+ self.assertEqual(domain_literal.domain, '[127.0.0.1]')
+ self.assertEqual(domain_literal.ip, '127.0.0.1')
+
+ def test_get_domain_literal_with_internal_ws(self):
+ domain_literal = self._test_get_x(parser.get_domain_literal,
+ '[ 127.0.0.1\t ]',
+ '[ 127.0.0.1\t ]',
+ '[ 127.0.0.1 ]',
+ [],
+ '')
+ self.assertEqual(domain_literal.domain, '[127.0.0.1]')
+ self.assertEqual(domain_literal.ip, '127.0.0.1')
+
+ def test_get_domain_literal_with_surrounding_cfws(self):
+ domain_literal = self._test_get_x(parser.get_domain_literal,
+ '(foo)[ 127.0.0.1] (bar)',
+ '(foo)[ 127.0.0.1] (bar)',
+ ' [ 127.0.0.1] ',
+ [],
+ '')
+ self.assertEqual(domain_literal.domain, '[127.0.0.1]')
+ self.assertEqual(domain_literal.ip, '127.0.0.1')
+
+ def test_get_domain_literal_no_start_char_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_domain_literal('(foo) ')
+
+ def test_get_domain_literal_no_start_char_before_special_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_domain_literal('(foo) @')
+
+ def test_get_domain_literal_bad_dtext_char_before_special_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_domain_literal('(foo) [abc[@')
+
+ # get_domain
+
+ def test_get_domain_regular_domain_only(self):
+ domain = self._test_get_x(parser.get_domain,
+ 'example.com',
+ 'example.com',
+ 'example.com',
+ [],
+ '')
+ self.assertEqual(domain.token_type, 'domain')
+ self.assertEqual(domain.domain, 'example.com')
+
+ def test_get_domain_domain_literal_only(self):
+ domain = self._test_get_x(parser.get_domain,
+ '[127.0.0.1]',
+ '[127.0.0.1]',
+ '[127.0.0.1]',
+ [],
+ '')
+ self.assertEqual(domain.token_type, 'domain')
+ self.assertEqual(domain.domain, '[127.0.0.1]')
+
+ def test_get_domain_with_cfws(self):
+ domain = self._test_get_x(parser.get_domain,
+ '(foo) example.com(bar)\t',
+ '(foo) example.com(bar)\t',
+ ' example.com ',
+ [],
+ '')
+ self.assertEqual(domain.domain, 'example.com')
+
+ def test_get_domain_domain_literal_with_cfws(self):
+ domain = self._test_get_x(parser.get_domain,
+ '(foo)[127.0.0.1]\t(bar)',
+ '(foo)[127.0.0.1]\t(bar)',
+ ' [127.0.0.1] ',
+ [],
+ '')
+ self.assertEqual(domain.domain, '[127.0.0.1]')
+
+ def test_get_domain_domain_with_cfws_ends_at_special(self):
+ domain = self._test_get_x(parser.get_domain,
+ '(foo)example.com\t(bar), next',
+ '(foo)example.com\t(bar)',
+ ' example.com ',
+ [],
+ ', next')
+ self.assertEqual(domain.domain, 'example.com')
+
+ def test_get_domain_domain_literal_with_cfws_ends_at_special(self):
+ domain = self._test_get_x(parser.get_domain,
+ '(foo)[127.0.0.1]\t(bar), next',
+ '(foo)[127.0.0.1]\t(bar)',
+ ' [127.0.0.1] ',
+ [],
+ ', next')
+ self.assertEqual(domain.domain, '[127.0.0.1]')
+
+ def test_get_domain_obsolete(self):
+ domain = self._test_get_x(parser.get_domain,
+ '(foo) example . (bird)com(bar)\t',
+ '(foo) example . (bird)com(bar)\t',
+ ' example . com ',
+ [errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(domain.domain, 'example.com')
+
+ def test_get_domain_no_non_cfws_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_domain(" (foo)\t")
+
+ def test_get_domain_no_atom_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_domain(" (foo)\t, broken")
+
+
+ # get_addr_spec
+
+ def test_get_addr_spec_normal(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ [],
+ '')
+ self.assertEqual(addr_spec.token_type, 'addr-spec')
+ self.assertEqual(addr_spec.local_part, 'dinsdale')
+ self.assertEqual(addr_spec.domain, 'example.com')
+ self.assertEqual(addr_spec.addr_spec, 'dinsdale@example.com')
+
+ def test_get_addr_spec_with_doamin_literal(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ 'dinsdale@[127.0.0.1]',
+ 'dinsdale@[127.0.0.1]',
+ 'dinsdale@[127.0.0.1]',
+ [],
+ '')
+ self.assertEqual(addr_spec.local_part, 'dinsdale')
+ self.assertEqual(addr_spec.domain, '[127.0.0.1]')
+ self.assertEqual(addr_spec.addr_spec, 'dinsdale@[127.0.0.1]')
+
+ def test_get_addr_spec_with_cfws(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ '(foo) dinsdale(bar)@ (bird) example.com (bog)',
+ '(foo) dinsdale(bar)@ (bird) example.com (bog)',
+ ' dinsdale@example.com ',
+ [],
+ '')
+ self.assertEqual(addr_spec.local_part, 'dinsdale')
+ self.assertEqual(addr_spec.domain, 'example.com')
+ self.assertEqual(addr_spec.addr_spec, 'dinsdale@example.com')
+
+ def test_get_addr_spec_with_qouoted_string_and_cfws(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ '(foo) "roy a bug"(bar)@ (bird) example.com (bog)',
+ '(foo) "roy a bug"(bar)@ (bird) example.com (bog)',
+ ' "roy a bug"@example.com ',
+ [],
+ '')
+ self.assertEqual(addr_spec.local_part, 'roy a bug')
+ self.assertEqual(addr_spec.domain, 'example.com')
+ self.assertEqual(addr_spec.addr_spec, '"roy a bug"@example.com')
+
+ def test_get_addr_spec_ends_at_special(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ '(foo) "roy a bug"(bar)@ (bird) example.com (bog) , next',
+ '(foo) "roy a bug"(bar)@ (bird) example.com (bog) ',
+ ' "roy a bug"@example.com ',
+ [],
+ ', next')
+ self.assertEqual(addr_spec.local_part, 'roy a bug')
+ self.assertEqual(addr_spec.domain, 'example.com')
+ self.assertEqual(addr_spec.addr_spec, '"roy a bug"@example.com')
+
+ def test_get_addr_spec_quoted_strings_in_atom_list(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ '""example" example"@example.com',
+ '""example" example"@example.com',
+ 'example example@example.com',
+ [errors.InvalidHeaderDefect]*3,
+ '')
+ self.assertEqual(addr_spec.local_part, 'example example')
+ self.assertEqual(addr_spec.domain, 'example.com')
+ self.assertEqual(addr_spec.addr_spec, '"example example"@example.com')
+
+ def test_get_addr_spec_dot_atom(self):
+ addr_spec = self._test_get_x(parser.get_addr_spec,
+ 'star.a.star@example.com',
+ 'star.a.star@example.com',
+ 'star.a.star@example.com',
+ [],
+ '')
+ self.assertEqual(addr_spec.local_part, 'star.a.star')
+ self.assertEqual(addr_spec.domain, 'example.com')
+ self.assertEqual(addr_spec.addr_spec, 'star.a.star@example.com')
+
+ # get_obs_route
+
+ def test_get_obs_route_simple(self):
+ obs_route = self._test_get_x(parser.get_obs_route,
+ '@example.com, @two.example.com:',
+ '@example.com, @two.example.com:',
+ '@example.com, @two.example.com:',
+ [],
+ '')
+ self.assertEqual(obs_route.token_type, 'obs-route')
+ self.assertEqual(obs_route.domains, ['example.com', 'two.example.com'])
+
+ def test_get_obs_route_complex(self):
+ obs_route = self._test_get_x(parser.get_obs_route,
+ '(foo),, (blue)@example.com (bar),@two.(foo) example.com (bird):',
+ '(foo),, (blue)@example.com (bar),@two.(foo) example.com (bird):',
+ ' ,, @example.com ,@two. example.com :',
+ [errors.ObsoleteHeaderDefect], # This is the obs-domain
+ '')
+ self.assertEqual(obs_route.token_type, 'obs-route')
+ self.assertEqual(obs_route.domains, ['example.com', 'two.example.com'])
+
+ def test_get_obs_route_no_route_before_end_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_obs_route('(foo) @example.com,')
+
+ def test_get_obs_route_no_route_before_special_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_obs_route('(foo) [abc],')
+
+ def test_get_obs_route_no_route_before_special_raises2(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_obs_route('(foo) @example.com [abc],')
+
+ # get_angle_addr
+
+ def test_get_angle_addr_simple(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(angle_addr.token_type, 'angle-addr')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_with_cfws(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ ' (foo) <dinsdale@example.com>(bar)',
+ ' (foo) <dinsdale@example.com>(bar)',
+ ' <dinsdale@example.com> ',
+ [],
+ '')
+ self.assertEqual(angle_addr.token_type, 'angle-addr')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_qs_and_domain_literal(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '<"Fred Perfect"@[127.0.0.1]>',
+ '<"Fred Perfect"@[127.0.0.1]>',
+ '<"Fred Perfect"@[127.0.0.1]>',
+ [],
+ '')
+ self.assertEqual(angle_addr.local_part, 'Fred Perfect')
+ self.assertEqual(angle_addr.domain, '[127.0.0.1]')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, '"Fred Perfect"@[127.0.0.1]')
+
+ def test_get_angle_addr_internal_cfws(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '<(foo) dinsdale@example.com(bar)>',
+ '<(foo) dinsdale@example.com(bar)>',
+ '< dinsdale@example.com >',
+ [],
+ '')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_obs_route(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '(foo)<@example.com, (bird) @two.example.com: dinsdale@example.com> (bar) ',
+ '(foo)<@example.com, (bird) @two.example.com: dinsdale@example.com> (bar) ',
+ ' <@example.com, @two.example.com: dinsdale@example.com> ',
+ [errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertEqual(angle_addr.route, ['example.com', 'two.example.com'])
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_missing_closing_angle(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '<dinsdale@example.com',
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ [errors.InvalidHeaderDefect],
+ '')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_missing_closing_angle_with_cfws(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '<dinsdale@example.com (foo)',
+ '<dinsdale@example.com (foo)>',
+ '<dinsdale@example.com >',
+ [errors.InvalidHeaderDefect],
+ '')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_ends_at_special(self):
+ angle_addr = self._test_get_x(parser.get_angle_addr,
+ '<dinsdale@example.com> (foo), next',
+ '<dinsdale@example.com> (foo)',
+ '<dinsdale@example.com> ',
+ [],
+ ', next')
+ self.assertEqual(angle_addr.local_part, 'dinsdale')
+ self.assertEqual(angle_addr.domain, 'example.com')
+ self.assertIsNone(angle_addr.route)
+ self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_angle_addr_no_angle_raise(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_angle_addr('(foo) ')
+
+ def test_get_angle_addr_no_angle_before_special_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_angle_addr('(foo) , next')
+
+ def test_get_angle_addr_no_angle_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_angle_addr('bar')
+
+ def test_get_angle_addr_special_after_angle_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_angle_addr('(foo) <, bar')
+
+ # get_display_name This is phrase but with a different value.
+
+ def test_get_display_name_simple(self):
+ display_name = self._test_get_x(parser.get_display_name,
+ 'Fred A Johnson',
+ 'Fred A Johnson',
+ 'Fred A Johnson',
+ [],
+ '')
+ self.assertEqual(display_name.token_type, 'display-name')
+ self.assertEqual(display_name.display_name, 'Fred A Johnson')
+
+ def test_get_display_name_complex1(self):
+ display_name = self._test_get_x(parser.get_display_name,
+ '"Fred A. Johnson" is his name, oh.',
+ '"Fred A. Johnson" is his name',
+ '"Fred A. Johnson is his name"',
+ [],
+ ', oh.')
+ self.assertEqual(display_name.token_type, 'display-name')
+ self.assertEqual(display_name.display_name, 'Fred A. Johnson is his name')
+
+ def test_get_display_name_complex2(self):
+ display_name = self._test_get_x(parser.get_display_name,
+ ' (A) bird (in (my|your)) "hand " is messy\t<>\t',
+ ' (A) bird (in (my|your)) "hand " is messy\t',
+ ' "bird hand is messy" ',
+ [],
+ '<>\t')
+ self.assertEqual(display_name[0][0].comments, ['A'])
+ self.assertEqual(display_name[0][2].comments, ['in (my|your)'])
+ self.assertEqual(display_name.display_name, 'bird hand is messy')
+
+ def test_get_display_name_obsolete(self):
+ display_name = self._test_get_x(parser.get_display_name,
+ 'Fred A.(weird).O Johnson',
+ 'Fred A.(weird).O Johnson',
+ '"Fred A. .O Johnson"',
+ [errors.ObsoleteHeaderDefect]*3,
+ '')
+ self.assertEqual(len(display_name), 7)
+ self.assertEqual(display_name[3].comments, ['weird'])
+ self.assertEqual(display_name.display_name, 'Fred A. .O Johnson')
+
+ def test_get_display_name_pharse_must_start_with_word(self):
+ display_name = self._test_get_x(parser.get_display_name,
+ '(even weirder).name',
+ '(even weirder).name',
+ ' ".name"',
+ [errors.InvalidHeaderDefect] + [errors.ObsoleteHeaderDefect]*2,
+ '')
+ self.assertEqual(len(display_name), 3)
+ self.assertEqual(display_name[0].comments, ['even weirder'])
+ self.assertEqual(display_name.display_name, '.name')
+
+ def test_get_display_name_ending_with_obsolete(self):
+ display_name = self._test_get_x(parser.get_display_name,
+ 'simple phrase.(with trailing comment):boo',
+ 'simple phrase.(with trailing comment)',
+ '"simple phrase." ',
+ [errors.ObsoleteHeaderDefect]*2,
+ ':boo')
+ self.assertEqual(len(display_name), 4)
+ self.assertEqual(display_name[3].comments, ['with trailing comment'])
+ self.assertEqual(display_name.display_name, 'simple phrase.')
+
+ # get_name_addr
+
+ def test_get_name_addr_angle_addr_only(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(name_addr.token_type, 'name-addr')
+ self.assertIsNone(name_addr.display_name)
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_atom_name(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ 'Dinsdale <dinsdale@example.com>',
+ 'Dinsdale <dinsdale@example.com>',
+ 'Dinsdale <dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(name_addr.token_type, 'name-addr')
+ self.assertEqual(name_addr.display_name, 'Dinsdale')
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_atom_name_with_cfws(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '(foo) Dinsdale (bar) <dinsdale@example.com> (bird)',
+ '(foo) Dinsdale (bar) <dinsdale@example.com> (bird)',
+ ' Dinsdale <dinsdale@example.com> ',
+ [],
+ '')
+ self.assertEqual(name_addr.display_name, 'Dinsdale')
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_name_with_cfws_and_dots(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '(foo) Roy.A.Bear (bar) <dinsdale@example.com> (bird)',
+ '(foo) Roy.A.Bear (bar) <dinsdale@example.com> (bird)',
+ ' "Roy.A.Bear" <dinsdale@example.com> ',
+ [errors.ObsoleteHeaderDefect]*2,
+ '')
+ self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_qs_name(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '"Roy.A.Bear" <dinsdale@example.com>',
+ '"Roy.A.Bear" <dinsdale@example.com>',
+ '"Roy.A.Bear" <dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_with_route(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
+ '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
+ '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
+ [errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertEqual(name_addr.route, ['two.example.com'])
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_ends_at_special(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '"Roy.A.Bear" <dinsdale@example.com>, next',
+ '"Roy.A.Bear" <dinsdale@example.com>',
+ '"Roy.A.Bear" <dinsdale@example.com>',
+ [],
+ ', next')
+ self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
+ self.assertEqual(name_addr.local_part, 'dinsdale')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
+
+ def test_get_name_addr_no_content_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_name_addr(' (foo) ')
+
+ def test_get_name_addr_no_content_before_special_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_name_addr(' (foo) ,')
+
+ def test_get_name_addr_no_angle_after_display_name_raises(self):
+ with self.assertRaises(errors.HeaderParseError):
+ parser.get_name_addr('foo bar')
+
+ # get_mailbox
+
+ def test_get_mailbox_addr_spec_only(self):
+ mailbox = self._test_get_x(parser.get_mailbox,
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ [],
+ '')
+ self.assertEqual(mailbox.token_type, 'mailbox')
+ self.assertIsNone(mailbox.display_name)
+ self.assertEqual(mailbox.local_part, 'dinsdale')
+ self.assertEqual(mailbox.domain, 'example.com')
+ self.assertIsNone(mailbox.route)
+ self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
+
+ def test_get_mailbox_angle_addr_only(self):
+ mailbox = self._test_get_x(parser.get_mailbox,
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ '<dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(mailbox.token_type, 'mailbox')
+ self.assertIsNone(mailbox.display_name)
+ self.assertEqual(mailbox.local_part, 'dinsdale')
+ self.assertEqual(mailbox.domain, 'example.com')
+ self.assertIsNone(mailbox.route)
+ self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
+
+ def test_get_mailbox_name_addr(self):
+ mailbox = self._test_get_x(parser.get_mailbox,
+ '"Roy A. Bear" <dinsdale@example.com>',
+ '"Roy A. Bear" <dinsdale@example.com>',
+ '"Roy A. Bear" <dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(mailbox.token_type, 'mailbox')
+ self.assertEqual(mailbox.display_name, 'Roy A. Bear')
+ self.assertEqual(mailbox.local_part, 'dinsdale')
+ self.assertEqual(mailbox.domain, 'example.com')
+ self.assertIsNone(mailbox.route)
+ self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
+
+ def test_get_mailbox_ends_at_special(self):
+ mailbox = self._test_get_x(parser.get_mailbox,
+ '"Roy A. Bear" <dinsdale@example.com>, rest',
+ '"Roy A. Bear" <dinsdale@example.com>',
+ '"Roy A. Bear" <dinsdale@example.com>',
+ [],
+ ', rest')
+ self.assertEqual(mailbox.token_type, 'mailbox')
+ self.assertEqual(mailbox.display_name, 'Roy A. Bear')
+ self.assertEqual(mailbox.local_part, 'dinsdale')
+ self.assertEqual(mailbox.domain, 'example.com')
+ self.assertIsNone(mailbox.route)
+ self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
+
+ def test_get_mailbox_quoted_strings_in_atom_list(self):
+ mailbox = self._test_get_x(parser.get_mailbox,
+ '""example" example"@example.com',
+ '""example" example"@example.com',
+ 'example example@example.com',
+ [errors.InvalidHeaderDefect]*3,
+ '')
+ self.assertEqual(mailbox.local_part, 'example example')
+ self.assertEqual(mailbox.domain, 'example.com')
+ self.assertEqual(mailbox.addr_spec, '"example example"@example.com')
+
+ # get_mailbox_list
+
+ def test_get_mailbox_list_single_addr(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ [],
+ '')
+ self.assertEqual(mailbox_list.token_type, 'mailbox-list')
+ self.assertEqual(len(mailbox_list.mailboxes), 1)
+ mailbox = mailbox_list.mailboxes[0]
+ self.assertIsNone(mailbox.display_name)
+ self.assertEqual(mailbox.local_part, 'dinsdale')
+ self.assertEqual(mailbox.domain, 'example.com')
+ self.assertIsNone(mailbox.route)
+ self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
+ self.assertEqual(mailbox_list.mailboxes,
+ mailbox_list.all_mailboxes)
+
+ def test_get_mailbox_list_two_simple_addr(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ 'dinsdale@example.com, dinsdale@test.example.com',
+ 'dinsdale@example.com, dinsdale@test.example.com',
+ 'dinsdale@example.com, dinsdale@test.example.com',
+ [],
+ '')
+ self.assertEqual(mailbox_list.token_type, 'mailbox-list')
+ self.assertEqual(len(mailbox_list.mailboxes), 2)
+ self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
+ 'dinsdale@example.com')
+ self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
+ 'dinsdale@test.example.com')
+ self.assertEqual(mailbox_list.mailboxes,
+ mailbox_list.all_mailboxes)
+
+ def test_get_mailbox_list_two_name_addr(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ ('"Roy A. Bear" <dinsdale@example.com>,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ [],
+ '')
+ self.assertEqual(len(mailbox_list.mailboxes), 2)
+ self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
+ 'dinsdale@example.com')
+ self.assertEqual(mailbox_list.mailboxes[0].display_name,
+ 'Roy A. Bear')
+ self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
+ 'dinsdale@test.example.com')
+ self.assertEqual(mailbox_list.mailboxes[1].display_name,
+ 'Fred Flintstone')
+ self.assertEqual(mailbox_list.mailboxes,
+ mailbox_list.all_mailboxes)
+
+ def test_get_mailbox_list_two_complex(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ ('(foo) "Roy A. Bear" <dinsdale@example.com>(bar),'
+ ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'),
+ ('(foo) "Roy A. Bear" <dinsdale@example.com>(bar),'
+ ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'),
+ (' "Roy A. Bear" <dinsdale@example.com> ,'
+ ' "Fred Flintstone" <dinsdale@test. example.com>'),
+ [errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(len(mailbox_list.mailboxes), 2)
+ self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
+ 'dinsdale@example.com')
+ self.assertEqual(mailbox_list.mailboxes[0].display_name,
+ 'Roy A. Bear')
+ self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
+ 'dinsdale@test.example.com')
+ self.assertEqual(mailbox_list.mailboxes[1].display_name,
+ 'Fred Flintstone')
+ self.assertEqual(mailbox_list.mailboxes,
+ mailbox_list.all_mailboxes)
+
+ def test_get_mailbox_list_unparseable_mailbox_null(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ ('"Roy A. Bear"[] dinsdale@example.com,'
+ ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'),
+ ('"Roy A. Bear"[] dinsdale@example.com,'
+ ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'),
+ ('"Roy A. Bear"[] dinsdale@example.com,'
+ ' "Fred Flintstone" <dinsdale@test. example.com>'),
+ [errors.InvalidHeaderDefect, # the 'extra' text after the local part
+ errors.InvalidHeaderDefect, # the local part with no angle-addr
+ errors.ObsoleteHeaderDefect, # period in extra text (example.com)
+ errors.ObsoleteHeaderDefect], # (bird) in valid address.
+ '')
+ self.assertEqual(len(mailbox_list.mailboxes), 1)
+ self.assertEqual(len(mailbox_list.all_mailboxes), 2)
+ self.assertEqual(mailbox_list.all_mailboxes[0].token_type,
+ 'invalid-mailbox')
+ self.assertIsNone(mailbox_list.all_mailboxes[0].display_name)
+ self.assertEqual(mailbox_list.all_mailboxes[0].local_part,
+ 'Roy A. Bear')
+ self.assertIsNone(mailbox_list.all_mailboxes[0].domain)
+ self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec,
+ '"Roy A. Bear"')
+ self.assertIs(mailbox_list.all_mailboxes[1],
+ mailbox_list.mailboxes[0])
+ self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
+ 'dinsdale@test.example.com')
+ self.assertEqual(mailbox_list.mailboxes[0].display_name,
+ 'Fred Flintstone')
+
+ def test_get_mailbox_list_junk_after_valid_address(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ ('"Roy A. Bear" <dinsdale@example.com>@@,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>@@,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>@@,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ [errors.InvalidHeaderDefect],
+ '')
+ self.assertEqual(len(mailbox_list.mailboxes), 1)
+ self.assertEqual(len(mailbox_list.all_mailboxes), 2)
+ self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec,
+ 'dinsdale@example.com')
+ self.assertEqual(mailbox_list.all_mailboxes[0].display_name,
+ 'Roy A. Bear')
+ self.assertEqual(mailbox_list.all_mailboxes[0].token_type,
+ 'invalid-mailbox')
+ self.assertIs(mailbox_list.all_mailboxes[1],
+ mailbox_list.mailboxes[0])
+ self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
+ 'dinsdale@test.example.com')
+ self.assertEqual(mailbox_list.mailboxes[0].display_name,
+ 'Fred Flintstone')
+
+ def test_get_mailbox_list_empty_list_element(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ ('"Roy A. Bear" <dinsdale@example.com>, (bird),,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>, (bird),,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>, ,,'
+ ' "Fred Flintstone" <dinsdale@test.example.com>'),
+ [errors.ObsoleteHeaderDefect]*2,
+ '')
+ self.assertEqual(len(mailbox_list.mailboxes), 2)
+ self.assertEqual(mailbox_list.all_mailboxes,
+ mailbox_list.mailboxes)
+ self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec,
+ 'dinsdale@example.com')
+ self.assertEqual(mailbox_list.all_mailboxes[0].display_name,
+ 'Roy A. Bear')
+ self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
+ 'dinsdale@test.example.com')
+ self.assertEqual(mailbox_list.mailboxes[1].display_name,
+ 'Fred Flintstone')
+
+ def test_get_mailbox_list_only_empty_elements(self):
+ mailbox_list = self._test_get_x(parser.get_mailbox_list,
+ '(foo),, (bar)',
+ '(foo),, (bar)',
+ ' ,, ',
+ [errors.ObsoleteHeaderDefect]*3,
+ '')
+ self.assertEqual(len(mailbox_list.mailboxes), 0)
+ self.assertEqual(mailbox_list.all_mailboxes,
+ mailbox_list.mailboxes)
+
+ # get_group_list
+
+ def test_get_group_list_cfws_only(self):
+ group_list = self._test_get_x(parser.get_group_list,
+ '(hidden);',
+ '(hidden)',
+ ' ',
+ [],
+ ';')
+ self.assertEqual(group_list.token_type, 'group-list')
+ self.assertEqual(len(group_list.mailboxes), 0)
+ self.assertEqual(group_list.mailboxes,
+ group_list.all_mailboxes)
+
+ def test_get_group_list_mailbox_list(self):
+ group_list = self._test_get_x(parser.get_group_list,
+ 'dinsdale@example.org, "Fred A. Bear" <dinsdale@example.org>',
+ 'dinsdale@example.org, "Fred A. Bear" <dinsdale@example.org>',
+ 'dinsdale@example.org, "Fred A. Bear" <dinsdale@example.org>',
+ [],
+ '')
+ self.assertEqual(group_list.token_type, 'group-list')
+ self.assertEqual(len(group_list.mailboxes), 2)
+ self.assertEqual(group_list.mailboxes,
+ group_list.all_mailboxes)
+ self.assertEqual(group_list.mailboxes[1].display_name,
+ 'Fred A. Bear')
+
+ def test_get_group_list_obs_group_list(self):
+ group_list = self._test_get_x(parser.get_group_list,
+ ', (foo),,(bar)',
+ ', (foo),,(bar)',
+ ', ,, ',
+ [errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(group_list.token_type, 'group-list')
+ self.assertEqual(len(group_list.mailboxes), 0)
+ self.assertEqual(group_list.mailboxes,
+ group_list.all_mailboxes)
+
+ def test_get_group_list_comment_only_invalid(self):
+ group_list = self._test_get_x(parser.get_group_list,
+ '(bar)',
+ '(bar)',
+ ' ',
+ [errors.InvalidHeaderDefect],
+ '')
+ self.assertEqual(group_list.token_type, 'group-list')
+ self.assertEqual(len(group_list.mailboxes), 0)
+ self.assertEqual(group_list.mailboxes,
+ group_list.all_mailboxes)
+
+ # get_group
+
+ def test_get_group_empty(self):
+ group = self._test_get_x(parser.get_group,
+ 'Monty Python:;',
+ 'Monty Python:;',
+ 'Monty Python:;',
+ [],
+ '')
+ self.assertEqual(group.token_type, 'group')
+ self.assertEqual(group.display_name, 'Monty Python')
+ self.assertEqual(len(group.mailboxes), 0)
+ self.assertEqual(group.mailboxes,
+ group.all_mailboxes)
+
+ def test_get_troup_null_addr_spec(self):
+ group = self._test_get_x(parser.get_group,
+ 'foo: <>;',
+ 'foo: <>;',
+ 'foo: <>;',
+ [errors.InvalidHeaderDefect],
+ '')
+ self.assertEqual(group.display_name, 'foo')
+ self.assertEqual(len(group.mailboxes), 0)
+ self.assertEqual(len(group.all_mailboxes), 1)
+ self.assertEqual(group.all_mailboxes[0].value, '<>')
+
+ def test_get_group_cfws_only(self):
+ group = self._test_get_x(parser.get_group,
+ 'Monty Python: (hidden);',
+ 'Monty Python: (hidden);',
+ 'Monty Python: ;',
+ [],
+ '')
+ self.assertEqual(group.token_type, 'group')
+ self.assertEqual(group.display_name, 'Monty Python')
+ self.assertEqual(len(group.mailboxes), 0)
+ self.assertEqual(group.mailboxes,
+ group.all_mailboxes)
+
+ def test_get_group_single_mailbox(self):
+ group = self._test_get_x(parser.get_group,
+ 'Monty Python: "Fred A. Bear" <dinsdale@example.com>;',
+ 'Monty Python: "Fred A. Bear" <dinsdale@example.com>;',
+ 'Monty Python: "Fred A. Bear" <dinsdale@example.com>;',
+ [],
+ '')
+ self.assertEqual(group.token_type, 'group')
+ self.assertEqual(group.display_name, 'Monty Python')
+ self.assertEqual(len(group.mailboxes), 1)
+ self.assertEqual(group.mailboxes,
+ group.all_mailboxes)
+ self.assertEqual(group.mailboxes[0].addr_spec,
+ 'dinsdale@example.com')
+
+ def test_get_group_mixed_list(self):
+ group = self._test_get_x(parser.get_group,
+ ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,'
+ '(foo) Roger <ping@exampele.com>, x@test.example.com;'),
+ ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,'
+ '(foo) Roger <ping@exampele.com>, x@test.example.com;'),
+ ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,'
+ ' Roger <ping@exampele.com>, x@test.example.com;'),
+ [],
+ '')
+ self.assertEqual(group.token_type, 'group')
+ self.assertEqual(group.display_name, 'Monty Python')
+ self.assertEqual(len(group.mailboxes), 3)
+ self.assertEqual(group.mailboxes,
+ group.all_mailboxes)
+ self.assertEqual(group.mailboxes[0].display_name,
+ 'Fred A. Bear')
+ self.assertEqual(group.mailboxes[1].display_name,
+ 'Roger')
+ self.assertEqual(group.mailboxes[2].local_part, 'x')
+
+ def test_get_group_one_invalid(self):
+ group = self._test_get_x(parser.get_group,
+ ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,'
+ '(foo) Roger ping@exampele.com, x@test.example.com;'),
+ ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,'
+ '(foo) Roger ping@exampele.com, x@test.example.com;'),
+ ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,'
+ ' Roger ping@exampele.com, x@test.example.com;'),
+ [errors.InvalidHeaderDefect, # non-angle addr makes local part invalid
+ errors.InvalidHeaderDefect], # and its not obs-local either: no dots.
+ '')
+ self.assertEqual(group.token_type, 'group')
+ self.assertEqual(group.display_name, 'Monty Python')
+ self.assertEqual(len(group.mailboxes), 2)
+ self.assertEqual(len(group.all_mailboxes), 3)
+ self.assertEqual(group.mailboxes[0].display_name,
+ 'Fred A. Bear')
+ self.assertEqual(group.mailboxes[1].local_part, 'x')
+ self.assertIsNone(group.all_mailboxes[1].display_name)
+
+ # get_address
+
+ def test_get_address_simple(self):
+ address = self._test_get_x(parser.get_address,
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ [],
+ '')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 1)
+ self.assertEqual(address.mailboxes,
+ address.all_mailboxes)
+ self.assertEqual(address.mailboxes[0].domain,
+ 'example.com')
+ self.assertEqual(address[0].token_type,
+ 'mailbox')
+
+ def test_get_address_complex(self):
+ address = self._test_get_x(parser.get_address,
+ '(foo) "Fred A. Bear" <(bird)dinsdale@example.com>',
+ '(foo) "Fred A. Bear" <(bird)dinsdale@example.com>',
+ ' "Fred A. Bear" < dinsdale@example.com>',
+ [],
+ '')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 1)
+ self.assertEqual(address.mailboxes,
+ address.all_mailboxes)
+ self.assertEqual(address.mailboxes[0].display_name,
+ 'Fred A. Bear')
+ self.assertEqual(address[0].token_type,
+ 'mailbox')
+
+ def test_get_address_empty_group(self):
+ address = self._test_get_x(parser.get_address,
+ 'Monty Python:;',
+ 'Monty Python:;',
+ 'Monty Python:;',
+ [],
+ '')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 0)
+ self.assertEqual(address.mailboxes,
+ address.all_mailboxes)
+ self.assertEqual(address[0].token_type,
+ 'group')
+ self.assertEqual(address[0].display_name,
+ 'Monty Python')
+
+ def test_get_address_group(self):
+ address = self._test_get_x(parser.get_address,
+ 'Monty Python: x@example.com, y@example.com;',
+ 'Monty Python: x@example.com, y@example.com;',
+ 'Monty Python: x@example.com, y@example.com;',
+ [],
+ '')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 2)
+ self.assertEqual(address.mailboxes,
+ address.all_mailboxes)
+ self.assertEqual(address[0].token_type,
+ 'group')
+ self.assertEqual(address[0].display_name,
+ 'Monty Python')
+ self.assertEqual(address.mailboxes[0].local_part, 'x')
+
+ def test_get_address_quoted_local_part(self):
+ address = self._test_get_x(parser.get_address,
+ '"foo bar"@example.com',
+ '"foo bar"@example.com',
+ '"foo bar"@example.com',
+ [],
+ '')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 1)
+ self.assertEqual(address.mailboxes,
+ address.all_mailboxes)
+ self.assertEqual(address.mailboxes[0].domain,
+ 'example.com')
+ self.assertEqual(address.mailboxes[0].local_part,
+ 'foo bar')
+ self.assertEqual(address[0].token_type, 'mailbox')
+
+ def test_get_address_ends_at_special(self):
+ address = self._test_get_x(parser.get_address,
+ 'dinsdale@example.com, next',
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ [],
+ ', next')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 1)
+ self.assertEqual(address.mailboxes,
+ address.all_mailboxes)
+ self.assertEqual(address.mailboxes[0].domain,
+ 'example.com')
+ self.assertEqual(address[0].token_type, 'mailbox')
+
+ def test_get_address_invalid_mailbox_invalid(self):
+ address = self._test_get_x(parser.get_address,
+ 'ping example.com, next',
+ 'ping example.com',
+ 'ping example.com',
+ [errors.InvalidHeaderDefect, # addr-spec with no domain
+ errors.InvalidHeaderDefect, # invalid local-part
+ errors.InvalidHeaderDefect, # missing .s in local-part
+ ],
+ ', next')
+ self.assertEqual(address.token_type, 'address')
+ self.assertEqual(len(address.mailboxes), 0)
+ self.assertEqual(len(address.all_mailboxes), 1)
+ self.assertIsNone(address.all_mailboxes[0].domain)
+ self.assertEqual(address.all_mailboxes[0].local_part, 'ping example.com')
+ self.assertEqual(address[0].token_type, 'invalid-mailbox')
+
+ def test_get_address_quoted_strings_in_atom_list(self):
+ address = self._test_get_x(parser.get_address,
+ '""example" example"@example.com',
+ '""example" example"@example.com',
+ 'example example@example.com',
+ [errors.InvalidHeaderDefect]*3,
+ '')
+ self.assertEqual(address.all_mailboxes[0].local_part, 'example example')
+ self.assertEqual(address.all_mailboxes[0].domain, 'example.com')
+ self.assertEqual(address.all_mailboxes[0].addr_spec, '"example example"@example.com')
+
+
+ # get_address_list
+
+ def test_get_address_list_mailboxes_simple(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ 'dinsdale@example.com',
+ [],
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 1)
+ self.assertEqual(address_list.mailboxes,
+ address_list.all_mailboxes)
+ self.assertEqual([str(x) for x in address_list.mailboxes],
+ [str(x) for x in address_list.addresses])
+ self.assertEqual(address_list.mailboxes[0].domain, 'example.com')
+ self.assertEqual(address_list[0].token_type, 'address')
+ self.assertIsNone(address_list[0].display_name)
+
+ def test_get_address_list_mailboxes_two_simple(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ 'foo@example.com, "Fred A. Bar" <bar@example.com>',
+ 'foo@example.com, "Fred A. Bar" <bar@example.com>',
+ 'foo@example.com, "Fred A. Bar" <bar@example.com>',
+ [],
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 2)
+ self.assertEqual(address_list.mailboxes,
+ address_list.all_mailboxes)
+ self.assertEqual([str(x) for x in address_list.mailboxes],
+ [str(x) for x in address_list.addresses])
+ self.assertEqual(address_list.mailboxes[0].local_part, 'foo')
+ self.assertEqual(address_list.mailboxes[1].display_name, "Fred A. Bar")
+
+ def test_get_address_list_mailboxes_complex(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ ('"Roy A. Bear" <dinsdale@example.com>, '
+ '(ping) Foo <x@example.com>,'
+ 'Nobody Is. Special <y@(bird)example.(bad)com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>, '
+ '(ping) Foo <x@example.com>,'
+ 'Nobody Is. Special <y@(bird)example.(bad)com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>, '
+ 'Foo <x@example.com>,'
+ '"Nobody Is. Special" <y@example. com>'),
+ [errors.ObsoleteHeaderDefect, # period in Is.
+ errors.ObsoleteHeaderDefect], # cfws in domain
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 3)
+ self.assertEqual(address_list.mailboxes,
+ address_list.all_mailboxes)
+ self.assertEqual([str(x) for x in address_list.mailboxes],
+ [str(x) for x in address_list.addresses])
+ self.assertEqual(address_list.mailboxes[0].domain, 'example.com')
+ self.assertEqual(address_list.mailboxes[0].token_type, 'mailbox')
+ self.assertEqual(address_list.addresses[0].token_type, 'address')
+ self.assertEqual(address_list.mailboxes[1].local_part, 'x')
+ self.assertEqual(address_list.mailboxes[2].display_name,
+ 'Nobody Is. Special')
+
+ def test_get_address_list_mailboxes_invalid_addresses(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ ('"Roy A. Bear" <dinsdale@example.com>, '
+ '(ping) Foo x@example.com[],'
+ 'Nobody Is. Special <(bird)example.(bad)com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>, '
+ '(ping) Foo x@example.com[],'
+ 'Nobody Is. Special <(bird)example.(bad)com>'),
+ ('"Roy A. Bear" <dinsdale@example.com>, '
+ 'Foo x@example.com[],'
+ '"Nobody Is. Special" < example. com>'),
+ [errors.InvalidHeaderDefect, # invalid address in list
+ errors.InvalidHeaderDefect, # 'Foo x' local part invalid.
+ errors.InvalidHeaderDefect, # Missing . in 'Foo x' local part
+ errors.ObsoleteHeaderDefect, # period in 'Is.' disp-name phrase
+ errors.InvalidHeaderDefect, # no domain part in addr-spec
+ errors.ObsoleteHeaderDefect], # addr-spec has comment in it
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 1)
+ self.assertEqual(len(address_list.all_mailboxes), 3)
+ self.assertEqual([str(x) for x in address_list.all_mailboxes],
+ [str(x) for x in address_list.addresses])
+ self.assertEqual(address_list.mailboxes[0].domain, 'example.com')
+ self.assertEqual(address_list.mailboxes[0].token_type, 'mailbox')
+ self.assertEqual(address_list.addresses[0].token_type, 'address')
+ self.assertEqual(address_list.addresses[1].token_type, 'address')
+ self.assertEqual(len(address_list.addresses[0].mailboxes), 1)
+ self.assertEqual(len(address_list.addresses[1].mailboxes), 0)
+ self.assertEqual(len(address_list.addresses[1].mailboxes), 0)
+ self.assertEqual(
+ address_list.addresses[1].all_mailboxes[0].local_part, 'Foo x')
+ self.assertEqual(
+ address_list.addresses[2].all_mailboxes[0].display_name,
+ "Nobody Is. Special")
+
+ def test_get_address_list_group_empty(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ 'Monty Python: ;',
+ 'Monty Python: ;',
+ 'Monty Python: ;',
+ [],
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 0)
+ self.assertEqual(address_list.mailboxes,
+ address_list.all_mailboxes)
+ self.assertEqual(len(address_list.addresses), 1)
+ self.assertEqual(address_list.addresses[0].token_type, 'address')
+ self.assertEqual(address_list.addresses[0].display_name, 'Monty Python')
+ self.assertEqual(len(address_list.addresses[0].mailboxes), 0)
+
+ def test_get_address_list_group_simple(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ 'Monty Python: dinsdale@example.com;',
+ 'Monty Python: dinsdale@example.com;',
+ 'Monty Python: dinsdale@example.com;',
+ [],
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 1)
+ self.assertEqual(address_list.mailboxes,
+ address_list.all_mailboxes)
+ self.assertEqual(address_list.mailboxes[0].domain, 'example.com')
+ self.assertEqual(address_list.addresses[0].display_name,
+ 'Monty Python')
+ self.assertEqual(address_list.addresses[0].mailboxes[0].domain,
+ 'example.com')
+
+ def test_get_address_list_group_and_mailboxes(self):
+ address_list = self._test_get_x(parser.get_address_list,
+ ('Monty Python: dinsdale@example.com, "Fred" <flint@example.com>;, '
+ 'Abe <x@example.com>, Bee <y@example.com>'),
+ ('Monty Python: dinsdale@example.com, "Fred" <flint@example.com>;, '
+ 'Abe <x@example.com>, Bee <y@example.com>'),
+ ('Monty Python: dinsdale@example.com, "Fred" <flint@example.com>;, '
+ 'Abe <x@example.com>, Bee <y@example.com>'),
+ [],
+ '')
+ self.assertEqual(address_list.token_type, 'address-list')
+ self.assertEqual(len(address_list.mailboxes), 4)
+ self.assertEqual(address_list.mailboxes,
+ address_list.all_mailboxes)
+ self.assertEqual(len(address_list.addresses), 3)
+ self.assertEqual(address_list.mailboxes[0].local_part, 'dinsdale')
+ self.assertEqual(address_list.addresses[0].display_name,
+ 'Monty Python')
+ self.assertEqual(address_list.addresses[0].mailboxes[0].domain,
+ 'example.com')
+ self.assertEqual(address_list.addresses[0].mailboxes[1].local_part,
+ 'flint')
+ self.assertEqual(address_list.addresses[1].mailboxes[0].local_part,
+ 'x')
+ self.assertEqual(address_list.addresses[2].mailboxes[0].local_part,
+ 'y')
+ self.assertEqual(str(address_list.addresses[1]),
+ str(address_list.mailboxes[2]))
+
+
+class TestFolding(TestEmailBase):
+
+ policy = policy.default
+
+ def _test(self, tl, folded, policy=policy):
+ self.assertEqual(tl.fold(policy=policy), folded, tl.ppstr())
+
+ def test_simple_unstructured_no_folds(self):
+ self._test(parser.get_unstructured("This is a test"),
+ "This is a test\n")
+
+ def test_simple_unstructured_folded(self):
+ self._test(parser.get_unstructured("This is also a test, but this "
+ "time there are enough words (and even some "
+ "symbols) to make it wrap; at least in theory."),
+ "This is also a test, but this time there are enough "
+ "words (and even some\n"
+ " symbols) to make it wrap; at least in theory.\n")
+
+ def test_unstructured_with_unicode_no_folds(self):
+ self._test(parser.get_unstructured("hübsch kleiner beißt"),
+ "=?utf-8?q?h=C3=BCbsch_kleiner_bei=C3=9Ft?=\n")
+
+ def test_one_ew_on_each_of_two_wrapped_lines(self):
+ self._test(parser.get_unstructured("Mein kleiner Kaktus ist sehr "
+ "hübsch. Es hat viele Stacheln "
+ "und oft beißt mich."),
+ "Mein kleiner Kaktus ist sehr =?utf-8?q?h=C3=BCbsch=2E?= "
+ "Es hat viele Stacheln\n"
+ " und oft =?utf-8?q?bei=C3=9Ft?= mich.\n")
+
+ def test_ews_combined_before_wrap(self):
+ self._test(parser.get_unstructured("Mein Kaktus ist hübsch. "
+ "Es beißt mich. "
+ "And that's all I'm sayin."),
+ "Mein Kaktus ist =?utf-8?q?h=C3=BCbsch=2E__Es_bei=C3=9Ft?= "
+ "mich. And that's\n"
+ " all I'm sayin.\n")
+
+ # XXX Need test of an encoded word so long that it needs to be wrapped
+
+ def test_simple_address(self):
+ self._test(parser.get_address_list("abc <xyz@example.com>")[0],
+ "abc <xyz@example.com>\n")
+
+ def test_address_list_folding_at_commas(self):
+ self._test(parser.get_address_list('abc <xyz@example.com>, '
+ '"Fred Blunt" <sharp@example.com>, '
+ '"J.P.Cool" <hot@example.com>, '
+ '"K<>y" <key@example.com>, '
+ 'Firesale <cheap@example.com>, '
+ '<end@example.com>')[0],
+ 'abc <xyz@example.com>, "Fred Blunt" <sharp@example.com>,\n'
+ ' "J.P.Cool" <hot@example.com>, "K<>y" <key@example.com>,\n'
+ ' Firesale <cheap@example.com>, <end@example.com>\n')
+
+ def test_address_list_with_unicode_names(self):
+ self._test(parser.get_address_list(
+ 'Hübsch Kaktus <beautiful@example.com>, '
+ 'beißt beißt <biter@example.com>')[0],
+ '=?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>,\n'
+ ' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n')
+
+ def test_address_list_with_unicode_names_in_quotes(self):
+ self._test(parser.get_address_list(
+ '"Hübsch Kaktus" <beautiful@example.com>, '
+ '"beißt" beißt <biter@example.com>')[0],
+ '=?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>,\n'
+ ' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n')
+
+ # XXX Need tests with comments on various sides of a unicode token,
+ # and with unicode tokens in the comments. Spaces inside the quotes
+ # currently don't do the right thing.
+
+ def test_initial_whitespace_splitting(self):
+ body = parser.get_unstructured(' ' + 'x'*77)
+ header = parser.Header([
+ parser.HeaderLabel([parser.ValueTerminal('test:', 'atext')]),
+ parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]), body])
+ self._test(header, 'test: \n ' + 'x'*77 + '\n')
+
+ def test_whitespace_splitting(self):
+ self._test(parser.get_unstructured('xxx ' + 'y'*77),
+ 'xxx \n ' + 'y'*77 + '\n')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_email/test__headerregistry.py b/Lib/test/test_email/test__headerregistry.py
new file mode 100644
index 0000000..4398e29
--- /dev/null
+++ b/Lib/test/test_email/test__headerregistry.py
@@ -0,0 +1,717 @@
+import datetime
+import textwrap
+import unittest
+from email import errors
+from email import policy
+from test.test_email import TestEmailBase
+from email import _headerregistry
+# Address and Group are public but I'm not sure where to put them yet.
+from email._headerregistry import Address, Group
+
+
+class TestHeaderRegistry(TestEmailBase):
+
+ def test_arbitrary_name_unstructured(self):
+ factory = _headerregistry.HeaderRegistry()
+ h = factory('foobar', 'test')
+ self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+
+ def test_name_case_ignored(self):
+ factory = _headerregistry.HeaderRegistry()
+ # Whitebox check that test is valid
+ self.assertNotIn('Subject', factory.registry)
+ h = factory('Subject', 'test')
+ self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, _headerregistry.UniqueUnstructuredHeader)
+
+ class FooBase:
+ def __init__(self, *args, **kw):
+ pass
+
+ def test_override_default_base_class(self):
+ factory = _headerregistry.HeaderRegistry(base_class=self.FooBase)
+ h = factory('foobar', 'test')
+ self.assertIsInstance(h, self.FooBase)
+ self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+
+ class FooDefault:
+ parse = _headerregistry.UnstructuredHeader.parse
+
+ def test_override_default_class(self):
+ factory = _headerregistry.HeaderRegistry(default_class=self.FooDefault)
+ h = factory('foobar', 'test')
+ self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, self.FooDefault)
+
+ def test_override_default_class_only_overrides_default(self):
+ factory = _headerregistry.HeaderRegistry(default_class=self.FooDefault)
+ h = factory('subject', 'test')
+ self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, _headerregistry.UniqueUnstructuredHeader)
+
+ def test_dont_use_default_map(self):
+ factory = _headerregistry.HeaderRegistry(use_default_map=False)
+ h = factory('subject', 'test')
+ self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+
+ def test_map_to_type(self):
+ factory = _headerregistry.HeaderRegistry()
+ h1 = factory('foobar', 'test')
+ factory.map_to_type('foobar', _headerregistry.UniqueUnstructuredHeader)
+ h2 = factory('foobar', 'test')
+ self.assertIsInstance(h1, _headerregistry.BaseHeader)
+ self.assertIsInstance(h1, _headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h2, _headerregistry.BaseHeader)
+ self.assertIsInstance(h2, _headerregistry.UniqueUnstructuredHeader)
+
+
+class TestHeaderBase(TestEmailBase):
+
+ factory = _headerregistry.HeaderRegistry()
+
+ def make_header(self, name, value):
+ return self.factory(name, value)
+
+
+class TestBaseHeaderFeatures(TestHeaderBase):
+
+ def test_str(self):
+ h = self.make_header('subject', 'this is a test')
+ self.assertIsInstance(h, str)
+ self.assertEqual(h, 'this is a test')
+ self.assertEqual(str(h), 'this is a test')
+
+ def test_substr(self):
+ h = self.make_header('subject', 'this is a test')
+ self.assertEqual(h[5:7], 'is')
+
+ def test_has_name(self):
+ h = self.make_header('subject', 'this is a test')
+ self.assertEqual(h.name, 'subject')
+
+ def _test_attr_ro(self, attr):
+ h = self.make_header('subject', 'this is a test')
+ with self.assertRaises(AttributeError):
+ setattr(h, attr, 'foo')
+
+ def test_name_read_only(self):
+ self._test_attr_ro('name')
+
+ def test_defects_read_only(self):
+ self._test_attr_ro('defects')
+
+ def test_defects_is_tuple(self):
+ h = self.make_header('subject', 'this is a test')
+ self.assertEqual(len(h.defects), 0)
+ self.assertIsInstance(h.defects, tuple)
+ # Make sure it is still true when there are defects.
+ h = self.make_header('date', '')
+ self.assertEqual(len(h.defects), 1)
+ self.assertIsInstance(h.defects, tuple)
+
+ # XXX: FIXME
+ #def test_CR_in_value(self):
+ # # XXX: this also re-raises the issue of embedded headers,
+ # # need test and solution for that.
+ # value = '\r'.join(['this is', ' a test'])
+ # h = self.make_header('subject', value)
+ # self.assertEqual(h, value)
+ # self.assertDefectsEqual(h.defects, [errors.ObsoleteHeaderDefect])
+
+ def test_RFC2047_value_decoded(self):
+ value = '=?utf-8?q?this_is_a_test?='
+ h = self.make_header('subject', value)
+ self.assertEqual(h, 'this is a test')
+
+
+class TestDateHeader(TestHeaderBase):
+
+ datestring = 'Sun, 23 Sep 2001 20:10:55 -0700'
+ utcoffset = datetime.timedelta(hours=-7)
+ tz = datetime.timezone(utcoffset)
+ dt = datetime.datetime(2001, 9, 23, 20, 10, 55, tzinfo=tz)
+
+ def test_parse_date(self):
+ h = self.make_header('date', self.datestring)
+ self.assertEqual(h, self.datestring)
+ self.assertEqual(h.datetime, self.dt)
+ self.assertEqual(h.datetime.utcoffset(), self.utcoffset)
+ self.assertEqual(h.defects, ())
+
+ def test_set_from_datetime(self):
+ h = self.make_header('date', self.dt)
+ self.assertEqual(h, self.datestring)
+ self.assertEqual(h.datetime, self.dt)
+ self.assertEqual(h.defects, ())
+
+ def test_date_header_properties(self):
+ h = self.make_header('date', self.datestring)
+ self.assertIsInstance(h, _headerregistry.UniqueDateHeader)
+ self.assertEqual(h.max_count, 1)
+ self.assertEqual(h.defects, ())
+
+ def test_resent_date_header_properties(self):
+ h = self.make_header('resent-date', self.datestring)
+ self.assertIsInstance(h, _headerregistry.DateHeader)
+ self.assertEqual(h.max_count, None)
+ self.assertEqual(h.defects, ())
+
+ def test_no_value_is_defect(self):
+ h = self.make_header('date', '')
+ self.assertEqual(len(h.defects), 1)
+ self.assertIsInstance(h.defects[0], errors.HeaderMissingRequiredValue)
+
+ def test_datetime_read_only(self):
+ h = self.make_header('date', self.datestring)
+ with self.assertRaises(AttributeError):
+ h.datetime = 'foo'
+
+
+class TestAddressHeader(TestHeaderBase):
+
+ examples = {
+
+ 'empty':
+ ('<>',
+ [errors.InvalidHeaderDefect],
+ '<>',
+ '',
+ '<>',
+ '',
+ '',
+ None),
+
+ 'address_only':
+ ('zippy@pinhead.com',
+ [],
+ 'zippy@pinhead.com',
+ '',
+ 'zippy@pinhead.com',
+ 'zippy',
+ 'pinhead.com',
+ None),
+
+ 'name_and_address':
+ ('Zaphrod Beblebrux <zippy@pinhead.com>',
+ [],
+ 'Zaphrod Beblebrux <zippy@pinhead.com>',
+ 'Zaphrod Beblebrux',
+ 'zippy@pinhead.com',
+ 'zippy',
+ 'pinhead.com',
+ None),
+
+ 'quoted_local_part':
+ ('Zaphrod Beblebrux <"foo bar"@pinhead.com>',
+ [],
+ 'Zaphrod Beblebrux <"foo bar"@pinhead.com>',
+ 'Zaphrod Beblebrux',
+ '"foo bar"@pinhead.com',
+ 'foo bar',
+ 'pinhead.com',
+ None),
+
+ 'quoted_parens_in_name':
+ (r'"A \(Special\) Person" <person@dom.ain>',
+ [],
+ '"A (Special) Person" <person@dom.ain>',
+ 'A (Special) Person',
+ 'person@dom.ain',
+ 'person',
+ 'dom.ain',
+ None),
+
+ 'quoted_backslashes_in_name':
+ (r'"Arthur \\Backslash\\ Foobar" <person@dom.ain>',
+ [],
+ r'"Arthur \\Backslash\\ Foobar" <person@dom.ain>',
+ r'Arthur \Backslash\ Foobar',
+ 'person@dom.ain',
+ 'person',
+ 'dom.ain',
+ None),
+
+ 'name_with_dot':
+ ('John X. Doe <jxd@example.com>',
+ [errors.ObsoleteHeaderDefect],
+ '"John X. Doe" <jxd@example.com>',
+ 'John X. Doe',
+ 'jxd@example.com',
+ 'jxd',
+ 'example.com',
+ None),
+
+ 'quoted_strings_in_local_part':
+ ('""example" example"@example.com',
+ [errors.InvalidHeaderDefect]*3,
+ '"example example"@example.com',
+ '',
+ '"example example"@example.com',
+ 'example example',
+ 'example.com',
+ None),
+
+ 'escaped_quoted_strings_in_local_part':
+ (r'"\"example\" example"@example.com',
+ [],
+ r'"\"example\" example"@example.com',
+ '',
+ r'"\"example\" example"@example.com',
+ r'"example" example',
+ 'example.com',
+ None),
+
+ 'escaped_escapes_in_local_part':
+ (r'"\\"example\\" example"@example.com',
+ [errors.InvalidHeaderDefect]*5,
+ r'"\\example\\\\ example"@example.com',
+ '',
+ r'"\\example\\\\ example"@example.com',
+ r'\example\\ example',
+ 'example.com',
+ None),
+
+ 'spaces_in_unquoted_local_part_collapsed':
+ ('merwok wok @example.com',
+ [errors.InvalidHeaderDefect]*2,
+ '"merwok wok"@example.com',
+ '',
+ '"merwok wok"@example.com',
+ 'merwok wok',
+ 'example.com',
+ None),
+
+ 'spaces_around_dots_in_local_part_removed':
+ ('merwok. wok . wok@example.com',
+ [errors.ObsoleteHeaderDefect],
+ 'merwok.wok.wok@example.com',
+ '',
+ 'merwok.wok.wok@example.com',
+ 'merwok.wok.wok',
+ 'example.com',
+ None),
+
+ }
+
+ # XXX: Need many more examples, and in particular some with names in
+ # trailing comments, which aren't currently handled. comments in
+ # general are not handled yet.
+
+ def _test_single_addr(self, source, defects, decoded, display_name,
+ addr_spec, username, domain, comment):
+ h = self.make_header('sender', source)
+ self.assertEqual(h, decoded)
+ self.assertDefectsEqual(h.defects, defects)
+ a = h.address
+ self.assertEqual(str(a), decoded)
+ self.assertEqual(len(h.groups), 1)
+ self.assertEqual([a], list(h.groups[0].addresses))
+ self.assertEqual([a], list(h.addresses))
+ self.assertEqual(a.display_name, display_name)
+ self.assertEqual(a.addr_spec, addr_spec)
+ self.assertEqual(a.username, username)
+ self.assertEqual(a.domain, domain)
+ # XXX: we have no comment support yet.
+ #self.assertEqual(a.comment, comment)
+
+ for name in examples:
+ locals()['test_'+name] = (
+ lambda self, name=name:
+ self._test_single_addr(*self.examples[name]))
+
+ def _test_group_single_addr(self, source, defects, decoded, display_name,
+ addr_spec, username, domain, comment):
+ source = 'foo: {};'.format(source)
+ gdecoded = 'foo: {};'.format(decoded) if decoded else 'foo:;'
+ h = self.make_header('to', source)
+ self.assertEqual(h, gdecoded)
+ self.assertDefectsEqual(h.defects, defects)
+ self.assertEqual(h.groups[0].addresses, h.addresses)
+ self.assertEqual(len(h.groups), 1)
+ self.assertEqual(len(h.addresses), 1)
+ a = h.addresses[0]
+ self.assertEqual(str(a), decoded)
+ self.assertEqual(a.display_name, display_name)
+ self.assertEqual(a.addr_spec, addr_spec)
+ self.assertEqual(a.username, username)
+ self.assertEqual(a.domain, domain)
+
+ for name in examples:
+ locals()['test_group_'+name] = (
+ lambda self, name=name:
+ self._test_group_single_addr(*self.examples[name]))
+
+ def test_simple_address_list(self):
+ value = ('Fred <dinsdale@python.org>, foo@example.com, '
+ '"Harry W. Hastings" <hasty@example.com>')
+ h = self.make_header('to', value)
+ self.assertEqual(h, value)
+ self.assertEqual(len(h.groups), 3)
+ self.assertEqual(len(h.addresses), 3)
+ for i in range(3):
+ self.assertEqual(h.groups[i].addresses[0], h.addresses[i])
+ self.assertEqual(str(h.addresses[0]), 'Fred <dinsdale@python.org>')
+ self.assertEqual(str(h.addresses[1]), 'foo@example.com')
+ self.assertEqual(str(h.addresses[2]),
+ '"Harry W. Hastings" <hasty@example.com>')
+ self.assertEqual(h.addresses[2].display_name,
+ 'Harry W. Hastings')
+
+ def test_complex_address_list(self):
+ examples = list(self.examples.values())
+ source = ('dummy list:;, another: (empty);,' +
+ ', '.join([x[0] for x in examples[:4]]) + ', ' +
+ r'"A \"list\"": ' +
+ ', '.join([x[0] for x in examples[4:6]]) + ';,' +
+ ', '.join([x[0] for x in examples[6:]])
+ )
+ # XXX: the fact that (empty) disappears here is a potential API design
+ # bug. We don't currently have a way to preserve comments.
+ expected = ('dummy list:;, another:;, ' +
+ ', '.join([x[2] for x in examples[:4]]) + ', ' +
+ r'"A \"list\"": ' +
+ ', '.join([x[2] for x in examples[4:6]]) + ';, ' +
+ ', '.join([x[2] for x in examples[6:]])
+ )
+
+ h = self.make_header('to', source)
+ self.assertEqual(h.split(','), expected.split(','))
+ self.assertEqual(h, expected)
+ self.assertEqual(len(h.groups), 7 + len(examples) - 6)
+ self.assertEqual(h.groups[0].display_name, 'dummy list')
+ self.assertEqual(h.groups[1].display_name, 'another')
+ self.assertEqual(h.groups[6].display_name, 'A "list"')
+ self.assertEqual(len(h.addresses), len(examples))
+ for i in range(4):
+ self.assertIsNone(h.groups[i+2].display_name)
+ self.assertEqual(str(h.groups[i+2].addresses[0]), examples[i][2])
+ for i in range(7, 7 + len(examples) - 6):
+ self.assertIsNone(h.groups[i].display_name)
+ self.assertEqual(str(h.groups[i].addresses[0]), examples[i-1][2])
+ for i in range(len(examples)):
+ self.assertEqual(str(h.addresses[i]), examples[i][2])
+ self.assertEqual(h.addresses[i].addr_spec, examples[i][4])
+
+ def test_address_read_only(self):
+ h = self.make_header('sender', 'abc@xyz.com')
+ with self.assertRaises(AttributeError):
+ h.address = 'foo'
+
+ def test_addresses_read_only(self):
+ h = self.make_header('sender', 'abc@xyz.com')
+ with self.assertRaises(AttributeError):
+ h.addresses = 'foo'
+
+ def test_groups_read_only(self):
+ h = self.make_header('sender', 'abc@xyz.com')
+ with self.assertRaises(AttributeError):
+ h.groups = 'foo'
+
+ def test_addresses_types(self):
+ source = 'me <who@example.com>'
+ h = self.make_header('to', source)
+ self.assertIsInstance(h.addresses, tuple)
+ self.assertIsInstance(h.addresses[0], Address)
+
+ def test_groups_types(self):
+ source = 'me <who@example.com>'
+ h = self.make_header('to', source)
+ self.assertIsInstance(h.groups, tuple)
+ self.assertIsInstance(h.groups[0], Group)
+
+ def test_set_from_Address(self):
+ h = self.make_header('to', Address('me', 'foo', 'example.com'))
+ self.assertEqual(h, 'me <foo@example.com>')
+
+ def test_set_from_Address_list(self):
+ h = self.make_header('to', [Address('me', 'foo', 'example.com'),
+ Address('you', 'bar', 'example.com')])
+ self.assertEqual(h, 'me <foo@example.com>, you <bar@example.com>')
+
+ def test_set_from_Address_and_Group_list(self):
+ h = self.make_header('to', [Address('me', 'foo', 'example.com'),
+ Group('bing', [Address('fiz', 'z', 'b.com'),
+ Address('zif', 'f', 'c.com')]),
+ Address('you', 'bar', 'example.com')])
+ self.assertEqual(h, 'me <foo@example.com>, bing: fiz <z@b.com>, '
+ 'zif <f@c.com>;, you <bar@example.com>')
+ self.assertEqual(h.fold(policy=policy.default.clone(max_line_length=40)),
+ 'to: me <foo@example.com>,\n'
+ ' bing: fiz <z@b.com>, zif <f@c.com>;,\n'
+ ' you <bar@example.com>\n')
+
+ def test_set_from_Group_list(self):
+ h = self.make_header('to', [Group('bing', [Address('fiz', 'z', 'b.com'),
+ Address('zif', 'f', 'c.com')])])
+ self.assertEqual(h, 'bing: fiz <z@b.com>, zif <f@c.com>;')
+
+
+class TestAddressAndGroup(TestEmailBase):
+
+ def _test_attr_ro(self, obj, attr):
+ with self.assertRaises(AttributeError):
+ setattr(obj, attr, 'foo')
+
+ def test_address_display_name_ro(self):
+ self._test_attr_ro(Address('foo', 'bar', 'baz'), 'display_name')
+
+ def test_address_username_ro(self):
+ self._test_attr_ro(Address('foo', 'bar', 'baz'), 'username')
+
+ def test_address_domain_ro(self):
+ self._test_attr_ro(Address('foo', 'bar', 'baz'), 'domain')
+
+ def test_group_display_name_ro(self):
+ self._test_attr_ro(Group('foo'), 'display_name')
+
+ def test_group_addresses_ro(self):
+ self._test_attr_ro(Group('foo'), 'addresses')
+
+ def test_address_from_username_domain(self):
+ a = Address('foo', 'bar', 'baz')
+ self.assertEqual(a.display_name, 'foo')
+ self.assertEqual(a.username, 'bar')
+ self.assertEqual(a.domain, 'baz')
+ self.assertEqual(a.addr_spec, 'bar@baz')
+ self.assertEqual(str(a), 'foo <bar@baz>')
+
+ def test_address_from_addr_spec(self):
+ a = Address('foo', addr_spec='bar@baz')
+ self.assertEqual(a.display_name, 'foo')
+ self.assertEqual(a.username, 'bar')
+ self.assertEqual(a.domain, 'baz')
+ self.assertEqual(a.addr_spec, 'bar@baz')
+ self.assertEqual(str(a), 'foo <bar@baz>')
+
+ def test_address_with_no_display_name(self):
+ a = Address(addr_spec='bar@baz')
+ self.assertEqual(a.display_name, '')
+ self.assertEqual(a.username, 'bar')
+ self.assertEqual(a.domain, 'baz')
+ self.assertEqual(a.addr_spec, 'bar@baz')
+ self.assertEqual(str(a), 'bar@baz')
+
+ def test_null_address(self):
+ a = Address()
+ self.assertEqual(a.display_name, '')
+ self.assertEqual(a.username, '')
+ self.assertEqual(a.domain, '')
+ self.assertEqual(a.addr_spec, '<>')
+ self.assertEqual(str(a), '<>')
+
+ def test_domain_only(self):
+ # This isn't really a valid address.
+ a = Address(domain='buzz')
+ self.assertEqual(a.display_name, '')
+ self.assertEqual(a.username, '')
+ self.assertEqual(a.domain, 'buzz')
+ self.assertEqual(a.addr_spec, '@buzz')
+ self.assertEqual(str(a), '@buzz')
+
+ def test_username_only(self):
+ # This isn't really a valid address.
+ a = Address(username='buzz')
+ self.assertEqual(a.display_name, '')
+ self.assertEqual(a.username, 'buzz')
+ self.assertEqual(a.domain, '')
+ self.assertEqual(a.addr_spec, 'buzz')
+ self.assertEqual(str(a), 'buzz')
+
+ def test_display_name_only(self):
+ a = Address('buzz')
+ self.assertEqual(a.display_name, 'buzz')
+ self.assertEqual(a.username, '')
+ self.assertEqual(a.domain, '')
+ self.assertEqual(a.addr_spec, '<>')
+ self.assertEqual(str(a), 'buzz <>')
+
+ def test_quoting(self):
+ # Ideally we'd check every special individually, but I'm not up for
+ # writing that many tests.
+ a = Address('Sara J.', 'bad name', 'example.com')
+ self.assertEqual(a.display_name, 'Sara J.')
+ self.assertEqual(a.username, 'bad name')
+ self.assertEqual(a.domain, 'example.com')
+ self.assertEqual(a.addr_spec, '"bad name"@example.com')
+ self.assertEqual(str(a), '"Sara J." <"bad name"@example.com>')
+
+ def test_il8n(self):
+ a = Address('Éric', 'wok', 'exàmple.com')
+ self.assertEqual(a.display_name, 'Éric')
+ self.assertEqual(a.username, 'wok')
+ self.assertEqual(a.domain, 'exàmple.com')
+ self.assertEqual(a.addr_spec, 'wok@exàmple.com')
+ self.assertEqual(str(a), 'Éric <wok@exàmple.com>')
+
+ # XXX: there is an API design issue that needs to be solved here.
+ #def test_non_ascii_username_raises(self):
+ # with self.assertRaises(ValueError):
+ # Address('foo', 'wők', 'example.com')
+
+ def test_non_ascii_username_in_addr_spec_raises(self):
+ with self.assertRaises(ValueError):
+ Address('foo', addr_spec='wők@example.com')
+
+ def test_address_addr_spec_and_username_raises(self):
+ with self.assertRaises(TypeError):
+ Address('foo', username='bing', addr_spec='bar@baz')
+
+ def test_address_addr_spec_and_domain_raises(self):
+ with self.assertRaises(TypeError):
+ Address('foo', domain='bing', addr_spec='bar@baz')
+
+ def test_address_addr_spec_and_username_and_domain_raises(self):
+ with self.assertRaises(TypeError):
+ Address('foo', username='bong', domain='bing', addr_spec='bar@baz')
+
+ def test_space_in_addr_spec_username_raises(self):
+ with self.assertRaises(ValueError):
+ Address('foo', addr_spec="bad name@example.com")
+
+ def test_bad_addr_sepc_raises(self):
+ with self.assertRaises(ValueError):
+ Address('foo', addr_spec="name@ex[]ample.com")
+
+ def test_empty_group(self):
+ g = Group('foo')
+ self.assertEqual(g.display_name, 'foo')
+ self.assertEqual(g.addresses, tuple())
+ self.assertEqual(str(g), 'foo:;')
+
+ def test_empty_group_list(self):
+ g = Group('foo', addresses=[])
+ self.assertEqual(g.display_name, 'foo')
+ self.assertEqual(g.addresses, tuple())
+ self.assertEqual(str(g), 'foo:;')
+
+ def test_null_group(self):
+ g = Group()
+ self.assertIsNone(g.display_name)
+ self.assertEqual(g.addresses, tuple())
+ self.assertEqual(str(g), 'None:;')
+
+ def test_group_with_addresses(self):
+ addrs = [Address('b', 'b', 'c'), Address('a', 'b','c')]
+ g = Group('foo', addrs)
+ self.assertEqual(g.display_name, 'foo')
+ self.assertEqual(g.addresses, tuple(addrs))
+ self.assertEqual(str(g), 'foo: b <b@c>, a <b@c>;')
+
+ def test_group_with_addresses_no_display_name(self):
+ addrs = [Address('b', 'b', 'c'), Address('a', 'b','c')]
+ g = Group(addresses=addrs)
+ self.assertIsNone(g.display_name)
+ self.assertEqual(g.addresses, tuple(addrs))
+ self.assertEqual(str(g), 'None: b <b@c>, a <b@c>;')
+
+ def test_group_with_one_address_no_display_name(self):
+ addrs = [Address('b', 'b', 'c')]
+ g = Group(addresses=addrs)
+ self.assertIsNone(g.display_name)
+ self.assertEqual(g.addresses, tuple(addrs))
+ self.assertEqual(str(g), 'b <b@c>')
+
+ def test_display_name_quoting(self):
+ g = Group('foo.bar')
+ self.assertEqual(g.display_name, 'foo.bar')
+ self.assertEqual(g.addresses, tuple())
+ self.assertEqual(str(g), '"foo.bar":;')
+
+ def test_display_name_blanks_not_quoted(self):
+ g = Group('foo bar')
+ self.assertEqual(g.display_name, 'foo bar')
+ self.assertEqual(g.addresses, tuple())
+ self.assertEqual(str(g), 'foo bar:;')
+
+
+class TestFolding(TestHeaderBase):
+
+ def test_short_unstructured(self):
+ h = self.make_header('subject', 'this is a test')
+ self.assertEqual(h.fold(policy=self.policy),
+ 'subject: this is a test\n')
+
+ def test_long_unstructured(self):
+ h = self.make_header('Subject', 'This is a long header '
+ 'line that will need to be folded into two lines '
+ 'and will demonstrate basic folding')
+ self.assertEqual(h.fold(policy=self.policy),
+ 'Subject: This is a long header line that will '
+ 'need to be folded into two lines\n'
+ ' and will demonstrate basic folding\n')
+
+ def test_unstructured_short_max_line_length(self):
+ h = self.make_header('Subject', 'this is a short header '
+ 'that will be folded anyway')
+ self.assertEqual(
+ h.fold(policy=policy.default.clone(max_line_length=20)),
+ textwrap.dedent("""\
+ Subject: this is a
+ short header that
+ will be folded
+ anyway
+ """))
+
+ def test_fold_unstructured_single_word(self):
+ h = self.make_header('Subject', 'test')
+ self.assertEqual(h.fold(policy=self.policy), 'Subject: test\n')
+
+ def test_fold_unstructured_short(self):
+ h = self.make_header('Subject', 'test test test')
+ self.assertEqual(h.fold(policy=self.policy),
+ 'Subject: test test test\n')
+
+ def test_fold_unstructured_with_overlong_word(self):
+ h = self.make_header('Subject', 'thisisaverylonglineconsistingofa'
+ 'singlewordthatwontfit')
+ self.assertEqual(
+ h.fold(policy=policy.default.clone(max_line_length=20)),
+ 'Subject: thisisaverylonglineconsistingofasinglewordthatwontfit\n')
+
+ def test_fold_unstructured_with_two_overlong_words(self):
+ h = self.make_header('Subject', 'thisisaverylonglineconsistingofa'
+ 'singlewordthatwontfit plusanotherverylongwordthatwontfit')
+ self.assertEqual(
+ h.fold(policy=policy.default.clone(max_line_length=20)),
+ 'Subject: thisisaverylonglineconsistingofasinglewordthatwontfit\n'
+ ' plusanotherverylongwordthatwontfit\n')
+
+ def test_fold_unstructured_with_slightly_long_word(self):
+ h = self.make_header('Subject', 'thislongwordislessthanmaxlinelen')
+ self.assertEqual(
+ h.fold(policy=policy.default.clone(max_line_length=35)),
+ 'Subject:\n thislongwordislessthanmaxlinelen\n')
+
+ def test_fold_unstructured_with_commas(self):
+ # The old wrapper would fold this at the commas.
+ h = self.make_header('Subject', "This header is intended to "
+ "demonstrate, in a fairly susinct way, that we now do "
+ "not give a , special treatment in unstructured headers.")
+ self.assertEqual(
+ h.fold(policy=policy.default.clone(max_line_length=60)),
+ textwrap.dedent("""\
+ Subject: This header is intended to demonstrate, in a fairly
+ susinct way, that we now do not give a , special treatment
+ in unstructured headers.
+ """))
+
+ def test_fold_address_list(self):
+ h = self.make_header('To', '"Theodore H. Perfect" <yes@man.com>, '
+ '"My address is very long because my name is long" <foo@bar.com>, '
+ '"Only A. Friend" <no@yes.com>')
+ self.assertEqual(h.fold(policy=self.policy), textwrap.dedent("""\
+ To: "Theodore H. Perfect" <yes@man.com>,
+ "My address is very long because my name is long" <foo@bar.com>,
+ "Only A. Friend" <no@yes.com>
+ """))
+
+ def test_fold_date_header(self):
+ h = self.make_header('Date', 'Sat, 2 Feb 2002 17:00:06 -0800')
+ self.assertEqual(h.fold(policy=self.policy),
+ 'Date: Sat, 02 Feb 2002 17:00:06 -0800\n')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py
index 8f5fde7..c1af024 100644
--- a/Lib/test/test_email/test_generator.py
+++ b/Lib/test/test_email/test_generator.py
@@ -6,14 +6,16 @@ from email.generator import Generator, BytesGenerator
from email import policy
from test.test_email import TestEmailBase
-# XXX: move generator tests from test_email into here at some point.
+class TestGeneratorBase:
-class TestGeneratorBase():
+ policy = policy.default
- policy = policy.compat32
+ def msgmaker(self, msg, policy=None):
+ policy = self.policy if policy is None else policy
+ return self.msgfunc(msg, policy=policy)
- long_subject = {
+ refold_long_expected = {
0: textwrap.dedent("""\
To: whom_it_may_concern@example.com
From: nobody_you_want_to_know@example.com
@@ -23,33 +25,32 @@ class TestGeneratorBase():
None
"""),
+ # From is wrapped because wrapped it fits in 40.
40: textwrap.dedent("""\
To: whom_it_may_concern@example.com
- From:\x20
+ From:
nobody_you_want_to_know@example.com
Subject: We the willing led by the
- unknowing are doing the
- impossible for the ungrateful. We have
- done so much for so long with so little
- we are now qualified to do anything
- with nothing.
+ unknowing are doing the impossible for
+ the ungrateful. We have done so much
+ for so long with so little we are now
+ qualified to do anything with nothing.
None
"""),
+ # Neither to nor from fit even if put on a new line,
+ # so we leave them sticking out on the first line.
20: textwrap.dedent("""\
- To:\x20
- whom_it_may_concern@example.com
- From:\x20
- nobody_you_want_to_know@example.com
+ To: whom_it_may_concern@example.com
+ From: nobody_you_want_to_know@example.com
Subject: We the
willing led by the
unknowing are doing
- the
- impossible for the
- ungrateful. We have
- done so much for so
- long with so little
- we are now
+ the impossible for
+ the ungrateful. We
+ have done so much
+ for so long with so
+ little we are now
qualified to do
anything with
nothing.
@@ -57,65 +58,90 @@ class TestGeneratorBase():
None
"""),
}
- long_subject[100] = long_subject[0]
-
- def maxheaderlen_parameter_test(self, n):
- msg = self.msgmaker(self.typ(self.long_subject[0]))
+ refold_long_expected[100] = refold_long_expected[0]
+
+ refold_all_expected = refold_long_expected.copy()
+ refold_all_expected[0] = (
+ "To: whom_it_may_concern@example.com\n"
+ "From: nobody_you_want_to_know@example.com\n"
+ "Subject: We the willing led by the unknowing are doing the "
+ "impossible for the ungrateful. We have done so much for "
+ "so long with so little we are now qualified to do anything "
+ "with nothing.\n"
+ "\n"
+ "None\n")
+ refold_all_expected[100] = (
+ "To: whom_it_may_concern@example.com\n"
+ "From: nobody_you_want_to_know@example.com\n"
+ "Subject: We the willing led by the unknowing are doing the "
+ "impossible for the ungrateful. We have\n"
+ " done so much for so long with so little we are now qualified "
+ "to do anything with nothing.\n"
+ "\n"
+ "None\n")
+
+ def _test_maxheaderlen_parameter(self, n):
+ msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
- g = self.genclass(s, maxheaderlen=n)
+ g = self.genclass(s, maxheaderlen=n, policy=self.policy)
g.flatten(msg)
- self.assertEqual(s.getvalue(), self.typ(self.long_subject[n]))
+ self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n]))
- def test_maxheaderlen_parameter_0(self):
- self.maxheaderlen_parameter_test(0)
+ for n in refold_long_expected:
+ locals()['test_maxheaderlen_parameter_' + str(n)] = (
+ lambda self, n=n:
+ self._test_maxheaderlen_parameter(n))
- def test_maxheaderlen_parameter_100(self):
- self.maxheaderlen_parameter_test(100)
-
- def test_maxheaderlen_parameter_40(self):
- self.maxheaderlen_parameter_test(40)
-
- def test_maxheaderlen_parameter_20(self):
- self.maxheaderlen_parameter_test(20)
-
- def maxheaderlen_policy_test(self, n):
- msg = self.msgmaker(self.typ(self.long_subject[0]))
+ def _test_max_line_length_policy(self, n):
+ msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
- g = self.genclass(s, policy=policy.default.clone(max_line_length=n))
+ g = self.genclass(s, policy=self.policy.clone(max_line_length=n))
g.flatten(msg)
- self.assertEqual(s.getvalue(), self.typ(self.long_subject[n]))
-
- def test_maxheaderlen_policy_0(self):
- self.maxheaderlen_policy_test(0)
-
- def test_maxheaderlen_policy_100(self):
- self.maxheaderlen_policy_test(100)
-
- def test_maxheaderlen_policy_40(self):
- self.maxheaderlen_policy_test(40)
+ self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n]))
- def test_maxheaderlen_policy_20(self):
- self.maxheaderlen_policy_test(20)
+ for n in refold_long_expected:
+ locals()['test_max_line_length_policy' + str(n)] = (
+ lambda self, n=n:
+ self._test_max_line_length_policy(n))
- def maxheaderlen_parm_overrides_policy_test(self, n):
- msg = self.msgmaker(self.typ(self.long_subject[0]))
+ def _test_maxheaderlen_parm_overrides_policy(self, n):
+ msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, maxheaderlen=n,
- policy=policy.default.clone(max_line_length=10))
+ policy=self.policy.clone(max_line_length=10))
g.flatten(msg)
- self.assertEqual(s.getvalue(), self.typ(self.long_subject[n]))
+ self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n]))
- def test_maxheaderlen_parm_overrides_policy_0(self):
- self.maxheaderlen_parm_overrides_policy_test(0)
+ for n in refold_long_expected:
+ locals()['test_maxheaderlen_parm_overrides_policy' + str(n)] = (
+ lambda self, n=n:
+ self._test_maxheaderlen_parm_overrides_policy(n))
- def test_maxheaderlen_parm_overrides_policy_100(self):
- self.maxheaderlen_parm_overrides_policy_test(100)
+ def _test_refold_none_does_not_fold(self, n):
+ msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
+ s = self.ioclass()
+ g = self.genclass(s, policy=self.policy.clone(refold_source='none',
+ max_line_length=n))
+ g.flatten(msg)
+ self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[0]))
+
+ for n in refold_long_expected:
+ locals()['test_refold_none_does_not_fold' + str(n)] = (
+ lambda self, n=n:
+ self._test_refold_none_does_not_fold(n))
- def test_maxheaderlen_parm_overrides_policy_40(self):
- self.maxheaderlen_parm_overrides_policy_test(40)
+ def _test_refold_all(self, n):
+ msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
+ s = self.ioclass()
+ g = self.genclass(s, policy=self.policy.clone(refold_source='all',
+ max_line_length=n))
+ g.flatten(msg)
+ self.assertEqual(s.getvalue(), self.typ(self.refold_all_expected[n]))
- def test_maxheaderlen_parm_overrides_policy_20(self):
- self.maxheaderlen_parm_overrides_policy_test(20)
+ for n in refold_long_expected:
+ locals()['test_refold_all' + str(n)] = (
+ lambda self, n=n:
+ self._test_refold_all(n))
def test_crlf_control_via_policy(self):
source = "Subject: test\r\n\r\ntest body\r\n"
@@ -138,30 +164,24 @@ class TestGeneratorBase():
class TestGenerator(TestGeneratorBase, TestEmailBase):
+ msgfunc = staticmethod(message_from_string)
genclass = Generator
ioclass = io.StringIO
typ = str
- def msgmaker(self, msg, policy=None):
- policy = self.policy if policy is None else policy
- return message_from_string(msg, policy=policy)
-
class TestBytesGenerator(TestGeneratorBase, TestEmailBase):
+ msgfunc = staticmethod(message_from_bytes)
genclass = BytesGenerator
ioclass = io.BytesIO
typ = lambda self, x: x.encode('ascii')
- def msgmaker(self, msg, policy=None):
- policy = self.policy if policy is None else policy
- return message_from_bytes(msg, policy=policy)
-
def test_cte_type_7bit_handles_unknown_8bit(self):
source = ("Subject: Maintenant je vous présente mon "
"collègue\n\n").encode('utf-8')
- expected = ('Subject: =?unknown-8bit?q?Maintenant_je_vous_pr=C3=A9sente_mon_'
- 'coll=C3=A8gue?=\n\n').encode('ascii')
+ expected = ('Subject: Maintenant je vous =?unknown-8bit?q?'
+ 'pr=C3=A9sente_mon_coll=C3=A8gue?=\n\n').encode('ascii')
msg = message_from_bytes(source)
s = io.BytesIO()
g = BytesGenerator(s, policy=self.policy.clone(cte_type='7bit'))
diff --git a/Lib/test/test_email/test_pickleable.py b/Lib/test/test_email/test_pickleable.py
new file mode 100644
index 0000000..e4c77ca
--- /dev/null
+++ b/Lib/test/test_email/test_pickleable.py
@@ -0,0 +1,57 @@
+import unittest
+import textwrap
+import copy
+import pickle
+from email import policy
+from email import message_from_string
+from email._headerregistry import HeaderRegistry
+from test.test_email import TestEmailBase
+
+class TestPickleCopyHeader(TestEmailBase):
+
+ unstructured = HeaderRegistry()('subject', 'this is a test')
+
+ def test_deepcopy_unstructured(self):
+ h = copy.deepcopy(self.unstructured)
+ self.assertEqual(str(h), str(self.unstructured))
+
+ def test_pickle_unstructured(self):
+ p = pickle.dumps(self.unstructured)
+ h = pickle.loads(p)
+ self.assertEqual(str(h), str(self.unstructured))
+
+ address = HeaderRegistry()('from', 'frodo@mordor.net')
+
+ def test_deepcopy_address(self):
+ h = copy.deepcopy(self.address)
+ self.assertEqual(str(h), str(self.address))
+
+ def test_pickle_address(self):
+ p = pickle.dumps(self.address)
+ h = pickle.loads(p)
+ self.assertEqual(str(h), str(self.address))
+
+
+class TestPickleCopyMessage(TestEmailBase):
+
+ testmsg = message_from_string(textwrap.dedent("""\
+ From: frodo@mordor.net
+ To: bilbo@underhill.org
+ Subject: help
+
+ I think I forgot the ring.
+ """), policy=policy.default)
+
+ def test_deepcopy(self):
+ msg2 = copy.deepcopy(self.testmsg)
+ self.assertEqual(msg2.as_string(), self.testmsg.as_string())
+
+ def test_pickle(self):
+ p = pickle.dumps(self.testmsg)
+ msg2 = pickle.loads(p)
+ self.assertEqual(msg2.as_string(), self.testmsg.as_string())
+
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py
index 07925a7..45a7666 100644
--- a/Lib/test/test_email/test_policy.py
+++ b/Lib/test/test_email/test_policy.py
@@ -5,49 +5,70 @@ import unittest
import email.policy
import email.parser
import email.generator
+from email import _headerregistry
+
+def make_defaults(base_defaults, differences):
+ defaults = base_defaults.copy()
+ defaults.update(differences)
+ return defaults
class PolicyAPITests(unittest.TestCase):
longMessage = True
- # These default values are the ones set on email.policy.default.
- # If any of these defaults change, the docs must be updated.
- policy_defaults = {
+ # Base default values.
+ compat32_defaults = {
'max_line_length': 78,
'linesep': '\n',
'cte_type': '8bit',
'raise_on_defect': False,
}
-
- # For each policy under test, we give here the values of the attributes
- # that are different from the defaults for that policy.
+ # These default values are the ones set on email.policy.default.
+ # If any of these defaults change, the docs must be updated.
+ policy_defaults = compat32_defaults.copy()
+ policy_defaults.update({
+ 'raise_on_defect': False,
+ 'header_factory': email.policy.EmailPolicy.header_factory,
+ 'refold_source': 'long',
+ })
+
+ # For each policy under test, we give here what we expect the defaults to
+ # be for that policy. The second argument to make defaults is the
+ # difference between the base defaults and that for the particular policy.
+ new_policy = email.policy.EmailPolicy()
policies = {
- email.policy.Compat32(): {},
- email.policy.compat32: {},
- email.policy.default: {},
- email.policy.SMTP: {'linesep': '\r\n'},
- email.policy.HTTP: {'linesep': '\r\n', 'max_line_length': None},
- email.policy.strict: {'raise_on_defect': True},
+ email.policy.compat32: make_defaults(compat32_defaults, {}),
+ email.policy.default: make_defaults(policy_defaults, {}),
+ email.policy.SMTP: make_defaults(policy_defaults,
+ {'linesep': '\r\n'}),
+ email.policy.HTTP: make_defaults(policy_defaults,
+ {'linesep': '\r\n',
+ 'max_line_length': None}),
+ email.policy.strict: make_defaults(policy_defaults,
+ {'raise_on_defect': True}),
+ new_policy: make_defaults(policy_defaults, {}),
}
+ # Creating a new policy creates a new header factory. There is a test
+ # later that proves this.
+ policies[new_policy]['header_factory'] = new_policy.header_factory
def test_defaults(self):
- for policy, changed_defaults in self.policies.items():
- expected = self.policy_defaults.copy()
- expected.update(changed_defaults)
+ for policy, expected in self.policies.items():
for attr, value in expected.items():
self.assertEqual(getattr(policy, attr), value,
("change {} docs/docstrings if defaults have "
"changed").format(policy))
def test_all_attributes_covered(self):
- for attr in dir(email.policy.default):
- if (attr.startswith('_') or
- isinstance(getattr(email.policy.Policy, attr),
- types.FunctionType)):
- continue
- else:
- self.assertIn(attr, self.policy_defaults,
- "{} is not fully tested".format(attr))
+ for policy, expected in self.policies.items():
+ for attr in dir(policy):
+ if (attr.startswith('_') or
+ isinstance(getattr(email.policy.EmailPolicy, attr),
+ types.FunctionType)):
+ continue
+ else:
+ self.assertIn(attr, expected,
+ "{} is not fully tested".format(attr))
def test_abc(self):
with self.assertRaises(TypeError) as cm:
@@ -62,18 +83,20 @@ class PolicyAPITests(unittest.TestCase):
self.assertIn(method, msg)
def test_policy_is_immutable(self):
- for policy in self.policies:
- for attr in self.policy_defaults:
+ for policy, defaults in self.policies.items():
+ for attr in defaults:
with self.assertRaisesRegex(AttributeError, attr+".*read-only"):
setattr(policy, attr, None)
with self.assertRaisesRegex(AttributeError, 'no attribute.*foo'):
policy.foo = None
- def test_set_policy_attrs_when_calledl(self):
- testattrdict = { attr: None for attr in self.policy_defaults }
- for policyclass in self.policies:
+ def test_set_policy_attrs_when_cloned(self):
+ # None of the attributes has a default value of None, so we set them
+ # all to None in the clone call and check that it worked.
+ for policyclass, defaults in self.policies.items():
+ testattrdict = {attr: None for attr in defaults}
policy = policyclass.clone(**testattrdict)
- for attr in self.policy_defaults:
+ for attr in defaults:
self.assertIsNone(getattr(policy, attr))
def test_reject_non_policy_keyword_when_called(self):
@@ -105,7 +128,7 @@ class PolicyAPITests(unittest.TestCase):
self.defects = []
obj = Dummy()
defect = object()
- policy = email.policy.Compat32()
+ policy = email.policy.EmailPolicy()
policy.register_defect(obj, defect)
self.assertEqual(obj.defects, [defect])
defect2 = object()
@@ -134,7 +157,7 @@ class PolicyAPITests(unittest.TestCase):
email.policy.default.handle_defect(foo, defect2)
self.assertEqual(foo.defects, [defect1, defect2])
- class MyPolicy(email.policy.Compat32):
+ class MyPolicy(email.policy.EmailPolicy):
defects = None
def __init__(self, *args, **kw):
super().__init__(*args, defects=[], **kw)
@@ -159,6 +182,49 @@ class PolicyAPITests(unittest.TestCase):
self.assertEqual(my_policy.defects, [defect1, defect2])
self.assertEqual(foo.defects, [])
+ def test_default_header_factory(self):
+ h = email.policy.default.header_factory('Test', 'test')
+ self.assertEqual(h.name, 'Test')
+ self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h, _headerregistry.BaseHeader)
+
+ class Foo:
+ parse = _headerregistry.UnstructuredHeader.parse
+
+ def test_each_Policy_gets_unique_factory(self):
+ policy1 = email.policy.EmailPolicy()
+ policy2 = email.policy.EmailPolicy()
+ policy1.header_factory.map_to_type('foo', self.Foo)
+ h = policy1.header_factory('foo', 'test')
+ self.assertIsInstance(h, self.Foo)
+ self.assertNotIsInstance(h, _headerregistry.UnstructuredHeader)
+ h = policy2.header_factory('foo', 'test')
+ self.assertNotIsInstance(h, self.Foo)
+ self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+
+ def test_clone_copies_factory(self):
+ policy1 = email.policy.EmailPolicy()
+ policy2 = policy1.clone()
+ policy1.header_factory.map_to_type('foo', self.Foo)
+ h = policy1.header_factory('foo', 'test')
+ self.assertIsInstance(h, self.Foo)
+ h = policy2.header_factory('foo', 'test')
+ self.assertIsInstance(h, self.Foo)
+
+ def test_new_factory_overrides_default(self):
+ mypolicy = email.policy.EmailPolicy()
+ myfactory = mypolicy.header_factory
+ newpolicy = mypolicy + email.policy.strict
+ self.assertEqual(newpolicy.header_factory, myfactory)
+ newpolicy = email.policy.strict + mypolicy
+ self.assertEqual(newpolicy.header_factory, myfactory)
+
+ def test_adding_default_policies_preserves_default_factory(self):
+ newpolicy = email.policy.default + email.policy.strict
+ self.assertEqual(newpolicy.header_factory,
+ email.policy.EmailPolicy.header_factory)
+ self.assertEqual(newpolicy.__dict__, {'raise_on_defect': True})
+
# XXX: Need subclassing tests.
# For adding subclassed objects, make sure the usual rules apply (subclass
# wins), but that the order still works (right overrides left).