test_quopri.py 7.78 KB
Newer Older
1 2
import unittest

3
import sys, io, subprocess
4
import quopri
5 6


7

8
ENCSAMPLE = b"""\
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Here's a bunch of special=20

=A1=A2=A3=A4=A5=A6=A7=A8=A9
=AA=AB=AC=AD=AE=AF=B0=B1=B2=B3
=B4=B5=B6=B7=B8=B9=BA=BB=BC=BD=BE
=BF=C0=C1=C2=C3=C4=C5=C6
=C7=C8=C9=CA=CB=CC=CD=CE=CF
=D0=D1=D2=D3=D4=D5=D6=D7
=D8=D9=DA=DB=DC=DD=DE=DF
=E0=E1=E2=E3=E4=E5=E6=E7
=E8=E9=EA=EB=EC=ED=EE=EF
=F0=F1=F2=F3=F4=F5=F6=F7
=F8=F9=FA=FB=FC=FD=FE=FF

characters... have fun!
"""

# First line ends with a space
27 28
DECSAMPLE = b"Here's a bunch of special \n" + \
b"""\
29

30 31 32 33 34 35 36 37 38 39 40
\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9
\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3
\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe
\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6
\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf
\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7
\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf
\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7
\xe8\xe9\xea\xeb\xec\xed\xee\xef
\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7
\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
41 42 43 44 45

characters... have fun!
"""


46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
def withpythonimplementation(testfunc):
    def newtest(self):
        # Test default implementation
        testfunc(self)
        # Test Python implementation
        if quopri.b2a_qp is not None or quopri.a2b_qp is not None:
            oldencode = quopri.b2a_qp
            olddecode = quopri.a2b_qp
            try:
                quopri.b2a_qp = None
                quopri.a2b_qp = None
                testfunc(self)
            finally:
                quopri.b2a_qp = oldencode
                quopri.a2b_qp = olddecode
    newtest.__name__ = testfunc.__name__
    return newtest
63

64 65 66 67 68
class QuopriTestCase(unittest.TestCase):
    # Each entry is a tuple of (plaintext, encoded string).  These strings are
    # used in the "quotetabs=0" tests.
    STRINGS = (
        # Some normal strings
69 70
        (b'hello', b'hello'),
        (b'''hello
71
        there
72
        world''', b'''hello
73 74
        there
        world'''),
75
        (b'''hello
76 77
        there
        world
78
''', b'''hello
79 80 81
        there
        world
'''),
82
        (b'\201\202\203', b'=81=82=83'),
83
        # Add some trailing MUST QUOTE strings
84 85
        (b'hello ', b'hello=20'),
        (b'hello\t', b'hello=09'),
86
        # Some long lines.  First, a single line of 108 characters
87 88
        (b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\xd8\xd9\xda\xdb\xdc\xdd\xde\xdfxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
         b'''xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=D8=D9=DA=DB=DC=DD=DE=DFx=
89 90
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'''),
        # A line of exactly 76 characters, no soft line break should be needed
91 92
        (b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
        b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'),
93 94 95
        # A line of 77 characters, forcing a soft line break at position 75,
        # and a second line of exactly 2 characters (because the soft line
        # break `=' sign counts against the line length limit).
96 97
        (b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
         b'''zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
98 99 100
zz'''),
        # A line of 151 characters, forcing a soft line break at position 75,
        # with a second line of exactly 76 characters and no trailing =
101 102
        (b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
         b'''zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
103 104 105 106 107
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'''),
        # A string containing a hard line break, but which the first line is
        # 151 characters and the second line is exactly 76 characters.  This
        # should leave us with three lines, the first which has a soft line
        # break, and which the second and third do not.
108
        (b'''yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
109
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz''',
110
         b'''yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy=
111 112 113
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'''),
        # Now some really complex stuff ;)
114 115 116 117 118
        (DECSAMPLE, ENCSAMPLE),
        )

    # These are used in the "quotetabs=1" tests.
    ESTRINGS = (
119 120
        (b'hello world', b'hello=20world'),
        (b'hello\tworld', b'hello=09world'),
121
        )
122

123 124
    # These are used in the "header=1" tests.
    HSTRINGS = (
125 126
        (b'hello world', b'hello_world'),
        (b'hello_world', b'hello=5Fworld'),
127 128
        )

129
    @withpythonimplementation
130 131
    def test_encodestring(self):
        for p, e in self.STRINGS:
132
            self.assertEqual(quopri.encodestring(p), e)
133

134
    @withpythonimplementation
135 136
    def test_decodestring(self):
        for p, e in self.STRINGS:
137
            self.assertEqual(quopri.decodestring(e), p)
138

139 140 141 142 143 144 145
    @withpythonimplementation
    def test_decodestring_double_equals(self):
        # Issue 21511 - Ensure that byte string is compared to byte string
        # instead of int byte value
        decoded_value, encoded_value = (b"123=four", b"123==four")
        self.assertEqual(quopri.decodestring(encoded_value), decoded_value)

146
    @withpythonimplementation
147 148
    def test_idempotent_string(self):
        for p, e in self.STRINGS:
149
            self.assertEqual(quopri.decodestring(quopri.encodestring(e)), e)
150

151
    @withpythonimplementation
152 153
    def test_encode(self):
        for p, e in self.STRINGS:
154 155
            infp = io.BytesIO(p)
            outfp = io.BytesIO()
156
            quopri.encode(infp, outfp, quotetabs=False)
157
            self.assertEqual(outfp.getvalue(), e)
158

159
    @withpythonimplementation
160 161
    def test_decode(self):
        for p, e in self.STRINGS:
162 163
            infp = io.BytesIO(e)
            outfp = io.BytesIO()
164
            quopri.decode(infp, outfp)
165
            self.assertEqual(outfp.getvalue(), p)
166

167
    @withpythonimplementation
168 169
    def test_embedded_ws(self):
        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 175
    def test_encode_header(self):
        for p, e in self.HSTRINGS:
176
            self.assertEqual(quopri.encodestring(p, header=True), e)
177

178
    @withpythonimplementation
179 180
    def test_decode_header(self):
        for p, e in self.HSTRINGS:
181
            self.assertEqual(quopri.decodestring(e, header=True), p)
182

183
    def test_scriptencode(self):
Tim Peters's avatar
Tim Peters committed
184
        (p, e) = self.STRINGS[-1]
185 186
        process = subprocess.Popen([sys.executable, "-mquopri"],
                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE)
187
        self.addCleanup(process.stdout.close)
188 189 190 191
        cout, cerr = process.communicate(p)
        # On Windows, Python will output the result to stdout using
        # CRLF, as the mode of stdout is text mode. To compare this
        # with the expected result, we need to do a line-by-line comparison.
192 193 194 195 196 197
        cout = cout.decode('latin-1').splitlines()
        e = e.decode('latin-1').splitlines()
        assert len(cout)==len(e)
        for i in range(len(cout)):
            self.assertEqual(cout[i], e[i])
        self.assertEqual(cout, e)
198 199

    def test_scriptdecode(self):
Tim Peters's avatar
Tim Peters committed
200
        (p, e) = self.STRINGS[-1]
201 202
        process = subprocess.Popen([sys.executable, "-mquopri", "-d"],
                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE)
203
        self.addCleanup(process.stdout.close)
204
        cout, cerr = process.communicate(e)
205 206
        cout = cout.decode('latin-1')
        p = p.decode('latin-1')
207
        self.assertEqual(cout.splitlines(), p.splitlines())
208

209
if __name__ == "__main__":
210
    unittest.main()