@@ -186,7 +186,18 @@ extern void *TST_RET_PTR;
TST_MSG_(TPASS, " passed", #SCALL, ##__VA_ARGS__); \
} while (0) \
-#define TST_EXP_FAIL_SILENT_(PASS_COND, SCALL, SSCALL, ERRNO, ...) \
+/*
+ * Returns true if err is in the exp_err array.
+ */
+int tst_errno_in_set(int err, const int *exp_errs, int exp_errs_cnt);
+
+/*
+ * Fills in the buf with the errno names in the exp_err set. The buf must be at
+ * least 20 * exp_errs_cnt bytes long.
+ */
+const char *tst_errno_names(char *buf, const int *exp_errs, int exp_errs_cnt);
+
+#define TST_EXP_FAIL_SILENT_(PASS_COND, SCALL, SSCALL, ERRNOS, ERRNOS_CNT, ...)\
do { \
TEST(SCALL); \
\
@@ -203,36 +214,68 @@ extern void *TST_RET_PTR;
break; \
} \
\
- if (TST_ERR == (ERRNO)) { \
+ if (tst_errno_in_set(TST_ERR, ERRNOS, ERRNOS_CNT)) { \
TST_PASS = 1; \
} else { \
+ char tst_str_buf__[ERRNOS_CNT * 20]; \
TST_MSGP_(TFAIL | TTERRNO, " expected %s", \
- tst_strerrno(ERRNO), \
+ tst_errno_names(tst_str_buf__, \
+ ERRNOS, ERRNOS_CNT), \
SSCALL, ##__VA_ARGS__); \
} \
} while (0)
-#define TST_EXP_FAIL(SCALL, ERRNO, ...) \
+#define TST_EXP_FAIL_ARR_(SCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \
do { \
TST_EXP_FAIL_SILENT_(TST_RET == 0, SCALL, #SCALL, \
- ERRNO, ##__VA_ARGS__); \
+ EXP_ERRS, EXP_ERRS_CNT, ##__VA_ARGS__); \
if (TST_PASS) \
TST_MSG_(TPASS | TTERRNO, " ", #SCALL, ##__VA_ARGS__); \
} while (0)
-#define TST_EXP_FAIL2(SCALL, ERRNO, ...) \
+#define TST_EXP_FAIL(SCALL, EXP_ERR, ...) \
+ do { \
+ int tst_exp_err__ = EXP_ERR; \
+ TST_EXP_FAIL_ARR_(SCALL, &tst_exp_err__, 1, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define TST_EXP_FAIL_ARR(SCALL, EXP_ERRS, ...) \
+ TST_EXP_FAIL_ARR_(SCALL, EXP_ERRS, ARRAY_SIZE(EXP_ERRS), \
+ ##__VA_ARGS__); \
+
+#define TST_EXP_FAIL2_ARR_(SCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \
do { \
TST_EXP_FAIL_SILENT_(TST_RET >= 0, SCALL, #SCALL, \
- ERRNO, ##__VA_ARGS__); \
+ EXP_ERRS, EXP_ERRS_CNT, ##__VA_ARGS__); \
if (TST_PASS) \
TST_MSG_(TPASS | TTERRNO, " ", #SCALL, ##__VA_ARGS__); \
} while (0)
-#define TST_EXP_FAIL_SILENT(SCALL, ERRNO, ...) \
- TST_EXP_FAIL_SILENT_(TST_RET == 0, SCALL, #SCALL, ERRNO, ##__VA_ARGS__)
+#define TST_EXP_FAIL2_ARR(SCALL, EXP_ERRS, ...) \
+ TST_EXP_FAIL2_ARR_(SCALL, EXP_ERRS, ARRAY_SIZE(EXP_ERRS), \
+ ##__VA_ARGS__); \
-#define TST_EXP_FAIL2_SILENT(SCALL, ERRNO, ...) \
- TST_EXP_FAIL_SILENT_(TST_RET >= 0, SCALL, #SCALL, ERRNO, ##__VA_ARGS__)
+#define TST_EXP_FAIL2(SCALL, EXP_ERR, ...) \
+ do { \
+ int tst_exp_err__ = EXP_ERR; \
+ TST_EXP_FAIL2_ARR_(SCALL, &tst_exp_err__, 1, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define TST_EXP_FAIL_SILENT(SCALL, EXP_ERR, ...) \
+ do { \
+ int tst_exp_err__ = EXP_ERR; \
+ TST_EXP_FAIL_SILENT_(TST_RET == 0, SCALL, #SCALL, \
+ &tst_exp_err__, 1, ##__VA_ARGS__); \
+ } while (0)
+
+#define TST_EXP_FAIL2_SILENT(SCALL, EXP_ERR, ...) \
+ do { \
+ int tst_exp_err__ = EXP_ERR; \
+ TST_EXP_FAIL_SILENT_(TST_RET >= 0, SCALL, #SCALL, \
+ &tst_exp_err__, 1, ##__VA_ARGS__); \
+ } while (0)
#define TST_EXP_EXPR(EXPR, FMT, ...) \
tst_res_(__FILE__, __LINE__, (EXPR) ? TPASS : TFAIL, "Expect: " FMT, ##__VA_ARGS__);
@@ -27,6 +27,9 @@ static int inval_ret_fn(void)
static void do_test(void)
{
+ const int exp_errs_pass[] = {ENOTTY, EINVAL};
+ const int exp_errs_fail[] = {ENOTTY, EISDIR};
+
tst_res(TINFO, "Testing TST_EXP_FAIL macro");
TST_EXP_FAIL(fail_fn(), EINVAL, "fail_fn()");
tst_res(TINFO, "TST_PASS = %i", TST_PASS);
@@ -36,6 +39,10 @@ static void do_test(void)
tst_res(TINFO, "TST_PASS = %i", TST_PASS);
TST_EXP_FAIL(inval_ret_fn(), ENOTTY, "inval_ret_fn()");
tst_res(TINFO, "TST_PASS = %i", TST_PASS);
+ TST_EXP_FAIL_ARR(fail_fn(), exp_errs_pass, "fail_fn()");
+ tst_res(TINFO, "TST_PASS = %i", TST_PASS);
+ TST_EXP_FAIL_ARR(fail_fn(), exp_errs_fail, "fail_fn()");
+ tst_res(TINFO, "TST_PASS = %i", TST_PASS);
tst_res(TINFO, "Testing TST_EXP_FAIL2 macro");
TST_EXP_FAIL2(fail_fn(), EINVAL, "fail_fn()");
@@ -46,6 +53,10 @@ static void do_test(void)
tst_res(TINFO, "TST_PASS = %i", TST_PASS);
TST_EXP_FAIL2(inval_ret_fn(), ENOTTY, "inval_ret_fn()");
tst_res(TINFO, "TST_PASS = %i", TST_PASS);
+ TST_EXP_FAIL2_ARR(fail_fn(), exp_errs_pass, "fail_fn()");
+ tst_res(TINFO, "TST_PASS = %i", TST_PASS);
+ TST_EXP_FAIL2_ARR(fail_fn(), exp_errs_fail, "fail_fn()");
+ tst_res(TINFO, "TST_PASS = %i", TST_PASS);
}
static struct tst_test test = {
new file mode 100644
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <stdio.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_test_macros.h"
+
+int tst_errno_in_set(int err, const int *exp_errs, int exp_errs_cnt)
+{
+ int i;
+
+ for (i = 0; i < exp_errs_cnt; i++) {
+ if (err == exp_errs[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *tst_errno_names(char *buf, const int *exp_errs, int exp_errs_cnt)
+{
+ int i;
+ char *cb = buf;
+
+ for (i = 0; i < exp_errs_cnt-1; i++)
+ cb += sprintf(cb, "%s, ", tst_strerrno(exp_errs[i]));
+
+ cb += sprintf(cb, "%s", tst_strerrno(exp_errs[i]));
+
+ *cb = 0;
+
+ return buf;
+}
For certain cases there is a possibility of a failure with more than one errno, for instance testcases with invalid fd may return either EINVAL or EBADFD and in many cases either one is fine, at least that was the feedback from kernel devs. This change also adds a tst_errno_in_set() function that is now the single place to validate errno for all TST_EXP_FAIL*() variants. That is intentional since this allows us to implement code to relax the conditions if needed, e.g. we had requests to allow additional errnos for systems with SELinux where failures may be caused by the SELinux policies and the errors may differ. Signed-off-by: Cyril Hrubis <chrubis@suse.cz> --- include/tst_test_macros.h | 65 ++++++++++++++++++++++++++------ lib/newlib_tests/test_macros02.c | 11 ++++++ lib/tst_test_macros.c | 36 ++++++++++++++++++ 3 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 lib/tst_test_macros.c