def assert_advance(escaped_str: str, expected_unescaped_str: str, expected_after: str, **kwargs) -> None: """Wrapper around advance_past_string_with_gdb_escapes to make testing easier """ ( actual_unescaped_str, after_quote_index, ) = advance_past_string_with_gdb_escapes(escaped_str, **kwargs) assert_match(actual_unescaped_str, expected_unescaped_str) actual_after = escaped_str[after_quote_index:] assert_match(actual_after, expected_after)
def test_api(self): raw_text = 'abc- "d" ""ef"" g' stream = StringStream(raw_text) assert stream.index == 0 assert stream.len == len(raw_text) buf = stream.read(1) assert_match(buf, "a") assert stream.index == 1 stream.seek(-1) assert stream.index == 0 buf = stream.advance_past_chars(['"']) buf = stream.advance_past_string_with_gdb_escapes() assert_match(buf, "d") buf = stream.advance_past_chars(['"']) buf = stream.advance_past_chars(['"']) buf = stream.advance_past_string_with_gdb_escapes() assert_match(buf, "ef") # read way past end to test it gracefully returns the # remainder of the string without failing buf = stream.read(50) assert_match(buf, '" g')
def test_parser(self): """Test that the parser returns dictionaries from gdb mi strings as expected""" # Test basic types assert_match(parse_response('^done'), { 'type': 'result', 'payload': None, 'message': 'done', "token": None }) assert_match(parse_response('~"done"'), { 'type': 'console', 'payload': 'done', 'message': None }) assert_match(parse_response('@"done"'), { "type": 'target', "payload": 'done', 'message': None }) assert_match(parse_response('&"done"'), { "type": 'log', "payload": 'done', 'message': None }) assert_match(parse_response('done'), { 'type': 'output', 'payload': 'done', 'message': None }) # Test escape sequences assert_match(parse_response('~""'), { "type": "console", "payload": "", 'message': None }) assert_match(parse_response('~"\b\f\n\r\t\""'), { "type": "console", "payload": '\b\f\n\r\t\"', 'message': None }) assert_match(parse_response('@""'), { "type": "target", "payload": "", 'message': None }) assert_match(parse_response('@"\b\f\n\r\t\""'), { "type": "target", "payload": '\b\f\n\r\t\"', 'message': None }) assert_match(parse_response('&""'), { "type": "log", "payload": "", 'message': None }) assert_match(parse_response('&"\b\f\n\r\t\""'), { "type": "log", "payload": '\b\f\n\r\t\"', 'message': None }) # Test that a dictionary with repeated keys (a gdb bug) is gracefully worked-around by pygdbmi # See https://sourceware.org/bugzilla/show_bug.cgi?id=22217 # and https://github.com/cs01/pygdbmi/issues/19 assert_match( parse_response( '^done,thread-ids={thread-id="3",thread-id="2",thread-id="1"}, current-thread-id="1",number-of-threads="3"' ), { "type": "result", "payload": { 'thread-ids': { 'thread-id': ['3', '2', '1'] }, 'current-thread-id': '1', 'number-of-threads': '3', }, 'message': 'done', 'token': None }) # Test a real world Dictionary assert_match( parse_response( '=breakpoint-modified,bkpt={number="1",empty_arr=[],type="breakpoint",disp="keep",enabled="y",addr="0x000000000040059c",func="main",file="hello.c",fullname="/home/git/pygdbmi/tests/sample_c_app/hello.c",line="9",thread-groups=["i1"],times="1",original-location="hello.c:9"}' ), { 'message': 'breakpoint-modified', 'payload': { 'bkpt': { 'addr': '0x000000000040059c', 'disp': 'keep', 'enabled': 'y', 'file': 'hello.c', 'fullname': '/home/git/pygdbmi/tests/sample_c_app/hello.c', 'func': 'main', 'line': '9', 'number': '1', 'empty_arr': [], 'original-location': 'hello.c:9', 'thread-groups': ['i1'], 'times': '1', 'type': 'breakpoint' } }, 'type': 'notify', 'token': None }) # Test records with token assert_match(parse_response('1342^done'), { 'type': 'result', 'payload': None, 'message': 'done', "token": 1342 })
def test_controller_buffer_randomized(self): """ The following code reads a sample gdb mi stream randomly to ensure partial output is read and that the buffer is working as expected on all streams. """ test_directory = os.path.dirname(os.path.abspath(__file__)) datafile_path = "%s/response_samples.txt" % (test_directory) gdbmi = GdbController() for stream in gdbmi._incomplete_output.keys(): responses = [] with open(datafile_path, "rb") as f: while True: n = random.randint(1, 100) # read random number of bytes to simulate incomplete responses gdb_mi_simulated_output = f.read(n) if gdb_mi_simulated_output == b"": break # EOF # let the controller try to parse this additional raw gdb output responses += gdbmi._get_responses_list( gdb_mi_simulated_output, stream) assert len(responses) == 141 # spot check a few assert_match( responses[0], { "message": None, "type": "console", "payload": u"0x00007fe2c5c58920 in __nanosleep_nocancel () at ../sysdeps/unix/syscall-template.S:81\\n", "stream": stream, }, ) if not USING_WINDOWS: # can't get this to pass in windows assert_match( responses[71], { "stream": stream, "message": u"done", "type": "result", "payload": None, "token": None, }, ) assert_match( responses[82], { "message": None, "type": "output", "payload": u"The inferior program printed this! Can you still parse it?", "stream": stream, }, ) assert_match( responses[137], { "stream": stream, "message": u"thread-group-exited", "type": "notify", "payload": { u"exit-code": u"0", u"id": u"i1" }, "token": None, }, ) assert_match( responses[138], { "stream": stream, "message": u"thread-group-started", "type": "notify", "payload": { u"pid": u"48337", u"id": u"i1" }, "token": None, }, ) assert_match( responses[139], { "stream": stream, "message": u"tsv-created", "type": "notify", "payload": { u"name": "trace_timestamp", u"initial": "0" }, "token": None, }, ) assert_match( responses[140], { "stream": stream, "message": u"tsv-created", "type": "notify", "payload": { u"name": "trace_timestamp", u"initial": "0" }, "token": None, }, ) for stream in gdbmi._incomplete_output.keys(): assert gdbmi._incomplete_output[stream] is None
def test_parser(self): """Test that the parser returns dictionaries from gdb mi strings as expected""" # Test basic types assert_match( parse_response("^done"), { "type": "result", "payload": None, "message": "done", "token": None }, ) assert_match( parse_response('~"done"'), { "type": "console", "payload": "done", "message": None }, ) assert_match( parse_response('@"done"'), { "type": "target", "payload": "done", "message": None }, ) assert_match( parse_response('&"done"'), { "type": "log", "payload": "done", "message": None }, ) assert_match( parse_response("done"), { "type": "output", "payload": "done", "message": None }, ) # Test escape sequences assert_match(parse_response('~""'), { "type": "console", "payload": "", "message": None }) assert_match( parse_response('~"\b\f\n\r\t""'), { "type": "console", "payload": '\b\f\n\r\t"', "message": None }, ) assert_match(parse_response('@""'), { "type": "target", "payload": "", "message": None }) assert_match( parse_response('@"\b\f\n\r\t""'), { "type": "target", "payload": '\b\f\n\r\t"', "message": None }, ) assert_match(parse_response('&""'), { "type": "log", "payload": "", "message": None }) assert_match( parse_response('&"\b\f\n\r\t""'), { "type": "log", "payload": '\b\f\n\r\t"', "message": None }, ) assert_match(parse_response('&"\\"'), { "type": "log", "payload": "\\", "message": None }) # test that an escaped backslash gets captured # Test that a dictionary with repeated keys (a gdb bug) is gracefully worked-around by pygdbmi # See https://sourceware.org/bugzilla/show_bug.cgi?id=22217 # and https://github.com/cs01/pygdbmi/issues/19 assert_match( parse_response( '^done,thread-ids={thread-id="3",thread-id="2",thread-id="1"}, current-thread-id="1",number-of-threads="3"' ), { "type": "result", "payload": { "thread-ids": { "thread-id": ["3", "2", "1"] }, "current-thread-id": "1", "number-of-threads": "3", }, "message": "done", "token": None, }, ) # Test a real world Dictionary assert_match( parse_response( '=breakpoint-modified,bkpt={number="1",empty_arr=[],type="breakpoint",disp="keep",enabled="y",addr="0x000000000040059c",func="main",file="hello.c",fullname="/home/git/pygdbmi/tests/sample_c_app/hello.c",line="9",thread-groups=["i1"],times="1",original-location="hello.c:9"}' ), { "message": "breakpoint-modified", "payload": { "bkpt": { "addr": "0x000000000040059c", "disp": "keep", "enabled": "y", "file": "hello.c", "fullname": "/home/git/pygdbmi/tests/sample_c_app/hello.c", "func": "main", "line": "9", "number": "1", "empty_arr": [], "original-location": "hello.c:9", "thread-groups": ["i1"], "times": "1", "type": "breakpoint", } }, "type": "notify", "token": None, }, ) # Test records with token assert_match( parse_response("1342^done"), { "type": "result", "payload": None, "message": "done", "token": 1342 }, ) # Test extra characters at end of dictionary are discarded (issue #30) assert_match( parse_response('=event,name="gdb"discardme'), { "type": "notify", "payload": { "name": "gdb" }, "message": "event", "token": None, }, )
def test_controller_buffer_randomized(self): """ The following code reads a sample gdb mi stream randomly to ensure partial output is read and that the buffer is working as expected on all streams. """ test_directory = os.path.dirname(os.path.abspath(__file__)) datafile_path = '%s/response_samples.txt' % (test_directory) gdbmi = GdbController() for stream in gdbmi._incomplete_output.keys(): responses = [] with open(datafile_path, 'rb') as f: while(True): n = random.randint(1, 100) # read random number of bytes to simulate incomplete responses gdb_mi_simulated_output = f.read(n) if gdb_mi_simulated_output == b'': break # EOF # let the controller try to parse this additional raw gdb output responses += gdbmi._get_responses_list(gdb_mi_simulated_output, stream, False) assert(len(responses) == 141) # spot check a few assert_match(responses[0], {'message': None, 'type': 'console', 'payload': u'0x00007fe2c5c58920 in __nanosleep_nocancel () at ../sysdeps/unix/syscall-template.S:81\\n', 'stream': stream}) if not USING_WINDOWS: # can't get this to pass in windows assert_match(responses[71], {'stream': stream, 'message': u'done', 'type': 'result', 'payload': None, 'token': None}) assert_match(responses[82], {'message': None, 'type': 'output', 'payload': u'The inferior program printed this! Can you still parse it?', 'stream': stream}) assert_match(responses[137], {'stream': stream, 'message': u'thread-group-exited', 'type': 'notify', 'payload': {u'exit-code': u'0', u'id': u'i1'}, 'token': None}) assert_match(responses[138], {'stream': stream, 'message': u'thread-group-started', 'type': 'notify', 'payload': {u'pid': u'48337', u'id': u'i1'}, 'token': None}) assert_match(responses[139], {'stream': stream, 'message': u'tsv-created', 'type': 'notify', 'payload': {u'name': 'trace_timestamp', u'initial': '0'}, 'token': None}) assert_match(responses[140], {'stream': stream, 'message': u'tsv-created', 'type': 'notify', 'payload': {u'name': 'trace_timestamp', u'initial': '0'}, 'token': None}) for stream in gdbmi._incomplete_output.keys(): assert(gdbmi._incomplete_output[stream] is None)
def test_parser(self): """Test that the parser returns dictionaries from gdb mi strings as expected""" # Test basic types assert_match(parse_response('^done'), { 'type': 'result', 'payload': None, 'message': 'done', "token": None }) assert_match(parse_response('~"done"'), { 'type': 'console', 'payload': 'done', 'message': None }) assert_match(parse_response('@"done"'), { "type": 'target', "payload": 'done', 'message': None }) assert_match(parse_response('&"done"'), { "type": 'log', "payload": 'done', 'message': None }) assert_match(parse_response('done'), { 'type': 'output', 'payload': 'done', 'message': None }) # Test escape sequences assert_match(parse_response('~""'), { "type": "console", "payload": "", 'message': None }) assert_match(parse_response('~"\b\f\n\r\t\""'), { "type": "console", "payload": '\b\f\n\r\t\"', 'message': None }) assert_match(parse_response('@""'), { "type": "target", "payload": "", 'message': None }) assert_match(parse_response('@"\b\f\n\r\t\""'), { "type": "target", "payload": '\b\f\n\r\t\"', 'message': None }) assert_match(parse_response('&""'), { "type": "log", "payload": "", 'message': None }) assert_match(parse_response('&"\b\f\n\r\t\""'), { "type": "log", "payload": '\b\f\n\r\t\"', 'message': None }) # Test a real world Dictionary assert_match( parse_response( '=breakpoint-modified,bkpt={number="1",empty_arr=[],type="breakpoint",disp="keep",enabled="y",addr="0x000000000040059c",func="main",file="hello.c",fullname="/home/git/pygdbmi/tests/sample_c_app/hello.c",line="9",thread-groups=["i1"],times="1",original-location="hello.c:9"}' ), { 'message': 'breakpoint-modified', 'payload': { 'bkpt': { 'addr': '0x000000000040059c', 'disp': 'keep', 'enabled': 'y', 'file': 'hello.c', 'fullname': '/home/git/pygdbmi/tests/sample_c_app/hello.c', 'func': 'main', 'line': '9', 'number': '1', 'empty_arr': [], 'original-location': 'hello.c:9', 'thread-groups': ['i1'], 'times': '1', 'type': 'breakpoint' } }, 'type': 'notify', 'token': None }) #Test records with token assert_match(parse_response('1342^done'), { 'type': 'result', 'payload': None, 'message': 'done', "token": 1342 })
def test_unescape(self) -> None: """Test the unescape function""" assert_match(unescape(r"a"), "a") assert_match(unescape(r"hello world"), "hello world") assert_match(unescape(r"hello\nworld"), "hello\nworld") assert_match(unescape(r"quote: <\">"), 'quote: <">') # UTF-8 text encoded as a sequence of octal characters. assert_match(unescape(self.GDB_ESCAPED_PIZZA), "\N{SLICE OF PIZZA}") # Similar but for a simple space. assert_match(unescape(self.GDB_ESCAPED_SPACE), " ") # Several escapes in the same string. assert_match( unescape(rf"\tmultiple\nescapes\tin\"the\'same\"string\"foo" rf"{self.GDB_ESCAPED_SPACE}bar{self.GDB_ESCAPED_PIZZA}"), '\tmultiple\nescapes\tin"the\'same"string"foo bar\N{SLICE OF PIZZA}', ) for bad in (r'"', r'"x', r'a"', r'a"x', r'a"x"foo'): with self.assertRaisesRegex(ValueError, "Unescaped quote found"): unescape(bad) for bad in (r"\777", r"\400"): with self.assertRaisesRegex(ValueError, "Invalid octal number"): unescape(bad) for bad in (r"\X", r"\1", r"\11"): with self.assertRaisesRegex(ValueError, "Invalid escape character"): unescape(bad)