test: add --output-file to print test logs to a file
authorPeter Hutterer <peter.hutterer@who-t.net>
Mon, 21 Oct 2024 02:12:45 +0000 (12:12 +1000)
committerMarge Bot <emma+marge@anholt.net>
Wed, 30 Oct 2024 23:20:42 +0000 (23:20 +0000)
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1067>

test/litest-runner.c
test/litest-runner.h
test/litest.c

index c5507a352f32a9f30ed7222aed5054cefd9fbfca..6d9b929a8138445af6c223f747f26e5202bf7301 100644 (file)
@@ -87,6 +87,7 @@ struct litest_runner {
        unsigned int timeout;
        bool verbose;
        bool exit_on_fail;
+       FILE *fp;
 
        int terminating;
 
@@ -664,23 +665,24 @@ litest_runner_log_test_result(struct litest_runner *runner, struct litest_runner
                case LITEST_SYSTEM_ERROR: color = ANSI_BRIGHT_MAGENTA; break;
        }
 
-       fprintf(stderr, "  - name: \"%s\"\n", t->desc.name);
+       fprintf(runner->fp, "  - name: \"%s\"\n", t->desc.name);
        int min = t->desc.args.range.lower,
            max = t->desc.args.range.upper;
        if (range_is_valid(&t->desc.args.range))
-               fprintf(stderr, "    rangeval: %d  # %d..%d\n", t->desc.rangeval, min, max);
+               fprintf(runner->fp, "    rangeval: %d  # %d..%d\n", t->desc.rangeval, min, max);
 
-       fprintf(stderr,
+       fprintf(runner->fp,
                "    duration: %ld  # (ms), total test run time: %02d:%02d\n",
                t->times.end_millis - t->times.start_millis,
                (ms2s(t->times.end_millis - runner->times.start_millis)) / 60,
                (ms2s(t->times.end_millis - runner->times.start_millis)) % 60);
 
        status = litest_runner_result_as_str(t->result);
-       fprintf(stderr, "    status: %s%s%s\n",
-               isatty(STDERR_FILENO) ? color : "",
+       bool is_tty = isatty(fileno(runner->fp));
+       fprintf(runner->fp, "    status: %s%s%s\n",
+               is_tty ? color : "",
                &status[7], /* skip LITEST_ prefix */
-               isatty(STDERR_FILENO) ? ANSI_NORMAL : "");
+               is_tty ? ANSI_NORMAL : "");
 
        switch (t->result) {
                case LITEST_PASS:
@@ -694,28 +696,28 @@ litest_runner_log_test_result(struct litest_runner *runner, struct litest_runner
        }
 
        if (t->sig_or_errno > 0)
-               fprintf(stderr, "    signal: %d # SIG%s \n",
+               fprintf(runner->fp, "    signal: %d # SIG%s \n",
                       t->sig_or_errno,
                       sigabbrev_np(t->sig_or_errno));
        else if (t->sig_or_errno < 0)
-               fprintf(stderr, "    errno: %d # %s\n",
+               fprintf(runner->fp, "    errno: %d # %s\n",
                       -t->sig_or_errno,
                       strerror(-t->sig_or_errno));
        if (!stringbuf_is_empty(&t->logs[FD_LOG])) {
-               fprintf(stderr, "    log: |\n");
-               print_lines(stderr, t->logs[FD_LOG].data, "      ");
+               fprintf(runner->fp, "    log: |\n");
+               print_lines(runner->fp, t->logs[FD_LOG].data, "      ");
        }
        if (!stringbuf_is_empty(&t->logs[FD_STDOUT])) {
-               fprintf(stderr, "    stdout: |\n");
-               print_lines(stderr, t->logs[FD_STDOUT].data, "      ");
+               fprintf(runner->fp, "    stdout: |\n");
+               print_lines(runner->fp, t->logs[FD_STDOUT].data, "      ");
        }
        if (!stringbuf_is_empty(&t->logs[FD_STDERR])) {
-               fprintf(stderr, "    stderr: |\n");
-               print_lines(stderr, t->logs[FD_STDERR].data, "      ");
+               fprintf(runner->fp, "    stderr: |\n");
+               print_lines(runner->fp, t->logs[FD_STDERR].data, "      ");
        }
        if (!stringbuf_is_empty(&t->logs[FD_VALGRIND])) {
-               fprintf(stderr, "    valgrind: |\n");
-               print_lines(stderr, t->logs[FD_VALGRIND].data, "      ");
+               fprintf(runner->fp, "    valgrind: |\n");
+               print_lines(runner->fp, t->logs[FD_VALGRIND].data, "      ");
        }
 }
 
@@ -729,6 +731,7 @@ litest_runner_new(void)
        list_init(&runner->tests_running);
        runner->timeout = LITEST_RUNNER_DEFAULT_TIMEOUT;
        runner->max_forks = get_nprocs() * 2;
+       runner->fp = stderr;
 
        return runner;
 }
@@ -740,6 +743,14 @@ litest_runner_set_timeout(struct litest_runner *runner,
        runner->timeout = timeout;
 }
 
+void
+litest_runner_set_output_file(struct litest_runner *runner,
+                             FILE *fp)
+{
+       setlinebuf(fp);
+       runner->fp = fp;
+}
+
 void
 litest_runner_set_num_parallel(struct litest_runner *runner,
                               size_t num_jobs)
@@ -875,9 +886,9 @@ litest_runner_run_tests(struct litest_runner *runner)
        runner->times.start = time(NULL);
        ltime = localtime(&runner->times.start);
        strftime(timestamp, sizeof(timestamp), "%FT%H:%M", ltime);
-       fprintf(stderr, "start: %ld  # \"%s\"\n", runner->times.start, timestamp);
-       fprintf(stderr, "jobs: %zd\n", runner->max_forks);
-       fprintf(stderr, "tests:\n");
+       fprintf(runner->fp, "start: %ld  # \"%s\"\n", runner->times.start, timestamp);
+       fprintf(runner->fp, "jobs: %zd\n", runner->max_forks);
+       fprintf(runner->fp, "tests:\n");
        list_for_each_safe(t, &runner->tests, node) {
                int r = litest_runner_run_test(runner, t);
                if (r >= 0) {
@@ -949,26 +960,26 @@ litest_runner_run_tests(struct litest_runner *runner)
        runner->times.end = time(NULL);
        ltime = localtime(&runner->times.end);
        strftime(timestamp, sizeof(timestamp), "%FT%H:%M", ltime);
-       fprintf(stderr, "end: %ld  # \"%s\"\n", runner->times.end, timestamp);
-       fprintf(stderr,
+       fprintf(runner->fp, "end: %ld  # \"%s\"\n", runner->times.end, timestamp);
+       fprintf(runner->fp,
                "duration: %ld  # (s) %02ld:%02ld\n",
                runner->times.end - runner->times.start,
                (runner->times.end - runner->times.start) / 60,
                (runner->times.end - runner->times.start) % 60);
-       fprintf(stderr, "summary:\n");
-       fprintf(stderr, "  completed: %zd\n", ncomplete);
-       fprintf(stderr, "  pass: %zd\n", npass);
-       fprintf(stderr, "  na: %zd\n", nna);
-       fprintf(stderr, "  fail: %zd\n", nfail);
-       fprintf(stderr, "  skip: %zd\n", nskip);
+       fprintf(runner->fp, "summary:\n");
+       fprintf(runner->fp, "  completed: %zd\n", ncomplete);
+       fprintf(runner->fp, "  pass: %zd\n", npass);
+       fprintf(runner->fp, "  na: %zd\n", nna);
+       fprintf(runner->fp, "  fail: %zd\n", nfail);
+       fprintf(runner->fp, "  skip: %zd\n", nskip);
        if (nfail > 0) {
-               fprintf(stderr, "  failed:\n");
+               fprintf(runner->fp, "  failed:\n");
                list_for_each(t, &runner->tests_complete, node) {
                        switch (t->result) {
                                case LITEST_FAIL:
                                case LITEST_SYSTEM_ERROR:
                                case LITEST_TIMEOUT:
-                                       fprintf(stderr, "    - \"%s\"\n",  t->desc.name);
+                                       fprintf(runner->fp, "    - \"%s\"\n",  t->desc.name);
                                        break;
                                default:
                                        break;
@@ -981,9 +992,9 @@ litest_runner_run_tests(struct litest_runner *runner)
                struct stringbuf *b = stringbuf_new();
 
                collect_file(filename, b);
-               fprintf(stderr, "valgrind:\n");
-               print_lines(stderr, b->data, "  ");
-               fprintf(stderr, "# Valgrind log is incomplete, see %s for full log\n", filename);
+               fprintf(runner->fp, "valgrind:\n");
+               print_lines(runner->fp, b->data, "  ");
+               fprintf(runner->fp, "# Valgrind log is incomplete, see %s for full log\n", filename);
                free(filename);
                stringbuf_destroy(b);
        }
@@ -1006,7 +1017,7 @@ litest_runner_run_tests(struct litest_runner *runner)
                }
        }
        /* Status is always prefixed with LITEST_ */
-       fprintf(stderr, "  status: %s\n", &litest_runner_result_as_str(result)[7]);
+       fprintf(runner->fp, "  status: %s\n", &litest_runner_result_as_str(result)[7]);
 
        return result;
 }
index 14f0a11d6498bdb5249005d8a6d152f5b7d0e146..9bee9dcdf3c27a19043f81beede0a24b46253eae 100644 (file)
@@ -78,6 +78,7 @@ void litest_runner_set_num_parallel(struct litest_runner *runner, size_t num_job
 void litest_runner_set_timeout(struct litest_runner *runner, unsigned int timeout);
 void litest_runner_set_verbose(struct litest_runner *runner, bool verbose);
 void litest_runner_set_exit_on_fail(struct litest_runner *runner, bool do_exit);
+void litest_runner_set_output_file(struct litest_runner *runner, FILE *fp);
 void litest_runner_add_test(struct litest_runner *runner,
                            const struct litest_runner_test_description *t);
 enum litest_runner_result litest_runner_run_tests(struct litest_runner *runner);
index 1f8b2094ca782fb00acddbe1bcef4e5c1adfc3d4..d9e55023aa9e8966c72c6b4742a1d59ae8e38b41 100644 (file)
@@ -83,6 +83,7 @@ static bool verbose = false;
 static bool run_deviceless = false;
 static bool use_system_rules_quirks = false;
 static bool exit_first = false;
+static FILE * outfile = NULL;
 static const char *filter_test = NULL;
 static const char *filter_device = NULL;
 static const char *filter_group = NULL;
@@ -943,6 +944,8 @@ litest_run_suite(struct list *suites, int njobs)
        struct litest_runner *runner = litest_runner_new();
 
        litest_runner_set_num_parallel(runner, jobs > 0 ? jobs : 0);
+       if (outfile)
+               litest_runner_set_output_file(runner, outfile);
        litest_runner_set_verbose(runner, verbose);
        litest_runner_set_timeout(runner, 30);
        litest_runner_set_exit_on_fail(runner, exit_first);
@@ -4520,6 +4523,7 @@ litest_parse_argv(int argc, char **argv)
                OPT_FILTER_GROUP,
                OPT_FILTER_RANGEVAL,
                OPT_FILTER_DEVICELESS,
+               OPT_OUTPUT_FILE,
                OPT_JOBS,
                OPT_LIST,
                OPT_VERBOSE,
@@ -4530,6 +4534,7 @@ litest_parse_argv(int argc, char **argv)
                { "filter-group", 1, 0, OPT_FILTER_GROUP },
                { "filter-rangeval", 1, 0, OPT_FILTER_RANGEVAL },
                { "filter-deviceless", 0, 0, OPT_FILTER_DEVICELESS },
+               { "output-file", 1, 0, OPT_OUTPUT_FILE },
                { "exitfirst", 0, 0, OPT_EXIT_FIRST },
                { "jobs", 1, 0, OPT_JOBS },
                { "list", 0, 0, OPT_LIST },
@@ -4628,6 +4633,13 @@ litest_parse_argv(int argc, char **argv)
                case OPT_FILTER_DEVICELESS:
                        run_deviceless = true;
                        break;
+               case OPT_OUTPUT_FILE:
+                       outfile = fopen(optarg, "w+");
+                       if (!outfile) {
+                               fprintf(stderr, "Failed to open %s: %m\n", optarg);
+                               exit(1);
+                       }
+                       break;
                case 'x':
                case OPT_EXIT_FIRST:
                        exit_first = true;