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