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