1"""
2Tests for uu module.
3Nick Mathewson
4"""
5
6import unittest
7from test import support
8
9import sys, os
10import uu
11import io
12
13plaintext = b"The smooth-scaled python crept over the sleeping dog\n"
14
15encodedtext = b"""\
16M5&AE('-M;V]T:\"US8V%L960@<'ET:&]N(&-R97!T(&]V97(@=&AE('-L965P
17(:6YG(&1O9PH """
18
19# Stolen from io.py
20class FakeIO(io.TextIOWrapper):
21    """Text I/O implementation using an in-memory buffer.
22
23    Can be a used as a drop-in replacement for sys.stdin and sys.stdout.
24    """
25
26    # XXX This is really slow, but fully functional
27
28    def __init__(self, initial_value="", encoding="utf-8",
29                 errors="strict", newline="\n"):
30        super(FakeIO, self).__init__(io.BytesIO(),
31                                     encoding=encoding,
32                                     errors=errors,
33                                     newline=newline)
34        self._encoding = encoding
35        self._errors = errors
36        if initial_value:
37            if not isinstance(initial_value, str):
38                initial_value = str(initial_value)
39            self.write(initial_value)
40            self.seek(0)
41
42    def getvalue(self):
43        self.flush()
44        return self.buffer.getvalue().decode(self._encoding, self._errors)
45
46
47def encodedtextwrapped(mode, filename):
48    return (bytes("begin %03o %s\n" % (mode, filename), "ascii") +
49            encodedtext + b"\n \nend\n")
50
51class UUTest(unittest.TestCase):
52
53    def test_encode(self):
54        inp = io.BytesIO(plaintext)
55        out = io.BytesIO()
56        uu.encode(inp, out, "t1")
57        self.assertEqual(out.getvalue(), encodedtextwrapped(0o666, "t1"))
58        inp = io.BytesIO(plaintext)
59        out = io.BytesIO()
60        uu.encode(inp, out, "t1", 0o644)
61        self.assertEqual(out.getvalue(), encodedtextwrapped(0o644, "t1"))
62
63    def test_decode(self):
64        inp = io.BytesIO(encodedtextwrapped(0o666, "t1"))
65        out = io.BytesIO()
66        uu.decode(inp, out)
67        self.assertEqual(out.getvalue(), plaintext)
68        inp = io.BytesIO(
69            b"UUencoded files may contain many lines,\n" +
70            b"even some that have 'begin' in them.\n" +
71            encodedtextwrapped(0o666, "t1")
72        )
73        out = io.BytesIO()
74        uu.decode(inp, out)
75        self.assertEqual(out.getvalue(), plaintext)
76
77    def test_truncatedinput(self):
78        inp = io.BytesIO(b"begin 644 t1\n" + encodedtext)
79        out = io.BytesIO()
80        try:
81            uu.decode(inp, out)
82            self.fail("No exception raised")
83        except uu.Error as e:
84            self.assertEqual(str(e), "Truncated input file")
85
86    def test_missingbegin(self):
87        inp = io.BytesIO(b"")
88        out = io.BytesIO()
89        try:
90            uu.decode(inp, out)
91            self.fail("No exception raised")
92        except uu.Error as e:
93            self.assertEqual(str(e), "No valid begin line found in input file")
94
95    def test_garbage_padding(self):
96        # Issue #22406
97        encodedtext = (
98            b"begin 644 file\n"
99            # length 1; bits 001100 111111 111111 111111
100            b"\x21\x2C\x5F\x5F\x5F\n"
101            b"\x20\n"
102            b"end\n"
103        )
104        plaintext = b"\x33"  # 00110011
105
106        with self.subTest("uu.decode()"):
107            inp = io.BytesIO(encodedtext)
108            out = io.BytesIO()
109            uu.decode(inp, out, quiet=True)
110            self.assertEqual(out.getvalue(), plaintext)
111
112        with self.subTest("uu_codec"):
113            import codecs
114            decoded = codecs.decode(encodedtext, "uu_codec")
115            self.assertEqual(decoded, plaintext)
116
117class UUStdIOTest(unittest.TestCase):
118
119    def setUp(self):
120        self.stdin = sys.stdin
121        self.stdout = sys.stdout
122
123    def tearDown(self):
124        sys.stdin = self.stdin
125        sys.stdout = self.stdout
126
127    def test_encode(self):
128        sys.stdin = FakeIO(plaintext.decode("ascii"))
129        sys.stdout = FakeIO()
130        uu.encode("-", "-", "t1", 0o666)
131        self.assertEqual(sys.stdout.getvalue(),
132                         encodedtextwrapped(0o666, "t1").decode("ascii"))
133
134    def test_decode(self):
135        sys.stdin = FakeIO(encodedtextwrapped(0o666, "t1").decode("ascii"))
136        sys.stdout = FakeIO()
137        uu.decode("-", "-")
138        stdout = sys.stdout
139        sys.stdout = self.stdout
140        sys.stdin = self.stdin
141        self.assertEqual(stdout.getvalue(), plaintext.decode("ascii"))
142
143class UUFileTest(unittest.TestCase):
144
145    def _kill(self, f):
146        # close and remove file
147        if f is None:
148            return
149        try:
150            f.close()
151        except (SystemExit, KeyboardInterrupt):
152            raise
153        except:
154            pass
155        try:
156            os.unlink(f.name)
157        except (SystemExit, KeyboardInterrupt):
158            raise
159        except:
160            pass
161
162    def setUp(self):
163        self.tmpin  = support.TESTFN + "i"
164        self.tmpout = support.TESTFN + "o"
165
166    def tearDown(self):
167        del self.tmpin
168        del self.tmpout
169
170    def test_encode(self):
171        fin = fout = None
172        try:
173            support.unlink(self.tmpin)
174            fin = open(self.tmpin, 'wb')
175            fin.write(plaintext)
176            fin.close()
177
178            fin = open(self.tmpin, 'rb')
179            fout = open(self.tmpout, 'wb')
180            uu.encode(fin, fout, self.tmpin, mode=0o644)
181            fin.close()
182            fout.close()
183
184            fout = open(self.tmpout, 'rb')
185            s = fout.read()
186            fout.close()
187            self.assertEqual(s, encodedtextwrapped(0o644, self.tmpin))
188
189            # in_file and out_file as filenames
190            uu.encode(self.tmpin, self.tmpout, self.tmpin, mode=0o644)
191            fout = open(self.tmpout, 'rb')
192            s = fout.read()
193            fout.close()
194            self.assertEqual(s, encodedtextwrapped(0o644, self.tmpin))
195
196        finally:
197            self._kill(fin)
198            self._kill(fout)
199
200    def test_decode(self):
201        f = None
202        try:
203            support.unlink(self.tmpin)
204            f = open(self.tmpin, 'wb')
205            f.write(encodedtextwrapped(0o644, self.tmpout))
206            f.close()
207
208            f = open(self.tmpin, 'rb')
209            uu.decode(f)
210            f.close()
211
212            f = open(self.tmpout, 'rb')
213            s = f.read()
214            f.close()
215            self.assertEqual(s, plaintext)
216            # XXX is there an xp way to verify the mode?
217        finally:
218            self._kill(f)
219
220    def test_decode_filename(self):
221        f = None
222        try:
223            support.unlink(self.tmpin)
224            f = open(self.tmpin, 'wb')
225            f.write(encodedtextwrapped(0o644, self.tmpout))
226            f.close()
227
228            uu.decode(self.tmpin)
229
230            f = open(self.tmpout, 'rb')
231            s = f.read()
232            f.close()
233            self.assertEqual(s, plaintext)
234        finally:
235            self._kill(f)
236
237    def test_decodetwice(self):
238        # Verify that decode() will refuse to overwrite an existing file
239        f = None
240        try:
241            f = io.BytesIO(encodedtextwrapped(0o644, self.tmpout))
242
243            f = open(self.tmpin, 'rb')
244            uu.decode(f)
245            f.close()
246
247            f = open(self.tmpin, 'rb')
248            self.assertRaises(uu.Error, uu.decode, f)
249            f.close()
250        finally:
251            self._kill(f)
252
253def test_main():
254    support.run_unittest(UUTest,
255                              UUStdIOTest,
256                              UUFileTest,
257                              )
258
259if __name__=="__main__":
260    test_main()
261