Home | History | Annotate | Download | only in test
      1 import unittest
      2 
      3 import sys, io, subprocess
      4 import quopri
      5 
      6 
      7 
      8 ENCSAMPLE = b"""\
      9 Here's a bunch of special=20
     10 
     11 =A1=A2=A3=A4=A5=A6=A7=A8=A9
     12 =AA=AB=AC=AD=AE=AF=B0=B1=B2=B3
     13 =B4=B5=B6=B7=B8=B9=BA=BB=BC=BD=BE
     14 =BF=C0=C1=C2=C3=C4=C5=C6
     15 =C7=C8=C9=CA=CB=CC=CD=CE=CF
     16 =D0=D1=D2=D3=D4=D5=D6=D7
     17 =D8=D9=DA=DB=DC=DD=DE=DF
     18 =E0=E1=E2=E3=E4=E5=E6=E7
     19 =E8=E9=EA=EB=EC=ED=EE=EF
     20 =F0=F1=F2=F3=F4=F5=F6=F7
     21 =F8=F9=FA=FB=FC=FD=FE=FF
     22 
     23 characters... have fun!
     24 """
     25 
     26 # First line ends with a space
     27 DECSAMPLE = b"Here's a bunch of special \n" + \
     28 b"""\
     29 
     30 \xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9
     31 \xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3
     32 \xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe
     33 \xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6
     34 \xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf
     35 \xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7
     36 \xd8\xd9\xda\xdb\xdc\xdd\xde\xdf
     37 \xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7
     38 \xe8\xe9\xea\xeb\xec\xed\xee\xef
     39 \xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7
     40 \xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
     41 
     42 characters... have fun!
     43 """
     44 
     45 
     46 def withpythonimplementation(testfunc):
     47     def newtest(self):
     48         # Test default implementation
     49         testfunc(self)
     50         # Test Python implementation
     51         if quopri.b2a_qp is not None or quopri.a2b_qp is not None:
     52             oldencode = quopri.b2a_qp
     53             olddecode = quopri.a2b_qp
     54             try:
     55                 quopri.b2a_qp = None
     56                 quopri.a2b_qp = None
     57                 testfunc(self)
     58             finally:
     59                 quopri.b2a_qp = oldencode
     60                 quopri.a2b_qp = olddecode
     61     newtest.__name__ = testfunc.__name__
     62     return newtest
     63 
     64 class QuopriTestCase(unittest.TestCase):
     65     # Each entry is a tuple of (plaintext, encoded string).  These strings are
     66     # used in the "quotetabs=0" tests.
     67     STRINGS = (
     68         # Some normal strings
     69         (b'hello', b'hello'),
     70         (b'''hello
     71         there
     72         world''', b'''hello
     73         there
     74         world'''),
     75         (b'''hello
     76         there
     77         world
     78 ''', b'''hello
     79         there
     80         world
     81 '''),
     82         (b'\201\202\203', b'=81=82=83'),
     83         # Add some trailing MUST QUOTE strings
     84         (b'hello ', b'hello=20'),
     85         (b'hello\t', b'hello=09'),
     86         # Some long lines.  First, a single line of 108 characters
     87         (b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\xd8\xd9\xda\xdb\xdc\xdd\xde\xdfxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
     88          b'''xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=D8=D9=DA=DB=DC=DD=DE=DFx=
     89 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'''),
     90         # A line of exactly 76 characters, no soft line break should be needed
     91         (b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
     92         b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'),
     93         # A line of 77 characters, forcing a soft line break at position 75,
     94         # and a second line of exactly 2 characters (because the soft line
     95         # break `=' sign counts against the line length limit).
     96         (b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
     97          b'''zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
     98 zz'''),
     99         # A line of 151 characters, forcing a soft line break at position 75,
    100         # with a second line of exactly 76 characters and no trailing =
    101         (b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
    102          b'''zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
    103 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'''),
    104         # A string containing a hard line break, but which the first line is
    105         # 151 characters and the second line is exactly 76 characters.  This
    106         # should leave us with three lines, the first which has a soft line
    107         # break, and which the second and third do not.
    108         (b'''yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
    109 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz''',
    110          b'''yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy=
    111 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
    112 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'''),
    113         # Now some really complex stuff ;)
    114         (DECSAMPLE, ENCSAMPLE),
    115         )
    116 
    117     # These are used in the "quotetabs=1" tests.
    118     ESTRINGS = (
    119         (b'hello world', b'hello=20world'),
    120         (b'hello\tworld', b'hello=09world'),
    121         )
    122 
    123     # These are used in the "header=1" tests.
    124     HSTRINGS = (
    125         (b'hello world', b'hello_world'),
    126         (b'hello_world', b'hello=5Fworld'),
    127         )
    128 
    129     @withpythonimplementation
    130     def test_encodestring(self):
    131         for p, e in self.STRINGS:
    132             self.assertEqual(quopri.encodestring(p), e)
    133 
    134     @withpythonimplementation
    135     def test_decodestring(self):
    136         for p, e in self.STRINGS:
    137             self.assertEqual(quopri.decodestring(e), p)
    138 
    139     @withpythonimplementation
    140     def test_decodestring_double_equals(self):
    141         # Issue 21511 - Ensure that byte string is compared to byte string
    142         # instead of int byte value
    143         decoded_value, encoded_value = (b"123=four", b"123==four")
    144         self.assertEqual(quopri.decodestring(encoded_value), decoded_value)
    145 
    146     @withpythonimplementation
    147     def test_idempotent_string(self):
    148         for p, e in self.STRINGS:
    149             self.assertEqual(quopri.decodestring(quopri.encodestring(e)), e)
    150 
    151     @withpythonimplementation
    152     def test_encode(self):
    153         for p, e in self.STRINGS:
    154             infp = io.BytesIO(p)
    155             outfp = io.BytesIO()
    156             quopri.encode(infp, outfp, quotetabs=False)
    157             self.assertEqual(outfp.getvalue(), e)
    158 
    159     @withpythonimplementation
    160     def test_decode(self):
    161         for p, e in self.STRINGS:
    162             infp = io.BytesIO(e)
    163             outfp = io.BytesIO()
    164             quopri.decode(infp, outfp)
    165             self.assertEqual(outfp.getvalue(), p)
    166 
    167     @withpythonimplementation
    168     def test_embedded_ws(self):
    169         for p, e in self.ESTRINGS:
    170             self.assertEqual(quopri.encodestring(p, quotetabs=True), e)
    171             self.assertEqual(quopri.decodestring(e), p)
    172 
    173     @withpythonimplementation
    174     def test_encode_header(self):
    175         for p, e in self.HSTRINGS:
    176             self.assertEqual(quopri.encodestring(p, header=True), e)
    177 
    178     @withpythonimplementation
    179     def test_decode_header(self):
    180         for p, e in self.HSTRINGS:
    181             self.assertEqual(quopri.decodestring(e, header=True), p)
    182 
    183     def test_scriptencode(self):
    184         (p, e) = self.STRINGS[-1]
    185         process = subprocess.Popen([sys.executable, "-mquopri"],
    186                                    stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    187         self.addCleanup(process.stdout.close)
    188         cout, cerr = process.communicate(p)
    189         # On Windows, Python will output the result to stdout using
    190         # CRLF, as the mode of stdout is text mode. To compare this
    191         # with the expected result, we need to do a line-by-line comparison.
    192         cout = cout.decode('latin-1').splitlines()
    193         e = e.decode('latin-1').splitlines()
    194         assert len(cout)==len(e)
    195         for i in range(len(cout)):
    196             self.assertEqual(cout[i], e[i])
    197         self.assertEqual(cout, e)
    198 
    199     def test_scriptdecode(self):
    200         (p, e) = self.STRINGS[-1]
    201         process = subprocess.Popen([sys.executable, "-mquopri", "-d"],
    202                                    stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    203         self.addCleanup(process.stdout.close)
    204         cout, cerr = process.communicate(e)
    205         cout = cout.decode('latin-1')
    206         p = p.decode('latin-1')
    207         self.assertEqual(cout.splitlines(), p.splitlines())
    208 
    209 if __name__ == "__main__":
    210     unittest.main()
    211