diff mbox series

[2/2] qmi: Add unit test for QRTR qmi_service_send

Message ID 20240405160702.29261-2-steve.schrock@getcruise.com (mailing list archive)
State Superseded
Headers show
Series [1/2] qmi: Create QRTR unit testing framework | expand

Commit Message

Steve Schrock April 5, 2024, 4:07 p.m. UTC
The unit test causes a qmi request to be sent to the test QRTR service
and echos the data back in its response.
---
 unit/test-qmimodem-qmi.c | 165 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 164 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/unit/test-qmimodem-qmi.c b/unit/test-qmimodem-qmi.c
index 619e813f78d6..2d15332637f6 100644
--- a/unit/test-qmimodem-qmi.c
+++ b/unit/test-qmimodem-qmi.c
@@ -28,7 +28,14 @@  struct test_info {
 	struct qmi_device *device;
 	struct l_timeout *timeout;
 	struct l_queue *services;
-	bool discovery_callback_called : 1;
+
+	/* Data sent to our test service */
+	struct sockaddr_qrtr sender;
+	size_t received_len;
+	void *received;
+
+	bool discovery_callback_called		: 1;
+	bool service_send_callback_called	: 1;
 };
 
 static uint32_t unique_service_type(uint32_t index)
@@ -248,6 +255,161 @@  static void test_create_services(const void *data)
 	test_cleanup(info);
 }
 
+static bool received_data(struct l_io *io, void *user_data)
+{
+	struct test_info *info = user_data;
+	struct sockaddr_qrtr addr;
+	unsigned char buf[2048];
+	ssize_t bytes_read;
+	socklen_t addr_size;
+
+	addr_size = sizeof(addr);
+	bytes_read = recvfrom(l_io_get_fd(io), buf, sizeof(buf), 0,
+				(struct sockaddr *) &addr, &addr_size);
+	memcpy(&info->sender, &addr, sizeof(addr));
+
+	assert(!info->received); /* Only expect one message */
+	info->received_len = bytes_read;
+	info->received = l_memdup(buf, bytes_read);
+
+	return true;
+}
+
+#define TEST_TLV_TYPE		21	/* Its data value is 1 byte */
+#define TEST_DATA_VALUE		0x89
+
+static void send_test_data_cb(struct qmi_result *result, void *user_data)
+{
+	struct test_info *info = user_data;
+
+	uint8_t data;
+
+	assert(!qmi_result_set_error(result, NULL));
+	assert(qmi_result_get_uint8(result, TEST_TLV_TYPE, &data));
+	assert(data == TEST_DATA_VALUE);
+
+	info->service_send_callback_called = true;
+}
+
+/*
+ * We know exactly how the qmi data should be packed so we can hard code the
+ * structure layout to simplify the tests.
+ */
+
+#define TEST_REQ_MESSAGE_ID	42
+#define TEST_RESP_MESSAGE_ID	43
+#define QMI_HDR_SIZE		7
+
+struct qmi_test_service_request {
+	uint8_t  type;
+	uint16_t transaction;
+	uint16_t message;
+	uint16_t length;	/* Message size without header */
+	uint8_t  data_type;
+	uint16_t data_length;
+	uint8_t  data_value;
+} __attribute__ ((packed));
+
+struct qmi_test_service_response {
+	uint8_t  type;
+	uint16_t transaction;
+	uint16_t message;
+	uint16_t length;	/* Message size without header */
+	uint8_t  error_type;
+	uint16_t error_length;
+	uint16_t error_result;
+	uint16_t error_error;
+	uint8_t  data_type;
+	uint16_t data_length;
+	uint8_t  data_value;
+} __attribute__ ((packed));
+
+static void send_request_via_qmi(struct test_info *info,
+						struct qmi_service *service)
+{
+	struct qmi_param *param;
+
+	param = qmi_param_new();
+	qmi_param_append_uint8(param, TEST_TLV_TYPE, TEST_DATA_VALUE);
+	assert(qmi_service_send(service, TEST_REQ_MESSAGE_ID, param,
+					send_test_data_cb, info, NULL));
+
+	while (!info->received)
+		l_main_iterate(-1);
+}
+
+static void send_response_via_socket(struct test_info *info, struct l_io *io)
+{
+	const struct qmi_test_service_request *request;
+	struct qmi_test_service_response response;
+
+	/* First validate that the qrtr code sent the qmi request properly. */
+	assert(info->received_len == sizeof(*request));
+	request = info->received;
+	assert(request->type == 0x00);
+	assert(request->message == L_CPU_TO_LE16(TEST_REQ_MESSAGE_ID));
+	assert(request->length == L_CPU_TO_LE16(
+					sizeof(*request) - QMI_HDR_SIZE));
+	assert(request->data_type == TEST_TLV_TYPE);
+	assert(request->data_length == L_CPU_TO_LE16(1));
+	assert(request->data_value == TEST_DATA_VALUE);
+
+	/*
+	 * Now echo it back to the qrtr client. The qmi_service send callback
+	 * will validate that the client processed this response correctly.
+	 */
+	memset(&response, 0, sizeof(response));
+	response.type = 0x02;
+	response.transaction = request->transaction;
+	response.message = L_CPU_TO_LE16(TEST_RESP_MESSAGE_ID);
+	response.length = L_CPU_TO_LE16(sizeof(response) - QMI_HDR_SIZE);
+	response.error_type = 2;
+	response.error_length = L_CPU_TO_LE16(4);
+	response.data_type = request->data_type;
+	response.data_length = request->data_length;
+	response.data_value = request->data_value;
+
+	sendto(l_io_get_fd(io), &response, sizeof(response), 0,
+					(struct sockaddr *) &info->sender,
+					sizeof(info->sender));
+
+	while (!info->service_send_callback_called)
+		l_main_iterate(-1);
+}
+
+/*
+ * Initiates a send of the TLV data payload to the test service. The test
+ * service will respond with the same data payload.
+ */
+static void test_send_data(const void *data)
+{
+	struct test_info *info = test_setup();
+	struct l_io *io;
+	uint32_t service_type;
+	struct qmi_service *service;
+
+	perform_discovery(info);
+
+	service_type = unique_service_type(0); /* Use the first service */
+	assert(qmi_service_create(info->device, service_type,
+					create_service_cb, info, NULL));
+	perform_all_pending_work();
+	service = l_queue_pop_head(info->services);
+	assert(service);
+
+	io = l_io_new(info->service_fds[0]);
+	assert(io);
+	l_io_set_read_handler(io, received_data, info, NULL);
+
+	send_request_via_qmi(info, service);
+	send_response_via_socket(info, io);
+
+	l_io_destroy(io);
+	qmi_service_unref(service);
+
+	test_cleanup(info);
+}
+
 int main(int argc, char **argv)
 {
 	int result;
@@ -259,6 +421,7 @@  int main(int argc, char **argv)
 	l_test_add("QRTR device creation", test_create_qrtr_device, NULL);
 	l_test_add("QRTR discovery", test_discovery, NULL);
 	l_test_add("QRTR services may be created", test_create_services, NULL);
+	l_test_add("QRTR service sends/responses", test_send_data, NULL);
 	result = l_test_run();
 
 	return result;