1  """Test the parser and generator are inverses.
       2  
       3  Note that this is only strictly true if we are parsing RFC valid messages and
       4  producing RFC valid messages.
       5  """
       6  
       7  import io
       8  import unittest
       9  from email import policy, message_from_bytes
      10  from email.message import EmailMessage
      11  from email.generator import BytesGenerator
      12  from test.test_email import TestEmailBase, parameterize
      13  
      14  # This is like textwrap.dedent for bytes, except that it uses \r\n for the line
      15  # separators on the rebuilt string.
      16  def dedent(bstr):
      17      lines = bstr.splitlines()
      18      if not lines[0].strip():
      19          raise ValueError("First line must contain text")
      20      stripamt = len(lines[0]) - len(lines[0].lstrip())
      21      return b'\r\n'.join(
      22          [x[stripamt:] if len(x)>=stripamt else b''
      23              for x in lines])
      24  
      25  
      26  @parameterize
      27  class ESC[4;38;5;81mTestInversion(ESC[4;38;5;149mTestEmailBase):
      28  
      29      policy = policy.default
      30      message = EmailMessage
      31  
      32      def msg_as_input(self, msg):
      33          m = message_from_bytes(msg, policy=policy.SMTP)
      34          b = io.BytesIO()
      35          g = BytesGenerator(b)
      36          g.flatten(m)
      37          self.assertEqual(b.getvalue(), msg)
      38  
      39      # XXX: spaces are not preserved correctly here yet in the general case.
      40      msg_params = {
      41          'header_with_one_space_body': (dedent(b"""\
      42              From: abc@xyz.com
      43              X-Status:\x20
      44              Subject: test
      45  
      46              foo
      47              """),),
      48  
      49          'header_with_invalid_date': (dedent(b"""\
      50              Date: Tue, 06 Jun 2017 27:39:33 +0600
      51              From: abc@xyz.com
      52              Subject: timezones
      53  
      54              How do they work even?
      55              """),),
      56  
      57              }
      58  
      59      payload_params = {
      60          'plain_text': dict(payload='This is a test\n'*20),
      61          'base64_text': dict(payload=(('xy a'*40+'\n')*5), cte='base64'),
      62          'qp_text': dict(payload=(('xy a'*40+'\n')*5), cte='quoted-printable'),
      63          }
      64  
      65      def payload_as_body(self, payload, **kw):
      66          msg = self._make_message()
      67          msg['From'] = 'foo'
      68          msg['To'] = 'bar'
      69          msg['Subject'] = 'payload round trip test'
      70          msg.set_content(payload, **kw)
      71          b = bytes(msg)
      72          msg2 = message_from_bytes(b, policy=self.policy)
      73          self.assertEqual(bytes(msg2), b)
      74          self.assertEqual(msg2.get_content(), payload)
      75  
      76  
      77  if __name__ == '__main__':
      78      unittest.main()