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