Commit | Line | Data |
---|---|---|
31d0247c MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Timeout extension for test vector framework | |
4 | * | |
5 | * (c) 2024 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the mLib utilities library. | |
11 | * | |
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. | |
16 | * | |
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. | |
21 | * | |
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, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | /*----- Header files ------------------------------------------------------*/ | |
29 | ||
30 | #include <ctype.h> | |
31 | #include <errno.h> | |
32 | #include <string.h> | |
33 | ||
34 | #include <sys/time.h> | |
35 | #include <sys/types.h> | |
36 | #include <unistd.h> | |
37 | ||
38 | #include "tvec.h" | |
39 | ||
40 | /*----- Main code ---------------------------------------------------------*/ | |
41 | ||
42 | static void reset(struct tvec_timeoutctx *tc) | |
43 | { | |
44 | const struct tvec_timeoutenv *te = tc->te; | |
45 | ||
46 | tc->timer = te->timer; tc->t = te->t; tc->f &= ~TVTF_SETMASK; | |
47 | } | |
48 | ||
49 | /* --- @tvec_timeoutsetup@ --- * | |
50 | * | |
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 | |
55 | * | |
56 | * Returns: --- | |
57 | * | |
58 | * Use: Initialize a timeout environment context. | |
59 | * | |
60 | * The environment description should be a @struct | |
61 | * tvec_timeoutenv@. | |
62 | */ | |
63 | ||
64 | void tvec_timeoutsetup(struct tvec_state *tv, const struct tvec_env *env, | |
65 | void *pctx, void *ctx) | |
66 | { | |
67 | struct tvec_timeoutctx *tc = ctx; | |
68 | const struct tvec_timeoutenv *te = (struct tvec_timeoutenv *)env; | |
69 | const struct tvec_env *subenv = te->env; | |
70 | ||
71 | tc->te = te; | |
72 | ||
73 | reset(tc); | |
74 | ||
75 | tc->subctx = 0; | |
76 | if (subenv && subenv->ctxsz) tc->subctx = xmalloc(subenv->ctxsz); | |
77 | if (subenv && subenv->setup) subenv->setup(tv, subenv, tc, tc->subctx); | |
78 | } | |
79 | ||
814e42ff | 80 | /* --- @tvec_timeoutfindvar@, @setvar@ --- * |
31d0247c MW |
81 | * |
82 | * Arguments: @struct tvec_state *tv@ = test vector state | |
83 | * @const char *var@ = variable name to set | |
814e42ff MW |
84 | * @const union tvec_regval *rv@ = register value |
85 | * @void **ctx_out@ = where to put the @setvar@ context | |
31d0247c MW |
86 | * @void *ctx@ = context pointer |
87 | * | |
814e42ff MW |
88 | * Returns: @tvec_timeoutfindvar@ returns a pointer to the variable |
89 | * definition, or null; @setvar@ returns zero on success or | |
90 | * %$-1$% on error. | |
31d0247c | 91 | * |
814e42ff MW |
92 | * Use: Find a definition for a special variable. The following |
93 | * special variables are supported. | |
31d0247c | 94 | * |
814e42ff MW |
95 | * * %|@timeout|% is the duration to wait before killing the |
96 | * process. | |
97 | * | |
98 | * * %|@timer|% is the timer to use to measure the duration. | |
31d0247c MW |
99 | * |
100 | * Unrecognized variables are passed to the subordinate | |
101 | * environment, if there is one. | |
102 | */ | |
103 | ||
814e42ff MW |
104 | static int setvar(struct tvec_state *tv, const char *var, |
105 | const union tvec_regval *rv, void *ctx) | |
31d0247c MW |
106 | { |
107 | struct tvec_timeoutctx *tc = ctx; | |
31d0247c MW |
108 | |
109 | if (STRCMP(var, ==, "@timeout")) { | |
814e42ff MW |
110 | if (tc->f&TVTF_SETTMO) return (tvec_dupreg(tv, var)); |
111 | tc->t = rv->f; tc->f |= TVTF_SETTMO; | |
112 | } else if (STRCMP(var, ==, "@timer")) { | |
113 | if (tc->f&TVTF_SETTMR) return (tvec_dupreg(tv, var)); | |
114 | tc->timer = rv->i; tc->f |= TVTF_SETTMR; | |
115 | } else assert(!"unknown var"); | |
116 | return (0); | |
117 | } | |
31d0247c | 118 | |
814e42ff MW |
119 | static const struct tvec_vardef timeout_var = |
120 | { sizeof(struct tvec_reg), setvar, | |
d056fbdf | 121 | { "@timeout", &tvty_duration, -1, 0 } }; |
814e42ff MW |
122 | |
123 | static const struct tvec_iassoc timer_assocs[] = { | |
124 | { "REAL", ITIMER_REAL }, | |
125 | { "VIRTUAL", ITIMER_VIRTUAL }, | |
126 | { "PROF", ITIMER_PROF }, | |
127 | TVEC_ENDENUM | |
128 | }; | |
129 | static const struct tvec_ienuminfo timer_enum = | |
130 | { "interval-timer", timer_assocs, &tvrange_int }; | |
131 | static const struct tvec_vardef timer_var = | |
132 | { sizeof(struct tvec_reg), setvar, | |
d056fbdf | 133 | { "@timer", &tvty_ienum, -1, 0, { &timer_enum } } }; |
814e42ff MW |
134 | |
135 | const struct tvec_vardef *tvec_timeoutfindvar | |
136 | (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx) | |
137 | { | |
138 | struct tvec_timeoutctx *tc = ctx; | |
139 | const struct tvec_timeoutenv *te = tc->te; | |
140 | const struct tvec_env *subenv = te->env; | |
31d0247c | 141 | |
814e42ff MW |
142 | if (STRCMP(var, ==, "@timeout")) { *ctx_out = tc; return (&timeout_var); } |
143 | else if (STRCMP(var, ==, "@timer")) { *ctx_out = tc; return (&timer_var); } | |
144 | else if (subenv && subenv->findvar) | |
145 | return (subenv->findvar(tv, var, ctx_out, tc->subctx)); | |
146 | else return (0); | |
31d0247c MW |
147 | } |
148 | ||
149 | /* --- @tvec_timeoutbefore@ --- * | |
150 | * | |
151 | * Arguments: @struct tvec_state *tv@ = test vector state | |
152 | * @void *ctx@ = context pointer | |
153 | * | |
154 | * Returns: --- | |
155 | * | |
156 | * Use: Invoke the subordinate environment's @before@ function to | |
157 | * prepare for the test. | |
158 | */ | |
159 | ||
160 | void tvec_timeoutbefore(struct tvec_state *tv, void *ctx) | |
161 | { | |
162 | struct tvec_timeoutctx *tc = ctx; | |
163 | const struct tvec_timeoutenv *te = tc->te; | |
164 | const struct tvec_env *subenv = te->env; | |
165 | ||
31d0247c MW |
166 | if (subenv && subenv->before) subenv->before(tv, tc->subctx); |
167 | } | |
168 | ||
c81c35df | 169 | /* --- @tvec_timeoutrun@ --- * |
31d0247c MW |
170 | * |
171 | * Arguments: @struct tvec_state *tv@ = test vector state | |
c81c35df MW |
172 | * @tvec_testfn *fn@ = test function to run |
173 | * @void *ctx@ = context pointer for the test function | |
31d0247c MW |
174 | * |
175 | * Returns: --- | |
176 | * | |
c81c35df | 177 | * Use: Runs a test with a timeout attached. |
31d0247c MW |
178 | */ |
179 | ||
c81c35df | 180 | void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx) |
31d0247c MW |
181 | { |
182 | struct tvec_timeoutctx *tc = ctx; | |
183 | const struct tvec_timeoutenv *te = tc->te; | |
184 | const struct tvec_env *subenv = te->env; | |
c81c35df | 185 | struct itimerval itv; |
31d0247c | 186 | |
c81c35df MW |
187 | itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; |
188 | itv.it_value.tv_sec = tc->t; | |
189 | itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6; | |
31d0247c | 190 | |
c81c35df MW |
191 | if (setitimer(tc->timer, &itv, 0)) |
192 | tvec_skip(tv, "failed to set interval timer: %s", strerror(errno)); | |
193 | else { | |
194 | if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx); | |
c4ccbbf9 | 195 | else { fn(tv->in, tv->out, tc->subctx); tvec_check(tv, 0); } |
c81c35df MW |
196 | |
197 | itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; | |
198 | itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0; | |
199 | if (setitimer(tc->timer, &itv, 0)) | |
200 | tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno)); | |
201 | } | |
31d0247c MW |
202 | } |
203 | ||
c81c35df | 204 | /* --- @tvec_timeoutafter@ --- * |
31d0247c MW |
205 | * |
206 | * Arguments: @struct tvec_state *tv@ = test vector state | |
207 | * @void *ctx@ = context pointer | |
208 | * | |
209 | * Returns: --- | |
210 | * | |
c81c35df MW |
211 | * Use: Invoke the subordinate environment's @after@ function to |
212 | * clean up after the test. | |
31d0247c MW |
213 | */ |
214 | ||
c81c35df | 215 | void tvec_timeoutafter(struct tvec_state *tv, void *ctx) |
31d0247c MW |
216 | { |
217 | struct tvec_timeoutctx *tc = ctx; | |
218 | const struct tvec_timeoutenv *te = tc->te; | |
219 | const struct tvec_env *subenv = te->env; | |
220 | ||
c81c35df MW |
221 | /* Reset variables. */ |
222 | reset(tc); | |
223 | ||
224 | /* Pass the call on to the subsidiary environment. */ | |
225 | if (subenv && subenv->after) subenv->after(tv, tc->subctx); | |
31d0247c MW |
226 | } |
227 | ||
c81c35df | 228 | /* --- @tvec_timeoutteardown@ --- * |
31d0247c MW |
229 | * |
230 | * Arguments: @struct tvec_state *tv@ = test vector state | |
c81c35df | 231 | * @void *ctx@ = context pointer |
31d0247c MW |
232 | * |
233 | * Returns: --- | |
234 | * | |
c81c35df | 235 | * Use: Tear down the timeoutmark environment. |
31d0247c MW |
236 | */ |
237 | ||
c81c35df | 238 | void tvec_timeoutteardown(struct tvec_state *tv, void *ctx) |
31d0247c MW |
239 | { |
240 | struct tvec_timeoutctx *tc = ctx; | |
241 | const struct tvec_timeoutenv *te = tc->te; | |
242 | const struct tvec_env *subenv = te->env; | |
31d0247c | 243 | |
c81c35df MW |
244 | /* Just call the subsidiary environment. */ |
245 | if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx); | |
31d0247c MW |
246 | } |
247 | ||
248 | /*----- That's all, folks -------------------------------------------------*/ |