1import string 2import unittest 3from email import _header_value_parser as parser 4from email import errors 5from email import policy 6from test.test_email import TestEmailBase, parameterize 7 8class TestTokens(TestEmailBase): 9 10 # EWWhiteSpaceTerminal 11 12 def test_EWWhiteSpaceTerminal(self): 13 x = parser.EWWhiteSpaceTerminal(' \t', 'fws') 14 self.assertEqual(x, ' \t') 15 self.assertEqual(str(x), '') 16 self.assertEqual(x.value, '') 17 self.assertEqual(x.token_type, 'fws') 18 19 20class TestParserMixin: 21 22 def _assert_results(self, tl, rest, string, value, defects, remainder, 23 comments=None): 24 self.assertEqual(str(tl), string) 25 self.assertEqual(tl.value, value) 26 self.assertDefectsEqual(tl.all_defects, defects) 27 self.assertEqual(rest, remainder) 28 if comments is not None: 29 self.assertEqual(tl.comments, comments) 30 31 def _test_get_x(self, method, source, string, value, defects, 32 remainder, comments=None): 33 tl, rest = method(source) 34 self._assert_results(tl, rest, string, value, defects, remainder, 35 comments=None) 36 return tl 37 38 def _test_parse_x(self, method, input, string, value, defects, 39 comments=None): 40 tl = method(input) 41 self._assert_results(tl, '', string, value, defects, '', comments) 42 return tl 43 44 45class TestParser(TestParserMixin, TestEmailBase): 46 47 # _wsp_splitter 48 49 rfc_printable_ascii = bytes(range(33, 127)).decode('ascii') 50 rfc_atext_chars = (string.ascii_letters + string.digits + 51 "!#$%&\'*+-/=?^_`{}|~") 52 rfc_dtext_chars = rfc_printable_ascii.translate(str.maketrans('','',r'\[]')) 53 54 def test__wsp_splitter_one_word(self): 55 self.assertEqual(parser._wsp_splitter('foo', 1), ['foo']) 56 57 def test__wsp_splitter_two_words(self): 58 self.assertEqual(parser._wsp_splitter('foo def', 1), 59 ['foo', ' ', 'def']) 60 61 def test__wsp_splitter_ws_runs(self): 62 self.assertEqual(parser._wsp_splitter('foo \t def jik', 1), 63 ['foo', ' \t ', 'def jik']) 64 65 66 # get_fws 67 68 def test_get_fws_only(self): 69 fws = self._test_get_x(parser.get_fws, ' \t ', ' \t ', ' ', [], '') 70 self.assertEqual(fws.token_type, 'fws') 71 72 def test_get_fws_space(self): 73 self._test_get_x(parser.get_fws, ' foo', ' ', ' ', [], 'foo') 74 75 def test_get_fws_ws_run(self): 76 self._test_get_x(parser.get_fws, ' \t foo ', ' \t ', ' ', [], 'foo ') 77 78 # get_encoded_word 79 80 def test_get_encoded_word_missing_start_raises(self): 81 with self.assertRaises(errors.HeaderParseError): 82 parser.get_encoded_word('abc') 83 84 def test_get_encoded_word_missing_end_raises(self): 85 with self.assertRaises(errors.HeaderParseError): 86 parser.get_encoded_word('=?abc') 87 88 def test_get_encoded_word_missing_middle_raises(self): 89 with self.assertRaises(errors.HeaderParseError): 90 parser.get_encoded_word('=?abc?=') 91 92 def test_get_encoded_word_invalid_cte(self): 93 with self.assertRaises(errors.HeaderParseError): 94 parser.get_encoded_word('=?utf-8?X?somevalue?=') 95 96 def test_get_encoded_word_valid_ew(self): 97 self._test_get_x(parser.get_encoded_word, 98 '=?us-ascii?q?this_is_a_test?= bird', 99 'this is a test', 100 'this is a test', 101 [], 102 ' bird') 103 104 def test_get_encoded_word_internal_spaces(self): 105 self._test_get_x(parser.get_encoded_word, 106 '=?us-ascii?q?this is a test?= bird', 107 'this is a test', 108 'this is a test', 109 [errors.InvalidHeaderDefect], 110 ' bird') 111 112 def test_get_encoded_word_gets_first(self): 113 self._test_get_x(parser.get_encoded_word, 114 '=?us-ascii?q?first?= =?utf-8?q?second?=', 115 'first', 116 'first', 117 [], 118 ' =?utf-8?q?second?=') 119 120 def test_get_encoded_word_gets_first_even_if_no_space(self): 121 self._test_get_x(parser.get_encoded_word, 122 '=?us-ascii?q?first?==?utf-8?q?second?=', 123 'first', 124 'first', 125 [errors.InvalidHeaderDefect], 126 '=?utf-8?q?second?=') 127 128 def test_get_encoded_word_sets_extra_attributes(self): 129 ew = self._test_get_x(parser.get_encoded_word, 130 '=?us-ascii*jive?q?first_second?=', 131 'first second', 132 'first second', 133 [], 134 '') 135 self.assertEqual(ew.charset, 'us-ascii') 136 self.assertEqual(ew.lang, 'jive') 137 138 def test_get_encoded_word_lang_default_is_blank(self): 139 ew = self._test_get_x(parser.get_encoded_word, 140 '=?us-ascii?q?first_second?=', 141 'first second', 142 'first second', 143 [], 144 '') 145 self.assertEqual(ew.charset, 'us-ascii') 146 self.assertEqual(ew.lang, '') 147 148 def test_get_encoded_word_non_printable_defect(self): 149 self._test_get_x(parser.get_encoded_word, 150 '=?us-ascii?q?first\x02second?=', 151 'first\x02second', 152 'first\x02second', 153 [errors.NonPrintableDefect], 154 '') 155 156 def test_get_encoded_word_leading_internal_space(self): 157 self._test_get_x(parser.get_encoded_word, 158 '=?us-ascii?q?=20foo?=', 159 ' foo', 160 ' foo', 161 [], 162 '') 163 164 def test_get_encoded_word_quopri_utf_escape_follows_cte(self): 165 # Issue 18044 166 self._test_get_x(parser.get_encoded_word, 167 '=?utf-8?q?=C3=89ric?=', 168 'Éric', 169 'Éric', 170 [], 171 '') 172 173 # get_unstructured 174 175 def _get_unst(self, value): 176 token = parser.get_unstructured(value) 177 return token, '' 178 179 def test_get_unstructured_null(self): 180 self._test_get_x(self._get_unst, '', '', '', [], '') 181 182 def test_get_unstructured_one_word(self): 183 self._test_get_x(self._get_unst, 'foo', 'foo', 'foo', [], '') 184 185 def test_get_unstructured_normal_phrase(self): 186 self._test_get_x(self._get_unst, 'foo bar bird', 187 'foo bar bird', 188 'foo bar bird', 189 [], 190 '') 191 192 def test_get_unstructured_normal_phrase_with_whitespace(self): 193 self._test_get_x(self._get_unst, 'foo \t bar bird', 194 'foo \t bar bird', 195 'foo bar bird', 196 [], 197 '') 198 199 def test_get_unstructured_leading_whitespace(self): 200 self._test_get_x(self._get_unst, ' foo bar', 201 ' foo bar', 202 ' foo bar', 203 [], 204 '') 205 206 def test_get_unstructured_trailing_whitespace(self): 207 self._test_get_x(self._get_unst, 'foo bar ', 208 'foo bar ', 209 'foo bar ', 210 [], 211 '') 212 213 def test_get_unstructured_leading_and_trailing_whitespace(self): 214 self._test_get_x(self._get_unst, ' foo bar ', 215 ' foo bar ', 216 ' foo bar ', 217 [], 218 '') 219 220 def test_get_unstructured_one_valid_ew_no_ws(self): 221 self._test_get_x(self._get_unst, '=?us-ascii?q?bar?=', 222 'bar', 223 'bar', 224 [], 225 '') 226 227 def test_get_unstructured_one_ew_trailing_ws(self): 228 self._test_get_x(self._get_unst, '=?us-ascii?q?bar?= ', 229 'bar ', 230 'bar ', 231 [], 232 '') 233 234 def test_get_unstructured_one_valid_ew_trailing_text(self): 235 self._test_get_x(self._get_unst, '=?us-ascii?q?bar?= bird', 236 'bar bird', 237 'bar bird', 238 [], 239 '') 240 241 def test_get_unstructured_phrase_with_ew_in_middle_of_text(self): 242 self._test_get_x(self._get_unst, 'foo =?us-ascii?q?bar?= bird', 243 'foo bar bird', 244 'foo bar bird', 245 [], 246 '') 247 248 def test_get_unstructured_phrase_with_two_ew(self): 249 self._test_get_x(self._get_unst, 250 'foo =?us-ascii?q?bar?= =?us-ascii?q?bird?=', 251 'foo barbird', 252 'foo barbird', 253 [], 254 '') 255 256 def test_get_unstructured_phrase_with_two_ew_trailing_ws(self): 257 self._test_get_x(self._get_unst, 258 'foo =?us-ascii?q?bar?= =?us-ascii?q?bird?= ', 259 'foo barbird ', 260 'foo barbird ', 261 [], 262 '') 263 264 def test_get_unstructured_phrase_with_ew_with_leading_ws(self): 265 self._test_get_x(self._get_unst, 266 ' =?us-ascii?q?bar?=', 267 ' bar', 268 ' bar', 269 [], 270 '') 271 272 def test_get_unstructured_phrase_with_two_ew_extra_ws(self): 273 self._test_get_x(self._get_unst, 274 'foo =?us-ascii?q?bar?= \t =?us-ascii?q?bird?=', 275 'foo barbird', 276 'foo barbird', 277 [], 278 '') 279 280 def test_get_unstructured_two_ew_extra_ws_trailing_text(self): 281 self._test_get_x(self._get_unst, 282 '=?us-ascii?q?test?= =?us-ascii?q?foo?= val', 283 'testfoo val', 284 'testfoo val', 285 [], 286 '') 287 288 def test_get_unstructured_ew_with_internal_ws(self): 289 self._test_get_x(self._get_unst, 290 '=?iso-8859-1?q?hello=20world?=', 291 'hello world', 292 'hello world', 293 [], 294 '') 295 296 def test_get_unstructured_ew_with_internal_leading_ws(self): 297 self._test_get_x(self._get_unst, 298 ' =?us-ascii?q?=20test?= =?us-ascii?q?=20foo?= val', 299 ' test foo val', 300 ' test foo val', 301 [], 302 '') 303 304 def test_get_unstructured_invalid_ew(self): 305 self._test_get_x(self._get_unst, 306 '=?test val', 307 '=?test val', 308 '=?test val', 309 [], 310 '') 311 312 def test_get_unstructured_undecodable_bytes(self): 313 self._test_get_x(self._get_unst, 314 b'test \xACfoo val'.decode('ascii', 'surrogateescape'), 315 'test \uDCACfoo val', 316 'test \uDCACfoo val', 317 [errors.UndecodableBytesDefect], 318 '') 319 320 def test_get_unstructured_undecodable_bytes_in_EW(self): 321 self._test_get_x(self._get_unst, 322 (b'=?us-ascii?q?=20test?= =?us-ascii?q?=20\xACfoo?=' 323 b' val').decode('ascii', 'surrogateescape'), 324 ' test \uDCACfoo val', 325 ' test \uDCACfoo val', 326 [errors.UndecodableBytesDefect]*2, 327 '') 328 329 def test_get_unstructured_missing_base64_padding(self): 330 self._test_get_x(self._get_unst, 331 '=?utf-8?b?dmk?=', 332 'vi', 333 'vi', 334 [errors.InvalidBase64PaddingDefect], 335 '') 336 337 def test_get_unstructured_invalid_base64_character(self): 338 self._test_get_x(self._get_unst, 339 '=?utf-8?b?dm\x01k===?=', 340 'vi', 341 'vi', 342 [errors.InvalidBase64CharactersDefect], 343 '') 344 345 def test_get_unstructured_invalid_base64_character_and_bad_padding(self): 346 self._test_get_x(self._get_unst, 347 '=?utf-8?b?dm\x01k?=', 348 'vi', 349 'vi', 350 [errors.InvalidBase64CharactersDefect, 351 errors.InvalidBase64PaddingDefect], 352 '') 353 354 def test_get_unstructured_invalid_base64_length(self): 355 # bpo-27397: Return the encoded string since there's no way to decode. 356 self._test_get_x(self._get_unst, 357 '=?utf-8?b?abcde?=', 358 'abcde', 359 'abcde', 360 [errors.InvalidBase64LengthDefect], 361 '') 362 363 def test_get_unstructured_no_whitespace_between_ews(self): 364 self._test_get_x(self._get_unst, 365 '=?utf-8?q?foo?==?utf-8?q?bar?=', 366 'foobar', 367 'foobar', 368 [errors.InvalidHeaderDefect, 369 errors.InvalidHeaderDefect], 370 '') 371 372 def test_get_unstructured_ew_without_leading_whitespace(self): 373 self._test_get_x( 374 self._get_unst, 375 'nowhitespace=?utf-8?q?somevalue?=', 376 'nowhitespacesomevalue', 377 'nowhitespacesomevalue', 378 [errors.InvalidHeaderDefect], 379 '') 380 381 def test_get_unstructured_ew_without_trailing_whitespace(self): 382 self._test_get_x( 383 self._get_unst, 384 '=?utf-8?q?somevalue?=nowhitespace', 385 'somevaluenowhitespace', 386 'somevaluenowhitespace', 387 [errors.InvalidHeaderDefect], 388 '') 389 390 def test_get_unstructured_without_trailing_whitespace_hang_case(self): 391 self._test_get_x(self._get_unst, 392 '=?utf-8?q?somevalue?=aa', 393 'somevalueaa', 394 'somevalueaa', 395 [errors.InvalidHeaderDefect], 396 '') 397 398 def test_get_unstructured_invalid_ew2(self): 399 self._test_get_x(self._get_unst, 400 '=?utf-8?q?=somevalue?=', 401 '=?utf-8?q?=somevalue?=', 402 '=?utf-8?q?=somevalue?=', 403 [], 404 '') 405 406 def test_get_unstructured_invalid_ew_cte(self): 407 self._test_get_x(self._get_unst, 408 '=?utf-8?X?=somevalue?=', 409 '=?utf-8?X?=somevalue?=', 410 '=?utf-8?X?=somevalue?=', 411 [], 412 '') 413 414 # get_qp_ctext 415 416 def test_get_qp_ctext_only(self): 417 ptext = self._test_get_x(parser.get_qp_ctext, 418 'foobar', 'foobar', ' ', [], '') 419 self.assertEqual(ptext.token_type, 'ptext') 420 421 def test_get_qp_ctext_all_printables(self): 422 with_qp = self.rfc_printable_ascii.replace('\\', '\\\\') 423 with_qp = with_qp. replace('(', r'\(') 424 with_qp = with_qp.replace(')', r'\)') 425 ptext = self._test_get_x(parser.get_qp_ctext, 426 with_qp, self.rfc_printable_ascii, ' ', [], '') 427 428 def test_get_qp_ctext_two_words_gets_first(self): 429 self._test_get_x(parser.get_qp_ctext, 430 'foo de', 'foo', ' ', [], ' de') 431 432 def test_get_qp_ctext_following_wsp_preserved(self): 433 self._test_get_x(parser.get_qp_ctext, 434 'foo \t\tde', 'foo', ' ', [], ' \t\tde') 435 436 def test_get_qp_ctext_up_to_close_paren_only(self): 437 self._test_get_x(parser.get_qp_ctext, 438 'foo)', 'foo', ' ', [], ')') 439 440 def test_get_qp_ctext_wsp_before_close_paren_preserved(self): 441 self._test_get_x(parser.get_qp_ctext, 442 'foo )', 'foo', ' ', [], ' )') 443 444 def test_get_qp_ctext_close_paren_mid_word(self): 445 self._test_get_x(parser.get_qp_ctext, 446 'foo)bar', 'foo', ' ', [], ')bar') 447 448 def test_get_qp_ctext_up_to_open_paren_only(self): 449 self._test_get_x(parser.get_qp_ctext, 450 'foo(', 'foo', ' ', [], '(') 451 452 def test_get_qp_ctext_wsp_before_open_paren_preserved(self): 453 self._test_get_x(parser.get_qp_ctext, 454 'foo (', 'foo', ' ', [], ' (') 455 456 def test_get_qp_ctext_open_paren_mid_word(self): 457 self._test_get_x(parser.get_qp_ctext, 458 'foo(bar', 'foo', ' ', [], '(bar') 459 460 def test_get_qp_ctext_non_printables(self): 461 ptext = self._test_get_x(parser.get_qp_ctext, 462 'foo\x00bar)', 'foo\x00bar', ' ', 463 [errors.NonPrintableDefect], ')') 464 self.assertEqual(ptext.defects[0].non_printables[0], '\x00') 465 466 # get_qcontent 467 468 def test_get_qcontent_only(self): 469 ptext = self._test_get_x(parser.get_qcontent, 470 'foobar', 'foobar', 'foobar', [], '') 471 self.assertEqual(ptext.token_type, 'ptext') 472 473 def test_get_qcontent_all_printables(self): 474 with_qp = self.rfc_printable_ascii.replace('\\', '\\\\') 475 with_qp = with_qp. replace('"', r'\"') 476 ptext = self._test_get_x(parser.get_qcontent, with_qp, 477 self.rfc_printable_ascii, 478 self.rfc_printable_ascii, [], '') 479 480 def test_get_qcontent_two_words_gets_first(self): 481 self._test_get_x(parser.get_qcontent, 482 'foo de', 'foo', 'foo', [], ' de') 483 484 def test_get_qcontent_following_wsp_preserved(self): 485 self._test_get_x(parser.get_qcontent, 486 'foo \t\tde', 'foo', 'foo', [], ' \t\tde') 487 488 def test_get_qcontent_up_to_dquote_only(self): 489 self._test_get_x(parser.get_qcontent, 490 'foo"', 'foo', 'foo', [], '"') 491 492 def test_get_qcontent_wsp_before_close_paren_preserved(self): 493 self._test_get_x(parser.get_qcontent, 494 'foo "', 'foo', 'foo', [], ' "') 495 496 def test_get_qcontent_close_paren_mid_word(self): 497 self._test_get_x(parser.get_qcontent, 498 'foo"bar', 'foo', 'foo', [], '"bar') 499 500 def test_get_qcontent_non_printables(self): 501 ptext = self._test_get_x(parser.get_qcontent, 502 'foo\x00fg"', 'foo\x00fg', 'foo\x00fg', 503 [errors.NonPrintableDefect], '"') 504 self.assertEqual(ptext.defects[0].non_printables[0], '\x00') 505 506 # get_atext 507 508 def test_get_atext_only(self): 509 atext = self._test_get_x(parser.get_atext, 510 'foobar', 'foobar', 'foobar', [], '') 511 self.assertEqual(atext.token_type, 'atext') 512 513 def test_get_atext_all_atext(self): 514 atext = self._test_get_x(parser.get_atext, self.rfc_atext_chars, 515 self.rfc_atext_chars, 516 self.rfc_atext_chars, [], '') 517 518 def test_get_atext_two_words_gets_first(self): 519 self._test_get_x(parser.get_atext, 520 'foo bar', 'foo', 'foo', [], ' bar') 521 522 def test_get_atext_following_wsp_preserved(self): 523 self._test_get_x(parser.get_atext, 524 'foo \t\tbar', 'foo', 'foo', [], ' \t\tbar') 525 526 def test_get_atext_up_to_special(self): 527 self._test_get_x(parser.get_atext, 528 'foo@bar', 'foo', 'foo', [], '@bar') 529 530 def test_get_atext_non_printables(self): 531 atext = self._test_get_x(parser.get_atext, 532 'foo\x00bar(', 'foo\x00bar', 'foo\x00bar', 533 [errors.NonPrintableDefect], '(') 534 self.assertEqual(atext.defects[0].non_printables[0], '\x00') 535 536 # get_bare_quoted_string 537 538 def test_get_bare_quoted_string_only(self): 539 bqs = self._test_get_x(parser.get_bare_quoted_string, 540 '"foo"', '"foo"', 'foo', [], '') 541 self.assertEqual(bqs.token_type, 'bare-quoted-string') 542 543 def test_get_bare_quoted_string_must_start_with_dquote(self): 544 with self.assertRaises(errors.HeaderParseError): 545 parser.get_bare_quoted_string('foo"') 546 with self.assertRaises(errors.HeaderParseError): 547 parser.get_bare_quoted_string(' "foo"') 548 549 def test_get_bare_quoted_string_only_quotes(self): 550 self._test_get_x(parser.get_bare_quoted_string, 551 '""', '""', '', [], '') 552 553 def test_get_bare_quoted_string_missing_endquotes(self): 554 self._test_get_x(parser.get_bare_quoted_string, 555 '"', '""', '', [errors.InvalidHeaderDefect], '') 556 557 def test_get_bare_quoted_string_following_wsp_preserved(self): 558 self._test_get_x(parser.get_bare_quoted_string, 559 '"foo"\t bar', '"foo"', 'foo', [], '\t bar') 560 561 def test_get_bare_quoted_string_multiple_words(self): 562 self._test_get_x(parser.get_bare_quoted_string, 563 '"foo bar moo"', '"foo bar moo"', 'foo bar moo', [], '') 564 565 def test_get_bare_quoted_string_multiple_words_wsp_preserved(self): 566 self._test_get_x(parser.get_bare_quoted_string, 567 '" foo moo\t"', '" foo moo\t"', ' foo moo\t', [], '') 568 569 def test_get_bare_quoted_string_end_dquote_mid_word(self): 570 self._test_get_x(parser.get_bare_quoted_string, 571 '"foo"bar', '"foo"', 'foo', [], 'bar') 572 573 def test_get_bare_quoted_string_quoted_dquote(self): 574 self._test_get_x(parser.get_bare_quoted_string, 575 r'"foo\"in"a', r'"foo\"in"', 'foo"in', [], 'a') 576 577 def test_get_bare_quoted_string_non_printables(self): 578 self._test_get_x(parser.get_bare_quoted_string, 579 '"a\x01a"', '"a\x01a"', 'a\x01a', 580 [errors.NonPrintableDefect], '') 581 582 def test_get_bare_quoted_string_no_end_dquote(self): 583 self._test_get_x(parser.get_bare_quoted_string, 584 '"foo', '"foo"', 'foo', 585 [errors.InvalidHeaderDefect], '') 586 self._test_get_x(parser.get_bare_quoted_string, 587 '"foo ', '"foo "', 'foo ', 588 [errors.InvalidHeaderDefect], '') 589 590 def test_get_bare_quoted_string_empty_quotes(self): 591 self._test_get_x(parser.get_bare_quoted_string, 592 '""', '""', '', [], '') 593 594 # Issue 16983: apply postel's law to some bad encoding. 595 def test_encoded_word_inside_quotes(self): 596 self._test_get_x(parser.get_bare_quoted_string, 597 '"=?utf-8?Q?not_really_valid?="', 598 '"not really valid"', 599 'not really valid', 600 [errors.InvalidHeaderDefect, 601 errors.InvalidHeaderDefect], 602 '') 603 604 # get_comment 605 606 def test_get_comment_only(self): 607 comment = self._test_get_x(parser.get_comment, 608 '(comment)', '(comment)', ' ', [], '', ['comment']) 609 self.assertEqual(comment.token_type, 'comment') 610 611 def test_get_comment_must_start_with_paren(self): 612 with self.assertRaises(errors.HeaderParseError): 613 parser.get_comment('foo"') 614 with self.assertRaises(errors.HeaderParseError): 615 parser.get_comment(' (foo"') 616 617 def test_get_comment_following_wsp_preserved(self): 618 self._test_get_x(parser.get_comment, 619 '(comment) \t', '(comment)', ' ', [], ' \t', ['comment']) 620 621 def test_get_comment_multiple_words(self): 622 self._test_get_x(parser.get_comment, 623 '(foo bar) \t', '(foo bar)', ' ', [], ' \t', ['foo bar']) 624 625 def test_get_comment_multiple_words_wsp_preserved(self): 626 self._test_get_x(parser.get_comment, 627 '( foo bar\t ) \t', '( foo bar\t )', ' ', [], ' \t', 628 [' foo bar\t ']) 629 630 def test_get_comment_end_paren_mid_word(self): 631 self._test_get_x(parser.get_comment, 632 '(foo)bar', '(foo)', ' ', [], 'bar', ['foo']) 633 634 def test_get_comment_quoted_parens(self): 635 self._test_get_x(parser.get_comment, 636 r'(foo\) \(\)bar)', r'(foo\) \(\)bar)', ' ', [], '', ['foo) ()bar']) 637 638 def test_get_comment_non_printable(self): 639 self._test_get_x(parser.get_comment, 640 '(foo\x7Fbar)', '(foo\x7Fbar)', ' ', 641 [errors.NonPrintableDefect], '', ['foo\x7Fbar']) 642 643 def test_get_comment_no_end_paren(self): 644 self._test_get_x(parser.get_comment, 645 '(foo bar', '(foo bar)', ' ', 646 [errors.InvalidHeaderDefect], '', ['foo bar']) 647 self._test_get_x(parser.get_comment, 648 '(foo bar ', '(foo bar )', ' ', 649 [errors.InvalidHeaderDefect], '', ['foo bar ']) 650 651 def test_get_comment_nested_comment(self): 652 comment = self._test_get_x(parser.get_comment, 653 '(foo(bar))', '(foo(bar))', ' ', [], '', ['foo(bar)']) 654 self.assertEqual(comment[1].content, 'bar') 655 656 def test_get_comment_nested_comment_wsp(self): 657 comment = self._test_get_x(parser.get_comment, 658 '(foo ( bar ) )', '(foo ( bar ) )', ' ', [], '', ['foo ( bar ) ']) 659 self.assertEqual(comment[2].content, ' bar ') 660 661 def test_get_comment_empty_comment(self): 662 self._test_get_x(parser.get_comment, 663 '()', '()', ' ', [], '', ['']) 664 665 def test_get_comment_multiple_nesting(self): 666 comment = self._test_get_x(parser.get_comment, 667 '(((((foo)))))', '(((((foo)))))', ' ', [], '', ['((((foo))))']) 668 for i in range(4, 0, -1): 669 self.assertEqual(comment[0].content, '('*(i-1)+'foo'+')'*(i-1)) 670 comment = comment[0] 671 self.assertEqual(comment.content, 'foo') 672 673 def test_get_comment_missing_end_of_nesting(self): 674 self._test_get_x(parser.get_comment, 675 '(((((foo)))', '(((((foo)))))', ' ', 676 [errors.InvalidHeaderDefect]*2, '', ['((((foo))))']) 677 678 def test_get_comment_qs_in_nested_comment(self): 679 comment = self._test_get_x(parser.get_comment, 680 r'(foo (b\)))', r'(foo (b\)))', ' ', [], '', [r'foo (b\))']) 681 self.assertEqual(comment[2].content, 'b)') 682 683 # get_cfws 684 685 def test_get_cfws_only_ws(self): 686 cfws = self._test_get_x(parser.get_cfws, 687 ' \t \t', ' \t \t', ' ', [], '', []) 688 self.assertEqual(cfws.token_type, 'cfws') 689 690 def test_get_cfws_only_comment(self): 691 cfws = self._test_get_x(parser.get_cfws, 692 '(foo)', '(foo)', ' ', [], '', ['foo']) 693 self.assertEqual(cfws[0].content, 'foo') 694 695 def test_get_cfws_only_mixed(self): 696 cfws = self._test_get_x(parser.get_cfws, 697 ' (foo ) ( bar) ', ' (foo ) ( bar) ', ' ', [], '', 698 ['foo ', ' bar']) 699 self.assertEqual(cfws[1].content, 'foo ') 700 self.assertEqual(cfws[3].content, ' bar') 701 702 def test_get_cfws_ends_at_non_leader(self): 703 cfws = self._test_get_x(parser.get_cfws, 704 '(foo) bar', '(foo) ', ' ', [], 'bar', ['foo']) 705 self.assertEqual(cfws[0].content, 'foo') 706 707 def test_get_cfws_ends_at_non_printable(self): 708 cfws = self._test_get_x(parser.get_cfws, 709 '(foo) \x07', '(foo) ', ' ', [], '\x07', ['foo']) 710 self.assertEqual(cfws[0].content, 'foo') 711 712 def test_get_cfws_non_printable_in_comment(self): 713 cfws = self._test_get_x(parser.get_cfws, 714 '(foo \x07) "test"', '(foo \x07) ', ' ', 715 [errors.NonPrintableDefect], '"test"', ['foo \x07']) 716 self.assertEqual(cfws[0].content, 'foo \x07') 717 718 def test_get_cfws_header_ends_in_comment(self): 719 cfws = self._test_get_x(parser.get_cfws, 720 ' (foo ', ' (foo )', ' ', 721 [errors.InvalidHeaderDefect], '', ['foo ']) 722 self.assertEqual(cfws[1].content, 'foo ') 723 724 def test_get_cfws_multiple_nested_comments(self): 725 cfws = self._test_get_x(parser.get_cfws, 726 '(foo (bar)) ((a)(a))', '(foo (bar)) ((a)(a))', ' ', [], 727 '', ['foo (bar)', '(a)(a)']) 728 self.assertEqual(cfws[0].comments, ['foo (bar)']) 729 self.assertEqual(cfws[2].comments, ['(a)(a)']) 730 731 # get_quoted_string 732 733 def test_get_quoted_string_only(self): 734 qs = self._test_get_x(parser.get_quoted_string, 735 '"bob"', '"bob"', 'bob', [], '') 736 self.assertEqual(qs.token_type, 'quoted-string') 737 self.assertEqual(qs.quoted_value, '"bob"') 738 self.assertEqual(qs.content, 'bob') 739 740 def test_get_quoted_string_with_wsp(self): 741 qs = self._test_get_x(parser.get_quoted_string, 742 '\t "bob" ', '\t "bob" ', ' bob ', [], '') 743 self.assertEqual(qs.quoted_value, ' "bob" ') 744 self.assertEqual(qs.content, 'bob') 745 746 def test_get_quoted_string_with_comments_and_wsp(self): 747 qs = self._test_get_x(parser.get_quoted_string, 748 ' (foo) "bob"(bar)', ' (foo) "bob"(bar)', ' bob ', [], '') 749 self.assertEqual(qs[0][1].content, 'foo') 750 self.assertEqual(qs[2][0].content, 'bar') 751 self.assertEqual(qs.content, 'bob') 752 self.assertEqual(qs.quoted_value, ' "bob" ') 753 754 def test_get_quoted_string_with_multiple_comments(self): 755 qs = self._test_get_x(parser.get_quoted_string, 756 ' (foo) (bar) "bob"(bird)', ' (foo) (bar) "bob"(bird)', ' bob ', 757 [], '') 758 self.assertEqual(qs[0].comments, ['foo', 'bar']) 759 self.assertEqual(qs[2].comments, ['bird']) 760 self.assertEqual(qs.content, 'bob') 761 self.assertEqual(qs.quoted_value, ' "bob" ') 762 763 def test_get_quoted_string_non_printable_in_comment(self): 764 qs = self._test_get_x(parser.get_quoted_string, 765 ' (\x0A) "bob"', ' (\x0A) "bob"', ' bob', 766 [errors.NonPrintableDefect], '') 767 self.assertEqual(qs[0].comments, ['\x0A']) 768 self.assertEqual(qs.content, 'bob') 769 self.assertEqual(qs.quoted_value, ' "bob"') 770 771 def test_get_quoted_string_non_printable_in_qcontent(self): 772 qs = self._test_get_x(parser.get_quoted_string, 773 ' (a) "a\x0B"', ' (a) "a\x0B"', ' a\x0B', 774 [errors.NonPrintableDefect], '') 775 self.assertEqual(qs[0].comments, ['a']) 776 self.assertEqual(qs.content, 'a\x0B') 777 self.assertEqual(qs.quoted_value, ' "a\x0B"') 778 779 def test_get_quoted_string_internal_ws(self): 780 qs = self._test_get_x(parser.get_quoted_string, 781 ' (a) "foo bar "', ' (a) "foo bar "', ' foo bar ', 782 [], '') 783 self.assertEqual(qs[0].comments, ['a']) 784 self.assertEqual(qs.content, 'foo bar ') 785 self.assertEqual(qs.quoted_value, ' "foo bar "') 786 787 def test_get_quoted_string_header_ends_in_comment(self): 788 qs = self._test_get_x(parser.get_quoted_string, 789 ' (a) "bob" (a', ' (a) "bob" (a)', ' bob ', 790 [errors.InvalidHeaderDefect], '') 791 self.assertEqual(qs[0].comments, ['a']) 792 self.assertEqual(qs[2].comments, ['a']) 793 self.assertEqual(qs.content, 'bob') 794 self.assertEqual(qs.quoted_value, ' "bob" ') 795 796 def test_get_quoted_string_header_ends_in_qcontent(self): 797 qs = self._test_get_x(parser.get_quoted_string, 798 ' (a) "bob', ' (a) "bob"', ' bob', 799 [errors.InvalidHeaderDefect], '') 800 self.assertEqual(qs[0].comments, ['a']) 801 self.assertEqual(qs.content, 'bob') 802 self.assertEqual(qs.quoted_value, ' "bob"') 803 804 def test_get_quoted_string_no_quoted_string(self): 805 with self.assertRaises(errors.HeaderParseError): 806 parser.get_quoted_string(' (ab) xyz') 807 808 def test_get_quoted_string_qs_ends_at_noncfws(self): 809 qs = self._test_get_x(parser.get_quoted_string, 810 '\t "bob" fee', '\t "bob" ', ' bob ', [], 'fee') 811 self.assertEqual(qs.content, 'bob') 812 self.assertEqual(qs.quoted_value, ' "bob" ') 813 814 # get_atom 815 816 def test_get_atom_only(self): 817 atom = self._test_get_x(parser.get_atom, 818 'bob', 'bob', 'bob', [], '') 819 self.assertEqual(atom.token_type, 'atom') 820 821 def test_get_atom_with_wsp(self): 822 self._test_get_x(parser.get_atom, 823 '\t bob ', '\t bob ', ' bob ', [], '') 824 825 def test_get_atom_with_comments_and_wsp(self): 826 atom = self._test_get_x(parser.get_atom, 827 ' (foo) bob(bar)', ' (foo) bob(bar)', ' bob ', [], '') 828 self.assertEqual(atom[0][1].content, 'foo') 829 self.assertEqual(atom[2][0].content, 'bar') 830 831 def test_get_atom_with_multiple_comments(self): 832 atom = self._test_get_x(parser.get_atom, 833 ' (foo) (bar) bob(bird)', ' (foo) (bar) bob(bird)', ' bob ', 834 [], '') 835 self.assertEqual(atom[0].comments, ['foo', 'bar']) 836 self.assertEqual(atom[2].comments, ['bird']) 837 838 def test_get_atom_non_printable_in_comment(self): 839 atom = self._test_get_x(parser.get_atom, 840 ' (\x0A) bob', ' (\x0A) bob', ' bob', 841 [errors.NonPrintableDefect], '') 842 self.assertEqual(atom[0].comments, ['\x0A']) 843 844 def test_get_atom_non_printable_in_atext(self): 845 atom = self._test_get_x(parser.get_atom, 846 ' (a) a\x0B', ' (a) a\x0B', ' a\x0B', 847 [errors.NonPrintableDefect], '') 848 self.assertEqual(atom[0].comments, ['a']) 849 850 def test_get_atom_header_ends_in_comment(self): 851 atom = self._test_get_x(parser.get_atom, 852 ' (a) bob (a', ' (a) bob (a)', ' bob ', 853 [errors.InvalidHeaderDefect], '') 854 self.assertEqual(atom[0].comments, ['a']) 855 self.assertEqual(atom[2].comments, ['a']) 856 857 def test_get_atom_no_atom(self): 858 with self.assertRaises(errors.HeaderParseError): 859 parser.get_atom(' (ab) ') 860 861 def test_get_atom_no_atom_before_special(self): 862 with self.assertRaises(errors.HeaderParseError): 863 parser.get_atom(' (ab) @') 864 865 def test_get_atom_atom_ends_at_special(self): 866 atom = self._test_get_x(parser.get_atom, 867 ' (foo) bob(bar) @bang', ' (foo) bob(bar) ', ' bob ', [], '@bang') 868 self.assertEqual(atom[0].comments, ['foo']) 869 self.assertEqual(atom[2].comments, ['bar']) 870 871 def test_get_atom_atom_ends_at_noncfws(self): 872 self._test_get_x(parser.get_atom, 873 'bob fred', 'bob ', 'bob ', [], 'fred') 874 875 def test_get_atom_rfc2047_atom(self): 876 self._test_get_x(parser.get_atom, 877 '=?utf-8?q?=20bob?=', ' bob', ' bob', [], '') 878 879 # get_dot_atom_text 880 881 def test_get_dot_atom_text(self): 882 dot_atom_text = self._test_get_x(parser.get_dot_atom_text, 883 'foo.bar.bang', 'foo.bar.bang', 'foo.bar.bang', [], '') 884 self.assertEqual(dot_atom_text.token_type, 'dot-atom-text') 885 self.assertEqual(len(dot_atom_text), 5) 886 887 def test_get_dot_atom_text_lone_atom_is_valid(self): 888 dot_atom_text = self._test_get_x(parser.get_dot_atom_text, 889 'foo', 'foo', 'foo', [], '') 890 891 def test_get_dot_atom_text_raises_on_leading_dot(self): 892 with self.assertRaises(errors.HeaderParseError): 893 parser.get_dot_atom_text('.foo.bar') 894 895 def test_get_dot_atom_text_raises_on_trailing_dot(self): 896 with self.assertRaises(errors.HeaderParseError): 897 parser.get_dot_atom_text('foo.bar.') 898 899 def test_get_dot_atom_text_raises_on_leading_non_atext(self): 900 with self.assertRaises(errors.HeaderParseError): 901 parser.get_dot_atom_text(' foo.bar') 902 with self.assertRaises(errors.HeaderParseError): 903 parser.get_dot_atom_text('@foo.bar') 904 with self.assertRaises(errors.HeaderParseError): 905 parser.get_dot_atom_text('"foo.bar"') 906 907 def test_get_dot_atom_text_trailing_text_preserved(self): 908 dot_atom_text = self._test_get_x(parser.get_dot_atom_text, 909 'foo@bar', 'foo', 'foo', [], '@bar') 910 911 def test_get_dot_atom_text_trailing_ws_preserved(self): 912 dot_atom_text = self._test_get_x(parser.get_dot_atom_text, 913 'foo .bar', 'foo', 'foo', [], ' .bar') 914 915 # get_dot_atom 916 917 def test_get_dot_atom_only(self): 918 dot_atom = self._test_get_x(parser.get_dot_atom, 919 'foo.bar.bing', 'foo.bar.bing', 'foo.bar.bing', [], '') 920 self.assertEqual(dot_atom.token_type, 'dot-atom') 921 self.assertEqual(len(dot_atom), 1) 922 923 def test_get_dot_atom_with_wsp(self): 924 self._test_get_x(parser.get_dot_atom, 925 '\t foo.bar.bing ', '\t foo.bar.bing ', ' foo.bar.bing ', [], '') 926 927 def test_get_dot_atom_with_comments_and_wsp(self): 928 self._test_get_x(parser.get_dot_atom, 929 ' (sing) foo.bar.bing (here) ', ' (sing) foo.bar.bing (here) ', 930 ' foo.bar.bing ', [], '') 931 932 def test_get_dot_atom_space_ends_dot_atom(self): 933 self._test_get_x(parser.get_dot_atom, 934 ' (sing) foo.bar .bing (here) ', ' (sing) foo.bar ', 935 ' foo.bar ', [], '.bing (here) ') 936 937 def test_get_dot_atom_no_atom_raises(self): 938 with self.assertRaises(errors.HeaderParseError): 939 parser.get_dot_atom(' (foo) ') 940 941 def test_get_dot_atom_leading_dot_raises(self): 942 with self.assertRaises(errors.HeaderParseError): 943 parser.get_dot_atom(' (foo) .bar') 944 945 def test_get_dot_atom_two_dots_raises(self): 946 with self.assertRaises(errors.HeaderParseError): 947 parser.get_dot_atom('bar..bang') 948 949 def test_get_dot_atom_trailing_dot_raises(self): 950 with self.assertRaises(errors.HeaderParseError): 951 parser.get_dot_atom(' (foo) bar.bang. foo') 952 953 def test_get_dot_atom_rfc2047_atom(self): 954 self._test_get_x(parser.get_dot_atom, 955 '=?utf-8?q?=20bob?=', ' bob', ' bob', [], '') 956 957 # get_word (if this were black box we'd repeat all the qs/atom tests) 958 959 def test_get_word_atom_yields_atom(self): 960 word = self._test_get_x(parser.get_word, 961 ' (foo) bar (bang) :ah', ' (foo) bar (bang) ', ' bar ', [], ':ah') 962 self.assertEqual(word.token_type, 'atom') 963 self.assertEqual(word[0].token_type, 'cfws') 964 965 def test_get_word_all_CFWS(self): 966 # bpo-29412: Test that we don't raise IndexError when parsing CFWS only 967 # token. 968 with self.assertRaises(errors.HeaderParseError): 969 parser.get_word('(Recipients list suppressed') 970 971 def test_get_word_qs_yields_qs(self): 972 word = self._test_get_x(parser.get_word, 973 '"bar " (bang) ah', '"bar " (bang) ', 'bar ', [], 'ah') 974 self.assertEqual(word.token_type, 'quoted-string') 975 self.assertEqual(word[0].token_type, 'bare-quoted-string') 976 self.assertEqual(word[0].value, 'bar ') 977 self.assertEqual(word.content, 'bar ') 978 979 def test_get_word_ends_at_dot(self): 980 self._test_get_x(parser.get_word, 981 'foo.', 'foo', 'foo', [], '.') 982 983 # get_phrase 984 985 def test_get_phrase_simple(self): 986 phrase = self._test_get_x(parser.get_phrase, 987 '"Fred A. Johnson" is his name, oh.', 988 '"Fred A. Johnson" is his name', 989 'Fred A. Johnson is his name', 990 [], 991 ', oh.') 992 self.assertEqual(phrase.token_type, 'phrase') 993 994 def test_get_phrase_complex(self): 995 phrase = self._test_get_x(parser.get_phrase, 996 ' (A) bird (in (my|your)) "hand " is messy\t<>\t', 997 ' (A) bird (in (my|your)) "hand " is messy\t', 998 ' bird hand is messy ', 999 [], 1000 '<>\t') 1001 self.assertEqual(phrase[0][0].comments, ['A']) 1002 self.assertEqual(phrase[0][2].comments, ['in (my|your)']) 1003 1004 def test_get_phrase_obsolete(self): 1005 phrase = self._test_get_x(parser.get_phrase, 1006 'Fred A.(weird).O Johnson', 1007 'Fred A.(weird).O Johnson', 1008 'Fred A. .O Johnson', 1009 [errors.ObsoleteHeaderDefect]*3, 1010 '') 1011 self.assertEqual(len(phrase), 7) 1012 self.assertEqual(phrase[3].comments, ['weird']) 1013 1014 def test_get_phrase_pharse_must_start_with_word(self): 1015 phrase = self._test_get_x(parser.get_phrase, 1016 '(even weirder).name', 1017 '(even weirder).name', 1018 ' .name', 1019 [errors.InvalidHeaderDefect] + [errors.ObsoleteHeaderDefect]*2, 1020 '') 1021 self.assertEqual(len(phrase), 3) 1022 self.assertEqual(phrase[0].comments, ['even weirder']) 1023 1024 def test_get_phrase_ending_with_obsolete(self): 1025 phrase = self._test_get_x(parser.get_phrase, 1026 'simple phrase.(with trailing comment):boo', 1027 'simple phrase.(with trailing comment)', 1028 'simple phrase. ', 1029 [errors.ObsoleteHeaderDefect]*2, 1030 ':boo') 1031 self.assertEqual(len(phrase), 4) 1032 self.assertEqual(phrase[3].comments, ['with trailing comment']) 1033 1034 def get_phrase_cfws_only_raises(self): 1035 with self.assertRaises(errors.HeaderParseError): 1036 parser.get_phrase(' (foo) ') 1037 1038 # get_local_part 1039 1040 def test_get_local_part_simple(self): 1041 local_part = self._test_get_x(parser.get_local_part, 1042 'dinsdale@python.org', 'dinsdale', 'dinsdale', [], '@python.org') 1043 self.assertEqual(local_part.token_type, 'local-part') 1044 self.assertEqual(local_part.local_part, 'dinsdale') 1045 1046 def test_get_local_part_with_dot(self): 1047 local_part = self._test_get_x(parser.get_local_part, 1048 'Fred.A.Johnson@python.org', 1049 'Fred.A.Johnson', 1050 'Fred.A.Johnson', 1051 [], 1052 '@python.org') 1053 self.assertEqual(local_part.local_part, 'Fred.A.Johnson') 1054 1055 def test_get_local_part_with_whitespace(self): 1056 local_part = self._test_get_x(parser.get_local_part, 1057 ' Fred.A.Johnson @python.org', 1058 ' Fred.A.Johnson ', 1059 ' Fred.A.Johnson ', 1060 [], 1061 '@python.org') 1062 self.assertEqual(local_part.local_part, 'Fred.A.Johnson') 1063 1064 def test_get_local_part_with_cfws(self): 1065 local_part = self._test_get_x(parser.get_local_part, 1066 ' (foo) Fred.A.Johnson (bar (bird)) @python.org', 1067 ' (foo) Fred.A.Johnson (bar (bird)) ', 1068 ' Fred.A.Johnson ', 1069 [], 1070 '@python.org') 1071 self.assertEqual(local_part.local_part, 'Fred.A.Johnson') 1072 self.assertEqual(local_part[0][0].comments, ['foo']) 1073 self.assertEqual(local_part[0][2].comments, ['bar (bird)']) 1074 1075 def test_get_local_part_simple_quoted(self): 1076 local_part = self._test_get_x(parser.get_local_part, 1077 '"dinsdale"@python.org', '"dinsdale"', '"dinsdale"', [], '@python.org') 1078 self.assertEqual(local_part.token_type, 'local-part') 1079 self.assertEqual(local_part.local_part, 'dinsdale') 1080 1081 def test_get_local_part_with_quoted_dot(self): 1082 local_part = self._test_get_x(parser.get_local_part, 1083 '"Fred.A.Johnson"@python.org', 1084 '"Fred.A.Johnson"', 1085 '"Fred.A.Johnson"', 1086 [], 1087 '@python.org') 1088 self.assertEqual(local_part.local_part, 'Fred.A.Johnson') 1089 1090 def test_get_local_part_quoted_with_whitespace(self): 1091 local_part = self._test_get_x(parser.get_local_part, 1092 ' "Fred A. Johnson" @python.org', 1093 ' "Fred A. Johnson" ', 1094 ' "Fred A. Johnson" ', 1095 [], 1096 '@python.org') 1097 self.assertEqual(local_part.local_part, 'Fred A. Johnson') 1098 1099 def test_get_local_part_quoted_with_cfws(self): 1100 local_part = self._test_get_x(parser.get_local_part, 1101 ' (foo) " Fred A. Johnson " (bar (bird)) @python.org', 1102 ' (foo) " Fred A. Johnson " (bar (bird)) ', 1103 ' " Fred A. Johnson " ', 1104 [], 1105 '@python.org') 1106 self.assertEqual(local_part.local_part, ' Fred A. Johnson ') 1107 self.assertEqual(local_part[0][0].comments, ['foo']) 1108 self.assertEqual(local_part[0][2].comments, ['bar (bird)']) 1109 1110 1111 def test_get_local_part_simple_obsolete(self): 1112 local_part = self._test_get_x(parser.get_local_part, 1113 'Fred. A.Johnson@python.org', 1114 'Fred. A.Johnson', 1115 'Fred. A.Johnson', 1116 [errors.ObsoleteHeaderDefect], 1117 '@python.org') 1118 self.assertEqual(local_part.local_part, 'Fred.A.Johnson') 1119 1120 def test_get_local_part_complex_obsolete_1(self): 1121 local_part = self._test_get_x(parser.get_local_part, 1122 ' (foo )Fred (bar).(bird) A.(sheep)Johnson."and dogs "@python.org', 1123 ' (foo )Fred (bar).(bird) A.(sheep)Johnson."and dogs "', 1124 ' Fred . A. Johnson.and dogs ', 1125 [errors.ObsoleteHeaderDefect], 1126 '@python.org') 1127 self.assertEqual(local_part.local_part, 'Fred.A.Johnson.and dogs ') 1128 1129 def test_get_local_part_complex_obsolete_invalid(self): 1130 local_part = self._test_get_x(parser.get_local_part, 1131 ' (foo )Fred (bar).(bird) A.(sheep)Johnson "and dogs"@python.org', 1132 ' (foo )Fred (bar).(bird) A.(sheep)Johnson "and dogs"', 1133 ' Fred . A. Johnson and dogs', 1134 [errors.InvalidHeaderDefect]*2, 1135 '@python.org') 1136 self.assertEqual(local_part.local_part, 'Fred.A.Johnson and dogs') 1137 1138 def test_get_local_part_no_part_raises(self): 1139 with self.assertRaises(errors.HeaderParseError): 1140 parser.get_local_part(' (foo) ') 1141 1142 def test_get_local_part_special_instead_raises(self): 1143 with self.assertRaises(errors.HeaderParseError): 1144 parser.get_local_part(' (foo) @python.org') 1145 1146 def test_get_local_part_trailing_dot(self): 1147 local_part = self._test_get_x(parser.get_local_part, 1148 ' borris.@python.org', 1149 ' borris.', 1150 ' borris.', 1151 [errors.InvalidHeaderDefect]*2, 1152 '@python.org') 1153 self.assertEqual(local_part.local_part, 'borris.') 1154 1155 def test_get_local_part_trailing_dot_with_ws(self): 1156 local_part = self._test_get_x(parser.get_local_part, 1157 ' borris. @python.org', 1158 ' borris. ', 1159 ' borris. ', 1160 [errors.InvalidHeaderDefect]*2, 1161 '@python.org') 1162 self.assertEqual(local_part.local_part, 'borris.') 1163 1164 def test_get_local_part_leading_dot(self): 1165 local_part = self._test_get_x(parser.get_local_part, 1166 '.borris@python.org', 1167 '.borris', 1168 '.borris', 1169 [errors.InvalidHeaderDefect]*2, 1170 '@python.org') 1171 self.assertEqual(local_part.local_part, '.borris') 1172 1173 def test_get_local_part_leading_dot_after_ws(self): 1174 local_part = self._test_get_x(parser.get_local_part, 1175 ' .borris@python.org', 1176 ' .borris', 1177 ' .borris', 1178 [errors.InvalidHeaderDefect]*2, 1179 '@python.org') 1180 self.assertEqual(local_part.local_part, '.borris') 1181 1182 def test_get_local_part_double_dot_raises(self): 1183 local_part = self._test_get_x(parser.get_local_part, 1184 ' borris.(foo).natasha@python.org', 1185 ' borris.(foo).natasha', 1186 ' borris. .natasha', 1187 [errors.InvalidHeaderDefect]*2, 1188 '@python.org') 1189 self.assertEqual(local_part.local_part, 'borris..natasha') 1190 1191 def test_get_local_part_quoted_strings_in_atom_list(self): 1192 local_part = self._test_get_x(parser.get_local_part, 1193 '""example" example"@example.com', 1194 '""example" example"', 1195 'example example', 1196 [errors.InvalidHeaderDefect]*3, 1197 '@example.com') 1198 self.assertEqual(local_part.local_part, 'example example') 1199 1200 def test_get_local_part_valid_and_invalid_qp_in_atom_list(self): 1201 local_part = self._test_get_x(parser.get_local_part, 1202 r'"\\"example\\" example"@example.com', 1203 r'"\\"example\\" example"', 1204 r'\example\\ example', 1205 [errors.InvalidHeaderDefect]*5, 1206 '@example.com') 1207 self.assertEqual(local_part.local_part, r'\example\\ example') 1208 1209 def test_get_local_part_unicode_defect(self): 1210 # Currently this only happens when parsing unicode, not when parsing 1211 # stuff that was originally binary. 1212 local_part = self._test_get_x(parser.get_local_part, 1213 'exámple@example.com', 1214 'exámple', 1215 'exámple', 1216 [errors.NonASCIILocalPartDefect], 1217 '@example.com') 1218 self.assertEqual(local_part.local_part, 'exámple') 1219 1220 # get_dtext 1221 1222 def test_get_dtext_only(self): 1223 dtext = self._test_get_x(parser.get_dtext, 1224 'foobar', 'foobar', 'foobar', [], '') 1225 self.assertEqual(dtext.token_type, 'ptext') 1226 1227 def test_get_dtext_all_dtext(self): 1228 dtext = self._test_get_x(parser.get_dtext, self.rfc_dtext_chars, 1229 self.rfc_dtext_chars, 1230 self.rfc_dtext_chars, [], '') 1231 1232 def test_get_dtext_two_words_gets_first(self): 1233 self._test_get_x(parser.get_dtext, 1234 'foo bar', 'foo', 'foo', [], ' bar') 1235 1236 def test_get_dtext_following_wsp_preserved(self): 1237 self._test_get_x(parser.get_dtext, 1238 'foo \t\tbar', 'foo', 'foo', [], ' \t\tbar') 1239 1240 def test_get_dtext_non_printables(self): 1241 dtext = self._test_get_x(parser.get_dtext, 1242 'foo\x00bar]', 'foo\x00bar', 'foo\x00bar', 1243 [errors.NonPrintableDefect], ']') 1244 self.assertEqual(dtext.defects[0].non_printables[0], '\x00') 1245 1246 def test_get_dtext_with_qp(self): 1247 ptext = self._test_get_x(parser.get_dtext, 1248 r'foo\]\[\\bar\b\e\l\l', 1249 r'foo][\barbell', 1250 r'foo][\barbell', 1251 [errors.ObsoleteHeaderDefect], 1252 '') 1253 1254 def test_get_dtext_up_to_close_bracket_only(self): 1255 self._test_get_x(parser.get_dtext, 1256 'foo]', 'foo', 'foo', [], ']') 1257 1258 def test_get_dtext_wsp_before_close_bracket_preserved(self): 1259 self._test_get_x(parser.get_dtext, 1260 'foo ]', 'foo', 'foo', [], ' ]') 1261 1262 def test_get_dtext_close_bracket_mid_word(self): 1263 self._test_get_x(parser.get_dtext, 1264 'foo]bar', 'foo', 'foo', [], ']bar') 1265 1266 def test_get_dtext_up_to_open_bracket_only(self): 1267 self._test_get_x(parser.get_dtext, 1268 'foo[', 'foo', 'foo', [], '[') 1269 1270 def test_get_dtext_wsp_before_open_bracket_preserved(self): 1271 self._test_get_x(parser.get_dtext, 1272 'foo [', 'foo', 'foo', [], ' [') 1273 1274 def test_get_dtext_open_bracket_mid_word(self): 1275 self._test_get_x(parser.get_dtext, 1276 'foo[bar', 'foo', 'foo', [], '[bar') 1277 1278 # get_domain_literal 1279 1280 def test_get_domain_literal_only(self): 1281 domain_literal = domain_literal = self._test_get_x(parser.get_domain_literal, 1282 '[127.0.0.1]', 1283 '[127.0.0.1]', 1284 '[127.0.0.1]', 1285 [], 1286 '') 1287 self.assertEqual(domain_literal.token_type, 'domain-literal') 1288 self.assertEqual(domain_literal.domain, '[127.0.0.1]') 1289 self.assertEqual(domain_literal.ip, '127.0.0.1') 1290 1291 def test_get_domain_literal_with_internal_ws(self): 1292 domain_literal = self._test_get_x(parser.get_domain_literal, 1293 '[ 127.0.0.1\t ]', 1294 '[ 127.0.0.1\t ]', 1295 '[ 127.0.0.1 ]', 1296 [], 1297 '') 1298 self.assertEqual(domain_literal.domain, '[127.0.0.1]') 1299 self.assertEqual(domain_literal.ip, '127.0.0.1') 1300 1301 def test_get_domain_literal_with_surrounding_cfws(self): 1302 domain_literal = self._test_get_x(parser.get_domain_literal, 1303 '(foo)[ 127.0.0.1] (bar)', 1304 '(foo)[ 127.0.0.1] (bar)', 1305 ' [ 127.0.0.1] ', 1306 [], 1307 '') 1308 self.assertEqual(domain_literal.domain, '[127.0.0.1]') 1309 self.assertEqual(domain_literal.ip, '127.0.0.1') 1310 1311 def test_get_domain_literal_no_start_char_raises(self): 1312 with self.assertRaises(errors.HeaderParseError): 1313 parser.get_domain_literal('(foo) ') 1314 1315 def test_get_domain_literal_no_start_char_before_special_raises(self): 1316 with self.assertRaises(errors.HeaderParseError): 1317 parser.get_domain_literal('(foo) @') 1318 1319 def test_get_domain_literal_bad_dtext_char_before_special_raises(self): 1320 with self.assertRaises(errors.HeaderParseError): 1321 parser.get_domain_literal('(foo) [abc[@') 1322 1323 # get_domain 1324 1325 def test_get_domain_regular_domain_only(self): 1326 domain = self._test_get_x(parser.get_domain, 1327 'example.com', 1328 'example.com', 1329 'example.com', 1330 [], 1331 '') 1332 self.assertEqual(domain.token_type, 'domain') 1333 self.assertEqual(domain.domain, 'example.com') 1334 1335 def test_get_domain_domain_literal_only(self): 1336 domain = self._test_get_x(parser.get_domain, 1337 '[127.0.0.1]', 1338 '[127.0.0.1]', 1339 '[127.0.0.1]', 1340 [], 1341 '') 1342 self.assertEqual(domain.token_type, 'domain') 1343 self.assertEqual(domain.domain, '[127.0.0.1]') 1344 1345 def test_get_domain_with_cfws(self): 1346 domain = self._test_get_x(parser.get_domain, 1347 '(foo) example.com(bar)\t', 1348 '(foo) example.com(bar)\t', 1349 ' example.com ', 1350 [], 1351 '') 1352 self.assertEqual(domain.domain, 'example.com') 1353 1354 def test_get_domain_domain_literal_with_cfws(self): 1355 domain = self._test_get_x(parser.get_domain, 1356 '(foo)[127.0.0.1]\t(bar)', 1357 '(foo)[127.0.0.1]\t(bar)', 1358 ' [127.0.0.1] ', 1359 [], 1360 '') 1361 self.assertEqual(domain.domain, '[127.0.0.1]') 1362 1363 def test_get_domain_domain_with_cfws_ends_at_special(self): 1364 domain = self._test_get_x(parser.get_domain, 1365 '(foo)example.com\t(bar), next', 1366 '(foo)example.com\t(bar)', 1367 ' example.com ', 1368 [], 1369 ', next') 1370 self.assertEqual(domain.domain, 'example.com') 1371 1372 def test_get_domain_domain_literal_with_cfws_ends_at_special(self): 1373 domain = self._test_get_x(parser.get_domain, 1374 '(foo)[127.0.0.1]\t(bar), next', 1375 '(foo)[127.0.0.1]\t(bar)', 1376 ' [127.0.0.1] ', 1377 [], 1378 ', next') 1379 self.assertEqual(domain.domain, '[127.0.0.1]') 1380 1381 def test_get_domain_obsolete(self): 1382 domain = self._test_get_x(parser.get_domain, 1383 '(foo) example . (bird)com(bar)\t', 1384 '(foo) example . (bird)com(bar)\t', 1385 ' example . com ', 1386 [errors.ObsoleteHeaderDefect], 1387 '') 1388 self.assertEqual(domain.domain, 'example.com') 1389 1390 def test_get_domain_no_non_cfws_raises(self): 1391 with self.assertRaises(errors.HeaderParseError): 1392 parser.get_domain(" (foo)\t") 1393 1394 def test_get_domain_no_atom_raises(self): 1395 with self.assertRaises(errors.HeaderParseError): 1396 parser.get_domain(" (foo)\t, broken") 1397 1398 1399 # get_addr_spec 1400 1401 def test_get_addr_spec_normal(self): 1402 addr_spec = self._test_get_x(parser.get_addr_spec, 1403 'dinsdale@example.com', 1404 'dinsdale@example.com', 1405 'dinsdale@example.com', 1406 [], 1407 '') 1408 self.assertEqual(addr_spec.token_type, 'addr-spec') 1409 self.assertEqual(addr_spec.local_part, 'dinsdale') 1410 self.assertEqual(addr_spec.domain, 'example.com') 1411 self.assertEqual(addr_spec.addr_spec, 'dinsdale@example.com') 1412 1413 def test_get_addr_spec_with_doamin_literal(self): 1414 addr_spec = self._test_get_x(parser.get_addr_spec, 1415 'dinsdale@[127.0.0.1]', 1416 'dinsdale@[127.0.0.1]', 1417 'dinsdale@[127.0.0.1]', 1418 [], 1419 '') 1420 self.assertEqual(addr_spec.local_part, 'dinsdale') 1421 self.assertEqual(addr_spec.domain, '[127.0.0.1]') 1422 self.assertEqual(addr_spec.addr_spec, 'dinsdale@[127.0.0.1]') 1423 1424 def test_get_addr_spec_with_cfws(self): 1425 addr_spec = self._test_get_x(parser.get_addr_spec, 1426 '(foo) dinsdale(bar)@ (bird) example.com (bog)', 1427 '(foo) dinsdale(bar)@ (bird) example.com (bog)', 1428 ' dinsdale@example.com ', 1429 [], 1430 '') 1431 self.assertEqual(addr_spec.local_part, 'dinsdale') 1432 self.assertEqual(addr_spec.domain, 'example.com') 1433 self.assertEqual(addr_spec.addr_spec, 'dinsdale@example.com') 1434 1435 def test_get_addr_spec_with_qouoted_string_and_cfws(self): 1436 addr_spec = self._test_get_x(parser.get_addr_spec, 1437 '(foo) "roy a bug"(bar)@ (bird) example.com (bog)', 1438 '(foo) "roy a bug"(bar)@ (bird) example.com (bog)', 1439 ' "roy a bug"@example.com ', 1440 [], 1441 '') 1442 self.assertEqual(addr_spec.local_part, 'roy a bug') 1443 self.assertEqual(addr_spec.domain, 'example.com') 1444 self.assertEqual(addr_spec.addr_spec, '"roy a bug"@example.com') 1445 1446 def test_get_addr_spec_ends_at_special(self): 1447 addr_spec = self._test_get_x(parser.get_addr_spec, 1448 '(foo) "roy a bug"(bar)@ (bird) example.com (bog) , next', 1449 '(foo) "roy a bug"(bar)@ (bird) example.com (bog) ', 1450 ' "roy a bug"@example.com ', 1451 [], 1452 ', next') 1453 self.assertEqual(addr_spec.local_part, 'roy a bug') 1454 self.assertEqual(addr_spec.domain, 'example.com') 1455 self.assertEqual(addr_spec.addr_spec, '"roy a bug"@example.com') 1456 1457 def test_get_addr_spec_quoted_strings_in_atom_list(self): 1458 addr_spec = self._test_get_x(parser.get_addr_spec, 1459 '""example" example"@example.com', 1460 '""example" example"@example.com', 1461 'example example@example.com', 1462 [errors.InvalidHeaderDefect]*3, 1463 '') 1464 self.assertEqual(addr_spec.local_part, 'example example') 1465 self.assertEqual(addr_spec.domain, 'example.com') 1466 self.assertEqual(addr_spec.addr_spec, '"example example"@example.com') 1467 1468 def test_get_addr_spec_dot_atom(self): 1469 addr_spec = self._test_get_x(parser.get_addr_spec, 1470 'star.a.star@example.com', 1471 'star.a.star@example.com', 1472 'star.a.star@example.com', 1473 [], 1474 '') 1475 self.assertEqual(addr_spec.local_part, 'star.a.star') 1476 self.assertEqual(addr_spec.domain, 'example.com') 1477 self.assertEqual(addr_spec.addr_spec, 'star.a.star@example.com') 1478 1479 def test_get_addr_spec_multiple_domains(self): 1480 with self.assertRaises(errors.HeaderParseError): 1481 parser.get_addr_spec('star@a.star@example.com') 1482 1483 with self.assertRaises(errors.HeaderParseError): 1484 parser.get_addr_spec('star@a@example.com') 1485 1486 with self.assertRaises(errors.HeaderParseError): 1487 parser.get_addr_spec('star@172.17.0.1@example.com') 1488 1489 # get_obs_route 1490 1491 def test_get_obs_route_simple(self): 1492 obs_route = self._test_get_x(parser.get_obs_route, 1493 '@example.com, @two.example.com:', 1494 '@example.com, @two.example.com:', 1495 '@example.com, @two.example.com:', 1496 [], 1497 '') 1498 self.assertEqual(obs_route.token_type, 'obs-route') 1499 self.assertEqual(obs_route.domains, ['example.com', 'two.example.com']) 1500 1501 def test_get_obs_route_complex(self): 1502 obs_route = self._test_get_x(parser.get_obs_route, 1503 '(foo),, (blue)@example.com (bar),@two.(foo) example.com (bird):', 1504 '(foo),, (blue)@example.com (bar),@two.(foo) example.com (bird):', 1505 ' ,, @example.com ,@two. example.com :', 1506 [errors.ObsoleteHeaderDefect], # This is the obs-domain 1507 '') 1508 self.assertEqual(obs_route.token_type, 'obs-route') 1509 self.assertEqual(obs_route.domains, ['example.com', 'two.example.com']) 1510 1511 def test_get_obs_route_no_route_before_end_raises(self): 1512 with self.assertRaises(errors.HeaderParseError): 1513 parser.get_obs_route('(foo) @example.com,') 1514 1515 def test_get_obs_route_no_route_before_special_raises(self): 1516 with self.assertRaises(errors.HeaderParseError): 1517 parser.get_obs_route('(foo) [abc],') 1518 1519 def test_get_obs_route_no_route_before_special_raises2(self): 1520 with self.assertRaises(errors.HeaderParseError): 1521 parser.get_obs_route('(foo) @example.com [abc],') 1522 1523 # get_angle_addr 1524 1525 def test_get_angle_addr_simple(self): 1526 angle_addr = self._test_get_x(parser.get_angle_addr, 1527 '<dinsdale@example.com>', 1528 '<dinsdale@example.com>', 1529 '<dinsdale@example.com>', 1530 [], 1531 '') 1532 self.assertEqual(angle_addr.token_type, 'angle-addr') 1533 self.assertEqual(angle_addr.local_part, 'dinsdale') 1534 self.assertEqual(angle_addr.domain, 'example.com') 1535 self.assertIsNone(angle_addr.route) 1536 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1537 1538 def test_get_angle_addr_empty(self): 1539 angle_addr = self._test_get_x(parser.get_angle_addr, 1540 '<>', 1541 '<>', 1542 '<>', 1543 [errors.InvalidHeaderDefect], 1544 '') 1545 self.assertEqual(angle_addr.token_type, 'angle-addr') 1546 self.assertIsNone(angle_addr.local_part) 1547 self.assertIsNone(angle_addr.domain) 1548 self.assertIsNone(angle_addr.route) 1549 self.assertEqual(angle_addr.addr_spec, '<>') 1550 1551 def test_get_angle_addr_qs_only_quotes(self): 1552 angle_addr = self._test_get_x(parser.get_angle_addr, 1553 '<""@example.com>', 1554 '<""@example.com>', 1555 '<""@example.com>', 1556 [], 1557 '') 1558 self.assertEqual(angle_addr.token_type, 'angle-addr') 1559 self.assertEqual(angle_addr.local_part, '') 1560 self.assertEqual(angle_addr.domain, 'example.com') 1561 self.assertIsNone(angle_addr.route) 1562 self.assertEqual(angle_addr.addr_spec, '""@example.com') 1563 1564 def test_get_angle_addr_with_cfws(self): 1565 angle_addr = self._test_get_x(parser.get_angle_addr, 1566 ' (foo) <dinsdale@example.com>(bar)', 1567 ' (foo) <dinsdale@example.com>(bar)', 1568 ' <dinsdale@example.com> ', 1569 [], 1570 '') 1571 self.assertEqual(angle_addr.token_type, 'angle-addr') 1572 self.assertEqual(angle_addr.local_part, 'dinsdale') 1573 self.assertEqual(angle_addr.domain, 'example.com') 1574 self.assertIsNone(angle_addr.route) 1575 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1576 1577 def test_get_angle_addr_qs_and_domain_literal(self): 1578 angle_addr = self._test_get_x(parser.get_angle_addr, 1579 '<"Fred Perfect"@[127.0.0.1]>', 1580 '<"Fred Perfect"@[127.0.0.1]>', 1581 '<"Fred Perfect"@[127.0.0.1]>', 1582 [], 1583 '') 1584 self.assertEqual(angle_addr.local_part, 'Fred Perfect') 1585 self.assertEqual(angle_addr.domain, '[127.0.0.1]') 1586 self.assertIsNone(angle_addr.route) 1587 self.assertEqual(angle_addr.addr_spec, '"Fred Perfect"@[127.0.0.1]') 1588 1589 def test_get_angle_addr_internal_cfws(self): 1590 angle_addr = self._test_get_x(parser.get_angle_addr, 1591 '<(foo) dinsdale@example.com(bar)>', 1592 '<(foo) dinsdale@example.com(bar)>', 1593 '< dinsdale@example.com >', 1594 [], 1595 '') 1596 self.assertEqual(angle_addr.local_part, 'dinsdale') 1597 self.assertEqual(angle_addr.domain, 'example.com') 1598 self.assertIsNone(angle_addr.route) 1599 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1600 1601 def test_get_angle_addr_obs_route(self): 1602 angle_addr = self._test_get_x(parser.get_angle_addr, 1603 '(foo)<@example.com, (bird) @two.example.com: dinsdale@example.com> (bar) ', 1604 '(foo)<@example.com, (bird) @two.example.com: dinsdale@example.com> (bar) ', 1605 ' <@example.com, @two.example.com: dinsdale@example.com> ', 1606 [errors.ObsoleteHeaderDefect], 1607 '') 1608 self.assertEqual(angle_addr.local_part, 'dinsdale') 1609 self.assertEqual(angle_addr.domain, 'example.com') 1610 self.assertEqual(angle_addr.route, ['example.com', 'two.example.com']) 1611 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1612 1613 def test_get_angle_addr_missing_closing_angle(self): 1614 angle_addr = self._test_get_x(parser.get_angle_addr, 1615 '<dinsdale@example.com', 1616 '<dinsdale@example.com>', 1617 '<dinsdale@example.com>', 1618 [errors.InvalidHeaderDefect], 1619 '') 1620 self.assertEqual(angle_addr.local_part, 'dinsdale') 1621 self.assertEqual(angle_addr.domain, 'example.com') 1622 self.assertIsNone(angle_addr.route) 1623 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1624 1625 def test_get_angle_addr_missing_closing_angle_with_cfws(self): 1626 angle_addr = self._test_get_x(parser.get_angle_addr, 1627 '<dinsdale@example.com (foo)', 1628 '<dinsdale@example.com (foo)>', 1629 '<dinsdale@example.com >', 1630 [errors.InvalidHeaderDefect], 1631 '') 1632 self.assertEqual(angle_addr.local_part, 'dinsdale') 1633 self.assertEqual(angle_addr.domain, 'example.com') 1634 self.assertIsNone(angle_addr.route) 1635 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1636 1637 def test_get_angle_addr_ends_at_special(self): 1638 angle_addr = self._test_get_x(parser.get_angle_addr, 1639 '<dinsdale@example.com> (foo), next', 1640 '<dinsdale@example.com> (foo)', 1641 '<dinsdale@example.com> ', 1642 [], 1643 ', next') 1644 self.assertEqual(angle_addr.local_part, 'dinsdale') 1645 self.assertEqual(angle_addr.domain, 'example.com') 1646 self.assertIsNone(angle_addr.route) 1647 self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com') 1648 1649 def test_get_angle_addr_no_angle_raise(self): 1650 with self.assertRaises(errors.HeaderParseError): 1651 parser.get_angle_addr('(foo) ') 1652 1653 def test_get_angle_addr_no_angle_before_special_raises(self): 1654 with self.assertRaises(errors.HeaderParseError): 1655 parser.get_angle_addr('(foo) , next') 1656 1657 def test_get_angle_addr_no_angle_raises(self): 1658 with self.assertRaises(errors.HeaderParseError): 1659 parser.get_angle_addr('bar') 1660 1661 def test_get_angle_addr_special_after_angle_raises(self): 1662 with self.assertRaises(errors.HeaderParseError): 1663 parser.get_angle_addr('(foo) <, bar') 1664 1665 # get_display_name This is phrase but with a different value. 1666 1667 def test_get_display_name_simple(self): 1668 display_name = self._test_get_x(parser.get_display_name, 1669 'Fred A Johnson', 1670 'Fred A Johnson', 1671 'Fred A Johnson', 1672 [], 1673 '') 1674 self.assertEqual(display_name.token_type, 'display-name') 1675 self.assertEqual(display_name.display_name, 'Fred A Johnson') 1676 1677 def test_get_display_name_complex1(self): 1678 display_name = self._test_get_x(parser.get_display_name, 1679 '"Fred A. Johnson" is his name, oh.', 1680 '"Fred A. Johnson" is his name', 1681 '"Fred A. Johnson is his name"', 1682 [], 1683 ', oh.') 1684 self.assertEqual(display_name.token_type, 'display-name') 1685 self.assertEqual(display_name.display_name, 'Fred A. Johnson is his name') 1686 1687 def test_get_display_name_complex2(self): 1688 display_name = self._test_get_x(parser.get_display_name, 1689 ' (A) bird (in (my|your)) "hand " is messy\t<>\t', 1690 ' (A) bird (in (my|your)) "hand " is messy\t', 1691 ' "bird hand is messy" ', 1692 [], 1693 '<>\t') 1694 self.assertEqual(display_name[0][0].comments, ['A']) 1695 self.assertEqual(display_name[0][2].comments, ['in (my|your)']) 1696 self.assertEqual(display_name.display_name, 'bird hand is messy') 1697 1698 def test_get_display_name_obsolete(self): 1699 display_name = self._test_get_x(parser.get_display_name, 1700 'Fred A.(weird).O Johnson', 1701 'Fred A.(weird).O Johnson', 1702 '"Fred A. .O Johnson"', 1703 [errors.ObsoleteHeaderDefect]*3, 1704 '') 1705 self.assertEqual(len(display_name), 7) 1706 self.assertEqual(display_name[3].comments, ['weird']) 1707 self.assertEqual(display_name.display_name, 'Fred A. .O Johnson') 1708 1709 def test_get_display_name_pharse_must_start_with_word(self): 1710 display_name = self._test_get_x(parser.get_display_name, 1711 '(even weirder).name', 1712 '(even weirder).name', 1713 ' ".name"', 1714 [errors.InvalidHeaderDefect] + [errors.ObsoleteHeaderDefect]*2, 1715 '') 1716 self.assertEqual(len(display_name), 3) 1717 self.assertEqual(display_name[0].comments, ['even weirder']) 1718 self.assertEqual(display_name.display_name, '.name') 1719 1720 def test_get_display_name_ending_with_obsolete(self): 1721 display_name = self._test_get_x(parser.get_display_name, 1722 'simple phrase.(with trailing comment):boo', 1723 'simple phrase.(with trailing comment)', 1724 '"simple phrase." ', 1725 [errors.ObsoleteHeaderDefect]*2, 1726 ':boo') 1727 self.assertEqual(len(display_name), 4) 1728 self.assertEqual(display_name[3].comments, ['with trailing comment']) 1729 self.assertEqual(display_name.display_name, 'simple phrase.') 1730 1731 def test_get_display_name_for_invalid_address_field(self): 1732 # bpo-32178: Test that address fields starting with `:` don't cause 1733 # IndexError when parsing the display name. 1734 display_name = self._test_get_x( 1735 parser.get_display_name, 1736 ':Foo ', '', '', [errors.InvalidHeaderDefect], ':Foo ') 1737 self.assertEqual(display_name.value, '') 1738 1739 # get_name_addr 1740 1741 def test_get_name_addr_angle_addr_only(self): 1742 name_addr = self._test_get_x(parser.get_name_addr, 1743 '<dinsdale@example.com>', 1744 '<dinsdale@example.com>', 1745 '<dinsdale@example.com>', 1746 [], 1747 '') 1748 self.assertEqual(name_addr.token_type, 'name-addr') 1749 self.assertIsNone(name_addr.display_name) 1750 self.assertEqual(name_addr.local_part, 'dinsdale') 1751 self.assertEqual(name_addr.domain, 'example.com') 1752 self.assertIsNone(name_addr.route) 1753 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1754 1755 def test_get_name_addr_atom_name(self): 1756 name_addr = self._test_get_x(parser.get_name_addr, 1757 'Dinsdale <dinsdale@example.com>', 1758 'Dinsdale <dinsdale@example.com>', 1759 'Dinsdale <dinsdale@example.com>', 1760 [], 1761 '') 1762 self.assertEqual(name_addr.token_type, 'name-addr') 1763 self.assertEqual(name_addr.display_name, 'Dinsdale') 1764 self.assertEqual(name_addr.local_part, 'dinsdale') 1765 self.assertEqual(name_addr.domain, 'example.com') 1766 self.assertIsNone(name_addr.route) 1767 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1768 1769 def test_get_name_addr_atom_name_with_cfws(self): 1770 name_addr = self._test_get_x(parser.get_name_addr, 1771 '(foo) Dinsdale (bar) <dinsdale@example.com> (bird)', 1772 '(foo) Dinsdale (bar) <dinsdale@example.com> (bird)', 1773 ' Dinsdale <dinsdale@example.com> ', 1774 [], 1775 '') 1776 self.assertEqual(name_addr.display_name, 'Dinsdale') 1777 self.assertEqual(name_addr.local_part, 'dinsdale') 1778 self.assertEqual(name_addr.domain, 'example.com') 1779 self.assertIsNone(name_addr.route) 1780 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1781 1782 def test_get_name_addr_name_with_cfws_and_dots(self): 1783 name_addr = self._test_get_x(parser.get_name_addr, 1784 '(foo) Roy.A.Bear (bar) <dinsdale@example.com> (bird)', 1785 '(foo) Roy.A.Bear (bar) <dinsdale@example.com> (bird)', 1786 ' "Roy.A.Bear" <dinsdale@example.com> ', 1787 [errors.ObsoleteHeaderDefect]*2, 1788 '') 1789 self.assertEqual(name_addr.display_name, 'Roy.A.Bear') 1790 self.assertEqual(name_addr.local_part, 'dinsdale') 1791 self.assertEqual(name_addr.domain, 'example.com') 1792 self.assertIsNone(name_addr.route) 1793 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1794 1795 def test_get_name_addr_qs_name(self): 1796 name_addr = self._test_get_x(parser.get_name_addr, 1797 '"Roy.A.Bear" <dinsdale@example.com>', 1798 '"Roy.A.Bear" <dinsdale@example.com>', 1799 '"Roy.A.Bear" <dinsdale@example.com>', 1800 [], 1801 '') 1802 self.assertEqual(name_addr.display_name, 'Roy.A.Bear') 1803 self.assertEqual(name_addr.local_part, 'dinsdale') 1804 self.assertEqual(name_addr.domain, 'example.com') 1805 self.assertIsNone(name_addr.route) 1806 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1807 1808 def test_get_name_addr_with_route(self): 1809 name_addr = self._test_get_x(parser.get_name_addr, 1810 '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>', 1811 '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>', 1812 '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>', 1813 [errors.ObsoleteHeaderDefect], 1814 '') 1815 self.assertEqual(name_addr.display_name, 'Roy.A.Bear') 1816 self.assertEqual(name_addr.local_part, 'dinsdale') 1817 self.assertEqual(name_addr.domain, 'example.com') 1818 self.assertEqual(name_addr.route, ['two.example.com']) 1819 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1820 1821 def test_get_name_addr_ends_at_special(self): 1822 name_addr = self._test_get_x(parser.get_name_addr, 1823 '"Roy.A.Bear" <dinsdale@example.com>, next', 1824 '"Roy.A.Bear" <dinsdale@example.com>', 1825 '"Roy.A.Bear" <dinsdale@example.com>', 1826 [], 1827 ', next') 1828 self.assertEqual(name_addr.display_name, 'Roy.A.Bear') 1829 self.assertEqual(name_addr.local_part, 'dinsdale') 1830 self.assertEqual(name_addr.domain, 'example.com') 1831 self.assertIsNone(name_addr.route) 1832 self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com') 1833 1834 def test_get_name_addr_no_content_raises(self): 1835 with self.assertRaises(errors.HeaderParseError): 1836 parser.get_name_addr(' (foo) ') 1837 1838 def test_get_name_addr_no_content_before_special_raises(self): 1839 with self.assertRaises(errors.HeaderParseError): 1840 parser.get_name_addr(' (foo) ,') 1841 1842 def test_get_name_addr_no_angle_after_display_name_raises(self): 1843 with self.assertRaises(errors.HeaderParseError): 1844 parser.get_name_addr('foo bar') 1845 1846 # get_mailbox 1847 1848 def test_get_mailbox_addr_spec_only(self): 1849 mailbox = self._test_get_x(parser.get_mailbox, 1850 'dinsdale@example.com', 1851 'dinsdale@example.com', 1852 'dinsdale@example.com', 1853 [], 1854 '') 1855 self.assertEqual(mailbox.token_type, 'mailbox') 1856 self.assertIsNone(mailbox.display_name) 1857 self.assertEqual(mailbox.local_part, 'dinsdale') 1858 self.assertEqual(mailbox.domain, 'example.com') 1859 self.assertIsNone(mailbox.route) 1860 self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com') 1861 1862 def test_get_mailbox_angle_addr_only(self): 1863 mailbox = self._test_get_x(parser.get_mailbox, 1864 '<dinsdale@example.com>', 1865 '<dinsdale@example.com>', 1866 '<dinsdale@example.com>', 1867 [], 1868 '') 1869 self.assertEqual(mailbox.token_type, 'mailbox') 1870 self.assertIsNone(mailbox.display_name) 1871 self.assertEqual(mailbox.local_part, 'dinsdale') 1872 self.assertEqual(mailbox.domain, 'example.com') 1873 self.assertIsNone(mailbox.route) 1874 self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com') 1875 1876 def test_get_mailbox_name_addr(self): 1877 mailbox = self._test_get_x(parser.get_mailbox, 1878 '"Roy A. Bear" <dinsdale@example.com>', 1879 '"Roy A. Bear" <dinsdale@example.com>', 1880 '"Roy A. Bear" <dinsdale@example.com>', 1881 [], 1882 '') 1883 self.assertEqual(mailbox.token_type, 'mailbox') 1884 self.assertEqual(mailbox.display_name, 'Roy A. Bear') 1885 self.assertEqual(mailbox.local_part, 'dinsdale') 1886 self.assertEqual(mailbox.domain, 'example.com') 1887 self.assertIsNone(mailbox.route) 1888 self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com') 1889 1890 def test_get_mailbox_ends_at_special(self): 1891 mailbox = self._test_get_x(parser.get_mailbox, 1892 '"Roy A. Bear" <dinsdale@example.com>, rest', 1893 '"Roy A. Bear" <dinsdale@example.com>', 1894 '"Roy A. Bear" <dinsdale@example.com>', 1895 [], 1896 ', rest') 1897 self.assertEqual(mailbox.token_type, 'mailbox') 1898 self.assertEqual(mailbox.display_name, 'Roy A. Bear') 1899 self.assertEqual(mailbox.local_part, 'dinsdale') 1900 self.assertEqual(mailbox.domain, 'example.com') 1901 self.assertIsNone(mailbox.route) 1902 self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com') 1903 1904 def test_get_mailbox_quoted_strings_in_atom_list(self): 1905 mailbox = self._test_get_x(parser.get_mailbox, 1906 '""example" example"@example.com', 1907 '""example" example"@example.com', 1908 'example example@example.com', 1909 [errors.InvalidHeaderDefect]*3, 1910 '') 1911 self.assertEqual(mailbox.local_part, 'example example') 1912 self.assertEqual(mailbox.domain, 'example.com') 1913 self.assertEqual(mailbox.addr_spec, '"example example"@example.com') 1914 1915 # get_mailbox_list 1916 1917 def test_get_mailbox_list_single_addr(self): 1918 mailbox_list = self._test_get_x(parser.get_mailbox_list, 1919 'dinsdale@example.com', 1920 'dinsdale@example.com', 1921 'dinsdale@example.com', 1922 [], 1923 '') 1924 self.assertEqual(mailbox_list.token_type, 'mailbox-list') 1925 self.assertEqual(len(mailbox_list.mailboxes), 1) 1926 mailbox = mailbox_list.mailboxes[0] 1927 self.assertIsNone(mailbox.display_name) 1928 self.assertEqual(mailbox.local_part, 'dinsdale') 1929 self.assertEqual(mailbox.domain, 'example.com') 1930 self.assertIsNone(mailbox.route) 1931 self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com') 1932 self.assertEqual(mailbox_list.mailboxes, 1933 mailbox_list.all_mailboxes) 1934 1935 def test_get_mailbox_list_two_simple_addr(self): 1936 mailbox_list = self._test_get_x(parser.get_mailbox_list, 1937 'dinsdale@example.com, dinsdale@test.example.com', 1938 'dinsdale@example.com, dinsdale@test.example.com', 1939 'dinsdale@example.com, dinsdale@test.example.com', 1940 [], 1941 '') 1942 self.assertEqual(mailbox_list.token_type, 'mailbox-list') 1943 self.assertEqual(len(mailbox_list.mailboxes), 2) 1944 self.assertEqual(mailbox_list.mailboxes[0].addr_spec, 1945 'dinsdale@example.com') 1946 self.assertEqual(mailbox_list.mailboxes[1].addr_spec, 1947 'dinsdale@test.example.com') 1948 self.assertEqual(mailbox_list.mailboxes, 1949 mailbox_list.all_mailboxes) 1950 1951 def test_get_mailbox_list_two_name_addr(self): 1952 mailbox_list = self._test_get_x(parser.get_mailbox_list, 1953 ('"Roy A. Bear" <dinsdale@example.com>,' 1954 ' "Fred Flintstone" <dinsdale@test.example.com>'), 1955 ('"Roy A. Bear" <dinsdale@example.com>,' 1956 ' "Fred Flintstone" <dinsdale@test.example.com>'), 1957 ('"Roy A. Bear" <dinsdale@example.com>,' 1958 ' "Fred Flintstone" <dinsdale@test.example.com>'), 1959 [], 1960 '') 1961 self.assertEqual(len(mailbox_list.mailboxes), 2) 1962 self.assertEqual(mailbox_list.mailboxes[0].addr_spec, 1963 'dinsdale@example.com') 1964 self.assertEqual(mailbox_list.mailboxes[0].display_name, 1965 'Roy A. Bear') 1966 self.assertEqual(mailbox_list.mailboxes[1].addr_spec, 1967 'dinsdale@test.example.com') 1968 self.assertEqual(mailbox_list.mailboxes[1].display_name, 1969 'Fred Flintstone') 1970 self.assertEqual(mailbox_list.mailboxes, 1971 mailbox_list.all_mailboxes) 1972 1973 def test_get_mailbox_list_two_complex(self): 1974 mailbox_list = self._test_get_x(parser.get_mailbox_list, 1975 ('(foo) "Roy A. Bear" <dinsdale@example.com>(bar),' 1976 ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'), 1977 ('(foo) "Roy A. Bear" <dinsdale@example.com>(bar),' 1978 ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'), 1979 (' "Roy A. Bear" <dinsdale@example.com> ,' 1980 ' "Fred Flintstone" <dinsdale@test. example.com>'), 1981 [errors.ObsoleteHeaderDefect], 1982 '') 1983 self.assertEqual(len(mailbox_list.mailboxes), 2) 1984 self.assertEqual(mailbox_list.mailboxes[0].addr_spec, 1985 'dinsdale@example.com') 1986 self.assertEqual(mailbox_list.mailboxes[0].display_name, 1987 'Roy A. Bear') 1988 self.assertEqual(mailbox_list.mailboxes[1].addr_spec, 1989 'dinsdale@test.example.com') 1990 self.assertEqual(mailbox_list.mailboxes[1].display_name, 1991 'Fred Flintstone') 1992 self.assertEqual(mailbox_list.mailboxes, 1993 mailbox_list.all_mailboxes) 1994 1995 def test_get_mailbox_list_unparseable_mailbox_null(self): 1996 mailbox_list = self._test_get_x(parser.get_mailbox_list, 1997 ('"Roy A. Bear"[] dinsdale@example.com,' 1998 ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'), 1999 ('"Roy A. Bear"[] dinsdale@example.com,' 2000 ' "Fred Flintstone" <dinsdale@test.(bird)example.com>'), 2001 ('"Roy A. Bear"[] dinsdale@example.com,' 2002 ' "Fred Flintstone" <dinsdale@test. example.com>'), 2003 [errors.InvalidHeaderDefect, # the 'extra' text after the local part 2004 errors.InvalidHeaderDefect, # the local part with no angle-addr 2005 errors.ObsoleteHeaderDefect, # period in extra text (example.com) 2006 errors.ObsoleteHeaderDefect], # (bird) in valid address. 2007 '') 2008 self.assertEqual(len(mailbox_list.mailboxes), 1) 2009 self.assertEqual(len(mailbox_list.all_mailboxes), 2) 2010 self.assertEqual(mailbox_list.all_mailboxes[0].token_type, 2011 'invalid-mailbox') 2012 self.assertIsNone(mailbox_list.all_mailboxes[0].display_name) 2013 self.assertEqual(mailbox_list.all_mailboxes[0].local_part, 2014 'Roy A. Bear') 2015 self.assertIsNone(mailbox_list.all_mailboxes[0].domain) 2016 self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec, 2017 '"Roy A. Bear"') 2018 self.assertIs(mailbox_list.all_mailboxes[1], 2019 mailbox_list.mailboxes[0]) 2020 self.assertEqual(mailbox_list.mailboxes[0].addr_spec, 2021 'dinsdale@test.example.com') 2022 self.assertEqual(mailbox_list.mailboxes[0].display_name, 2023 'Fred Flintstone') 2024 2025 def test_get_mailbox_list_junk_after_valid_address(self): 2026 mailbox_list = self._test_get_x(parser.get_mailbox_list, 2027 ('"Roy A. Bear" <dinsdale@example.com>@@,' 2028 ' "Fred Flintstone" <dinsdale@test.example.com>'), 2029 ('"Roy A. Bear" <dinsdale@example.com>@@,' 2030 ' "Fred Flintstone" <dinsdale@test.example.com>'), 2031 ('"Roy A. Bear" <dinsdale@example.com>@@,' 2032 ' "Fred Flintstone" <dinsdale@test.example.com>'), 2033 [errors.InvalidHeaderDefect], 2034 '') 2035 self.assertEqual(len(mailbox_list.mailboxes), 1) 2036 self.assertEqual(len(mailbox_list.all_mailboxes), 2) 2037 self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec, 2038 'dinsdale@example.com') 2039 self.assertEqual(mailbox_list.all_mailboxes[0].display_name, 2040 'Roy A. Bear') 2041 self.assertEqual(mailbox_list.all_mailboxes[0].token_type, 2042 'invalid-mailbox') 2043 self.assertIs(mailbox_list.all_mailboxes[1], 2044 mailbox_list.mailboxes[0]) 2045 self.assertEqual(mailbox_list.mailboxes[0].addr_spec, 2046 'dinsdale@test.example.com') 2047 self.assertEqual(mailbox_list.mailboxes[0].display_name, 2048 'Fred Flintstone') 2049 2050 def test_get_mailbox_list_empty_list_element(self): 2051 mailbox_list = self._test_get_x(parser.get_mailbox_list, 2052 ('"Roy A. Bear" <dinsdale@example.com>, (bird),,' 2053 ' "Fred Flintstone" <dinsdale@test.example.com>'), 2054 ('"Roy A. Bear" <dinsdale@example.com>, (bird),,' 2055 ' "Fred Flintstone" <dinsdale@test.example.com>'), 2056 ('"Roy A. Bear" <dinsdale@example.com>, ,,' 2057 ' "Fred Flintstone" <dinsdale@test.example.com>'), 2058 [errors.ObsoleteHeaderDefect]*2, 2059 '') 2060 self.assertEqual(len(mailbox_list.mailboxes), 2) 2061 self.assertEqual(mailbox_list.all_mailboxes, 2062 mailbox_list.mailboxes) 2063 self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec, 2064 'dinsdale@example.com') 2065 self.assertEqual(mailbox_list.all_mailboxes[0].display_name, 2066 'Roy A. Bear') 2067 self.assertEqual(mailbox_list.mailboxes[1].addr_spec, 2068 'dinsdale@test.example.com') 2069 self.assertEqual(mailbox_list.mailboxes[1].display_name, 2070 'Fred Flintstone') 2071 2072 def test_get_mailbox_list_only_empty_elements(self): 2073 mailbox_list = self._test_get_x(parser.get_mailbox_list, 2074 '(foo),, (bar)', 2075 '(foo),, (bar)', 2076 ' ,, ', 2077 [errors.ObsoleteHeaderDefect]*3, 2078 '') 2079 self.assertEqual(len(mailbox_list.mailboxes), 0) 2080 self.assertEqual(mailbox_list.all_mailboxes, 2081 mailbox_list.mailboxes) 2082 2083 # get_group_list 2084 2085 def test_get_group_list_cfws_only(self): 2086 group_list = self._test_get_x(parser.get_group_list, 2087 '(hidden);', 2088 '(hidden)', 2089 ' ', 2090 [], 2091 ';') 2092 self.assertEqual(group_list.token_type, 'group-list') 2093 self.assertEqual(len(group_list.mailboxes), 0) 2094 self.assertEqual(group_list.mailboxes, 2095 group_list.all_mailboxes) 2096 2097 def test_get_group_list_mailbox_list(self): 2098 group_list = self._test_get_x(parser.get_group_list, 2099 'dinsdale@example.org, "Fred A. Bear" <dinsdale@example.org>', 2100 'dinsdale@example.org, "Fred A. Bear" <dinsdale@example.org>', 2101 'dinsdale@example.org, "Fred A. Bear" <dinsdale@example.org>', 2102 [], 2103 '') 2104 self.assertEqual(group_list.token_type, 'group-list') 2105 self.assertEqual(len(group_list.mailboxes), 2) 2106 self.assertEqual(group_list.mailboxes, 2107 group_list.all_mailboxes) 2108 self.assertEqual(group_list.mailboxes[1].display_name, 2109 'Fred A. Bear') 2110 2111 def test_get_group_list_obs_group_list(self): 2112 group_list = self._test_get_x(parser.get_group_list, 2113 ', (foo),,(bar)', 2114 ', (foo),,(bar)', 2115 ', ,, ', 2116 [errors.ObsoleteHeaderDefect], 2117 '') 2118 self.assertEqual(group_list.token_type, 'group-list') 2119 self.assertEqual(len(group_list.mailboxes), 0) 2120 self.assertEqual(group_list.mailboxes, 2121 group_list.all_mailboxes) 2122 2123 def test_get_group_list_comment_only_invalid(self): 2124 group_list = self._test_get_x(parser.get_group_list, 2125 '(bar)', 2126 '(bar)', 2127 ' ', 2128 [errors.InvalidHeaderDefect], 2129 '') 2130 self.assertEqual(group_list.token_type, 'group-list') 2131 self.assertEqual(len(group_list.mailboxes), 0) 2132 self.assertEqual(group_list.mailboxes, 2133 group_list.all_mailboxes) 2134 2135 # get_group 2136 2137 def test_get_group_empty(self): 2138 group = self._test_get_x(parser.get_group, 2139 'Monty Python:;', 2140 'Monty Python:;', 2141 'Monty Python:;', 2142 [], 2143 '') 2144 self.assertEqual(group.token_type, 'group') 2145 self.assertEqual(group.display_name, 'Monty Python') 2146 self.assertEqual(len(group.mailboxes), 0) 2147 self.assertEqual(group.mailboxes, 2148 group.all_mailboxes) 2149 2150 def test_get_group_null_addr_spec(self): 2151 group = self._test_get_x(parser.get_group, 2152 'foo: <>;', 2153 'foo: <>;', 2154 'foo: <>;', 2155 [errors.InvalidHeaderDefect], 2156 '') 2157 self.assertEqual(group.display_name, 'foo') 2158 self.assertEqual(len(group.mailboxes), 0) 2159 self.assertEqual(len(group.all_mailboxes), 1) 2160 self.assertEqual(group.all_mailboxes[0].value, '<>') 2161 2162 def test_get_group_cfws_only(self): 2163 group = self._test_get_x(parser.get_group, 2164 'Monty Python: (hidden);', 2165 'Monty Python: (hidden);', 2166 'Monty Python: ;', 2167 [], 2168 '') 2169 self.assertEqual(group.token_type, 'group') 2170 self.assertEqual(group.display_name, 'Monty Python') 2171 self.assertEqual(len(group.mailboxes), 0) 2172 self.assertEqual(group.mailboxes, 2173 group.all_mailboxes) 2174 2175 def test_get_group_single_mailbox(self): 2176 group = self._test_get_x(parser.get_group, 2177 'Monty Python: "Fred A. Bear" <dinsdale@example.com>;', 2178 'Monty Python: "Fred A. Bear" <dinsdale@example.com>;', 2179 'Monty Python: "Fred A. Bear" <dinsdale@example.com>;', 2180 [], 2181 '') 2182 self.assertEqual(group.token_type, 'group') 2183 self.assertEqual(group.display_name, 'Monty Python') 2184 self.assertEqual(len(group.mailboxes), 1) 2185 self.assertEqual(group.mailboxes, 2186 group.all_mailboxes) 2187 self.assertEqual(group.mailboxes[0].addr_spec, 2188 'dinsdale@example.com') 2189 2190 def test_get_group_mixed_list(self): 2191 group = self._test_get_x(parser.get_group, 2192 ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,' 2193 '(foo) Roger <ping@exampele.com>, x@test.example.com;'), 2194 ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,' 2195 '(foo) Roger <ping@exampele.com>, x@test.example.com;'), 2196 ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,' 2197 ' Roger <ping@exampele.com>, x@test.example.com;'), 2198 [], 2199 '') 2200 self.assertEqual(group.token_type, 'group') 2201 self.assertEqual(group.display_name, 'Monty Python') 2202 self.assertEqual(len(group.mailboxes), 3) 2203 self.assertEqual(group.mailboxes, 2204 group.all_mailboxes) 2205 self.assertEqual(group.mailboxes[0].display_name, 2206 'Fred A. Bear') 2207 self.assertEqual(group.mailboxes[1].display_name, 2208 'Roger') 2209 self.assertEqual(group.mailboxes[2].local_part, 'x') 2210 2211 def test_get_group_one_invalid(self): 2212 group = self._test_get_x(parser.get_group, 2213 ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,' 2214 '(foo) Roger ping@exampele.com, x@test.example.com;'), 2215 ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,' 2216 '(foo) Roger ping@exampele.com, x@test.example.com;'), 2217 ('Monty Python: "Fred A. Bear" <dinsdale@example.com>,' 2218 ' Roger ping@exampele.com, x@test.example.com;'), 2219 [errors.InvalidHeaderDefect, # non-angle addr makes local part invalid 2220 errors.InvalidHeaderDefect], # and its not obs-local either: no dots. 2221 '') 2222 self.assertEqual(group.token_type, 'group') 2223 self.assertEqual(group.display_name, 'Monty Python') 2224 self.assertEqual(len(group.mailboxes), 2) 2225 self.assertEqual(len(group.all_mailboxes), 3) 2226 self.assertEqual(group.mailboxes[0].display_name, 2227 'Fred A. Bear') 2228 self.assertEqual(group.mailboxes[1].local_part, 'x') 2229 self.assertIsNone(group.all_mailboxes[1].display_name) 2230 2231 def test_get_group_missing_final_semicol(self): 2232 group = self._test_get_x(parser.get_group, 2233 ('Monty Python:"Fred A. Bear" <dinsdale@example.com>,' 2234 'eric@where.test,John <jdoe@test>'), 2235 ('Monty Python:"Fred A. Bear" <dinsdale@example.com>,' 2236 'eric@where.test,John <jdoe@test>;'), 2237 ('Monty Python:"Fred A. Bear" <dinsdale@example.com>,' 2238 'eric@where.test,John <jdoe@test>;'), 2239 [errors.InvalidHeaderDefect], 2240 '') 2241 self.assertEqual(group.token_type, 'group') 2242 self.assertEqual(group.display_name, 'Monty Python') 2243 self.assertEqual(len(group.mailboxes), 3) 2244 self.assertEqual(group.mailboxes, 2245 group.all_mailboxes) 2246 self.assertEqual(group.mailboxes[0].addr_spec, 2247 'dinsdale@example.com') 2248 self.assertEqual(group.mailboxes[0].display_name, 2249 'Fred A. Bear') 2250 self.assertEqual(group.mailboxes[1].addr_spec, 2251 'eric@where.test') 2252 self.assertEqual(group.mailboxes[2].display_name, 2253 'John') 2254 self.assertEqual(group.mailboxes[2].addr_spec, 2255 'jdoe@test') 2256 # get_address 2257 2258 def test_get_address_simple(self): 2259 address = self._test_get_x(parser.get_address, 2260 'dinsdale@example.com', 2261 'dinsdale@example.com', 2262 'dinsdale@example.com', 2263 [], 2264 '') 2265 self.assertEqual(address.token_type, 'address') 2266 self.assertEqual(len(address.mailboxes), 1) 2267 self.assertEqual(address.mailboxes, 2268 address.all_mailboxes) 2269 self.assertEqual(address.mailboxes[0].domain, 2270 'example.com') 2271 self.assertEqual(address[0].token_type, 2272 'mailbox') 2273 2274 def test_get_address_complex(self): 2275 address = self._test_get_x(parser.get_address, 2276 '(foo) "Fred A. Bear" <(bird)dinsdale@example.com>', 2277 '(foo) "Fred A. Bear" <(bird)dinsdale@example.com>', 2278 ' "Fred A. Bear" < dinsdale@example.com>', 2279 [], 2280 '') 2281 self.assertEqual(address.token_type, 'address') 2282 self.assertEqual(len(address.mailboxes), 1) 2283 self.assertEqual(address.mailboxes, 2284 address.all_mailboxes) 2285 self.assertEqual(address.mailboxes[0].display_name, 2286 'Fred A. Bear') 2287 self.assertEqual(address[0].token_type, 2288 'mailbox') 2289 2290 def test_get_address_rfc2047_display_name(self): 2291 address = self._test_get_x(parser.get_address, 2292 '=?utf-8?q?=C3=89ric?= <foo@example.com>', 2293 'Éric <foo@example.com>', 2294 'Éric <foo@example.com>', 2295 [], 2296 '') 2297 self.assertEqual(address.token_type, 'address') 2298 self.assertEqual(len(address.mailboxes), 1) 2299 self.assertEqual(address.mailboxes, 2300 address.all_mailboxes) 2301 self.assertEqual(address.mailboxes[0].display_name, 2302 'Éric') 2303 self.assertEqual(address[0].token_type, 2304 'mailbox') 2305 2306 def test_get_address_empty_group(self): 2307 address = self._test_get_x(parser.get_address, 2308 'Monty Python:;', 2309 'Monty Python:;', 2310 'Monty Python:;', 2311 [], 2312 '') 2313 self.assertEqual(address.token_type, 'address') 2314 self.assertEqual(len(address.mailboxes), 0) 2315 self.assertEqual(address.mailboxes, 2316 address.all_mailboxes) 2317 self.assertEqual(address[0].token_type, 2318 'group') 2319 self.assertEqual(address[0].display_name, 2320 'Monty Python') 2321 2322 def test_get_address_group(self): 2323 address = self._test_get_x(parser.get_address, 2324 'Monty Python: x@example.com, y@example.com;', 2325 'Monty Python: x@example.com, y@example.com;', 2326 'Monty Python: x@example.com, y@example.com;', 2327 [], 2328 '') 2329 self.assertEqual(address.token_type, 'address') 2330 self.assertEqual(len(address.mailboxes), 2) 2331 self.assertEqual(address.mailboxes, 2332 address.all_mailboxes) 2333 self.assertEqual(address[0].token_type, 2334 'group') 2335 self.assertEqual(address[0].display_name, 2336 'Monty Python') 2337 self.assertEqual(address.mailboxes[0].local_part, 'x') 2338 2339 def test_get_address_quoted_local_part(self): 2340 address = self._test_get_x(parser.get_address, 2341 '"foo bar"@example.com', 2342 '"foo bar"@example.com', 2343 '"foo bar"@example.com', 2344 [], 2345 '') 2346 self.assertEqual(address.token_type, 'address') 2347 self.assertEqual(len(address.mailboxes), 1) 2348 self.assertEqual(address.mailboxes, 2349 address.all_mailboxes) 2350 self.assertEqual(address.mailboxes[0].domain, 2351 'example.com') 2352 self.assertEqual(address.mailboxes[0].local_part, 2353 'foo bar') 2354 self.assertEqual(address[0].token_type, 'mailbox') 2355 2356 def test_get_address_ends_at_special(self): 2357 address = self._test_get_x(parser.get_address, 2358 'dinsdale@example.com, next', 2359 'dinsdale@example.com', 2360 'dinsdale@example.com', 2361 [], 2362 ', next') 2363 self.assertEqual(address.token_type, 'address') 2364 self.assertEqual(len(address.mailboxes), 1) 2365 self.assertEqual(address.mailboxes, 2366 address.all_mailboxes) 2367 self.assertEqual(address.mailboxes[0].domain, 2368 'example.com') 2369 self.assertEqual(address[0].token_type, 'mailbox') 2370 2371 def test_get_address_invalid_mailbox_invalid(self): 2372 address = self._test_get_x(parser.get_address, 2373 'ping example.com, next', 2374 'ping example.com', 2375 'ping example.com', 2376 [errors.InvalidHeaderDefect, # addr-spec with no domain 2377 errors.InvalidHeaderDefect, # invalid local-part 2378 errors.InvalidHeaderDefect, # missing .s in local-part 2379 ], 2380 ', next') 2381 self.assertEqual(address.token_type, 'address') 2382 self.assertEqual(len(address.mailboxes), 0) 2383 self.assertEqual(len(address.all_mailboxes), 1) 2384 self.assertIsNone(address.all_mailboxes[0].domain) 2385 self.assertEqual(address.all_mailboxes[0].local_part, 'ping example.com') 2386 self.assertEqual(address[0].token_type, 'invalid-mailbox') 2387 2388 def test_get_address_quoted_strings_in_atom_list(self): 2389 address = self._test_get_x(parser.get_address, 2390 '""example" example"@example.com', 2391 '""example" example"@example.com', 2392 'example example@example.com', 2393 [errors.InvalidHeaderDefect]*3, 2394 '') 2395 self.assertEqual(address.all_mailboxes[0].local_part, 'example example') 2396 self.assertEqual(address.all_mailboxes[0].domain, 'example.com') 2397 self.assertEqual(address.all_mailboxes[0].addr_spec, '"example example"@example.com') 2398 2399 2400 # get_address_list 2401 2402 def test_get_address_list_CFWS(self): 2403 address_list = self._test_get_x(parser.get_address_list, 2404 '(Recipient list suppressed)', 2405 '(Recipient list suppressed)', 2406 ' ', 2407 [errors.ObsoleteHeaderDefect], # no content in address list 2408 '') 2409 self.assertEqual(address_list.token_type, 'address-list') 2410 self.assertEqual(len(address_list.mailboxes), 0) 2411 self.assertEqual(address_list.mailboxes, address_list.all_mailboxes) 2412 2413 def test_get_address_list_mailboxes_simple(self): 2414 address_list = self._test_get_x(parser.get_address_list, 2415 'dinsdale@example.com', 2416 'dinsdale@example.com', 2417 'dinsdale@example.com', 2418 [], 2419 '') 2420 self.assertEqual(address_list.token_type, 'address-list') 2421 self.assertEqual(len(address_list.mailboxes), 1) 2422 self.assertEqual(address_list.mailboxes, 2423 address_list.all_mailboxes) 2424 self.assertEqual([str(x) for x in address_list.mailboxes], 2425 [str(x) for x in address_list.addresses]) 2426 self.assertEqual(address_list.mailboxes[0].domain, 'example.com') 2427 self.assertEqual(address_list[0].token_type, 'address') 2428 self.assertIsNone(address_list[0].display_name) 2429 2430 def test_get_address_list_mailboxes_two_simple(self): 2431 address_list = self._test_get_x(parser.get_address_list, 2432 'foo@example.com, "Fred A. Bar" <bar@example.com>', 2433 'foo@example.com, "Fred A. Bar" <bar@example.com>', 2434 'foo@example.com, "Fred A. Bar" <bar@example.com>', 2435 [], 2436 '') 2437 self.assertEqual(address_list.token_type, 'address-list') 2438 self.assertEqual(len(address_list.mailboxes), 2) 2439 self.assertEqual(address_list.mailboxes, 2440 address_list.all_mailboxes) 2441 self.assertEqual([str(x) for x in address_list.mailboxes], 2442 [str(x) for x in address_list.addresses]) 2443 self.assertEqual(address_list.mailboxes[0].local_part, 'foo') 2444 self.assertEqual(address_list.mailboxes[1].display_name, "Fred A. Bar") 2445 2446 def test_get_address_list_mailboxes_complex(self): 2447 address_list = self._test_get_x(parser.get_address_list, 2448 ('"Roy A. Bear" <dinsdale@example.com>, ' 2449 '(ping) Foo <x@example.com>,' 2450 'Nobody Is. Special <y@(bird)example.(bad)com>'), 2451 ('"Roy A. Bear" <dinsdale@example.com>, ' 2452 '(ping) Foo <x@example.com>,' 2453 'Nobody Is. Special <y@(bird)example.(bad)com>'), 2454 ('"Roy A. Bear" <dinsdale@example.com>, ' 2455 'Foo <x@example.com>,' 2456 '"Nobody Is. Special" <y@example. com>'), 2457 [errors.ObsoleteHeaderDefect, # period in Is. 2458 errors.ObsoleteHeaderDefect], # cfws in domain 2459 '') 2460 self.assertEqual(address_list.token_type, 'address-list') 2461 self.assertEqual(len(address_list.mailboxes), 3) 2462 self.assertEqual(address_list.mailboxes, 2463 address_list.all_mailboxes) 2464 self.assertEqual([str(x) for x in address_list.mailboxes], 2465 [str(x) for x in address_list.addresses]) 2466 self.assertEqual(address_list.mailboxes[0].domain, 'example.com') 2467 self.assertEqual(address_list.mailboxes[0].token_type, 'mailbox') 2468 self.assertEqual(address_list.addresses[0].token_type, 'address') 2469 self.assertEqual(address_list.mailboxes[1].local_part, 'x') 2470 self.assertEqual(address_list.mailboxes[2].display_name, 2471 'Nobody Is. Special') 2472 2473 def test_get_address_list_mailboxes_invalid_addresses(self): 2474 address_list = self._test_get_x(parser.get_address_list, 2475 ('"Roy A. Bear" <dinsdale@example.com>, ' 2476 '(ping) Foo x@example.com[],' 2477 'Nobody Is. Special <(bird)example.(bad)com>'), 2478 ('"Roy A. Bear" <dinsdale@example.com>, ' 2479 '(ping) Foo x@example.com[],' 2480 'Nobody Is. Special <(bird)example.(bad)com>'), 2481 ('"Roy A. Bear" <dinsdale@example.com>, ' 2482 'Foo x@example.com[],' 2483 '"Nobody Is. Special" < example. com>'), 2484 [errors.InvalidHeaderDefect, # invalid address in list 2485 errors.InvalidHeaderDefect, # 'Foo x' local part invalid. 2486 errors.InvalidHeaderDefect, # Missing . in 'Foo x' local part 2487 errors.ObsoleteHeaderDefect, # period in 'Is.' disp-name phrase 2488 errors.InvalidHeaderDefect, # no domain part in addr-spec 2489 errors.ObsoleteHeaderDefect], # addr-spec has comment in it 2490 '') 2491 self.assertEqual(address_list.token_type, 'address-list') 2492 self.assertEqual(len(address_list.mailboxes), 1) 2493 self.assertEqual(len(address_list.all_mailboxes), 3) 2494 self.assertEqual([str(x) for x in address_list.all_mailboxes], 2495 [str(x) for x in address_list.addresses]) 2496 self.assertEqual(address_list.mailboxes[0].domain, 'example.com') 2497 self.assertEqual(address_list.mailboxes[0].token_type, 'mailbox') 2498 self.assertEqual(address_list.addresses[0].token_type, 'address') 2499 self.assertEqual(address_list.addresses[1].token_type, 'address') 2500 self.assertEqual(len(address_list.addresses[0].mailboxes), 1) 2501 self.assertEqual(len(address_list.addresses[1].mailboxes), 0) 2502 self.assertEqual(len(address_list.addresses[1].mailboxes), 0) 2503 self.assertEqual( 2504 address_list.addresses[1].all_mailboxes[0].local_part, 'Foo x') 2505 self.assertEqual( 2506 address_list.addresses[2].all_mailboxes[0].display_name, 2507 "Nobody Is. Special") 2508 2509 def test_get_address_list_group_empty(self): 2510 address_list = self._test_get_x(parser.get_address_list, 2511 'Monty Python: ;', 2512 'Monty Python: ;', 2513 'Monty Python: ;', 2514 [], 2515 '') 2516 self.assertEqual(address_list.token_type, 'address-list') 2517 self.assertEqual(len(address_list.mailboxes), 0) 2518 self.assertEqual(address_list.mailboxes, 2519 address_list.all_mailboxes) 2520 self.assertEqual(len(address_list.addresses), 1) 2521 self.assertEqual(address_list.addresses[0].token_type, 'address') 2522 self.assertEqual(address_list.addresses[0].display_name, 'Monty Python') 2523 self.assertEqual(len(address_list.addresses[0].mailboxes), 0) 2524 2525 def test_get_address_list_group_simple(self): 2526 address_list = self._test_get_x(parser.get_address_list, 2527 'Monty Python: dinsdale@example.com;', 2528 'Monty Python: dinsdale@example.com;', 2529 'Monty Python: dinsdale@example.com;', 2530 [], 2531 '') 2532 self.assertEqual(address_list.token_type, 'address-list') 2533 self.assertEqual(len(address_list.mailboxes), 1) 2534 self.assertEqual(address_list.mailboxes, 2535 address_list.all_mailboxes) 2536 self.assertEqual(address_list.mailboxes[0].domain, 'example.com') 2537 self.assertEqual(address_list.addresses[0].display_name, 2538 'Monty Python') 2539 self.assertEqual(address_list.addresses[0].mailboxes[0].domain, 2540 'example.com') 2541 2542 def test_get_address_list_group_and_mailboxes(self): 2543 address_list = self._test_get_x(parser.get_address_list, 2544 ('Monty Python: dinsdale@example.com, "Fred" <flint@example.com>;, ' 2545 'Abe <x@example.com>, Bee <y@example.com>'), 2546 ('Monty Python: dinsdale@example.com, "Fred" <flint@example.com>;, ' 2547 'Abe <x@example.com>, Bee <y@example.com>'), 2548 ('Monty Python: dinsdale@example.com, "Fred" <flint@example.com>;, ' 2549 'Abe <x@example.com>, Bee <y@example.com>'), 2550 [], 2551 '') 2552 self.assertEqual(address_list.token_type, 'address-list') 2553 self.assertEqual(len(address_list.mailboxes), 4) 2554 self.assertEqual(address_list.mailboxes, 2555 address_list.all_mailboxes) 2556 self.assertEqual(len(address_list.addresses), 3) 2557 self.assertEqual(address_list.mailboxes[0].local_part, 'dinsdale') 2558 self.assertEqual(address_list.addresses[0].display_name, 2559 'Monty Python') 2560 self.assertEqual(address_list.addresses[0].mailboxes[0].domain, 2561 'example.com') 2562 self.assertEqual(address_list.addresses[0].mailboxes[1].local_part, 2563 'flint') 2564 self.assertEqual(address_list.addresses[1].mailboxes[0].local_part, 2565 'x') 2566 self.assertEqual(address_list.addresses[2].mailboxes[0].local_part, 2567 'y') 2568 self.assertEqual(str(address_list.addresses[1]), 2569 str(address_list.mailboxes[2])) 2570 2571 def test_invalid_content_disposition(self): 2572 content_disp = self._test_parse_x( 2573 parser.parse_content_disposition_header, 2574 ";attachment", "; attachment", ";attachment", 2575 [errors.InvalidHeaderDefect]*2 2576 ) 2577 2578 def test_invalid_content_transfer_encoding(self): 2579 cte = self._test_parse_x( 2580 parser.parse_content_transfer_encoding_header, 2581 ";foo", ";foo", ";foo", [errors.InvalidHeaderDefect]*3 2582 ) 2583 2584 # get_msg_id 2585 2586 def test_get_msg_id_empty(self): 2587 # bpo-38708: Test that HeaderParseError is raised and not IndexError. 2588 with self.assertRaises(errors.HeaderParseError): 2589 parser.get_msg_id('') 2590 2591 def test_get_msg_id_valid(self): 2592 msg_id = self._test_get_x( 2593 parser.get_msg_id, 2594 "<simeple.local@example.something.com>", 2595 "<simeple.local@example.something.com>", 2596 "<simeple.local@example.something.com>", 2597 [], 2598 '', 2599 ) 2600 self.assertEqual(msg_id.token_type, 'msg-id') 2601 2602 def test_get_msg_id_obsolete_local(self): 2603 msg_id = self._test_get_x( 2604 parser.get_msg_id, 2605 '<"simeple.local"@example.com>', 2606 '<"simeple.local"@example.com>', 2607 '<simeple.local@example.com>', 2608 [errors.ObsoleteHeaderDefect], 2609 '', 2610 ) 2611 self.assertEqual(msg_id.token_type, 'msg-id') 2612 2613 def test_get_msg_id_non_folding_literal_domain(self): 2614 msg_id = self._test_get_x( 2615 parser.get_msg_id, 2616 "<simple.local@[someexamplecom.domain]>", 2617 "<simple.local@[someexamplecom.domain]>", 2618 "<simple.local@[someexamplecom.domain]>", 2619 [], 2620 "", 2621 ) 2622 self.assertEqual(msg_id.token_type, 'msg-id') 2623 2624 2625 def test_get_msg_id_obsolete_domain_part(self): 2626 msg_id = self._test_get_x( 2627 parser.get_msg_id, 2628 "<simplelocal@(old)example.com>", 2629 "<simplelocal@(old)example.com>", 2630 "<simplelocal@ example.com>", 2631 [errors.ObsoleteHeaderDefect], 2632 "" 2633 ) 2634 2635 def test_get_msg_id_no_id_right_part(self): 2636 msg_id = self._test_get_x( 2637 parser.get_msg_id, 2638 "<simplelocal>", 2639 "<simplelocal>", 2640 "<simplelocal>", 2641 [errors.InvalidHeaderDefect], 2642 "" 2643 ) 2644 self.assertEqual(msg_id.token_type, 'msg-id') 2645 2646 def test_get_msg_id_invalid_expected_msg_id_not_found(self): 2647 text = "935-XPB-567:0:45327:9:90305:17843586-40@example.com" 2648 msg_id = parser.parse_message_id(text) 2649 self.assertDefectsEqual( 2650 msg_id.all_defects, 2651 [errors.InvalidHeaderDefect]) 2652 2653 def test_parse_invalid_message_id(self): 2654 message_id = self._test_parse_x( 2655 parser.parse_message_id, 2656 "935-XPB-567:0:45327:9:90305:17843586-40@example.com", 2657 "935-XPB-567:0:45327:9:90305:17843586-40@example.com", 2658 "935-XPB-567:0:45327:9:90305:17843586-40@example.com", 2659 [errors.InvalidHeaderDefect], 2660 ) 2661 self.assertEqual(message_id.token_type, 'invalid-message-id') 2662 2663 def test_parse_valid_message_id(self): 2664 message_id = self._test_parse_x( 2665 parser.parse_message_id, 2666 "<aperson@somedomain>", 2667 "<aperson@somedomain>", 2668 "<aperson@somedomain>", 2669 [], 2670 ) 2671 self.assertEqual(message_id.token_type, 'message-id') 2672 2673 def test_parse_message_id_with_remaining(self): 2674 message_id = self._test_parse_x( 2675 parser.parse_message_id, 2676 "<validmessageid@example>thensomething", 2677 "<validmessageid@example>", 2678 "<validmessageid@example>", 2679 [errors.InvalidHeaderDefect], 2680 [], 2681 ) 2682 self.assertEqual(message_id.token_type, 'message-id') 2683 self.assertEqual(str(message_id.all_defects[0]), 2684 "Unexpected 'thensomething'") 2685 2686 def test_get_msg_id_no_angle_start(self): 2687 with self.assertRaises(errors.HeaderParseError): 2688 parser.get_msg_id("msgwithnoankle") 2689 2690 def test_get_msg_id_no_angle_end(self): 2691 msg_id = self._test_get_x( 2692 parser.get_msg_id, 2693 "<simplelocal@domain", 2694 "<simplelocal@domain>", 2695 "<simplelocal@domain>", 2696 [errors.InvalidHeaderDefect], 2697 "" 2698 ) 2699 self.assertEqual(msg_id.token_type, 'msg-id') 2700 2701 2702 2703@parameterize 2704class Test_parse_mime_parameters(TestParserMixin, TestEmailBase): 2705 2706 def mime_parameters_as_value(self, 2707 value, 2708 tl_str, 2709 tl_value, 2710 params, 2711 defects): 2712 mime_parameters = self._test_parse_x(parser.parse_mime_parameters, 2713 value, tl_str, tl_value, defects) 2714 self.assertEqual(mime_parameters.token_type, 'mime-parameters') 2715 self.assertEqual(list(mime_parameters.params), params) 2716 2717 2718 mime_parameters_params = { 2719 2720 'simple': ( 2721 'filename="abc.py"', 2722 ' filename="abc.py"', 2723 'filename=abc.py', 2724 [('filename', 'abc.py')], 2725 []), 2726 2727 'multiple_keys': ( 2728 'filename="abc.py"; xyz=abc', 2729 ' filename="abc.py"; xyz="abc"', 2730 'filename=abc.py; xyz=abc', 2731 [('filename', 'abc.py'), ('xyz', 'abc')], 2732 []), 2733 2734 'split_value': ( 2735 "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", 2736 ' filename="201.tif"', 2737 "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", 2738 [('filename', '201.tif')], 2739 []), 2740 2741 # Note that it is undefined what we should do for error recovery when 2742 # there are duplicate parameter names or duplicate parts in a split 2743 # part. We choose to ignore all duplicate parameters after the first 2744 # and to take duplicate or missing rfc 2231 parts in appearance order. 2745 # This is backward compatible with get_param's behavior, but the 2746 # decisions are arbitrary. 2747 2748 'duplicate_key': ( 2749 'filename=abc.gif; filename=def.tiff', 2750 ' filename="abc.gif"', 2751 "filename=abc.gif; filename=def.tiff", 2752 [('filename', 'abc.gif')], 2753 [errors.InvalidHeaderDefect]), 2754 2755 'duplicate_key_with_split_value': ( 2756 "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" 2757 " filename=abc.gif", 2758 ' filename="201.tif"', 2759 "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" 2760 " filename=abc.gif", 2761 [('filename', '201.tif')], 2762 [errors.InvalidHeaderDefect]), 2763 2764 'duplicate_key_with_split_value_other_order': ( 2765 "filename=abc.gif; " 2766 " filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", 2767 ' filename="abc.gif"', 2768 "filename=abc.gif;" 2769 " filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", 2770 [('filename', 'abc.gif')], 2771 [errors.InvalidHeaderDefect]), 2772 2773 'duplicate_in_split_value': ( 2774 "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" 2775 " filename*1*=abc.gif", 2776 ' filename="201.tifabc.gif"', 2777 "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" 2778 " filename*1*=abc.gif", 2779 [('filename', '201.tifabc.gif')], 2780 [errors.InvalidHeaderDefect]), 2781 2782 'missing_split_value': ( 2783 "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;", 2784 ' filename="201.tif"', 2785 "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;", 2786 [('filename', '201.tif')], 2787 [errors.InvalidHeaderDefect]), 2788 2789 'duplicate_and_missing_split_value': ( 2790 "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;" 2791 " filename*3*=abc.gif", 2792 ' filename="201.tifabc.gif"', 2793 "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;" 2794 " filename*3*=abc.gif", 2795 [('filename', '201.tifabc.gif')], 2796 [errors.InvalidHeaderDefect]*2), 2797 2798 # Here we depart from get_param and assume the *0* was missing. 2799 'duplicate_with_broken_split_value': ( 2800 "filename=abc.gif; " 2801 " filename*2*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66", 2802 ' filename="abc.gif201.tif"', 2803 "filename=abc.gif;" 2804 " filename*2*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66", 2805 [('filename', 'abc.gif201.tif')], 2806 # Defects are apparent missing *0*, and two 'out of sequence'. 2807 [errors.InvalidHeaderDefect]*3), 2808 2809 # bpo-37461: Check that we don't go into an infinite loop. 2810 'extra_dquote': ( 2811 'r*="\'a\'\\"', 2812 ' r="\\""', 2813 'r*=\'a\'"', 2814 [('r', '"')], 2815 [errors.InvalidHeaderDefect]*2), 2816 } 2817 2818@parameterize 2819class Test_parse_mime_version(TestParserMixin, TestEmailBase): 2820 2821 def mime_version_as_value(self, 2822 value, 2823 tl_str, 2824 tl_value, 2825 major, 2826 minor, 2827 defects): 2828 mime_version = self._test_parse_x(parser.parse_mime_version, 2829 value, tl_str, tl_value, defects) 2830 self.assertEqual(mime_version.major, major) 2831 self.assertEqual(mime_version.minor, minor) 2832 2833 mime_version_params = { 2834 2835 'rfc_2045_1': ( 2836 '1.0', 2837 '1.0', 2838 '1.0', 2839 1, 2840 0, 2841 []), 2842 2843 'RFC_2045_2': ( 2844 '1.0 (produced by MetaSend Vx.x)', 2845 '1.0 (produced by MetaSend Vx.x)', 2846 '1.0 ', 2847 1, 2848 0, 2849 []), 2850 2851 'RFC_2045_3': ( 2852 '(produced by MetaSend Vx.x) 1.0', 2853 '(produced by MetaSend Vx.x) 1.0', 2854 ' 1.0', 2855 1, 2856 0, 2857 []), 2858 2859 'RFC_2045_4': ( 2860 '1.(produced by MetaSend Vx.x)0', 2861 '1.(produced by MetaSend Vx.x)0', 2862 '1. 0', 2863 1, 2864 0, 2865 []), 2866 2867 'empty': ( 2868 '', 2869 '', 2870 '', 2871 None, 2872 None, 2873 [errors.HeaderMissingRequiredValue]), 2874 2875 } 2876 2877 2878 2879class TestFolding(TestEmailBase): 2880 2881 policy = policy.default 2882 2883 def _test(self, tl, folded, policy=policy): 2884 self.assertEqual(tl.fold(policy=policy), folded, tl.ppstr()) 2885 2886 def test_simple_unstructured_no_folds(self): 2887 self._test(parser.get_unstructured("This is a test"), 2888 "This is a test\n") 2889 2890 def test_simple_unstructured_folded(self): 2891 self._test(parser.get_unstructured("This is also a test, but this " 2892 "time there are enough words (and even some " 2893 "symbols) to make it wrap; at least in theory."), 2894 "This is also a test, but this time there are enough " 2895 "words (and even some\n" 2896 " symbols) to make it wrap; at least in theory.\n") 2897 2898 def test_unstructured_with_unicode_no_folds(self): 2899 self._test(parser.get_unstructured("hübsch kleiner beißt"), 2900 "=?utf-8?q?h=C3=BCbsch_kleiner_bei=C3=9Ft?=\n") 2901 2902 def test_one_ew_on_each_of_two_wrapped_lines(self): 2903 self._test(parser.get_unstructured("Mein kleiner Kaktus ist sehr " 2904 "hübsch. Es hat viele Stacheln " 2905 "und oft beißt mich."), 2906 "Mein kleiner Kaktus ist sehr =?utf-8?q?h=C3=BCbsch=2E?= " 2907 "Es hat viele Stacheln\n" 2908 " und oft =?utf-8?q?bei=C3=9Ft?= mich.\n") 2909 2910 def test_ews_combined_before_wrap(self): 2911 self._test(parser.get_unstructured("Mein Kaktus ist hübsch. " 2912 "Es beißt mich. " 2913 "And that's all I'm sayin."), 2914 "Mein Kaktus ist =?utf-8?q?h=C3=BCbsch=2E__Es_bei=C3=9Ft?= " 2915 "mich. And that's\n" 2916 " all I'm sayin.\n") 2917 2918 # XXX Need test of an encoded word so long that it needs to be wrapped 2919 2920 def test_simple_address(self): 2921 self._test(parser.get_address_list("abc <xyz@example.com>")[0], 2922 "abc <xyz@example.com>\n") 2923 2924 def test_address_list_folding_at_commas(self): 2925 self._test(parser.get_address_list('abc <xyz@example.com>, ' 2926 '"Fred Blunt" <sharp@example.com>, ' 2927 '"J.P.Cool" <hot@example.com>, ' 2928 '"K<>y" <key@example.com>, ' 2929 'Firesale <cheap@example.com>, ' 2930 '<end@example.com>')[0], 2931 'abc <xyz@example.com>, "Fred Blunt" <sharp@example.com>,\n' 2932 ' "J.P.Cool" <hot@example.com>, "K<>y" <key@example.com>,\n' 2933 ' Firesale <cheap@example.com>, <end@example.com>\n') 2934 2935 def test_address_list_with_unicode_names(self): 2936 self._test(parser.get_address_list( 2937 'Hübsch Kaktus <beautiful@example.com>, ' 2938 'beißt beißt <biter@example.com>')[0], 2939 '=?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>,\n' 2940 ' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n') 2941 2942 def test_address_list_with_unicode_names_in_quotes(self): 2943 self._test(parser.get_address_list( 2944 '"Hübsch Kaktus" <beautiful@example.com>, ' 2945 '"beißt" beißt <biter@example.com>')[0], 2946 '=?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>,\n' 2947 ' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n') 2948 2949 # XXX Need tests with comments on various sides of a unicode token, 2950 # and with unicode tokens in the comments. Spaces inside the quotes 2951 # currently don't do the right thing. 2952 2953 def test_split_at_whitespace_after_header_before_long_token(self): 2954 body = parser.get_unstructured(' ' + 'x'*77) 2955 header = parser.Header([ 2956 parser.HeaderLabel([parser.ValueTerminal('test:', 'atext')]), 2957 parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]), body]) 2958 self._test(header, 'test: \n ' + 'x'*77 + '\n') 2959 2960 def test_split_at_whitespace_before_long_token(self): 2961 self._test(parser.get_unstructured('xxx ' + 'y'*77), 2962 'xxx \n ' + 'y'*77 + '\n') 2963 2964 def test_overlong_encodeable_is_wrapped(self): 2965 first_token_with_whitespace = 'xxx ' 2966 chrome_leader = '=?utf-8?q?' 2967 len_chrome = len(chrome_leader) + 2 2968 len_non_y = len_chrome + len(first_token_with_whitespace) 2969 self._test(parser.get_unstructured(first_token_with_whitespace + 2970 'y'*80), 2971 first_token_with_whitespace + chrome_leader + 2972 'y'*(78-len_non_y) + '?=\n' + 2973 ' ' + chrome_leader + 'y'*(80-(78-len_non_y)) + '?=\n') 2974 2975 def test_long_filename_attachment(self): 2976 self._test(parser.parse_content_disposition_header( 2977 'attachment; filename="TEST_TEST_TEST_TEST' 2978 '_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TES.txt"'), 2979 "attachment;\n" 2980 " filename*0*=us-ascii''TEST_TEST_TEST_TEST_TEST_TEST" 2981 "_TEST_TEST_TEST_TEST_TEST;\n" 2982 " filename*1*=_TEST_TES.txt\n", 2983 ) 2984 2985if __name__ == '__main__': 2986 unittest.main() 2987