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