diff options
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 000000000..a47ec425e --- /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 000000000..681c7467f --- /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 000000000..9d1fca483 --- /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 661a81024..5d8a50d21 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 13088321c..80c1edbdd 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 f39aa1cff..078a88f0b 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 e9650769f..f7e78ad92 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 000000000..e30f2c641 --- /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 000000000..103f18806 --- /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 000000000..8474ec22f --- /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 80d1777c2..41dd206ab 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 000000000..8bd9e51ec --- /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 000000000..a78000255 --- /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 000000000..b86b93e09 --- /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 000000000..c891918cb --- /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 000000000..4666b2b61 --- /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 000000000..9ea593631 --- /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 000000000..81974121e --- /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 000000000..9d4afb509 --- /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 000000000..4a3764833 --- /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 000000000..1180035b4 --- /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 000000000..577bfdc8f --- /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 000000000..c956bc30a --- /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 000000000..bd457f77b --- /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 000000000..9c9cbc815 --- /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 000000000..4b61bc1d7 --- /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 000000000..e5c92304b --- /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 000000000..7cbd452c9 --- /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 000000000..c4ba0e07f --- /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 2d7c21af4..0e6fd1bea 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 000000000..378eac25d --- /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 000000000..125485d77 --- /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 000000000..0bb523ffd --- /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 000000000..0f4c8b789 --- /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 000000000..14a1e53e6 --- /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 000000000..8ed4a7ea3 --- /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 000000000..a524a6e46 --- /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 7924f7c6c..01387bc9e 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 000000000..378eac25d --- /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 000000000..7540178f8 --- /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 000000000..5e5aaa5d8 --- /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 000000000..7e5b28684 --- /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 000000000..1c708f657 --- /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 8ccc3b3c5..85a0a36f2 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 000000000..25bab9ead --- /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 000000000..e28da2448 --- /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 000000000..9b25fa679 --- /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 000000000..d0a00e02b --- /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 000000000..888d2ece1 --- /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 000000000..c31b367bb --- /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 */ |