Skip to content

Commit

Permalink
lib: Add new tst_kvcmp for shell
Browse files Browse the repository at this point in the history
Add tst_kvcmp inspired by the shell test syntax.

Now we can write the kernel version checks as:

if tst_kvcmp -ge 3.8.0 -a -le 4.3.2; then
	tst_brk TCONF "Needs kernel older than 3.8.0 or newer than 4.3.2"
fi

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
  • Loading branch information
metan-ucw committed Nov 1, 2016
1 parent 573adf8 commit f18331d
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 27 deletions.
38 changes: 29 additions & 9 deletions doc/test-writing-guidelines.txt
Expand Up @@ -1682,21 +1682,41 @@ and 'tst_resm TFAIL' otherwise. 'EXPECT_FAIL' does vice versa.
Output redirection rules are the same as for the 'ROD' function. In addition
to that, 'EXPECT_FAIL' always redirects the command's stderr to '/dev/null'.

tst_kvercmp
+++++++++++
tst_kvcmp
+++++++++

This command takes the kernel version as a parameter and compares the it with
the currently running kernel. It works mostly as the C function but the result
is mapped into the exit value as follows:
This command compares the currently running kernel version given conditions
with syntax similar to the shell test command.

[source,sh]
-------------------------------------------------------------------------------
# Exit the test if kernel version is older or equal to 2.6.8
if tst_kvcmp -le 2.6.8; then
tst_brk TCONF "Kernel newer than 2.6.8 is needed"
fi

# Exit the test if kernel is newer than 3.8 and older than 4.0.1
if tst_kvcmp -gt 3.8 -a -lt 4.0.1
tst_brk TCONF "Kernel must be older than 3.8 or newer than 4.0.1"
fi
-------------------------------------------------------------------------------

[options="header"]
|=======================================================================
| C function return | Shell command exit value | Explanation
| < 0 | 0 | Kernel is older
| 0 | 1 | Kernel version is equal
| > 0 | 2 | Kernel is newer
| expression | description
| -eq kver | Returns true if kernel version is equal
| -ne kver | Returns true if kernel version is not equal
| -gt kver | Returns true if kernel version is greater
| -ge kver | Returns true if kernel version is greater or equal
| -lt kver | Returns true if kernel version is lesser
| -le kver | Returns true if kernel version is lesser or equal
| -a | Does logical and between two expressions
| -o | Does logical or between two expressions
|=======================================================================

The format for kernel version has to either be with one dot e.g. '2.6' or with
two dots e.g. '4.8.1'.

.tst_fs_has_free
[source,sh]
-------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion include/tst_kvercmp.h
Expand Up @@ -39,7 +39,7 @@
*
* If version has only two numbers, i.e. 2.4 the third integer is set to zero.
*/
void tst_parse_kver(const char *str_kver, int *v1, int *v2, int *v3);
int tst_parse_kver(const char *str_kver, int *v1, int *v2, int *v3);

/*
* Compare given kernel version with currently running kernel.
Expand Down
41 changes: 26 additions & 15 deletions lib/tst_kvercmp.c
Expand Up @@ -39,13 +39,10 @@ static char *parse_digit(const char *str, int *d)

*d = v;

if (*end != '.')
return NULL;

return end + 1;
return end;
}

void tst_parse_kver(const char *str_kver, int *v1, int *v2, int *v3)
int tst_parse_kver(const char *str_kver, int *v1, int *v2, int *v3)
{
const char *str = str_kver;

Expand All @@ -54,21 +51,31 @@ void tst_parse_kver(const char *str_kver, int *v1, int *v2, int *v3)
*v3 = 0;

if (!(str = parse_digit(str, v1)))
goto err;
return 1;

if (*(str++) != '.')
return 1;

if (!(str = parse_digit(str, v2)))
goto err;
return 1;

/*
* Check for a short version e.g '2.4'
*/
if (*str == ' ' || *str == '\0')
return 0;

if (*(str++) != '.')
return 1;

/*
* We ignore all errors here in order not to fail with versions as
* "2.4" or "2.6.18".
* Ignore rest of the string in order not to break on versions as
* 4.8.1-52-default.
*/
parse_digit(str, v3);
if (!parse_digit(str, v3))
return 1;

return;
err:
tst_resm(TWARN,
"Invalid kernel version %s, expected %%d.%%d.%%d", str_kver);
return 0;
}

int tst_kvercmp(int r1, int r2, int r3)
Expand All @@ -78,7 +85,11 @@ int tst_kvercmp(int r1, int r2, int r3)
struct utsname uval;

uname(&uval);
tst_parse_kver(uval.release, &a1, &a2, &a3);
if (tst_parse_kver(uval.release, &a1, &a2, &a3)) {
tst_resm(TWARN,
"Invalid kernel version %s, expected %%d.%%d.%%d",
uval.release);
}

testver = (r1 << 16) + (r2 << 8) + r3;
currver = (a1 << 16) + (a2 << 8) + a3;
Expand Down
6 changes: 5 additions & 1 deletion lib/tst_test.c
Expand Up @@ -519,7 +519,11 @@ void check_kver(void)
{
int v1, v2, v3;

tst_parse_kver(tst_test->min_kver, &v1, &v2, &v3);
if (tst_parse_kver(tst_test->min_kver, &v1, &v2, &v3)) {
tst_res(TWARN,
"Invalid kernel version %s, expected %%d.%%d.%%d",
tst_test->min_kver);
}

if (tst_kvercmp(v1, v2, v3) < 0) {
tst_brk(TCONF, "The test requires kernel %s or newer",
Expand Down
1 change: 1 addition & 0 deletions testcases/lib/.gitignore
Expand Up @@ -2,3 +2,4 @@ tst_sleep
tst_random
tst_checkpoint
tst_rod
tst_kvcmp
2 changes: 1 addition & 1 deletion testcases/lib/Makefile
Expand Up @@ -26,6 +26,6 @@ include $(top_srcdir)/include/mk/testcases.mk

INSTALL_TARGETS := *.sh

MAKE_TARGETS := tst_sleep tst_random tst_checkpoint tst_rod
MAKE_TARGETS := tst_sleep tst_random tst_checkpoint tst_rod tst_kvcmp

include $(top_srcdir)/include/mk/generic_leaf_target.mk
191 changes: 191 additions & 0 deletions testcases/lib/tst_kvcmp.c
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2016 Cyril Hrubis <chrubis@suse.cz>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#define TST_NO_DEFAULT_MAIN
#include <stdio.h>
#include <stdlib.h>
#include <tst_test.h>

enum op {
EQ,
NE,
GE,
GT,
LE,
LT,
AND,
OR,
ERR,
};

static enum op strtop(const char *op)
{
if (!strcmp(op, "-eq"))
return EQ;

if (!strcmp(op, "-ne"))
return NE;

if (!strcmp(op, "-ge"))
return GE;

if (!strcmp(op, "-gt"))
return GT;

if (!strcmp(op, "-le"))
return LE;

if (!strcmp(op, "-lt"))
return LT;

if (!strcmp(op, "-a"))
return AND;

if (!strcmp(op, "-o"))
return OR;

return ERR;
}

static void help(const char *fname)
{
printf("usage: %s -eq|-ne|-gt|-ge|-lt|-le kver [-a|-o] ...\n\n", fname);
printf("-eq kver\tReturns true if kernel version is equal\n");
printf("-ne kver\tReturns true if kernel version is not equal\n");
printf("-gt kver\tReturns true if kernel version is greater\n");
printf("-ge kver\tReturns true if kernel version is greater or equal\n");
printf("-lt kver\tReturns true if kernel version is lesser\n");
printf("-le kver\tReturns true if kernel version is lesser or equal\n");
printf("-a \t\tDoes logical and between two expressions\n");
printf("-o \t\tDoes logical or between two expressions\n\n");
printf("Kernel version format has either one or two dots: ");
printf("'2.6' or '4.8.1'\n");
}

int main(int argc, char *argv[])
{
int i = 1;
int ret = -1;
int v1, v2, v3;
enum op prev_op = ERR;

if (argc <= 1 || !strcmp(argv[1], "-h")) {
help(argv[0]);
return 0;
}

while (i < argc) {
const char *strop = argv[i++];
const char *strkver;
int res;

enum op op = strtop(strop);

switch (op) {
case EQ:
case NE:
case GE:
case GT:
case LE:
case LT:
if (ret != -1 && prev_op == ERR) {
fprintf(stderr, "Expected -a or -o\n");
return 2;
}

if (i >= argc) {
fprintf(stderr,
"Expected kernel version after '%s'\n",
strop);
return 2;
}

strkver = argv[i++];

if (tst_parse_kver(strkver, &v1, &v2, &v3)) {
fprintf(stderr,
"Invalid kernel version '%s'\n",
strkver);
return 2;
}
break;
case AND:
case OR:
if (ret == -1) {
fprintf(stderr,
"The %s must follow expression\n",
strop);
return 2;
}
prev_op = op;
continue;
break;
case ERR:
fprintf(stderr, "Invalid operation %s\n", argv[i]);
return 2;
}

res = tst_kvercmp(v1, v2, v3);

switch (op) {
case EQ:
res = (res == 0);
break;
case NE:
res = (res != 0);
break;
case GE:
res = (res >= 0);
break;
case GT:
res = (res > 0);
break;
case LE:
res = (res <= 0);
break;
case LT:
res = (res < 0);
break;
default:
break;
}

switch (prev_op) {
case ERR:
ret = res;
break;
case AND:
ret = ret && res;
prev_op = ERR;
break;
case OR:
ret = ret || res;
prev_op = ERR;
break;
default:
break;
}
}

if (prev_op != ERR) {
fprintf(stderr, "Useless -a or -o at the end\n");
return 2;
}

return !ret;
}

0 comments on commit f18331d

Please sign in to comment.