1""" 2Test lldb Python API for file handles. 3""" 4 5 6import os 7import io 8import re 9import sys 10from contextlib import contextmanager 11 12import lldb 13from lldbsuite.test import lldbtest 14from lldbsuite.test.decorators import * 15 16class OhNoe(Exception): 17 pass 18 19class BadIO(io.TextIOBase): 20 @property 21 def closed(self): 22 return False 23 def writable(self): 24 return True 25 def readable(self): 26 return True 27 def write(self, s): 28 raise OhNoe('OH NOE') 29 def read(self, n): 30 raise OhNoe("OH NOE") 31 def flush(self): 32 raise OhNoe('OH NOE') 33 34# This class will raise an exception while it's being 35# converted into a C++ object by swig 36class ReallyBadIO(io.TextIOBase): 37 def fileno(self): 38 return 999 39 def writable(self): 40 raise OhNoe("OH NOE!!!") 41 42class MutableBool(): 43 def __init__(self, value): 44 self.value = value 45 def set(self, value): 46 self.value = bool(value) 47 def __bool__(self): 48 return self.value 49 50class FlushTestIO(io.StringIO): 51 def __init__(self, mutable_flushed, mutable_closed): 52 super(FlushTestIO, self).__init__() 53 self.mut_flushed = mutable_flushed 54 self.mut_closed = mutable_closed 55 def close(self): 56 self.mut_closed.set(True) 57 return super(FlushTestIO, self).close() 58 def flush(self): 59 self.mut_flushed.set(True) 60 return super(FlushTestIO, self).flush() 61 62@contextmanager 63def replace_stdout(new): 64 old = sys.stdout 65 sys.stdout = new 66 try: 67 yield 68 finally: 69 sys.stdout = old 70 71def readStrippedLines(f): 72 def i(): 73 for line in f: 74 line = line.strip() 75 if line: 76 yield line 77 return list(i()) 78 79 80class FileHandleTestCase(lldbtest.TestBase): 81 82 NO_DEBUG_INFO_TESTCASE = True 83 mydir = lldbtest.Base.compute_mydir(__file__) 84 85 # The way normal tests evaluate debugger commands is 86 # by using a SBCommandInterpreter directly, which captures 87 # the output in a result object. For many of tests tests 88 # we want the debugger to write the output directly to 89 # its I/O streams like it would have done interactively. 90 # 91 # For this reason we also define handleCmd() here, even though 92 # it is similar to runCmd(). 93 94 def setUp(self): 95 super(FileHandleTestCase, self).setUp() 96 self.out_filename = self.getBuildArtifact('output') 97 self.in_filename = self.getBuildArtifact('input') 98 99 def tearDown(self): 100 super(FileHandleTestCase, self).tearDown() 101 for name in (self.out_filename, self.in_filename): 102 if os.path.exists(name): 103 os.unlink(name) 104 105 # Similar to runCmd(), but letting the debugger just print the results 106 # instead of collecting them. 107 def handleCmd(self, cmd, check=True, collect_result=True): 108 assert not check or collect_result 109 ret = lldb.SBCommandReturnObject() 110 if collect_result: 111 interpreter = self.dbg.GetCommandInterpreter() 112 interpreter.HandleCommand(cmd, ret) 113 else: 114 self.dbg.HandleCommand(cmd) 115 self.dbg.GetOutputFile().Flush() 116 self.dbg.GetErrorFile().Flush() 117 if collect_result and check: 118 self.assertTrue(ret.Succeeded()) 119 return ret.GetOutput() 120 121 122 @add_test_categories(['pyapi']) 123 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 124 def test_legacy_file_out_script(self): 125 with open(self.out_filename, 'w') as f: 126 self.dbg.SetOutputFileHandle(f, False) 127 # scripts print to output even if you capture the results 128 # I'm not sure I love that behavior, but that's the way 129 # it's been for a long time. That's why this test works 130 # even with collect_result=True. 131 self.handleCmd('script 1+1') 132 self.dbg.GetOutputFileHandle().write('FOO\n') 133 self.dbg.GetOutputFileHandle().flush() 134 with open(self.out_filename, 'r') as f: 135 self.assertEqual(readStrippedLines(f), ['2', 'FOO']) 136 137 138 @add_test_categories(['pyapi']) 139 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 140 def test_legacy_file_out(self): 141 with open(self.out_filename, 'w') as f: 142 self.dbg.SetOutputFileHandle(f, False) 143 self.handleCmd('p/x 3735928559', collect_result=False, check=False) 144 with open(self.out_filename, 'r') as f: 145 self.assertIn('deadbeef', f.read()) 146 147 @add_test_categories(['pyapi']) 148 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 149 def test_legacy_file_err_with_get(self): 150 with open(self.out_filename, 'w') as f: 151 self.dbg.SetErrorFileHandle(f, False) 152 self.handleCmd('lolwut', check=False, collect_result=False) 153 f2 = self.dbg.GetErrorFileHandle() 154 f2.write('FOOBAR\n') 155 f2.flush() 156 with open(self.out_filename, 'r') as f: 157 errors = f.read() 158 self.assertTrue(re.search(r'error:.*lolwut', errors)) 159 self.assertTrue(re.search(r'FOOBAR', errors)) 160 161 162 @add_test_categories(['pyapi']) 163 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 164 def test_legacy_file_err(self): 165 with open(self.out_filename, 'w') as f: 166 self.dbg.SetErrorFileHandle(f, False) 167 self.handleCmd('lol', check=False, collect_result=False) 168 with open(self.out_filename, 'r') as f: 169 self.assertIn("is not a valid command", f.read()) 170 171 172 @add_test_categories(['pyapi']) 173 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 174 def test_legacy_file_error(self): 175 with open(self.out_filename, 'w') as f: 176 self.dbg.SetErrorFileHandle(f, False) 177 self.handleCmd('lolwut', check=False, collect_result=False) 178 with open(self.out_filename, 'r') as f: 179 errors = f.read() 180 self.assertTrue(re.search(r'error:.*lolwut', errors)) 181 182 @add_test_categories(['pyapi']) 183 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 184 def test_sbfile_type_errors(self): 185 sbf = lldb.SBFile() 186 self.assertRaises(Exception, sbf.Write, None) 187 self.assertRaises(Exception, sbf.Read, None) 188 self.assertRaises(Exception, sbf.Read, b'this bytes is not mutable') 189 self.assertRaises(Exception, sbf.Write, u"ham sandwich") 190 self.assertRaises(Exception, sbf.Read, u"ham sandwich") 191 192 193 @add_test_categories(['pyapi']) 194 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 195 def test_sbfile_write_fileno(self): 196 with open(self.out_filename, 'w') as f: 197 sbf = lldb.SBFile(f.fileno(), "w", False) 198 self.assertTrue(sbf.IsValid()) 199 e, n = sbf.Write(b'FOO\nBAR') 200 self.assertTrue(e.Success()) 201 self.assertEqual(n, 7) 202 sbf.Close() 203 self.assertFalse(sbf.IsValid()) 204 with open(self.out_filename, 'r') as f: 205 self.assertEqual(readStrippedLines(f), ['FOO', 'BAR']) 206 207 208 @add_test_categories(['pyapi']) 209 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 210 def test_sbfile_write(self): 211 with open(self.out_filename, 'w') as f: 212 sbf = lldb.SBFile(f) 213 e, n = sbf.Write(b'FOO\n') 214 self.assertTrue(e.Success()) 215 self.assertEqual(n, 4) 216 sbf.Close() 217 self.assertTrue(f.closed) 218 with open(self.out_filename, 'r') as f: 219 self.assertEqual(f.read().strip(), 'FOO') 220 221 222 @add_test_categories(['pyapi']) 223 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 224 def test_sbfile_read_fileno(self): 225 with open(self.out_filename, 'w') as f: 226 f.write('FOO') 227 with open(self.out_filename, 'r') as f: 228 sbf = lldb.SBFile(f.fileno(), "r", False) 229 self.assertTrue(sbf.IsValid()) 230 buffer = bytearray(100) 231 e, n = sbf.Read(buffer) 232 self.assertTrue(e.Success()) 233 self.assertEqual(buffer[:n], b'FOO') 234 235 236 @add_test_categories(['pyapi']) 237 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 238 def test_sbfile_read(self): 239 with open(self.out_filename, 'w') as f: 240 f.write('foo') 241 with open(self.out_filename, 'r') as f: 242 sbf = lldb.SBFile(f) 243 buf = bytearray(100) 244 e, n = sbf.Read(buf) 245 self.assertTrue(e.Success()) 246 self.assertEqual(n, 3) 247 self.assertEqual(buf[:n], b'foo') 248 sbf.Close() 249 self.assertTrue(f.closed) 250 251 252 @add_test_categories(['pyapi']) 253 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 254 def test_fileno_out(self): 255 with open(self.out_filename, 'w') as f: 256 sbf = lldb.SBFile(f.fileno(), "w", False) 257 status = self.dbg.SetOutputFile(sbf) 258 self.assertTrue(status.Success()) 259 self.handleCmd('script 1+2') 260 self.dbg.GetOutputFile().Write(b'quux') 261 self.dbg.GetOutputFile().Flush() 262 263 with open(self.out_filename, 'r') as f: 264 self.assertEqual(readStrippedLines(f), ['3', 'quux']) 265 266 267 @add_test_categories(['pyapi']) 268 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 269 def test_fileno_help(self): 270 with open(self.out_filename, 'w') as f: 271 sbf = lldb.SBFile(f.fileno(), "w", False) 272 status = self.dbg.SetOutputFile(sbf) 273 self.assertTrue(status.Success()) 274 self.handleCmd("help help", collect_result=False, check=False) 275 with open(self.out_filename, 'r') as f: 276 self.assertTrue(re.search(r'Show a list of all debugger commands', f.read())) 277 278 279 @add_test_categories(['pyapi']) 280 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 281 def test_help(self): 282 with open(self.out_filename, 'w') as f: 283 status = self.dbg.SetOutputFile(lldb.SBFile(f)) 284 self.assertTrue(status.Success()) 285 self.handleCmd("help help", check=False, collect_result=False) 286 with open(self.out_filename, 'r') as f: 287 self.assertIn('Show a list of all debugger commands', f.read()) 288 289 290 @add_test_categories(['pyapi']) 291 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 292 def test_immediate(self): 293 with open(self.out_filename, 'w') as f: 294 ret = lldb.SBCommandReturnObject() 295 ret.SetImmediateOutputFile(f) 296 interpreter = self.dbg.GetCommandInterpreter() 297 interpreter.HandleCommand("help help", ret) 298 # make sure the file wasn't closed early. 299 f.write("\nQUUX\n") 300 ret = None # call destructor and flush streams 301 with open(self.out_filename, 'r') as f: 302 output = f.read() 303 self.assertTrue(re.search(r'Show a list of all debugger commands', output)) 304 self.assertTrue(re.search(r'QUUX', output)) 305 306 307 @add_test_categories(['pyapi']) 308 @skipIf(py_version=['<', (3,)]) 309 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 310 def test_immediate_string(self): 311 f = io.StringIO() 312 ret = lldb.SBCommandReturnObject() 313 ret.SetImmediateOutputFile(f) 314 interpreter = self.dbg.GetCommandInterpreter() 315 interpreter.HandleCommand("help help", ret) 316 # make sure the file wasn't closed early. 317 f.write("\nQUUX\n") 318 ret = None # call destructor and flush streams 319 output = f.getvalue() 320 self.assertTrue(re.search(r'Show a list of all debugger commands', output)) 321 self.assertTrue(re.search(r'QUUX', output)) 322 323 324 @add_test_categories(['pyapi']) 325 @skipIf(py_version=['<', (3,)]) 326 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 327 def test_immediate_sbfile_string(self): 328 f = io.StringIO() 329 ret = lldb.SBCommandReturnObject() 330 ret.SetImmediateOutputFile(lldb.SBFile(f)) 331 interpreter = self.dbg.GetCommandInterpreter() 332 interpreter.HandleCommand("help help", ret) 333 output = f.getvalue() 334 ret = None # call destructor and flush streams 335 # sbfile default constructor doesn't borrow the file 336 self.assertTrue(f.closed) 337 self.assertTrue(re.search(r'Show a list of all debugger commands', output)) 338 339 340 @add_test_categories(['pyapi']) 341 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 342 def test_fileno_inout(self): 343 with open(self.in_filename, 'w') as f: 344 f.write("help help\n") 345 346 with open(self.out_filename, 'w') as outf, open(self.in_filename, 'r') as inf: 347 348 outsbf = lldb.SBFile(outf.fileno(), "w", False) 349 status = self.dbg.SetOutputFile(outsbf) 350 self.assertTrue(status.Success()) 351 352 insbf = lldb.SBFile(inf.fileno(), "r", False) 353 status = self.dbg.SetInputFile(insbf) 354 self.assertTrue(status.Success()) 355 356 opts = lldb.SBCommandInterpreterRunOptions() 357 self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False) 358 self.dbg.GetOutputFile().Flush() 359 360 with open(self.out_filename, 'r') as f: 361 self.assertTrue(re.search(r'Show a list of all debugger commands', f.read())) 362 363 364 @add_test_categories(['pyapi']) 365 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 366 def test_inout(self): 367 with open(self.in_filename, 'w') as f: 368 f.write("help help\n") 369 with open(self.out_filename, 'w') as outf, \ 370 open(self.in_filename, 'r') as inf: 371 status = self.dbg.SetOutputFile(lldb.SBFile(outf)) 372 self.assertTrue(status.Success()) 373 status = self.dbg.SetInputFile(lldb.SBFile(inf)) 374 self.assertTrue(status.Success()) 375 opts = lldb.SBCommandInterpreterRunOptions() 376 self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False) 377 self.dbg.GetOutputFile().Flush() 378 with open(self.out_filename, 'r') as f: 379 output = f.read() 380 self.assertIn('Show a list of all debugger commands', output) 381 382 383 @add_test_categories(['pyapi']) 384 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 385 def test_binary_inout(self): 386 with open(self.in_filename, 'w') as f: 387 f.write("help help\n") 388 with open(self.out_filename, 'wb') as outf, \ 389 open(self.in_filename, 'rb') as inf: 390 status = self.dbg.SetOutputFile(lldb.SBFile(outf)) 391 self.assertTrue(status.Success()) 392 status = self.dbg.SetInputFile(lldb.SBFile(inf)) 393 self.assertTrue(status.Success()) 394 opts = lldb.SBCommandInterpreterRunOptions() 395 self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False) 396 self.dbg.GetOutputFile().Flush() 397 with open(self.out_filename, 'r') as f: 398 output = f.read() 399 self.assertIn('Show a list of all debugger commands', output) 400 401 402 @add_test_categories(['pyapi']) 403 @skipIf(py_version=['<', (3,)]) 404 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 405 def test_string_inout(self): 406 inf = io.StringIO("help help\np/x ~0\n") 407 outf = io.StringIO() 408 status = self.dbg.SetOutputFile(lldb.SBFile(outf)) 409 self.assertTrue(status.Success()) 410 status = self.dbg.SetInputFile(lldb.SBFile(inf)) 411 self.assertTrue(status.Success()) 412 opts = lldb.SBCommandInterpreterRunOptions() 413 self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False) 414 self.dbg.GetOutputFile().Flush() 415 output = outf.getvalue() 416 self.assertIn('Show a list of all debugger commands', output) 417 self.assertIn('0xfff', output) 418 419 420 @add_test_categories(['pyapi']) 421 @skipIf(py_version=['<', (3,)]) 422 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 423 def test_bytes_inout(self): 424 inf = io.BytesIO(b"help help\nhelp b\n") 425 outf = io.BytesIO() 426 status = self.dbg.SetOutputFile(lldb.SBFile(outf)) 427 self.assertTrue(status.Success()) 428 status = self.dbg.SetInputFile(lldb.SBFile(inf)) 429 self.assertTrue(status.Success()) 430 opts = lldb.SBCommandInterpreterRunOptions() 431 self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False) 432 self.dbg.GetOutputFile().Flush() 433 output = outf.getvalue() 434 self.assertIn(b'Show a list of all debugger commands', output) 435 self.assertIn(b'Set a breakpoint', output) 436 437 438 @add_test_categories(['pyapi']) 439 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 440 def test_fileno_error(self): 441 with open(self.out_filename, 'w') as f: 442 443 sbf = lldb.SBFile(f.fileno(), 'w', False) 444 status = self.dbg.SetErrorFile(sbf) 445 self.assertTrue(status.Success()) 446 447 self.handleCmd('lolwut', check=False, collect_result=False) 448 449 self.dbg.GetErrorFile().Write(b'\nzork\n') 450 451 with open(self.out_filename, 'r') as f: 452 errors = f.read() 453 self.assertTrue(re.search(r'error:.*lolwut', errors)) 454 self.assertTrue(re.search(r'zork', errors)) 455 456 457 @add_test_categories(['pyapi']) 458 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 459 def test_replace_stdout(self): 460 f = io.StringIO() 461 with replace_stdout(f): 462 self.assertEqual(sys.stdout, f) 463 self.handleCmd('script sys.stdout.write("lol")', 464 collect_result=False, check=False) 465 self.assertEqual(sys.stdout, f) 466 467 468 @add_test_categories(['pyapi']) 469 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 470 def test_replace_stdout_with_nonfile(self): 471 f = io.StringIO() 472 with replace_stdout(f): 473 class Nothing(): 474 pass 475 with replace_stdout(Nothing): 476 self.assertEqual(sys.stdout, Nothing) 477 self.handleCmd('script sys.stdout.write("lol")', 478 check=False, collect_result=False) 479 self.assertEqual(sys.stdout, Nothing) 480 sys.stdout.write(u"FOO") 481 self.assertEqual(f.getvalue(), "FOO") 482 483 484 @add_test_categories(['pyapi']) 485 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 486 def test_sbfile_write_borrowed(self): 487 with open(self.out_filename, 'w') as f: 488 sbf = lldb.SBFile.Create(f, borrow=True) 489 e, n = sbf.Write(b'FOO') 490 self.assertTrue(e.Success()) 491 self.assertEqual(n, 3) 492 sbf.Close() 493 self.assertFalse(f.closed) 494 f.write('BAR\n') 495 with open(self.out_filename, 'r') as f: 496 self.assertEqual(f.read().strip(), 'FOOBAR') 497 498 499 500 @add_test_categories(['pyapi']) 501 @skipIf(py_version=['<', (3,)]) 502 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 503 def test_sbfile_write_forced(self): 504 with open(self.out_filename, 'w') as f: 505 written = MutableBool(False) 506 orig_write = f.write 507 def mywrite(x): 508 written.set(True) 509 return orig_write(x) 510 f.write = mywrite 511 sbf = lldb.SBFile.Create(f, force_io_methods=True) 512 e, n = sbf.Write(b'FOO') 513 self.assertTrue(written) 514 self.assertTrue(e.Success()) 515 self.assertEqual(n, 3) 516 sbf.Close() 517 self.assertTrue(f.closed) 518 with open(self.out_filename, 'r') as f: 519 self.assertEqual(f.read().strip(), 'FOO') 520 521 522 @add_test_categories(['pyapi']) 523 @skipIf(py_version=['<', (3,)]) 524 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 525 def test_sbfile_write_forced_borrowed(self): 526 with open(self.out_filename, 'w') as f: 527 written = MutableBool(False) 528 orig_write = f.write 529 def mywrite(x): 530 written.set(True) 531 return orig_write(x) 532 f.write = mywrite 533 sbf = lldb.SBFile.Create(f, borrow=True, force_io_methods=True) 534 e, n = sbf.Write(b'FOO') 535 self.assertTrue(written) 536 self.assertTrue(e.Success()) 537 self.assertEqual(n, 3) 538 sbf.Close() 539 self.assertFalse(f.closed) 540 with open(self.out_filename, 'r') as f: 541 self.assertEqual(f.read().strip(), 'FOO') 542 543 544 @add_test_categories(['pyapi']) 545 @skipIf(py_version=['<', (3,)]) 546 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 547 def test_sbfile_write_string(self): 548 f = io.StringIO() 549 sbf = lldb.SBFile(f) 550 e, n = sbf.Write(b'FOO') 551 self.assertEqual(f.getvalue().strip(), "FOO") 552 self.assertTrue(e.Success()) 553 self.assertEqual(n, 3) 554 sbf.Close() 555 self.assertTrue(f.closed) 556 557 558 @add_test_categories(['pyapi']) 559 @skipIf(py_version=['<', (3,)]) 560 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 561 def test_string_out(self): 562 f = io.StringIO() 563 status = self.dbg.SetOutputFile(f) 564 self.assertTrue(status.Success()) 565 self.handleCmd("script 'foobar'") 566 self.assertEqual(f.getvalue().strip(), "'foobar'") 567 568 569 @add_test_categories(['pyapi']) 570 @skipIf(py_version=['<', (3,)]) 571 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 572 def test_string_error(self): 573 f = io.StringIO() 574 status = self.dbg.SetErrorFile(f) 575 self.assertTrue(status.Success()) 576 self.handleCmd('lolwut', check=False, collect_result=False) 577 errors = f.getvalue() 578 self.assertTrue(re.search(r'error:.*lolwut', errors)) 579 580 581 @add_test_categories(['pyapi']) 582 @skipIf(py_version=['<', (3,)]) 583 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 584 def test_sbfile_write_bytes(self): 585 f = io.BytesIO() 586 sbf = lldb.SBFile(f) 587 e, n = sbf.Write(b'FOO') 588 self.assertEqual(f.getvalue().strip(), b"FOO") 589 self.assertTrue(e.Success()) 590 self.assertEqual(n, 3) 591 sbf.Close() 592 self.assertTrue(f.closed) 593 594 @add_test_categories(['pyapi']) 595 @skipIf(py_version=['<', (3,)]) 596 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 597 def test_sbfile_read_string(self): 598 f = io.StringIO('zork') 599 sbf = lldb.SBFile(f) 600 buf = bytearray(100) 601 e, n = sbf.Read(buf) 602 self.assertTrue(e.Success()) 603 self.assertEqual(buf[:n], b'zork') 604 605 606 @add_test_categories(['pyapi']) 607 @skipIf(py_version=['<', (3,)]) 608 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 609 def test_sbfile_read_string_one_byte(self): 610 f = io.StringIO('z') 611 sbf = lldb.SBFile(f) 612 buf = bytearray(1) 613 e, n = sbf.Read(buf) 614 self.assertTrue(e.Fail()) 615 self.assertEqual(n, 0) 616 self.assertEqual(e.GetCString(), "can't read less than 6 bytes from a utf8 text stream") 617 618 619 @add_test_categories(['pyapi']) 620 @skipIf(py_version=['<', (3,)]) 621 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 622 def test_sbfile_read_bytes(self): 623 f = io.BytesIO(b'zork') 624 sbf = lldb.SBFile(f) 625 buf = bytearray(100) 626 e, n = sbf.Read(buf) 627 self.assertTrue(e.Success()) 628 self.assertEqual(buf[:n], b'zork') 629 630 631 @add_test_categories(['pyapi']) 632 @skipIf(py_version=['<', (3,)]) 633 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 634 def test_sbfile_out(self): 635 with open(self.out_filename, 'w') as f: 636 sbf = lldb.SBFile(f) 637 status = self.dbg.SetOutputFile(sbf) 638 self.assertTrue(status.Success()) 639 self.handleCmd('script 2+2') 640 with open(self.out_filename, 'r') as f: 641 self.assertEqual(f.read().strip(), '4') 642 643 644 @add_test_categories(['pyapi']) 645 @skipIf(py_version=['<', (3,)]) 646 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 647 def test_file_out(self): 648 with open(self.out_filename, 'w') as f: 649 status = self.dbg.SetOutputFile(f) 650 self.assertTrue(status.Success()) 651 self.handleCmd('script 2+2') 652 with open(self.out_filename, 'r') as f: 653 self.assertEqual(f.read().strip(), '4') 654 655 656 @add_test_categories(['pyapi']) 657 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 658 def test_sbfile_error(self): 659 with open(self.out_filename, 'w') as f: 660 sbf = lldb.SBFile(f) 661 status = self.dbg.SetErrorFile(sbf) 662 self.assertTrue(status.Success()) 663 self.handleCmd('lolwut', check=False, collect_result=False) 664 with open(self.out_filename, 'r') as f: 665 errors = f.read() 666 self.assertTrue(re.search(r'error:.*lolwut', errors)) 667 668 669 @add_test_categories(['pyapi']) 670 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 671 def test_file_error(self): 672 with open(self.out_filename, 'w') as f: 673 status = self.dbg.SetErrorFile(f) 674 self.assertTrue(status.Success()) 675 self.handleCmd('lolwut', check=False, collect_result=False) 676 with open(self.out_filename, 'r') as f: 677 errors = f.read() 678 self.assertTrue(re.search(r'error:.*lolwut', errors)) 679 680 681 @add_test_categories(['pyapi']) 682 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 683 def test_exceptions(self): 684 self.assertRaises(Exception, lldb.SBFile, None) 685 self.assertRaises(Exception, lldb.SBFile, "ham sandwich") 686 if sys.version_info[0] < 3: 687 self.assertRaises(Exception, lldb.SBFile, ReallyBadIO()) 688 else: 689 self.assertRaises(OhNoe, lldb.SBFile, ReallyBadIO()) 690 error, n = lldb.SBFile(BadIO()).Write(b"FOO") 691 self.assertEqual(n, 0) 692 self.assertTrue(error.Fail()) 693 self.assertIn('OH NOE', error.GetCString()) 694 error, n = lldb.SBFile(BadIO()).Read(bytearray(100)) 695 self.assertEqual(n, 0) 696 self.assertTrue(error.Fail()) 697 self.assertIn('OH NOE', error.GetCString()) 698 699 700 @add_test_categories(['pyapi']) 701 @skipIf(py_version=['<', (3,)]) 702 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 703 def test_exceptions_logged(self): 704 messages = list() 705 self.dbg.SetLoggingCallback(messages.append) 706 self.handleCmd('log enable lldb script') 707 self.dbg.SetOutputFile(lldb.SBFile(BadIO())) 708 self.handleCmd('script 1+1') 709 self.assertTrue(any('OH NOE' in msg for msg in messages)) 710 711 712 @add_test_categories(['pyapi']) 713 @skipIf(py_version=['<', (3,)]) 714 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 715 def test_flush(self): 716 flushed = MutableBool(False) 717 closed = MutableBool(False) 718 f = FlushTestIO(flushed, closed) 719 self.assertFalse(flushed) 720 self.assertFalse(closed) 721 sbf = lldb.SBFile(f) 722 self.assertFalse(flushed) 723 self.assertFalse(closed) 724 sbf = None 725 self.assertFalse(flushed) 726 self.assertTrue(closed) 727 self.assertTrue(f.closed) 728 729 flushed = MutableBool(False) 730 closed = MutableBool(False) 731 f = FlushTestIO(flushed, closed) 732 self.assertFalse(flushed) 733 self.assertFalse(closed) 734 sbf = lldb.SBFile.Create(f, borrow=True) 735 self.assertFalse(flushed) 736 self.assertFalse(closed) 737 sbf = None 738 self.assertTrue(flushed) 739 self.assertFalse(closed) 740 self.assertFalse(f.closed) 741 742 743 @add_test_categories(['pyapi']) 744 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 745 def test_fileno_flush(self): 746 with open(self.out_filename, 'w') as f: 747 f.write("foo") 748 sbf = lldb.SBFile(f) 749 sbf.Write(b'bar') 750 sbf = None 751 self.assertTrue(f.closed) 752 with open(self.out_filename, 'r') as f: 753 self.assertEqual(f.read(), 'foobar') 754 755 with open(self.out_filename, 'w+') as f: 756 f.write("foo") 757 sbf = lldb.SBFile.Create(f, borrow=True) 758 sbf.Write(b'bar') 759 sbf = None 760 self.assertFalse(f.closed) 761 f.seek(0) 762 self.assertEqual(f.read(), 'foobar') 763 764 765 @add_test_categories(['pyapi']) 766 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 767 def test_close(self): 768 with open(self.out_filename, 'w') as f: 769 status = self.dbg.SetOutputFile(f) 770 self.assertTrue(status.Success()) 771 self.handleCmd("help help", check=False, collect_result=False) 772 # make sure the file wasn't closed early. 773 f.write("\nZAP\n") 774 lldb.SBDebugger.Destroy(self.dbg) 775 # check that output file was closed when debugger was destroyed. 776 with self.assertRaises(ValueError): 777 f.write("\nQUUX\n") 778 with open(self.out_filename, 'r') as f: 779 output = f.read() 780 self.assertTrue(re.search(r'Show a list of all debugger commands', output)) 781 self.assertTrue(re.search(r'ZAP', output)) 782 783 784 @add_test_categories(['pyapi']) 785 @skipIf(py_version=['<', (3,)]) 786 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 787 def test_stdout(self): 788 f = io.StringIO() 789 status = self.dbg.SetOutputFile(f) 790 self.assertTrue(status.Success()) 791 self.handleCmd(r"script sys.stdout.write('foobar\n')") 792 self.assertEqual(f.getvalue().strip().split(), ["foobar", "7"]) 793 794 795 @add_test_categories(['pyapi']) 796 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 797 def test_stdout_file(self): 798 with open(self.out_filename, 'w') as f: 799 status = self.dbg.SetOutputFile(f) 800 self.assertTrue(status.Success()) 801 self.handleCmd(r"script sys.stdout.write('foobar\n')") 802 with open(self.out_filename, 'r') as f: 803 # In python2 sys.stdout.write() returns None, which 804 # the REPL will ignore, but in python3 it will 805 # return the number of bytes written, which the REPL 806 # will print out. 807 lines = [x for x in f.read().strip().split() if x != "7"] 808 self.assertEqual(lines, ["foobar"]) 809 810 811 @add_test_categories(['pyapi']) 812 @skipIf(py_version=['<', (3,)]) 813 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 814 def test_identity(self): 815 816 f = io.StringIO() 817 sbf = lldb.SBFile(f) 818 self.assertTrue(f is sbf.GetFile()) 819 sbf.Close() 820 self.assertTrue(f.closed) 821 822 f = io.StringIO() 823 sbf = lldb.SBFile.Create(f, borrow=True) 824 self.assertTrue(f is sbf.GetFile()) 825 sbf.Close() 826 self.assertFalse(f.closed) 827 828 with open(self.out_filename, 'w') as f: 829 sbf = lldb.SBFile(f) 830 self.assertTrue(f is sbf.GetFile()) 831 sbf.Close() 832 self.assertTrue(f.closed) 833 834 with open(self.out_filename, 'w') as f: 835 sbf = lldb.SBFile.Create(f, borrow=True) 836 self.assertFalse(f is sbf.GetFile()) 837 sbf.Write(b"foobar\n") 838 self.assertEqual(f.fileno(), sbf.GetFile().fileno()) 839 sbf.Close() 840 self.assertFalse(f.closed) 841 842 with open(self.out_filename, 'r') as f: 843 self.assertEqual("foobar", f.read().strip()) 844 845 with open(self.out_filename, 'wb') as f: 846 sbf = lldb.SBFile.Create(f, borrow=True, force_io_methods=True) 847 self.assertTrue(f is sbf.GetFile()) 848 sbf.Write(b"foobar\n") 849 self.assertEqual(f.fileno(), sbf.GetFile().fileno()) 850 sbf.Close() 851 self.assertFalse(f.closed) 852 853 with open(self.out_filename, 'r') as f: 854 self.assertEqual("foobar", f.read().strip()) 855 856 with open(self.out_filename, 'wb') as f: 857 sbf = lldb.SBFile.Create(f, force_io_methods=True) 858 self.assertTrue(f is sbf.GetFile()) 859 sbf.Write(b"foobar\n") 860 self.assertEqual(f.fileno(), sbf.GetFile().fileno()) 861 sbf.Close() 862 self.assertTrue(f.closed) 863 864 with open(self.out_filename, 'r') as f: 865 self.assertEqual("foobar", f.read().strip()) 866 867 868 @add_test_categories(['pyapi']) 869 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 870 def test_back_and_forth(self): 871 with open(self.out_filename, 'w') as f: 872 # at each step here we're borrowing the file, so we have to keep 873 # them all alive until the end. 874 sbf = lldb.SBFile.Create(f, borrow=True) 875 def i(sbf): 876 for i in range(10): 877 f = sbf.GetFile() 878 self.assertEqual(f.mode, "w") 879 yield f 880 sbf = lldb.SBFile.Create(f, borrow=True) 881 yield sbf 882 sbf.Write(str(i).encode('ascii') + b"\n") 883 files = list(i(sbf)) 884 with open(self.out_filename, 'r') as f: 885 self.assertEqual(list(range(10)), list(map(int, f.read().strip().split()))) 886 887 888 @add_test_categories(['pyapi']) 889 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 890 def test_set_filehandle_none(self): 891 self.assertRaises(Exception, self.dbg.SetOutputFile, None) 892 self.assertRaises(Exception, self.dbg.SetOutputFile, "ham sandwich") 893 self.assertRaises(Exception, self.dbg.SetOutputFileHandle, "ham sandwich") 894 self.assertRaises(Exception, self.dbg.SetInputFile, None) 895 self.assertRaises(Exception, self.dbg.SetInputFile, "ham sandwich") 896 self.assertRaises(Exception, self.dbg.SetInputFileHandle, "ham sandwich") 897 self.assertRaises(Exception, self.dbg.SetErrorFile, None) 898 self.assertRaises(Exception, self.dbg.SetErrorFile, "ham sandwich") 899 self.assertRaises(Exception, self.dbg.SetErrorFileHandle, "ham sandwich") 900 901 with open(self.out_filename, 'w') as f: 902 status = self.dbg.SetOutputFile(f) 903 self.assertTrue(status.Success()) 904 status = self.dbg.SetErrorFile(f) 905 self.assertTrue(status.Success()) 906 self.dbg.SetOutputFileHandle(None, False) 907 self.dbg.SetErrorFileHandle(None, False) 908 sbf = self.dbg.GetOutputFile() 909 if sys.version_info.major >= 3: 910 # python 2 lacks PyFile_FromFd, so GetFile() will 911 # have to duplicate the file descriptor and make a FILE* 912 # in order to convert a NativeFile it back to a python 913 # file. 914 self.assertEqual(sbf.GetFile().fileno(), 1) 915 sbf = self.dbg.GetErrorFile() 916 if sys.version_info.major >= 3: 917 self.assertEqual(sbf.GetFile().fileno(), 2) 918 with open(self.out_filename, 'r') as f: 919 status = self.dbg.SetInputFile(f) 920 self.assertTrue(status.Success()) 921 self.dbg.SetInputFileHandle(None, False) 922 sbf = self.dbg.GetInputFile() 923 if sys.version_info.major >= 3: 924 self.assertEqual(sbf.GetFile().fileno(), 0) 925 926 927 @add_test_categories(['pyapi']) 928 @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. 929 def test_sbstream(self): 930 931 with open(self.out_filename, 'w') as f: 932 stream = lldb.SBStream() 933 stream.RedirectToFile(f) 934 stream.Print("zork") 935 with open(self.out_filename, 'r') as f: 936 self.assertEqual(f.read().strip(), "zork") 937 938 with open(self.out_filename, 'w') as f: 939 stream = lldb.SBStream() 940 stream.RedirectToFileHandle(f, True) 941 stream.Print("Yendor") 942 with open(self.out_filename, 'r') as f: 943 self.assertEqual(f.read().strip(), "Yendor") 944 945 stream = lldb.SBStream() 946 f = open(self.out_filename, 'w') 947 stream.RedirectToFile(lldb.SBFile.Create(f, borrow=False)) 948 stream.Print("Frobozz") 949 stream = None 950 self.assertTrue(f.closed) 951 with open(self.out_filename, 'r') as f: 952 self.assertEqual(f.read().strip(), "Frobozz") 953