17db96d56Sopenharmony_ci"""
27db96d56Sopenharmony_ciTests for uu module.
37db96d56Sopenharmony_ciNick Mathewson
47db96d56Sopenharmony_ci"""
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ciimport unittest
77db96d56Sopenharmony_cifrom test.support import os_helper, warnings_helper
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ciuu = warnings_helper.import_deprecated("uu")
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ciimport os
127db96d56Sopenharmony_ciimport stat
137db96d56Sopenharmony_ciimport sys
147db96d56Sopenharmony_ciimport io
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ciplaintext = b"The symbols on top of your keyboard are !@#$%^&*()_+|~\n"
177db96d56Sopenharmony_ci
187db96d56Sopenharmony_ciencodedtext = b"""\
197db96d56Sopenharmony_ciM5&AE('-Y;6)O;',@;VX@=&]P(&]F('EO=7(@:V5Y8F]A<F0@87)E("% (R0E
207db96d56Sopenharmony_ci*7B8J*"E?*WQ^"@  """
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_ci# Stolen from io.py
237db96d56Sopenharmony_ciclass FakeIO(io.TextIOWrapper):
247db96d56Sopenharmony_ci    """Text I/O implementation using an in-memory buffer.
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_ci    Can be a used as a drop-in replacement for sys.stdin and sys.stdout.
277db96d56Sopenharmony_ci    """
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci    # XXX This is really slow, but fully functional
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci    def __init__(self, initial_value="", encoding="utf-8",
327db96d56Sopenharmony_ci                 errors="strict", newline="\n"):
337db96d56Sopenharmony_ci        super(FakeIO, self).__init__(io.BytesIO(),
347db96d56Sopenharmony_ci                                     encoding=encoding,
357db96d56Sopenharmony_ci                                     errors=errors,
367db96d56Sopenharmony_ci                                     newline=newline)
377db96d56Sopenharmony_ci        self._encoding = encoding
387db96d56Sopenharmony_ci        self._errors = errors
397db96d56Sopenharmony_ci        if initial_value:
407db96d56Sopenharmony_ci            if not isinstance(initial_value, str):
417db96d56Sopenharmony_ci                initial_value = str(initial_value)
427db96d56Sopenharmony_ci            self.write(initial_value)
437db96d56Sopenharmony_ci            self.seek(0)
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ci    def getvalue(self):
467db96d56Sopenharmony_ci        self.flush()
477db96d56Sopenharmony_ci        return self.buffer.getvalue().decode(self._encoding, self._errors)
487db96d56Sopenharmony_ci
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_cidef encodedtextwrapped(mode, filename, backtick=False):
517db96d56Sopenharmony_ci    if backtick:
527db96d56Sopenharmony_ci        res = (bytes("begin %03o %s\n" % (mode, filename), "ascii") +
537db96d56Sopenharmony_ci               encodedtext.replace(b' ', b'`') + b"\n`\nend\n")
547db96d56Sopenharmony_ci    else:
557db96d56Sopenharmony_ci        res = (bytes("begin %03o %s\n" % (mode, filename), "ascii") +
567db96d56Sopenharmony_ci               encodedtext + b"\n \nend\n")
577db96d56Sopenharmony_ci    return res
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ciclass UUTest(unittest.TestCase):
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci    def test_encode(self):
627db96d56Sopenharmony_ci        inp = io.BytesIO(plaintext)
637db96d56Sopenharmony_ci        out = io.BytesIO()
647db96d56Sopenharmony_ci        uu.encode(inp, out, "t1")
657db96d56Sopenharmony_ci        self.assertEqual(out.getvalue(), encodedtextwrapped(0o666, "t1"))
667db96d56Sopenharmony_ci        inp = io.BytesIO(plaintext)
677db96d56Sopenharmony_ci        out = io.BytesIO()
687db96d56Sopenharmony_ci        uu.encode(inp, out, "t1", 0o644)
697db96d56Sopenharmony_ci        self.assertEqual(out.getvalue(), encodedtextwrapped(0o644, "t1"))
707db96d56Sopenharmony_ci        inp = io.BytesIO(plaintext)
717db96d56Sopenharmony_ci        out = io.BytesIO()
727db96d56Sopenharmony_ci        uu.encode(inp, out, "t1", backtick=True)
737db96d56Sopenharmony_ci        self.assertEqual(out.getvalue(), encodedtextwrapped(0o666, "t1", True))
747db96d56Sopenharmony_ci        with self.assertRaises(TypeError):
757db96d56Sopenharmony_ci            uu.encode(inp, out, "t1", 0o644, True)
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ci    @os_helper.skip_unless_working_chmod
787db96d56Sopenharmony_ci    def test_decode(self):
797db96d56Sopenharmony_ci        for backtick in True, False:
807db96d56Sopenharmony_ci            inp = io.BytesIO(encodedtextwrapped(0o666, "t1", backtick=backtick))
817db96d56Sopenharmony_ci            out = io.BytesIO()
827db96d56Sopenharmony_ci            uu.decode(inp, out)
837db96d56Sopenharmony_ci            self.assertEqual(out.getvalue(), plaintext)
847db96d56Sopenharmony_ci            inp = io.BytesIO(
857db96d56Sopenharmony_ci                b"UUencoded files may contain many lines,\n" +
867db96d56Sopenharmony_ci                b"even some that have 'begin' in them.\n" +
877db96d56Sopenharmony_ci                encodedtextwrapped(0o666, "t1", backtick=backtick)
887db96d56Sopenharmony_ci            )
897db96d56Sopenharmony_ci            out = io.BytesIO()
907db96d56Sopenharmony_ci            uu.decode(inp, out)
917db96d56Sopenharmony_ci            self.assertEqual(out.getvalue(), plaintext)
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_ci    def test_truncatedinput(self):
947db96d56Sopenharmony_ci        inp = io.BytesIO(b"begin 644 t1\n" + encodedtext)
957db96d56Sopenharmony_ci        out = io.BytesIO()
967db96d56Sopenharmony_ci        try:
977db96d56Sopenharmony_ci            uu.decode(inp, out)
987db96d56Sopenharmony_ci            self.fail("No exception raised")
997db96d56Sopenharmony_ci        except uu.Error as e:
1007db96d56Sopenharmony_ci            self.assertEqual(str(e), "Truncated input file")
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_ci    def test_missingbegin(self):
1037db96d56Sopenharmony_ci        inp = io.BytesIO(b"")
1047db96d56Sopenharmony_ci        out = io.BytesIO()
1057db96d56Sopenharmony_ci        try:
1067db96d56Sopenharmony_ci            uu.decode(inp, out)
1077db96d56Sopenharmony_ci            self.fail("No exception raised")
1087db96d56Sopenharmony_ci        except uu.Error as e:
1097db96d56Sopenharmony_ci            self.assertEqual(str(e), "No valid begin line found in input file")
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_ci    def test_garbage_padding(self):
1127db96d56Sopenharmony_ci        # Issue #22406
1137db96d56Sopenharmony_ci        encodedtext1 = (
1147db96d56Sopenharmony_ci            b"begin 644 file\n"
1157db96d56Sopenharmony_ci            # length 1; bits 001100 111111 111111 111111
1167db96d56Sopenharmony_ci            b"\x21\x2C\x5F\x5F\x5F\n"
1177db96d56Sopenharmony_ci            b"\x20\n"
1187db96d56Sopenharmony_ci            b"end\n"
1197db96d56Sopenharmony_ci        )
1207db96d56Sopenharmony_ci        encodedtext2 = (
1217db96d56Sopenharmony_ci            b"begin 644 file\n"
1227db96d56Sopenharmony_ci            # length 1; bits 001100 111111 111111 111111
1237db96d56Sopenharmony_ci            b"\x21\x2C\x5F\x5F\x5F\n"
1247db96d56Sopenharmony_ci            b"\x60\n"
1257db96d56Sopenharmony_ci            b"end\n"
1267db96d56Sopenharmony_ci        )
1277db96d56Sopenharmony_ci        plaintext = b"\x33"  # 00110011
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_ci        for encodedtext in encodedtext1, encodedtext2:
1307db96d56Sopenharmony_ci            with self.subTest("uu.decode()"):
1317db96d56Sopenharmony_ci                inp = io.BytesIO(encodedtext)
1327db96d56Sopenharmony_ci                out = io.BytesIO()
1337db96d56Sopenharmony_ci                uu.decode(inp, out, quiet=True)
1347db96d56Sopenharmony_ci                self.assertEqual(out.getvalue(), plaintext)
1357db96d56Sopenharmony_ci
1367db96d56Sopenharmony_ci            with self.subTest("uu_codec"):
1377db96d56Sopenharmony_ci                import codecs
1387db96d56Sopenharmony_ci                decoded = codecs.decode(encodedtext, "uu_codec")
1397db96d56Sopenharmony_ci                self.assertEqual(decoded, plaintext)
1407db96d56Sopenharmony_ci
1417db96d56Sopenharmony_ci    def test_newlines_escaped(self):
1427db96d56Sopenharmony_ci        # Test newlines are escaped with uu.encode
1437db96d56Sopenharmony_ci        inp = io.BytesIO(plaintext)
1447db96d56Sopenharmony_ci        out = io.BytesIO()
1457db96d56Sopenharmony_ci        filename = "test.txt\n\roverflow.txt"
1467db96d56Sopenharmony_ci        safefilename = b"test.txt\\n\\roverflow.txt"
1477db96d56Sopenharmony_ci        uu.encode(inp, out, filename)
1487db96d56Sopenharmony_ci        self.assertIn(safefilename, out.getvalue())
1497db96d56Sopenharmony_ci
1507db96d56Sopenharmony_ci    def test_no_directory_traversal(self):
1517db96d56Sopenharmony_ci        relative_bad = b"""\
1527db96d56Sopenharmony_cibegin 644 ../../../../../../../../tmp/test1
1537db96d56Sopenharmony_ci$86)C"@``
1547db96d56Sopenharmony_ci`
1557db96d56Sopenharmony_ciend
1567db96d56Sopenharmony_ci"""
1577db96d56Sopenharmony_ci        with self.assertRaisesRegex(uu.Error, 'directory'):
1587db96d56Sopenharmony_ci            uu.decode(io.BytesIO(relative_bad))
1597db96d56Sopenharmony_ci        if os.altsep:
1607db96d56Sopenharmony_ci            relative_bad_bs = relative_bad.replace(b'/', b'\\')
1617db96d56Sopenharmony_ci            with self.assertRaisesRegex(uu.Error, 'directory'):
1627db96d56Sopenharmony_ci                uu.decode(io.BytesIO(relative_bad_bs))
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci        absolute_bad = b"""\
1657db96d56Sopenharmony_cibegin 644 /tmp/test2
1667db96d56Sopenharmony_ci$86)C"@``
1677db96d56Sopenharmony_ci`
1687db96d56Sopenharmony_ciend
1697db96d56Sopenharmony_ci"""
1707db96d56Sopenharmony_ci        with self.assertRaisesRegex(uu.Error, 'directory'):
1717db96d56Sopenharmony_ci            uu.decode(io.BytesIO(absolute_bad))
1727db96d56Sopenharmony_ci        if os.altsep:
1737db96d56Sopenharmony_ci            absolute_bad_bs = absolute_bad.replace(b'/', b'\\')
1747db96d56Sopenharmony_ci            with self.assertRaisesRegex(uu.Error, 'directory'):
1757db96d56Sopenharmony_ci                uu.decode(io.BytesIO(absolute_bad_bs))
1767db96d56Sopenharmony_ci
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ciclass UUStdIOTest(unittest.TestCase):
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci    def setUp(self):
1817db96d56Sopenharmony_ci        self.stdin = sys.stdin
1827db96d56Sopenharmony_ci        self.stdout = sys.stdout
1837db96d56Sopenharmony_ci
1847db96d56Sopenharmony_ci    def tearDown(self):
1857db96d56Sopenharmony_ci        sys.stdin = self.stdin
1867db96d56Sopenharmony_ci        sys.stdout = self.stdout
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ci    def test_encode(self):
1897db96d56Sopenharmony_ci        sys.stdin = FakeIO(plaintext.decode("ascii"))
1907db96d56Sopenharmony_ci        sys.stdout = FakeIO()
1917db96d56Sopenharmony_ci        uu.encode("-", "-", "t1", 0o666)
1927db96d56Sopenharmony_ci        self.assertEqual(sys.stdout.getvalue(),
1937db96d56Sopenharmony_ci                         encodedtextwrapped(0o666, "t1").decode("ascii"))
1947db96d56Sopenharmony_ci
1957db96d56Sopenharmony_ci    def test_decode(self):
1967db96d56Sopenharmony_ci        sys.stdin = FakeIO(encodedtextwrapped(0o666, "t1").decode("ascii"))
1977db96d56Sopenharmony_ci        sys.stdout = FakeIO()
1987db96d56Sopenharmony_ci        uu.decode("-", "-")
1997db96d56Sopenharmony_ci        stdout = sys.stdout
2007db96d56Sopenharmony_ci        sys.stdout = self.stdout
2017db96d56Sopenharmony_ci        sys.stdin = self.stdin
2027db96d56Sopenharmony_ci        self.assertEqual(stdout.getvalue(), plaintext.decode("ascii"))
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ciclass UUFileTest(unittest.TestCase):
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ci    def setUp(self):
2077db96d56Sopenharmony_ci        # uu.encode() supports only ASCII file names
2087db96d56Sopenharmony_ci        self.tmpin  = os_helper.TESTFN_ASCII + "i"
2097db96d56Sopenharmony_ci        self.tmpout = os_helper.TESTFN_ASCII + "o"
2107db96d56Sopenharmony_ci        self.addCleanup(os_helper.unlink, self.tmpin)
2117db96d56Sopenharmony_ci        self.addCleanup(os_helper.unlink, self.tmpout)
2127db96d56Sopenharmony_ci
2137db96d56Sopenharmony_ci    def test_encode(self):
2147db96d56Sopenharmony_ci        with open(self.tmpin, 'wb') as fin:
2157db96d56Sopenharmony_ci            fin.write(plaintext)
2167db96d56Sopenharmony_ci
2177db96d56Sopenharmony_ci        with open(self.tmpin, 'rb') as fin:
2187db96d56Sopenharmony_ci            with open(self.tmpout, 'wb') as fout:
2197db96d56Sopenharmony_ci                uu.encode(fin, fout, self.tmpin, mode=0o644)
2207db96d56Sopenharmony_ci
2217db96d56Sopenharmony_ci        with open(self.tmpout, 'rb') as fout:
2227db96d56Sopenharmony_ci            s = fout.read()
2237db96d56Sopenharmony_ci        self.assertEqual(s, encodedtextwrapped(0o644, self.tmpin))
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_ci        # in_file and out_file as filenames
2267db96d56Sopenharmony_ci        uu.encode(self.tmpin, self.tmpout, self.tmpin, mode=0o644)
2277db96d56Sopenharmony_ci        with open(self.tmpout, 'rb') as fout:
2287db96d56Sopenharmony_ci            s = fout.read()
2297db96d56Sopenharmony_ci        self.assertEqual(s, encodedtextwrapped(0o644, self.tmpin))
2307db96d56Sopenharmony_ci
2317db96d56Sopenharmony_ci    # decode() calls chmod()
2327db96d56Sopenharmony_ci    @os_helper.skip_unless_working_chmod
2337db96d56Sopenharmony_ci    def test_decode(self):
2347db96d56Sopenharmony_ci        with open(self.tmpin, 'wb') as f:
2357db96d56Sopenharmony_ci            f.write(encodedtextwrapped(0o644, self.tmpout))
2367db96d56Sopenharmony_ci
2377db96d56Sopenharmony_ci        with open(self.tmpin, 'rb') as f:
2387db96d56Sopenharmony_ci            uu.decode(f)
2397db96d56Sopenharmony_ci
2407db96d56Sopenharmony_ci        with open(self.tmpout, 'rb') as f:
2417db96d56Sopenharmony_ci            s = f.read()
2427db96d56Sopenharmony_ci        self.assertEqual(s, plaintext)
2437db96d56Sopenharmony_ci        # XXX is there an xp way to verify the mode?
2447db96d56Sopenharmony_ci
2457db96d56Sopenharmony_ci    @os_helper.skip_unless_working_chmod
2467db96d56Sopenharmony_ci    def test_decode_filename(self):
2477db96d56Sopenharmony_ci        with open(self.tmpin, 'wb') as f:
2487db96d56Sopenharmony_ci            f.write(encodedtextwrapped(0o644, self.tmpout))
2497db96d56Sopenharmony_ci
2507db96d56Sopenharmony_ci        uu.decode(self.tmpin)
2517db96d56Sopenharmony_ci
2527db96d56Sopenharmony_ci        with open(self.tmpout, 'rb') as f:
2537db96d56Sopenharmony_ci            s = f.read()
2547db96d56Sopenharmony_ci        self.assertEqual(s, plaintext)
2557db96d56Sopenharmony_ci
2567db96d56Sopenharmony_ci    @os_helper.skip_unless_working_chmod
2577db96d56Sopenharmony_ci    def test_decodetwice(self):
2587db96d56Sopenharmony_ci        # Verify that decode() will refuse to overwrite an existing file
2597db96d56Sopenharmony_ci        with open(self.tmpin, 'wb') as f:
2607db96d56Sopenharmony_ci            f.write(encodedtextwrapped(0o644, self.tmpout))
2617db96d56Sopenharmony_ci        with open(self.tmpin, 'rb') as f:
2627db96d56Sopenharmony_ci            uu.decode(f)
2637db96d56Sopenharmony_ci
2647db96d56Sopenharmony_ci        with open(self.tmpin, 'rb') as f:
2657db96d56Sopenharmony_ci            self.assertRaises(uu.Error, uu.decode, f)
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_ci    @os_helper.skip_unless_working_chmod
2687db96d56Sopenharmony_ci    def test_decode_mode(self):
2697db96d56Sopenharmony_ci        # Verify that decode() will set the given mode for the out_file
2707db96d56Sopenharmony_ci        expected_mode = 0o444
2717db96d56Sopenharmony_ci        with open(self.tmpin, 'wb') as f:
2727db96d56Sopenharmony_ci            f.write(encodedtextwrapped(expected_mode, self.tmpout))
2737db96d56Sopenharmony_ci
2747db96d56Sopenharmony_ci        # make file writable again, so it can be removed (Windows only)
2757db96d56Sopenharmony_ci        self.addCleanup(os.chmod, self.tmpout, expected_mode | stat.S_IWRITE)
2767db96d56Sopenharmony_ci
2777db96d56Sopenharmony_ci        with open(self.tmpin, 'rb') as f:
2787db96d56Sopenharmony_ci            uu.decode(f)
2797db96d56Sopenharmony_ci
2807db96d56Sopenharmony_ci        self.assertEqual(
2817db96d56Sopenharmony_ci            stat.S_IMODE(os.stat(self.tmpout).st_mode),
2827db96d56Sopenharmony_ci            expected_mode
2837db96d56Sopenharmony_ci        )
2847db96d56Sopenharmony_ci
2857db96d56Sopenharmony_ci
2867db96d56Sopenharmony_ciif __name__=="__main__":
2877db96d56Sopenharmony_ci    unittest.main()
288