/* * Copyright © 2013 Red Hat, Inc. * Copyright © 2013 Marcin Slusarz * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/input.h" #ifdef HAVE_LIBSYSTEMD #include #endif #ifdef __FreeBSD__ #include #endif #include #include "util-backtrace.h" #include "util-files.h" #include "util-libinput.h" #include "builddir.h" #include "libinput-util.h" #include "litest-int.h" #include "litest-runner.h" #include "litest.h" #include "quirks.h" static struct list all_test_suites = LIST_INIT(all_test_suites); /* struct suite */ static int jobs; extern bool use_colors; extern bool in_debugger; extern bool verbose; extern bool run_deviceless; extern struct suite *current_suite; static bool is_debugger_attached(void) { int status; bool rc; int pid = fork(); if (pid == -1) return 0; if (pid == 0) { int ppid = getppid(); if (ptrace(PTRACE_ATTACH, ppid, NULL, 0) == 0) { waitpid(ppid, NULL, 0); ptrace(PTRACE_CONT, ppid, NULL, 0); ptrace(PTRACE_DETACH, ppid, NULL, 0); rc = false; } else { rc = true; } _exit(rc); } else { waitpid(pid, &status, 0); rc = WEXITSTATUS(status); } return !!rc; } static void litest_list_tests(struct list *tests) { struct suite *s; const char *last_test_name = ""; const char *last_dev_name = ""; printf("groups:\n"); list_for_each(s, tests, node) { struct test *t; printf(" - group: \"%s\"\n", s->name); printf(" tests:\n"); list_for_each(t, &s->tests, node) { bool same_test = streq(last_test_name, t->name); bool same_dev = streq(last_dev_name, t->devname); if (!same_test) { printf(" - name: \"%s\"\n", t->name); printf(" devices:\n"); } if (!same_test || !same_dev) { last_test_name = t->name; last_dev_name = t->devname; printf(" - name: \"%s\"\n", t->devname); } } } } extern const struct test_device __start_test_device_section, __stop_test_device_section; static void litest_init_test_devices(void) { const struct test_device *t; const struct test_device *start = &__start_test_device_section; const struct test_device *stop = &__stop_test_device_section; for (t = start; t < stop; t++) litest_add_test_device( &t->device->node); /* NOLINT(clang-analyzer-security.ArrayBound) */ } extern const struct test_collection __start_test_collection_section, __stop_test_collection_section; static void setup_tests(void) { /* Iterate through linker-provided section boundaries. * These symbols mark the start and end of the test_collection_section. */ const struct test_collection *start = &__start_test_collection_section; const struct test_collection *stop = &__stop_test_collection_section; const struct test_collection *c; for (c = start; c < stop; c++) { /* NOLINT(clang-analyzer-security.ArrayBound) */ struct suite *s; s = zalloc(sizeof(*s)); s->name = safe_strdup( c->name); /* NOLINT(clang-analyzer-security.ArrayBound) */ list_init(&s->tests); list_append(&all_test_suites, &s->node); current_suite = s; c->setup(); current_suite = NULL; } } static int check_device_access(void) { if (getuid() != 0) { fprintf(stderr, "%s must be run as root.\n", program_invocation_short_name); return 77; } if (access("/dev/uinput", F_OK) == -1 && access("/dev/input/uinput", F_OK) == -1) { fprintf(stderr, "uinput device is missing, skipping tests.\n"); return 77; } return 0; } static void litest_free_test_list(struct list *tests) { struct suite *s; list_for_each_safe(s, tests, node) { struct test *t; list_for_each_safe(t, &s->tests, node) { litest_test_parameters_unref(t->params); free(t->name); free(t->devname); list_remove(&t->node); free(t); } list_remove(&s->node); free(s->name); free(s); } } int main(int argc, char **argv) { enum litest_mode mode; int rc; const char *meson_testthreads; use_colors = getenv("FORCE_COLOR") || isatty(STDERR_FILENO); if (getenv("NO_COLOR")) use_colors = false; in_debugger = is_debugger_attached(); if (in_debugger) { jobs = 0; } else if ((meson_testthreads = getenv("MESON_TESTTHREADS")) == NULL || !safe_atoi(meson_testthreads, &jobs)) { jobs = get_nprocs(); if (!RUNNING_ON_VALGRIND) jobs *= 2; } if (getenv("LITEST_VERBOSE")) verbose = true; mode = litest_parse_argv(argc, argv, &jobs); if (mode == LITEST_MODE_ERROR) return EXIT_FAILURE; litest_init_test_devices(); setup_tests(); if (list_empty(&all_test_suites)) { fprintf(stderr, "Error: filters are too strict, no tests to run.\n"); return EXIT_FAILURE; } if (mode == LITEST_MODE_LIST) { litest_list_tests(&all_test_suites); return EXIT_SUCCESS; } if (!run_deviceless && (rc = check_device_access()) != 0) return rc; enum litest_runner_result result = litest_run(&all_test_suites, jobs); litest_free_test_list(&all_test_suites); switch (result) { case LITEST_PASS: return EXIT_SUCCESS; case LITEST_SKIP: return 77; default: return result; } }