1 /*
2 * Check: a unit test framework for C
3 * Copyright (C) 2001, 2002 Arien Malec
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 * MA 02110-1301, USA.
19 */
20
21 #include "../lib/libcompat.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <check.h>
26 #if ENABLE_SUBUNIT
27 #include <subunit/child.h>
28 #endif
29
30 #include "check_error.h"
31 #include "check_list.h"
32 #include "check_impl.h"
33 #include "check_log.h"
34 #include "check_print.h"
35 #include "check_str.h"
36
37 /*
38 * If a log file is specified to be "-", then instead of
39 * opening a file the log output is printed to stdout.
40 */
41 #define STDOUT_OVERRIDE_LOG_FILE_NAME "-"
42
43 static void srunner_send_evt(SRunner * sr, void *obj, enum cl_event evt);
44
srunner_set_log(SRunner * sr,const char * fname)45 void srunner_set_log(SRunner * sr, const char *fname)
46 {
47 if(sr->log_fname)
48 return;
49 sr->log_fname = fname;
50 }
51
srunner_has_log(SRunner * sr)52 int srunner_has_log(SRunner * sr)
53 {
54 return srunner_log_fname(sr) != NULL;
55 }
56
srunner_log_fname(SRunner * sr)57 const char *srunner_log_fname(SRunner * sr)
58 {
59 /* check if log filename have been set explicitly */
60 if(sr->log_fname != NULL)
61 return sr->log_fname;
62
63 return getenv("CK_LOG_FILE_NAME");
64 }
65
66
srunner_set_xml(SRunner * sr,const char * fname)67 void srunner_set_xml(SRunner * sr, const char *fname)
68 {
69 if(sr->xml_fname)
70 return;
71 sr->xml_fname = fname;
72 }
73
srunner_has_xml(SRunner * sr)74 int srunner_has_xml(SRunner * sr)
75 {
76 return srunner_xml_fname(sr) != NULL;
77 }
78
srunner_xml_fname(SRunner * sr)79 const char *srunner_xml_fname(SRunner * sr)
80 {
81 /* check if XML log filename have been set explicitly */
82 if(sr->xml_fname != NULL)
83 {
84 return sr->xml_fname;
85 }
86
87 return getenv("CK_XML_LOG_FILE_NAME");
88 }
89
srunner_set_tap(SRunner * sr,const char * fname)90 void srunner_set_tap(SRunner * sr, const char *fname)
91 {
92 if(sr->tap_fname)
93 return;
94 sr->tap_fname = fname;
95 }
96
srunner_has_tap(SRunner * sr)97 int srunner_has_tap(SRunner * sr)
98 {
99 return srunner_tap_fname(sr) != NULL;
100 }
101
srunner_tap_fname(SRunner * sr)102 const char *srunner_tap_fname(SRunner * sr)
103 {
104 /* check if tap log filename have been set explicitly */
105 if(sr->tap_fname != NULL)
106 {
107 return sr->tap_fname;
108 }
109
110 return getenv("CK_TAP_LOG_FILE_NAME");
111 }
112
srunner_register_lfun(SRunner * sr,FILE * lfile,int close,LFun lfun,enum print_output printmode)113 void srunner_register_lfun(SRunner * sr, FILE * lfile, int close,
114 LFun lfun, enum print_output printmode)
115 {
116 Log *l = (Log *)emalloc(sizeof(Log));
117
118 if(printmode == CK_ENV)
119 {
120 printmode = get_env_printmode();
121 }
122
123 l->lfile = lfile;
124 l->lfun = lfun;
125 l->close = close;
126 l->mode = printmode;
127 check_list_add_end(sr->loglst, l);
128 return;
129 }
130
log_srunner_start(SRunner * sr)131 void log_srunner_start(SRunner * sr)
132 {
133 srunner_send_evt(sr, NULL, CLSTART_SR);
134 }
135
log_srunner_end(SRunner * sr)136 void log_srunner_end(SRunner * sr)
137 {
138 srunner_send_evt(sr, NULL, CLEND_SR);
139 }
140
log_suite_start(SRunner * sr,Suite * s)141 void log_suite_start(SRunner * sr, Suite * s)
142 {
143 srunner_send_evt(sr, s, CLSTART_S);
144 }
145
log_suite_end(SRunner * sr,Suite * s)146 void log_suite_end(SRunner * sr, Suite * s)
147 {
148 srunner_send_evt(sr, s, CLEND_S);
149 }
150
log_test_start(SRunner * sr,TCase * tc,TF * tfun)151 void log_test_start(SRunner * sr, TCase * tc, TF * tfun)
152 {
153 char buffer[100];
154
155 snprintf(buffer, 99, "%s:%s", tc->name, tfun->ttest->name);
156 srunner_send_evt(sr, buffer, CLSTART_T);
157 }
158
log_test_end(SRunner * sr,TestResult * tr)159 void log_test_end(SRunner * sr, TestResult * tr)
160 {
161 srunner_send_evt(sr, tr, CLEND_T);
162 }
163
srunner_send_evt(SRunner * sr,void * obj,enum cl_event evt)164 static void srunner_send_evt(SRunner * sr, void *obj, enum cl_event evt)
165 {
166 List *l;
167
168 l = sr->loglst;
169 for(check_list_front(l); !check_list_at_end(l); check_list_advance(l))
170 {
171 Log *lg = (Log *)check_list_val(l);
172 fflush(lg->lfile);
173 lg->lfun(sr, lg->lfile, lg->mode, obj, evt);
174 fflush(lg->lfile);
175 }
176 }
177
stdout_lfun(SRunner * sr,FILE * file,enum print_output printmode,void * obj,enum cl_event evt)178 void stdout_lfun(SRunner * sr, FILE * file, enum print_output printmode,
179 void *obj, enum cl_event evt)
180 {
181 Suite *s;
182
183 switch (evt)
184 {
185 case CLINITLOG_SR:
186 break;
187 case CLENDLOG_SR:
188 break;
189 case CLSTART_SR:
190 if(printmode > CK_SILENT)
191 {
192 fprintf(file, "Running suite(s):");
193 }
194 break;
195 case CLSTART_S:
196 s = (Suite *)obj;
197 if(printmode > CK_SILENT)
198 {
199 fprintf(file, " %s\n", s->name);
200 }
201 break;
202 case CLEND_SR:
203 if(printmode > CK_SILENT)
204 {
205 /* we don't want a newline before printing here, newlines should
206 come after printing a string, not before. it's better to add
207 the newline above in CLSTART_S.
208 */
209 srunner_fprint(file, sr, printmode);
210 }
211 break;
212 case CLEND_S:
213 break;
214 case CLSTART_T:
215 break;
216 case CLEND_T:
217 break;
218 default:
219 eprintf("Bad event type received in stdout_lfun", __FILE__,
220 __LINE__);
221 }
222
223
224 }
225
lfile_lfun(SRunner * sr,FILE * file,enum print_output printmode CK_ATTRIBUTE_UNUSED,void * obj,enum cl_event evt)226 void lfile_lfun(SRunner * sr, FILE * file,
227 enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj,
228 enum cl_event evt)
229 {
230 TestResult *tr;
231 Suite *s;
232
233 switch (evt)
234 {
235 case CLINITLOG_SR:
236 break;
237 case CLENDLOG_SR:
238 break;
239 case CLSTART_SR:
240 break;
241 case CLSTART_S:
242 s = (Suite *)obj;
243 fprintf(file, "Running suite %s\n", s->name);
244 break;
245 case CLEND_SR:
246 fprintf(file, "Results for all suites run:\n");
247 srunner_fprint(file, sr, CK_MINIMAL);
248 break;
249 case CLEND_S:
250 break;
251 case CLSTART_T:
252 break;
253 case CLEND_T:
254 tr = (TestResult *)obj;
255 tr_fprint(file, tr, CK_VERBOSE);
256 break;
257 default:
258 eprintf("Bad event type received in lfile_lfun", __FILE__,
259 __LINE__);
260 }
261
262
263 }
264
xml_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED,FILE * file,enum print_output printmode CK_ATTRIBUTE_UNUSED,void * obj,enum cl_event evt)265 void xml_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file,
266 enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj,
267 enum cl_event evt)
268 {
269 TestResult *tr;
270 Suite *s;
271 static struct timespec ts_start = { 0, 0 };
272 static char t[sizeof "yyyy-mm-dd hh:mm:ss"] = { 0 };
273
274 if(t[0] == 0)
275 {
276 struct timeval inittv;
277 struct tm now;
278
279 gettimeofday(&inittv, NULL);
280 clock_gettime(check_get_clockid(), &ts_start);
281 if(localtime_r((const time_t *)&(inittv.tv_sec), &now) != NULL)
282 {
283 strftime(t, sizeof("yyyy-mm-dd hh:mm:ss"), "%Y-%m-%d %H:%M:%S",
284 &now);
285 }
286 }
287
288 switch (evt)
289 {
290 case CLINITLOG_SR:
291 fprintf(file,
292 "<?xml version=\"1.0\"?>\n"
293 "<?xml-stylesheet type=\"text/xsl\" "
294 "href=\"http://check.sourceforge.net/xml/check_unittest.xslt\"?>\n"
295 "<testsuites xmlns=\"http://check.sourceforge.net/ns\">\n"
296 " <datetime>%s</datetime>\n", t);
297 break;
298 case CLENDLOG_SR:
299 {
300 struct timespec ts_end = { 0, 0 };
301 unsigned long duration;
302
303 /* calculate time the test were running */
304 clock_gettime(check_get_clockid(), &ts_end);
305 duration = (unsigned long)DIFF_IN_USEC(ts_start, ts_end);
306 fprintf(file,
307 " <duration>%lu.%06lu</duration>\n"
308 "</testsuites>\n",
309 duration / US_PER_SEC, duration % US_PER_SEC);
310 }
311 break;
312 case CLSTART_SR:
313 break;
314 case CLSTART_S:
315 s = (Suite *)obj;
316 fprintf(file,
317 " <suite>\n"
318 " <title>");
319 fprint_xml_esc(file, s->name);
320 fprintf(file, "</title>\n");
321 break;
322 case CLEND_SR:
323 break;
324 case CLEND_S:
325 fprintf(file, " </suite>\n");
326 break;
327 case CLSTART_T:
328 break;
329 case CLEND_T:
330 tr = (TestResult *)obj;
331 tr_xmlprint(file, tr, CK_VERBOSE);
332 break;
333 default:
334 eprintf("Bad event type received in xml_lfun", __FILE__,
335 __LINE__);
336 }
337
338 }
339
tap_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED,FILE * file,enum print_output printmode CK_ATTRIBUTE_UNUSED,void * obj,enum cl_event evt)340 void tap_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file,
341 enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj,
342 enum cl_event evt)
343 {
344 TestResult *tr;
345
346 static int num_tests_run = 0;
347
348 switch (evt)
349 {
350 case CLINITLOG_SR:
351 /* As this is a new log file, reset the number of tests executed */
352 num_tests_run = 0;
353 break;
354 case CLENDLOG_SR:
355 /* Output the test plan as the last line */
356 fprintf(file, "1..%d\n", num_tests_run);
357 fflush(file);
358 break;
359 case CLSTART_SR:
360 break;
361 case CLSTART_S:
362 break;
363 case CLEND_SR:
364 break;
365 case CLEND_S:
366 break;
367 case CLSTART_T:
368 break;
369 case CLEND_T:
370 /* Print the test result to the tap file */
371 num_tests_run += 1;
372 tr = (TestResult *)obj;
373 fprintf(file, "%s %d - %s:%s:%s: %s\n",
374 tr->rtype == CK_PASS ? "ok" : "not ok", num_tests_run,
375 tr->file, tr->tcname, tr->tname, tr->msg);
376 fflush(file);
377 break;
378 default:
379 eprintf("Bad event type received in tap_lfun", __FILE__,
380 __LINE__);
381 }
382 }
383
384 #if ENABLE_SUBUNIT
subunit_lfun(SRunner * sr,FILE * file,enum print_output printmode,void * obj,enum cl_event evt)385 void subunit_lfun(SRunner * sr, FILE * file, enum print_output printmode,
386 void *obj, enum cl_event evt)
387 {
388 TestResult *tr;
389 char const *name;
390
391 /* assert(printmode == CK_SUBUNIT); */
392
393 switch (evt)
394 {
395 case CLINITLOG_SR:
396 break;
397 case CLENDLOG_SR:
398 break;
399 case CLSTART_SR:
400 break;
401 case CLSTART_S:
402 break;
403 case CLEND_SR:
404 if(printmode > CK_SILENT)
405 {
406 fprintf(file, "\n");
407 srunner_fprint(file, sr, printmode);
408 }
409 break;
410 case CLEND_S:
411 break;
412 case CLSTART_T:
413 name = (const char *)obj;
414 subunit_test_start(name);
415 break;
416 case CLEND_T:
417 tr = (TestResult *)obj;
418 {
419 char *name = ck_strdup_printf("%s:%s", tr->tcname, tr->tname);
420 char *msg = tr_short_str(tr);
421
422 switch (tr->rtype)
423 {
424 case CK_PASS:
425 subunit_test_pass(name);
426 break;
427 case CK_FAILURE:
428 subunit_test_fail(name, msg);
429 break;
430 case CK_ERROR:
431 subunit_test_error(name, msg);
432 break;
433 case CK_TEST_RESULT_INVALID:
434 default:
435 eprintf("Bad result type in subunit_lfun", __FILE__,
436 __LINE__);
437 free(name);
438 free(msg);
439 }
440 }
441 break;
442 default:
443 eprintf("Bad event type received in subunit_lfun", __FILE__,
444 __LINE__);
445 }
446 }
447 #endif
448
srunner_open_file(const char * filename)449 static FILE *srunner_open_file(const char *filename)
450 {
451 FILE *f = NULL;
452
453 if(strcmp(filename, STDOUT_OVERRIDE_LOG_FILE_NAME) == 0)
454 {
455 f = stdout;
456 }
457 else
458 {
459 f = fopen(filename, "w");
460 if(f == NULL)
461 {
462 eprintf("Error in call to fopen while opening file %s:", __FILE__,
463 __LINE__ - 2, filename);
464 }
465 }
466 return f;
467 }
468
srunner_open_lfile(SRunner * sr)469 FILE *srunner_open_lfile(SRunner * sr)
470 {
471 FILE *f = NULL;
472
473 if(srunner_has_log(sr))
474 {
475 f = srunner_open_file(srunner_log_fname(sr));
476 }
477 return f;
478 }
479
srunner_open_xmlfile(SRunner * sr)480 FILE *srunner_open_xmlfile(SRunner * sr)
481 {
482 FILE *f = NULL;
483
484 if(srunner_has_xml(sr))
485 {
486 f = srunner_open_file(srunner_xml_fname(sr));
487 }
488 return f;
489 }
490
srunner_open_tapfile(SRunner * sr)491 FILE *srunner_open_tapfile(SRunner * sr)
492 {
493 FILE *f = NULL;
494
495 if(srunner_has_tap(sr))
496 {
497 f = srunner_open_file(srunner_tap_fname(sr));
498 }
499 return f;
500 }
501
srunner_init_logging(SRunner * sr,enum print_output print_mode)502 void srunner_init_logging(SRunner * sr, enum print_output print_mode)
503 {
504 FILE *f;
505
506 sr->loglst = check_list_create();
507 #if ENABLE_SUBUNIT
508 if(print_mode != CK_SUBUNIT)
509 #endif
510 srunner_register_lfun(sr, stdout, 0, stdout_lfun, print_mode);
511 #if ENABLE_SUBUNIT
512 else
513 srunner_register_lfun(sr, stdout, 0, subunit_lfun, print_mode);
514 #endif
515 f = srunner_open_lfile(sr);
516 if(f)
517 {
518 srunner_register_lfun(sr, f, f != stdout, lfile_lfun, print_mode);
519 }
520 f = srunner_open_xmlfile(sr);
521 if(f)
522 {
523 srunner_register_lfun(sr, f, f != stdout, xml_lfun, print_mode);
524 }
525 f = srunner_open_tapfile(sr);
526 if(f)
527 {
528 srunner_register_lfun(sr, f, f != stdout, tap_lfun, print_mode);
529 }
530 srunner_send_evt(sr, NULL, CLINITLOG_SR);
531 }
532
srunner_end_logging(SRunner * sr)533 void srunner_end_logging(SRunner * sr)
534 {
535 List *l;
536 int rval;
537
538 srunner_send_evt(sr, NULL, CLENDLOG_SR);
539
540 l = sr->loglst;
541 for(check_list_front(l); !check_list_at_end(l); check_list_advance(l))
542 {
543 Log *lg = (Log *)check_list_val(l);
544
545 if(lg->close)
546 {
547 rval = fclose(lg->lfile);
548 if(rval != 0)
549 eprintf("Error in call to fclose while closing log file:",
550 __FILE__, __LINE__ - 2);
551 }
552 free(lg);
553 }
554 check_list_free(l);
555 sr->loglst = NULL;
556 }
557