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