class PugdebugMessageParserTest(unittest.TestCase): #maxDiff = None def setUp(self): self.parser = PugdebugMessageParser() def test_parse_init_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" fileuri="file:///home/robert/www/pxdebug/index.php" language="PHP" protocol_version="1.0" appid="3696" idekey="1"><engine version="2.2.7"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2015 by Derick Rethans]]></copyright></init>' result = self.parser.parse_init_message(message) expected = { 'fileuri': '/home/robert/www/pxdebug/index.php', 'idekey': '1', 'engine': 'Xdebug 2.2.7', 'author': 'Derick Rethans', 'url': 'http://xdebug.org', 'copyright': 'Copyright (c) 2002-2015 by Derick Rethans' } self.assertEqual(expected, result) def test_parse_status_break_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="step_into" transaction_id="1" status="break" reason="ok"><xdebug:message filename="file:///home/robert/www/pxdebug/index.php" lineno="3"></xdebug:message></response>' result = self.parser.parse_continuation_message(message) expected = { 'command': 'step_into', 'transaction_id': '1', 'status': 'break', 'reason': 'ok', 'filename': '/home/robert/www/pxdebug/index.php', 'lineno': '3' } self.assertEqual(expected, result) def test_parse_status_stopping_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="step_into" transaction_id="28" status="stopping" reason="ok"></response>' result = self.parser.parse_continuation_message(message) expected = { 'command': 'step_into', 'transaction_id': '28', 'status': 'stopping', 'reason': 'ok' } self.assertEqual(expected, result) def test_parse_variable_contexts_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="context_names" transaction_id="2"><context name="Locals" id="0"></context><context name="Superglobals" id="1"></context></response>' result = self.parser.parse_variable_contexts_message(message) expected = [{ 'name': 'Locals', 'id': '0' }, { 'name': 'Superglobals', 'id': '1' }] self.assertEqual(expected, result) def test_parse_variables_simple_local(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="context_get" transaction_id="2;" context="0"><property name="$i" fullname="$i" type="int"><![CDATA[1]]></property></response>' result = self.parser.parse_variables_message(message) expected = [{'name': '$i', 'type': 'int', 'value': '1'}] self.assertEqual(expected, result) def test_parse_variables_superglobals(self): file = open('./pugdebug/tests/_files/superglobals.xml', 'r') message = file.read() file.close() result = self.parser.parse_variables_message(message) expected = [{ 'name': '$_COOKIE', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_ENV', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_FILES', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_GET', 'type': 'array', 'variables': [{ 'name': 'XDEBUG_SESSION_START', 'type': 'string', 'encoding': 'base64', 'value': 'MQ==', 'size': '1' }], 'numchildren': '1' }, { 'name': '$_POST', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_REQUEST', 'type': 'array', 'variables': [{ 'name': 'XDEBUG_SESSION_START', 'type': 'string', 'encoding': 'base64', 'value': 'MQ==', 'size': '1' }], 'numchildren': '1' }, { 'name': '$_SERVER', 'type': 'array', 'variables': [{ 'name': 'UNIQUE_ID', 'type': 'string', 'encoding': 'base64', 'value': 'VlBBajZpYWgxVGtGQGlDVzFuNzhCZ0FBQUFB', 'size': '27' }, { 'name': 'HTTP_HOST', 'type': 'string', 'encoding': 'base64', 'value': 'bG9jYWxob3N0', 'size': '9' }, { 'name': 'HTTP_USER_AGENT', 'type': 'string', 'encoding': 'base64', 'value': 'TW96aWxsYS81LjAgKFgxMTsgRmVkb3JhOyBMaW51eCB4ODZfNjQ7IHJ2OjM2LjApIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvMzYuMA==', 'size': '76' }, { 'name': 'HTTP_ACCEPT', 'type': 'string', 'encoding': 'base64', 'value': 'dGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksKi8qO3E9MC44', 'size': '63' }, { 'name': 'HTTP_ACCEPT_LANGUAGE', 'type': 'string', 'encoding': 'base64', 'value': 'ZW4tVVMsZW47cT0wLjU=', 'size': '14' }, { 'name': 'HTTP_ACCEPT_ENCODING', 'type': 'string', 'encoding': 'base64', 'value': 'Z3ppcCwgZGVmbGF0ZQ==', 'size': '13' }, { 'name': 'HTTP_CONNECTION', 'type': 'string', 'encoding': 'base64', 'value': 'a2VlcC1hbGl2ZQ==', 'size': '10' }, { 'name': 'PATH', 'type': 'string', 'encoding': 'base64', 'value': 'L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbg==', 'size': '49' }, { 'name': 'SERVER_SIGNATURE', 'type': 'string', 'encoding': 'base64', 'value': None, 'size': '0' }, { 'name': 'SERVER_SOFTWARE', 'type': 'string', 'encoding': 'base64', 'value': 'QXBhY2hlLzIuNC4xMCAoRmVkb3JhKSBPcGVuU1NMLzEuMC4xay1maXBzIFBIUC81LjYuNg==', 'size': '52' }, { 'name': 'SERVER_NAME', 'type': 'string', 'encoding': 'base64', 'value': 'bG9jYWxob3N0', 'size': '9' }, { 'name': 'SERVER_ADDR', 'type': 'string', 'encoding': 'base64', 'value': 'MTI3LjAuMC4x', 'size': '9' }, { 'name': 'SERVER_PORT', 'type': 'string', 'encoding': 'base64', 'value': 'ODA=', 'size': '2' }, { 'name': 'REMOTE_ADDR', 'type': 'string', 'encoding': 'base64', 'value': 'MTI3LjAuMC4x', 'size': '9' }, { 'name': 'DOCUMENT_ROOT', 'type': 'string', 'encoding': 'base64', 'value': 'L2hvbWUvcm9iZXJ0L3d3dy9weGRlYnVn', 'size': '24' }, { 'name': 'REQUEST_SCHEME', 'type': 'string', 'encoding': 'base64', 'value': 'aHR0cA==', 'size': '4' }, { 'name': 'CONTEXT_PREFIX', 'type': 'string', 'encoding': 'base64', 'value': None, 'size': '0' }, { 'name': 'CONTEXT_DOCUMENT_ROOT', 'type': 'string', 'encoding': 'base64', 'value': 'L2hvbWUvcm9iZXJ0L3d3dy9weGRlYnVn', 'size': '24' }, { 'name': 'SERVER_ADMIN', 'type': 'string', 'encoding': 'base64', 'value': 'd2VibWFzdGVyQGxvY2FsaG9zdA==', 'size': '19' }, { 'name': 'SCRIPT_FILENAME', 'type': 'string', 'encoding': 'base64', 'value': 'L2hvbWUvcm9iZXJ0L3d3dy9weGRlYnVnL2luZGV4LnBocA==', 'size': '34' }, { 'name': 'REMOTE_PORT', 'type': 'string', 'encoding': 'base64', 'value': 'NTg3MDI=', 'size': '5' }, { 'name': 'GATEWAY_INTERFACE', 'type': 'string', 'encoding': 'base64', 'value': 'Q0dJLzEuMQ==', 'size': '7' }, { 'name': 'SERVER_PROTOCOL', 'type': 'string', 'encoding': 'base64', 'value': 'SFRUUC8xLjE=', 'size': '8' }, { 'name': 'REQUEST_METHOD', 'type': 'string', 'encoding': 'base64', 'value': 'R0VU', 'size': '3' }, { 'name': 'QUERY_STRING', 'type': 'string', 'encoding': 'base64', 'value': 'WERFQlVHX1NFU1NJT05fU1RBUlQ9MQ==', 'size': '22' }, { 'name': 'REQUEST_URI', 'type': 'string', 'encoding': 'base64', 'value': 'Lz9YREVCVUdfU0VTU0lPTl9TVEFSVD0x', 'size': '24' }, { 'name': 'SCRIPT_NAME', 'type': 'string', 'encoding': 'base64', 'value': 'L2luZGV4LnBocA==', 'size': '10' }, { 'name': 'PHP_SELF', 'type': 'string', 'encoding': 'base64', 'value': 'L2luZGV4LnBocA==', 'size': '10' }, { 'name': 'REQUEST_TIME_FLOAT', 'type': 'float', 'value': '1425023978.289' }, { 'name': 'REQUEST_TIME', 'type': 'int', 'value': '1425023978' }], 'numchildren': '30' }] #self.assertEqual(expected, result) self.assertEqual(expected[0], result[0]) self.assertEqual(expected[1], result[1]) self.assertEqual(expected[2], result[2]) self.assertEqual(expected[3], result[3]) self.assertEqual(expected[4], result[4]) self.assertEqual(expected[5], result[5]) self.assertEqual(expected[6], result[6]) self.assertEqual(expected[6]['variables'][28], result[6]['variables'][28]) def test_parse_successful_breakpoint_set_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="9" id="32310001"></response>' result = self.parser.parse_breakpoint_set_message(message) self.assertTrue(result) def test_parse_unsuccessful_breakpoint_set_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="9" status="break" reason="ok"><error code="3"><message><![CDATA[invalid or missing options]]></message></error></response>' result = self.parser.parse_breakpoint_set_message(message) self.assertFalse(result) def test_parse_successful_breakpoint_remove_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_remove" transaction_id="11"><breakpoint type="line" filename="file:///home/robert/www/pugdebug/index.php" lineno="10" state="enabled" hit_count="0" hit_value="0" id="41240003"></breakpoint></response>' result = self.parser.parse_breakpoint_remove_message(message) expected = 41240003 self.assertEqual(expected, result) def test_parse_unsuccessful_breakpoint_remove_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_remove" transaction_id="11" status="break" reason="ok"><error code="205"><message><![CDATA[no such breakpoint]]></message></error></response>' result = self.parser.parse_breakpoint_remove_message(message) self.assertFalse(result) def test_parse_breakpoint_list_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_list" transaction_id="12"><breakpoint type="line" filename="file:///home/robert/www/pugdebug/index.php" lineno="3" state="enabled" hit_count="0" hit_value="0" id="32350002"></breakpoint><breakpoint type="line" filename="file:///home/robert/www/pugdebug/index.php" lineno="10" state="enabled" hit_count="0" hit_value="0" id="32350001"></breakpoint></response>' result = self.parser.parse_breakpoint_list_message(message) expected = [{ 'filename': '/home/robert/www/pugdebug/index.php', 'id': '32350002', 'lineno': '3', 'state': 'enabled', 'type': 'line' }, { 'filename': '/home/robert/www/pugdebug/index.php', 'id': '32350001', 'lineno': '10', 'state': 'enabled', 'type': 'line' }] self.assertEqual(expected, result) def test_parse_stacktraces_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="stack_get" transaction_id="118"><stack where="{main}" level="0" type="file" filename="file:///home/robert/www/pugdebug/index.php" lineno="30"></stack></response>' result = self.parser.parse_stacktraces_message(message) expected = [{ 'filename': '/home/robert/www/pugdebug/index.php', 'lineno': '30', 'where': '{main}', 'level': '0' }] self.assertEqual(expected, result) message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="stack_get" transaction_id="22"><stack where="include_once" level="0" type="file" filename="file:///home/robert/www/pugdebug/dir/foo.php" lineno="3"></stack><stack where="include_once" level="1" type="file" filename="file:///home/robert/www/pugdebug/file.php" lineno="3"></stack><stack where="{main}" level="2" type="file" filename="file:///home/robert/www/pugdebug/index.php" lineno="3"></stack></response>' result = self.parser.parse_stacktraces_message(message) expected = [{ 'filename': '/home/robert/www/pugdebug/dir/foo.php', 'lineno': '3', 'where': 'include_once', 'level': '0' }, { 'filename': '/home/robert/www/pugdebug/file.php', 'lineno': '3', 'where': 'include_once', 'level': '1' }, { 'filename': '/home/robert/www/pugdebug/index.php', 'lineno': '3', 'where': '{main}', 'level': '2' }] self.assertEqual(expected, result) message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="stack_get" transaction_id="54"><stack where="foo" level="0" type="file" filename="file:///home/robert/www/pugdebug/dir/foo.php" lineno="4"></stack><stack where="call_foo" level="1" type="file" filename="file:///home/robert/www/pugdebug/file.php" lineno="6"></stack><stack where="{main}" level="2" type="file" filename="file:///home/robert/www/pugdebug/index.php" lineno="34"></stack></response>' result = self.parser.parse_stacktraces_message(message) expected = [{ 'filename': '/home/robert/www/pugdebug/dir/foo.php', 'lineno': '4', 'where': 'foo', 'level': '0' }, { 'filename': '/home/robert/www/pugdebug/file.php', 'lineno': '6', 'where': 'call_foo', 'level': '1' }, { 'filename': '/home/robert/www/pugdebug/index.php', 'lineno': '34', 'where': '{main}', 'level': '2' }] self.assertEqual(expected, result)
class PugdebugMessageParserTest(unittest.TestCase): #maxDiff = None def setUp(self): self.parser = PugdebugMessageParser() def test_parse_init_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" fileuri="file:///home/robert/www/pxdebug/index.php" language="PHP" protocol_version="1.0" appid="3696" idekey="1"><engine version="2.2.7"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2015 by Derick Rethans]]></copyright></init>' result = self.parser.parse_init_message(message) expected = { 'fileuri': '/home/robert/www/pxdebug/index.php', 'idekey': '1', 'engine': 'Xdebug 2.2.7', 'author': 'Derick Rethans', 'url': 'http://xdebug.org', 'copyright': 'Copyright (c) 2002-2015 by Derick Rethans' } self.assertEqual(expected, result) def test_parse_status_break_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="step_into" transaction_id="1" status="break" reason="ok"><xdebug:message filename="file:///home/robert/www/pxdebug/index.php" lineno="3"></xdebug:message></response>' result = self.parser.parse_continuation_message(message) expected = { 'command': 'step_into', 'transaction_id': '1', 'status': 'break', 'reason': 'ok', 'filename': '/home/robert/www/pxdebug/index.php', 'lineno': '3' } self.assertEqual(expected, result) def test_parse_status_stopping_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="step_into" transaction_id="28" status="stopping" reason="ok"></response>' result = self.parser.parse_continuation_message(message) expected = { 'command': 'step_into', 'transaction_id': '28', 'status': 'stopping', 'reason': 'ok' } self.assertEqual(expected, result) def test_parse_variable_contexts_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="context_names" transaction_id="2"><context name="Locals" id="0"></context><context name="Superglobals" id="1"></context></response>' result = self.parser.parse_variable_contexts_message(message) expected = [ { 'name': 'Locals', 'id': '0' }, { 'name': 'Superglobals', 'id': '1' } ] self.assertEqual(expected, result) def test_parse_variables_simple_local(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="context_get" transaction_id="2;" context="0"><property name="$i" fullname="$i" type="int"><![CDATA[1]]></property></response>' result = self.parser.parse_variables_message(message) expected = [ { 'name': '$i', 'type': 'int', 'value': '1' } ] self.assertEqual(expected, result) def test_parse_variables_superglobals(self): file = open('./pugdebug/tests/_files/superglobals.xml', 'r') message = file.read() file.close() result = self.parser.parse_variables_message(message) expected = [ { 'name': '$_COOKIE', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_ENV', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_FILES', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_GET', 'type': 'array', 'variables': [ { 'name': 'XDEBUG_SESSION_START', 'type': 'string', 'encoding': 'base64', 'value': 'MQ==', 'size': '1' } ], 'numchildren': '1' }, { 'name': '$_POST', 'type': 'array', 'variables': [], 'numchildren': '0' }, { 'name': '$_REQUEST', 'type': 'array', 'variables': [ { 'name': 'XDEBUG_SESSION_START', 'type': 'string', 'encoding': 'base64', 'value': 'MQ==', 'size': '1' } ], 'numchildren': '1' }, { 'name': '$_SERVER', 'type': 'array', 'variables': [ { 'name': 'UNIQUE_ID', 'type': 'string', 'encoding': 'base64', 'value': 'VlBBajZpYWgxVGtGQGlDVzFuNzhCZ0FBQUFB', 'size': '27' }, { 'name': 'HTTP_HOST', 'type': 'string', 'encoding': 'base64', 'value': 'bG9jYWxob3N0', 'size': '9' }, { 'name': 'HTTP_USER_AGENT', 'type': 'string', 'encoding': 'base64', 'value': 'TW96aWxsYS81LjAgKFgxMTsgRmVkb3JhOyBMaW51eCB4ODZfNjQ7IHJ2OjM2LjApIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvMzYuMA==', 'size': '76' }, { 'name': 'HTTP_ACCEPT', 'type': 'string', 'encoding': 'base64', 'value': 'dGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksKi8qO3E9MC44', 'size': '63' }, { 'name': 'HTTP_ACCEPT_LANGUAGE', 'type': 'string', 'encoding': 'base64', 'value': 'ZW4tVVMsZW47cT0wLjU=', 'size': '14' }, { 'name': 'HTTP_ACCEPT_ENCODING', 'type': 'string', 'encoding': 'base64', 'value': 'Z3ppcCwgZGVmbGF0ZQ==', 'size': '13' }, { 'name': 'HTTP_CONNECTION', 'type': 'string', 'encoding': 'base64', 'value': 'a2VlcC1hbGl2ZQ==', 'size': '10' }, { 'name': 'PATH', 'type': 'string', 'encoding': 'base64', 'value': 'L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbg==', 'size': '49' }, { 'name': 'SERVER_SIGNATURE', 'type': 'string', 'encoding': 'base64', 'value': None, 'size': '0' }, { 'name': 'SERVER_SOFTWARE', 'type': 'string', 'encoding': 'base64', 'value': 'QXBhY2hlLzIuNC4xMCAoRmVkb3JhKSBPcGVuU1NMLzEuMC4xay1maXBzIFBIUC81LjYuNg==', 'size': '52' }, { 'name': 'SERVER_NAME', 'type': 'string', 'encoding': 'base64', 'value': 'bG9jYWxob3N0', 'size': '9' }, { 'name': 'SERVER_ADDR', 'type': 'string', 'encoding': 'base64', 'value': 'MTI3LjAuMC4x', 'size': '9' }, { 'name': 'SERVER_PORT', 'type': 'string', 'encoding': 'base64', 'value': 'ODA=', 'size': '2' }, { 'name': 'REMOTE_ADDR', 'type': 'string', 'encoding': 'base64', 'value': 'MTI3LjAuMC4x', 'size': '9' }, { 'name': 'DOCUMENT_ROOT', 'type': 'string', 'encoding': 'base64', 'value': 'L2hvbWUvcm9iZXJ0L3d3dy9weGRlYnVn', 'size': '24' }, { 'name': 'REQUEST_SCHEME', 'type': 'string', 'encoding': 'base64', 'value': 'aHR0cA==', 'size': '4' }, { 'name': 'CONTEXT_PREFIX', 'type': 'string', 'encoding': 'base64', 'value': None, 'size': '0' }, { 'name': 'CONTEXT_DOCUMENT_ROOT', 'type': 'string', 'encoding': 'base64', 'value': 'L2hvbWUvcm9iZXJ0L3d3dy9weGRlYnVn', 'size': '24' }, { 'name': 'SERVER_ADMIN', 'type': 'string', 'encoding': 'base64', 'value': 'd2VibWFzdGVyQGxvY2FsaG9zdA==', 'size': '19' }, { 'name': 'SCRIPT_FILENAME', 'type': 'string', 'encoding': 'base64', 'value': 'L2hvbWUvcm9iZXJ0L3d3dy9weGRlYnVnL2luZGV4LnBocA==', 'size': '34' }, { 'name': 'REMOTE_PORT', 'type': 'string', 'encoding': 'base64', 'value': 'NTg3MDI=', 'size': '5' }, { 'name': 'GATEWAY_INTERFACE', 'type': 'string', 'encoding': 'base64', 'value': 'Q0dJLzEuMQ==', 'size': '7' }, { 'name': 'SERVER_PROTOCOL', 'type': 'string', 'encoding': 'base64', 'value': 'SFRUUC8xLjE=', 'size': '8' }, { 'name': 'REQUEST_METHOD', 'type': 'string', 'encoding': 'base64', 'value': 'R0VU', 'size': '3' }, { 'name': 'QUERY_STRING', 'type': 'string', 'encoding': 'base64', 'value': 'WERFQlVHX1NFU1NJT05fU1RBUlQ9MQ==', 'size': '22' }, { 'name': 'REQUEST_URI', 'type': 'string', 'encoding': 'base64', 'value': 'Lz9YREVCVUdfU0VTU0lPTl9TVEFSVD0x', 'size': '24' }, { 'name': 'SCRIPT_NAME', 'type': 'string', 'encoding': 'base64', 'value': 'L2luZGV4LnBocA==', 'size': '10' }, { 'name': 'PHP_SELF', 'type': 'string', 'encoding': 'base64', 'value': 'L2luZGV4LnBocA==', 'size': '10' }, { 'name': 'REQUEST_TIME_FLOAT', 'type': 'float', 'value': '1425023978.289' }, { 'name': 'REQUEST_TIME', 'type': 'int', 'value': '1425023978' } ], 'numchildren': '30' } ] #self.assertEqual(expected, result) self.assertEqual(expected[0], result[0]) self.assertEqual(expected[1], result[1]) self.assertEqual(expected[2], result[2]) self.assertEqual(expected[3], result[3]) self.assertEqual(expected[4], result[4]) self.assertEqual(expected[5], result[5]) self.assertEqual(expected[6], result[6]) self.assertEqual(expected[6]['variables'][28], result[6]['variables'][28]) def test_parse_successful_breakpoint_set_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="9" id="32310001"></response>' result = self.parser.parse_breakpoint_set_message(message) self.assertTrue(result) def test_parse_unsuccessful_breakpoint_set_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="9" status="break" reason="ok"><error code="3"><message><![CDATA[invalid or missing options]]></message></error></response>' result = self.parser.parse_breakpoint_set_message(message) self.assertFalse(result) def test_parse_successful_breakpoint_remove_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_remove" transaction_id="11"><breakpoint type="line" filename="file:///home/robert/www/pugdebug/index.php" lineno="10" state="enabled" hit_count="0" hit_value="0" id="41240003"></breakpoint></response>' result = self.parser.parse_breakpoint_remove_message(message) expected = 41240003 self.assertEqual(expected, result) def test_parse_unsuccessful_breakpoint_remove_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_remove" transaction_id="11" status="break" reason="ok"><error code="205"><message><![CDATA[no such breakpoint]]></message></error></response>' result = self.parser.parse_breakpoint_remove_message(message) self.assertFalse(result) def test_parse_breakpoint_list_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="breakpoint_list" transaction_id="12"><breakpoint type="line" filename="file:///home/robert/www/pugdebug/index.php" lineno="3" state="enabled" hit_count="0" hit_value="0" id="32350002"></breakpoint><breakpoint type="line" filename="file:///home/robert/www/pugdebug/index.php" lineno="10" state="enabled" hit_count="0" hit_value="0" id="32350001"></breakpoint></response>' result = self.parser.parse_breakpoint_list_message(message) expected = [ { 'filename': '/home/robert/www/pugdebug/index.php', 'id': '32350002', 'lineno': '3', 'state': 'enabled', 'type': 'line' }, { 'filename': '/home/robert/www/pugdebug/index.php', 'id': '32350001', 'lineno': '10', 'state': 'enabled', 'type': 'line' } ] self.assertEqual(expected, result) def test_parse_stacktraces_message(self): message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="stack_get" transaction_id="118"><stack where="{main}" level="0" type="file" filename="file:///home/robert/www/pugdebug/index.php" lineno="30"></stack></response>' result = self.parser.parse_stacktraces_message(message) expected = [ { 'filename': '/home/robert/www/pugdebug/index.php', 'lineno': '30', 'where': '{main}', 'level': '0' } ] self.assertEqual(expected, result) message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="stack_get" transaction_id="22"><stack where="include_once" level="0" type="file" filename="file:///home/robert/www/pugdebug/dir/foo.php" lineno="3"></stack><stack where="include_once" level="1" type="file" filename="file:///home/robert/www/pugdebug/file.php" lineno="3"></stack><stack where="{main}" level="2" type="file" filename="file:///home/robert/www/pugdebug/index.php" lineno="3"></stack></response>' result = self.parser.parse_stacktraces_message(message) expected = [ { 'filename': '/home/robert/www/pugdebug/dir/foo.php', 'lineno': '3', 'where': 'include_once', 'level': '0' }, { 'filename': '/home/robert/www/pugdebug/file.php', 'lineno': '3', 'where': 'include_once', 'level': '1' }, { 'filename': '/home/robert/www/pugdebug/index.php', 'lineno': '3', 'where': '{main}', 'level': '2' } ] self.assertEqual(expected, result) message = '<?xml version="1.0" encoding="iso-8859-1"?>\ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="stack_get" transaction_id="54"><stack where="foo" level="0" type="file" filename="file:///home/robert/www/pugdebug/dir/foo.php" lineno="4"></stack><stack where="call_foo" level="1" type="file" filename="file:///home/robert/www/pugdebug/file.php" lineno="6"></stack><stack where="{main}" level="2" type="file" filename="file:///home/robert/www/pugdebug/index.php" lineno="34"></stack></response>' result = self.parser.parse_stacktraces_message(message) expected = [ { 'filename': '/home/robert/www/pugdebug/dir/foo.php', 'lineno': '4', 'where': 'foo', 'level': '0' }, { 'filename': '/home/robert/www/pugdebug/file.php', 'lineno': '6', 'where': 'call_foo', 'level': '1' }, { 'filename': '/home/robert/www/pugdebug/index.php', 'lineno': '34', 'where': '{main}', 'level': '2' } ] self.assertEqual(expected, result)
class PugdebugServerConnection(QObject): socket = None mutex = None parser = None is_valid = False init_message = None transaction_id = 0 xdebug_encoding = 'iso-8859-1' post_start_signal = pyqtSignal() stopped_signal = pyqtSignal() detached_signal = pyqtSignal() stepped_signal = pyqtSignal(dict) got_variables_signal = pyqtSignal(object) got_stacktraces_signal = pyqtSignal(object) set_breakpoint_signal = pyqtSignal(bool) removed_breakpoint_signal = pyqtSignal(object) listed_breakpoints_signal = pyqtSignal(list) expression_evaluated_signal = pyqtSignal(int, dict) expressions_evaluated_signal = pyqtSignal(list) connection_error_signal = pyqtSignal(str, str) def __init__(self, socket): super(PugdebugServerConnection, self).__init__() self.socket = socket self.mutex = QMutex() self.parser = PugdebugMessageParser() def init_connection(self): """Init a new connection Read in the init message from xdebug and decide based on the idekey should this connection be accepted or not. Do note that it is not needed to call it from a new thread, as it is already called from a thread separate from the main application thread and thus should not block the main thread. """ idekey = get_setting('debugger/idekey') response = self.__receive_message() init_message = self.parser.parse_init_message(response) # See if the init message from xdebug is meant for us if idekey != '' and init_message['idekey'] != idekey: return False self.init_message = init_message return True def start(self, action, data=None): QThreadPool.globalInstance().start( PugdebugAsyncTask(self, action, data)) def perform(self, action, data): self.mutex.lock() try: if action == 'post_start': response = self.__post_start(data) self.listed_breakpoints_signal.emit(response['breakpoints']) self.post_start_signal.emit() elif action == 'stop': response = self.__stop() self.stopped_signal.emit() elif action == 'detach': response = self.__detach() self.detached_signal.emit() elif action == 'step_run': response = self.__step_run() self.stepped_signal.emit(response) elif action == 'step_into': response = self.__step_into() self.stepped_signal.emit(response) elif action == 'step_over': response = self.__step_over() self.stepped_signal.emit(response) elif action == 'step_out': response = self.__step_out() self.stepped_signal.emit(response) elif action == 'post_step': response = self.__post_step(data) self.got_variables_signal.emit(response['variables']) self.got_stacktraces_signal.emit(response['stacktraces']) self.expressions_evaluated_signal.emit(response['expressions']) elif action == 'breakpoint_set': response = self.__set_breakpoint(data) self.set_breakpoint_signal.emit(response) elif action == 'breakpoint_remove': response = self.__remove_breakpoint(data) self.removed_breakpoint_signal.emit(response) elif action == 'breakpoint_list': response = self.__list_breakpoints() self.listed_breakpoints_signal.emit(response) elif action == 'evaluate_expression': (index, expression) = data response = self.__evaluate_expression(expression) self.expression_evaluated_signal.emit(index, response) elif action == 'set_debugger_features': self.__set_debugger_features() except OSError as error: self.disconnect() self.connection_error_signal.emit(action, error.strerror) self.mutex.unlock() def disconnect(self): if self.socket is not None: self.socket.close() def post_start_command(self, post_start_data): self.start('post_start', post_start_data) def stop(self): self.start('stop') def detach(self): self.start('detach') def step_run(self): self.start('step_run') def step_into(self): self.start('step_into') def step_over(self): self.start('step_over') def step_out(self): self.start('step_out') def post_step_command(self, post_step_data): self.start('post_step', post_step_data) def set_breakpoint(self, breakpoint): self.start('breakpoint_set', breakpoint) def remove_breakpoint(self, breakpoint_id): self.start('breakpoint_remove', breakpoint_id) def list_breakpoints(self): self.start('breakpoint_list') def evaluate_expression(self, index, expression): self.start('evaluate_expression', (index, expression)) def set_debugger_features(self): self.start('set_debugger_features') def load_typemap(self): command = 'typemap_get -i %d' % self.__get_transaction_id() response = self.__send_command(command) self.parser.set_typemap(self.parser.parse_typemap_message(response)) return True def __post_start(self, data): self.__set_breakpoints(data['breakpoints']) post_start_response = { 'debugger_features': self.__set_debugger_features(), 'breakpoints': self.__list_breakpoints() } return post_start_response def __stop(self): command = 'stop -i %d' % self.__get_transaction_id() self.__send_command(command) return True def __detach(self): command = 'detach -i %d' % self.__get_transaction_id() self.__send_command(command) return True def __step_run(self): command = 'run -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __step_into(self): command = 'step_into -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __step_over(self): command = 'step_over -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __step_out(self): command = 'step_out -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __do_step_command(self, command): response = self.__send_command(command) response = self.parser.parse_continuation_message(response) return response def __post_step(self, data): post_step_response = { 'variables': self.__get_variables(), 'stacktraces': self.__get_stacktraces(), 'expressions': self.__evaluate_expressions(data['expressions']) } return post_step_response def __get_variables(self): command = 'context_names -i %d' % self.__get_transaction_id() response = self.__send_command(command) contexts = self.parser.parse_variable_contexts_message(response) variables = {} for context in contexts: context_id = int(context['id']) command = 'context_get -i %d -c %d' % (self.__get_transaction_id(), context_id) response = self.__send_command(command) var = self.parser.parse_variables_message(response) variables[context['name']] = var return variables def __get_stacktraces(self): command = 'stack_get -i %d' % self.__get_transaction_id() response = self.__send_command(command) stacktraces = self.parser.parse_stacktraces_message(response) return stacktraces def __set_breakpoints(self, breakpoints): all_successful = True for breakpoint in breakpoints: response = self.__set_breakpoint(breakpoint) if response is False: all_successful = False return all_successful def __set_breakpoint(self, breakpoint): command = 'breakpoint_set -i %d -t %s -f %s -n %d' % ( self.__get_transaction_id(), 'line', breakpoint['filename'], int(breakpoint['lineno'])) response = self.__send_command(command) return self.parser.parse_breakpoint_set_message(response) def __remove_breakpoint(self, breakpoint_id): command = 'breakpoint_remove -i %d -d %d' % ( self.__get_transaction_id(), breakpoint_id) response = self.__send_command(command) return self.parser.parse_breakpoint_remove_message(response) def __list_breakpoints(self): command = 'breakpoint_list -i %d' % self.__get_transaction_id() response = self.__send_command(command) breakpoints = self.parser.parse_breakpoint_list_message(response) return breakpoints def __evaluate_expressions(self, expressions): results = [] for index, expression in enumerate(expressions): results.append(self.__evaluate_expression(expression)) return results def __evaluate_expression(self, expression): tid = self.__get_transaction_id() b64 = b64encode(bytes(expression, 'UTF-8')).decode() command = 'eval -i %d -- %s' % (tid, b64) response = self.__send_command(command) return self.parser.parse_eval_message(response) def __set_debugger_features(self): max_depth = int(get_setting('debugger/max_depth')) command = 'feature_set -i %d -n max_depth -v %d' % ( self.__get_transaction_id(), max_depth) self.__send_command(command) max_children = int(get_setting('debugger/max_children')) command = 'feature_set -i %d -n max_children -v %d' % ( self.__get_transaction_id(), max_children) self.__send_command(command) max_data = int(get_setting('debugger/max_data')) command = 'feature_set -i %d -n max_data -v %d' % ( self.__get_transaction_id(), max_data) self.__send_command(command) return True def __send_command(self, command): self.socket.send(bytes(command + '\0', 'utf-8')) return self.__receive_message() def __receive_message(self): length = self.__get_message_length() body = self.__get_message_body(length) return body def __get_message_length(self): length = '' while True: character = self.socket.recv(1) if self.__is_eof(character): self.disconnect() if character.isdigit(): length = length + character.decode(self.xdebug_encoding) if character.decode(self.xdebug_encoding) == '\0': if length == '': return 0 return int(length) def __get_message_body(self, length): body = '' while length > 0: data = self.socket.recv(length) if self.__is_eof(data): self.disconnect() body = body + data.decode(self.xdebug_encoding) length = length - len(data) self.__get_null() return body def __get_null(self): while True: character = self.socket.recv(1) if self.__is_eof(character): self.disconnect() if character.decode(self.xdebug_encoding) == '\0': return def __is_eof(self, data): return data.decode(self.xdebug_encoding) == '' def __get_transaction_id(self): self.transaction_id += 1 return self.transaction_id
class PugdebugServerConnection(QThread): socket = None mutex = None parser = None action = None data = None is_valid = False init_message = None transaction_id = 0 xdebug_encoding = 'iso-8859-1' post_start_signal = pyqtSignal() stopped_signal = pyqtSignal() detached_signal = pyqtSignal() stepped_signal = pyqtSignal(dict) got_variables_signal = pyqtSignal(object) got_stacktraces_signal = pyqtSignal(object) set_breakpoint_signal = pyqtSignal(bool) removed_breakpoint_signal = pyqtSignal(object) listed_breakpoints_signal = pyqtSignal(list) expression_evaluated_signal = pyqtSignal(int, dict) expressions_evaluated_signal = pyqtSignal(list) connection_error_signal = pyqtSignal(str, str) def __init__(self, socket): super(PugdebugServerConnection, self).__init__() self.socket = socket self.mutex = QMutex() self.parser = PugdebugMessageParser() def init_connection(self): """Init a new connection Read in the init message from xdebug and decide based on the idekey should this connection be accepted or not. Do note that it is not needed to call it from a new thread, as it is already called from a thread separate from the main application thread and thus should not block the main thread. """ idekey = get_setting('debugger/idekey') response = self.__receive_message() init_message = self.parser.parse_init_message(response) # See if the init message from xdebug is meant for us if idekey != '' and init_message['idekey'] != idekey: return False self.init_message = init_message return True def run(self): self.mutex.lock() data = self.data action = self.action try: if action == 'post_start': response = self.__post_start(data) self.listed_breakpoints_signal.emit( response['breakpoints'] ) self.post_start_signal.emit() elif action == 'stop': response = self.__stop() self.stopped_signal.emit() elif action == 'detach': response = self.__detach() self.detached_signal.emit() elif action == 'step_run': response = self.__step_run() self.stepped_signal.emit(response) elif action == 'step_into': response = self.__step_into() self.stepped_signal.emit(response) elif action == 'step_over': response = self.__step_over() self.stepped_signal.emit(response) elif action == 'step_out': response = self.__step_out() self.stepped_signal.emit(response) elif action == 'post_step': response = self.__post_step(data) self.got_variables_signal.emit(response['variables']) self.got_stacktraces_signal.emit(response['stacktraces']) self.expressions_evaluated_signal.emit( response['expressions'] ) elif action == 'breakpoint_set': response = self.__set_breakpoint(data) self.set_breakpoint_signal.emit(response) elif action == 'breakpoint_remove': response = self.__remove_breakpoint(data) self.removed_breakpoint_signal.emit(response) elif action == 'breakpoint_list': response = self.__list_breakpoints() self.listed_breakpoints_signal.emit(response) elif action == 'evaluate_expression': (index, expression) = data response = self.__evaluate_expression(expression) self.expression_evaluated_signal.emit(index, response) elif action == 'set_debugger_features': self.__set_debugger_features() except OSError as error: self.disconnect() self.connection_error_signal.emit(action, error.strerror) self.mutex.unlock() def disconnect(self): if self.socket is not None: self.socket.close() def post_start_command(self, post_start_data): self.data = post_start_data self.action = 'post_start' self.start() def stop(self): self.action = 'stop' self.start() def detach(self): self.action = 'detach' self.start() def step_run(self): self.action = 'step_run' self.start() def step_into(self): self.action = 'step_into' self.start() def step_over(self): self.action = 'step_over' self.start() def step_out(self): self.action = 'step_out' self.start() def post_step_command(self, post_step_data): self.data = post_step_data self.action = 'post_step' self.start() def set_breakpoint(self, breakpoint): self.action = 'breakpoint_set' self.data = breakpoint self.start() def remove_breakpoint(self, breakpoint_id): self.action = 'breakpoint_remove' self.data = breakpoint_id self.start() def list_breakpoints(self): self.action = 'breakpoint_list' self.start() def evaluate_expression(self, index, expression): self.action = 'evaluate_expression' self.data = (index, expression) self.start() def set_debugger_features(self): self.action = 'set_debugger_features' self.start() def __post_start(self, data): post_start_response = { 'debugger_features': self.__set_debugger_features(), 'breakpoints': self.__set_breakpoints( data['breakpoints'] ), 'breakpoints': self.__list_breakpoints() } return post_start_response def __stop(self): command = 'stop -i %d' % self.__get_transaction_id() self.__send_command(command) return True def __detach(self): command = 'detach -i %d' % self.__get_transaction_id() self.__send_command(command) return True def __step_run(self): command = 'run -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __step_into(self): command = 'step_into -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __step_over(self): command = 'step_over -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __step_out(self): command = 'step_out -i %d' % self.__get_transaction_id() return self.__do_step_command(command) def __do_step_command(self, command): response = self.__send_command(command) response = self.parser.parse_continuation_message(response) return response def __post_step(self, data): post_step_response = { 'variables': self.__get_variables(), 'stacktraces': self.__get_stacktraces(), 'expressions': self.__evaluate_expressions(data['expressions']) } return post_step_response def __get_variables(self): command = 'context_names -i %d' % self.__get_transaction_id() response = self.__send_command(command) contexts = self.parser.parse_variable_contexts_message(response) variables = {} for context in contexts: context_id = int(context['id']) command = 'context_get -i %d -c %d' % ( self.__get_transaction_id(), context_id ) response = self.__send_command(command) var = self.parser.parse_variables_message(response) variables[context['name']] = var return variables def __get_stacktraces(self): command = 'stack_get -i %d' % self.__get_transaction_id() response = self.__send_command(command) stacktraces = self.parser.parse_stacktraces_message(response) return stacktraces def __set_breakpoints(self, breakpoints): all_successful = True for breakpoint in breakpoints: response = self.__set_breakpoint(breakpoint) if response is False: all_successful = False return all_successful def __set_breakpoint(self, breakpoint): command = 'breakpoint_set -i %d -t %s -f %s -n %d' % ( self.__get_transaction_id(), 'line', breakpoint['filename'], int(breakpoint['lineno']) ) response = self.__send_command(command) return self.parser.parse_breakpoint_set_message(response) def __remove_breakpoint(self, breakpoint_id): command = 'breakpoint_remove -i %d -d %d' % ( self.__get_transaction_id(), breakpoint_id ) response = self.__send_command(command) return self.parser.parse_breakpoint_remove_message(response) def __list_breakpoints(self): command = 'breakpoint_list -i %d' % self.__get_transaction_id() response = self.__send_command(command) breakpoints = self.parser.parse_breakpoint_list_message(response) return breakpoints def __evaluate_expressions(self, expressions): results = [] for index, expression in enumerate(expressions): results.append( self.__evaluate_expression(expression) ) return results def __evaluate_expression(self, expression): tid = self.__get_transaction_id() b64 = b64encode(bytes(expression, 'UTF-8')).decode() command = 'eval -i %d -- %s' % (tid, b64) response = self.__send_command(command) return self.parser.parse_eval_message(response) def __set_debugger_features(self): max_depth = int(get_setting('debugger/max_depth')) command = 'feature_set -i %d -n max_depth -v %d' % ( self.__get_transaction_id(), max_depth ) response = self.__send_command(command) max_children = int(get_setting('debugger/max_children')) command = 'feature_set -i %d -n max_children -v %d' % ( self.__get_transaction_id(), max_children ) response = self.__send_command(command) max_data = int(get_setting('debugger/max_data')) command = 'feature_set -i %d -n max_data -v %d' % ( self.__get_transaction_id(), max_data ) response = self.__send_command(command) return True def __send_command(self, command): self.socket.send(bytes(command + '\0', 'utf-8')) return self.__receive_message() def __receive_message(self): length = self.__get_message_length() body = self.__get_message_body(length) return body def __get_message_length(self): length = '' while True: character = self.socket.recv(1) if self.__is_eof(character): self.disconnect() if character.isdigit(): length = length + character.decode(self.xdebug_encoding) if character.decode(self.xdebug_encoding) == '\0': if length == '': return 0 return int(length) def __get_message_body(self, length): body = '' while length > 0: data = self.socket.recv(length) if self.__is_eof(data): self.disconnect() body = body + data.decode(self.xdebug_encoding) length = length - len(data) self.__get_null() return body def __get_null(self): while True: character = self.socket.recv(1) if self.__is_eof(character): self.disconnect() if character.decode(self.xdebug_encoding) == '\0': return def __is_eof(self, data): return data.decode(self.xdebug_encoding) == '' def __get_transaction_id(self): self.transaction_id += 1 return self.transaction_id