Infrastructure for running tests under Linux in primary VM.
Includes an initial test of simply inserting and removing the Hafnium
kernel module.
Change-Id: I832a30d902f58ca71f89374300ab39b2ba3ab877
diff --git a/test/hftest/hftest_common.c b/test/hftest/hftest_common.c
new file mode 100644
index 0000000..871aa54
--- /dev/null
+++ b/test/hftest/hftest_common.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2018 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hftest_common.h"
+
+#include "hf/arch/std.h"
+#include "hf/arch/vm/power_mgmt.h"
+
+#include "hf/memiter.h"
+
+#include "hftest.h"
+
+HFTEST_ENABLE();
+
+static struct hftest_test hftest_constructed[HFTEST_MAX_TESTS];
+static size_t hftest_count;
+static struct hftest_test *hftest_list;
+
+static struct hftest_context global_context;
+
+struct hftest_context *hftest_get_context(void)
+{
+ return &global_context;
+}
+
+/**
+ * Adds the given test information to the global list, to be used by
+ * `hftest_use_registered_list`.
+ */
+void hftest_register(struct hftest_test test)
+{
+ if (hftest_count < HFTEST_MAX_TESTS) {
+ hftest_constructed[hftest_count++] = test;
+ } else {
+ HFTEST_FAIL("Too many tests", true);
+ }
+}
+
+/**
+ * Uses the list of tests registered by `hftest_register(...)` as the ones to
+ * run.
+ */
+void hftest_use_registered_list(void)
+{
+ hftest_list = hftest_constructed;
+}
+
+/**
+ * Uses the given list of tests as the ones to run.
+ */
+void hftest_use_list(struct hftest_test list[], size_t count)
+{
+ hftest_list = list;
+ hftest_count = count;
+}
+
+/**
+ * Writes out a JSON structure describing the available tests.
+ */
+void hftest_json(void)
+{
+ const char *suite = NULL;
+ size_t i;
+ size_t suites_in_image = 0;
+ size_t tests_in_suite = 0;
+
+ HFTEST_LOG("{");
+ HFTEST_LOG(" \"suites\": [");
+ for (i = 0; i < hftest_count; ++i) {
+ struct hftest_test *test = &hftest_list[i];
+ if (test->suite != suite) {
+ /* Close out previously open suite. */
+ if (tests_in_suite) {
+ HFTEST_LOG(" ]");
+ HFTEST_LOG(" },");
+ }
+ /* Move onto new suite. */
+ ++suites_in_image;
+ suite = test->suite;
+ tests_in_suite = 0;
+ HFTEST_LOG(" {");
+ HFTEST_LOG(" \"name\": \"%s\",", test->suite);
+ }
+ if (test->kind == HFTEST_KIND_SET_UP) {
+ HFTEST_LOG(" \"setup\": true,");
+ }
+ if (test->kind == HFTEST_KIND_TEAR_DOWN) {
+ HFTEST_LOG(" \"teardown\": true,");
+ }
+ if (test->kind == HFTEST_KIND_TEST) {
+ if (!tests_in_suite) {
+ HFTEST_LOG(" \"tests\": [");
+ }
+ /*
+ * It's easier to put the comma at the start of the line
+ * than the end even
+ * though the JSON looks a bit funky.
+ */
+ HFTEST_LOG(" %c\"%s\"",
+ tests_in_suite ? ',' : ' ', test->name);
+ ++tests_in_suite;
+ }
+ }
+ if (tests_in_suite) {
+ HFTEST_LOG(" ]");
+ HFTEST_LOG(" }");
+ }
+ HFTEST_LOG(" ]");
+ HFTEST_LOG("}");
+}
+
+/**
+ * Logs a failure message and shut down.
+ */
+static noreturn void abort(void)
+{
+ HFTEST_LOG("FAIL");
+ arch_power_off();
+}
+
+static void run_test(hftest_test_fn set_up, hftest_test_fn test,
+ hftest_test_fn tear_down)
+{
+ /* Prepare the context. */
+ struct hftest_context *ctx = hftest_get_context();
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->abort = abort;
+
+ /* Run any set up functions. */
+ if (set_up) {
+ set_up();
+ if (ctx->failures) {
+ abort();
+ }
+ }
+
+ /* Run the test. */
+ test();
+ if (ctx->failures) {
+ abort();
+ }
+
+ /* Run any tear down functions. */
+ if (tear_down) {
+ tear_down();
+ if (ctx->failures) {
+ abort();
+ }
+ }
+
+ HFTEST_LOG("FINISHED");
+}
+
+/**
+ * Runs the given test case.
+ */
+void hftest_run(struct memiter suite_name, struct memiter test_name)
+{
+ size_t i;
+ bool found_suite = false;
+ const char *suite = NULL;
+ hftest_test_fn suite_set_up = NULL;
+ hftest_test_fn suite_tear_down = NULL;
+
+ for (i = 0; i < hftest_count; ++i) {
+ struct hftest_test *test = &hftest_list[i];
+ /* Find the test suite. */
+ if (found_suite) {
+ if (test->suite != suite) {
+ /* Test wasn't in the suite. */
+ break;
+ }
+ } else {
+ if (test->suite == suite) {
+ /* This isn't the right suite so keep going. */
+ continue;
+ }
+ /* Examine a new suite. */
+ suite = test->suite;
+ if (memiter_iseq(&suite_name, test->suite)) {
+ found_suite = true;
+ }
+ }
+
+ switch (test->kind) {
+ /*
+ * The first entries in the suite are the set up and tear down
+ * functions.
+ */
+ case HFTEST_KIND_SET_UP:
+ suite_set_up = test->fn;
+ break;
+ case HFTEST_KIND_TEAR_DOWN:
+ suite_tear_down = test->fn;
+ break;
+ /* Find the test. */
+ case HFTEST_KIND_TEST:
+ if (memiter_iseq(&test_name, test->name)) {
+ run_test(suite_set_up, test->fn,
+ suite_tear_down);
+ return;
+ }
+ break;
+ default:
+ /* Ignore other kinds. */
+ break;
+ }
+ }
+
+ HFTEST_LOG("Unable to find requested tests.");
+}
+
+/**
+ * Writes out usage information.
+ */
+void hftest_help(void)
+{
+ HFTEST_LOG("usage:");
+ HFTEST_LOG("");
+ HFTEST_LOG(" help");
+ HFTEST_LOG("");
+ HFTEST_LOG(" Show this help.");
+ HFTEST_LOG("");
+ HFTEST_LOG(" json");
+ HFTEST_LOG("");
+ HFTEST_LOG(
+ " Print a directory of test suites and tests in "
+ "JSON "
+ "format.");
+ HFTEST_LOG("");
+ HFTEST_LOG(" run <suite> <test>");
+ HFTEST_LOG("");
+ HFTEST_LOG(" Run the named test from the named test suite.");
+}