[LTP] [PATCH v3 1/2] library: add cmd check handler in needs_cmds

Yang Xu xuyang2018.jy@fujitsu.com
Thu Dec 9 04:21:07 CET 2021


Testcase ie statx05 needs mkfs.ext4 >= 1.43.0 because of encrypt feature.

As Cyril suggested, add cmd check handler in needs_cmd.

This tst_check_cmd not only check cmd whether exists but also check the cmd
version whether meets test's requirement.

In tst_check_cmd function, use strtok_r to split cmd_token,op_token,version_token.
It only supports six operations '>=' '<=' '>' '<' '==' '!='.

Currently, for the command version check, it only supports mkfs.ext4 command. If you
want to support more commands, just add your own .parser and .table_get methond in
version_parsers structure.

Suggested-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Yang Xu <xuyang2018.jy@fujitsu.com>
---
v2-v3:
1. rename check_cmd to tst_check_cmd
2. move code into tst_cmd.c instead of tst_test.c
3. export tst_cmd_check function into tst_private.h
4. add test_needs_cmds cases into runtest.sh
5. fix typo
v1->v2
1. rename tst_version_parser to check_cmd
2. For mkfs_ext4_version_table_get method, use sscanf instead of strtok_r
3. use enum for cmd op
4. fix description
5. add more newlib test for this
 doc/c-test-api.txt                   |  14 +++
 include/tst_private.h                |   9 ++
 lib/newlib_tests/.gitignore          |   8 ++
 lib/newlib_tests/runtest.sh          |   5 +-
 lib/newlib_tests/test_needs_cmds01.c |  25 ++++
 lib/newlib_tests/test_needs_cmds02.c |  24 ++++
 lib/newlib_tests/test_needs_cmds03.c |  24 ++++
 lib/newlib_tests/test_needs_cmds04.c |  24 ++++
 lib/newlib_tests/test_needs_cmds05.c |  24 ++++
 lib/newlib_tests/test_needs_cmds06.c |  24 ++++
 lib/newlib_tests/test_needs_cmds07.c |  24 ++++
 lib/newlib_tests/test_needs_cmds08.c |  27 +++++
 lib/tst_cmd.c                        | 168 +++++++++++++++++++++++++++
 lib/tst_test.c                       |   6 +-
 14 files changed, 400 insertions(+), 6 deletions(-)
 create mode 100644 lib/newlib_tests/test_needs_cmds01.c
 create mode 100644 lib/newlib_tests/test_needs_cmds02.c
 create mode 100644 lib/newlib_tests/test_needs_cmds03.c
 create mode 100644 lib/newlib_tests/test_needs_cmds04.c
 create mode 100644 lib/newlib_tests/test_needs_cmds05.c
 create mode 100644 lib/newlib_tests/test_needs_cmds06.c
 create mode 100644 lib/newlib_tests/test_needs_cmds07.c
 create mode 100644 lib/newlib_tests/test_needs_cmds08.c

diff --git a/doc/c-test-api.txt b/doc/c-test-api.txt
index 64d0630ce..0555cd614 100644
--- a/doc/c-test-api.txt
+++ b/doc/c-test-api.txt
@@ -2013,6 +2013,20 @@ terminated array of strings such as:
 },
 -------------------------------------------------------------------------------
 
+Also can check required commands version whether is satisfied by using 'needs_cmds',
+
+[source,c]
+-------------------------------------------------------------------------------
+.needs_cmds = (const char *const []) {
+	"mkfs.ext4 >= 1.43.0",
+	NULL
+},
++-------------------------------------------------------------------------------
+
+Currently, we only support mkfs.ext4 command version check.
+If you want to support more commands, please fill your own .parser and .table_get
+method in the version_parsers structure of lib/tst_cmd.c.
+
 1.36 Assert sys or proc file value
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Using TST_ASSERT_INT/STR(path, val) to assert that integer value or string stored in
diff --git a/include/tst_private.h b/include/tst_private.h
index fe0955f3b..b02f91228 100644
--- a/include/tst_private.h
+++ b/include/tst_private.h
@@ -37,4 +37,13 @@ int tst_get_prefix(const char *ip_str, int is_ipv6);
  */
 char tst_kconfig_get(const char *confname);
 
+/*
+ * If cmd argument is a single command, this function just checks command
+ * whether exists. If not, case skips.
+ * If cmd argument is a complex string ie 'mkfs.ext4 >= 1.43.0', this
+ * function checks command version whether meets this requirement.
+ * If not, case skips.
+ */
+void tst_check_cmd(const char *cmd);
+
 #endif
diff --git a/lib/newlib_tests/.gitignore b/lib/newlib_tests/.gitignore
index cf467b5a0..a19fa22e8 100644
--- a/lib/newlib_tests/.gitignore
+++ b/lib/newlib_tests/.gitignore
@@ -46,4 +46,12 @@ test_macros06
 tst_fuzzy_sync01
 tst_fuzzy_sync02
 tst_fuzzy_sync03
+test_needs_cmds01
+test_needs_cmds02
+test_needs_cmds03
+test_needs_cmds04
+test_needs_cmds05
+test_needs_cmds06
+test_needs_cmds07
+test_needs_cmds08
 test_zero_hugepage
diff --git a/lib/newlib_tests/runtest.sh b/lib/newlib_tests/runtest.sh
index 05c76228b..da01f36d9 100755
--- a/lib/newlib_tests/runtest.sh
+++ b/lib/newlib_tests/runtest.sh
@@ -2,8 +2,9 @@
 # Copyright (c) 2021 Petr Vorel <pvorel@suse.cz>
 
 LTP_C_API_TESTS="${LTP_C_API_TESTS:-test05 test07 test09 test12 test15 test18
-tst_bool_expr test_exec test_timer tst_res_hexd tst_strstatus tst_fuzzy_sync03
-test_zero_hugepage.sh}"
+test_needs_cmds01 test_needs_cmds02 test_needs_cmds03 test_needs_cmds06
+test_needs_cmds07 tst_bool_expr test_exec test_timer tst_res_hexd tst_strstatus
+tst_fuzzy_sync03 test_zero_hugepage.sh}"
 
 LTP_SHELL_API_TESTS="${LTP_SHELL_API_TESTS:-shell/tst_check_driver.sh shell/net/*.sh}"
 
diff --git a/lib/newlib_tests/test_needs_cmds01.c b/lib/newlib_tests/test_needs_cmds01.c
new file mode 100644
index 000000000..92305ee97
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds01.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TPASS, "Testing tst_check_cmd() functionality OK.");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext4",
+		"mkfs.ext4 >= 1.0.0",
+		"mkfs.ext4 <= 2.0.0",
+		"mkfs.ext4 != 2.0.0",
+		"mkfs.ext4 > 1.0.0",
+		"mkfs.ext4 < 2.0.0",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds02.c b/lib/newlib_tests/test_needs_cmds02.c
new file mode 100644
index 000000000..1eeaf6351
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds02.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test Illegal format by using non-existing cmd.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Nonexisting command is present!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext45 >= 1.43.0",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds03.c b/lib/newlib_tests/test_needs_cmds03.c
new file mode 100644
index 000000000..c50077f4e
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds03.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test Illegal format by using Illegal operation.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Wrong operator was evaluated!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext4 ! 1.43.0",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds04.c b/lib/newlib_tests/test_needs_cmds04.c
new file mode 100644
index 000000000..5d05ed46d
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds04.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test Illegal format by using incomplete version.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Incomplete version was parsed!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext4 > 1.43",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds05.c b/lib/newlib_tests/test_needs_cmds05.c
new file mode 100644
index 000000000..f4b509b68
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds05.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test Illegal format by using version that has garbage.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Garbage version was parsed!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext4 > 1.43.0-1",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds06.c b/lib/newlib_tests/test_needs_cmds06.c
new file mode 100644
index 000000000..f1234820e
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds06.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test Illegal format with garbage.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Garbage format was parsed!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext4 > 1.43.0 2",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds07.c b/lib/newlib_tests/test_needs_cmds07.c
new file mode 100644
index 000000000..e2d2643f4
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds07.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test non-existed cmd whether still can be detected.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Nonexisting command is present!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.ext45",
+		NULL
+	}
+};
diff --git a/lib/newlib_tests/test_needs_cmds08.c b/lib/newlib_tests/test_needs_cmds08.c
new file mode 100644
index 000000000..acc28d926
--- /dev/null
+++ b/lib/newlib_tests/test_needs_cmds08.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Yang Xu <xuyang2018.jy@fujitsu.com>
+ */
+
+/*
+ * Test mkfs.xfs that it doesn't have own parser and table_get function
+ * at the version_parsers structure in lib/tst_cmd.c.
+ * So it should report parser function for this cmd is not implemented.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	tst_res(TFAIL, "Nonexisting parser function for mkfs.xfs is present!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.needs_cmds = (const char *[]) {
+		"mkfs.xfs",
+		"mkfs.xfs >= 4.20.0",
+		NULL
+	}
+};
diff --git a/lib/tst_cmd.c b/lib/tst_cmd.c
index 7446249f9..229848c28 100644
--- a/lib/tst_cmd.c
+++ b/lib/tst_cmd.c
@@ -28,10 +28,21 @@
 #include <signal.h>
 #include "test.h"
 #include "tst_cmd.h"
+#include "tst_private.h"
 
 #define OPEN_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
 #define OPEN_FLAGS	(O_WRONLY | O_APPEND | O_CREAT)
 
+enum cmd_op {
+	OP_GE, /* >= */
+	OP_GT, /* >  */
+	OP_LE, /* <= */
+	OP_LT, /* <  */
+	OP_EQ, /* == */
+	OP_NE, /* != */
+};
+
+
 int tst_cmd_fds_(void (cleanup_fn)(void),
 		const char *const argv[],
 		int stdout_fd,
@@ -176,3 +187,160 @@ int tst_system(const char *command)
 	signal(SIGCHLD, old_handler);
 	return ret;
 }
+
+static int mkfs_ext4_version_parser(void)
+{
+	FILE *f;
+	int rc, major, minor, patch;
+
+	f = popen("mkfs.ext4 -V 2>&1", "r");
+	if (!f) {
+		tst_resm(TWARN, "Could not run mkfs.ext4 -V 2>&1 cmd");
+		return -1;
+	}
+
+	rc = fscanf(f, "mke2fs %d.%d.%d", &major, &minor, &patch);
+	pclose(f);
+	if (rc != 3) {
+		tst_resm(TWARN, "Unable to parse mkfs.ext4 version");
+		return -1;
+	}
+
+	return major * 10000 +  minor * 100 + patch;
+}
+
+static int mkfs_ext4_version_table_get(char *version)
+{
+	int major, minor, patch;
+	int len;
+
+	if (sscanf(version, "%u.%u.%u %n", &major, &minor, &patch, &len) != 3) {
+		tst_resm(TWARN, "Illegal version(%s), should use format like 1.43.0", version);
+		return -1;
+	}
+
+	if (len != (int)strlen(version)) {
+		tst_resm(TWARN, "Grabage after version");
+		return -1;
+	}
+
+	return major * 10000 + minor * 100 + patch;
+}
+
+static struct version_parser {
+	const char *cmd;
+	int (*parser)(void);
+	int (*table_get)(char *version);
+} version_parsers[] = {
+	{"mkfs.ext4", mkfs_ext4_version_parser, mkfs_ext4_version_table_get},
+	{},
+};
+
+void tst_check_cmd(const char *cmd)
+{
+	struct version_parser *p;
+	char *cmd_token, *op_token, *version_token, *next, *str;
+	char path[PATH_MAX];
+	char parser_cmd[100];
+	int ver_parser, ver_get;
+	int op_flag = 0;
+
+	strcpy(parser_cmd, cmd);
+
+	cmd_token = strtok_r(parser_cmd, " ", &next);
+	op_token = strtok_r(NULL, " ", &next);
+	version_token = strtok_r(NULL, " ", &next);
+	str = strtok_r(NULL, " ", &next);
+
+	if (tst_get_path(cmd_token, path, sizeof(path)))
+		tst_brkm(TCONF, NULL, "Couldn't find '%s' in $PATH", cmd_token);
+
+	if (!op_token)
+		return;
+
+	if (!strcmp(op_token, ">="))
+		op_flag = OP_GE;
+	else if (!strcmp(op_token, ">"))
+		op_flag = OP_GT;
+	else if (!strcmp(op_token, "<="))
+		op_flag = OP_LE;
+	else if (!strcmp(op_token, "<"))
+		op_flag = OP_LT;
+	else if (!strcmp(op_token, "=="))
+		op_flag = OP_EQ;
+	else if (!strcmp(op_token, "!="))
+		op_flag = OP_NE;
+	else
+		tst_brkm(TCONF, NULL, "Invalid op(%s)", op_token);
+
+	if (!version_token || str) {
+		tst_brkm(TCONF, NULL,
+			"Illegal format(%s), should use format like mkfs.ext4 >= 1.43.0",
+			cmd);
+	}
+
+	for (p = &version_parsers[0]; p->cmd; p++) {
+		if (!strcmp(p->cmd, cmd_token)) {
+			tst_resm(TINFO, "Parsing %s version", p->cmd);
+			break;
+		}
+	}
+
+	if (!p->cmd) {
+		tst_brkm(TBROK, NULL, "No version parser for %s implemented!",
+			cmd_token);
+	}
+
+	ver_parser = p->parser();
+	if (ver_parser < 0)
+		tst_brkm(TBROK, NULL, "Failed to parse %s version", p->cmd);
+
+	ver_get = p->table_get(version_token);
+	if (ver_get < 0)
+		tst_brkm(TBROK, NULL, "Failed to get %s version", p->cmd);
+
+	switch (op_flag) {
+	case OP_GE:
+		if (ver_parser < ver_get) {
+			tst_brkm(TCONF, NULL, "%s required >= %d, but got %d, "
+				"the version is required in order run the test.",
+				cmd, ver_get, ver_parser);
+		}
+		break;
+	case OP_GT:
+		if (ver_parser <= ver_get) {
+			tst_brkm(TCONF, NULL, "%s required > %d, but got %d, "
+				"the version is required in order run the test.",
+				cmd, ver_get, ver_parser);
+		}
+		break;
+	case OP_LE:
+		if (ver_parser > ver_get) {
+			tst_brkm(TCONF, NULL, "%s required <= %d, but got %d, "
+				"the version is required in order run the test.",
+				cmd, ver_get, ver_parser);
+		}
+		break;
+	case OP_LT:
+		if (ver_parser >= ver_get) {
+			tst_brkm(TCONF, NULL, "%s required < %d, but got %d, "
+				"the version is required in order run the test.",
+				cmd, ver_get, ver_parser);
+		}
+		break;
+	case OP_EQ:
+		if (ver_parser != ver_get) {
+			tst_brkm(TCONF, NULL, "%s required == %d, but got %d, "
+				"the version is required in order run the test.",
+				cmd, ver_get, ver_parser);
+		}
+		break;
+	case OP_NE:
+		if (ver_parser == ver_get) {
+			tst_brkm(TCONF, NULL, "%s required != %d, but got %d, "
+				"the version is required in order run the test.",
+				cmd, ver_get, ver_parser);
+		}
+		break;
+	}
+}
diff --git a/lib/tst_test.c b/lib/tst_test.c
index a79275722..c49e30f5b 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -29,7 +29,7 @@
 #include "tst_wallclock.h"
 #include "tst_sys_conf.h"
 #include "tst_kconfig.h"
-
+#include "tst_private.h"
 #include "old_resource.h"
 #include "old_device.h"
 #include "old_tmpdir.h"
@@ -987,12 +987,10 @@ static void do_setup(int argc, char *argv[])
 
 	if (tst_test->needs_cmds) {
 		const char *cmd;
-		char path[PATH_MAX];
 		int i;
 
 		for (i = 0; (cmd = tst_test->needs_cmds[i]); ++i)
-			if (tst_get_path(cmd, path, sizeof(path)))
-				tst_brk(TCONF, "Couldn't find '%s' in $PATH", cmd);
+			tst_check_cmd(cmd);
 	}
 
 	if (tst_test->needs_drivers) {
-- 
2.23.0



More information about the ltp mailing list