| import os | |
| import os.path | |
| import lldb | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test.decorators import requireSocketPermission | |
| from lldbsuite.test.gdbclientutils import * | |
| @requireSocketPermission # setUp binds a listening socket for the mock server | |
| class GDBRemoteTestBase(TestBase): | |
| """ | |
| Base class for GDB client tests. | |
| This class will setup and start a mock GDB server for the test to use. | |
| It also provides assertPacketLogReceived, which simplifies the checking | |
| of packets sent by the client. | |
| """ | |
| NO_DEBUG_INFO_TESTCASE = True | |
| server = None | |
| server_socket_class = TCPServerSocket | |
| def setUp(self): | |
| TestBase.setUp(self) | |
| self.server = MockGDBServer(self.server_socket_class()) | |
| self.server.start() | |
| def tearDown(self): | |
| # TestBase.tearDown will kill the process, but we need to kill it early | |
| # so its client connection closes and we can stop the server before | |
| # finally calling the base tearDown. | |
| if self.process() is not None: | |
| self.process().Kill() | |
| self.server.stop() | |
| TestBase.tearDown(self) | |
| def createTarget(self, yaml_path): | |
| """ | |
| Create a target by auto-generating the object based on the given yaml | |
| instructions. | |
| This will track the generated object so it can be automatically removed | |
| during tearDown. | |
| """ | |
| yaml_base, ext = os.path.splitext(yaml_path) | |
| obj_path = self.getBuildArtifact(yaml_base) | |
| self.yaml2obj(yaml_path, obj_path) | |
| return self.dbg.CreateTarget(obj_path) | |
| def connect(self, target, plugin="gdb-remote"): | |
| """ | |
| Create a process by connecting to the mock GDB server. | |
| Includes assertions that the process was successfully created. | |
| """ | |
| listener = self.dbg.GetListener() | |
| error = lldb.SBError() | |
| process = target.ConnectRemote( | |
| listener, self.server.get_connect_url(), plugin, error | |
| ) | |
| self.assertTrue(error.Success(), error.description) | |
| self.assertTrue(process, PROCESS_IS_VALID) | |
| return process | |
| def assertPacketLogReceived(self, packets, log: PacketLog = None): | |
| """ | |
| Assert that the mock server's packet log received the given packets. | |
| The packet log includes all packets sent by the client and received | |
| by the server. This function makes it easy to verify that the client | |
| sent the expected packets to the server. | |
| The check does not require that the packets be consecutive, but does | |
| require that they are ordered in the log as they ordered in the arg. | |
| """ | |
| if log is None: | |
| received = self.server.responder.packetLog.get_received() | |
| else: | |
| received = log.get_received() | |
| i = 0 | |
| j = 0 | |
| while i < len(packets) and j < len(received): | |
| if received[j] == packets[i]: | |
| i += 1 | |
| j += 1 | |
| if i < len(packets): | |
| self.fail( | |
| "Did not receive: %s\nLast 10 packets:\n\t%s" | |
| % (packets[i], "\n\t".join(received)) | |
| ) | |
| class GDBPlatformClientTestBase(GDBRemoteTestBase): | |
| """ | |
| Base class for platform server clients. | |
| This class extends GDBRemoteTestBase by automatically connecting | |
| via "platform connect" in the setUp() method. | |
| """ | |
| def setUp(self): | |
| super().setUp() | |
| self.runCmd("platform select remote-gdb-server") | |
| self.runCmd("platform connect " + self.server.get_connect_url()) | |
| self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected()) | |
| def tearDown(self): | |
| self.dbg.GetSelectedPlatform().DisconnectRemote() | |
| super().tearDown() |