aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjulhal01 <julian.hall@arm.com>2021-02-05 17:30:49 +0000
committerGyorgy Szing <Gyorgy.Szing@arm.com>2021-04-14 16:59:24 +0200
commit3ec4c32dc835bffa0f5af07b3e170f4b8586a799 (patch)
treeba25f104b66f4a22c1364e991c0f726ac4ff4efe
parent2c18fbfda57fdc7b3971bffbe5902caf52a9530a (diff)
downloadtrusted-services-3ec4c32dc835bffa0f5af07b3e170f4b8586a799.tar.gz
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
-rw-r--r--components/app/remote-test-runner/component.cmake14
-rw-r--r--components/app/remote-test-runner/remote_test_runner.cpp147
-rw-r--r--components/app/remote-test-runner/remote_test_runner.h40
-rw-r--r--components/common/tlv/test/tlv_tests.cpp20
-rw-r--r--components/common/tlv/tlv.c7
-rw-r--r--components/service/crypto/provider/serializer/packed-c/packedc_crypto_provider_serializer.c36
-rw-r--r--components/service/locator/linux/ffa/linuxffa_location_strategy.c1
-rw-r--r--components/service/locator/standalone/services/test-runner/component.cmake14
-rw-r--r--components/service/locator/standalone/services/test-runner/test_runner_service_context.cpp35
-rw-r--r--components/service/locator/standalone/services/test-runner/test_runner_service_context.h28
-rw-r--r--components/service/locator/standalone/standalone_env.cpp7
-rw-r--r--components/service/test_runner/client/cpp/component.cmake14
-rw-r--r--components/service/test_runner/client/cpp/test_runner_client.cpp248
-rw-r--r--components/service/test_runner/client/cpp/test_runner_client.h59
-rw-r--r--components/service/test_runner/common/test_runner.h67
-rw-r--r--components/service/test_runner/provider/backend/mock/component.cmake14
-rw-r--r--components/service/test_runner/provider/backend/mock/mock_test_runner.c133
-rw-r--r--components/service/test_runner/provider/backend/null/component.cmake14
-rw-r--r--components/service/test_runner/provider/backend/null/null_test_runner.c18
-rw-r--r--components/service/test_runner/provider/component.cmake13
-rw-r--r--components/service/test_runner/provider/serializer/packed-c/component.cmake13
-rw-r--r--components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c190
-rw-r--r--components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h25
-rw-r--r--components/service/test_runner/provider/serializer/test_runner_provider_serializer.h38
-rw-r--r--components/service/test_runner/provider/test_runner_backend.h53
-rw-r--r--components/service/test_runner/provider/test_runner_provider.c205
-rw-r--r--components/service/test_runner/provider/test_runner_provider.h43
-rw-r--r--components/service/test_runner/test/service/component.cmake14
-rw-r--r--components/service/test_runner/test/service/test_runner_service_tests.cpp201
-rw-r--r--deployments/component-test/component-test.cmake4
-rw-r--r--deployments/env-test/opteesp/.gitignore1
-rw-r--r--deployments/env-test/opteesp/CMakeLists.txt129
-rw-r--r--deployments/env-test/opteesp/env_test.c91
-rw-r--r--deployments/env-test/opteesp/env_test.h18
-rw-r--r--deployments/env-test/opteesp/env_test_config.c16
-rw-r--r--deployments/env-test/opteesp/env_test_config.h18
-rw-r--r--deployments/env-test/opteesp/optee_sp_user_defines.h22
-rw-r--r--deployments/libts/linux-pc/CMakeLists.txt4
-rw-r--r--deployments/ts-remote-test/arm-linux/.gitignore1
-rw-r--r--deployments/ts-remote-test/arm-linux/CMakeLists.txt31
-rw-r--r--deployments/ts-remote-test/linux-pc/CMakeLists.txt24
-rw-r--r--deployments/ts-remote-test/ts-remote-test.cmake50
-rw-r--r--deployments/ts-remote-test/ts-remote-test.cpp55
-rw-r--r--deployments/ts-service-test/linux-pc/CMakeLists.txt12
-rw-r--r--protocols/service/test_runner/packed-c/list_tests.h20
-rw-r--r--protocols/service/test_runner/packed-c/opcodes.h16
-rw-r--r--protocols/service/test_runner/packed-c/run_tests.h23
-rw-r--r--protocols/service/test_runner/packed-c/status.h34
-rw-r--r--protocols/service/test_runner/packed-c/test_result.h58
-rw-r--r--protocols/service/test_runner/packed-c/test_spec.h29
50 files changed, 2346 insertions, 21 deletions
diff --git a/components/app/remote-test-runner/component.cmake b/components/app/remote-test-runner/component.cmake
new file mode 100644
index 0000000..a47ec42
--- /dev/null
+++ b/components/app/remote-test-runner/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}/remote_test_runner.cpp"
+ )
+
diff --git a/components/app/remote-test-runner/remote_test_runner.cpp b/components/app/remote-test-runner/remote_test_runner.cpp
new file mode 100644
index 0000000..681c746
--- /dev/null
+++ b/components/app/remote-test-runner/remote_test_runner.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "remote_test_runner.h"
+#include <protocols/service/test_runner/packed-c/status.h>
+#include <vector>
+#include <string>
+#include <cstring>
+#include <cstdio>
+
+remote_test_runner::remote_test_runner() :
+ m_client(NULL)
+{
+
+}
+
+remote_test_runner::remote_test_runner(test_runner_client *client) :
+ m_client(client)
+{
+
+}
+
+remote_test_runner::~remote_test_runner()
+{
+
+}
+
+void remote_test_runner::set_client(test_runner_client *client)
+{
+ m_client = client;
+}
+
+int remote_test_runner::execute(int argc, char *argv[])
+{
+ int test_status = TS_TEST_RUNNER_STATUS_ERROR;
+ struct test_spec spec;
+
+ /* Parse command line parameters */
+ bool list_only = option_selected("-l", argc, argv);
+ parse_test_spec_params(argc, argv, spec);
+
+ /* Run or list tests qualified bu spec */
+ struct test_summary summary;
+ std::vector<struct test_result> results;
+
+ if (list_only) {
+
+ test_status = m_client->list_tests(spec, summary, results);
+ output_list(summary, results);
+ }
+ else {
+
+ test_status = m_client->run_tests(spec, summary, results);
+ output_results(summary, results);
+ }
+
+ if (test_status != TS_TEST_RUNNER_STATUS_SUCCESS) {
+
+ printf("Tests failed to run with error: %d\n", test_status);
+ }
+
+ return test_status;
+}
+
+void remote_test_runner::parse_test_spec_params(int argc, char *argv[], struct test_spec &spec) const
+{
+ std::string name = parse_option("-n", argc, argv);
+ std::string group = parse_option("-g", argc, argv);
+
+ memset(spec.name, 0, TEST_NAME_MAX_LEN);
+ name.copy(spec.name, TEST_NAME_MAX_LEN - 1);
+
+ memset(spec.group, 0, TEST_GROUP_MAX_LEN);
+ group.copy(spec.group, TEST_GROUP_MAX_LEN - 1);
+}
+
+std::string remote_test_runner::parse_option(const char *option_switch, int argc, char *argv[]) const
+{
+ std::string option;
+
+ for (int i = 1; i + 1 < argc; ++i) {
+
+ if (strcmp(argv[i], option_switch) == 0) {
+
+ option = std::string(argv[i +1]);
+ break;
+ }
+ }
+
+ return option;
+}
+
+bool remote_test_runner::option_selected(const char *option_switch, int argc, char *argv[]) const
+{
+ bool selected = false;
+
+ for (int i = 1; (i < argc) && !selected; ++i) {
+
+ selected = (strcmp(argv[i], option_switch) == 0);
+ }
+
+ return selected;
+}
+
+void remote_test_runner::output_summary(const struct test_summary &summary)
+{
+ printf("\n");
+
+ if (summary.num_failed == 0) printf("OK (");
+ else printf("Errors (%d failures, ", summary.num_failed);
+
+ printf("%d tests, %d ran)\n", summary.num_tests, summary.num_failed + summary.num_passed);
+}
+
+
+void remote_test_runner::output_list(const struct test_summary &summary,
+ const std::vector<struct test_result> &results)
+{
+
+}
+
+void remote_test_runner::output_results(const struct test_summary &summary,
+ const std::vector<struct test_result> &results)
+{
+ for (int i = 0; i < results.size(); ++i) {
+
+ printf("TEST(%s, %s) ", results[i].group, results[i].name);
+
+ if (results[i].run_state == TEST_RUN_STATE_PASSED) {
+
+ printf("OK\n");
+ }
+ else if (results[i].run_state == TEST_RUN_STATE_FAILED) {
+
+ printf("error\n");
+ }
+ else {
+
+ printf("did not run\n");
+ }
+ }
+
+ output_summary(summary);
+}
diff --git a/components/app/remote-test-runner/remote_test_runner.h b/components/app/remote-test-runner/remote_test_runner.h
new file mode 100644
index 0000000..9d1fca4
--- /dev/null
+++ b/components/app/remote-test-runner/remote_test_runner.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef REMOTE_TEST_RUNNER_H
+#define REMOTE_TEST_RUNNER_H
+
+#include <service/test_runner/client/cpp/test_runner_client.h>
+#include <service/test_runner/common/test_runner.h>
+
+/*
+ * Provides a command line interface for running remote tests.
+ */
+class remote_test_runner
+{
+public:
+ remote_test_runner();
+ remote_test_runner(test_runner_client *client);
+ virtual ~remote_test_runner();
+
+ void set_client(test_runner_client *client);
+
+ int execute(int argc, char *argv[]);
+
+private:
+
+ void parse_test_spec_params(int argc, char *argv[], struct test_spec &spec) const;
+ std::string parse_option(const char *option_switch, int argc, char *argv[]) const;
+ bool option_selected(const char *option_switch, int argc, char *argv[]) const;
+
+ void output_summary(const struct test_summary &summary);
+ void output_list(const struct test_summary &summary, const std::vector<struct test_result> &results);
+ void output_results(const struct test_summary &summary, const std::vector<struct test_result> &results);
+
+ test_runner_client *m_client;
+};
+
+#endif /* REMOTE_TEST_RUNNER_H */
diff --git a/components/common/tlv/test/tlv_tests.cpp b/components/common/tlv/test/tlv_tests.cpp
index 661a810..5d8a50d 100644
--- a/components/common/tlv/test/tlv_tests.cpp
+++ b/components/common/tlv/test/tlv_tests.cpp
@@ -76,6 +76,26 @@ TEST(TlvTests, findAndDecode)
UNSIGNED_LONGS_EQUAL(1, decoded_record.length);
}
+TEST(TlvTests, findAndDecodeMissingOptional)
+{
+ struct tlv_const_iterator iter;
+ struct tlv_record decoded_record;
+
+ /*
+ * Checks finding a missing record is correctly
+ * identified as not present but that the following
+ * record is found.
+ */
+ const uint8_t encoded_records[] = {
+ 0x00, 0x07, 0x00, 0x02, 0x77, 0x77
+ };
+
+ tlv_const_iterator_begin(&iter, encoded_records, sizeof(encoded_records));
+ CHECK(!tlv_find_decode(&iter, 0x0001, &decoded_record));
+ CHECK(tlv_find_decode(&iter, 0x0007, &decoded_record));
+ CHECK_EQUAL(2, decoded_record.length);
+}
+
TEST(TlvTests, decodeBadRecords)
{
struct tlv_const_iterator iter;
diff --git a/components/common/tlv/tlv.c b/components/common/tlv/tlv.c
index 1308832..80c1edb 100644
--- a/components/common/tlv/tlv.c
+++ b/components/common/tlv/tlv.c
@@ -82,10 +82,13 @@ bool tlv_decode(struct tlv_const_iterator *iter, struct tlv_record *output)
bool tlv_find_decode(struct tlv_const_iterator *iter, uint16_t tag, struct tlv_record *output)
{
- while (tlv_decode(iter, output)) {
+ struct tlv_const_iterator temp_iter = *iter;
+
+ while (tlv_decode(&temp_iter, output)) {
if (output->tag == tag) {
- /* Found a record */
+ /* Found a record - update input iterator to next record */
+ *iter = temp_iter;
return true;
}
else if (output->tag > tag) {
diff --git a/components/service/crypto/provider/serializer/packed-c/packedc_crypto_provider_serializer.c b/components/service/crypto/provider/serializer/packed-c/packedc_crypto_provider_serializer.c
index f39aa1c..078a88f 100644
--- a/components/service/crypto/provider/serializer/packed-c/packedc_crypto_provider_serializer.c
+++ b/components/service/crypto/provider/serializer/packed-c/packedc_crypto_provider_serializer.c
@@ -233,7 +233,7 @@ static rpc_status_t deserialize_import_key_req(const struct call_param_buf *req_
if (expected_fixed_len <= req_buf->data_len) {
- struct tlv_const_iterator resp_iter;
+ struct tlv_const_iterator req_iter;
struct tlv_record decoded_record;
rpc_status = TS_RPC_CALL_ACCEPTED;
@@ -241,11 +241,11 @@ static rpc_status_t deserialize_import_key_req(const struct call_param_buf *req_
memcpy(&recv_msg, req_buf->data, expected_fixed_len);
packedc_crypto_provider_translate_key_attributes(attributes, &recv_msg.attributes);
- tlv_const_iterator_begin(&resp_iter,
+ tlv_const_iterator_begin(&req_iter,
(uint8_t*)req_buf->data + expected_fixed_len,
req_buf->data_len - expected_fixed_len);
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_IMPORT_KEY_IN_TAG_DATA, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_IMPORT_KEY_IN_TAG_DATA, &decoded_record)) {
if (decoded_record.length <= *data_len) {
@@ -296,7 +296,7 @@ static rpc_status_t deserialize_sign_hash_req(const struct call_param_buf *req_b
if (expected_fixed_len <= req_buf->data_len) {
- struct tlv_const_iterator resp_iter;
+ struct tlv_const_iterator req_iter;
struct tlv_record decoded_record;
rpc_status = TS_RPC_CALL_ACCEPTED;
@@ -306,11 +306,11 @@ static rpc_status_t deserialize_sign_hash_req(const struct call_param_buf *req_b
*handle = recv_msg.handle;
*alg = recv_msg.alg;
- tlv_const_iterator_begin(&resp_iter,
+ tlv_const_iterator_begin(&req_iter,
(uint8_t*)req_buf->data + expected_fixed_len,
req_buf->data_len - expected_fixed_len);
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_SIGN_HASH_IN_TAG_HASH, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_SIGN_HASH_IN_TAG_HASH, &decoded_record)) {
if (decoded_record.length <= *hash_len) {
@@ -365,7 +365,7 @@ static rpc_status_t deserialize_verify_hash_req(const struct call_param_buf *req
if (expected_fixed_len <= req_buf->data_len) {
- struct tlv_const_iterator resp_iter;
+ struct tlv_const_iterator req_iter;
struct tlv_record decoded_record;
rpc_status = TS_RPC_CALL_ACCEPTED;
@@ -375,11 +375,11 @@ static rpc_status_t deserialize_verify_hash_req(const struct call_param_buf *req
*handle = recv_msg.handle;
*alg = recv_msg.alg;
- tlv_const_iterator_begin(&resp_iter,
+ tlv_const_iterator_begin(&req_iter,
(uint8_t*)req_buf->data + expected_fixed_len,
req_buf->data_len - expected_fixed_len);
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_VERIFY_HASH_IN_TAG_HASH, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_VERIFY_HASH_IN_TAG_HASH, &decoded_record)) {
if (decoded_record.length <= *hash_len) {
@@ -396,7 +396,7 @@ static rpc_status_t deserialize_verify_hash_req(const struct call_param_buf *req
*hash_len = 0;
}
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_VERIFY_HASH_IN_TAG_SIGNATURE, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_VERIFY_HASH_IN_TAG_SIGNATURE, &decoded_record)) {
if (decoded_record.length <= *sig_len) {
@@ -429,7 +429,7 @@ static rpc_status_t deserialize_asymmetric_decrypt_req(const struct call_param_b
if (expected_fixed_len <= req_buf->data_len) {
- struct tlv_const_iterator resp_iter;
+ struct tlv_const_iterator req_iter;
struct tlv_record decoded_record;
rpc_status = TS_RPC_CALL_ACCEPTED;
@@ -439,11 +439,11 @@ static rpc_status_t deserialize_asymmetric_decrypt_req(const struct call_param_b
*handle = recv_msg.handle;
*alg = recv_msg.alg;
- tlv_const_iterator_begin(&resp_iter,
+ tlv_const_iterator_begin(&req_iter,
(uint8_t*)req_buf->data + expected_fixed_len,
req_buf->data_len - expected_fixed_len);
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_ASYMMETRIC_DECRYPT_IN_TAG_CIPHERTEXT, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_ASYMMETRIC_DECRYPT_IN_TAG_CIPHERTEXT, &decoded_record)) {
if (decoded_record.length <= *ciphertext_len) {
@@ -460,7 +460,7 @@ static rpc_status_t deserialize_asymmetric_decrypt_req(const struct call_param_b
*ciphertext_len = 0;
}
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_ASYMMETRIC_DECRYPT_IN_TAG_SALT, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_ASYMMETRIC_DECRYPT_IN_TAG_SALT, &decoded_record)) {
if (decoded_record.length <= *salt_len) {
@@ -515,7 +515,7 @@ static rpc_status_t deserialize_asymmetric_encrypt_req(const struct call_param_b
if (expected_fixed_len <= req_buf->data_len) {
- struct tlv_const_iterator resp_iter;
+ struct tlv_const_iterator req_iter;
struct tlv_record decoded_record;
rpc_status = TS_RPC_CALL_ACCEPTED;
@@ -525,11 +525,11 @@ static rpc_status_t deserialize_asymmetric_encrypt_req(const struct call_param_b
*handle = recv_msg.handle;
*alg = recv_msg.alg;
- tlv_const_iterator_begin(&resp_iter,
+ tlv_const_iterator_begin(&req_iter,
(uint8_t*)req_buf->data + expected_fixed_len,
req_buf->data_len - expected_fixed_len);
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_ASYMMETRIC_ENCRYPT_IN_TAG_PLAINTEXT, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_ASYMMETRIC_ENCRYPT_IN_TAG_PLAINTEXT, &decoded_record)) {
if (decoded_record.length <= *plaintext_len) {
@@ -546,7 +546,7 @@ static rpc_status_t deserialize_asymmetric_encrypt_req(const struct call_param_b
*plaintext_len = 0;
}
- if (tlv_find_decode(&resp_iter, TS_CRYPTO_ASYMMETRIC_ENCRYPT_IN_TAG_SALT, &decoded_record)) {
+ if (tlv_find_decode(&req_iter, TS_CRYPTO_ASYMMETRIC_ENCRYPT_IN_TAG_SALT, &decoded_record)) {
if (decoded_record.length <= *salt_len) {
diff --git a/components/service/locator/linux/ffa/linuxffa_location_strategy.c b/components/service/locator/linux/ffa/linuxffa_location_strategy.c
index e965076..f7e78ad 100644
--- a/components/service/locator/linux/ffa/linuxffa_location_strategy.c
+++ b/components/service/locator/linux/ffa/linuxffa_location_strategy.c
@@ -90,6 +90,7 @@ static size_t suggest_tf_org_partition_uuids(const char *sn, struct uuid_canonic
{
{"crypto", "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0"},
{"secure-storage", "dc1eef48-b17a-4ccf-ac8b-dfcff7711b14"},
+ {"test-runner", "33c75baf-ac6a-4fe4-8ac7-e9909bee2d17"},
{NULL, NULL}
};
diff --git a/components/service/locator/standalone/services/test-runner/component.cmake b/components/service/locator/standalone/services/test-runner/component.cmake
new file mode 100644
index 0000000..e30f2c6
--- /dev/null
+++ b/components/service/locator/standalone/services/test-runner/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}/test_runner_service_context.cpp"
+ )
+
diff --git a/components/service/locator/standalone/services/test-runner/test_runner_service_context.cpp b/components/service/locator/standalone/services/test-runner/test_runner_service_context.cpp
new file mode 100644
index 0000000..103f188
--- /dev/null
+++ b/components/service/locator/standalone/services/test-runner/test_runner_service_context.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "test_runner_service_context.h"
+#include <service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h>
+
+test_runner_service_context::test_runner_service_context(const char *sn) :
+ standalone_service_context(sn),
+ m_test_runner_provider()
+{
+
+}
+
+test_runner_service_context::~test_runner_service_context()
+{
+
+}
+
+void test_runner_service_context::do_init()
+{
+ struct rpc_interface *test_runner_ep = test_runner_provider_init(&m_test_runner_provider);
+
+ test_runner_provider_register_serializer(&m_test_runner_provider,
+ TS_RPC_ENCODING_PACKED_C, packedc_test_runner_provider_serializer_instance());
+
+ standalone_service_context::set_rpc_interface(test_runner_ep);
+}
+
+void test_runner_service_context::do_deinit()
+{
+ test_runner_provider_deinit(&m_test_runner_provider);
+}
diff --git a/components/service/locator/standalone/services/test-runner/test_runner_service_context.h b/components/service/locator/standalone/services/test-runner/test_runner_service_context.h
new file mode 100644
index 0000000..8474ec2
--- /dev/null
+++ b/components/service/locator/standalone/services/test-runner/test_runner_service_context.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STANDALONE_TEST_RUNNER_SERVICE_CONTEXT_H
+#define STANDALONE_TEST_RUNNER_SERVICE_CONTEXT_H
+
+#include <service/locator/standalone/standalone_service_context.h>
+#include <rpc/direct/direct_caller.h>
+#include <service/test_runner/provider/test_runner_provider.h>
+
+class test_runner_service_context : public standalone_service_context
+{
+public:
+ test_runner_service_context(const char *sn);
+ virtual ~test_runner_service_context();
+
+private:
+
+ void do_init();
+ void do_deinit();
+
+ struct test_runner_provider m_test_runner_provider;
+};
+
+#endif /* STANDALONE_TEST_RUNNER_SERVICE_CONTEXT_H */
diff --git a/components/service/locator/standalone/standalone_env.cpp b/components/service/locator/standalone/standalone_env.cpp
index 80d1777..41dd206 100644
--- a/components/service/locator/standalone/standalone_env.cpp
+++ b/components/service/locator/standalone/standalone_env.cpp
@@ -1,11 +1,12 @@
/*
- * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <service_locator.h>
#include <service/locator/standalone/services/crypto/crypto_service_context.h>
+#include <service/locator/standalone/services/test-runner/test_runner_service_context.h>
#include "standalone_location_strategy.h"
#include "standalone_service_registry.h"
@@ -13,5 +14,9 @@ void service_locator_envinit(void)
{
static crypto_service_context crypto_context("sn:trustedfirmware.org:crypto:0");
standalone_service_registry::instance()->regsiter_service_instance(&crypto_context);
+
+ static test_runner_service_context test_runner_context("sn:trustedfirmware.org:test-runner:0");
+ standalone_service_registry::instance()->regsiter_service_instance(&test_runner_context);
+
service_locator_register_strategy(standalone_location_strategy());
} \ No newline at end of file
diff --git a/components/service/test_runner/client/cpp/component.cmake b/components/service/test_runner/client/cpp/component.cmake
new file mode 100644
index 0000000..8bd9e51
--- /dev/null
+++ b/components/service/test_runner/client/cpp/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}/test_runner_client.cpp"
+ )
+
diff --git a/components/service/test_runner/client/cpp/test_runner_client.cpp b/components/service/test_runner/client/cpp/test_runner_client.cpp
new file mode 100644
index 0000000..a780002
--- /dev/null
+++ b/components/service/test_runner/client/cpp/test_runner_client.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "test_runner_client.h"
+#include <protocols/rpc/common/packed-c/status.h>
+#include <protocols/service/test_runner/packed-c/opcodes.h>
+#include <protocols/service/test_runner/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 <rpc_caller.h>
+#include <common/tlv/tlv.h>
+#include <cstddef>
+#include <cstring>
+#include <string>
+
+test_runner_client::test_runner_client() :
+ m_caller(NULL),
+ m_err_rpc_status(TS_RPC_CALL_ACCEPTED)
+{
+
+}
+
+test_runner_client::test_runner_client(struct rpc_caller *caller) :
+ m_caller(caller),
+ m_err_rpc_status(TS_RPC_CALL_ACCEPTED)
+{
+
+}
+
+test_runner_client::~test_runner_client()
+{
+
+}
+
+void test_runner_client::set_caller(struct rpc_caller *caller)
+{
+ m_caller = caller;
+}
+
+int test_runner_client::err_rpc_status() const
+{
+ return m_err_rpc_status;
+}
+
+int test_runner_client::run_tests(
+ const struct test_spec &spec,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results)
+{
+ return iterate_over_tests(spec, false, summary, results);
+}
+
+int test_runner_client::list_tests(
+ const struct test_spec &spec,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results)
+{
+ return iterate_over_tests(spec, true, summary, results);
+}
+
+int test_runner_client::iterate_over_tests(
+ const struct test_spec &spec, bool list_only,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results)
+{
+ int test_status = TS_TEST_RUNNER_STATUS_ERROR;
+ m_err_rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE;
+ rpc_call_handle call_handle;
+ uint8_t *req_buf;
+ std::vector<uint8_t> req_param;
+
+ serialize_test_spec(req_param, spec);
+
+ size_t req_len = req_param.size();
+ call_handle = rpc_caller_begin(m_caller, &req_buf, req_len);
+
+ if (call_handle) {
+
+ uint8_t *resp_buf;
+ size_t resp_len;
+ int opstatus;
+
+ memcpy(req_buf, req_param.data(), req_len);
+
+ uint32_t opcode = (list_only) ?
+ TS_TEST_RUNNER_OPCODE_LIST_TESTS :
+ TS_TEST_RUNNER_OPCODE_RUN_TESTS;
+
+ m_err_rpc_status = rpc_caller_invoke(m_caller, call_handle,
+ opcode, &opstatus, &resp_buf, &resp_len);
+
+ if (m_err_rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+ test_status = opstatus;
+
+ if (opstatus == TS_TEST_RUNNER_STATUS_SUCCESS) {
+
+ test_status = deserialize_results(resp_buf, resp_len, summary, results);
+ }
+ }
+
+ rpc_caller_end(m_caller, call_handle);
+ }
+
+ return test_status;
+}
+
+void test_runner_client::serialize_test_spec(
+ std::vector<uint8_t> &serialized_data,
+ const struct test_spec &spec) const
+{
+ size_t name_len = strlen(spec.name);
+ size_t group_len = strlen(spec.group);
+ size_t tlv_space = 0;
+
+ /* First determine buffer space needed for TLV parameters */
+ if (name_len) tlv_space += tlv_required_space(name_len);
+ if (group_len) tlv_space += tlv_required_space(group_len);
+
+ /* Extend the params vector and write the tlv records */
+ if (tlv_space) {
+
+ serialized_data.resize(tlv_space);
+ struct tlv_iterator iter;
+ uint8_t *buf = serialized_data.data();
+
+ tlv_iterator_begin(&iter, buf, tlv_space);
+
+ if (name_len) {
+
+ struct tlv_record record;
+ record.tag = TS_TEST_RUNNER_TEST_SPEC_TAG_NAME;
+ record.length = name_len;
+ record.value = (const uint8_t*)spec.name;
+ tlv_encode(&iter, &record);
+ }
+
+ if (group_len) {
+
+ struct tlv_record record;
+ record.tag = TS_TEST_RUNNER_TEST_SPEC_TAG_GROUP;
+ record.length = group_len;
+ record.value = (const uint8_t*)spec.group;
+ tlv_encode(&iter, &record);
+ }
+ }
+}
+
+int test_runner_client::deserialize_results(
+ const uint8_t *resp_buf, size_t resp_len,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results) const
+{
+ int test_status = TS_TEST_RUNNER_STATUS_SUCCESS;
+ size_t fixed_size = sizeof(ts_test_runner_result_summary);
+
+ if (resp_len >= fixed_size) {
+
+ /* Deserialize fixed size summary structure */
+ struct ts_test_runner_result_summary packed_summary;
+ memcpy(&packed_summary, resp_buf, fixed_size);
+
+ summary.num_tests = packed_summary.num_tests;
+ summary.num_passed = packed_summary.num_passed;
+ summary.num_failed = packed_summary.num_failed;
+ summary.num_results = 0;
+
+ /* Deserialize any test result records */
+ struct tlv_const_iterator tlv_iter;
+ struct tlv_record result_record;
+ tlv_const_iterator_begin(&tlv_iter, &resp_buf[fixed_size], resp_len - fixed_size);
+
+ while (tlv_find_decode(&tlv_iter, TS_TEST_RUNNER_TEST_RESULT_TAG, &result_record)) {
+
+ struct test_result result;
+
+ test_status = deserialize_result(result_record.value, result_record.length, result);
+
+ if (test_status == TS_TEST_RUNNER_STATUS_SUCCESS) {
+
+ results.push_back(result);
+ ++summary.num_results;
+ }
+ else {
+ /* Failed to decode result record */
+ break;
+ }
+ }
+ }
+ else {
+ /* Failed to mandatory test summary */
+ test_status = TS_TEST_RUNNER_STATUS_INVALID_TEST_RESULTS;
+ }
+
+ return test_status;
+}
+
+int test_runner_client::deserialize_result(
+ const uint8_t *value_buf, size_t value_len,
+ struct test_result &result) const
+{
+ int test_status = TS_TEST_RUNNER_STATUS_SUCCESS;
+ size_t fixed_size = sizeof(ts_test_runner_test_result);
+
+ if (value_len >= fixed_size) {
+
+ struct ts_test_runner_test_result packed_result;
+ memcpy(&packed_result, value_buf, fixed_size);
+
+ result.run_state = (enum test_run_state)packed_result.run_state;
+ result.fail_line = packed_result.fail_line;
+ result.name[0] = 0;
+ result.group[0] = 0;
+
+ /* Deserialize name and group if present */
+ struct tlv_const_iterator req_iter;
+ struct tlv_record decoded_record;
+
+ tlv_const_iterator_begin(&req_iter, &value_buf[fixed_size], value_len - fixed_size);
+
+ if (tlv_find_decode(&req_iter, TS_TEST_RUNNER_TEST_RESULT_TAG_NAME, &decoded_record)) {
+
+ if ((decoded_record.length > 0) && (decoded_record.length < TEST_NAME_MAX_LEN)) {
+
+ memcpy(result.name, decoded_record.value, decoded_record.length);
+ result.name[decoded_record.length] = 0;
+ }
+ }
+
+ if (tlv_find_decode(&req_iter, TS_TEST_RUNNER_TEST_RESULT_TAG_GROUP, &decoded_record)) {
+
+ if ((decoded_record.length > 0) && (decoded_record.length < TEST_GROUP_MAX_LEN)) {
+
+ memcpy(result.group, decoded_record.value, decoded_record.length);
+ result.group[decoded_record.length] = 0;
+ }
+ }
+ }
+ else {
+ /* Invalid test result */
+ test_status = TS_TEST_RUNNER_STATUS_INVALID_TEST_RESULTS;
+ }
+
+ return test_status;
+}
diff --git a/components/service/test_runner/client/cpp/test_runner_client.h b/components/service/test_runner/client/cpp/test_runner_client.h
new file mode 100644
index 0000000..b86b93e
--- /dev/null
+++ b/components/service/test_runner/client/cpp/test_runner_client.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TEST_RUNNER_CLIENT_H
+#define TEST_RUNNER_CLIENT_H
+
+#include <cstdint>
+#include <vector>
+#include <string>
+#include <service/test_runner/common/test_runner.h>
+
+struct rpc_caller;
+
+/*
+ * Provides a client interface for running remote tests using the test-runner
+ * service access protocol.
+ */
+class test_runner_client
+{
+public:
+ test_runner_client();
+ test_runner_client(struct rpc_caller *caller);
+ virtual ~test_runner_client();
+
+ void set_caller(struct rpc_caller *caller);
+ int err_rpc_status() const;
+
+ int run_tests(const struct test_spec &spec,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results);
+
+ int list_tests(const struct test_spec &spec,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results);
+
+private:
+
+ int iterate_over_tests(const struct test_spec &spec, bool list_only,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results);
+
+ void serialize_test_spec(std::vector<uint8_t> &serialized_data,
+ const struct test_spec &spec) const;
+
+ int deserialize_results(const uint8_t *resp_buf, size_t resp_len,
+ struct test_summary &summary,
+ std::vector<struct test_result> &results) const;
+
+ int deserialize_result(const uint8_t *value_buf, size_t value_len,
+ struct test_result &result) const;
+
+ struct rpc_caller *m_caller;
+ int m_err_rpc_status;
+};
+
+#endif /* TEST_RUNNER_CLIENT_H */
diff --git a/components/service/test_runner/common/test_runner.h b/components/service/test_runner/common/test_runner.h
new file mode 100644
index 0000000..c891918
--- /dev/null
+++ b/components/service/test_runner/common/test_runner.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CPPUTEST_TEST_RUNNER_H
+#define CPPUTEST_TEST_RUNNER_H
+
+#include <protocols/service/test_runner/packed-c/test_result.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Constants
+ */
+#define TEST_NAME_MAX_LEN (30)
+#define TEST_GROUP_MAX_LEN (30)
+
+/**
+ * Specifies a set of tests for running or listing.
+ */
+struct test_spec
+{
+ char name[TEST_NAME_MAX_LEN];
+ char group[TEST_GROUP_MAX_LEN];
+};
+
+/**
+ * A summary of a set of tests qualified by a test_spec.
+ */
+struct test_summary
+{
+ int num_tests; /* Number of qualifying tests */
+ int num_results; /* Number of available test result objects */
+ int num_passed; /* Number that ran and passed */
+ int num_failed; /* Number that ran and failed */
+};
+
+/**
+ * The run state of a test case
+ */
+enum test_run_state
+{
+ TEST_RUN_STATE_NOT_RUN = TS_TEST_RUNNER_TEST_RESULT_RUN_STATE_NOT_RUN,
+ TEST_RUN_STATE_PASSED = TS_TEST_RUNNER_TEST_RESULT_RUN_STATE_PASSED,
+ TEST_RUN_STATE_FAILED = TS_TEST_RUNNER_TEST_RESULT_RUN_STATE_FAILED
+};
+
+/**
+ * The result for a particular test case.
+ */
+struct test_result
+{
+ char name[TEST_NAME_MAX_LEN];
+ char group[TEST_GROUP_MAX_LEN];
+ enum test_run_state run_state;
+ unsigned int fail_line;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CPPUTEST_TEST_RUNNER_H */
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 */
diff --git a/components/service/test_runner/test/service/component.cmake b/components/service/test_runner/test/service/component.cmake
new file mode 100644
index 0000000..7cbd452
--- /dev/null
+++ b/components/service/test_runner/test/service/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}/test_runner_service_tests.cpp"
+ )
+
diff --git a/components/service/test_runner/test/service/test_runner_service_tests.cpp b/components/service/test_runner/test/service/test_runner_service_tests.cpp
new file mode 100644
index 0000000..c4ba0e0
--- /dev/null
+++ b/components/service/test_runner/test/service/test_runner_service_tests.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <service/test_runner/client/cpp/test_runner_client.h>
+#include <protocols/rpc/common/packed-c/encoding.h>
+#include <protocols/service/test_runner/packed-c/status.h>
+#include <service_locator.h>
+#include <CppUTest/TestHarness.h>
+#include <vector>
+#include <cstring>
+
+/*
+ * Service-level tests for the test_runner service. These tests assume
+ * that the mock_test_runner backend is used as the only registered
+ * backend. It includes some referenece test cases that are assumed
+ * by these tests.
+ */
+TEST_GROUP(TestRunnerServiceTests)
+{
+ void setup()
+ {
+ struct rpc_caller *caller;
+ int status;
+
+ m_rpc_session_handle = NULL;
+ m_test_runner_service_context = NULL;
+ m_test_runner_client = NULL;
+
+ service_locator_init();
+
+ m_test_runner_service_context = service_locator_query("sn:trustedfirmware.org:test-runner:0", &status);
+ CHECK(m_test_runner_service_context);
+
+ m_rpc_session_handle = service_context_open(m_test_runner_service_context, TS_RPC_ENCODING_PACKED_C, &caller);
+ CHECK(m_rpc_session_handle);
+
+ m_test_runner_client = new test_runner_client(caller);
+ }
+
+ void teardown()
+ {
+ delete m_test_runner_client;
+ m_test_runner_client = NULL;
+
+ service_context_close(m_test_runner_service_context, m_rpc_session_handle);
+ m_rpc_session_handle = NULL;
+
+ service_context_relinquish(m_test_runner_service_context);
+ m_test_runner_service_context = NULL;
+ }
+
+ rpc_session_handle m_rpc_session_handle;
+ struct service_context *m_test_runner_service_context;
+ test_runner_client *m_test_runner_client;
+};
+
+TEST(TestRunnerServiceTests, listAllTests)
+{
+ int test_status;
+ struct test_spec spec;
+ struct test_summary summary;
+ std::vector<struct test_result> results;
+
+ /* Create spec that qualifies all tests */
+ spec.name[0] = 0;
+ spec.group[0] = 0;
+
+ test_status = m_test_runner_client->list_tests(spec, summary, results);
+
+ CHECK_EQUAL(TS_TEST_RUNNER_STATUS_SUCCESS, test_status);
+
+ /* Check test summary */
+ CHECK_EQUAL(4, summary.num_tests);
+ CHECK_EQUAL(4, summary.num_results);
+ CHECK_EQUAL(0, summary.num_passed);
+ CHECK_EQUAL(0, summary.num_failed);
+
+ /* Check each test result is listed but not run */
+ CHECK(strcmp(results[0].group, "PlatformTests") == 0);
+ CHECK(strcmp(results[0].name, "Trng") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_NOT_RUN, results[0].run_state);
+
+ CHECK(strcmp(results[1].group, "PlatformTests") == 0);
+ CHECK(strcmp(results[1].name, "CheckIOmap") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_NOT_RUN, results[1].run_state);
+
+ CHECK(strcmp(results[2].group, "ConfigTests") == 0);
+ CHECK(strcmp(results[2].name, "ValidateConfig") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_NOT_RUN, results[2].run_state);
+
+ CHECK(strcmp(results[3].group, "ConfigTests") == 0);
+ CHECK(strcmp(results[3].name, "ApplyConfig") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_NOT_RUN, results[3].run_state);
+}
+
+TEST(TestRunnerServiceTests, runAllTests)
+{
+ int test_status;
+ struct test_spec spec;
+ struct test_summary summary;
+ std::vector<struct test_result> results;
+
+ /* Create spec that qualifies all tests */
+ spec.name[0] = 0;
+ spec.group[0] = 0;
+
+ test_status = m_test_runner_client->run_tests(spec, summary, results);
+
+ CHECK_EQUAL(TS_TEST_RUNNER_STATUS_SUCCESS, test_status);
+
+ CHECK_EQUAL(4, summary.num_tests);
+ CHECK_EQUAL(4, summary.num_results);
+ CHECK_EQUAL(3, summary.num_passed);
+ CHECK_EQUAL(1, summary.num_failed);
+
+ /* Check each test result has run with the expected outcome */
+ CHECK(strcmp(results[0].group, "PlatformTests") == 0);
+ CHECK(strcmp(results[0].name, "Trng") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_PASSED, results[0].run_state);
+
+ CHECK(strcmp(results[1].group, "PlatformTests") == 0);
+ CHECK(strcmp(results[1].name, "CheckIOmap") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_PASSED, results[1].run_state);
+
+ CHECK(strcmp(results[2].group, "ConfigTests") == 0);
+ CHECK(strcmp(results[2].name, "ValidateConfig") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_FAILED, results[2].run_state);
+
+ CHECK(strcmp(results[3].group, "ConfigTests") == 0);
+ CHECK(strcmp(results[3].name, "ApplyConfig") == 0);
+ CHECK_EQUAL(TEST_RUN_STATE_PASSED, results[3].run_state);
+}
+
+TEST(TestRunnerServiceTests, listPlatformTests)
+{
+ int test_status;
+ struct test_spec spec;
+ struct test_summary summary;
+ std::vector<struct test_result> results;
+
+ /* Create spec that qualifies all tests in a group*/
+ spec.name[0] = 0;
+ strcpy(spec.group, "PlatformTests");
+
+ test_status = m_test_runner_client->list_tests(spec, summary, results);
+
+ CHECK_EQUAL(TS_TEST_RUNNER_STATUS_SUCCESS, test_status);
+
+ /* Check test summary */
+ CHECK_EQUAL(2, summary.num_tests);
+ CHECK_EQUAL(2, summary.num_results);
+ CHECK_EQUAL(0, summary.num_passed);
+ CHECK_EQUAL(0, summary.num_failed);
+}
+
+TEST(TestRunnerServiceTests, runConfigTests)
+{
+ int test_status;
+ struct test_spec spec;
+ struct test_summary summary;
+ std::vector<struct test_result> results;
+
+ /* Create spec that qualifies all tests in a group*/
+ spec.name[0] = 0;
+ strcpy(spec.group, "ConfigTests");
+
+ test_status = m_test_runner_client->run_tests(spec, summary, results);
+
+ CHECK_EQUAL(TS_TEST_RUNNER_STATUS_SUCCESS, test_status);
+
+ /* Check test summary */
+ CHECK_EQUAL(2, summary.num_tests);
+ CHECK_EQUAL(2, summary.num_results);
+ CHECK_EQUAL(1, summary.num_passed);
+ CHECK_EQUAL(1, summary.num_failed);
+}
+
+TEST(TestRunnerServiceTests, runSpecificTest)
+{
+ int test_status;
+ struct test_spec spec;
+ struct test_summary summary;
+ std::vector<struct test_result> results;
+
+ /* Create spec that qualifies a specific test */
+ strcpy(spec.name, "ValidateConfig");
+ strcpy(spec.group, "ConfigTests");
+
+ test_status = m_test_runner_client->run_tests(spec, summary, results);
+
+ CHECK_EQUAL(TS_TEST_RUNNER_STATUS_SUCCESS, test_status);
+
+ /* Check test summary */
+ CHECK_EQUAL(1, summary.num_tests);
+ CHECK_EQUAL(1, summary.num_results);
+ CHECK_EQUAL(0, summary.num_passed);
+ CHECK_EQUAL(1, summary.num_failed);
+}
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index 2d7c21a..0e6fd1b 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -36,6 +36,7 @@ add_components(
"components/service/locator/test"
"components/service/locator/standalone"
"components/service/locator/standalone/services/crypto"
+ "components/service/locator/standalone/services/test-runner"
"components/service/crypto/client/cpp"
"components/service/crypto/client/cpp/protobuf"
"components/service/crypto/client/cpp/packed-c"
@@ -55,6 +56,9 @@ add_components(
"components/service/secure_storage/provider/secure_flash_store/flash_fs"
"components/service/secure_storage/provider/secure_flash_store/flash"
"components/service/secure_storage/test"
+ "components/service/test_runner/provider"
+ "components/service/test_runner/provider/serializer/packed-c"
+ "components/service/test_runner/provider/backend/null"
"protocols/rpc/common/protobuf"
"protocols/rpc/common/packed-c"
"protocols/service/crypto/packed-c"
diff --git a/deployments/env-test/opteesp/.gitignore b/deployments/env-test/opteesp/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/deployments/env-test/opteesp/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/deployments/env-test/opteesp/CMakeLists.txt b/deployments/env-test/opteesp/CMakeLists.txt
new file mode 100644
index 0000000..125485d
--- /dev/null
+++ b/deployments/env-test/opteesp/CMakeLists.txt
@@ -0,0 +1,129 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+cmake_minimum_required(VERSION 3.16)
+include(../../deployment.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+# The CMakeLists.txt for building the env-test deployment for opteesp
+#
+# Builds the test_runner service provider for running in an SEL0 secure partition
+# hosted by OPTEE in the role of SPM. Environment tests are added and CppUnit
+# test cases.
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/environments/opteesp/env.cmake)
+project(trusted-services LANGUAGES C CXX ASM)
+add_executable(env_test)
+target_include_directories(env_test PRIVATE "${TOP_LEVEL_INCLUDE_DIRS}")
+set(SP_UUID "33c75baf-ac6a-4fe4-8ac7-e9909bee2d17")
+
+
+# Include SP DEV KIT interface
+set(SP_DEV_KIT_INC_DIR ${CMAKE_CURRENT_LIST_DIR})
+list(APPEND CMAKE_MODULE_PATH "${TS_ROOT}/external/Spdevkit")
+find_package(Spdevkit REQUIRED)
+sp_dev_kit_configure_linking(TARGET env_test DEFINES ARM64=1)
+target_link_libraries(env_test PRIVATE ${SP_DEV_KIT_LIBRARIES})
+
+#-------------------------------------------------------------------------------
+# Components that are env_testecific to deployment in the opteesp
+# environment.
+#-------------------------------------------------------------------------------
+add_components(TARGET "env_test"
+ BASE_DIR ${TS_ROOT}
+ COMPONENTS
+ "components/common/tlv"
+ "components/config/ramstore"
+ "components/messaging/ffa/libsp"
+ "components/rpc/ffarpc/endpoint"
+ "components/rpc/common/interface"
+ "components/service/common"
+ "components/service/common/provider"
+ "components/service/test_runner/provider"
+ "components/service/test_runner/provider/serializer/packed-c"
+ "components/service/test_runner/provider/backend/mock"
+ "protocols/rpc/common/packed-c"
+ "environments/opteesp"
+)
+
+target_sources(env_test PRIVATE
+ env_test.c
+ env_test_config.c
+)
+
+#-------------------------------------------------------------------------------
+# Use the selected platform to provide drivers needed by the deployment
+#
+#-------------------------------------------------------------------------------
+# temporarily force platform - remove when external builder updated
+set(TS_PLATFORM "arm/fvp/fvp_base_revc-2xaemv8a" CACHE STRING "Overridden" FORCE)
+
+add_platform(TARGET "env_test")
+
+#-------------------------------------------------------------------------------
+# Components used from external projects
+#
+#-------------------------------------------------------------------------------
+
+if(CMAKE_CROSSCOMPILING)
+ target_link_libraries(env_test PRIVATE stdc++ gcc m)
+endif()
+
+#################################################################
+
+target_compile_definitions(env_test PRIVATE
+ ARM64=1
+)
+
+target_include_directories(env_test PRIVATE
+ ${TS_ROOT}
+ ${TS_ROOT}/components
+ ${TS_ROOT}/deployments/env-test/opteesp
+)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ target_compile_options(env_test PRIVATE
+ -fdiagnostics-show-option
+ -fpic
+ -gdwarf-2
+ -mstrict-align
+ -O0
+ $<$<COMPILE_LANGUAGE:C>:-std=gnu99>
+ $<$<COMPILE_LANGUAGE:CXX>:-fno-use-cxa-atexit>
+ )
+
+ # Options for GCC that control linking
+ target_link_options(env_test PRIVATE
+ -e __sp_entry
+ -fno-lto
+ -nostdlib
+ -pie
+ -zmax-page-size=4096
+ )
+ # Options directly for LD, these are not understood by GCC
+ target_link_options(env_test PRIVATE
+ -Wl,--as-needed
+ -Wl,--sort-section=alignment
+ # -Wl,--dynamic-list ${CMAKE_CURRENT_LIST_DIR}/dyn_list
+ )
+endif()
+
+compiler_generate_stripped_elf(TARGET env_test NAME "${SP_UUID}.stripped.elf" RES STRIPPED_ELF)
+
+######################################## install
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "location to install build output to." FORCE)
+endif()
+#TODO: api headers
+install(TARGETS env_test
+ PUBLIC_HEADER DESTINATION include
+ RUNTIME DESTINATION bin
+ )
+install(FILES ${STRIPPED_ELF} DESTINATION bin)
+
+set(EXPORT_SP_NAME "env-test")
+set(EXPORT_SP_UUID ${SP_UUID})
+include(${TS_ROOT}/environments/opteesp/ExportSp.cmake)
diff --git a/deployments/env-test/opteesp/env_test.c b/deployments/env-test/opteesp/env_test.c
new file mode 100644
index 0000000..0bb523f
--- /dev/null
+++ b/deployments/env-test/opteesp/env_test.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <rpc/ffarpc/caller/sp/ffarpc_caller.h>
+#include <rpc/ffarpc/endpoint/ffarpc_call_ep.h>
+#include <service/test_runner/provider/test_runner_provider.h>
+#include <service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <ffa_api.h>
+#include <sp_api.h>
+#include <sp_rxtx.h>
+#include <trace.h>
+#include "env_test_config.h"
+
+
+uint16_t own_id = 0; /* !!Needs refactoring as parameter to ffarpc_caller_init */
+
+
+static int sp_init(uint16_t *own_sp_id);
+
+void __noreturn sp_main(struct ffa_init_info *init_info)
+{
+ struct test_runner_provider test_runner_provider;
+ struct ffa_call_ep ffarpc_call_ep;
+ struct rpc_interface *test_runner_iface;
+ struct ffarpc_caller ffarpc_caller;
+ struct ffa_direct_msg req_msg;
+
+ /* Boot */
+ (void) init_info;
+
+ if (sp_init(&own_id) != 0) goto fatal_error;
+
+ load_sp_config(init_info);
+
+ /* Initialize the test_runner service */
+ test_runner_iface = test_runner_provider_init(&test_runner_provider);
+
+ test_runner_provider_register_serializer(&test_runner_provider,
+ TS_RPC_ENCODING_PACKED_C, packedc_test_runner_provider_serializer_instance());
+
+ ffa_call_ep_init(&ffarpc_call_ep, test_runner_iface);
+
+ /* End of boot phase */
+ ffa_msg_wait(&req_msg);
+
+ while (1) {
+ if (req_msg.function_id == FFA_MSG_SEND_DIRECT_REQ_32) {
+
+ struct ffa_direct_msg resp_msg;
+
+ ffa_call_ep_receive(&ffarpc_call_ep, &req_msg, &resp_msg);
+
+ ffa_msg_send_direct_resp(req_msg.destination_id,
+ req_msg.source_id, resp_msg.args[0], resp_msg.args[1],
+ resp_msg.args[2], resp_msg.args[3], resp_msg.args[4],
+ &req_msg);
+ }
+ }
+
+fatal_error:
+ /* SP is not viable */
+ EMSG("environment-test SP error");
+ while (1) {}
+}
+
+void sp_interrupt_handler(uint32_t interrupt_id)
+{
+ (void)interrupt_id;
+}
+
+static int sp_init(uint16_t *own_sp_id)
+{
+ int status = -1;
+ ffa_result ffa_res;
+ sp_result sp_res;
+ static uint8_t tx_buffer[4096] __aligned(4096);
+ static uint8_t rx_buffer[4096] __aligned(4096);
+
+ sp_res = sp_rxtx_buffer_map(tx_buffer, rx_buffer, sizeof(rx_buffer));
+ if (sp_res == SP_RESULT_OK) {
+ ffa_res = ffa_id_get(own_sp_id);
+ if (ffa_res == FFA_OK) {
+ status = 0;
+ }
+ }
+
+ return status;
+}
diff --git a/deployments/env-test/opteesp/env_test.h b/deployments/env-test/opteesp/env_test.h
new file mode 100644
index 0000000..0f4c8b7
--- /dev/null
+++ b/deployments/env-test/opteesp/env_test.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ENV_TEST_SP_H
+#define ENV_TEST_SP_H
+
+#define ENV_TEST_SP_UUID \
+ {0x33c75baf, 0xac6a, 0x4fe4, \
+ {0x8a, 0xc7, 0xe9, 0x90, 0x9b, 0xee, 0x2d, 0x17}}
+
+#define ENV_TEST_SP_UUID_BYTES \
+ {0x33, 0xc7, 0x5b, 0xaf, 0xac, 0x6a, 0x4f, 0xef, \
+ 0x8a, 0xcy, 0xe9, 0x90, 0x9b, 0xee, 0x2d, 0x17}
+
+#endif /* ENV_TEST_SP_H */
diff --git a/deployments/env-test/opteesp/env_test_config.c b/deployments/env-test/opteesp/env_test_config.c
new file mode 100644
index 0000000..14a1e53
--- /dev/null
+++ b/deployments/env-test/opteesp/env_test_config.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <config/ramstore/config_ramstore.h>
+#include "env_test_config.h"
+
+
+void load_sp_config(struct ffa_init_info *init_info)
+{
+ config_ramstore_init();
+
+ /* Load deployment specific configuration */
+ (void)init_info;
+} \ No newline at end of file
diff --git a/deployments/env-test/opteesp/env_test_config.h b/deployments/env-test/opteesp/env_test_config.h
new file mode 100644
index 0000000..8ed4a7e
--- /dev/null
+++ b/deployments/env-test/opteesp/env_test_config.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ENV_TEST_CONFIG_H
+#define ENV_TEST_CONFIG_H
+
+#include <ffa_api.h>
+
+/**
+ * Loads the SP specific configuration passed as SP initialization parameters.
+ */
+void load_sp_config(struct ffa_init_info *init_info);
+
+
+#endif /* ENV_TEST_CONFIG_H */
diff --git a/deployments/env-test/opteesp/optee_sp_user_defines.h b/deployments/env-test/opteesp/optee_sp_user_defines.h
new file mode 100644
index 0000000..a524a6e
--- /dev/null
+++ b/deployments/env-test/opteesp/optee_sp_user_defines.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SP_HEADER_DEFINES_H
+#define SP_HEADER_DEFINES_H
+
+/* To get UUID definition */
+#include "env_test.h"
+
+#define OPTEE_SP_UUID ENV_TEST_SP_UUID
+#define OPTEE_SP_FLAGS 0
+
+/* Provisioned stack size */
+#define OPTEE_SP_STACK_SIZE (64 * 1024)
+
+/* Provisioned heap size */
+#define OPTEE_SP_HEAP_SIZE (32 * 1024)
+
+#endif /* SP_HEADER_DEFINES_H */
diff --git a/deployments/libts/linux-pc/CMakeLists.txt b/deployments/libts/linux-pc/CMakeLists.txt
index 7924f7c..01387bc 100644
--- a/deployments/libts/linux-pc/CMakeLists.txt
+++ b/deployments/libts/linux-pc/CMakeLists.txt
@@ -36,6 +36,7 @@ add_components(
"components/service/common/provider"
"components/service/locator/standalone"
"components/service/locator/standalone/services/crypto"
+ "components/service/locator/standalone/services/test-runner"
"components/service/crypto/provider/mbedcrypto"
"components/service/crypto/provider/mbedcrypto/trng_adapter/linux"
"components/service/crypto/provider/serializer/protobuf"
@@ -44,6 +45,9 @@ add_components(
"components/service/secure_storage/provider/secure_flash_store"
"components/service/secure_storage/provider/secure_flash_store/flash_fs"
"components/service/secure_storage/provider/secure_flash_store/flash"
+ "components/service/test_runner/provider"
+ "components/service/test_runner/provider/serializer/packed-c"
+ "components/service/test_runner/provider/backend/mock"
"protocols/rpc/common/packed-c"
"protocols/service/crypto/packed-c"
"protocols/service/crypto/protobuf"
diff --git a/deployments/ts-remote-test/arm-linux/.gitignore b/deployments/ts-remote-test/arm-linux/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/deployments/ts-remote-test/arm-linux/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/deployments/ts-remote-test/arm-linux/CMakeLists.txt b/deployments/ts-remote-test/arm-linux/CMakeLists.txt
new file mode 100644
index 0000000..7540178
--- /dev/null
+++ b/deployments/ts-remote-test/arm-linux/CMakeLists.txt
@@ -0,0 +1,31 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+cmake_minimum_required(VERSION 3.16)
+include(../../deployment.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+# The CMakeLists.txt for building the ts-remote-test deployment for arm-linux
+#
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/environments/arm-linux/env.cmake)
+project(trusted-services LANGUAGES CXX C)
+add_executable(ts-remote-test)
+target_include_directories(ts-remote-test PRIVATE "${TOP_LEVEL_INCLUDE_DIRS}")
+
+#-------------------------------------------------------------------------------
+# Extend with components that are common across all deployments of
+# ts-remote-test
+#
+#-------------------------------------------------------------------------------
+include(../ts-remote-test.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+# Define library options and dependencies.
+#
+#-------------------------------------------------------------------------------
+env_set_link_options(TGT ts-remote-test)
+target_link_libraries(ts-remote-test PRIVATE stdc++ gcc m)
diff --git a/deployments/ts-remote-test/linux-pc/CMakeLists.txt b/deployments/ts-remote-test/linux-pc/CMakeLists.txt
new file mode 100644
index 0000000..5e5aaa5
--- /dev/null
+++ b/deployments/ts-remote-test/linux-pc/CMakeLists.txt
@@ -0,0 +1,24 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+cmake_minimum_required(VERSION 3.16)
+include(../../deployment.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+# The CMakeLists.txt for building the ts-remote-test deployment for linux-pc
+#
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/environments/linux-pc/env.cmake)
+project(trusted-services LANGUAGES CXX C)
+add_executable(ts-remote-test)
+target_include_directories(ts-remote-test PRIVATE "${TOP_LEVEL_INCLUDE_DIRS}")
+
+#-------------------------------------------------------------------------------
+# Extend with components that are common across all deployments of
+# ts-remote-test
+#
+#-------------------------------------------------------------------------------
+include(../ts-remote-test.cmake REQUIRED)
diff --git a/deployments/ts-remote-test/ts-remote-test.cmake b/deployments/ts-remote-test/ts-remote-test.cmake
new file mode 100644
index 0000000..7e5b286
--- /dev/null
+++ b/deployments/ts-remote-test/ts-remote-test.cmake
@@ -0,0 +1,50 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+# The base build file shared between deployments of 'ts-remote-test' for
+# different environments. Acts as a client for tests running in a remote
+# processing environment.
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+# Use libts for locating and accessing services. An appropriate version of
+# libts will be imported for the enviroment in which tests are
+# deployed.
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/deployments/libts/libts-import.cmake)
+target_link_libraries(ts-remote-test PRIVATE libts)
+
+#-------------------------------------------------------------------------------
+# Common main for all deployments
+#
+#-------------------------------------------------------------------------------
+target_sources(ts-remote-test PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/ts-remote-test.cpp"
+)
+
+#-------------------------------------------------------------------------------
+# Components that are common accross all deployments
+#
+#-------------------------------------------------------------------------------
+add_components(
+ TARGET "ts-remote-test"
+ BASE_DIR ${TS_ROOT}
+ COMPONENTS
+ "components/app/remote-test-runner"
+ "components/common/tlv"
+ "components/service/test_runner/client/cpp"
+)
+
+#-------------------------------------------------------------------------------
+# Define install content.
+#
+#-------------------------------------------------------------------------------
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "location to install build output to." FORCE)
+endif()
+install(TARGETS ts-remote-test RUNTIME DESTINATION bin) \ No newline at end of file
diff --git a/deployments/ts-remote-test/ts-remote-test.cpp b/deployments/ts-remote-test/ts-remote-test.cpp
new file mode 100644
index 0000000..1c708f6
--- /dev/null
+++ b/deployments/ts-remote-test/ts-remote-test.cpp
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <service/test_runner/client/cpp/test_runner_client.h>
+#include <app/remote-test-runner/remote_test_runner.h>
+#include <protocols/rpc/common/packed-c/encoding.h>
+#include <service_locator.h>
+#include <rpc_caller.h>
+#include <cstdio>
+
+int main(int argc, char *argv[]) {
+ (void) argc;
+ (void) argv;
+
+ int status = -1;
+ struct service_context *test_runner_service_context = NULL;
+
+ service_locator_init();
+
+ test_runner_service_context = service_locator_query("sn:trustedfirmware.org:test-runner:0", &status);
+
+ if (test_runner_service_context) {
+
+ struct rpc_caller *caller;
+ rpc_session_handle rpc_session_handle;
+
+ rpc_session_handle = service_context_open(test_runner_service_context, TS_RPC_ENCODING_PACKED_C, &caller);
+
+ if (rpc_session_handle) {
+
+ test_runner_client test_runner_client(caller);
+ remote_test_runner commandline_runner(&test_runner_client);
+
+ status = commandline_runner.execute(argc, argv);
+
+ if (status != 0) {
+ printf("Command failed with test status: %d rpc status: %d\n", status, test_runner_client.err_rpc_status());
+ }
+
+ service_context_close(test_runner_service_context, rpc_session_handle);
+ }
+ else {
+ printf("Failed to open rpc session\n");
+ }
+
+ service_context_relinquish(test_runner_service_context);
+ }
+ else {
+ printf("Failed to discover test_runner service\n");
+ }
+
+ return status;
+}
diff --git a/deployments/ts-service-test/linux-pc/CMakeLists.txt b/deployments/ts-service-test/linux-pc/CMakeLists.txt
index 8ccc3b3..85a0a36 100644
--- a/deployments/ts-service-test/linux-pc/CMakeLists.txt
+++ b/deployments/ts-service-test/linux-pc/CMakeLists.txt
@@ -64,6 +64,18 @@ unit_test_add_suite(
target_include_directories(ts-service-test PRIVATE "${TOP_LEVEL_INCLUDE_DIRS}")
#-------------------------------------------------------------------------------
+# Components that are specific to deployment in the linux-pc environment.
+#
+#-------------------------------------------------------------------------------
+add_components(
+ TARGET "ts-service-test"
+ BASE_DIR ${TS_ROOT}
+ COMPONENTS
+ "components/service/test_runner/client/cpp"
+ "components/service/test_runner/test/service"
+)
+
+#-------------------------------------------------------------------------------
# Extend with components that are common across all deployments of
# ts-service-test
#
diff --git a/protocols/service/test_runner/packed-c/list_tests.h b/protocols/service/test_runner/packed-c/list_tests.h
new file mode 100644
index 0000000..25bab9e
--- /dev/null
+++ b/protocols/service/test_runner/packed-c/list_tests.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_TEST_RUNNER_LIST_TESTS
+#define TS_TEST_RUNNER_LIST_TESTS
+
+/**
+ * Input parmeters consist of test spec (defined in test_spec.h) to
+ * define the set of tests to run.
+ */
+#include "test_spec.h"
+
+/* Output parameters are the same as for run_tests except no
+ * tests are actually run.
+ */
+#include "test_result.h"
+
+#endif /* TS_TEST_RUNNER_LIST_TESTS */
diff --git a/protocols/service/test_runner/packed-c/opcodes.h b/protocols/service/test_runner/packed-c/opcodes.h
new file mode 100644
index 0000000..e28da24
--- /dev/null
+++ b/protocols/service/test_runner/packed-c/opcodes.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_TEST_RUNNER_OPCODES_H
+#define TS_TEST_RUNNER_OPCODES_H
+
+/**
+ * C/C++ definition of test_runner service opcodes
+ */
+#define TS_TEST_RUNNER_OPCODE_RUN_TESTS (0x0101)
+#define TS_TEST_RUNNER_OPCODE_LIST_TESTS (0x0102)
+
+#endif /* TS_TEST_RUNNER_OPCODES_H */
diff --git a/protocols/service/test_runner/packed-c/run_tests.h b/protocols/service/test_runner/packed-c/run_tests.h
new file mode 100644
index 0000000..9b25fa6
--- /dev/null
+++ b/protocols/service/test_runner/packed-c/run_tests.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_TEST_RUNNER_RUN_TESTS
+#define TS_TEST_RUNNER_RUN_TESTS
+
+/**
+ * Input parmeters consist of test spec (defined in test_spec.h) to
+ * define the set of tests to run.
+ */
+#include "test_spec.h"
+
+/* Output parameters consist of a test summary followed
+ * by a setof [0..*] variable length test result records.
+ * Each test result has a fixed size structure followed
+ * by variable length parameters that specify the test
+ * name and group.
+ */
+#include "test_result.h"
+
+#endif /* TS_TEST_RUNNER_RUN_TESTS */
diff --git a/protocols/service/test_runner/packed-c/status.h b/protocols/service/test_runner/packed-c/status.h
new file mode 100644
index 0000000..d0a00e0
--- /dev/null
+++ b/protocols/service/test_runner/packed-c/status.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_TEST_RUNNER_STATUS_H
+#define TS_TEST_RUNNER_STATUS_H
+
+/**
+ * Test runner service level statos codes
+ */
+enum
+{
+ /**
+ * Returned if an operation completed successfully.
+ * This doesn't mean that requested tests passed
+ * but rather that the test runner operation
+ * completed normally.
+ */
+ TS_TEST_RUNNER_STATUS_SUCCESS = 0,
+
+ /**
+ * Generic error occurred.
+ */
+ TS_TEST_RUNNER_STATUS_ERROR = -1,
+
+ /**
+ * Invalid test resuts returned by service provider.
+ */
+ TS_TEST_RUNNER_STATUS_INVALID_TEST_RESULTS = -2
+};
+
+#endif /* TS_TEST_RUNNER_STATUS_H */
diff --git a/protocols/service/test_runner/packed-c/test_result.h b/protocols/service/test_runner/packed-c/test_result.h
new file mode 100644
index 0000000..888d2ec
--- /dev/null
+++ b/protocols/service/test_runner/packed-c/test_result.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_TEST_RUNNER_TEST_RESULT
+#define TS_TEST_RUNNER_TEST_RESULT
+
+#include <stdint.h>
+
+/**
+ * Test result summary structure
+ */
+struct __attribute__ ((__packed__)) ts_test_runner_result_summary
+{
+ uint32_t num_tests;
+ uint32_t num_passed;
+ uint32_t num_failed;
+};
+
+/**
+ * Variable length parameter tag for a test result object.
+ * Multiple test results may be returned for a test run.
+ */
+enum
+{
+ /* A test result record describes the result of a
+ * particular test.
+ */
+ TS_TEST_RUNNER_TEST_RESULT_TAG = 1
+};
+
+/* Test run state values */
+enum
+{
+ TS_TEST_RUNNER_TEST_RESULT_RUN_STATE_NOT_RUN = 1,
+ TS_TEST_RUNNER_TEST_RESULT_RUN_STATE_PASSED = 2,
+ TS_TEST_RUNNER_TEST_RESULT_RUN_STATE_FAILED = 3
+};
+
+/* Test result fixed sized structure */
+struct __attribute__ ((__packed__)) ts_test_runner_test_result
+{
+ uint32_t run_state;
+ uint32_t fail_line;
+};
+
+/* Variable length output parameter tags */
+enum
+{
+ /* The name of the test */
+ TS_TEST_RUNNER_TEST_RESULT_TAG_NAME = 1,
+
+ /* The group the test belongs to */
+ TS_TEST_RUNNER_TEST_RESULT_TAG_GROUP = 2
+};
+
+#endif /* TS_TEST_RUNNER_TEST_RESULT */
diff --git a/protocols/service/test_runner/packed-c/test_spec.h b/protocols/service/test_runner/packed-c/test_spec.h
new file mode 100644
index 0000000..c31b367
--- /dev/null
+++ b/protocols/service/test_runner/packed-c/test_spec.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_TEST_RUNNER_TEST_SPEC
+#define TS_TEST_RUNNER_TEST_SPEC
+
+/**
+ * Variable length parameters used to specify a test or
+ * group of tests. A missing parameter is interpreted
+ * as a wildcard.
+ */
+enum
+{
+ /* Specifies the name of a particular test to run.
+ * The parameter should consist of an ascii string
+ * without a zero terminator.
+ */
+ TS_TEST_RUNNER_TEST_SPEC_TAG_NAME = 1,
+
+ /* Specifies a group of tests to run.
+ * The parameter should consist of an ascii string
+ * without a zero terminator.
+ */
+ TS_TEST_RUNNER_TEST_SPEC_TAG_GROUP = 2
+};
+
+#endif /* TS_TEST_RUNNER_TEST_SPEC */