[LTP] [PATCH 3/3] lib/tst_kconfig: Make use of boolean expression eval
Cyril Hrubis
chrubis@suse.cz
Tue Oct 20 12:09:10 CEST 2020
Now each string in the kconfig[] array in tst_test structure is an
boolean expression which is evaluated. All expressions has to be true in
order for the test to continue.
+ Update the docs.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
CC: Pengfei Xu <pengfei.xu@intel.com>
---
doc/test-writing-guidelines.txt | 21 ++--
lib/newlib_tests/test_kconfig.c | 1 +
lib/tst_kconfig.c | 186 ++++++++++++++++++++++++--------
3 files changed, 154 insertions(+), 54 deletions(-)
diff --git a/doc/test-writing-guidelines.txt b/doc/test-writing-guidelines.txt
index 1a51ef7c7..3c2ab7166 100644
--- a/doc/test-writing-guidelines.txt
+++ b/doc/test-writing-guidelines.txt
@@ -1643,21 +1643,26 @@ on the system, disabled syscalls can be detected by checking for 'ENOSYS'
errno etc.
However in rare cases core kernel features couldn't be detected based on the
-kernel userspace API and we have to resort to kernel .config parsing.
+kernel userspace API and we have to resort to parse the kernel .config.
-For this cases the test should set the 'NULL' terminated '.needs_kconfigs' array
-of kernel config options required for the test. The config option can be
-specified either as plain "CONFIG_FOO" in which case it's sufficient for the
-test continue if it's set to any value (typically =y or =m). Or with a value
-as "CONFIG_FOO=bar" in which case the value has to match as well. The test is
-aborted with 'TCONF' if any of the required options were not set.
+For this cases the test should set the 'NULL' terminated '.needs_kconfigs'
+array of boolean expressions with constraints on the kconfig variables. The
+boolean expression consits of variables, two binary operations '&' and '|',
+negation '!' and correct sequence of parentesis '()'. Variables are expected
+to be in a form of "CONFIG_FOO[=bar]".
+
+The test will continue to run if all expressions are evaluated to 'True'.
+Missing variable is mapped to 'False' as well as variable with different than
+specified value, e.g. 'CONFIG_FOO=bar' will evaluate to 'False' if the value
+is anything else but 'bar'. If config variable is specified as plain
+'CONFIG_FOO' it's evaluated to true it's set to any value (typically =y or =m).
[source,c]
-------------------------------------------------------------------------------
#include "tst_test.h"
static const char *kconfigs[] = {
- "CONFIG_X86_INTEL_UMIP",
+ "CONFIG_X86_INTEL_UMIP | CONFIG_X86_UMIP",
NULL
};
diff --git a/lib/newlib_tests/test_kconfig.c b/lib/newlib_tests/test_kconfig.c
index d9c662fc5..183d55611 100644
--- a/lib/newlib_tests/test_kconfig.c
+++ b/lib/newlib_tests/test_kconfig.c
@@ -14,6 +14,7 @@ static const char *kconfigs[] = {
"CONFIG_MMU",
"CONFIG_EXT4_FS=m",
"CONFIG_PGTABLE_LEVELS=4",
+ "CONFIG_MMU & CONFIG_EXT4_FS=m",
NULL
};
diff --git a/lib/tst_kconfig.c b/lib/tst_kconfig.c
index f80925cc9..cd99a3034 100644
--- a/lib/tst_kconfig.c
+++ b/lib/tst_kconfig.c
@@ -12,6 +12,7 @@
#define TST_NO_DEFAULT_MAIN
#include "tst_test.h"
#include "tst_kconfig.h"
+#include "tst_bool_expr.h"
static const char *kconfig_path(char *path_buf, size_t path_buf_len)
{
@@ -184,86 +185,179 @@ static size_t array_len(const char *const kconfigs[])
return i;
}
-static int compare_res(struct tst_kconfig_var *var, const char *kconfig,
- char choice, const char *val)
+static inline unsigned int get_len(const char* kconfig)
{
- if (var->choice != choice) {
- tst_res(TINFO, "Needs kernel %s, have %c", kconfig, var->choice);
- return 1;
- }
+ char *sep = index(kconfig, '=');
- if (choice != 'v')
- return 0;
+ if (!sep)
+ return strlen(kconfig);
- if (strcmp(var->val, val)) {
- tst_res(TINFO, "Needs kernel %s, have %s", kconfig, var->val);
- return 1;
+ return sep - kconfig;
+}
+
+static inline unsigned int get_var_cnt(struct tst_expr *exprs[],
+ unsigned int expr_cnt)
+{
+ unsigned int i;
+ struct tst_expr *j;
+ unsigned int cnt = 0;
+
+ for (i = 0; i < expr_cnt; i++) {
+ for (j = exprs[i]; j; j = j->next) {
+ if (j->op == TST_OP_VAR)
+ cnt++;
+ }
}
- return 0;
+ return cnt;
}
-static inline unsigned int get_len(const char* kconfig)
+static struct tst_kconfig_var *find_var(struct tst_kconfig_var vars[],
+ unsigned int var_cnt,
+ const char *var)
{
- char *sep = index(kconfig, '=');
+ unsigned int i;
- if (!sep)
- return strlen(kconfig);
+ for (i = 0; i < var_cnt; i++) {
+ if (!strcmp(vars[i].id, var))
+ return &vars[i];
+ }
- return sep - kconfig;
+ return NULL;
}
-void tst_kconfig_check(const char *const kconfigs[])
+/*
+ * Fill in the kconfig variables array from the expressions. Also makes sure
+ * that each variable is copied to the array exaclty once.
+ */
+static inline unsigned int get_vars(struct tst_expr *exprs[],
+ unsigned int expr_cnt,
+ struct tst_kconfig_var vars[])
{
- size_t vars_cnt = array_len(kconfigs);
- struct tst_kconfig_var vars[vars_cnt];
unsigned int i;
- int abort_test = 0;
+ struct tst_expr *j;
+ unsigned int cnt = 0;
+
+ for (i = 0; i < expr_cnt; i++) {
+ for (j = exprs[i]; j; j = j->next) {
+ struct tst_kconfig_var *var;
- memset(vars, 0, sizeof(*vars) * vars_cnt);
+ if (j->op != TST_OP_VAR)
+ continue;
- for (i = 0; i < vars_cnt; i++) {
- vars[i].id_len = get_len(kconfigs[i]);
+ vars[cnt].id_len = get_len(j->val);
- if (vars[i].id_len >= sizeof(vars[i].id))
- tst_brk(TBROK, "kconfig var id too long!");
+ if (vars[cnt].id_len >= sizeof(vars[cnt].id))
+ tst_brk(TBROK, "kconfig var id too long!");
- strncpy(vars[i].id, kconfigs[i], vars[i].id_len);
+ strncpy(vars[cnt].id, j->val, vars[cnt].id_len);
+
+ var = find_var(vars, cnt, vars[cnt].id);
+
+ if (var)
+ j->priv = var;
+ else
+ j->priv = &vars[cnt++];
+ }
}
- tst_kconfig_read(vars, vars_cnt);
+ return cnt;
+}
+
+static int map(struct tst_expr *expr)
+{
+ struct tst_kconfig_var *var = expr->priv;
- for (i = 0; i < vars_cnt; i++) {
- if (vars[i].choice == 0) {
- tst_res(TINFO, "Missing kernel %s", kconfigs[i]);
- abort_test = 1;
+ if (var->choice == 0)
+ return 0;
+
+ const char *val = strchr(expr->val, '=');
+
+ /* CONFIG_FOO evaluates to true if y or m */
+ if (!val)
+ return var->choice == 'y' || var->choice == 'm';
+
+ char choice = 'v';
+ val++;
+
+ if (!strcmp(val, "n"))
+ choice = 'n';
+
+ if (!strcmp(val, "y"))
+ choice = 'y';
+
+ if (!strcmp(val, "m"))
+ choice = 'm';
+
+ if (choice != 'v')
+ return var->choice == choice;
+
+ return !strcmp(val, var->val);
+}
+
+static void dump_vars(struct tst_expr *expr)
+{
+ struct tst_expr *i;
+ struct tst_kconfig_var *var;
+
+ tst_res(TINFO, "Variables:");
+
+ for (i = expr; i; i = i->next) {
+ if (i->op != TST_OP_VAR)
+ continue;
+
+ var = i->priv;
+
+ if (!var->choice) {
+ tst_res(TINFO, "%s Undefined", var->id);
continue;
}
- if (vars[i].choice == 'n') {
- tst_res(TINFO, "Kernel %s is not set", kconfigs[i]);
- abort_test = 1;
+ if (var->choice == 'v') {
+ tst_res(TINFO, "%s = %s", var->id, var->val);
continue;
}
- const char *val = strchr(kconfigs[i], '=');
+ tst_res(TINFO, "%s = %c", var->id, var->choice);
+ }
+}
- if (val) {
- char choice = 'v';
- val++;
+void tst_kconfig_check(const char *const kconfigs[])
+{
+ size_t expr_cnt = array_len(kconfigs);
+ struct tst_expr *exprs[expr_cnt];
+ unsigned int i, var_cnt;
+ int abort_test = 0;
+
+ for (i = 0; i < expr_cnt; i++) {
+ exprs[i] = tst_bool_expr_parse(kconfigs[i]);
+
+ if (!exprs[i])
+ tst_brk(TBROK, "Invalid kconfig expression!");
+ }
- if (!strcmp(val, "y"))
- choice = 'y';
+ var_cnt = get_var_cnt(exprs, expr_cnt);
+ struct tst_kconfig_var vars[var_cnt];
- if (!strcmp(val, "m"))
- choice = 'm';
+ var_cnt = get_vars(exprs, expr_cnt, vars);
- if (compare_res(&vars[i], kconfigs[i], choice, val))
- abort_test = 1;
+ tst_kconfig_read(vars, var_cnt);
+ for (i = 0; i < expr_cnt; i++) {
+ int val = tst_bool_expr_eval(exprs[i], map);
+
+ if (val != 1) {
+ abort_test = 1;
+ tst_res(TINFO, "Expression '%s' not satisfied!", kconfigs[i]);
+ dump_vars(exprs[i]);
}
- free(vars[i].val);
+ tst_bool_expr_free(exprs[i]);
+ }
+
+ for (i = 0; i < var_cnt; i++) {
+ if (vars[i].choice == 'v')
+ free(vars[i].val);
}
if (abort_test)
--
2.26.2
More information about the ltp
mailing list