1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2008  Kouhei Sutou <kou@cozmixng.org>
4  *
5  *  This library is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Lesser General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif /* HAVE_CONFIG_H */
23 
24 #include "cut-main.h"
25 #include "cut-sub-process.h"
26 #include "cut-pipeline.h"
27 #include "cut-test-context.h"
28 
29 #define CUT_SUB_PROCESS_GET_PRIVATE(obj) \
30     (G_TYPE_INSTANCE_GET_PRIVATE((obj), CUT_TYPE_SUB_PROCESS, CutSubProcessPrivate))
31 
32 typedef struct _CutSubProcessPrivate         CutSubProcessPrivate;
33 struct _CutSubProcessPrivate
34 {
35     CutRunContext *pipeline;
36     CutTestContext *test_context;
37     gboolean running;
38     gboolean is_success;
39     gboolean is_ran;
40 };
41 
42 enum {
43     PROP_0,
44     PROP_PIPELINE,
45     PROP_TEST_CONTEXT
46 };
47 
48 G_DEFINE_TYPE(CutSubProcess, cut_sub_process, G_TYPE_OBJECT)
49 
50 static void dispose        (GObject         *object);
51 static void set_property   (GObject         *object,
52                             guint            prop_id,
53                             const GValue    *value,
54                             GParamSpec      *pspec);
55 static void get_property   (GObject         *object,
56                             guint            prop_id,
57                             GValue          *value,
58                             GParamSpec      *pspec);
59 
60 static void
cut_sub_process_class_init(CutSubProcessClass * klass)61 cut_sub_process_class_init (CutSubProcessClass *klass)
62 {
63     GObjectClass *gobject_class;
64     GParamSpec *spec;
65 
66     gobject_class = G_OBJECT_CLASS(klass);
67 
68     gobject_class->dispose = dispose;
69     gobject_class->set_property = set_property;
70     gobject_class->get_property = get_property;
71 
72     spec = g_param_spec_object("pipeline",
73                                "Pipeline",
74                                "The pipeline",
75                                CUT_TYPE_PIPELINE,
76                                G_PARAM_READABLE | G_PARAM_WRITABLE);
77     g_object_class_install_property(gobject_class, PROP_PIPELINE, spec);
78 
79     spec = g_param_spec_object("test-context",
80                                "Test context",
81                                "The test context",
82                                CUT_TYPE_TEST_CONTEXT,
83                                G_PARAM_READABLE | G_PARAM_WRITABLE);
84     g_object_class_install_property(gobject_class, PROP_TEST_CONTEXT, spec);
85 
86     g_type_class_add_private(gobject_class, sizeof(CutSubProcessPrivate));
87 }
88 
89 static void
cut_sub_process_init(CutSubProcess * sub_process)90 cut_sub_process_init (CutSubProcess *sub_process)
91 {
92     CutSubProcessPrivate *priv;
93 
94     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
95     priv->pipeline = NULL;
96     priv->test_context = NULL;
97     priv->running = FALSE;
98     priv->is_success = TRUE;
99     priv->is_ran = FALSE;
100 }
101 
102 static void
dispose(GObject * object)103 dispose (GObject *object)
104 {
105     CutSubProcessPrivate *priv;
106 
107     priv = CUT_SUB_PROCESS_GET_PRIVATE(object);
108 
109     if (priv->running) {
110         if (priv->pipeline)
111             cut_run_context_emit_complete_run(priv->pipeline, FALSE);
112         priv->running = FALSE;
113     }
114 
115     if (priv->pipeline) {
116         g_object_unref(priv->pipeline);
117         priv->pipeline = NULL;
118     }
119 
120     if (priv->test_context) {
121         g_object_unref(priv->test_context);
122         priv->test_context = NULL;
123     }
124 
125     G_OBJECT_CLASS(cut_sub_process_parent_class)->dispose(object);
126 }
127 
128 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)129 set_property (GObject      *object,
130               guint         prop_id,
131               const GValue *value,
132               GParamSpec   *pspec)
133 {
134     CutSubProcess *sub_process;
135 
136     sub_process = CUT_SUB_PROCESS(object);
137     switch (prop_id) {
138       case PROP_PIPELINE:
139         cut_sub_process_set_pipeline(sub_process, g_value_get_object(value));
140         break;
141       case PROP_TEST_CONTEXT:
142         cut_sub_process_set_test_context(sub_process, g_value_get_object(value));
143         break;
144       default:
145         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
146         break;
147     }
148 }
149 
150 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)151 get_property (GObject    *object,
152               guint       prop_id,
153               GValue     *value,
154               GParamSpec *pspec)
155 {
156     CutSubProcessPrivate *priv = CUT_SUB_PROCESS_GET_PRIVATE(object);
157 
158     switch (prop_id) {
159       case PROP_PIPELINE:
160         g_value_set_object(value, priv->pipeline);
161         break;
162       case PROP_TEST_CONTEXT:
163         g_value_set_object(value, priv->test_context);
164         break;
165       default:
166         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
167         break;
168     }
169 }
170 
171 CutSubProcess *
cut_sub_process_new(const char * test_directory,CutTestContext * test_context)172 cut_sub_process_new (const char *test_directory, CutTestContext *test_context)
173 {
174     CutSubProcess *sub_process;
175     CutRunContext *parent_run_context = NULL;
176     CutRunContext *pipeline;
177 
178     if (test_context)
179         parent_run_context = cut_test_context_get_run_context(test_context);
180 
181     pipeline = cut_pipeline_new();
182     cut_run_context_set_source_directory(pipeline, test_directory);
183     cut_run_context_set_test_directory(pipeline, test_directory);
184 
185     if (parent_run_context) {
186         gint max_threads;
187 
188         if (cut_run_context_get_multi_thread(parent_run_context))
189             cut_run_context_set_multi_thread(pipeline, TRUE);
190 
191         max_threads = cut_run_context_get_max_threads(parent_run_context);
192         cut_run_context_set_max_threads(pipeline, max_threads);
193 
194         cut_run_context_delegate_signals(pipeline, parent_run_context);
195     }
196 
197     sub_process = g_object_new(CUT_TYPE_SUB_PROCESS,
198                                "test-context", test_context,
199                                "pipeline", pipeline,
200                                NULL);
201     g_object_unref(pipeline);
202 
203     return sub_process;
204 }
205 
206 CutRunContext *
cut_sub_process_get_pipeline(CutSubProcess * sub_process)207 cut_sub_process_get_pipeline (CutSubProcess *sub_process)
208 {
209     return CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
210 }
211 
212 void
cut_sub_process_set_pipeline(CutSubProcess * sub_process,CutRunContext * pipeline)213 cut_sub_process_set_pipeline (CutSubProcess *sub_process,
214                               CutRunContext *pipeline)
215 {
216     CutSubProcessPrivate *priv;
217 
218     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
219     if (priv->pipeline)
220         g_object_unref(priv->pipeline);
221     if (pipeline)
222         g_object_ref(pipeline);
223     priv->pipeline = pipeline;
224 }
225 
226 CutTestContext *
cut_sub_process_get_test_context(CutSubProcess * sub_process)227 cut_sub_process_get_test_context (CutSubProcess *sub_process)
228 {
229     return CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->test_context;
230 }
231 
232 void
cut_sub_process_set_test_context(CutSubProcess * sub_process,CutTestContext * test_context)233 cut_sub_process_set_test_context (CutSubProcess  *sub_process,
234                                   CutTestContext *test_context)
235 {
236     CutSubProcessPrivate *priv;
237 
238     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
239     if (priv->test_context)
240         g_object_unref(priv->test_context);
241     if (test_context)
242         g_object_ref(test_context);
243     priv->test_context = test_context;
244 }
245 
246 gboolean
cut_sub_process_run(CutSubProcess * sub_process)247 cut_sub_process_run (CutSubProcess *sub_process)
248 {
249     CutSubProcessPrivate *priv;
250 
251     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
252     if (!priv->is_ran) {
253         priv->running = TRUE;
254         priv->is_success = cut_run_context_start(priv->pipeline);
255         priv->running = FALSE;
256         priv->is_ran = TRUE;
257     }
258     return priv->is_success;
259 }
260 
261 static void
cb_complete_run(CutRunContext * pipeline,gboolean success,gpointer user_data)262 cb_complete_run (CutRunContext *pipeline, gboolean success, gpointer user_data)
263 {
264     CutSubProcess *sub_process = user_data;
265     CutSubProcessPrivate *priv;
266 
267     g_signal_handlers_disconnect_by_func(pipeline,
268                                          G_CALLBACK(cb_complete_run),
269                                          user_data);
270 
271     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
272     priv->is_success = success;
273     priv->running = FALSE;
274     priv->is_ran = TRUE;
275 }
276 
277 void
cut_sub_process_run_async(CutSubProcess * sub_process)278 cut_sub_process_run_async (CutSubProcess *sub_process)
279 {
280     CutSubProcessPrivate *priv;
281 
282     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
283     if (!priv->is_ran && !priv->running) {
284         priv->running = TRUE;
285         priv->is_success = TRUE;
286         g_signal_connect(priv->pipeline, "complete-run",
287                          G_CALLBACK(cb_complete_run), sub_process);
288         cut_run_context_start_async(priv->pipeline);
289     }
290 }
291 
292 gboolean
cut_sub_process_wait(CutSubProcess * sub_process)293 cut_sub_process_wait (CutSubProcess *sub_process)
294 {
295     CutSubProcessPrivate *priv;
296 
297     priv = CUT_SUB_PROCESS_GET_PRIVATE(sub_process);
298     if (!priv->is_ran) {
299         while (priv->running)
300             cut_run_iteration();
301     }
302     return priv->is_success;
303 }
304 
305 gboolean
cut_sub_process_is_success(CutSubProcess * sub_process)306 cut_sub_process_is_success (CutSubProcess *sub_process)
307 {
308     return CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->is_success;
309 }
310 
311 gboolean
cut_sub_process_is_running(CutSubProcess * sub_process)312 cut_sub_process_is_running (CutSubProcess *sub_process)
313 {
314     return CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->running;
315 }
316 
317 
318 const char *
cut_sub_process_get_test_directory(CutSubProcess * sub_process)319 cut_sub_process_get_test_directory (CutSubProcess *sub_process)
320 {
321     CutRunContext *pipeline;
322 
323     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
324     return cut_run_context_get_test_directory(pipeline);
325 }
326 
cut_sub_process_set_test_directory(CutSubProcess * sub_process,const char * test_directory)327 void cut_sub_process_set_test_directory (CutSubProcess  *sub_process,
328                                          const char     *test_directory)
329 {
330     CutRunContext *pipeline;
331 
332     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
333     cut_run_context_set_test_directory(pipeline, test_directory);
334 }
335 
336 const char *
cut_sub_process_get_source_directory(CutSubProcess * sub_process)337 cut_sub_process_get_source_directory (CutSubProcess *sub_process)
338 {
339     CutRunContext *pipeline;
340 
341     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
342     return cut_run_context_get_source_directory(pipeline);
343 }
344 
345 void
cut_sub_process_set_source_directory(CutSubProcess * sub_process,const char * source_directory)346 cut_sub_process_set_source_directory (CutSubProcess  *sub_process,
347                                       const char     *source_directory)
348 {
349     CutRunContext *pipeline;
350 
351     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
352     cut_run_context_set_source_directory(pipeline, source_directory);
353 }
354 
355 gboolean
cut_sub_process_get_multi_thread(CutSubProcess * sub_process)356 cut_sub_process_get_multi_thread (CutSubProcess  *sub_process)
357 {
358     CutRunContext *pipeline;
359 
360     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
361     return cut_run_context_get_multi_thread(pipeline);
362 }
363 
364 void
cut_sub_process_set_multi_thread(CutSubProcess * sub_process,gboolean multi_thread)365 cut_sub_process_set_multi_thread (CutSubProcess  *sub_process,
366                                   gboolean        multi_thread)
367 {
368     CutRunContext *pipeline;
369 
370     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
371     cut_run_context_set_multi_thread(pipeline, multi_thread);
372 }
373 
374 gint
cut_sub_process_get_max_threads(CutSubProcess * sub_process)375 cut_sub_process_get_max_threads (CutSubProcess *sub_process)
376 {
377     CutRunContext *pipeline;
378 
379     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
380     return cut_run_context_get_max_threads(pipeline);
381 }
382 
383 void
cut_sub_process_set_max_threads(CutSubProcess * sub_process,gint max_threads)384 cut_sub_process_set_max_threads (CutSubProcess *sub_process,
385                                  gint           max_threads)
386 {
387     CutRunContext *pipeline;
388 
389     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
390     cut_run_context_set_max_threads(pipeline, max_threads);
391 }
392 
393 const char **
cut_sub_process_get_exclude_files(CutSubProcess * sub_process)394 cut_sub_process_get_exclude_files (CutSubProcess  *sub_process)
395 {
396     CutRunContext *pipeline;
397 
398     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
399     return cut_run_context_get_exclude_files(pipeline);
400 }
401 
402 void
cut_sub_process_set_exclude_files(CutSubProcess * sub_process,const char ** files)403 cut_sub_process_set_exclude_files (CutSubProcess  *sub_process,
404                                    const char    **files)
405 {
406     CutRunContext *pipeline;
407 
408     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
409     cut_run_context_set_exclude_files(pipeline, files);
410 }
411 
412 
413 const char **
cut_sub_process_get_exclude_directories(CutSubProcess * sub_process)414 cut_sub_process_get_exclude_directories (CutSubProcess  *sub_process)
415 {
416     CutRunContext *pipeline;
417 
418     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
419     return cut_run_context_get_exclude_directories(pipeline);
420 }
421 
422 void
cut_sub_process_set_exclude_directories(CutSubProcess * sub_process,const char ** directories)423 cut_sub_process_set_exclude_directories (CutSubProcess  *sub_process,
424                                          const char    **directories)
425 {
426     CutRunContext *pipeline;
427 
428     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
429     cut_run_context_set_exclude_directories(pipeline, directories);
430 }
431 
432 
433 const char **
cut_sub_process_get_target_test_case_names(CutSubProcess * sub_process)434 cut_sub_process_get_target_test_case_names (CutSubProcess  *sub_process)
435 {
436     CutRunContext *pipeline;
437 
438     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
439     return cut_run_context_get_target_test_case_names(pipeline);
440 }
441 
442 void
cut_sub_process_set_target_test_case_names(CutSubProcess * sub_process,const char ** names)443 cut_sub_process_set_target_test_case_names (CutSubProcess  *sub_process,
444                                             const char    **names)
445 {
446     CutRunContext *pipeline;
447 
448     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
449     cut_run_context_set_target_test_case_names(pipeline, names);
450 }
451 
452 const char **
cut_sub_process_get_target_test_names(CutSubProcess * sub_process)453 cut_sub_process_get_target_test_names (CutSubProcess  *sub_process)
454 {
455     CutRunContext *pipeline;
456 
457     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
458     return cut_run_context_get_target_test_names(pipeline);
459 }
460 
461 void
cut_sub_process_set_target_test_names(CutSubProcess * sub_process,const char ** names)462 cut_sub_process_set_target_test_names (CutSubProcess  *sub_process,
463                                        const char    **names)
464 {
465     CutRunContext *pipeline;
466 
467     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
468     cut_run_context_set_target_test_names(pipeline, names);
469 }
470 
471 double
cut_sub_process_get_elapsed(CutSubProcess * sub_process)472 cut_sub_process_get_elapsed (CutSubProcess  *sub_process)
473 {
474     CutRunContext *pipeline;
475 
476     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
477     return cut_run_context_get_elapsed(pipeline);
478 }
479 
480 double
cut_sub_process_get_total_elapsed(CutSubProcess * sub_process)481 cut_sub_process_get_total_elapsed (CutSubProcess  *sub_process)
482 {
483     CutRunContext *pipeline;
484 
485     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
486     return cut_run_context_get_total_elapsed(pipeline);
487 }
488 
489 gboolean
cut_sub_process_is_crashed(CutSubProcess * sub_process)490 cut_sub_process_is_crashed (CutSubProcess  *sub_process)
491 {
492     CutRunContext *pipeline;
493 
494     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
495     return cut_run_context_is_crashed(pipeline);
496 }
497 
498 gboolean
cut_sub_process_get_fatal_failures(CutSubProcess * sub_process)499 cut_sub_process_get_fatal_failures (CutSubProcess  *sub_process)
500 {
501     CutRunContext *pipeline;
502 
503     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
504     return cut_run_context_get_fatal_failures(pipeline);
505 }
506 
507 void
cut_sub_process_set_fatal_failures(CutSubProcess * sub_process,gboolean fatal_failures)508 cut_sub_process_set_fatal_failures (CutSubProcess  *sub_process,
509                                     gboolean        fatal_failures)
510 {
511     CutRunContext *pipeline;
512 
513     pipeline = CUT_SUB_PROCESS_GET_PRIVATE(sub_process)->pipeline;
514     cut_run_context_set_fatal_failures(pipeline, fatal_failures);
515 }
516 
517 
518 /*
519 vi:ts=4:nowrap:ai:expandtab:sw=4
520 */
521