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