-
Notifications
You must be signed in to change notification settings - Fork 1
/
c2unit.c
407 lines (357 loc) · 12 KB
/
c2unit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
/*
c2unit.c - Chul-Woong Yang (cwyang@gmail.com)
Yet another C unit testing framework, ChulWoong's CUnit
*/
#ifdef __MCT
#include <mct.h>
#else
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#endif
#include "c2unit.h"
#define DFL_HASH_SIZE 2011 // prime number
static struct c2_si_ent c2_end_marker __section(".c2") __aligned(c2_alignof(struct c2_si_ent));
static struct c2_list_head func_head, test_head;
static struct c2_list_head *func_hash, *test_hash;
static unsigned hash_size;
struct c2_stat __c2_prog_stat;
static unsigned int hashval(char *name, char *file, char *path)
{
const unsigned int shift = 6;
const int mask = ~0U << (sizeof(int) * 8 - shift);
unsigned int val;
// we neglect file, path for now
for (val = 0; *name != 0; ++name)
val = (val & mask) ^ (val << shift) ^ *name;
return val;
}
static void c2_func_ins(struct c2_si_ent *ent)
{
struct c2_func_wrap *x = (struct c2_func_wrap *) ent->ptr;
struct c2_func *f = x->begin;
f->line = x->end_line - f->line; // function lines
f->has_test = 0;
init_c2_list_head(&f->link);
init_c2_list_head(&f->hash_link);
c2_list_add_tail(&f->link, &func_head);
c2_list_add_tail(&f->hash_link,
&func_hash[hashval(f->name,f->file,f->path) %
hash_size]);
__c2_prog_stat.nr_func++;
}
/* if the base portion filename end with _test, cut it off */
#define TRIM_PATTERN "_test."
static void trim_filename(char *filename)
{
char *d, *s;
if ((d = strstr(filename, TRIM_PATTERN)) == NULL)
return;
for (s = d + strlen(TRIM_PATTERN) - 1; *s != '\0'; s++, d++)
*d = *s;
*d = '\0';
}
static void c2_test_ins(struct c2_si_ent *ent)
{
struct c2_test_wrap *x = (struct c2_test_wrap *) ent->ptr;
struct c2_test *f = x->begin;
f->testfn = x->testfn;
f->file = strdup(f->file); // intentional leak
trim_filename(f->file);
f->has_func = 0;
init_c2_list_head(&f->link);
init_c2_list_head(&f->hash_link);
c2_list_add_tail(&f->link, &test_head);
c2_list_add_tail(&f->hash_link,
&test_hash[hashval(f->name,f->file,f->path) %
hash_size]);
__c2_prog_stat.nr_test++;
}
static int find_test(char *name, char *file, char *path)
{
int val = 0;
struct c2_test *t;
struct c2_list_head *le, *lh = &test_hash[hashval(name, file, path)
% hash_size ];
c2_list_for_each(le, lh) {
t = c2_list_entry(le, struct c2_test, hash_link);
if (!strcmp(name,t->name) &&
!strcmp(file,t->file) &&
!strcmp(path,t->path))
val++;
}
return val;
}
static int find_func(char *name, char *file, char *path)
{
int val = 0;
struct c2_func *f;
struct c2_list_head *le, *lh = &func_hash[hashval(name, file, path)
% hash_size ];
c2_list_for_each(le, lh) {
f = c2_list_entry(le, struct c2_func, hash_link);
if (!strcmp(name,f->name) &&
!strcmp(file,f->file) &&
!strcmp(path,f->path))
val++;
}
return val;
}
static void c2_match_test_func(void)
{
struct c2_func *f;
struct c2_test *t;
struct c2_list_head *le;
c2_list_for_each(le, &func_head) {
f = c2_list_entry(le, struct c2_func, link);
f->has_test = find_test(f->name, f->file, f->path);
if (f->has_test)
__c2_prog_stat.nr_tested_func++;
}
c2_list_for_each(le, &test_head) {
t = c2_list_entry(le, struct c2_test, link);
t->has_func = find_func(t->name, t->file, t->path);
}
}
static void c2_env_build(struct c2_si_ent *begin, struct c2_si_ent *end)
{
struct c2_si_ent *ent;
for (ent = begin; ent != end; ent++) {
if (ent->type == C2_FUNC)
c2_func_ins(ent);
else
c2_test_ins(ent);
}
c2_match_test_func();
}
// cwyang's heuristics
// Code safety is proportional to the square of code lines.
// Safe code is 2 time more reliable than normal code, and
// dangerous code is 5 time more dangerous than normal code.
//
// returns 0 ~ 1000000
static void calc_score(void)
{
int total, tested, s;
struct c2_func *f;
struct c2_list_head *le;
total = tested = 0;
c2_list_for_each(le, &func_head) {
f = c2_list_entry(le, struct c2_func, link);
s = f->line * f->line;
total += s;
if (f->has_test == 0)
continue;
switch(f->level) {
case DANGER: s /= 5;
case NORMAL: s /= 2;
case SAFE: break;
default: s = 0;
}
tested += s;
}
if (total != 0)
__c2_prog_stat.prog_score = tested * 1000000 / total;
else
__c2_prog_stat.prog_score = 0;
}
static void c2_dump(void)
{
struct c2_func *f;
struct c2_test *t;
struct c2_list_head *le;
printf("<FUNC DUMP>\n");
c2_list_for_each(le, &func_head) {
f = c2_list_entry(le, struct c2_func, link);
printf("%-20s (%s:%-10s) line:%4lu level:%d has_test:%d\n",
f->name, f->path, f->file, f->line, f->level, f->has_test);
}
printf("<TEST DUMP>\n");
c2_list_for_each(le, &test_head) {
t = c2_list_entry(le, struct c2_test, link);
printf("[%02d] %-20s (%s:%-10s) pri:%d has_func:%d desc:%s\n",
t->no, t->name, t->path, t->file, t->pri, t->has_func, t->desc);
}
}
static void hash_init(void)
{
int i;
hash_size = DFL_HASH_SIZE;
func_hash = malloc(hash_size * sizeof(struct c2_list_head));
test_hash = malloc(hash_size * sizeof(struct c2_list_head));
if (!func_hash || !test_hash) {
c2_panic("failed to allocate initial hash table");
}
for (i = 0; i < hash_size; i++) {
init_c2_list_head(&func_hash[i]);
init_c2_list_head(&test_hash[i]);
}
}
static void stat_init(void)
{
__c2_prog_stat.nr_func = 0;
__c2_prog_stat.nr_test = 0;
__c2_prog_stat.nr_tested_func = 0;
__c2_prog_stat.nr_assert = 0;
__c2_prog_stat.pass_assert = 0;
__c2_prog_stat.pass_test = 0;
__c2_prog_stat.prog_score = 0;
__c2_prog_stat.test_pri = 1;
__c2_prog_stat.test_dump_core = 0;
__c2_prog_stat.test_dump_info = 0;
__c2_prog_stat.test_verbose = 0;
__c2_prog_stat.test_path = "";
__c2_prog_stat.time_max = 0;
__c2_prog_stat.time_total = 0;
}
static void test_usage(void)
{
const char *help =
"Usage: exec_prog <options> \n"
" \n"
" The options are: \n"
" -P <test_path_prefix> test path prefix \n"
" -p <test_priority> test priority (1~3) \n"
" -d dump test and function information and exit \n"
" -c dump core when assert fails \n"
" -v be verbose \n"
" \n";
fprintf(stderr, "%s", help);
}
static void parse_arg(int argc, char *argv[])
{
struct c2_stat *p = &__c2_prog_stat;
int c;
while ((c = getopt(argc, argv, "P:p:dcvt")) != -1)
switch (c) {
case 'P': p->test_path = optarg; break;
case 'p': p->test_pri = atoi(optarg); break;
case 'c': p->test_dump_core = 1; break;
case 'd': p->test_dump_info = 1; break;
case 'v': p->test_verbose = 1; break;
case 't': break;
default:
test_usage();
__c2_exit(0);
}
}
static void test_init(int argc, char *argv[])
{
extern struct c2_si_ent c2_beg_marker; // from firstlink.c
struct c2_si_ent *start_ent, *end_ent;
hash_init();
stat_init();
parse_arg(argc, argv);
start_ent = &c2_beg_marker + 1;
end_ent = &c2_end_marker;
init_c2_list_head(&func_head);
init_c2_list_head(&test_head);
c2_env_build(start_ent, end_ent);
calc_score();
}
#if 0
// remove trailing zero. i.e. 2100 -> 21
static int trim_int(int n)
{
while (n != 0 && (n / 10) * 10 == n)
n = n / 10;
return n;
}
#endif
static void print_stat(void)
{
struct c2_stat *p = &__c2_prog_stat;
printf("\n<STATISTICS>\n");
printf("Test priority is %d, test path is [%s].\n",
p->test_pri, p->test_path);
printf("%d functions out of %d are test-covered.\n",
p->nr_tested_func, p->nr_func);
printf("%d tests are passed from total %d tests.\n",
p->pass_test, p->nr_test);
printf("%d asserts are passed from total %d asserts.\n",
p->pass_assert, p->nr_assert);
printf("Total duration is %d msec, took %d msec per a test on average.\n",
p->time_total,
p->time_total / p->nr_test);
printf("The longest test took %d msec, %s/%s/%s (%s).\n",
p->time_max,
p->longest_test->name,
p->longest_test->file,
p->longest_test->path,
p->longest_test->desc);
// printf("C2unit Program Score is %d.%d.\n\n",
// p->prog_score / 10000, trim_int(p->prog_score % 10000));
}
static struct timeval tstart, tend;
static void time_start(void)
{
gettimeofday(&tstart, NULL);
}
static unsigned time_end(void)
{
unsigned diff_msec;
gettimeofday(&tend, NULL);
diff_msec = (tend.tv_sec - tstart.tv_sec) * 1000
+ (tend.tv_usec - tstart.tv_usec) / 1000;
return diff_msec;
}
void test_run(int argc, char *argv[])
{
struct c2_test *t;
struct c2_list_head *le;
struct c2_stat *p = &__c2_prog_stat;
int no = 1, path_check = 0;
int msec;
test_init(argc, argv);
if (p->test_dump_info) {
c2_dump();
__c2_exit(0);
}
if (p->test_path && *p->test_path != '\0')
path_check = strlen(p->test_path);
c2_list_for_each(le, &test_head) {
t = c2_list_entry(le, struct c2_test, link);
if (p->test_pri < t->pri)
continue;
if (path_check &&
strncmp(p->test_path, t->path, path_check) != 0)
continue;
if (p->test_verbose) {
char buf[77];
snprintf(buf, 77, "[%03d] %s[%d] in %s [%s:%s].........................................",
no, t->name, t->no, t->file, t->path, t->desc);
buf[76] = '\0';
fprintf(stderr, "%s", buf);
}
time_start();
t->testfn();
msec = time_end();
p->time_total += msec;
if (p->time_max <= msec) {
p->time_max = msec;
p->longest_test = t;
}
if (p->test_verbose)
fprintf(stderr, "OK\n");
p->pass_test++;
no++;
}
print_stat();
}
void __c2_exit(int rc)
{
struct c2_stat *p = &__c2_prog_stat;
if (p->test_dump_core == 0 || rc == 0) {
#ifdef __MCT
die(0);
#else
exit(rc);
#endif
}
#ifdef __MCT
die(1);
#else
kill(getpid(), SIGABRT);
#endif
}