Add test_runner service

This is a new service with client and provider that can be
used for running tests in a secure processing environment
and retrieving the results.  The test_runner provider allows
for arbitrary test farmework backends.  The goal is to
have a cpputest backend.  In this commit, a mock backend
is included for testing the service itself.  The service
has its own access protocol defined under the protocols
top-level directory.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: If4e965c110763bd805abbdcb87e7e03cd76248b2
diff --git a/components/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 */