testsuite: Check the list of loaded modules after a test
Add a ->modules_loaded member to struct test, which is a comma-separated list of modules that should be present after the test finishes. Both missing and excess modules cause an error.
This commit is contained in:
parent
a4bd1441e5
commit
88ac40840f
|
@ -74,6 +74,7 @@ static DEFINE_TEST(test_insert,
|
||||||
[TC_ROOTFS] = TESTSUITE_ROOTFS "test-init/",
|
[TC_ROOTFS] = TESTSUITE_ROOTFS "test-init/",
|
||||||
[TC_INIT_MODULE_RETCODES] = "bla:1:20",
|
[TC_INIT_MODULE_RETCODES] = "bla:1:20",
|
||||||
},
|
},
|
||||||
|
.modules_loaded = "ext4",
|
||||||
.need_spawn = true);
|
.need_spawn = true);
|
||||||
|
|
||||||
static noreturn int test_remove(const struct test *t)
|
static noreturn int test_remove(const struct test *t)
|
||||||
|
|
|
@ -91,7 +91,9 @@ static DEFINE_TEST(modprobe_show_alias_to_none,
|
||||||
},
|
},
|
||||||
.output = {
|
.output = {
|
||||||
.out = TESTSUITE_ROOTFS "test-modprobe/show-depends/correct-psmouse.txt",
|
.out = TESTSUITE_ROOTFS "test-modprobe/show-depends/correct-psmouse.txt",
|
||||||
});
|
},
|
||||||
|
.modules_loaded = "",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
static noreturn int modprobe_builtin(const struct test *t)
|
static noreturn int modprobe_builtin(const struct test *t)
|
||||||
|
@ -131,7 +133,9 @@ static DEFINE_TEST(modprobe_softdep_loop,
|
||||||
[TC_UNAME_R] = "4.4.4",
|
[TC_UNAME_R] = "4.4.4",
|
||||||
[TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/softdep-loop",
|
[TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/softdep-loop",
|
||||||
[TC_INIT_MODULE_RETCODES] = "",
|
[TC_INIT_MODULE_RETCODES] = "",
|
||||||
});
|
},
|
||||||
|
.modules_loaded = "btusb,bluetooth",
|
||||||
|
);
|
||||||
|
|
||||||
static noreturn int modprobe_install_cmd_loop(const struct test *t)
|
static noreturn int modprobe_install_cmd_loop(const struct test *t)
|
||||||
{
|
{
|
||||||
|
@ -156,6 +160,7 @@ static DEFINE_TEST(modprobe_install_cmd_loop,
|
||||||
{ "MODPROBE", ABS_TOP_BUILDDIR "/tools/modprobe" },
|
{ "MODPROBE", ABS_TOP_BUILDDIR "/tools/modprobe" },
|
||||||
{ }
|
{ }
|
||||||
},
|
},
|
||||||
|
.modules_loaded = "snd,snd-pcm",
|
||||||
);
|
);
|
||||||
|
|
||||||
static noreturn int modprobe_param_kcmdline(const struct test *t)
|
static noreturn int modprobe_param_kcmdline(const struct test *t)
|
||||||
|
@ -178,7 +183,9 @@ static DEFINE_TEST(modprobe_param_kcmdline,
|
||||||
},
|
},
|
||||||
.output = {
|
.output = {
|
||||||
.out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline/correct.txt",
|
.out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline/correct.txt",
|
||||||
});
|
},
|
||||||
|
.modules_loaded = "",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
static const struct test *tests[] = {
|
static const struct test *tests[] = {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -528,12 +529,189 @@ fail:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmp_modnames(const void *m1, const void *m2)
|
||||||
|
{
|
||||||
|
const char *s1 = *(char *const *)m1;
|
||||||
|
const char *s2 = *(char *const *)m2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; s1[i] || s2[i]; i++) {
|
||||||
|
char c1 = s1[i], c2 = s2[i];
|
||||||
|
if (c1 == '-')
|
||||||
|
c1 = '_';
|
||||||
|
if (c2 == '-')
|
||||||
|
c2 = '_';
|
||||||
|
if (c1 != c2)
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the expected module names in buf and return a list of pointers to
|
||||||
|
* them.
|
||||||
|
*/
|
||||||
|
static const char **read_expected_modules(const struct test *t,
|
||||||
|
char **buf, int *count)
|
||||||
|
{
|
||||||
|
const char **res;
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (t->modules_loaded[0] == '\0') {
|
||||||
|
*count = 0;
|
||||||
|
*buf = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*buf = strdup(t->modules_loaded);
|
||||||
|
if (!*buf) {
|
||||||
|
*count = -1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
len = 1;
|
||||||
|
for (p = *buf; *p; p++)
|
||||||
|
if (*p == ',')
|
||||||
|
len++;
|
||||||
|
res = malloc(sizeof(char *) * len);
|
||||||
|
if (!res) {
|
||||||
|
perror("malloc");
|
||||||
|
*count = -1;
|
||||||
|
free(*buf);
|
||||||
|
*buf = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
res[i++] = *buf;
|
||||||
|
for (p = *buf; i < len; p++)
|
||||||
|
if (*p == ',') {
|
||||||
|
*p = '\0';
|
||||||
|
res[i++] = p + 1;
|
||||||
|
}
|
||||||
|
*count = len;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **read_loaded_modules(const struct test *t, char **buf, int *count)
|
||||||
|
{
|
||||||
|
char dirname[PATH_MAX];
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *dirent;
|
||||||
|
int i;
|
||||||
|
int len = 0, bufsz;
|
||||||
|
char **res = NULL;
|
||||||
|
char *p;
|
||||||
|
const char *rootfs = t->config[TC_ROOTFS] ? t->config[TC_ROOTFS] : "";
|
||||||
|
|
||||||
|
/* Store the entries in /sys/module to res */
|
||||||
|
if (snprintf(dirname, sizeof(dirname), "%s/sys/module", rootfs)
|
||||||
|
>= (int)sizeof(dirname)) {
|
||||||
|
ERR("rootfs path too long: %s\n", rootfs);
|
||||||
|
*buf = NULL;
|
||||||
|
len = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
dir = opendir(dirname);
|
||||||
|
/* not an error, simply return empty list */
|
||||||
|
if (!dir) {
|
||||||
|
*buf = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
bufsz = 0;
|
||||||
|
while ((dirent = readdir(dir))) {
|
||||||
|
if (dirent->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
len++;
|
||||||
|
bufsz += strlen(dirent->d_name) + 1;
|
||||||
|
}
|
||||||
|
res = malloc(sizeof(char *) * len);
|
||||||
|
if (!res) {
|
||||||
|
perror("malloc");
|
||||||
|
len = -1;
|
||||||
|
goto out_dir;
|
||||||
|
}
|
||||||
|
*buf = malloc(bufsz);
|
||||||
|
if (!*buf) {
|
||||||
|
perror("malloc");
|
||||||
|
free(res);
|
||||||
|
res = NULL;
|
||||||
|
len = -1;
|
||||||
|
goto out_dir;
|
||||||
|
}
|
||||||
|
rewinddir(dir);
|
||||||
|
i = 0;
|
||||||
|
p = *buf;
|
||||||
|
while ((dirent = readdir(dir))) {
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (dirent->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
size = strlen(dirent->d_name) + 1;
|
||||||
|
memcpy(p, dirent->d_name, size);
|
||||||
|
res[i++] = p;
|
||||||
|
p += size;
|
||||||
|
}
|
||||||
|
out_dir:
|
||||||
|
closedir(dir);
|
||||||
|
out:
|
||||||
|
*count = len;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_loaded_modules(const struct test *t)
|
||||||
|
{
|
||||||
|
int l1, l2, i1, i2;
|
||||||
|
const char **a1;
|
||||||
|
char **a2;
|
||||||
|
char *buf1, *buf2;
|
||||||
|
int err = false;
|
||||||
|
|
||||||
|
a1 = read_expected_modules(t, &buf1, &l1);
|
||||||
|
if (l1 < 0)
|
||||||
|
return err;
|
||||||
|
a2 = read_loaded_modules(t, &buf2, &l2);
|
||||||
|
if (l2 < 0)
|
||||||
|
goto out_a1;
|
||||||
|
qsort(a1, l1, sizeof(char *), cmp_modnames);
|
||||||
|
qsort(a2, l2, sizeof(char *), cmp_modnames);
|
||||||
|
i1 = i2 = 0;
|
||||||
|
err = true;
|
||||||
|
while (i1 < l1 || i2 < l2) {
|
||||||
|
int cmp;
|
||||||
|
|
||||||
|
if (i1 >= l1)
|
||||||
|
cmp = 1;
|
||||||
|
else if (i2 >= l2)
|
||||||
|
cmp = -1;
|
||||||
|
else
|
||||||
|
cmp = cmp_modnames(&a1[i1], &a2[i2]);
|
||||||
|
if (cmp == 0) {
|
||||||
|
i1++;
|
||||||
|
i2++;
|
||||||
|
} else if (cmp < 0) {
|
||||||
|
err = false;
|
||||||
|
ERR("module %s not loaded\n", a1[i1]);
|
||||||
|
i1++;
|
||||||
|
} else {
|
||||||
|
err = false;
|
||||||
|
ERR("module %s is loaded but should not be \n", a2[i2]);
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(a2);
|
||||||
|
free(buf2);
|
||||||
|
out_a1:
|
||||||
|
free(a1);
|
||||||
|
free(buf1);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int test_run_parent(const struct test *t, int fdout[2],
|
static inline int test_run_parent(const struct test *t, int fdout[2],
|
||||||
int fderr[2], int fdmonitor[2], pid_t child)
|
int fderr[2], int fdmonitor[2], pid_t child)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int err;
|
int err;
|
||||||
bool matchout;
|
bool matchout, match_modules;
|
||||||
|
|
||||||
/* Close write-fds */
|
/* Close write-fds */
|
||||||
if (t->output.out != NULL)
|
if (t->output.out != NULL)
|
||||||
|
@ -578,16 +756,21 @@ static inline int test_run_parent(const struct test *t, int fdout[2],
|
||||||
|
|
||||||
if (matchout)
|
if (matchout)
|
||||||
matchout = check_generated_files(t);
|
matchout = check_generated_files(t);
|
||||||
|
if (t->modules_loaded)
|
||||||
|
match_modules = check_loaded_modules(t);
|
||||||
|
else
|
||||||
|
match_modules = true;
|
||||||
|
|
||||||
if (t->expected_fail == false) {
|
if (t->expected_fail == false) {
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
if (matchout)
|
if (matchout && match_modules)
|
||||||
LOG("%sPASSED%s: %s\n",
|
LOG("%sPASSED%s: %s\n",
|
||||||
ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
|
ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
|
||||||
t->name);
|
t->name);
|
||||||
else {
|
else {
|
||||||
ERR("%sFAILED%s: exit ok but outputs do not match: %s\n",
|
ERR("%sFAILED%s: exit ok but %s do not match: %s\n",
|
||||||
ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
|
ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
|
||||||
|
matchout ? "loaded modules" : "outputs",
|
||||||
t->name);
|
t->name);
|
||||||
err = EXIT_FAILURE;
|
err = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,8 @@ struct test {
|
||||||
*/
|
*/
|
||||||
const struct keyval *files;
|
const struct keyval *files;
|
||||||
} output;
|
} output;
|
||||||
|
/* comma-separated list of loaded modules at the end of the test */
|
||||||
|
const char *modules_loaded;
|
||||||
testfunc func;
|
testfunc func;
|
||||||
const char *config[_TC_LAST];
|
const char *config[_TC_LAST];
|
||||||
const char *path;
|
const char *path;
|
||||||
|
|
Loading…
Reference in New Issue