Add test_runner service
This is a new service with client and provider that can be
used for running tests in a secure processing environment
and retrieving the results. The test_runner provider allows
for arbitrary test farmework backends. The goal is to
have a cpputest backend. In this commit, a mock backend
is included for testing the service itself. The service
has its own access protocol defined under the protocols
top-level directory.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: If4e965c110763bd805abbdcb87e7e03cd76248b2
diff --git a/components/service/test_runner/provider/backend/mock/component.cmake b/components/service/test_runner/provider/backend/mock/component.cmake
new file mode 100644
index 0000000..4666b2b
--- /dev/null
+++ b/components/service/test_runner/provider/backend/mock/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/mock_test_runner.c"
+ )
+
diff --git a/components/service/test_runner/provider/backend/mock/mock_test_runner.c b/components/service/test_runner/provider/backend/mock/mock_test_runner.c
new file mode 100644
index 0000000..9ea5936
--- /dev/null
+++ b/components/service/test_runner/provider/backend/mock/mock_test_runner.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <service/test_runner/provider/test_runner_backend.h>
+#include <service/test_runner/provider/test_runner_provider.h>
+#include <stdbool.h>
+#include <string.h>
+
+/**
+ * The mock backend is a test_runner that provides some mock test cases
+ * that can be used for testing the test_runner service iteslf.
+ */
+
+struct mock_test_case
+{
+ const char *group;
+ const char *name;
+ bool (*test_func)(void);
+};
+
+/* Mock test test functions */
+static bool test_that_passes(void) { return true; }
+static bool test_that_fails(void) { return false; }
+
+/* Mock test suite */
+const struct mock_test_case mock_test_suite[] =
+{
+ {.group = "PlatformTests", .name = "Trng", .test_func = test_that_passes},
+ {.group = "PlatformTests", .name = "CheckIOmap", .test_func = test_that_passes},
+ {.group = "ConfigTests", .name = "ValidateConfig", .test_func = test_that_fails},
+ {.group = "ConfigTests", .name = "ApplyConfig", .test_func = test_that_passes}
+};
+
+
+static bool does_qualify(const struct mock_test_case *test_case, const struct test_spec *spec)
+{
+ return
+ ((strlen(spec->group) == 0) || (strcmp(spec->group, test_case->group) == 0)) &&
+ ((strlen(spec->name) == 0) || (strcmp(spec->name, test_case->name) == 0));
+}
+
+static size_t count_tests(const struct test_spec *spec)
+{
+ size_t count = 0;
+
+ for (size_t i = 0; i < sizeof(mock_test_suite)/sizeof(struct mock_test_case); ++i) {
+
+ if (does_qualify(&mock_test_suite[i], spec)) ++count;
+ }
+
+ return count;
+}
+
+static int run_tests(const struct test_spec *spec, struct test_summary *summary,
+ struct test_result *results, size_t result_limit)
+{
+ summary->num_tests = 0;
+ summary->num_results = 0;
+ summary->num_passed = 0;
+ summary->num_failed = 0;
+
+ for (size_t i = 0; i < sizeof(mock_test_suite)/sizeof(struct mock_test_case); ++i) {
+
+ if (does_qualify(&mock_test_suite[i], spec)) {
+
+ bool did_pass = mock_test_suite[i].test_func();
+
+ if (did_pass)
+ ++summary->num_passed;
+ else
+ ++summary->num_failed;
+
+ if (summary->num_tests < result_limit) {
+
+ struct test_result *new_result = &results[summary->num_results];
+
+ new_result->run_state = (did_pass) ? TEST_RUN_STATE_PASSED : TEST_RUN_STATE_FAILED;
+ new_result->fail_line = 0;
+ strcpy(new_result->group, mock_test_suite[i].group);
+ strcpy(new_result->name, mock_test_suite[i].name);
+
+ ++summary->num_results;
+ }
+
+ ++summary->num_tests;
+ }
+ }
+
+ return 0;
+}
+
+static void list_tests(const struct test_spec *spec, struct test_summary *summary,
+ struct test_result *results, size_t result_limit)
+{
+ summary->num_tests = 0;
+ summary->num_results = 0;
+ summary->num_passed = 0;
+ summary->num_failed = 0;
+
+ for (size_t i = 0; i < sizeof(mock_test_suite)/sizeof(struct mock_test_case); ++i) {
+
+ if (does_qualify(&mock_test_suite[i], spec)) {
+
+ if (summary->num_tests < result_limit) {
+
+ struct test_result *new_result = &results[summary->num_results];
+
+ new_result->run_state = TEST_RUN_STATE_NOT_RUN;
+ new_result->fail_line = 0;
+ strcpy(new_result->group, mock_test_suite[i].group);
+ strcpy(new_result->name, mock_test_suite[i].name);
+
+ ++summary->num_results;
+ }
+
+ ++summary->num_tests;
+ }
+ }
+}
+
+void test_runner_register_default_backend(struct test_runner_provider *context)
+{
+ static struct test_runner_backend this_instance;
+
+ this_instance.count_tests = count_tests;
+ this_instance.run_tests = run_tests;
+ this_instance.list_tests = list_tests;
+ this_instance.next = NULL;
+
+ test_runner_provider_register_backend(context, &this_instance);
+}
\ No newline at end of file
diff --git a/components/service/test_runner/provider/backend/null/component.cmake b/components/service/test_runner/provider/backend/null/component.cmake
new file mode 100644
index 0000000..8197412
--- /dev/null
+++ b/components/service/test_runner/provider/backend/null/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/null_test_runner.c"
+ )
+
diff --git a/components/service/test_runner/provider/backend/null/null_test_runner.c b/components/service/test_runner/provider/backend/null/null_test_runner.c
new file mode 100644
index 0000000..9d4afb5
--- /dev/null
+++ b/components/service/test_runner/provider/backend/null/null_test_runner.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <service/test_runner/provider/test_runner_backend.h>
+
+/**
+ * The null backend is a non-existent test_runner. An implementation of
+ * the test_runner_register_default_backend() function is provided but it does
+ * nothing. This component should be used in deployments where there is
+ * no need for a default test_runner.
+ */
+void test_runner_register_default_backend(struct test_runner_provider *context)
+{
+ /* Don't register anything */
+ (void)context;
+}
\ No newline at end of file
diff --git a/components/service/test_runner/provider/component.cmake b/components/service/test_runner/provider/component.cmake
new file mode 100644
index 0000000..4a37648
--- /dev/null
+++ b/components/service/test_runner/provider/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/test_runner_provider.c"
+ )
diff --git a/components/service/test_runner/provider/serializer/packed-c/component.cmake b/components/service/test_runner/provider/serializer/packed-c/component.cmake
new file mode 100644
index 0000000..1180035
--- /dev/null
+++ b/components/service/test_runner/provider/serializer/packed-c/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/packedc_test_runner_provider_serializer.c"
+ )
diff --git a/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c b/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c
new file mode 100644
index 0000000..577bfdc
--- /dev/null
+++ b/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <common/tlv/tlv.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <protocols/service/test_runner/packed-c/run_tests.h>
+#include <protocols/service/test_runner/packed-c/list_tests.h>
+#include "packedc_test_runner_provider_serializer.h"
+
+/* Common rerialization methods used for different operations */
+static rpc_status_t deserialize_test_spec(const struct call_param_buf *req_buf,
+ struct test_spec *test_spec)
+{
+ struct tlv_const_iterator req_iter;
+ struct tlv_record decoded_record;
+
+ test_spec->name[0] = 0;
+ test_spec->group[0] = 0;
+
+ tlv_const_iterator_begin(&req_iter, (uint8_t*)req_buf->data, req_buf->data_len);
+
+ if (tlv_find_decode(&req_iter, TS_TEST_RUNNER_TEST_SPEC_TAG_NAME, &decoded_record)) {
+
+ if ((decoded_record.length > 0) && (decoded_record.length < TEST_NAME_MAX_LEN)) {
+
+ memcpy(test_spec->name, decoded_record.value, decoded_record.length);
+ test_spec->name[decoded_record.length] = 0;
+ }
+ }
+
+ if (tlv_find_decode(&req_iter, TS_TEST_RUNNER_TEST_SPEC_TAG_GROUP, &decoded_record)) {
+
+ if ((decoded_record.length > 0) && (decoded_record.length < TEST_GROUP_MAX_LEN)) {
+
+ memcpy(test_spec->group, decoded_record.value, decoded_record.length);
+ test_spec->group[decoded_record.length] = 0;
+ }
+ }
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+static uint8_t *serialize_test_result(const struct test_result *result, size_t *serialized_len)
+{
+ uint8_t *out_buf;
+ size_t fixed_len = sizeof(struct ts_test_runner_test_result);
+ size_t required_space = fixed_len;
+ size_t name_len = strlen(result->name);
+ size_t group_len = strlen(result->group);
+
+ if (name_len) required_space += tlv_required_space(name_len);
+ if (group_len) required_space += tlv_required_space(group_len);
+
+ *serialized_len = required_space;
+
+ out_buf = malloc(required_space);
+
+ if (out_buf) {
+
+ struct ts_test_runner_test_result result_msg;
+ result_msg.run_state = result->run_state;
+ result_msg.fail_line = result->fail_line;
+
+ memcpy(out_buf, &result_msg, fixed_len);
+
+ struct tlv_iterator tlv_iter;
+ tlv_iterator_begin(&tlv_iter, (uint8_t*)out_buf + fixed_len, required_space - fixed_len);
+
+ if (name_len) {
+
+ struct tlv_record record;
+ record.tag = TS_TEST_RUNNER_TEST_RESULT_TAG_NAME;
+ record.length = name_len;
+ record.value = result->name;
+ tlv_encode(&tlv_iter, &record);
+ }
+
+ if (group_len) {
+
+ struct tlv_record record;
+ record.tag = TS_TEST_RUNNER_TEST_RESULT_TAG_GROUP;
+ record.length = group_len;
+ record.value = result->group;
+ tlv_encode(&tlv_iter, &record);
+ }
+ }
+
+ return out_buf;
+}
+
+static rpc_status_t serialize_test_results(struct call_param_buf *resp_buf,
+ const struct test_summary *summary,
+ const struct test_result *results)
+{
+ size_t space_used = 0;
+ rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+
+ /* Serialize fixed size summary */
+ struct ts_test_runner_result_summary summary_msg;
+ size_t fixed_len = sizeof(struct ts_test_runner_result_summary);
+
+ summary_msg.num_tests = summary->num_tests;
+ summary_msg.num_passed = summary->num_passed;
+ summary_msg.num_failed = summary->num_failed;
+
+ if (fixed_len + space_used <= resp_buf->size) {
+
+ memcpy((uint8_t*)resp_buf->data + space_used, &summary_msg, fixed_len);
+ space_used += fixed_len;
+
+ /* Serialize test result objects */
+ struct tlv_iterator resp_iter;
+ tlv_iterator_begin(&resp_iter, (uint8_t*)resp_buf->data + space_used, resp_buf->size - space_used);
+
+ for (int i = 0; (i < summary->num_results) && (rpc_status == TS_RPC_CALL_ACCEPTED); ++i) {
+
+ size_t serialised_len;
+ uint8_t *serialize_buf = serialize_test_result(&results[i], &serialised_len);
+
+ if (serialize_buf) {
+
+ struct tlv_record result_record;
+ result_record.tag = TS_TEST_RUNNER_TEST_RESULT_TAG;
+ result_record.length = serialised_len;
+ result_record.value = serialize_buf;
+
+ if (tlv_encode(&resp_iter, &result_record)) {
+
+ space_used += tlv_required_space(serialised_len);
+ }
+ else {
+ /* Insufficient buffer space */
+ rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE;
+ }
+
+ free(serialize_buf);
+ }
+ }
+ }
+
+ resp_buf->data_len = space_used;
+
+ return rpc_status;
+}
+
+/* Operation: run_tests */
+static rpc_status_t deserialize_run_tests_req(const struct call_param_buf *req_buf,
+ struct test_spec *test_spec)
+{
+ return deserialize_test_spec(req_buf, test_spec);
+}
+
+static rpc_status_t serialize_run_tests_resp(struct call_param_buf *resp_buf,
+ const struct test_summary *summary,
+ const struct test_result *results)
+{
+ return serialize_test_results(resp_buf, summary, results);
+}
+
+/* Operation: list_tests */
+static rpc_status_t deserialize_list_tests_req(const struct call_param_buf *req_buf,
+ struct test_spec *test_spec)
+{
+ return deserialize_test_spec(req_buf, test_spec);
+}
+
+static rpc_status_t serialize_list_tests_resp(struct call_param_buf *resp_buf,
+ const struct test_summary *summary,
+ const struct test_result *results)
+{
+ return serialize_test_results(resp_buf, summary, results);
+}
+
+/* Singleton method to provide access to the serializer instance */
+const struct test_runner_provider_serializer *packedc_test_runner_provider_serializer_instance(void)
+{
+ static const struct test_runner_provider_serializer instance = {
+ deserialize_run_tests_req,
+ serialize_run_tests_resp,
+ deserialize_list_tests_req,
+ serialize_list_tests_resp
+ };
+
+ return &instance;
+}
diff --git a/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h b/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h
new file mode 100644
index 0000000..c956bc3
--- /dev/null
+++ b/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PACKEDC_TEST_RUNNER_PROVIDER_SERIALIZER_H
+#define PACKEDC_TEST_RUNNER_PROVIDER_SERIALIZER_H
+
+#include <service/test_runner/provider/serializer/test_runner_provider_serializer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Singleton method to provide access to the packed-c serializer
+ * for the test_runner service provider.
+ */
+const struct test_runner_provider_serializer *packedc_test_runner_provider_serializer_instance(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PACKEDC_TEST_RUNNER_PROVIDER_SERIALIZER_H */
diff --git a/components/service/test_runner/provider/serializer/test_runner_provider_serializer.h b/components/service/test_runner/provider/serializer/test_runner_provider_serializer.h
new file mode 100644
index 0000000..bd457f7
--- /dev/null
+++ b/components/service/test_runner/provider/serializer/test_runner_provider_serializer.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TEST_RUNNER_PROVIDER_SERIALIZER_H
+#define TEST_RUNNER_PROVIDER_SERIALIZER_H
+
+#include <rpc/common/endpoint/rpc_interface.h>
+#include <service/test_runner/common/test_runner.h>
+
+/* Provides a common interface for parameter serialization operations
+ * for the test_runner service provider. Allows alternative serialization
+ * protocols to be used without hard-wiring a particular protocol
+ * into the service provider code. A concrete serializer must
+ * implement this interface.
+ */
+struct test_runner_provider_serializer {
+
+ /* Operation: run_tests */
+ rpc_status_t (*deserialize_run_tests_req)(const struct call_param_buf *req_buf,
+ struct test_spec *test_spec);
+
+ rpc_status_t (*serialize_run_tests_resp)(struct call_param_buf *resp_buf,
+ const struct test_summary *summary,
+ const struct test_result *results);
+
+ /* Operation: list_tests */
+ rpc_status_t (*deserialize_list_tests_req)(const struct call_param_buf *req_buf,
+ struct test_spec *test_spec);
+
+ rpc_status_t (*serialize_list_tests_resp)(struct call_param_buf *resp_buf,
+ const struct test_summary *summary,
+ const struct test_result *results);
+};
+
+#endif /* TEST_RUNNER_PROVIDER_SERIALIZER_H */
diff --git a/components/service/test_runner/provider/test_runner_backend.h b/components/service/test_runner/provider/test_runner_backend.h
new file mode 100644
index 0000000..9c9cbc8
--- /dev/null
+++ b/components/service/test_runner/provider/test_runner_backend.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CPPUTEST_TEST_RUNNER_BACKEND_H
+#define CPPUTEST_TEST_RUNNER_BACKEND_H
+
+#include <stddef.h>
+#include <service/test_runner/common/test_runner.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct test_runner_provider;
+
+/**
+ * Provides an abstract interface for a backend test runner. A
+ * concrete implementation will map methods to a test framework
+ * such as CppUtets. test_runner objects may be linked to
+ * accommodate a mix of backend test frameworks.
+ */
+struct test_runner_backend
+{
+ /* Return the number of tests that are qualified by the test spec */
+ size_t (*count_tests)(const struct test_spec *spec);
+
+ /* Run a set of tests according to the provided test_spec */
+ int (*run_tests)(const struct test_spec *spec,
+ struct test_summary *summary, struct test_result *results, size_t result_limit);
+
+ /* List a set of tests according to the provided test_spec */
+ void (*list_tests)(const struct test_spec *spec,
+ struct test_summary *summary, struct test_result *results, size_t result_limit);
+
+ /* Used by the test_runner_provider to maintain a linked list */
+ struct test_runner_backend *next;
+};
+
+/**
+ * A concrete test_runner may provide an implementation of this function if it
+ * is to be the default test runner for a deployment. Additional test runners
+ * may be registered but there can only be one default for a deployment.
+ */
+void test_runner_register_default_backend(struct test_runner_provider *context);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CPPUTEST_TEST_RUNNER_BACKEND_H */
diff --git a/components/service/test_runner/provider/test_runner_provider.c b/components/service/test_runner/provider/test_runner_provider.c
new file mode 100644
index 0000000..4b61bc1
--- /dev/null
+++ b/components/service/test_runner/provider/test_runner_provider.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <stdlib.h>
+#include <stdbool.h>
+#include <protocols/service/test_runner/packed-c/opcodes.h>
+#include <protocols/service/test_runner/packed-c/status.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include "test_runner_provider.h"
+#include "test_runner_backend.h"
+
+/* Service request handlers */
+static rpc_status_t run_tests_handler(void *context, struct call_req* req);
+static rpc_status_t list_tests_handler(void *context, struct call_req* req);
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+ {TS_TEST_RUNNER_OPCODE_RUN_TESTS, run_tests_handler},
+ {TS_TEST_RUNNER_OPCODE_LIST_TESTS, list_tests_handler}
+};
+
+struct rpc_interface *test_runner_provider_init(struct test_runner_provider *context)
+{
+ struct rpc_interface *rpc_interface = NULL;
+
+ if (context) {
+
+ for (size_t encoding = 0; encoding < TS_RPC_ENCODING_LIMIT; ++encoding)
+ context->serializers[encoding] = NULL;
+
+ context->backend_list = NULL;
+
+ service_provider_init(&context->base_provider, context,
+ handler_table, sizeof(handler_table)/sizeof(struct service_handler));
+
+ rpc_interface = service_provider_get_rpc_interface(&context->base_provider);
+
+ /* Allow a deployment specific test_runner backend to be registrered */
+ test_runner_register_default_backend(context);
+ }
+
+ return rpc_interface;
+}
+
+void test_runner_provider_deinit(struct test_runner_provider *context)
+{
+ (void)context;
+}
+
+void test_runner_provider_register_serializer(struct test_runner_provider *context,
+ unsigned int encoding, const struct test_runner_provider_serializer *serializer)
+{
+ if (encoding < TS_RPC_ENCODING_LIMIT)
+ context->serializers[encoding] = serializer;
+}
+
+void test_runner_provider_register_backend(struct test_runner_provider *context,
+ struct test_runner_backend *backend)
+{
+ /* Insert into list of backend test runners */
+ backend->next = context->backend_list;
+ context->backend_list = backend;
+}
+
+static const struct test_runner_provider_serializer* get_test_runner_serializer(
+ struct test_runner_provider *context, const struct call_req *req)
+{
+ const struct test_runner_provider_serializer* serializer = NULL;
+ unsigned int encoding = call_req_get_encoding(req);
+
+ if (encoding < TS_RPC_ENCODING_LIMIT) serializer = context->serializers[encoding];
+
+ return serializer;
+}
+
+static struct test_result *alloc_result_buf(struct test_runner_provider *context,
+ const struct test_spec *test_spec, size_t *result_limit)
+{
+ struct test_result *space = NULL;
+ size_t total_tests = 0;
+ struct test_runner_backend *backend = context->backend_list;
+
+ while (backend) {
+
+ total_tests += backend->count_tests(test_spec);
+ backend = backend->next;
+ }
+
+ space = malloc(total_tests * sizeof(struct test_result));
+
+ *result_limit = total_tests;
+ return space;
+}
+
+static int run_qualifying_tests(struct test_runner_provider *context, bool list_only, const struct test_spec *spec,
+ struct test_summary *summary, struct test_result *results, size_t result_limit)
+{
+ int test_status = TS_TEST_RUNNER_STATUS_SUCCESS;
+ size_t results_used = 0;
+ struct test_runner_backend *backend = context->backend_list;
+
+ summary->num_tests = 0;
+ summary->num_results = 0;
+ summary->num_passed = 0;
+ summary->num_failed = 0;
+
+ while (backend && (test_status == TS_TEST_RUNNER_STATUS_SUCCESS)) {
+
+ struct test_summary interim_summary;
+
+ if (list_only) {
+
+ backend->list_tests(spec, &interim_summary,
+ &results[summary->num_results],
+ result_limit - summary->num_results);
+ }
+ else {
+
+ test_status = backend->run_tests(spec, &interim_summary,
+ &results[summary->num_results],
+ result_limit - summary->num_results);
+ }
+
+ summary->num_tests += interim_summary.num_tests;
+ summary->num_results += interim_summary.num_results;
+ summary->num_passed += interim_summary.num_passed;
+ summary->num_failed += interim_summary.num_failed;
+
+ backend = backend->next;
+ }
+
+ return test_status;
+}
+
+static rpc_status_t run_tests_handler(void *context, struct call_req* req)
+{
+ struct test_runner_provider *this_instance = (struct test_runner_provider*)context;
+ rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+ struct test_spec test_spec;
+
+ struct call_param_buf *req_buf = call_req_get_req_buf(req);
+ const struct test_runner_provider_serializer *serializer = get_test_runner_serializer(this_instance, req);
+
+ if (serializer)
+ rpc_status = serializer->deserialize_run_tests_req(req_buf, &test_spec);
+
+ if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+ int test_status;
+ struct test_summary summary;
+ size_t result_limit = 0;
+ struct test_result *result_buf = alloc_result_buf(this_instance, &test_spec, &result_limit);
+
+ test_status = run_qualifying_tests(this_instance, false, &test_spec, &summary, result_buf, result_limit);
+
+ call_req_set_opstatus(req, test_status);
+
+ if (test_status == TS_TEST_RUNNER_STATUS_SUCCESS) {
+
+ struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+ rpc_status = serializer->serialize_run_tests_resp(resp_buf, &summary, result_buf);
+
+ free(result_buf);
+ }
+ }
+
+ return rpc_status;
+}
+
+static rpc_status_t list_tests_handler(void *context, struct call_req* req)
+{
+ struct test_runner_provider *this_instance = (struct test_runner_provider*)context;
+ rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+ struct test_spec test_spec;
+
+ struct call_param_buf *req_buf = call_req_get_req_buf(req);
+ const struct test_runner_provider_serializer *serializer = get_test_runner_serializer(this_instance, req);
+
+ if (serializer)
+ rpc_status = serializer->deserialize_list_tests_req(req_buf, &test_spec);
+
+ if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+ int test_status;
+ struct test_summary summary;
+ size_t result_limit = 0;
+ struct test_result *result_buf = alloc_result_buf(this_instance, &test_spec, &result_limit);
+
+ test_status = run_qualifying_tests(this_instance, true, &test_spec, &summary, result_buf, result_limit);
+
+ call_req_set_opstatus(req, test_status);
+
+ if (test_status == TS_TEST_RUNNER_STATUS_SUCCESS) {
+
+ struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+ rpc_status = serializer->serialize_list_tests_resp(resp_buf, &summary, result_buf);
+
+ free(result_buf);
+ }
+ }
+
+ return rpc_status;
+}
\ No newline at end of file
diff --git a/components/service/test_runner/provider/test_runner_provider.h b/components/service/test_runner/provider/test_runner_provider.h
new file mode 100644
index 0000000..e5c9230
--- /dev/null
+++ b/components/service/test_runner/provider/test_runner_provider.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TEST_RUNNER_PROVIDER_H
+#define TEST_RUNNER_PROVIDER_H
+
+#include <rpc/common/endpoint/rpc_interface.h>
+#include <rpc_caller.h>
+#include <service/common/provider/service_provider.h>
+#include <service/test_runner/provider/serializer/test_runner_provider_serializer.h>
+#include <protocols/rpc/common/packed-c/encoding.h>
+#include "test_runner_backend.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* test_runner_provider serice provider state */
+struct test_runner_provider
+{
+ struct service_provider base_provider;
+ const struct test_runner_provider_serializer *serializers[TS_RPC_ENCODING_LIMIT];
+ struct test_runner_backend *backend_list;
+};
+
+struct rpc_interface *test_runner_provider_init(struct test_runner_provider *context);
+
+void test_runner_provider_deinit(struct test_runner_provider *context);
+
+void test_runner_provider_register_serializer(struct test_runner_provider *context,
+ unsigned int encoding, const struct test_runner_provider_serializer *serializer);
+
+void test_runner_provider_register_backend(struct test_runner_provider *context,
+ struct test_runner_backend *backend);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CPPUTEST_TEST_RUNNER_PROVIDER_H */