• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2020 Igalia, S.L.
3  *   Author: Thibault Saunier <tsaunier@igalia.com>
4 
5  * gst-tester.c: tool to launch `.validatetest` files with
6  * TAP compatible output and supporting missing `gst-validate`
7  * application.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <gio/gio.h>
32 
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 
37 #ifdef G_OS_UNIX
38 #include <glib-unix.h>
39 #include <sys/wait.h>
40 #elif defined (G_OS_WIN32)
41 #include <windows.h>
42 #include <io.h>
43 #ifndef STDOUT_FILENO
44 #define STDOUT_FILENO 1
45 #endif
46 #define isatty _isatty
47 #endif
48 
49 #if defined (G_OS_WIN32)
50 #define VALIDATE_NAME "gst-validate-" GST_API_VERSION ".exe"
51 #else
52 #define VALIDATE_NAME "gst-validate-" GST_API_VERSION
53 #endif
54 
55 
56 typedef struct
57 {
58   const gchar *testname;
59 
60   GSubprocess *subproc;
61   GMainLoop *ml;
62 #if defined(G_OS_UNIX)
63   guint signal_watch_intr_id;
64 #endif
65   gint exitcode;
66 } Application;
67 
68 #if defined(G_OS_UNIX)
69 /* As the interrupt handler is dispatched from GMainContext as a GSourceFunc
70  * handler, we can react to this by posting a message. */
71 static gboolean
intr_handler(gpointer user_data)72 intr_handler (gpointer user_data)
73 {
74   Application *app = user_data;
75 
76   g_print ("Bail out! Got interupted.\n");
77 
78   g_subprocess_force_exit (app->subproc);
79 
80   /* remove signal handler */
81   app->signal_watch_intr_id = 0;
82   return G_SOURCE_REMOVE;
83 }
84 #endif
85 
86 static void
_run_app(Application * app)87 _run_app (Application * app)
88 {
89   GError *err = NULL;
90   gboolean bailed_out = FALSE, skipped = FALSE;
91   gchar *_stdout = NULL;
92   gboolean is_tty = isatty (STDOUT_FILENO);
93 
94   g_print ("1..1\n");
95   g_subprocess_communicate_utf8 (app->subproc, NULL, NULL,
96       is_tty ? NULL : &_stdout, NULL, &err);
97   if (_stdout) {
98     gchar *c;
99     GString *output = g_string_new (NULL);
100 
101     for (c = _stdout; *c != '\0'; c++) {
102       g_string_append_c (output, *c);
103       if (!bailed_out && !skipped && *c == '\n' && *(c + 1) != '\0') {
104         if (strstr ((c + 1), "Bail out!") == c + 1) {
105           bailed_out = TRUE;
106           continue;
107         }
108 
109         if (strstr ((c + 1), "ok") == c + 1 && strstr ((c + 1), "# SKIP")) {
110           skipped = TRUE;
111           app->exitcode = 0;
112           continue;
113         }
114 
115         g_string_append (output, "# ");
116       }
117     }
118     g_print ("# %s\n", output->str);
119     g_string_free (output, TRUE);
120     g_free (_stdout);
121   }
122 #ifdef G_OS_UNIX
123   if (app->signal_watch_intr_id > 0)
124     g_source_remove (app->signal_watch_intr_id);
125 #endif
126 
127   if (skipped || bailed_out)
128     goto done;
129 
130   if (g_subprocess_get_if_signaled (app->subproc))
131     app->exitcode = g_subprocess_get_term_sig (app->subproc);
132   else
133     app->exitcode = g_subprocess_get_exit_status (app->subproc);
134 
135   if (app->exitcode == 0) {
136     g_print ("ok 1 %s\n", app->testname);
137   } else if (app->exitcode == 18) {
138     g_print ("not ok 1 %s # Got a critical report\n", app->testname);
139   } else {
140     g_print ("not ok 1 %s # Unknown reason\n", app->testname);
141   }
142 
143 done:
144   g_clear_object (&app->subproc);
145   g_main_loop_quit (app->ml);
146 }
147 
148 int
main(int argc,gchar ** argv)149 main (int argc, gchar ** argv)
150 {
151   Application app = { 0, };
152   gchar *dirname;
153   GFile *f;
154   gchar **args = g_new0 (gchar *, argc + 2);
155   gint i;
156   GError *err = NULL;
157   gchar *filename;
158   gboolean is_tty = isatty (STDOUT_FILENO);
159 
160   if (argc < 2) {
161     g_print ("1..0\nnot ok # Missing <testfile> argument\n");
162     return 1;
163   }
164 
165   app.testname = argv[1];
166 
167   dirname = g_path_get_dirname (argv[0]);
168   filename = g_build_filename ("subprojects", "gst-devtools",
169       "validate", "tools", VALIDATE_NAME, NULL);
170   f = g_file_new_for_path (filename);
171   g_free (filename);
172 
173   if (g_file_query_exists (f, NULL)) {
174     /* Try to find `gst-validate` as a meson subproject */
175     g_free (args[0]);
176     g_clear_error (&err);
177     args[0] = g_file_get_path (f);
178     g_print ("# Running from meson subproject %s\n", args[0]);
179   }
180   g_free (dirname);
181   g_object_unref (f);
182 
183   if (!args[0])
184     args[0] = g_strdup (VALIDATE_NAME);
185   args[1] = g_strdup ("--set-test-file");
186   for (i = 1; i < argc; i++)
187     args[i + 1] = g_strdup (argv[i]);
188 
189   app.subproc = g_subprocess_newv ((const char *const *) args,
190       is_tty ? G_SUBPROCESS_FLAGS_STDIN_INHERIT :
191       G_SUBPROCESS_FLAGS_STDOUT_PIPE, &err);
192 
193   if (!app.subproc) {
194     g_printerr ("%s %s\n", args[0], err->message);
195     if (g_error_matches (err, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT)) {
196       g_print ("1..0 # Skipped: `" VALIDATE_NAME "` not available\n");
197       return 0;
198     }
199 
200     g_print ("1..0\nnot ok # %s\n", err->message);
201     return -1;
202   }
203 
204   app.ml = g_main_loop_new (NULL, TRUE);
205 
206 #ifdef G_OS_UNIX
207   app.signal_watch_intr_id =
208       g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, &app);
209 #endif
210 
211 /* Running the subprocess in it own thread so that we can properly catch
212  * interuptions in the main thread main loop */
213   g_thread_new ("gst-tester-thread", (GThreadFunc) _run_app, &app);
214   g_main_loop_run (app.ml);
215   g_main_loop_unref (app.ml);
216   g_strfreev (args);
217 
218   return app.exitcode;
219 }
220