Re: [PATCH testsuite v3 1/2] tests/file_contexts: refactor existing tests
Stephen Smalley <[email protected]> Mon, 27 Jul 2026 09:31:23 -0400
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAEjxPJ6bZRx02r_vMqENRdvw0uoBOz+5D+qw3zfeB6+1V+2Rhw@mail.gmail.com> |
On Sun, Jul 26, 2026 at 10:52 PM Thiébaud Weksteen <[email protected]> wrote: > > Change the logging format when reporting an error to better capture the > location and context of the error. > > Split tests into functions to better isolate the tested behaviour. > > Signed-off-by: Thiébaud Weksteen <[email protected]> Acked-by: Stephen Smalley <[email protected]> > --- > Changes since v2: > - Call test functions in test_validate.c > > tests/file_contexts/internal.c | 16 ++--- > tests/file_contexts/internal.h | 20 +++++- > tests/file_contexts/test_lookup.c | 60 ++++++++++++------ > tests/file_contexts/test_open.c | 94 +++++++++++++++++----------- > tests/file_contexts/test_open_base.c | 46 ++++++++++---- > tests/file_contexts/test_validate.c | 40 +++++++----- > 6 files changed, 181 insertions(+), 95 deletions(-) > > diff --git a/tests/file_contexts/internal.c b/tests/file_contexts/internal.c > index 22b5790..ba45dd6 100644 > --- a/tests/file_contexts/internal.c > +++ b/tests/file_contexts/internal.c > @@ -10,8 +10,8 @@ > > #include "internal.h" > > -void assertContextsMatch(struct selabel_handle *hnd, struct test_t *tests, > - size_t n) > +void assertContextsMatch(struct selabel_handle *hnd, const char *log_prefix, > + struct test_t *tests, size_t n) > { > for (int i = 0; i < n; i++) { > char *context = NULL; > @@ -19,20 +19,22 @@ void assertContextsMatch(struct selabel_handle *hnd, struct test_t *tests, > > if (selabel_lookup(hnd, &context, test.path, S_IFREG)) { > if (test.context) { > - perror("file_contexts:selabel_lookup"); > - fprintf(stderr, "Lookup for %s failed\n", test.path); > + log_errno("Lookup for %s from %s failed", > + test.path, log_prefix); > exit(2); > } > // Expected failure. Continue to the next test. > continue; > } else if (!test.context) { > - fprintf(stderr, "Lookup for %s was supposed to failed\n", test.path); > + log_err("Lookup for %s from %s was supposed to failed but got %s", > + test.path, log_prefix, context); > exit(2); > } > > if (strcmp(context, tests[i].context)) { > - fprintf(stderr, "Lookup for %s returned %s, expected %s\n", > - tests[i].path, context, tests[i].context); > + log_err("Lookup for %s from %s returned %s, expected %s", > + tests[i].path, log_prefix, context, > + tests[i].context); > exit(2); > } > > diff --git a/tests/file_contexts/internal.h b/tests/file_contexts/internal.h > index a07548f..c51bbc6 100644 > --- a/tests/file_contexts/internal.h > +++ b/tests/file_contexts/internal.h > @@ -1,8 +1,21 @@ > +#include <errno.h> > +#include <string.h> > + > #include <selinux/label.h> > #include <selinux/selinux.h> > > #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) > > +/* Log an error message with the file name, function name and line */ > +#define log_err(fmt, ...) \ > + fprintf(stderr, "%s:%s:%d: " fmt "\n", __FILE__, __func__, __LINE__, \ > + ##__VA_ARGS__) > + > +/* Log an error message as well as errno */ > +#define log_errno(fmt, ...) \ > + fprintf(stderr, "%s:%s:%d " fmt " (%s)\n", __FILE__, __func__, \ > + __LINE__, ##__VA_ARGS__, strerror(errno)) > + > /* Structure to capture a test. The |path| will be resolved and expected to > * match the |context|. If |context| is NULL, the lookup is expected to fail. */ > struct test_t { > @@ -11,6 +24,7 @@ struct test_t { > }; > > /* Assert that all paths described in |tests| resolve appropriately. |hnd| must > - * be opened. |n| is the number of tests in |tests|. */ > -void assertContextsMatch(struct selabel_handle *hnd, struct test_t *tests, > - size_t n); > + * be opened. |n| is the number of tests in |tests|. |log_prefix| is added to > + * any log message */ > +void assertContextsMatch(struct selabel_handle *hnd, const char *log_prefix, > + struct test_t *tests, size_t n); > diff --git a/tests/file_contexts/test_lookup.c b/tests/file_contexts/test_lookup.c > index 9a54d1d..5957db4 100644 > --- a/tests/file_contexts/test_lookup.c > +++ b/tests/file_contexts/test_lookup.c > @@ -9,38 +9,58 @@ > > #include "internal.h" > > -int main(int argc, char **argv) > +void test_lookup(const char *basedir) > { > - struct selabel_handle *hnd; > - > - if (argc != 2) { > - fprintf(stderr, "basedir not provided\n"); > - exit(1); > - } > - > char *path; > - asprintf(&path, "%s/f2.fc", argv[1]); > - struct selinux_opt f2_opts[] = { > - { .type = SELABEL_OPT_PATH, .value = path } > + asprintf(&path, "%s/f2.fc", basedir); > + struct selinux_opt f2_opts[] = { { > + .type = SELABEL_OPT_PATH, > + .value = path > + } > }; > > - hnd = selabel_open(SELABEL_CTX_FILE, f2_opts, ARRAY_SIZE(f2_opts)); > - > + struct selabel_handle *hnd = > + selabel_open(SELABEL_CTX_FILE, f2_opts, ARRAY_SIZE(f2_opts)); > free(path); > > if (!hnd) { > - perror("file_context:f2_options"); > + log_errno("Unable to open file backend"); > exit(2); > } > > struct test_t tests[] = { > - { .path = "/base", .context = "system_u:object_r:test_base_t:s0" }, > - { .path = "/base/unkown", .context = "system_u:object_r:test_base_wildcard_t:s0" }, > - { .path = "/base/sub", .context = "system_u:object_r:test_base_sub_t:s0" }, > - { .path = "/base/file.list", .context = "system_u:object_r:test_file_list_t:s0" }, > - { .path = "/base/file_list", .context = "system_u:object_r:test_base_wildcard_t:s0" }, > + { > + .path = "/base", > + .context = "system_u:object_r:test_base_t:s0" > + }, > + { > + .path = "/base/unkown", > + .context = "system_u:object_r:test_base_wildcard_t:s0" > + }, > + { > + .path = "/base/sub", > + .context = "system_u:object_r:test_base_sub_t:s0" > + }, > + { > + .path = "/base/file.list", > + .context = "system_u:object_r:test_file_list_t:s0" > + }, > + { > + .path = "/base/file_list", > + .context = "system_u:object_r:test_base_wildcard_t:s0" > + }, > }; > - assertContextsMatch(hnd, tests, ARRAY_SIZE(tests)); > + assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests)); > +} > + > +int main(int argc, char **argv) > +{ > + if (argc != 2) { > + log_err("basedir not provided"); > + exit(1); > + } > + > + test_lookup(argv[1]); > > return 0; > } > diff --git a/tests/file_contexts/test_open.c b/tests/file_contexts/test_open.c > index eae5691..4dd3300 100644 > --- a/tests/file_contexts/test_open.c > +++ b/tests/file_contexts/test_open.c > @@ -1,6 +1,5 @@ > #include <stdio.h> > #include <stdlib.h> > -#include <string.h> > #include <sys/stat.h> > #include <sys/types.h> > #include <unistd.h> > @@ -8,75 +7,96 @@ > #include <selinux/label.h> > #include <selinux/selinux.h> > > -int main(int argc, char **argv) > +#include "internal.h" > + > +void test_no_options(void) > { > - bool has_default_path = false; > - struct selabel_handle *hnd; > struct stat default_stat; > + if (stat(selinux_file_context_path(), &default_stat)) > + return; > > - if (argc != 2) { > - fprintf(stderr, "basedir not provided\n"); > - exit(1); > - } > + /* Empty options */ > + struct selabel_handle *hnd = selabel_open( > + SELABEL_CTX_FILE, /* options= */ NULL, /* nopt= */ 0); > > - const char *default_path = selinux_file_context_path(); > - if (!stat(default_path, &default_stat)) { > - has_default_path = true; > + if (!hnd) { > + log_errno("Unable to open default content file"); > + exit(2); > } > + selabel_close(hnd); > +} > > - if (has_default_path) { > - /* Empty options */ > - hnd = selabel_open(SELABEL_CTX_FILE, /* options= */ NULL, /* nopt= */ 0); > +void test_null_path(void) > +{ > + struct stat default_stat; > + if (stat(selinux_file_context_path(), &default_stat)) > + return; > > - if (!hnd) { > - perror("file_context:no_options"); > - exit(2); > + /* Default options */ > + struct selinux_opt null_opts[] = { { > + .type = SELABEL_OPT_PATH, > + .value = NULL > } > - selabel_close(hnd); > - > - /* Default options */ > - struct selinux_opt null_opts[] = { > - { .type = SELABEL_OPT_PATH, .value = NULL } > - }; > + }; > > - hnd = selabel_open(SELABEL_CTX_FILE, /* options= */ null_opts, /* nopt= */ 1); > + struct selabel_handle *hnd = selabel_open(SELABEL_CTX_FILE, null_opts, > + ARRAY_SIZE(null_opts)); > > - if (!hnd) { > - perror("file_context:default_options"); > - exit(2); > - } > - selabel_close(hnd); > + if (!hnd) { > + log_errno("Unable to open default content file"); > + exit(2); > } > + selabel_close(hnd); > +} > > +void test_valid_path(const char *basedir) > +{ > /* f1.fc file */ > char *path; > - asprintf(&path, "%s/f1.fc", argv[1]); > - struct selinux_opt f1_opts[] = { > - { .type = SELABEL_OPT_PATH, .value = path } > + asprintf(&path, "%s/f1.fc", basedir); > + struct selinux_opt f1_opts[] = { { > + .type = SELABEL_OPT_PATH, > + .value = path > + } > }; > > - hnd = selabel_open(SELABEL_CTX_FILE, /* options= */ f1_opts, /* nopt= */ 1); > + struct selabel_handle *hnd = > + selabel_open(SELABEL_CTX_FILE, f1_opts, ARRAY_SIZE(f1_opts)); > free(path); > > if (!hnd) { > - perror("file_context:f1_options"); > + log_errno("Unable to open file backend"); > exit(2); > } > > char *context = NULL; > if (selabel_lookup(hnd, &context, "/", S_IFREG)) { > - perror("file_contexts:f1_lookup"); > + log_errno("Unable to lookup \"/\""); > exit(2); > } > > - if (strcmp(context, "system_u:object_r:rootfs:s0")) { > - perror("file_contexts:f1_strcmp"); > + const char *expected = "system_u:object_r:rootfs:s0"; > + if (strcmp(context, expected)) { > + log_err("Incorrect context returned, expected %s got %s", > + expected, context); > exit(2); > } > > free(context); > > selabel_close(hnd); > +} > + > +int main(int argc, char **argv) > +{ > + if (argc != 2) { > + log_err("basedir not provided"); > + exit(1); > + } > + > + test_no_options(); > + test_null_path(); > + test_valid_path(argv[1]); > > return 0; > } > diff --git a/tests/file_contexts/test_open_base.c b/tests/file_contexts/test_open_base.c > index 33b0cac..3e4b4c6 100644 > --- a/tests/file_contexts/test_open_base.c > +++ b/tests/file_contexts/test_open_base.c > @@ -22,26 +22,40 @@ void test_default_options(const char *basedir) > > char *path; > asprintf(&path, "%s/f3.fc", basedir); > - struct selinux_opt f3_opts[] = { > - { .type = SELABEL_OPT_PATH, .value = path } > + struct selinux_opt f3_opts[] = { { > + .type = SELABEL_OPT_PATH, > + .value = path > + } > }; > > hnd = selabel_open(SELABEL_CTX_FILE, f3_opts, ARRAY_SIZE(f3_opts)); > free(path); > > if (!hnd) { > - perror("file_context:f3_default_options"); > + log_errno("Unable to open file backend"); > exit(2); > } > > struct test_t tests[] = { > { .path = "/", .context = "system_u:object_r:rootfs:s0" }, > - { .path = "/local", .context = "system_u:object_r:test_local:s0" }, > - { .path = "/homedirs", .context = "system_u:object_r:test_homedirs:s0" }, > - { .path = "/sub", .context = "system_u:object_r:test_subbed:s0" }, > - { .path = "/sub_dist", .context = "system_u:object_r:test_subbed:s0" }, > + { > + .path = "/local", > + .context = "system_u:object_r:test_local:s0" > + }, > + { > + .path = "/homedirs", > + .context = "system_u:object_r:test_homedirs:s0" > + }, > + { > + .path = "/sub", > + .context = "system_u:object_r:test_subbed:s0" > + }, > + { > + .path = "/sub_dist", > + .context = "system_u:object_r:test_subbed:s0" > + }, > }; > - assertContextsMatch(hnd, tests, ARRAY_SIZE(tests)); > + assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests)); > > selabel_close(hnd); > } > @@ -62,7 +76,7 @@ void test_base_only_option(const char *basedir) > free(path); > > if (!hnd) { > - perror("file_context:f3_base_only_options"); > + log_errno("Unable to open file backend"); > exit(2); > } > > @@ -70,10 +84,16 @@ void test_base_only_option(const char *basedir) > { .path = "/", .context = "system_u:object_r:rootfs:s0" }, > { .path = "/local", .context = NULL }, > { .path = "/homedirs", .context = NULL }, > - { .path = "/sub", .context = "system_u:object_r:test_subbed:s0" }, > - { .path = "/sub_dist", .context = "system_u:object_r:test_subbed:s0" }, > + { > + .path = "/sub", > + .context = "system_u:object_r:test_subbed:s0" > + }, > + { > + .path = "/sub_dist", > + .context = "system_u:object_r:test_subbed:s0" > + }, > }; > - assertContextsMatch(hnd, tests, ARRAY_SIZE(tests)); > + assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests)); > > selabel_close(hnd); > } > @@ -81,7 +101,7 @@ void test_base_only_option(const char *basedir) > int main(int argc, char **argv) > { > if (argc != 2) { > - fprintf(stderr, "basedir not provided\n"); > + log_err("basedir not provided"); > exit(1); > } > > diff --git a/tests/file_contexts/test_validate.c b/tests/file_contexts/test_validate.c > index 8abefed..4d9e920 100644 > --- a/tests/file_contexts/test_validate.c > +++ b/tests/file_contexts/test_validate.c > @@ -1,6 +1,5 @@ > #include <stdio.h> > #include <stdlib.h> > -#include <string.h> > #include <sys/stat.h> > #include <sys/types.h> > #include <unistd.h> > @@ -10,47 +9,58 @@ > > #include "internal.h" > > -int main(int argc, char **argv) > +void test_validate_unknown_fails(const char *basedir) > { > char *path; > - struct selabel_handle *hnd; > - > - if (argc != 2) { > - fprintf(stderr, "basedir not provided\n"); > - exit(1); > - } > - > - asprintf(&path, "%s/f1.fc", argv[1]); > + asprintf(&path, "%s/f1.fc", basedir); > struct selinux_opt f1_opts[] = { > { .type = SELABEL_OPT_PATH, .value = path }, > { .type = SELABEL_OPT_VALIDATE, .value = "1" } > }; > > /* f1 types are not defined in the test policy. */ > - hnd = selabel_open(SELABEL_CTX_FILE, f1_opts, ARRAY_SIZE(f1_opts)); > + struct selabel_handle *hnd = > + selabel_open(SELABEL_CTX_FILE, f1_opts, ARRAY_SIZE(f1_opts)); > free(path); > > if (hnd) { > - fprintf(stderr, "The validation of f1 should have failed.\n"); > + log_err("The validation of f1 should have failed"); > + selabel_close(hnd); > exit(2); > } > +} > > - asprintf(&path, "%s/f2.fc", argv[1]); > +void test_validate_known_succeeds(const char *basedir) > +{ > + char *path; > + asprintf(&path, "%s/f2.fc", basedir); > struct selinux_opt f2_opts[] = { > { .type = SELABEL_OPT_PATH, .value = path }, > { .type = SELABEL_OPT_VALIDATE, .value = "1" } > }; > > /* f2 types are defined in the test policy. */ > - hnd = selabel_open(SELABEL_CTX_FILE, f2_opts, ARRAY_SIZE(f2_opts)); > + struct selabel_handle *hnd = > + selabel_open(SELABEL_CTX_FILE, f2_opts, ARRAY_SIZE(f2_opts)); > free(path); > > if (!hnd) { > - perror("Unable to read valid file_contexts"); > + log_errno("Unable to read valid file_contexts"); > exit(2); > } > > selabel_close(hnd); > +} > + > +int main(int argc, char **argv) > +{ > + if (argc != 2) { > + log_err("basedir not provided"); > + exit(1); > + } > + > + test_validate_unknown_fails(argv[1]); > + test_validate_known_succeeds(argv[1]); > > return 0; > } > -- > 2.55.0.229.g6434b31f56-goog >