test_quopri.py 7.53 KB
Newer Older
1
from test import support
2 3
import unittest

4
import sys, os, io, subprocess
5
import quopri
6 7


8

9
ENCSAMPLE = b"""\
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
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
28 29
DECSAMPLE = b"Here's a bunch of special \n" + \
b"""\
30

31 32 33 34 35 36 37 38 39 40 41
\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
42 43 44 45 46

characters... have fun!
"""


47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
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
64

65 66 67 68 69
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
70 71
        (b'hello', b'hello'),
        (b'''hello
72
        there
73
        world''', b'''hello
74 75
        there
        world'''),
76
        (b'''hello
77 78
        there
        world
79
''', b'''hello
80 81 82
        there
        world
'''),
83
        (b'\201\202\203', b'=81=82=83'),
84
        # Add some trailing MUST QUOTE strings
85 86
        (b'hello ', b'hello=20'),
        (b'hello\t', b'hello=09'),
87
        # Some long lines.  First, a single line of 108 characters
88 89
        (b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\xd8\xd9\xda\xdb\xdc\xdd\xde\xdfxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
         b'''xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=D8=D9=DA=DB=DC=DD=DE=DFx=
90 91
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'''),
        # A line of exactly 76 characters, no soft line break should be needed
92 93
        (b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
        b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'),
94 95 96
        # 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).
97 98
        (b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
         b'''zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
99 100 101
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 =
102 103
        (b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',
         b'''zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
104 105 106 107 108
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.
109
        (b'''yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
110
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz''',
111
         b'''yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy=
112 113 114
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'''),
        # Now some really complex stuff ;)
115 116 117 118 119
        (DECSAMPLE, ENCSAMPLE),
        )

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

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

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

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

140
    @withpythonimplementation
141 142
    def test_idempotent_string(self):
        for p, e in self.STRINGS:
143
            self.assertEqual(quopri.decodestring(quopri.encodestring(e)), e)
144

145
    @withpythonimplementation
146 147
    def test_encode(self):
        for p, e in self.STRINGS:
148 149
            infp = io.BytesIO(p)
            outfp = io.BytesIO()
150
            quopri.encode(infp, outfp, quotetabs=False)
151
            self.assertEqual(outfp.getvalue(), e)
152

153
    @withpythonimplementation
154 155
    def test_decode(self):
        for p, e in self.STRINGS:
156 157
            infp = io.BytesIO(e)
            outfp = io.BytesIO()
158
            quopri.decode(infp, outfp)
159
            self.assertEqual(outfp.getvalue(), p)
160

161
    @withpythonimplementation
162 163
    def test_embedded_ws(self):
        for p, e in self.ESTRINGS:
164
            self.assertEqual(quopri.encodestring(p, quotetabs=True), e)
165
            self.assertEqual(quopri.decodestring(e), p)
166

167
    @withpythonimplementation
168 169
    def test_encode_header(self):
        for p, e in self.HSTRINGS:
170
            self.assertEqual(quopri.encodestring(p, header=True), e)
171

172
    @withpythonimplementation
173 174
    def test_decode_header(self):
        for p, e in self.HSTRINGS:
175
            self.assertEqual(quopri.decodestring(e, header=True), p)
176

177
    def test_scriptencode(self):
Tim Peters's avatar
Tim Peters committed
178
        (p, e) = self.STRINGS[-1]
179 180
        process = subprocess.Popen([sys.executable, "-mquopri"],
                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE)
181
        self.addCleanup(process.stdout.close)
182 183 184 185
        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.
186 187 188 189 190 191
        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)
192 193

    def test_scriptdecode(self):
Tim Peters's avatar
Tim Peters committed
194
        (p, e) = self.STRINGS[-1]
195 196
        process = subprocess.Popen([sys.executable, "-mquopri", "-d"],
                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE)
197
        self.addCleanup(process.stdout.close)
198
        cout, cerr = process.communicate(e)
199 200
        cout = cout.decode('latin-1')
        p = p.decode('latin-1')
201
        self.assertEqual(cout.splitlines(), p.splitlines())
202

203
def test_main():
204
    support.run_unittest(QuopriTestCase)
205 206 207 208


if __name__ == "__main__":
    test_main()