feat(hftest): test conditional execution

The structure 'hftest_test' defines the entry for the
test table that is built with the help of the "TEST_*"
macros.

This patch adds a new field "precondition". It is a
pointer to a function that returns a boolean.
If a test is configured with a precondition function,
this precondition must be executed to determine if
test must be skipped or not, in the running
setup.

The "json" command in the PVM, dumps a structure
with the information of the tests linked in the image.
This patch changed the "json" command to include
a "skip_test" field, which is used by "hftest.py"
to determine if test should be skipped or not.

This is a useful functionality, as we are adding more
and more testing configurations, whilst attempting
to reuse the test code as much as possible.
The precondition function might look into aspects
such as:
- Security state of test services.
- Exception level of test services.
- Support to given features.
- etc
... before deciding if the test must be skipped
for its running test setup.

As the hafnium CI seems to take long to execute, and tests
using FVP are particularly slow, being able to skip tests
given certain conditions might be useful to save execution
time in hafnium CI runs.

Change-Id: I741aa484b08f224aff3394f949aa1096dcdfce4e
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/test/hftest/common.c b/test/hftest/common.c
index 344ff24..8573eca 100644
--- a/test/hftest/common.c
+++ b/test/hftest/common.c
@@ -94,6 +94,14 @@
 			HFTEST_LOG("      \"teardown\": true,");
 		}
 		if (test->kind == HFTEST_KIND_TEST) {
+			/*
+			 * If test has a precondition, run respective function.
+			 * If it returns false, then the current setup is not
+			 * meant to run the test. Hence, we must skip it.
+			 */
+			bool skip_test = test->precondition != NULL &&
+					 !test->precondition();
+
 			if (!tests_in_suite) {
 				HFTEST_LOG("      \"tests\": [");
 			}
@@ -103,8 +111,10 @@
 			 */
 			HFTEST_LOG("       %c{", tests_in_suite ? ',' : ' ');
 			HFTEST_LOG("          \"name\": \"%s\",", test->name);
-			HFTEST_LOG("          \"is_long_running\": %s",
+			HFTEST_LOG("          \"is_long_running\": %s,",
 				   test->is_long_running ? "true" : "false");
+			HFTEST_LOG("          \"skip_test\": %s",
+				   skip_test ? "true" : "false");
 			HFTEST_LOG("       }");
 			++tests_in_suite;
 		}
diff --git a/test/hftest/hftest.py b/test/hftest/hftest.py
index 22eb5f9..d95c0b1 100755
--- a/test/hftest/hftest.py
+++ b/test/hftest/hftest.py
@@ -780,7 +780,7 @@
         test_xml.set("name", test["name"])
         test_xml.set("classname", suite["name"])
 
-        if self.skip_long_running_tests and test["is_long_running"]:
+        if (self.skip_long_running_tests and test["is_long_running"]) or test["skip_test"]:
             print("      SKIP", test["name"])
             test_xml.set("status", "notrun")
             skipped_xml = ET.SubElement(test_xml, "skipped")
diff --git a/test/inc/test/hftest.h b/test/inc/test/hftest.h
index 6411f74..4eceac6 100644
--- a/test/inc/test/hftest.h
+++ b/test/inc/test/hftest.h
@@ -30,12 +30,18 @@
 /*
  * Define a test as part of a test suite.
  */
-#define TEST(suite, test) HFTEST_TEST(suite, test, false)
+#define TEST(suite, test) HFTEST_TEST(suite, test, false, NULL)
 
 /*
  * Define a test as part of a test suite and mark it long-running.
  */
-#define TEST_LONG_RUNNING(suite, test) HFTEST_TEST(suite, test, true)
+#define TEST_LONG_RUNNING(suite, test) HFTEST_TEST(suite, test, true, NULL)
+
+/*
+ * Define a test as part of a test suite and add a precondition function.
+ */
+#define TEST_PRECONDITION(suite, test, precon_fn) \
+	HFTEST_TEST(suite, test, false, precon_fn)
 
 /*
  * Define a test service.
diff --git a/test/inc/test/hftest_impl.h b/test/inc/test/hftest_impl.h
index 8b83488..005b772 100644
--- a/test/inc/test/hftest_impl.h
+++ b/test/inc/test/hftest_impl.h
@@ -95,7 +95,7 @@
 	}                                                              \
 	static void HFTEST_TEAR_DOWN_FN(suite_name)(void)
 
-#define HFTEST_TEST(suite_name, test_name, long_running)                     \
+#define HFTEST_TEST(suite_name, test_name, long_running, precon_fn)          \
 	static void HFTEST_TEST_FN(suite_name, test_name)(void);             \
 	const struct hftest_test __attribute__((used))                       \
 	__attribute__((section(HFTEST_TEST_SECTION(suite_name, test_name)))) \
@@ -105,6 +105,7 @@
 		.name = #test_name,                                          \
 		.is_long_running = long_running,                             \
 		.fn = HFTEST_TEST_FN(suite_name, test_name),                 \
+		.precondition = precon_fn,                                   \
 	};                                                                   \
 	static void __attribute__((constructor))                             \
 	HFTEST_TEST_CONSTRUCTOR(suite_name, test_name)(void)                 \
@@ -121,6 +122,7 @@
 		.kind = HFTEST_KIND_SERVICE,                           \
 		.name = #service_name,                                 \
 		.fn = HFTEST_SERVICE_FN(service_name),                 \
+		.precondition = NULL,                                  \
 	};                                                             \
 	static void HFTEST_SERVICE_FN(service_name)(void)
 
@@ -142,6 +144,7 @@
 
 /* A test case. */
 typedef void (*hftest_test_fn)(void);
+typedef bool (*hftest_test_precondition)(void);
 
 enum hftest_kind {
 	HFTEST_KIND_SET_UP = 0,
@@ -161,6 +164,7 @@
 	const char *name;
 	bool is_long_running;
 	hftest_test_fn fn;
+	hftest_test_precondition precondition;
 };
 
 /*