3 * Timeout extension for test vector framework
5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
35 #include <sys/types.h>
40 /*----- Main code ---------------------------------------------------------*/
42 static void reset(struct tvec_timeoutctx *tc)
44 const struct tvec_timeoutenv *te = tc->te;
46 tc->timer = te->timer; tc->t = te->t; tc->f &= ~TVTF_SETMASK;
49 /* --- @tvec_timeoutsetup@ --- *
51 * Arguments: @struct tvec_state *tv@ = test vector state
52 * @const struct tvec_env *env@ = environment description
53 * @void *pctx@ = parent context (ignored)
54 * @void *ctx@ = context pointer to initialize
58 * Use: Initialize a timeout environment context.
60 * The environment description should be a @struct
64 void tvec_timeoutsetup(struct tvec_state *tv, const struct tvec_env *env,
65 void *pctx, void *ctx)
67 struct tvec_timeoutctx *tc = ctx;
68 const struct tvec_timeoutenv *te = (struct tvec_timeoutenv *)env;
69 const struct tvec_env *subenv = te->env;
76 if (subenv && subenv->ctxsz) tc->subctx = xmalloc(subenv->ctxsz);
77 if (subenv && subenv->setup) subenv->setup(tv, subenv, tc, tc->subctx);
80 /* --- @tvec_timeoutset@ --- *
82 * Arguments: @struct tvec_state *tv@ = test vector state
83 * @const char *var@ = variable name to set
84 * @void *ctx@ = context pointer
86 * Returns: Zero on success, @-1@ on failure.
88 * Use: Set a special variable. The following special variables are
91 * * %|@timeout|% is the number of seconds (or other unit) to
92 * wait before giving up and killing the test process. The
93 * string may also include a keyword %|REAL|%, %|VIRTUAL|%,
94 * or %|PROF|% to select the timer.
96 * Unrecognized variables are passed to the subordinate
97 * environment, if there is one.
100 int tvec_timeoutset(struct tvec_state *tv, const char *var, void *ctx)
102 struct tvec_timeoutctx *tc = ctx;
103 const struct tvec_timeoutenv *te = tc->te;
104 const struct tvec_env *subenv = te->env;
106 double t = 0.0; unsigned tmr = 0;
107 const char *p; char *q; size_t pos;
112 #define f_all (f_time | f_timer)
114 if (STRCMP(var, ==, "@timeout")) {
115 if (tc->f&TVTF_SETTMO) { rc = tvec_dupreg(tv, var); goto end; }
118 DRESET(&d); if (tvec_readword(tv, &d, ";", 0)) break;
121 if (!(f&f_timer) && STRCMP(p, ==, "REAL"))
122 { tmr = ITIMER_REAL; f |= f_timer; }
123 else if (!(f&f_timer) && STRCMP(p, ==, "VIRTUAL"))
124 { tmr = ITIMER_VIRTUAL; f |= f_timer; }
125 else if (!(f&f_timer) && STRCMP(p, ==, "PROF"))
126 { tmr = ITIMER_PROF; f |= f_timer; }
128 else if (!(f&f_time)) {
129 if (*p == '+' || *p == '-') p++;
132 tvec_syntax(tv, *d.buf, "floating-point number");
135 errno = 0; t = strtod(p, &q); f |= f_time;
137 tvec_error(tv, "invalid floating-point number `%s': %s",
138 d.buf, strerror(errno));
143 tvec_skipspc(tv); pos = d.len;
144 if (!tvec_readword(tv, &d, ";", 0)) pos++;
148 if (!*q || STRCMP(q, ==, "s") || STRCMP(q, ==, "sec"))
151 else if (STRCMP(q, ==, "ds")) t *= 1e-1;
152 else if (STRCMP(q, ==, "cs")) t *= 1e-2;
153 else if (STRCMP(q, ==, "ms")) t *= 1e-3;
154 else if (STRCMP(q, ==, "us") || STRCMP(q, ==, "µs")) t *= 1e-6;
155 else if (STRCMP(q, ==, "ns")) t *= 1e-9;
156 else if (STRCMP(q, ==, "ps")) t *= 1e-12;
157 else if (STRCMP(q, ==, "fs")) t *= 1e-15;
158 else if (STRCMP(q, ==, "as")) t *= 1e-18;
159 else if (STRCMP(q, ==, "zs")) t *= 1e-21;
160 else if (STRCMP(q, ==, "ys")) t *= 1e-24;
162 else if (STRCMP(q, ==, "das")) t *= 1e+1;
163 else if (STRCMP(q, ==, "hs")) t *= 1e+2;
164 else if (STRCMP(q, ==, "ks")) t *= 1e+3;
165 else if (STRCMP(q, ==, "Ms")) t *= 1e+6;
166 else if (STRCMP(q, ==, "Gs")) t *= 1e+9;
167 else if (STRCMP(q, ==, "Ts")) t *= 1e+12;
168 else if (STRCMP(q, ==, "Ps")) t *= 1e+15;
169 else if (STRCMP(q, ==, "Es")) t *= 1e+18;
170 else if (STRCMP(q, ==, "Zs")) t *= 1e+21;
171 else if (STRCMP(q, ==, "Ys")) t *= 1e+24;
173 else if (STRCMP(q, ==, "m") || STRCMP(q, ==, "min")) t *= 60;
174 else if (STRCMP(q, ==, "h") || STRCMP(q, ==, "hr")) t *= 3600;
175 else if (STRCMP(q, ==, "d") || STRCMP(q, ==, "dy")) t *= 86400;
176 else if (STRCMP(q, ==, "y") || STRCMP(q, ==, "yr")) t *= 31557600;
180 { rc = tvec_syntax(tv, *q, "end-of-line"); goto end; }
181 pos = q - d.buf; d.len -= pos;
182 memmove(d.buf, q, d.len);
187 if (!(~f&f_all)) break;
191 rc = tvec_syntax(tv, fgetc(tv->fp), "timeout or timer keyword");
194 rc = tvec_flushtoeol(tv, 0); if (rc) goto end;
195 if (f&f_time) tc->t = t;
196 if (f&f_timer) tc->timer = tmr;
197 tc->f |= TVTF_SETTMO;
199 } else if (subenv && subenv->set)
200 rc = subenv->set(tv, var, tc->subctx);
213 /* --- @tvec_timeoutbefore@ --- *
215 * Arguments: @struct tvec_state *tv@ = test vector state
216 * @void *ctx@ = context pointer
220 * Use: Invoke the subordinate environment's @before@ function to
221 * prepare for the test.
224 void tvec_timeoutbefore(struct tvec_state *tv, void *ctx)
226 struct tvec_timeoutctx *tc = ctx;
227 const struct tvec_timeoutenv *te = tc->te;
228 const struct tvec_env *subenv = te->env;
230 /* Just call the subsidiary environment. */
231 if (subenv && subenv->before) subenv->before(tv, tc->subctx);
234 /* --- @tvec_timeoutafter@ --- *
236 * Arguments: @struct tvec_state *tv@ = test vector state
237 * @void *ctx@ = context pointer
241 * Use: Invoke the subordinate environment's @after@ function to
242 * clean up after the test.
245 void tvec_timeoutafter(struct tvec_state *tv, void *ctx)
247 struct tvec_timeoutctx *tc = ctx;
248 const struct tvec_timeoutenv *te = tc->te;
249 const struct tvec_env *subenv = te->env;
251 /* Reset variables. */
254 /* Pass the call on to the subsidiary environment. */
255 if (subenv && subenv->after) subenv->after(tv, tc->subctx);
258 /* --- @tvec_timeoutteardown@ --- *
260 * Arguments: @struct tvec_state *tv@ = test vector state
261 * @void *ctx@ = context pointer
265 * Use: Tear down the timeoutmark environment.
268 void tvec_timeoutteardown(struct tvec_state *tv, void *ctx)
270 struct tvec_timeoutctx *tc = ctx;
271 const struct tvec_timeoutenv *te = tc->te;
272 const struct tvec_env *subenv = te->env;
274 /* Just call the subsidiary environment. */
275 if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx);
278 /* --- @tvec_timeoutrun@ --- *
280 * Arguments: @struct tvec_state *tv@ = test vector state
281 * @tvec_testfn *fn@ = test function to run
282 * @void *ctx@ = context pointer for the test function
286 * Use: Runs a test with a timeout attached.
289 void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
291 struct tvec_timeoutctx *tc = ctx;
292 const struct tvec_timeoutenv *te = tc->te;
293 const struct tvec_env *subenv = te->env;
294 struct itimerval itv;
296 itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
297 itv.it_value.tv_sec = tc->t;
298 itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6;
300 if (setitimer(tc->timer, &itv, 0))
301 tvec_skip(tv, "failed to set interval timer: %s", strerror(errno));
303 if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx);
304 else fn(tv->in, tv->out, tc->subctx);
306 itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
307 itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0;
308 if (setitimer(tc->timer, &itv, 0))
309 tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno));
313 /*----- That's all, folks -------------------------------------------------*/