1 /*
2 ** Licensed to the Apache Software Foundation (ASF) under one or more
3 ** contributor license agreements. See the NOTICE file distributed with
4 ** this work for additional information regarding copyright ownership.
5 ** The ASF licenses this file to You under the Apache License, Version 2.0
6 ** (the "License"); you may not use this file except in compliance with
7 ** the License. You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include "apr_file_io.h"
19 #include "at.h"
20 #include "apr_lib.h"
21 #include "apr_strings.h"
22 #include "apr_tables.h"
23 #include "apr_env.h"
24 #include <assert.h>
25
26
at_begin(at_t * t,int total)27 apr_status_t at_begin(at_t *t, int total)
28 {
29 char buf[32];
30 apr_snprintf(buf, 32, "1..%d", total);
31 return at_report(t, buf);
32 }
33
test_cleanup(void * data)34 static apr_status_t test_cleanup(void *data)
35 {
36 at_t *t = data;
37 if (t->current < t->plan)
38 return at_report(t, "Bail out!");
39 else
40 return at_report(t, "END");
41 }
42
at_end(at_t * t)43 void at_end(at_t *t)
44 {
45 apr_pool_cleanup_kill(t->pool, t, test_cleanup);
46 test_cleanup(t);
47 }
48
at_comment(at_t * t,const char * fmt,va_list vp)49 apr_status_t at_comment(at_t *t, const char *fmt, va_list vp)
50 {
51 apr_status_t s;
52 char buf[256], *b = buf + 2;
53 char *end;
54 int rv;
55 rv = apr_vsnprintf(b, 250, fmt, vp);
56
57 if (rv <= 0)
58 return APR_EINVAL;
59
60
61 end = b + rv;
62
63 buf[0] = '#';
64 buf[1] = ' ';
65
66 if (rv == 250) {
67 end[-1] = '.';
68 *end++ = '.';
69 *end++ = '.';
70 *end++ = '\n';
71 *end = 0;
72 }
73 else if (end[-1] != '\n') {
74 *end++ = '\n';
75 *end = 0;
76 }
77
78 b = buf;
79 while (1) {
80 char *eol;
81
82 eol = strchr(b + 2, '\n');
83 *eol = 0;
84 s = at_report(t, b);
85 if (s != APR_SUCCESS || eol == end - 1)
86 break;
87
88 b = eol - 1;
89 b[0] = '#';
90 b[1] = ' ';
91 }
92
93 return s;
94 }
95
at_ok(at_t * t,int is_ok,const char * label,const char * file,int line)96 void at_ok(at_t *t, int is_ok, const char *label, const char *file, int line)
97 {
98 char format[] = "not ok %d - %s # %s (%s:%d) test %d in %s";
99 char *end = format + 10;
100 char *fmt = is_ok ? format + 4 : format;
101 char buf[256];
102 const char *comment = NULL;
103 int rv, is_fatal = 0, is_skip = 0, is_todo = 0;
104
105 t->current++;
106
107 if (*t->fatal == t->current) {
108 t->fatal++;
109 is_fatal = 1;
110 }
111
112 if (*t->skip == t->current) {
113 t->skip++;
114 is_skip = 1;
115 }
116
117 if (*t->todo == t->current) {
118 t->todo++;
119 is_todo = 1;
120 }
121
122 if (AT_FLAG_CONCISE(t->flags))
123 format[9] = '\0';
124 else if (is_ok && !AT_FLAG_TRACE(t->flags))
125 format[14] = '\0';
126 else if (is_fatal && ! is_ok)
127 comment = "fatal";
128 else
129 comment = is_todo ? "todo" : is_skip ? "skip" : "at";
130
131 rv = apr_snprintf(buf, 256, fmt, t->current + t->prior,
132 label, comment, file, line, t->current, t->name);
133
134 if (rv <= 0)
135 exit(-1);
136
137 end = buf + rv;
138
139 if (rv == 250) {
140 *end++ = '.';
141 *end++ = '.';
142 *end++ = '.';
143 *end = '\0';
144 }
145
146 if (memchr(buf, '\n', rv) != NULL || at_report(t, buf) != APR_SUCCESS)
147 exit(-1);
148
149 if (!is_ok && is_fatal) {
150 while (t->current++ < t->plan) {
151 apr_snprintf(buf, 256, "not ok %d # skipped: aborting test %s",
152 t->prior + t->current, t->name);
153 at_report(t, buf);
154 }
155 longjmp(*t->abort, 0);
156 }
157 }
158
159 struct at_report_file {
160 at_report_t module;
161 apr_file_t *file;
162 };
163
164
at_report_file_write(at_report_t * ctx,const char * msg)165 static apr_status_t at_report_file_write(at_report_t *ctx, const char *msg)
166 {
167 struct at_report_file *r = (struct at_report_file *)ctx;
168 apr_file_t *f = r->file;
169 apr_size_t len = strlen(msg);
170 apr_status_t s;
171
172 s = apr_file_write_full(f, msg, len, &len);
173 if (s != APR_SUCCESS)
174 return s;
175
176 s = apr_file_putc('\n', f);
177 if (s != APR_SUCCESS)
178 return s;
179
180 return apr_file_flush(f);
181 }
182
at_report_file_make(apr_pool_t * p,apr_file_t * f)183 at_report_t *at_report_file_make(apr_pool_t *p, apr_file_t *f)
184 {
185 struct at_report_file *r = apr_palloc(p, sizeof *r);
186 r->module.func = at_report_file_write;
187 r->file = f;
188 return &r->module;
189 }
190
191
192
193 struct at_report_local {
194 at_report_t module;
195 at_t *t;
196 at_report_t *saved_report;
197 const int *saved_fatal;
198 int dummy_fatal;
199 const char *file;
200 int line;
201 int passed;
202 apr_pool_t *pool;
203 };
204
205
report_local_cleanup(void * data)206 static apr_status_t report_local_cleanup(void *data)
207 {
208 struct at_report_local *q = data;
209 dAT = q->t;
210 char label[32];
211
212 apr_snprintf(label, 32, "collected %d passing tests", q->passed);
213
214 AT->report = q->saved_report;
215 AT->fatal = q->saved_fatal;
216
217 at_ok(q->t, 1, label, q->file, q->line);
218 return APR_SUCCESS;
219 }
220
221
at_report_local_write(at_report_t * ctx,const char * msg)222 static apr_status_t at_report_local_write(at_report_t *ctx, const char *msg)
223 {
224 char buf[256];
225 struct at_report_local *q = (struct at_report_local *)ctx;
226 dAT = q->t;
227
228 if (strncmp(msg, "not ok", 6) == 0) {
229 q->saved_report->func(q->saved_report, msg);
230 AT->report = q->saved_report;
231 AT->fatal = q->saved_fatal;
232 apr_pool_cleanup_kill(q->pool, q, report_local_cleanup);
233 while (AT->current++ < AT->plan) {
234 apr_snprintf(buf, 256, "not ok %d # skipped: aborting test %s",
235 AT->prior + AT->current, AT->name);
236 at_report(AT, buf);
237 }
238 longjmp(*AT->abort, 0);
239 }
240 else if (strncmp(msg, "ok", 2) == 0) {
241 AT->current--;
242 q->passed++;
243 }
244 return APR_SUCCESS;
245 }
246
at_report_local(at_t * AT,apr_pool_t * p,const char * file,int line)247 void at_report_local(at_t *AT, apr_pool_t *p, const char *file, int line)
248 {
249 struct at_report_local *q = apr_palloc(p, sizeof *q);
250 q->module.func = at_report_local_write;
251 q->t = AT;
252 q->saved_report = AT->report;
253 q->saved_fatal = AT->fatal;
254 q->dummy_fatal = 0;
255 q->file = apr_pstrdup(p, file);
256 q->line = line;
257 q->passed = 0;
258 q->pool = p;
259
260 AT->fatal = &q->dummy_fatal;
261 AT->report = &q->module;
262
263 if (*q->saved_fatal == AT->current + 1)
264 q->saved_fatal++;
265
266 apr_pool_cleanup_register(p, q, report_local_cleanup,
267 report_local_cleanup);
268 }
269
270
at_create(apr_pool_t * pool,unsigned char flags,at_report_t * report)271 at_t *at_create(apr_pool_t *pool, unsigned char flags, at_report_t *report)
272 {
273 at_t *t = apr_pcalloc(pool, sizeof *t);
274 t->flags = flags;
275 t->report = report;
276 t->pool = pool;
277
278 apr_pool_cleanup_register(pool, t, test_cleanup, test_cleanup);
279 return t;
280 }
281
282
283 #define AT_NELTS 4
284
at_list(apr_pool_t * pool,const char * spec,int * list)285 static int* at_list(apr_pool_t *pool, const char *spec, int *list)
286 {
287 apr_array_header_t arr;
288 int prev, current = 0;
289
290 arr.pool = pool;
291 arr.elt_size = sizeof *list;
292 arr.nelts = 0;
293 arr.nalloc = AT_NELTS;
294 arr.elts = (char *)list;
295
296 do {
297 while (*spec && !apr_isdigit(*spec))
298 ++spec;
299
300 prev = current;
301 current = (int)apr_strtoi64(spec, (char **)(void *)&spec, 10);
302 *(int *)apr_array_push(&arr) = current;
303
304 } while (prev >= current);
305
306 return (int *)arr.elts;
307 }
308
309
310
at_run(at_t * AT,const at_test_t * test)311 apr_status_t at_run(at_t *AT, const at_test_t *test)
312 {
313 int dummy = 0, fbuf[AT_NELTS], sbuf[AT_NELTS], tbuf[AT_NELTS];
314 jmp_buf j;
315
316 AT->current = 0;
317 AT->prior += AT->plan;
318 AT->name = test->name;
319 AT->plan = test->plan;
320
321 if (test->fatals)
322 AT->fatal = at_list(AT->pool, test->fatals, fbuf);
323 else
324 AT->fatal = &dummy;
325
326 if (test->skips)
327 AT->skip = at_list(AT->pool, test->skips, sbuf);
328 else
329 AT->skip = &dummy;
330
331 if (test->todos)
332 AT->todo = at_list(AT->pool, test->todos, tbuf);
333 else
334 AT->todo = &dummy;
335
336 AT->abort = &j;
337 if (setjmp(j) == 0) {
338 test->func(AT);
339 return APR_SUCCESS;
340 }
341 AT->abort = NULL;
342 return APR_EGENERAL;
343 }
344