htest: service boilerplate for secondary VMs.
In order to keep it easy to add tests, we need to avoid requiring new
VMs for each case as this will lead to an explosion of duplication and
build artifacts.
Allowing secondary VMs to contain selectable services for different
tests means they don't each require a separate VM and run into resource
limits. The resulting services are also avoid boilerplate so the source
gets to the point of the test more easily.
The primary VMs are also refactored to encourage splitting tests across
file but bundling them into the same VM so as to cut down on build
artifacts and make it easier to find and run tests.
Assertions can now be used in any context of a test, including in a test
service.
Change-Id: Id3b8a7579d0facdfceb9d77f62ef57241b31a88a
diff --git a/test/hftest/inc/hftest.h b/test/hftest/inc/hftest.h
index 290b467..894d7f0 100644
--- a/test/hftest/inc/hftest.h
+++ b/test/hftest/inc/hftest.h
@@ -37,27 +37,39 @@
*/
#define TEST(suite, test) HFTEST_TEST(suite, test)
+/*
+ * Define a test service.
+ */
+#define TEST_SERVICE(service) HFTEST_TEST_SERVICE(service)
+
/* Assertions. */
-#define ASSERT_EQ(x, y) ASSERT_OP(x, y, ==, true)
-#define ASSERT_NE(x, y) ASSERT_OP(x, y, !=, true)
-#define ASSERT_LE(x, y) ASSERT_OP(x, y, <=, true)
-#define ASSERT_LT(x, y) ASSERT_OP(x, y, <, true)
-#define ASSERT_GE(x, y) ASSERT_OP(x, y, >=, true)
-#define ASSERT_GT(x, y) ASSERT_OP(x, y, >, true)
+#define ASSERT_EQ(x, y) HFTEST_ASSERT_OP(x, y, ==, true)
+#define ASSERT_NE(x, y) HFTEST_ASSERT_OP(x, y, !=, true)
+#define ASSERT_LE(x, y) HFTEST_ASSERT_OP(x, y, <=, true)
+#define ASSERT_LT(x, y) HFTEST_ASSERT_OP(x, y, <, true)
+#define ASSERT_GE(x, y) HFTEST_ASSERT_OP(x, y, >=, true)
+#define ASSERT_GT(x, y) HFTEST_ASSERT_OP(x, y, >, true)
#define ASSERT_TRUE(x) ASSERT_EQ(x, true);
#define ASSERT_FALSE(x) ASSERT_EQ(x, false);
-#define EXPECT_EQ(x, y) ASSERT_OP(x, y, ==, false)
-#define EXPECT_NE(x, y) ASSERT_OP(x, y, !=, false)
-#define EXPECT_LE(x, y) ASSERT_OP(x, y, <=, false)
-#define EXPECT_LT(x, y) ASSERT_OP(x, y, <, false)
-#define EXPECT_GE(x, y) ASSERT_OP(x, y, >=, false)
-#define EXPECT_GT(x, y) ASSERT_OP(x, y, >, false)
+#define EXPECT_EQ(x, y) HFTEST_ASSERT_OP(x, y, ==, false)
+#define EXPECT_NE(x, y) HFTEST_ASSERT_OP(x, y, !=, false)
+#define EXPECT_LE(x, y) HFTEST_ASSERT_OP(x, y, <=, false)
+#define EXPECT_LT(x, y) HFTEST_ASSERT_OP(x, y, <, false)
+#define EXPECT_GE(x, y) HFTEST_ASSERT_OP(x, y, >=, false)
+#define EXPECT_GT(x, y) HFTEST_ASSERT_OP(x, y, >, false)
#define EXPECT_TRUE(x) EXPECT_EQ(x, true);
#define EXPECT_FALSE(x) EXPECT_EQ(x, false);
+/* Service utilities. */
+#define SERVICE_SELECT(vm_id, service, send_buffer) \
+ HFTEST_SERVICE_SELECT(vm_id, service, send_buffer)
+
+#define SERVICE_SEND_BUFFER() HFTEST_SERVICE_SEND_BUFFER()
+#define SERVICE_RECV_BUFFER() HFTEST_SERVICE_RECV_BUFFER()
+
/*
* This must be used exactly once in a test image to signal to the linker that
* the .hftest section is allowed to be included in the generated image.
@@ -69,185 +81,10 @@
*/
#define HFTEST_LOG_PREFIX "[hftest] "
-/* Above this point is the public API. Below are the implementation details. */
-
-/* Log with the HFTEST_LOG_PREFIX and a new line. The zero is added so there is
- * always at least one variadic argument. */
-#define HFTEST_LOG(...) HFTEST_LOG_IMPL(__VA_ARGS__, 0)
-#define HFTEST_LOG_IMPL(format, ...) \
- dlog("%s" format "\n", HFTEST_LOG_PREFIX, __VA_ARGS__)
-
-/* Helper to wrap the argument in quotes. */
-#define HFTEST_STR(str) #str
-
-/* Sections are names such that when the linker sorts them, all entries for the
- * same test suite are contiguous and the set up and tear down entries come
- * before the tests. This order simplifies test discovery in the running image.
- */
-#define HFTEST_SET_UP_SECTION(suite_name) \
- HFTEST_STR(.hftest.suite_name .1set_up)
-#define HFTEST_TEAR_DOWN_SECTION(suite_name) \
- HFTEST_STR(.hftest.suite_name .1tear_down)
-#define HFTEST_TEST_SECTION(suite_name, test_name) \
- HFTEST_STR(.hftest.suite_name .2test.test_name)
-
-/* Helpers to construct unique identifiers. */
-#define HFTEST_SET_UP_STRUCT(suite_name) hftest_set_up_##suite_name
-#define HFTEST_TEAR_DOWN_STRUCT(suite_name) hftest_tear_down_##suite_name
-#define HFTEST_TEST_STRUCT(suite_name, test_name) \
- hftest_test_##suite_name##_##test_name
-
-#define HFTEST_SET_UP_FN(suite_name) hftest_set_up_fn_##suite_name
-#define HFTEST_TEAR_DOWN_FN(suite_name) hftest_tear_down_fn_##suite_name
-#define HFTEST_TEST_FN(suite_name, test_name) \
- hftest_test_fn_##suite_name##_##test_name
-
-/* Register test functions. */
-#define HFTEST_SET_UP(suite_name) \
- static void HFTEST_SET_UP_FN(suite_name)(struct hftest_context * \
- hftest_ctx); \
- const struct hftest_test __attribute__((used)) \
- __attribute__((section(HFTEST_SET_UP_SECTION(suite_name)))) \
- HFTEST_SET_UP_STRUCT(suite_name) = { \
- .suite = #suite_name, \
- .kind = HFTEST_KIND_SET_UP, \
- .fn = HFTEST_SET_UP_FN(suite_name), \
- }; \
- static void HFTEST_SET_UP_FN(suite_name)( \
- __attribute__((unused)) struct hftest_context * hftest_ctx)
-
-#define HFTEST_TEAR_DOWN(suite_name) \
- static void HFTEST_TEAR_DOWN_FN(suite_name)(struct hftest_context * \
- hftest_ctx); \
- const struct hftest_test __attribute__((used)) \
- __attribute__((section(HFTEST_TEAR_DOWN_SECTION(suite_name)))) \
- HFTEST_TEAR_DOWN_STRUCT(suite_name) = { \
- .suite = #suite_name, \
- .kind = HFTEST_KIND_TEAR_DOWN, \
- .fn = HFTEST_TEAR_DOWN_FN(suite_name), \
- }; \
- static void HFTEST_TEAR_DOWN_FN(suite_name)( \
- __attribute__((unused)) struct hftest_context * hftest_ctx)
-
-#define HFTEST_TEST(suite_name, test_name) \
- static void HFTEST_TEST_FN( \
- suite_name, test_name)(struct hftest_context * hftest_ctx); \
- const struct hftest_test __attribute__((used)) __attribute__( \
- (section(HFTEST_TEST_SECTION(suite_name, test_name)))) \
- HFTEST_TEST_STRUCT(suite_name, test_name) = { \
- .suite = #suite_name, \
- .kind = HFTEST_KIND_TEST, \
- .name = #test_name, \
- .fn = HFTEST_TEST_FN(suite_name, test_name), \
- }; \
- static void HFTEST_TEST_FN(suite_name, test_name)( \
- __attribute__((unused)) struct hftest_context * hftest_ctx)
-
-/* Context for tests. */
-struct hftest_context {
- uint32_t failures;
-};
-
-/* A test case. */
-typedef void (*hftest_test_fn)(struct hftest_context *);
-
-enum hftest_kind {
- HFTEST_KIND_SET_UP = 0,
- HFTEST_KIND_TEST = 1,
- HFTEST_KIND_TEAR_DOWN = 2,
-};
-
-struct hftest_test {
- const char *suite;
- enum hftest_kind kind;
- const char *name;
- hftest_test_fn fn;
-};
-
/*
- * This union can store any of the primitive types supported by the assertion
- * macros.
- *
- * It does not include pointers as comparison of pointers is not often needed
- * and could be a mistake for string comparison. If pointer comparison is needed
- * and explicit assertion such as ASSERT_PTR_EQ() would be more appropriate.
+ * Indentation used e.g. to give the reason for an assertion failure.
*/
-union hftest_any {
- bool b;
- char c;
- signed char sc;
- unsigned char uc;
- signed short ss;
- unsigned short us;
- signed int si;
- unsigned int ui;
- signed long int sli;
- unsigned long int uli;
- signed long long int slli;
- unsigned long long int ulli;
-};
+#define HFTEST_LOG_INDENT " "
-/* _Generic formatting doesn't seem to be supported so doing this manually. */
-/* clang-format off */
-
-/* Select the union member to match the type of the expression. */
-#define hftest_any_get(any, x) \
- _Generic((x), \
- bool: (any).b, \
- char: (any).c, \
- signed char: (any).sc, \
- unsigned char: (any).uc, \
- signed short: (any).ss, \
- unsigned short: (any).us, \
- signed int: (any).si, \
- unsigned int: (any).ui, \
- signed long int: (any).sli, \
- unsigned long int: (any).uli, \
- signed long long int: (any).slli, \
- unsigned long long int: (any).ulli)
-
-/*
- * dlog format specifier for types. Note, these aren't the standard specifiers
- * for the types.
- */
-#define hftest_dlog_format(x) \
- _Generic((x), \
- bool: "%u", \
- char: "%c", \
- signed char: "%d", \
- unsigned char: "%u", \
- signed short: "%d", \
- unsigned short: "%u", \
- signed int: "%d", \
- unsigned int: "%u", \
- signed long int: "%d", \
- unsigned long int: "%u", \
- signed long long int: "%d", \
- unsigned long long int: "%u")
-
-/* clang-format on */
-
-#define ASSERT_OP(lhs, rhs, op, fatal) \
- do { \
- union hftest_any lhs_value; \
- union hftest_any rhs_value; \
- hftest_any_get(lhs_value, lhs) = (lhs); \
- hftest_any_get(rhs_value, rhs) = (rhs); \
- if (!(hftest_any_get(lhs_value, lhs) \
- op hftest_any_get(rhs_value, rhs))) { \
- ++hftest_ctx->failures; \
- dlog(HFTEST_LOG_PREFIX " %s:%u: Failure\n", __FILE__, \
- __LINE__); \
- dlog(HFTEST_LOG_PREFIX " %s %s %s (%s=", #lhs, #op, \
- #rhs, #lhs); \
- dlog(hftest_dlog_format(lhs), \
- hftest_any_get(lhs_value, lhs)); \
- dlog(", %s=", #rhs); \
- dlog(hftest_dlog_format(rhs), \
- hftest_any_get(rhs_value, rhs)); \
- dlog(")\n"); \
- if (fatal) { \
- return; \
- } \
- } \
- } while (0)
+/* Above this point is the public API. Now include the implementation. */
+#include <hftest_impl.h>