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    @skipIf(py_version=['<', (3,)])
280    def test_immediate_string(self):
281        f = io.StringIO()
282        ret = lldb.SBCommandReturnObject()
283        ret.SetImmediateOutputFile(f)
284        interpreter = self.dbg.GetCommandInterpreter()
285        interpreter.HandleCommand("help help", ret)
286        # make sure the file wasn't closed early.
287        f.write("\nQUUX\n")
288        ret = None # call destructor and flush streams
289        output = f.getvalue()
290        self.assertTrue(re.search(r'Show a list of all debugger commands', output))
291        self.assertTrue(re.search(r'QUUX', output))
292
293
294    @skipIf(py_version=['<', (3,)])
295    def test_immediate_sbfile_string(self):
296        f = io.StringIO()
297        ret = lldb.SBCommandReturnObject()
298        ret.SetImmediateOutputFile(lldb.SBFile(f))
299        interpreter = self.dbg.GetCommandInterpreter()
300        interpreter.HandleCommand("help help", ret)
301        output = f.getvalue()
302        ret = None # call destructor and flush streams
303        # sbfile default constructor doesn't borrow the file
304        self.assertTrue(f.closed)
305        self.assertTrue(re.search(r'Show a list of all debugger commands', output))
306
307
308    def test_fileno_inout(self):
309        with open(self.in_filename, 'w') as f:
310            f.write("help help\n")
311
312        with open(self.out_filename, 'w') as outf, open(self.in_filename, 'r') as inf:
313
314            outsbf = lldb.SBFile(outf.fileno(), "w", False)
315            status = self.dbg.SetOutputFile(outsbf)
316            self.assertSuccess(status)
317
318            insbf = lldb.SBFile(inf.fileno(), "r", False)
319            status = self.dbg.SetInputFile(insbf)
320            self.assertSuccess(status)
321
322            opts = lldb.SBCommandInterpreterRunOptions()
323            self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
324            self.dbg.GetOutputFile().Flush()
325
326        with open(self.out_filename, 'r') as f:
327            self.assertTrue(re.search(r'Show a list of all debugger commands', f.read()))
328
329
330    def test_inout(self):
331        with open(self.in_filename, 'w') as f:
332            f.write("help help\n")
333        with  open(self.out_filename, 'w') as outf, \
334              open(self.in_filename, 'r') as inf:
335            status = self.dbg.SetOutputFile(lldb.SBFile(outf))
336            self.assertSuccess(status)
337            status = self.dbg.SetInputFile(lldb.SBFile(inf))
338            self.assertSuccess(status)
339            opts = lldb.SBCommandInterpreterRunOptions()
340            self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
341            self.dbg.GetOutputFile().Flush()
342        with open(self.out_filename, 'r') as f:
343            output = f.read()
344            self.assertIn('Show a list of all debugger commands', output)
345
346
347    def test_binary_inout(self):
348        with open(self.in_filename, 'w') as f:
349            f.write("help help\n")
350        with  open(self.out_filename, 'wb') as outf, \
351              open(self.in_filename, 'rb') as inf:
352            status = self.dbg.SetOutputFile(lldb.SBFile(outf))
353            self.assertSuccess(status)
354            status = self.dbg.SetInputFile(lldb.SBFile(inf))
355            self.assertSuccess(status)
356            opts = lldb.SBCommandInterpreterRunOptions()
357            self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
358            self.dbg.GetOutputFile().Flush()
359        with open(self.out_filename, 'r') as f:
360            output = f.read()
361            self.assertIn('Show a list of all debugger commands', output)
362
363
364    @skipIf(py_version=['<', (3,)])
365    def test_string_inout(self):
366        inf = io.StringIO("help help\np/x ~0\n")
367        outf = io.StringIO()
368        status = self.dbg.SetOutputFile(lldb.SBFile(outf))
369        self.assertSuccess(status)
370        status = self.dbg.SetInputFile(lldb.SBFile(inf))
371        self.assertSuccess(status)
372        opts = lldb.SBCommandInterpreterRunOptions()
373        self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
374        self.dbg.GetOutputFile().Flush()
375        output = outf.getvalue()
376        self.assertIn('Show a list of all debugger commands', output)
377        self.assertIn('0xfff', output)
378
379
380    @skipIf(py_version=['<', (3,)])
381    def test_bytes_inout(self):
382        inf = io.BytesIO(b"help help\nhelp b\n")
383        outf = io.BytesIO()
384        status = self.dbg.SetOutputFile(lldb.SBFile(outf))
385        self.assertSuccess(status)
386        status = self.dbg.SetInputFile(lldb.SBFile(inf))
387        self.assertSuccess(status)
388        opts = lldb.SBCommandInterpreterRunOptions()
389        self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
390        self.dbg.GetOutputFile().Flush()
391        output = outf.getvalue()
392        self.assertIn(b'Show a list of all debugger commands', output)
393        self.assertIn(b'Set a breakpoint', output)
394
395
396    def test_fileno_error(self):
397        with open(self.out_filename, 'w') as f:
398
399            sbf = lldb.SBFile(f.fileno(), 'w', False)
400            status = self.dbg.SetErrorFile(sbf)
401            self.assertSuccess(status)
402
403            self.handleCmd('lolwut', check=False, collect_result=False)
404
405            self.dbg.GetErrorFile().Write(b'\nzork\n')
406
407        with open(self.out_filename, 'r') as f:
408            errors = f.read()
409            self.assertTrue(re.search(r'error:.*lolwut', errors))
410            self.assertTrue(re.search(r'zork', errors))
411
412
413    def test_replace_stdout(self):
414        f = io.StringIO()
415        with replace_stdout(f):
416            self.assertEqual(sys.stdout, f)
417            self.handleCmd('script sys.stdout.write("lol")',
418                collect_result=False, check=False)
419            self.assertEqual(sys.stdout, f)
420
421
422    def test_replace_stdout_with_nonfile(self):
423        f = io.StringIO()
424        with replace_stdout(f):
425            class Nothing():
426                pass
427            with replace_stdout(Nothing):
428                self.assertEqual(sys.stdout, Nothing)
429                self.handleCmd('script sys.stdout.write("lol")',
430                    check=False, collect_result=False)
431                self.assertEqual(sys.stdout, Nothing)
432            sys.stdout.write(u"FOO")
433        self.assertEqual(f.getvalue(), "FOO")
434
435
436    def test_sbfile_write_borrowed(self):
437        with open(self.out_filename, 'w') as f:
438            sbf = lldb.SBFile.Create(f, borrow=True)
439            e, n = sbf.Write(b'FOO')
440            self.assertSuccess(e)
441            self.assertEqual(n, 3)
442            sbf.Close()
443            self.assertFalse(f.closed)
444            f.write('BAR\n')
445        with open(self.out_filename, 'r') as f:
446            self.assertEqual(f.read().strip(), 'FOOBAR')
447
448
449
450    @skipIf(py_version=['<', (3,)])
451    def test_sbfile_write_forced(self):
452        with open(self.out_filename, 'w') as f:
453            written = MutableBool(False)
454            orig_write = f.write
455            def mywrite(x):
456                written.set(True)
457                return orig_write(x)
458            f.write = mywrite
459            sbf = lldb.SBFile.Create(f, force_io_methods=True)
460            e, n = sbf.Write(b'FOO')
461            self.assertTrue(written)
462            self.assertSuccess(e)
463            self.assertEqual(n, 3)
464            sbf.Close()
465            self.assertTrue(f.closed)
466        with open(self.out_filename, 'r') as f:
467            self.assertEqual(f.read().strip(), 'FOO')
468
469
470    @skipIf(py_version=['<', (3,)])
471    def test_sbfile_write_forced_borrowed(self):
472        with open(self.out_filename, 'w') as f:
473            written = MutableBool(False)
474            orig_write = f.write
475            def mywrite(x):
476                written.set(True)
477                return orig_write(x)
478            f.write = mywrite
479            sbf = lldb.SBFile.Create(f, borrow=True, force_io_methods=True)
480            e, n = sbf.Write(b'FOO')
481            self.assertTrue(written)
482            self.assertSuccess(e)
483            self.assertEqual(n, 3)
484            sbf.Close()
485            self.assertFalse(f.closed)
486        with open(self.out_filename, 'r') as f:
487            self.assertEqual(f.read().strip(), 'FOO')
488
489
490    @skipIf(py_version=['<', (3,)])
491    def test_sbfile_write_string(self):
492        f = io.StringIO()
493        sbf = lldb.SBFile(f)
494        e, n = sbf.Write(b'FOO')
495        self.assertEqual(f.getvalue().strip(), "FOO")
496        self.assertSuccess(e)
497        self.assertEqual(n, 3)
498        sbf.Close()
499        self.assertTrue(f.closed)
500
501
502    @skipIf(py_version=['<', (3,)])
503    def test_string_out(self):
504        f = io.StringIO()
505        status = self.dbg.SetOutputFile(f)
506        self.assertSuccess(status)
507        self.handleCmd("script 'foobar'")
508        self.assertEqual(f.getvalue().strip(), "'foobar'")
509
510
511    @skipIf(py_version=['<', (3,)])
512    def test_string_error(self):
513        f = io.StringIO()
514        status = self.dbg.SetErrorFile(f)
515        self.assertSuccess(status)
516        self.handleCmd('lolwut', check=False, collect_result=False)
517        errors = f.getvalue()
518        self.assertTrue(re.search(r'error:.*lolwut', errors))
519
520
521    @skipIf(py_version=['<', (3,)])
522    def test_sbfile_write_bytes(self):
523        f = io.BytesIO()
524        sbf = lldb.SBFile(f)
525        e, n = sbf.Write(b'FOO')
526        self.assertEqual(f.getvalue().strip(), b"FOO")
527        self.assertSuccess(e)
528        self.assertEqual(n, 3)
529        sbf.Close()
530        self.assertTrue(f.closed)
531
532    @skipIf(py_version=['<', (3,)])
533    def test_sbfile_read_string(self):
534        f = io.StringIO('zork')
535        sbf = lldb.SBFile(f)
536        buf = bytearray(100)
537        e, n = sbf.Read(buf)
538        self.assertSuccess(e)
539        self.assertEqual(buf[:n], b'zork')
540
541
542    @skipIf(py_version=['<', (3,)])
543    def test_sbfile_read_string_one_byte(self):
544        f = io.StringIO('z')
545        sbf = lldb.SBFile(f)
546        buf = bytearray(1)
547        e, n = sbf.Read(buf)
548        self.assertTrue(e.Fail())
549        self.assertEqual(n, 0)
550        self.assertEqual(e.GetCString(), "can't read less than 6 bytes from a utf8 text stream")
551
552
553    @skipIf(py_version=['<', (3,)])
554    def test_sbfile_read_bytes(self):
555        f = io.BytesIO(b'zork')
556        sbf = lldb.SBFile(f)
557        buf = bytearray(100)
558        e, n = sbf.Read(buf)
559        self.assertSuccess(e)
560        self.assertEqual(buf[:n], b'zork')
561
562
563    @skipIf(py_version=['<', (3,)])
564    def test_sbfile_out(self):
565        with open(self.out_filename, 'w') as f:
566            sbf = lldb.SBFile(f)
567            status = self.dbg.SetOutputFile(sbf)
568            self.assertSuccess(status)
569            self.handleCmd('script 2+2')
570        with open(self.out_filename, 'r') as f:
571            self.assertEqual(f.read().strip(), '4')
572
573
574    @skipIf(py_version=['<', (3,)])
575    def test_file_out(self):
576        with open(self.out_filename, 'w') as f:
577            status = self.dbg.SetOutputFile(f)
578            self.assertSuccess(status)
579            self.handleCmd('script 2+2')
580        with open(self.out_filename, 'r') as f:
581            self.assertEqual(f.read().strip(), '4')
582
583
584    def test_sbfile_error(self):
585        with open(self.out_filename, 'w') as f:
586            sbf = lldb.SBFile(f)
587            status = self.dbg.SetErrorFile(sbf)
588            self.assertSuccess(status)
589            self.handleCmd('lolwut', check=False, collect_result=False)
590        with open(self.out_filename, 'r') as f:
591            errors = f.read()
592            self.assertTrue(re.search(r'error:.*lolwut', errors))
593
594
595    def test_file_error(self):
596        with open(self.out_filename, 'w') as f:
597            status = self.dbg.SetErrorFile(f)
598            self.assertSuccess(status)
599            self.handleCmd('lolwut', check=False, collect_result=False)
600        with open(self.out_filename, 'r') as f:
601            errors = f.read()
602            self.assertTrue(re.search(r'error:.*lolwut', errors))
603
604
605    def test_exceptions(self):
606        self.assertRaises(Exception, lldb.SBFile, None)
607        self.assertRaises(Exception, lldb.SBFile, "ham sandwich")
608        if sys.version_info[0] < 3:
609            self.assertRaises(Exception, lldb.SBFile, ReallyBadIO())
610        else:
611            self.assertRaises(OhNoe, lldb.SBFile, ReallyBadIO())
612            error, n = lldb.SBFile(BadIO()).Write(b"FOO")
613            self.assertEqual(n, 0)
614            self.assertTrue(error.Fail())
615            self.assertIn('OH NOE', error.GetCString())
616            error, n = lldb.SBFile(BadIO()).Read(bytearray(100))
617            self.assertEqual(n, 0)
618            self.assertTrue(error.Fail())
619            self.assertIn('OH NOE', error.GetCString())
620
621
622    @skipIf(py_version=['<', (3,)])
623    def test_exceptions_logged(self):
624        messages = list()
625        self.dbg.SetLoggingCallback(messages.append)
626        self.handleCmd('log enable lldb script')
627        self.dbg.SetOutputFile(lldb.SBFile(BadIO()))
628        self.handleCmd('script 1+1')
629        self.assertTrue(any('OH NOE' in msg for msg in messages))
630
631
632    @skipIf(py_version=['<', (3,)])
633    def test_flush(self):
634        flushed = MutableBool(False)
635        closed = MutableBool(False)
636        f = FlushTestIO(flushed, closed)
637        self.assertFalse(flushed)
638        self.assertFalse(closed)
639        sbf = lldb.SBFile(f)
640        self.assertFalse(flushed)
641        self.assertFalse(closed)
642        sbf = None
643        self.assertFalse(flushed)
644        self.assertTrue(closed)
645        self.assertTrue(f.closed)
646
647        flushed = MutableBool(False)
648        closed = MutableBool(False)
649        f = FlushTestIO(flushed, closed)
650        self.assertFalse(flushed)
651        self.assertFalse(closed)
652        sbf = lldb.SBFile.Create(f, borrow=True)
653        self.assertFalse(flushed)
654        self.assertFalse(closed)
655        sbf = None
656        self.assertTrue(flushed)
657        self.assertFalse(closed)
658        self.assertFalse(f.closed)
659
660
661    def test_fileno_flush(self):
662        with open(self.out_filename, 'w') as f:
663            f.write("foo")
664            sbf = lldb.SBFile(f)
665            sbf.Write(b'bar')
666            sbf = None
667            self.assertTrue(f.closed)
668        with open(self.out_filename, 'r') as f:
669            self.assertEqual(f.read(), 'foobar')
670
671        with open(self.out_filename, 'w+') as f:
672            f.write("foo")
673            sbf = lldb.SBFile.Create(f, borrow=True)
674            sbf.Write(b'bar')
675            sbf = None
676            self.assertFalse(f.closed)
677            f.seek(0)
678            self.assertEqual(f.read(), 'foobar')
679
680
681    def test_close(self):
682        with open(self.out_filename, 'w') as f:
683            status = self.dbg.SetOutputFile(f)
684            self.assertSuccess(status)
685            self.handleCmd("help help", check=False, collect_result=False)
686            # make sure the file wasn't closed early.
687            f.write("\nZAP\n")
688            lldb.SBDebugger.Destroy(self.dbg)
689            # check that output file was closed when debugger was destroyed.
690            with self.assertRaises(ValueError):
691                f.write("\nQUUX\n")
692        with open(self.out_filename, 'r') as f:
693            output = f.read()
694            self.assertTrue(re.search(r'Show a list of all debugger commands', output))
695            self.assertTrue(re.search(r'ZAP', output))
696
697
698    @skipIf(py_version=['<', (3,)])
699    def test_stdout(self):
700        f = io.StringIO()
701        status = self.dbg.SetOutputFile(f)
702        self.assertSuccess(status)
703        self.handleCmd(r"script sys.stdout.write('foobar\n')")
704        self.assertEqual(f.getvalue().strip().split(), ["foobar", "7"])
705
706
707    def test_stdout_file(self):
708        with open(self.out_filename, 'w') as f:
709            status = self.dbg.SetOutputFile(f)
710            self.assertSuccess(status)
711            self.handleCmd(r"script sys.stdout.write('foobar\n')")
712        with open(self.out_filename, 'r') as f:
713            # In python2 sys.stdout.write() returns None, which
714            # the REPL will ignore, but in python3 it will
715            # return the number of bytes written, which the REPL
716            # will print out.
717            lines = [x for x in f.read().strip().split() if x != "7"]
718            self.assertEqual(lines, ["foobar"])
719
720
721    @skipIf(py_version=['<', (3,)])
722    def test_identity(self):
723
724        f = io.StringIO()
725        sbf = lldb.SBFile(f)
726        self.assertTrue(f is sbf.GetFile())
727        sbf.Close()
728        self.assertTrue(f.closed)
729
730        f = io.StringIO()
731        sbf = lldb.SBFile.Create(f, borrow=True)
732        self.assertTrue(f is sbf.GetFile())
733        sbf.Close()
734        self.assertFalse(f.closed)
735
736        with open(self.out_filename, 'w') as f:
737            sbf = lldb.SBFile(f)
738            self.assertTrue(f is sbf.GetFile())
739            sbf.Close()
740            self.assertTrue(f.closed)
741
742        with open(self.out_filename, 'w') as f:
743            sbf = lldb.SBFile.Create(f, borrow=True)
744            self.assertFalse(f is sbf.GetFile())
745            sbf.Write(b"foobar\n")
746            self.assertEqual(f.fileno(), sbf.GetFile().fileno())
747            sbf.Close()
748            self.assertFalse(f.closed)
749
750        with open(self.out_filename, 'r') as f:
751            self.assertEqual("foobar", f.read().strip())
752
753        with open(self.out_filename, 'wb') as f:
754            sbf = lldb.SBFile.Create(f, borrow=True, force_io_methods=True)
755            self.assertTrue(f is sbf.GetFile())
756            sbf.Write(b"foobar\n")
757            self.assertEqual(f.fileno(), sbf.GetFile().fileno())
758            sbf.Close()
759            self.assertFalse(f.closed)
760
761        with open(self.out_filename, 'r') as f:
762            self.assertEqual("foobar", f.read().strip())
763
764        with open(self.out_filename, 'wb') as f:
765            sbf = lldb.SBFile.Create(f, force_io_methods=True)
766            self.assertTrue(f is sbf.GetFile())
767            sbf.Write(b"foobar\n")
768            self.assertEqual(f.fileno(), sbf.GetFile().fileno())
769            sbf.Close()
770            self.assertTrue(f.closed)
771
772        with open(self.out_filename, 'r') as f:
773            self.assertEqual("foobar", f.read().strip())
774
775
776    def test_back_and_forth(self):
777        with open(self.out_filename, 'w') as f:
778            # at each step here we're borrowing the file, so we have to keep
779            # them all alive until the end.
780            sbf = lldb.SBFile.Create(f, borrow=True)
781            def i(sbf):
782                for i in range(10):
783                    f = sbf.GetFile()
784                    self.assertEqual(f.mode, "w")
785                    yield f
786                    sbf = lldb.SBFile.Create(f, borrow=True)
787                    yield sbf
788                    sbf.Write(str(i).encode('ascii') + b"\n")
789            files = list(i(sbf))
790        with open(self.out_filename, 'r') as f:
791            self.assertEqual(list(range(10)), list(map(int, f.read().strip().split())))
792
793
794    def test_set_filehandle_none(self):
795        self.assertRaises(Exception, self.dbg.SetOutputFile, None)
796        self.assertRaises(Exception, self.dbg.SetOutputFile, "ham sandwich")
797        self.assertRaises(Exception, self.dbg.SetOutputFileHandle, "ham sandwich")
798        self.assertRaises(Exception, self.dbg.SetInputFile, None)
799        self.assertRaises(Exception, self.dbg.SetInputFile, "ham sandwich")
800        self.assertRaises(Exception, self.dbg.SetInputFileHandle, "ham sandwich")
801        self.assertRaises(Exception, self.dbg.SetErrorFile, None)
802        self.assertRaises(Exception, self.dbg.SetErrorFile, "ham sandwich")
803        self.assertRaises(Exception, self.dbg.SetErrorFileHandle, "ham sandwich")
804
805        with open(self.out_filename, 'w') as f:
806            status = self.dbg.SetOutputFile(f)
807            self.assertSuccess(status)
808            status = self.dbg.SetErrorFile(f)
809            self.assertSuccess(status)
810            self.dbg.SetOutputFileHandle(None, False)
811            self.dbg.SetErrorFileHandle(None, False)
812            sbf = self.dbg.GetOutputFile()
813            if sys.version_info.major >= 3:
814                # python 2 lacks PyFile_FromFd, so GetFile() will
815                # have to duplicate the file descriptor and make a FILE*
816                # in order to convert a NativeFile it back to a python
817                # file.
818                self.assertEqual(sbf.GetFile().fileno(), 1)
819            sbf = self.dbg.GetErrorFile()
820            if sys.version_info.major >= 3:
821                self.assertEqual(sbf.GetFile().fileno(), 2)
822        with open(self.out_filename, 'r') as f:
823            status = self.dbg.SetInputFile(f)
824            self.assertSuccess(status)
825            self.dbg.SetInputFileHandle(None, False)
826            sbf = self.dbg.GetInputFile()
827            if sys.version_info.major >= 3:
828                self.assertEqual(sbf.GetFile().fileno(), 0)
829
830
831    def test_sbstream(self):
832
833        with open(self.out_filename, 'w') as f:
834            stream = lldb.SBStream()
835            stream.RedirectToFile(f)
836            stream.Print("zork")
837        with open(self.out_filename, 'r') as f:
838            self.assertEqual(f.read().strip(), "zork")
839
840        with open(self.out_filename, 'w') as f:
841            stream = lldb.SBStream()
842            stream.RedirectToFileHandle(f, True)
843            stream.Print("Yendor")
844        with open(self.out_filename, 'r') as f:
845            self.assertEqual(f.read().strip(), "Yendor")
846
847        stream = lldb.SBStream()
848        f = open(self.out_filename,  'w')
849        stream.RedirectToFile(lldb.SBFile.Create(f, borrow=False))
850        stream.Print("Frobozz")
851        stream = None
852        self.assertTrue(f.closed)
853        with open(self.out_filename, 'r') as f:
854            self.assertEqual(f.read().strip(), "Frobozz")
855
856    def test_set_sbstream(self):
857        with open(self.out_filename, 'w') as outf:
858            outsbf = lldb.SBFile(outf.fileno(), "w", False)
859            status = self.dbg.SetOutputFile(outsbf)
860            self.assertSuccess(status)
861            self.dbg.SetInputString("help apropos\nhelp help\n")
862
863            opts = lldb.SBCommandInterpreterRunOptions()
864            self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
865            self.dbg.GetOutputFile().Flush()
866
867        with open(self.out_filename, 'r') as f:
868            output = f.read()
869            self.assertIn('Show a list of all debugger commands', output)
870            self.assertIn('List debugger commands related to a word', output)
871