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