diff --git a/lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py b/lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py --- a/lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py +++ b/lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py @@ -180,6 +180,19 @@ self.assertIn("is not a valid command", f.read()) + @add_test_categories(['pyapi']) + def test_legacy_file_error(self): + debugger = self.debugger + with open(self.out_filename, 'w') as f: + debugger.SetErrorFileHandle(f, False) + self.handleCmd('lolwut', check=False, collect_result=False) + debugger.GetErrorFileHandle().write('FOOBAR\n') + with open(self.out_filename, 'r') as f: + errors = f.read() + self.assertTrue(re.search(r'error:.*lolwut', errors)) + self.assertTrue(re.search(r'FOOBAR', errors)) + + @add_test_categories(['pyapi']) def test_sbfile_type_errors(self): sbf = lldb.SBFile() @@ -269,6 +282,17 @@ self.assertTrue(re.search(r'Show a list of all debugger commands', f.read())) + @add_test_categories(['pyapi']) + def test_help(self): + debugger = self.debugger + with open(self.out_filename, 'w') as f: + status = debugger.SetOutputFile(lldb.SBFile(f)) + self.assertTrue(status.Success()) + self.handleCmd("help help", check=False, collect_result=False) + with open(self.out_filename, 'r') as f: + self.assertIn('Show a list of all debugger commands', f.read()) + + @add_test_categories(['pyapi']) def test_immediate(self): with open(self.out_filename, 'w') as f: @@ -278,15 +302,44 @@ interpreter.HandleCommand("help help", ret) # make sure the file wasn't closed early. f.write("\nQUUX\n") - ret = None # call destructor and flush streams - with open(self.out_filename, 'r') as f: output = f.read() self.assertTrue(re.search(r'Show a list of all debugger commands', output)) self.assertTrue(re.search(r'QUUX', output)) + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME need SBFile interfaces on SBCommandReturnObject + def test_immediate_string(self): + f = io.StringIO() + ret = lldb.SBCommandReturnObject() + ret.SetImmediateOutputFile(f) + interpreter = self.debugger.GetCommandInterpreter() + interpreter.HandleCommand("help help", ret) + # make sure the file wasn't closed early. + f.write("\nQUUX\n") + ret = None # call destructor and flush streams + output = f.getvalue() + self.assertTrue(re.search(r'Show a list of all debugger commands', output)) + self.assertTrue(re.search(r'QUUX', output)) + + + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME need SBFile interfaces on SBCommandReturnObject + def test_immediate_sbfile_string(self): + f = io.StringIO() + ret = lldb.SBCommandReturnObject() + ret.SetImmediateOutputFile(lldb.SBFile(f)) + interpreter = self.debugger.GetCommandInterpreter() + interpreter.HandleCommand("help help", ret) + output = f.getvalue() + ret = None # call destructor and flush streams + # sbfile default constructor doesn't borrow the file + self.assertTrue(f.closed) + self.assertTrue(re.search(r'Show a list of all debugger commands', output)) + + @add_test_categories(['pyapi']) def test_fileno_inout(self): with open(self.in_filename, 'w') as f: @@ -310,6 +363,76 @@ self.assertTrue(re.search(r'Show a list of all debugger commands', f.read())) + @add_test_categories(['pyapi']) + def test_inout(self): + with open(self.in_filename, 'w') as f: + f.write("help help\n") + with open(self.out_filename, 'w') as outf, \ + open(self.in_filename, 'r') as inf: + status = self.debugger.SetOutputFile(lldb.SBFile(outf)) + self.assertTrue(status.Success()) + status = self.debugger.SetInputFile(lldb.SBFile(inf)) + self.assertTrue(status.Success()) + opts = lldb.SBCommandInterpreterRunOptions() + self.debugger.RunCommandInterpreter(True, False, opts, 0, False, False) + self.debugger.GetOutputFile().Flush() + with open(self.out_filename, 'r') as f: + output = f.read() + self.assertIn('Show a list of all debugger commands', output) + + + @add_test_categories(['pyapi']) + def test_binary_inout(self): + debugger = self.debugger + with open(self.in_filename, 'w') as f: + f.write("help help\n") + with open(self.out_filename, 'wb') as outf, \ + open(self.in_filename, 'rb') as inf: + status = debugger.SetOutputFile(lldb.SBFile(outf)) + self.assertTrue(status.Success()) + status = debugger.SetInputFile(lldb.SBFile(inf)) + self.assertTrue(status.Success()) + opts = lldb.SBCommandInterpreterRunOptions() + debugger.RunCommandInterpreter(True, False, opts, 0, False, False) + debugger.GetOutputFile().Flush() + with open(self.out_filename, 'r') as f: + output = f.read() + self.assertIn('Show a list of all debugger commands', output) + + + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME IOHandler still using FILE* + def test_string_inout(self): + inf = io.StringIO("help help\n") + outf = io.StringIO() + status = self.debugger.SetOutputFile(lldb.SBFile(outf)) + self.assertTrue(status.Success()) + status = self.debugger.SetInputFile(lldb.SBFile(inf)) + self.assertTrue(status.Success()) + opts = lldb.SBCommandInterpreterRunOptions() + self.debugger.RunCommandInterpreter(True, False, opts, 0, False, False) + self.debugger.GetOutputFile().Flush() + output = outf.getvalue() + self.assertIn('Show a list of all debugger commands', output) + + + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME IOHandler still using FILE* + def test_bytes_inout(self): + inf = io.BytesIO(b"help help\nhelp b\n") + outf = io.BytesIO() + status = self.debugger.SetOutputFile(lldb.SBFile(outf)) + self.assertTrue(status.Success()) + status = self.debugger.SetInputFile(lldb.SBFile(inf)) + self.assertTrue(status.Success()) + opts = lldb.SBCommandInterpreterRunOptions() + self.debugger.RunCommandInterpreter(True, False, opts, 0, False, False) + self.debugger.GetOutputFile().Flush() + output = outf.getvalue() + self.assertIn(b'Show a list of all debugger commands', output) + self.assertIn(b'Set a breakpoint', output) + + @add_test_categories(['pyapi']) def test_fileno_error(self): with open(self.out_filename, 'w') as f: @@ -339,6 +462,23 @@ self.assertEqual(sys.stdout, f) + @add_test_categories(['pyapi']) + @skipIf(True) #FIXME bug in ScriptInterpreterPython + def test_replace_stdout_with_nonfile(self): + debugger = self.debugger + f = io.StringIO() + with replace_stdout(f): + class Nothing(): + pass + with replace_stdout(Nothing): + self.assertEqual(sys.stdout, Nothing) + self.handleCmd('script sys.stdout.write("lol")', + check=False, collect_result=False) + self.assertEqual(sys.stdout, Nothing) + sys.stdout.write(u"FOO") + self.assertEqual(f.getvalue(), "FOO") + + @add_test_categories(['pyapi']) def test_sbfile_write_borrowed(self): with open(self.out_filename, 'w') as f: @@ -408,6 +548,31 @@ sbf.Close() self.assertTrue(f.closed) + + @add_test_categories(['pyapi']) + @skipIf(py_version=['<', (3,)]) + @skipIf(True) # fixme multiple problems with this + def test_string_out(self): + f = io.StringIO() + status = self.debugger.SetOutputFile(f) + self.assertTrue(status.Success()) + self.handleCmd("script 'foobar'") + self.assertEqual(f.getvalue().strip(), "'foobar'") + + + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME need FileSP version of SBDebugger::SetErrorFile + @skipIf(py_version=['<', (3,)]) + def test_string_error(self): + f = io.StringIO() + debugger = self.debugger + status = debugger.SetErrorFile(f) + self.assertTrue(status.Success()) + self.handleCmd('lolwut', check=False, collect_result=False) + errors = f.getvalue() + self.assertTrue(re.search(r'error:.*lolwut', errors)) + + @add_test_categories(['pyapi']) @skipIf(py_version=['<', (3,)]) def test_sbfile_write_bytes(self): @@ -466,6 +631,18 @@ self.assertEqual(f.read().strip(), '4') + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME need FileSP version of SBDebugger::SetErrorFile + @skipIf(py_version=['<', (3,)]) + def test_file_out(self): + with open(self.out_filename, 'w') as f: + status = self.debugger.SetOutputFile(f) + self.assertTrue(status.Success()) + self.handleCmd('script 2+2') + with open(self.out_filename, 'r') as f: + self.assertEqual(f.read().strip(), '4') + + @add_test_categories(['pyapi']) def test_sbfile_error(self): with open(self.out_filename, 'w') as f: @@ -478,6 +655,18 @@ self.assertTrue(re.search(r'error:.*lolwut', errors)) + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME need FileSP version of SBDebugger::SetErrorFile + def test_file_error(self): + with open(self.out_filename, 'w') as f: + status = self.debugger.SetErrorFile(f) + self.assertTrue(status.Success()) + self.handleCmd('lolwut', check=False, collect_result=False) + with open(self.out_filename, 'r') as f: + errors = f.read() + self.assertTrue(re.search(r'error:.*lolwut', errors)) + + @add_test_categories(['pyapi']) def test_exceptions(self): self.assertRaises(TypeError, lldb.SBFile, None) @@ -556,3 +745,91 @@ self.assertFalse(f.closed) f.seek(0) self.assertEqual(f.read(), 'foobar') + + + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME need FileSP version of SBDebugger::SetOutputFile + def test_close(self): + debugger = self.debugger + with open(self.out_filename, 'w') as f: + status = debugger.SetOutputFile(f) + self.assertTrue(status.Success()) + self.handleCmd("help help", check=False, collect_result=False) + # make sure the file wasn't closed early. + f.write("\nZAP\n") + lldb.SBDebugger.Destroy(debugger) + # check that output file was closed when debugger was destroyed. + with self.assertRaises(ValueError): + f.write("\nQUUX\n") + with open(self.out_filename, 'r') as f: + output = f.read() + self.assertTrue(re.search(r'Show a list of all debugger commands', output)) + self.assertTrue(re.search(r'ZAP', output)) + + + @add_test_categories(['pyapi']) + @skipIf(py_version=['<', (3,)]) + @skipIf(True) # FIXME need FileSP version of SBDebugger::SetOutputFile + def test_stdout(self): + f = io.StringIO() + status = self.debugger.SetOutputFile(f) + self.assertTrue(status.Success()) + self.handleCmd(r"script sys.stdout.write('foobar\n')") + self.assertEqual(f.getvalue().strip().split(), ["foobar", "7"]) + + + @add_test_categories(['pyapi']) + @skipIf(True) # FIXME implement SBFile::GetFile + @skipIf(py_version=['<', (3,)]) + def test_identity(self): + + f = io.StringIO() + sbf = lldb.SBFile(f) + self.assertTrue(f is sbf.GetFile()) + sbf.Close() + self.assertTrue(f.closed) + + f = io.StringIO() + sbf = lldb.SBFile.Create(f, borrow=True) + self.assertTrue(f is sbf.GetFile()) + sbf.Close() + self.assertFalse(f.closed) + + with open(self.out_filename, 'w') as f: + sbf = lldb.SBFile(f) + self.assertTrue(f is sbf.GetFile()) + sbf.Close() + self.assertTrue(f.closed) + + with open(self.out_filename, 'w') as f: + sbf = lldb.SBFile.Create(f, borrow=True) + self.assertFalse(f is sbf.GetFile()) + sbf.Write(b"foobar\n") + self.assertEqual(f.fileno(), sbf.GetFile().fileno()) + sbf.Close() + self.assertFalse(f.closed) + + with open(self.out_filename, 'r') as f: + self.assertEqual("foobar", f.read().strip()) + + with open(self.out_filename, 'wb') as f: + sbf = lldb.SBFile.Create(f, borrow=True, force_io_methods=True) + self.assertTrue(f is sbf.GetFile()) + sbf.Write(b"foobar\n") + self.assertEqual(f.fileno(), sbf.GetFile().fileno()) + sbf.Close() + self.assertFalse(f.closed) + + with open(self.out_filename, 'r') as f: + self.assertEqual("foobar", f.read().strip()) + + with open(self.out_filename, 'wb') as f: + sbf = lldb.SBFile.Create(f, force_io_methods=True) + self.assertTrue(f is sbf.GetFile()) + sbf.Write(b"foobar\n") + self.assertEqual(f.fileno(), sbf.GetFile().fileno()) + sbf.Close() + self.assertTrue(f.closed) + + with open(self.out_filename, 'r') as f: + self.assertEqual("foobar", f.read().strip()) diff --git a/lldb/scripts/Python/python-typemaps.swig b/lldb/scripts/Python/python-typemaps.swig --- a/lldb/scripts/Python/python-typemaps.swig +++ b/lldb/scripts/Python/python-typemaps.swig @@ -440,19 +440,19 @@ $1 = nullptr; else if (!lldb_private::PythonFile::Check($input)) { int fd = PyObject_AsFileDescriptor($input); + if (fd < 0 || PyErr_Occurred()) + return nullptr; PythonObject py_input(PyRefType::Borrowed, $input); PythonString py_mode = py_input.GetAttributeValue("mode").AsType(); - - if (-1 != fd && py_mode.IsValid()) { - FILE *f; - if ((f = fdopen(fd, py_mode.GetString().str().c_str()))) - $1 = f; - else - PyErr_SetString(PyExc_TypeError, strerror(errno)); - } else { - PyErr_SetString(PyExc_TypeError,"not a file-like object"); - return nullptr; - } + if (!py_mode.IsValid() || PyErr_Occurred()) + return nullptr; + FILE *f; + if ((f = fdopen(fd, py_mode.GetString().str().c_str()))) + $1 = f; + else { + PyErr_SetString(PyExc_TypeError, strerror(errno)); + return nullptr; + } } else {