Merge ~dirk.zimoch/epics-base:chfPluginImprovements into epics-base:7.0
- Git
- lp:~dirk.zimoch/epics-base
- chfPluginImprovements
- Merge into 7.0
Status: | Needs review |
---|---|
Proposed branch: | ~dirk.zimoch/epics-base:chfPluginImprovements |
Merge into: | epics-base:7.0 |
Diff against target: |
1138 lines (+412/-142) 9 files modified
documentation/RELEASE_NOTES.md (+16/-1) modules/database/src/ioc/db/chfPlugin.c (+97/-25) modules/database/src/ioc/db/chfPlugin.h (+12/-1) modules/database/src/ioc/db/dbChannel.c (+26/-0) modules/database/src/std/filters/filters.dbd.pod (+13/-5) modules/database/src/std/filters/sync.c (+9/-11) modules/database/test/ioc/db/chfPluginTest.c (+236/-96) modules/database/test/std/filters/decTest.c (+2/-2) modules/database/test/std/filters/tsTest.c (+1/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
EPICS Core Developers | Pending | ||
Review via email:
|
Commit message
Improved channel filter plugin interface
Description of the change
1. Allow string arguments with arbitrarily long strings.
In addition to the chfPluginArgString argument type which works on fixed size char array members, chfPluginArgStr
* If a chfPluginArgStr
* If the same char* member may be provided multiple times (maybe under different names), the old value is freed before a new value is written.
* When the structure is released (before freePvt is called), the allocated strings are automatically freed and the char* member NULLed.
* If the parameter is optional and not provided my the user, the pointer is left untouched, allowing to initialize the char* member with a static string as a default value.
2. Allow short forms for filters with 1 or 0 parameters
* If only 1 parameter is required, {filter:value} can be used instead of {filter:
* If no parameter is requires, {filter} can be used instead of {filter:{}}
3. Documentarion changed: Use JSON5 features in examples.

Dirk Zimoch (dirk.zimoch) wrote : | # |
Additional plan: Implement shortcut for one parameter.
If there is exacly one mandatory parameter, allow a shortcut without using an object.
Instead of: {filter:
allow: {filter:value}

Dirk Zimoch (dirk.zimoch) wrote : | # |
Now the short form works if there is only one parameter or only one parameter is mandatory (which means it is clear which parameter is meant).
Example: The "dec" filter:
Instead of: record.{dec:{n:5}} one can use record.{dec:5}
- ab85813... by Dirk Zimoch
-
typo fixed
- c1b22d0... by Dirk Zimoch
-
allow arbritrary state name length
- 06f8a37... by Dirk Zimoch
-
simplified filter syntax for only one argument

Dirk Zimoch (dirk.zimoch) wrote : | # |
Re-pushed, this time with tests.
- 4395cf4... by Dirk Zimoch
-
simplified filter syntax for no argument

Dirk Zimoch (dirk.zimoch) wrote : | # |
Another short form: If no argument at all is needed then just the filter name is sufficient (no :{} needed). Example: caget -a "record.{ts}"
I know this is not valid JSON5, but it is convenient and intuitive and works.
Can be stacked with other filters without problems: caget -a "record.
The filter is created as if called with {}.

Andrew Johnson (anj) wrote : | # |
Most of this looks great (although I haven't actually tried it).
To answer your first question, answer me this: If someone wrote an out-of-tree filter that has a chfPluginArgString parameter and they compiled their code unmodified after you had redefined the meaning of that parameter, would they find out at compile-time that their code needs to be fixed? If they would, the redefinition is probably fine; if not, you'd need to make it so that their code would not compile.
I'm not sure yet whether I like the extra-short form, although it certainly seems clever. Please convince me that this extra code won't cause any problems, it can't cause crashes if someone feeds it some other non-JSON syntax, what error messages does it generate when fed weird stuff etc. I'm not asking you to take it out, but to justify this approach.
- db29bb0... by Dirk Zimoch
-
check that filter without value is not followed by garbage

Dirk Zimoch (dirk.zimoch) wrote : | # |
I have added a check that the "extra-short form" is not followed by garbage. (And wrote tests for it.) It only accepts , or } now. It never crashed before but had accepted any separator like ; or # and did not check for closing }. That is fixed now.
The error message if } is missing is:
dbChannelCreate: parse error: premature EOF
If garbarge follows instead or a comma, the error is:
dbChannelCreate: lexical error: invalid char in json text.
These are exactly the same error one gets with the "long form" using :{}
Whitespace around the comma are possible just as usual.
---
I could not find a clever way to properly type check chfPluginArgString for char* vs char[] (without writing a new API in C++). Thus I will keep old chfPluginArgString and new chfPluginArgStr
- 1f76799... by Dirk Zimoch
-
check for out of memory
Unmerged commits
- 1f76799... by Dirk Zimoch
-
check for out of memory
- db29bb0... by Dirk Zimoch
-
check that filter without value is not followed by garbage
- 4395cf4... by Dirk Zimoch
-
simplified filter syntax for no argument
- 06f8a37... by Dirk Zimoch
-
simplified filter syntax for only one argument
- c1b22d0... by Dirk Zimoch
-
allow arbritrary state name length
- 24da53b... by Dirk Zimoch
-
allow strings with arbitrary length
- ab85813... by Dirk Zimoch
-
typo fixed
Preview Diff
1 | diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md |
2 | index 17b039c..b79e5f1 100644 |
3 | --- a/documentation/RELEASE_NOTES.md |
4 | +++ b/documentation/RELEASE_NOTES.md |
5 | @@ -17,6 +17,22 @@ should also be read to understand what has changed since earlier releases. |
6 | |
7 | <!-- Insert new items immediately below here ... --> |
8 | |
9 | +## Channel filter improvements |
10 | + |
11 | +The new chfStringAlloc and chfTagStringAlloc macros allows to store string |
12 | +arguments with arbitrary length in char* members instead of fixed size arrays. |
13 | +Memory is automaticaly allocated and released. |
14 | +The "sync" filter has has been updated to allow arbitrarily long state names. |
15 | + |
16 | +Channel filters that require only one parameter (either they have only one |
17 | +parameter or only one parameter is required) can be used with a simplified |
18 | +syntax where only the value of that parameter is given instead of an object |
19 | +with one member. |
20 | + |
21 | +Channel filters that require no parameter at all can be used with a simplified |
22 | +syntax where only the filter name is given but not even an empty parameter set. |
23 | +This extends the JSON5 syntax. |
24 | + |
25 | ### IOCsh sets `${PWD}` |
26 | |
27 | IOC shell will now ensure `${PWD}` is set on startup, |
28 | @@ -105,7 +121,6 @@ that the variables referenced by output pointers are initialized. |
29 | #endif |
30 | ``` |
31 | |
32 | - |
33 | ----- |
34 | |
35 | ## EPICS Release 7.0.5 |
36 | diff --git a/modules/database/src/ioc/db/chfPlugin.c b/modules/database/src/ioc/db/chfPlugin.c |
37 | index c84e086..4f103e3 100644 |
38 | --- a/modules/database/src/ioc/db/chfPlugin.c |
39 | +++ b/modules/database/src/ioc/db/chfPlugin.c |
40 | @@ -70,6 +70,7 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) |
41 | const chfPluginEnumType *emap; |
42 | double *dval; |
43 | char *sval; |
44 | + char **psval; |
45 | int ret; |
46 | char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */ |
47 | |
48 | @@ -104,6 +105,11 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) |
49 | strncpy(sval, buff, opt->size-1); |
50 | sval[opt->size-1]='\0'; |
51 | break; |
52 | + case chfPluginArgStringAlloc: |
53 | + psval = (char **) (user + opt->dataOffset); |
54 | + *psval = realloc(*psval, 22); |
55 | + if (*psval) sprintf(*psval, "%ld", (long)val); |
56 | + break; |
57 | case chfPluginArgEnum: |
58 | eval = (int*) (user + opt->dataOffset); |
59 | for (emap = opt->enums; emap && emap->name; emap++) { |
60 | @@ -116,7 +122,7 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) |
61 | return -1; |
62 | } |
63 | break; |
64 | - case chfPluginArgInvalid: |
65 | + default: |
66 | return -1; |
67 | } |
68 | return 0; |
69 | @@ -131,6 +137,7 @@ static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val) |
70 | epicsInt32 *ival; |
71 | double *dval; |
72 | char *sval; |
73 | + char **psval; |
74 | |
75 | #ifdef DEBUG_CHF |
76 | printf("Got a boolean for %s (type %d): %d\n", |
77 | @@ -162,8 +169,12 @@ static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val) |
78 | strncpy(sval, val ? "true" : "false", opt->size - 1); |
79 | sval[opt->size - 1] = '\0'; |
80 | break; |
81 | - case chfPluginArgEnum: |
82 | - case chfPluginArgInvalid: |
83 | + case chfPluginArgStringAlloc: |
84 | + psval = (char **) (user + opt->dataOffset); |
85 | + *psval = realloc(*psval, 6); |
86 | + if (*psval) strcpy(*psval, val ? "true" : "false"); |
87 | + break; |
88 | + default: |
89 | return -1; |
90 | } |
91 | return 0; |
92 | @@ -180,6 +191,7 @@ store_double_value(const chfPluginArgDef *opt, void *vuser, double val) |
93 | epicsInt32 *ival; |
94 | double *dval; |
95 | char *sval; |
96 | + char **psval; |
97 | int i; |
98 | |
99 | #ifdef DEBUG_CHF |
100 | @@ -217,8 +229,12 @@ store_double_value(const chfPluginArgDef *opt, void *vuser, double val) |
101 | return -1; |
102 | } |
103 | break; |
104 | - case chfPluginArgEnum: |
105 | - case chfPluginArgInvalid: |
106 | + case chfPluginArgStringAlloc: |
107 | + psval = (char **) (user + opt->dataOffset); |
108 | + *psval = realloc(*psval, 25); |
109 | + if (*psval) epicsSnprintf(*psval, 24, "%.17g", val); |
110 | + break; |
111 | + default: |
112 | return -1; |
113 | } |
114 | return 0; |
115 | @@ -237,6 +253,7 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val, |
116 | const chfPluginEnumType *emap; |
117 | double *dval; |
118 | char *sval; |
119 | + char **psval; |
120 | char *end; |
121 | size_t i; |
122 | |
123 | @@ -246,6 +263,7 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val, |
124 | #endif |
125 | |
126 | if (!opt->convert && opt->optType != chfPluginArgString && |
127 | + opt->optType != chfPluginArgStringAlloc && |
128 | opt->optType != chfPluginArgEnum) { |
129 | return -1; |
130 | } |
131 | @@ -279,6 +297,11 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val, |
132 | strncpy(sval, val, i); |
133 | sval[i] = '\0'; |
134 | break; |
135 | + case chfPluginArgStringAlloc: |
136 | + psval = (char **) (user + opt->dataOffset); |
137 | + free(*psval); |
138 | + *psval = epicsStrnDup(val, len); |
139 | + break; |
140 | case chfPluginArgEnum: |
141 | eval = (int*) (user + opt->dataOffset); |
142 | for (emap = opt->enums; emap && emap->name; emap++) { |
143 | @@ -291,7 +314,7 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val, |
144 | return -1; |
145 | } |
146 | break; |
147 | - case chfPluginArgInvalid: |
148 | + default: |
149 | return -1; |
150 | } |
151 | return 0; |
152 | @@ -314,6 +337,7 @@ static parse_result parse_start(chFilter *filter) |
153 | { |
154 | chfPlugin *p = (chfPlugin*) filter->plug->puser; |
155 | chfFilter *f; |
156 | + size_t i; |
157 | |
158 | /* Filter context */ |
159 | /* FIXME: Use a free-list */ |
160 | @@ -339,25 +363,61 @@ static parse_result parse_start(chFilter *filter) |
161 | } |
162 | } |
163 | |
164 | + /* If there is only one argument or only one required argument, |
165 | + allow shortcut without map */ |
166 | + if (p->nopts == 1) |
167 | + f->nextParam = 0; |
168 | + else |
169 | + for (i = 0; i < p->nopts; i++) { |
170 | + if (p->opts[i].required) { |
171 | + if (f->nextParam == -1) { |
172 | + f->nextParam = i; |
173 | + } else { |
174 | + f->nextParam = -1; |
175 | + break; |
176 | + } |
177 | + } |
178 | + } |
179 | + if (f->nextParam >= 0 && |
180 | + p->opts[f->nextParam].optType == chfPluginArgStringAlloc) { |
181 | + *(char **) ((char*) f->puser + p->opts[f->nextParam].dataOffset) = NULL; |
182 | + } |
183 | + |
184 | filter->puser = (void*) f; |
185 | |
186 | return parse_continue; |
187 | |
188 | errplugin: |
189 | - free(f->found); |
190 | errbitarray: |
191 | - free(f); /* FIXME: Use a free-list */ |
192 | + freeInstanceData(f); |
193 | errfctx: |
194 | return parse_stop; |
195 | } |
196 | |
197 | +static void freePvt(chFilter *filter) { |
198 | + chfPlugin *p = (chfPlugin*) filter->plug->puser; |
199 | + chfFilter *f = (chfFilter*) filter->puser; |
200 | + int i; |
201 | + |
202 | + /* free all chfPluginArgStringAlloc options we had allocated */ |
203 | + for(i = 0; i < p->nopts; i++) { |
204 | + if (p->opts[i].optType == chfPluginArgStringAlloc && |
205 | + (f->found[i/32] & (1<<(i%32)))) { |
206 | + char **psval = (char **) ((char*) f->puser + p->opts[i].dataOffset); |
207 | + free(*psval); |
208 | + *psval = NULL; |
209 | + } |
210 | + } |
211 | + if (p->pif->freePvt) p->pif->freePvt(f->puser); |
212 | +} |
213 | + |
214 | static void parse_abort(chFilter *filter) { |
215 | chfPlugin *p = (chfPlugin*) filter->plug->puser; |
216 | chfFilter *f = (chfFilter*) filter->puser; |
217 | |
218 | /* Call the plugin to tell it we're aborting */ |
219 | if (p->pif->parse_error) p->pif->parse_error(f->puser); |
220 | - if (p->pif->freePvt) p->pif->freePvt(f->puser); |
221 | + freePvt(filter); |
222 | freeInstanceData(f); |
223 | } |
224 | |
225 | @@ -365,22 +425,11 @@ static parse_result parse_end(chFilter *filter) |
226 | { |
227 | chfPlugin *p = (chfPlugin*) filter->plug->puser; |
228 | chfFilter *f = (chfFilter*) filter->puser; |
229 | - int i; |
230 | - |
231 | - /* Check if all required arguments were supplied */ |
232 | - for(i = 0; i < (p->nopts/32)+1; i++) { |
233 | - if ((f->found[i] & p->required[i]) != p->required[i]) { |
234 | - if (p->pif->parse_error) p->pif->parse_error(f->puser); |
235 | - if (p->pif->freePvt) p->pif->freePvt(f->puser); |
236 | - freeInstanceData(f); |
237 | - return parse_stop; |
238 | - } |
239 | - } |
240 | |
241 | /* Call the plugin to tell it we're done */ |
242 | if (p->pif->parse_ok) { |
243 | if (p->pif->parse_ok(f->puser)) { |
244 | - if (p->pif->freePvt) p->pif->freePvt(f->puser); |
245 | + freePvt(filter); |
246 | freeInstanceData(f); |
247 | return parse_stop; |
248 | } |
249 | @@ -478,6 +527,12 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen) |
250 | *tag = opts[i].choice; |
251 | } |
252 | |
253 | + /* NULL all found chfPluginArgStringAlloc options */ |
254 | + if (opts[i].optType == chfPluginArgStringAlloc && |
255 | + !(f->found[i/32] & 1<<(i%32))) { |
256 | + char **psval = (char **) ((char*) f->puser + opts[i].dataOffset); |
257 | + *psval = NULL; |
258 | + } |
259 | f->found[i/32] |= 1<<(i%32); |
260 | /* Mark tag and all other options pointing to the same data as found */ |
261 | for (cur = opts, j = 0; cur && cur->name; cur++, j++) { |
262 | @@ -491,6 +546,15 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen) |
263 | |
264 | static parse_result parse_end_map(chFilter *filter) |
265 | { |
266 | + chfPlugin *p = (chfPlugin*) filter->plug->puser; |
267 | + chfFilter *f = (chfFilter*) filter->puser; |
268 | + int i; |
269 | + |
270 | + /* Check if all required arguments were supplied */ |
271 | + for(i = 0; i < (p->nopts/32)+1; i++) { |
272 | + if ((f->found[i] & p->required[i]) != p->required[i]) |
273 | + return parse_stop; |
274 | + } |
275 | return parse_continue; |
276 | } |
277 | |
278 | @@ -545,9 +609,8 @@ static void channel_close(chFilter *filter) |
279 | chfFilter *f = (chfFilter*) filter->puser; |
280 | |
281 | if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser); |
282 | - if (p->pif->freePvt) p->pif->freePvt(f->puser); |
283 | - free(f->found); |
284 | - free(f); /* FIXME: Use a free-list */ |
285 | + freePvt(filter); |
286 | + freeInstanceData(f); |
287 | } |
288 | |
289 | static void plugin_free(void* puser) |
290 | @@ -649,7 +712,16 @@ chfPluginRegister(const char* key, const chfPluginIf *pif, |
291 | return -1; |
292 | } |
293 | break; |
294 | - case chfPluginArgInvalid: |
295 | + case chfPluginArgStringAlloc: |
296 | + if (cur->size != sizeof(char*)) { |
297 | + /* Catch if someone has given us a something else than a pointer |
298 | + */ |
299 | + errlogPrintf("Plugin %s: %d bytes not a pointer type for string %s\n", |
300 | + key, cur->size, cur->name); |
301 | + return -1; |
302 | + } |
303 | + break; |
304 | + default: |
305 | errlogPrintf("Plugin %s: storage type for %s is not defined\n", |
306 | key, cur->name); |
307 | return -1; |
308 | diff --git a/modules/database/src/ioc/db/chfPlugin.h b/modules/database/src/ioc/db/chfPlugin.h |
309 | index 5867a66..eed218d 100644 |
310 | --- a/modules/database/src/ioc/db/chfPlugin.h |
311 | +++ b/modules/database/src/ioc/db/chfPlugin.h |
312 | @@ -52,6 +52,7 @@ struct db_field_log; |
313 | * epicsInt32 ival2; |
314 | * int enumval; |
315 | * char strval[20]; |
316 | + * char* str; |
317 | * char boolval; |
318 | * } myStruct; |
319 | * |
320 | @@ -64,6 +65,7 @@ struct db_field_log; |
321 | * chfInt32 (myStruct, ival2, "Second" , 1, 0), |
322 | * chfDouble (myStruct, dval, "Double" , 1, 0), |
323 | * chfString (myStruct, strval , "String" , 1, 0), |
324 | + * chfStringAlloc(myStruct, str , "String" , 1, 0), |
325 | * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), |
326 | * chfBoolean (myStruct, boolval, "Bool" , 1, 0), |
327 | * chfPluginEnd |
328 | @@ -225,7 +227,8 @@ typedef enum chfPluginArg { |
329 | chfPluginArgInt32, |
330 | chfPluginArgDouble, |
331 | chfPluginArgString, |
332 | - chfPluginArgEnum |
333 | + chfPluginArgEnum, |
334 | + chfPluginArgStringAlloc |
335 | } chfPluginArg; |
336 | |
337 | typedef struct chfPluginEnumType { |
338 | @@ -268,6 +271,10 @@ typedef struct chfPluginArgDef { |
339 | {Name, chfPluginArgEnum, Req, Conv, 0, 0, 0, \ |
340 | OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} |
341 | |
342 | +#define chfStringAlloc(Struct, Member, Name, Req, Conv) \ |
343 | + {Name, chfPluginArgStringAlloc, Req, Conv, 0, 0, 0, \ |
344 | + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} |
345 | + |
346 | /* Tagged arguments */ |
347 | |
348 | #define chfTagInt32(Struct, Member, Name, Tag, Choice, Req, Conv) \ |
349 | @@ -290,6 +297,10 @@ typedef struct chfPluginArgDef { |
350 | {Name, chfPluginArgEnum, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ |
351 | OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} |
352 | |
353 | +#define chfTagStringAlloc(Struct, Member, Name, Tag, Choice, Req, Conv) \ |
354 | + {Name, chfPluginArgStringAlloc, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ |
355 | + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} |
356 | + |
357 | #define chfPluginArgEnd {0} |
358 | |
359 | /* Extra output when parsing and converting */ |
360 | diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c |
361 | index a806f3c..edf2124 100644 |
362 | --- a/modules/database/src/ioc/db/dbChannel.c |
363 | +++ b/modules/database/src/ioc/db/dbChannel.c |
364 | @@ -291,6 +291,32 @@ static long chf_parse(dbChannel *chan, const char **pjson) |
365 | case yajl_status_error: { |
366 | unsigned char *err; |
367 | |
368 | + if (parser.depth == 0 && parser.filter && |
369 | + (json[ylen-1] == ',' || json[ylen-1] == '}')) { |
370 | + /* We get here only if filter is called without arguments. */ |
371 | + /* Let's fake ":{}" */ |
372 | + if (chf_start_map(&parser) == parse_continue && |
373 | + chf_end_map(&parser) == parse_continue) { |
374 | + *pjson += ylen; |
375 | + status = 0; |
376 | + if (json[ylen]) { |
377 | + /* some filters left, let's parse them too */ |
378 | + char *jsoncopy = epicsStrDup(json+ylen-1); |
379 | + if (!jsoncopy) { |
380 | + printf("dbChannelCreate: out of memory\n"); |
381 | + break; |
382 | + } |
383 | + jsoncopy[0] = '{'; |
384 | + json = jsoncopy; |
385 | + status = chf_parse(chan, &json); |
386 | + ylen = json-jsoncopy-1; |
387 | + *pjson += ylen; |
388 | + free(jsoncopy); |
389 | + } |
390 | + break; |
391 | + } |
392 | + } |
393 | + |
394 | err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); |
395 | printf("dbChannelCreate: %s\n", err); |
396 | yajl_free_error(yh, err); |
397 | diff --git a/modules/database/src/std/filters/filters.dbd.pod b/modules/database/src/std/filters/filters.dbd.pod |
398 | index 7b31466..5c1219d 100644 |
399 | --- a/modules/database/src/std/filters/filters.dbd.pod |
400 | +++ b/modules/database/src/std/filters/filters.dbd.pod |
401 | @@ -40,6 +40,13 @@ Parameters to that filter are provided as the value part of the name/value pair, |
402 | and will normally appear as a child JSON object consisting of name/value pairs |
403 | inside a nested pair of braces C< {} >. |
404 | |
405 | +If a filter takes only one parameter or only one parameter is required, a short |
406 | +form may be used where the parameter vallue is passed directly instead of |
407 | +an object in braces C< {} >. |
408 | + |
409 | +If a filter does not need any parameter, the value part can be skipped |
410 | +completely. Only the filter name is needed in this case. |
411 | + |
412 | =head4 Example Filter |
413 | |
414 | Given a record called C<test:channel> the following would apply a filter C<f> to |
415 | @@ -69,12 +76,12 @@ to the EPICS epoch if the record has never processed. |
416 | |
417 | =head4 Parameters |
418 | |
419 | -None, use an empty pair of braces. |
420 | +None. |
421 | |
422 | =head4 Example |
423 | |
424 | - Hal$ caget -a 'test:channel.{"ts":{}}' |
425 | - test:channel.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID |
426 | + Hal$ caget -a 'test:channel.{"ts"}' |
427 | + test:channel.{"ts"} 2012-08-28 22:10:31.192547 0 UDF INVALID |
428 | Hal$ caget -a 'test:channel' |
429 | test:channel <undefined> 0 UDF INVALID |
430 | |
431 | @@ -281,9 +288,10 @@ client connects. |
432 | =head4 Example |
433 | |
434 | To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel |
435 | -once every minute: |
436 | +once every minute. As the "dec" filter requires only one parameter, the short |
437 | +syntax may be used: |
438 | |
439 | - Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}' |
440 | + Hal$ camonitor 'test:channel' 'test:channel.{"dec":60}' |
441 | ... |
442 | |
443 | =cut |
444 | diff --git a/modules/database/src/std/filters/sync.c b/modules/database/src/std/filters/sync.c |
445 | index 8140c36..f31f677 100644 |
446 | --- a/modules/database/src/std/filters/sync.c |
447 | +++ b/modules/database/src/std/filters/sync.c |
448 | @@ -21,8 +21,6 @@ |
449 | #include "epicsAssert.h" |
450 | #include "epicsExport.h" |
451 | |
452 | -#define STATE_NAME_LENGTH 20 |
453 | - |
454 | typedef enum syncMode { |
455 | syncModeBefore=0, |
456 | syncModeFirst=1, |
457 | @@ -45,7 +43,7 @@ chfPluginEnumType modeEnum[] = { |
458 | |
459 | typedef struct myStruct { |
460 | syncMode mode; |
461 | - char state[STATE_NAME_LENGTH]; |
462 | + char* state; |
463 | dbStateId id; |
464 | db_field_log *lastfl; |
465 | int laststate:1; |
466 | @@ -55,14 +53,14 @@ static void *myStructFreeList; |
467 | |
468 | static const |
469 | chfPluginArgDef opts[] = { |
470 | - chfEnum (myStruct, mode, "m", 1, 1, modeEnum), |
471 | - chfString (myStruct, state, "s", 1, 0), |
472 | - chfTagString (myStruct, state, "before", mode, 0, 1, 0), |
473 | - chfTagString (myStruct, state, "first", mode, 1, 1, 0), |
474 | - chfTagString (myStruct, state, "last", mode, 2, 1, 0), |
475 | - chfTagString (myStruct, state, "after", mode, 3, 1, 0), |
476 | - chfTagString (myStruct, state, "while", mode, 4, 1, 0), |
477 | - chfTagString (myStruct, state, "unless", mode, 5, 1, 0), |
478 | + chfEnum (myStruct, mode, "m", 1, 1, modeEnum), |
479 | + chfStringAlloc (myStruct, state, "s", 1, 0), |
480 | + chfTagStringAlloc (myStruct, state, "before", mode, 0, 1, 0), |
481 | + chfTagStringAlloc (myStruct, state, "first", mode, 1, 1, 0), |
482 | + chfTagStringAlloc (myStruct, state, "last", mode, 2, 1, 0), |
483 | + chfTagStringAlloc (myStruct, state, "after", mode, 3, 1, 0), |
484 | + chfTagStringAlloc (myStruct, state, "while", mode, 4, 1, 0), |
485 | + chfTagStringAlloc (myStruct, state, "unless", mode, 5, 1, 0), |
486 | chfPluginArgEnd |
487 | }; |
488 | |
489 | diff --git a/modules/database/test/ioc/db/chfPluginTest.c b/modules/database/test/ioc/db/chfPluginTest.c |
490 | index 4a1667c..6d281fc 100644 |
491 | --- a/modules/database/test/ioc/db/chfPluginTest.c |
492 | +++ b/modules/database/test/ioc/db/chfPluginTest.c |
493 | @@ -68,6 +68,7 @@ typedef struct myStruct { |
494 | char c1[2]; |
495 | int offpre; |
496 | int offpost; |
497 | + char* astr; |
498 | } myStruct; |
499 | |
500 | static const |
501 | @@ -75,54 +76,76 @@ chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} }; |
502 | |
503 | static const |
504 | chfPluginArgDef sloppyTaggedOpts[] = { |
505 | - chfInt32 (myStruct, tval, "t" , 0, 0), |
506 | - chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), |
507 | - chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0), |
508 | - chfTagDouble (myStruct, dval, "D" , tval, 3, 0, 0), |
509 | - chfTagString (myStruct, str, "S" , tval, 4, 0, 0), |
510 | - chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), |
511 | + chfInt32 (myStruct, tval, "t" , 0, 0), |
512 | + chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), |
513 | + chfTagBoolean (myStruct, flag, "F" , tval, 2, 0, 0), |
514 | + chfTagDouble (myStruct, dval, "D" , tval, 3, 0, 0), |
515 | + chfTagString (myStruct, str, "S" , tval, 4, 0, 0), |
516 | + chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), |
517 | + chfTagStringAlloc(myStruct, astr, "A" , tval, 6, 0, 0), |
518 | chfPluginArgEnd |
519 | }; |
520 | |
521 | static const |
522 | chfPluginArgDef strictTaggedOpts[] = { |
523 | - chfInt32 (myStruct, tval, "t" , 1, 0), |
524 | - chfBoolean (myStruct, flag, "f" , 1, 0), |
525 | - chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), |
526 | - chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0), |
527 | - chfTagDouble (myStruct, dval, "D" , tval, 3, 1, 0), |
528 | - chfTagDouble (myStruct, dval, "D2", tval, 4, 1, 0), |
529 | - chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), |
530 | + chfInt32 (myStruct, tval, "t" , 1, 0), |
531 | + chfBoolean (myStruct, flag, "f" , 1, 0), |
532 | + chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), |
533 | + chfTagBoolean (myStruct, flag, "F" , tval, 2, 0, 0), |
534 | + chfTagDouble (myStruct, dval, "D" , tval, 3, 1, 0), |
535 | + chfTagDouble (myStruct, dval, "D2", tval, 4, 1, 0), |
536 | + chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), |
537 | + chfTagStringAlloc(myStruct, astr, "A", tval, 6, 0, 0), |
538 | chfPluginArgEnd |
539 | }; |
540 | |
541 | static const |
542 | chfPluginArgDef strictOpts[] = { |
543 | - chfInt32 (myStruct, ival, "i" , 1, 0), |
544 | - chfBoolean(myStruct, flag, "f" , 1, 0), |
545 | - chfDouble (myStruct, dval, "d" , 1, 0), |
546 | - chfString (myStruct, str, "s" , 1, 0), |
547 | - chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum), |
548 | + chfInt32 (myStruct, ival, "i" , 1, 0), |
549 | + chfBoolean (myStruct, flag, "f" , 1, 0), |
550 | + chfDouble (myStruct, dval, "d" , 1, 0), |
551 | + chfString (myStruct, str, "s" , 1, 0), |
552 | + chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum), |
553 | + chfStringAlloc(myStruct, astr, "a", 1, 0), |
554 | chfPluginArgEnd |
555 | }; |
556 | |
557 | static const |
558 | chfPluginArgDef noconvOpts[] = { |
559 | - chfInt32 (myStruct, ival, "i" , 0, 0), |
560 | - chfBoolean(myStruct, flag, "f" , 0, 0), |
561 | - chfDouble (myStruct, dval, "d" , 0, 0), |
562 | - chfString (myStruct, str, "s" , 0, 0), |
563 | - chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum), |
564 | + chfInt32 (myStruct, ival, "i" , 0, 0), |
565 | + chfBoolean (myStruct, flag, "f" , 0, 0), |
566 | + chfDouble (myStruct, dval, "d" , 0, 0), |
567 | + chfString (myStruct, str, "s" , 0, 0), |
568 | + chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum), |
569 | + chfStringAlloc(myStruct, astr, "a", 0, 0), |
570 | chfPluginArgEnd |
571 | }; |
572 | |
573 | static const |
574 | chfPluginArgDef sloppyOpts[] = { |
575 | - chfInt32 (myStruct, ival, "i" , 0, 1), |
576 | - chfBoolean(myStruct, flag, "f" , 0, 1), |
577 | - chfDouble (myStruct, dval, "d" , 0, 1), |
578 | - chfString (myStruct, str, "s" , 0, 1), |
579 | - chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum), |
580 | + chfInt32 (myStruct, ival, "i" , 0, 1), |
581 | + chfBoolean (myStruct, flag, "f" , 0, 1), |
582 | + chfDouble (myStruct, dval, "d" , 0, 1), |
583 | + chfString (myStruct, str, "s" , 0, 1), |
584 | + chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum), |
585 | + chfStringAlloc(myStruct, astr, "a", 0, 1), |
586 | + chfPluginArgEnd |
587 | +}; |
588 | + |
589 | +static const |
590 | +chfPluginArgDef onlyOneOpts[] = { |
591 | + chfInt32 (myStruct, ival, "i" , 0, 0), |
592 | + chfPluginArgEnd |
593 | +}; |
594 | + |
595 | +static const |
596 | +chfPluginArgDef onlyOneRequiredOpts[] = { |
597 | + chfInt32 (myStruct, ival, "i" , 0, 0), |
598 | + chfBoolean (myStruct, flag, "f" , 0, 0), |
599 | + chfDouble (myStruct, dval, "d" , 1, 0), |
600 | + chfString (myStruct, str, "s" , 0, 0), |
601 | + chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum), |
602 | + chfStringAlloc(myStruct, astr, "a", 0, 0), |
603 | chfPluginArgEnd |
604 | }; |
605 | |
606 | @@ -154,6 +177,7 @@ chfPluginArgDef brokenOpts4[] = { |
607 | int p_ok_return = 0; |
608 | int c_open_return = 0; |
609 | void *puser1, *puser2; |
610 | +char defaultstring[] = "default"; |
611 | |
612 | static void clearStruct(void *p) { |
613 | myStruct *my = (myStruct*) p; |
614 | @@ -168,6 +192,7 @@ static void clearStruct(void *p) { |
615 | my->dval = 1.234e5; |
616 | strcpy(my->str, "hello"); |
617 | my->enumval = 4; |
618 | + my->astr = defaultstring; |
619 | } |
620 | |
621 | static char inst(void* user) { |
622 | @@ -177,7 +202,6 @@ static char inst(void* user) { |
623 | static void * allocPvt(void) |
624 | { |
625 | myStruct *my = (myStruct*) calloc(1, sizeof(myStruct)); |
626 | - |
627 | if (!puser1) { |
628 | puser1 = my; |
629 | testOk(e1 & e_alloc, "allocPvt (1) called"); |
630 | @@ -194,7 +218,7 @@ static void * allocPvt(void) |
631 | static void * allocPvtFail(void) |
632 | { |
633 | if (!puser1) { |
634 | - testOk(e1 & e_alloc, "allocPvt (1) called"); |
635 | + testOk(e1 & e_alloc, "allocPvtFail (1) called"); |
636 | c1 |= e_alloc; |
637 | } |
638 | return NULL; |
639 | @@ -202,7 +226,10 @@ static void * allocPvtFail(void) |
640 | |
641 | static void freePvt(void *user) |
642 | { |
643 | - if (user == puser1) { |
644 | + myStruct *my = (myStruct*) user; |
645 | + if ((user == puser1 || user == puser2) && (my->astr && my->astr != defaultstring)) { |
646 | + testFail("freePvt: allocated string not free'd"); |
647 | + } else if (user == puser1) { |
648 | testOk(e1 & e_free, "freePvt (1) called"); |
649 | c1 |= e_free; |
650 | free(user); |
651 | @@ -499,9 +526,9 @@ static chfPluginIf allocFailPif = { |
652 | }; |
653 | |
654 | static int checkValues(myStruct *my, |
655 | - char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c) { |
656 | + char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c, char *s3) { |
657 | int ret = 1; |
658 | - int s1fail, s2fail; |
659 | + int s1fail, s2fail, s3fail; |
660 | int s2valid = (s2 && s2[0] != '\0'); |
661 | |
662 | if (!my) return 0; |
663 | @@ -526,6 +553,11 @@ static int checkValues(myStruct *my, |
664 | if (s2valid && s2fail) testDiag("Fail: my->str (%s) != s (%s)", my->str, s2); |
665 | ret = 0; |
666 | } |
667 | + s3fail = (!s3 && my->astr) || (s3 && !my->astr) || (s3 && strcmp(s3, my->astr)); |
668 | + if (s3fail) { |
669 | + testDiag("Fail: my->str (%s) != s (%s)", my->astr, s3); |
670 | + ret = 0; |
671 | + } |
672 | return ret; |
673 | } |
674 | |
675 | @@ -549,7 +581,7 @@ MAIN(chfPluginTest) |
676 | #endif |
677 | #endif |
678 | |
679 | - testPlan(1433); |
680 | + testPlan(1612); |
681 | |
682 | dbChannelInit(); |
683 | db_init_events(); |
684 | @@ -599,6 +631,10 @@ MAIN(chfPluginTest) |
685 | "register plugin noconv"); |
686 | testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), |
687 | "register plugin sloppy"); |
688 | + testOk(!chfPluginRegister("onlyOne", &myPif, onlyOneOpts), |
689 | + "register plugin onlyOne"); |
690 | + testOk(!chfPluginRegister("onlyOneRequired", &myPif, onlyOneRequiredOpts), |
691 | + "register plugin onlyOneRequired"); |
692 | testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), |
693 | "register plugin pre"); |
694 | testOk(!chfPluginRegister("post", &postPif, sloppyOpts), |
695 | @@ -629,7 +665,7 @@ MAIN(chfPluginTest) |
696 | testOk(!!(pch = dbChannelCreate( |
697 | "x.{'strict-tagged':{D:1.2e15,f:false}}")), |
698 | "create channel for strict-tagged parsing: D (t and d) and f"); |
699 | - testOk(checkValues(puser1, 3, 12, 0, 1.2e15, "hello", 0, 4), |
700 | + testOk(checkValues(puser1, 3, 12, 0, 1.2e15, "hello", 0, 4, "default"), |
701 | "guards intact, values correct"); |
702 | if (!testOk(c1 == e1, "all expected calls happened")) |
703 | testDiag("expected %#x - called %#x", e1, c1); |
704 | @@ -643,7 +679,7 @@ MAIN(chfPluginTest) |
705 | testOk(!!(pch = dbChannelCreate( |
706 | "x.{'strict-tagged':{D2:1.2e15,f:false}}")), |
707 | "create channel for strict-tagged parsing: D2 (t and d) and f"); |
708 | - testOk(checkValues(puser1, 4, 12, 0, 1.2e15, "hello", 0, 4), |
709 | + testOk(checkValues(puser1, 4, 12, 0, 1.2e15, "hello", 0, 4, "default"), |
710 | "guards intact, values correct"); |
711 | if (!testOk(c1 == e1, "all expected calls happened")) |
712 | testDiag("expected %#x - called %#x", e1, c1); |
713 | @@ -673,12 +709,26 @@ MAIN(chfPluginTest) |
714 | |
715 | testHead("SLOPPY TAGGED parsing: all ok"); |
716 | |
717 | + /* no tag */ |
718 | + e1 = e_alloc | e_ok; c1 = 0; |
719 | + testOk(!!(pch = dbChannelCreate( |
720 | + "x.{'sloppy-tagged':{}}")), |
721 | + "create channel for sloppy-tagged parsing: no argument"); |
722 | + testOk(checkValues(puser1, 99, 12, 1, 1.234e5, "hello", 0, 4, "default"), |
723 | + "guards intact, values correct"); |
724 | + if (!testOk(c1 == e1, "all expected calls happened")) |
725 | + testDiag("expected %#x - called %#x", e1, c1); |
726 | + e1 = e_close | e_free; c1 = 0; |
727 | + if (pch) dbChannelDelete(pch); |
728 | + testOk(!puser1, "user part cleaned up"); |
729 | + if (!testOk(c1 == e1, "all expected calls happened")) |
730 | + testDiag("expected %#x - called %#x", e1, c1); |
731 | /* tag i */ |
732 | e1 = e_alloc | e_ok; c1 = 0; |
733 | testOk(!!(pch = dbChannelCreate( |
734 | "x.{'sloppy-tagged':{I:1}}")), |
735 | "create channel for sloppy-tagged parsing: I"); |
736 | - testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4), |
737 | + testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4, "default"), |
738 | "guards intact, values correct"); |
739 | if (!testOk(c1 == e1, "all expected calls happened")) |
740 | testDiag("expected %#x - called %#x", e1, c1); |
741 | @@ -692,7 +742,7 @@ MAIN(chfPluginTest) |
742 | testOk(!!(pch = dbChannelCreate( |
743 | "x.{'sloppy-tagged':{F:false}}")), |
744 | "create channel for sloppy-tagged parsing: F"); |
745 | - testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4), |
746 | + testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4, "default"), |
747 | "guards intact, values correct"); |
748 | if (!testOk(c1 == e1, "all expected calls happened")) |
749 | testDiag("expected %#x - called %#x", e1, c1); |
750 | @@ -706,7 +756,7 @@ MAIN(chfPluginTest) |
751 | testOk(!!(pch = dbChannelCreate( |
752 | "x.{'sloppy-tagged':{D:1.2e15}}")), |
753 | "create channel for sloppy-tagged parsing: D"); |
754 | - testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4), |
755 | + testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4, "default"), |
756 | "guards intact, values correct"); |
757 | if (!testOk(c1 == e1, "all expected calls happened")) |
758 | testDiag("expected %#x - called %#x", e1, c1); |
759 | @@ -720,7 +770,7 @@ MAIN(chfPluginTest) |
760 | testOk(!!(pch = dbChannelCreate( |
761 | "x.{'sloppy-tagged':{S:'bar'}}")), |
762 | "create channel for sloppy-tagged parsing: S"); |
763 | - testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4), |
764 | + testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4, "default"), |
765 | "guards intact, values correct"); |
766 | if (!testOk(c1 == e1, "all expected calls happened")) |
767 | testDiag("expected %#x - called %#x", e1, c1); |
768 | @@ -734,7 +784,21 @@ MAIN(chfPluginTest) |
769 | testOk(!!(pch = dbChannelCreate( |
770 | "x.{'sloppy-tagged':{C:'R'}}")), |
771 | "create channel for sloppy-tagged parsing: C"); |
772 | - testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1), |
773 | + testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1, "default"), |
774 | + "guards intact, values correct"); |
775 | + if (!testOk(c1 == e1, "all expected calls happened")) |
776 | + testDiag("expected %#x - called %#x", e1, c1); |
777 | + e1 = e_close | e_free; c1 = 0; |
778 | + if (pch) dbChannelDelete(pch); |
779 | + testOk(!puser1, "user part cleaned up"); |
780 | + if (!testOk(c1 == e1, "all expected calls happened")) |
781 | + testDiag("expected %#x - called %#x", e1, c1); |
782 | + /* tag A */ |
783 | + e1 = e_alloc | e_ok; c1 = 0; |
784 | + testOk(!!(pch = dbChannelCreate( |
785 | + "x.{'sloppy-tagged':{A:'bar'}}")), |
786 | + "create channel for sloppy-tagged parsing: A"); |
787 | + testOk(checkValues(puser1, 6, 12, 1, 1.234e5, "hello", 0, 4, "bar"), |
788 | "guards intact, values correct"); |
789 | if (!testOk(c1 == e1, "all expected calls happened")) |
790 | testDiag("expected %#x - called %#x", e1, c1); |
791 | @@ -749,9 +813,9 @@ MAIN(chfPluginTest) |
792 | /* All perfect */ |
793 | testHead("STRICT parsing: all ok"); |
794 | e1 = e_alloc | e_ok; c1 = 0; |
795 | - testOk(!!(pch = dbChannelCreate("x.{strict:{i:1,f:false,d:1.2e15,s:'bar',c:'R'}}")), |
796 | + testOk(!!(pch = dbChannelCreate("x.{strict:{i:1,f:false,d:1.2e15,s:'bar',c:'R',a:'foo'}}")), |
797 | "create channel for strict parsing: JSON correct"); |
798 | - testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1), |
799 | + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1, "foo"), |
800 | "guards intact, values correct"); |
801 | if (!testOk(c1 == e1, "all expected calls happened")) |
802 | testDiag("expected %#x - called %#x", e1, c1); |
803 | @@ -765,39 +829,46 @@ MAIN(chfPluginTest) |
804 | testHead("STRICT parsing: any missing parameter must fail"); |
805 | e1 = e_alloc | e_error | e_free; c1 = 0; |
806 | testOk(!(pch = dbChannelCreate( |
807 | - "x.{strict:{i:1,f:false,d:1.2e15,s:'bar'}}")), |
808 | + "x.{strict:{i:1,f:false,d:1.2e15,s:'bar',a:'foo'}}")), |
809 | "create channel for strict parsing: c missing"); |
810 | testOk(!puser1, "user part cleaned up"); |
811 | if (!testOk(c1 == e1, "all expected calls happened")) |
812 | testDiag("expected %#x - called %#x", e1, c1); |
813 | e1 = e_alloc | e_error | e_free; c1 = 0; |
814 | testOk(!(pch = dbChannelCreate( |
815 | - "x.{strict:{f:false,i:1,d:1.2e15,c:'R'}}")), |
816 | + "x.{strict:{f:false,i:1,d:1.2e15,c:'R',a:'foo'}}")), |
817 | "create channel for strict parsing: s missing"); |
818 | testOk(!puser1, "user part cleaned up"); |
819 | if (!testOk(c1 == e1, "all expected calls happened")) |
820 | testDiag("expected %#x - called %#x", e1, c1); |
821 | e1 = e_alloc | e_error | e_free; c1 = 0; |
822 | testOk(!(pch = dbChannelCreate( |
823 | - "x.{strict:{i:1,c:'R',f:false,s:'bar'}}")), |
824 | + "x.{strict:{i:1,c:'R',f:false,s:'bar',a:'foo'}}")), |
825 | "create channel for strict parsing: d missing"); |
826 | testOk(!puser1, "user part cleaned up"); |
827 | if (!testOk(c1 == e1, "all expected calls happened")) |
828 | testDiag("expected %#x - called %#x", e1, c1); |
829 | e1 = e_alloc | e_error | e_free; c1 = 0; |
830 | testOk(!(pch = dbChannelCreate( |
831 | - "x.{strict:{d:1.2e15,c:'R',i:1,s:'bar'}}")), |
832 | + "x.{strict:{d:1.2e15,c:'R',i:1,s:'bar',a:'foo'}}")), |
833 | "create channel for strict parsing: f missing"); |
834 | testOk(!puser1, "user part cleaned up"); |
835 | if (!testOk(c1 == e1, "all expected calls happened")) |
836 | testDiag("expected %#x - called %#x", e1, c1); |
837 | e1 = e_alloc | e_error | e_free; c1 = 0; |
838 | testOk(!(pch = dbChannelCreate( |
839 | - "x.{strict:{c:'R',s:'bar',f:false,d:1.2e15}}")), |
840 | + "x.{strict:{c:'R',s:'bar',f:false,d:1.2e15,a:'foo'}}")), |
841 | "create channel for strict parsing: i missing"); |
842 | testOk(!puser1, "user part cleaned up"); |
843 | if (!testOk(c1 == e1, "all expected calls happened")) |
844 | testDiag("expected %#x - called %#x", e1, c1); |
845 | + e1 = e_alloc | e_error | e_free; c1 = 0; |
846 | + testOk(!(pch = dbChannelCreate( |
847 | + "x.{strict:{i:1,f:false,d:1.2e15,s:'bar',c:'R'}}")), |
848 | + "create channel for strict parsing: a missing"); |
849 | + testOk(!puser1, "user part cleaned up"); |
850 | + if (!testOk(c1 == e1, "all expected calls happened")) |
851 | + testDiag("expected %#x - called %#x", e1, c1); |
852 | |
853 | /* NOCONV parsing: optional, no conversion */ |
854 | |
855 | @@ -805,9 +876,9 @@ MAIN(chfPluginTest) |
856 | testHead("NOCONV parsing: missing parameters get default value"); |
857 | e1 = e_alloc | e_ok; c1 = 0; |
858 | testOk(!!(pch = dbChannelCreate( |
859 | - "x.{noconv:{i:1,f:false,d:1.2e15,s:'bar'}}")), |
860 | + "x.{noconv:{i:1,f:false,d:1.2e15,s:'bar',a:'foo'}}")), |
861 | "create channel for noconv parsing: c missing"); |
862 | - testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 4), |
863 | + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 4, "foo"), |
864 | "guards intact, values correct"); |
865 | if (!testOk(c1 == e1, "all expected calls happened")) |
866 | testDiag("expected %#x - called %#x", e1, c1); |
867 | @@ -819,36 +890,90 @@ MAIN(chfPluginTest) |
868 | |
869 | e1 = e_any; |
870 | testOk(!!(pch = dbChannelCreate( |
871 | - "x.{noconv:{i:1,f:false,d:1.2e15,c:'R'}}")), |
872 | + "x.{noconv:{i:1,f:false,d:1.2e15,c:'R',a:'foo'}}")), |
873 | "create channel for noconv parsing: s missing"); |
874 | - testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "hello", 0, 1), |
875 | + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "hello", 0, 1, "foo"), |
876 | "guards intact, values correct"); |
877 | if (pch) dbChannelDelete(pch); |
878 | |
879 | testOk(!!(pch = dbChannelCreate( |
880 | - "x.{noconv:{i:1,f:false,s:'bar',c:'R'}}")), |
881 | + "x.{noconv:{i:1,f:false,s:'bar',c:'R',a:'foo'}}")), |
882 | "create channel for noconv parsing: d missing"); |
883 | - testOk(checkValues(puser1, 99, 1, 0, 1.234e5, "bar", 0, 1), |
884 | + testOk(checkValues(puser1, 99, 1, 0, 1.234e5, "bar", 0, 1, "foo"), |
885 | "guards intact, values correct"); |
886 | if (pch) dbChannelDelete(pch); |
887 | |
888 | testOk(!!(pch = dbChannelCreate( |
889 | - "x.{noconv:{i:1,d:1.2e15,s:'bar',c:'R'}}")), |
890 | + "x.{noconv:{i:1,d:1.2e15,s:'bar',c:'R',a:'foo'}}")), |
891 | "create channel for noconv parsing: f missing"); |
892 | - testOk(checkValues(puser1, 99, 1, 1, 1.2e15, "bar", 0, 1), |
893 | + testOk(checkValues(puser1, 99, 1, 1, 1.2e15, "bar", 0, 1, "foo"), |
894 | "guards intact, values correct"); |
895 | if (pch) dbChannelDelete(pch); |
896 | |
897 | testOk(!!(pch = dbChannelCreate( |
898 | - "x.{noconv:{f:false,d:1.2e15,s:'bar',c:'R'}}")), |
899 | + "x.{noconv:{f:false,d:1.2e15,s:'bar',c:'R',a:'foo'}}")), |
900 | "create channel for noconv parsing: i missing"); |
901 | - testOk(checkValues(puser1, 99, 12, 0, 1.2e15, "bar", 0, 1), |
902 | + testOk(checkValues(puser1, 99, 12, 0, 1.2e15, "bar", 0, 1, "foo"), |
903 | + "guards intact, values correct"); |
904 | + if (pch) dbChannelDelete(pch); |
905 | + |
906 | + testOk(!!(pch = dbChannelCreate( |
907 | + "x.{noconv:{i:1,f:false,d:1.2e15,s:'bar',c:'R'}}")), |
908 | + "create channel for noconv parsing: a missing"); |
909 | + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1, "default"), |
910 | + "guards intact, values correct"); |
911 | + if (pch) dbChannelDelete(pch); |
912 | + |
913 | + /* SIMPLIFIED parsing: only one argument */ |
914 | + |
915 | + /* */ |
916 | + testHead("SIMPLIFIED parsing: all ok"); |
917 | + |
918 | + /* only one arg (i) defined */ |
919 | + testOk(!!(pch = dbChannelCreate("x.{onlyOne:1}")), |
920 | + "create channel for onlyOne parsing: single arg"); |
921 | + testOk(checkValues(puser1, 99, 1, 1, 1.234e5, "hello", 0, 4, "default"), |
922 | + "guards intact, values correct"); |
923 | + if (pch) dbChannelDelete(pch); |
924 | + |
925 | + /* only one arg (d) required */ |
926 | + testOk(!!(pch = dbChannelCreate("x.{onlyOneRequired:1.23}")), |
927 | + "create channel for onlyOneRequired parsing: single arg"); |
928 | + testOk(checkValues(puser1, 99, 12, 1, 1.23, "hello", 0, 4, "default"), |
929 | + "guards intact, values correct"); |
930 | + if (pch) dbChannelDelete(pch); |
931 | + |
932 | + /* ambigous arg fails */ |
933 | + testOk(!(pch = dbChannelCreate("x.{sloppy:1}")), |
934 | + "create channel for sloppy parsing: ambigous arg"); |
935 | + if (pch) dbChannelDelete(pch); |
936 | + |
937 | + /* No arg at all */ |
938 | + testOk(!!(pch = dbChannelCreate("x.{sloppy}")), |
939 | + "create channel for sloppy parsing: no arg at all"); |
940 | + testOk(checkValues(puser1, 99, 12, 1, 1.234e5, "hello", 0, 4, "default"), |
941 | "guards intact, values correct"); |
942 | if (pch) dbChannelDelete(pch); |
943 | |
944 | + /* No arg at all followed by another filter */ |
945 | + testHead("Chains of filters without args"); \ |
946 | + e1 = e_any; e2 = e_any; |
947 | + testOk(!!(pch = dbChannelCreate("x.{sloppy,sloppy}")), |
948 | + "create channel for chained argless filters: comma separated"); |
949 | + if (pch) dbChannelDelete(pch); |
950 | + testOk(!!(pch = dbChannelCreate("x.{sloppy , sloppy}")), |
951 | + "create channel for chained argless filters: comma and space separated"); |
952 | + if (pch) dbChannelDelete(pch); |
953 | + testOk(!(pch = dbChannelCreate("x.{sloppy#sloppy}")), |
954 | + "create channel for chained argless filters fail: garbage separator"); |
955 | + if (pch) dbChannelDelete(pch); |
956 | + testOk(!(pch = dbChannelCreate("x.{sloppy")), |
957 | + "create channel for chained argless filters fail: missing end"); |
958 | + if (pch) dbChannelDelete(pch); |
959 | + |
960 | /* Reject wrong types */ |
961 | #define WRONGTYPETEST(Var, Val, Typ) \ |
962 | - e1 = e_alloc | e_error | e_free; c1 = 0; \ |
963 | + e1 = e_alloc | e_error | e_free; c1 = 0; e2 = 0; \ |
964 | testOk(!(pch = dbChannelCreate("x.{noconv:{'"#Var"':"#Val"}}")), \ |
965 | "create channel for noconv parsing: wrong type "#Typ" for "#Var); \ |
966 | testOk(!puser1, "user part cleaned up"); \ |
967 | @@ -872,15 +997,18 @@ MAIN(chfPluginTest) |
968 | WRONGTYPETEST(c, 1.23, double); |
969 | WRONGTYPETEST(c, true, boolean); |
970 | WRONGTYPETEST(c, 2, integer); |
971 | + WRONGTYPETEST(a, 1.23, double); |
972 | + WRONGTYPETEST(a, true, boolean); |
973 | + WRONGTYPETEST(a, 123, integer); |
974 | |
975 | /* SLOPPY parsing: optional, with conversion */ |
976 | |
977 | -#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval1, Sval2, Cval) \ |
978 | - e1 = e_alloc | e_ok; c1 = 0; \ |
979 | +#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval1, Sval2, Cval, Dstr) \ |
980 | + e1 = e_alloc | e_ok; c1 = 0; e2 = 0; \ |
981 | testDiag("Calling dbChannelCreate x.{sloppy:{"#Var":"#Val"}}"); \ |
982 | testOk(!!(pch = dbChannelCreate("x.{sloppy:{"#Var":"#Val"}}")), \ |
983 | "create channel for sloppy parsing: "#Typ" (good) for "#Var); \ |
984 | - testOk(checkValues(puser1, 99, Ival, Fval, Dval, Sval1, Sval2, Cval), \ |
985 | + testOk(checkValues(puser1, 99, Ival, Fval, Dval, Sval1, Sval2, Cval, Dstr), \ |
986 | "guards intact, values correct"); \ |
987 | if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ |
988 | testDiag("expected %#x - called %#x", e1, c1); \ |
989 | @@ -891,7 +1019,7 @@ MAIN(chfPluginTest) |
990 | testDiag("expected %#x - called %#x", e1, c1); |
991 | |
992 | #define CONVTESTBAD(Var, Val, Typ) \ |
993 | - e1 = e_alloc | e_error | e_free; c1 = 0; \ |
994 | + e1 = e_alloc | e_error | e_free; c1 = 0; e2 = 0; \ |
995 | testDiag("Calling dbChannelCreate x.{sloppy:{"#Var":"#Val"}}"); \ |
996 | testOk(!(pch = dbChannelCreate("x.{sloppy:{"#Var":"#Val"}}")), \ |
997 | "create channel for sloppy parsing: "#Typ" (bad) for "#Var); \ |
998 | @@ -901,67 +1029,79 @@ MAIN(chfPluginTest) |
999 | |
1000 | /* To integer */ |
1001 | testHead("SLOPPY parsing: conversion to integer"); |
1002 | - CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 0, 4); |
1003 | - CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 0, 4); |
1004 | + CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 0, 4, "default"); |
1005 | + CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 0, 4, "default"); |
1006 | CONVTESTBAD(i, "9234567890", out-of-range string); |
1007 | CONVTESTBAD(i, ".4", invalid string); |
1008 | - CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 0, 4); |
1009 | - CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 0, 4); |
1010 | + CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 0, 4, "default"); |
1011 | + CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 0, 4, "default"); |
1012 | CONVTESTBAD(i, 34.7e14, out-of-range double); |
1013 | |
1014 | /* To boolean */ |
1015 | testHead("SLOPPY parsing: conversion to boolean"); |
1016 | - CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 0, 4); |
1017 | - CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 0, 4); |
1018 | - CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 0, 4); |
1019 | - CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 0, 4); |
1020 | + CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 0, 4, "default"); |
1021 | + CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 0, 4, "default"); |
1022 | + CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 0, 4, "default"); |
1023 | + CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 0, 4, "default"); |
1024 | CONVTESTBAD(f, ".4", invalid .4 string); |
1025 | CONVTESTBAD(f, "Flase", misspelled invalid string); |
1026 | - CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 0, 4); |
1027 | - CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 0, 4); |
1028 | - CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 0, 4); |
1029 | - CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 0, 4); |
1030 | - CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 0, 4); |
1031 | - CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 0, 4); |
1032 | - CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 0, 4); |
1033 | + CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 0, 4, "default"); |
1034 | + CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 0, 4, "default"); |
1035 | + CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 0, 4, "default"); |
1036 | + CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 0, 4, "default"); |
1037 | + CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 0, 4, "default"); |
1038 | + CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 0, 4, "default"); |
1039 | + CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 0, 4, "default"); |
1040 | |
1041 | /* To double */ |
1042 | testHead("SLOPPY parsing: conversion to double"); |
1043 | - CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 0, 4); |
1044 | - CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 0, 4); |
1045 | - CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 0, 4); |
1046 | - CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 0, 4); |
1047 | + CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 0, 4, "default"); |
1048 | + CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 0, 4, "default"); |
1049 | + CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 0, 4, "default"); |
1050 | + CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 0, 4, "default"); |
1051 | CONVTESTBAD(d, "1.67e407", out-of-range double string); |
1052 | CONVTESTBAD(d, "blubb", invalid blubb string); |
1053 | - CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 0, 4); |
1054 | - CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 0, 4); |
1055 | - CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 0, 4); |
1056 | - CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 0, 4); |
1057 | + CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 0, 4, "default"); |
1058 | + CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 0, 4, "default"); |
1059 | + CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 0, 4, "default"); |
1060 | + CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 0, 4, "default"); |
1061 | |
1062 | /* To string */ |
1063 | testHead("SLOPPY parsing: conversion to string"); |
1064 | - CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 0, 4); |
1065 | - CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 0, 4); |
1066 | - CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 0, 4); |
1067 | - CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 0, 4); |
1068 | - CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 0, 4); |
1069 | - CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", "-1.23e+026", 4); |
1070 | - CONVTESTGOOD(s, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "-1.23456789123e+26", "-1.23456789123e+026", 4); |
1071 | + CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 0, 4, "default"); |
1072 | + CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 0, 4, "default"); |
1073 | + CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 0, 4, "default"); |
1074 | + CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 0, 4, "default"); |
1075 | + CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 0, 4, "default"); |
1076 | + CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", "-1.23e+026", 4, "default"); |
1077 | + CONVTESTGOOD(s, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "-1.23456789123e+26", "-1.23456789123e+026", 4, "default"); |
1078 | |
1079 | /* To Enum */ |
1080 | testHead("SLOPPY parsing: conversion to enum"); |
1081 | - CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 0, 2); |
1082 | + CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 0, 2, "default"); |
1083 | CONVTESTBAD(c, 3, invalid integer choice); |
1084 | CONVTESTBAD(c, 3.2, double); |
1085 | - CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 0, 1); |
1086 | + CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 0, 1, "default"); |
1087 | CONVTESTBAD(c, "blubb", invalid string choice); |
1088 | |
1089 | + /* To allocated string */ |
1090 | + testHead("SLOPPY parsing: conversion to string"); |
1091 | + CONVTESTGOOD(a, 12345, positive integer, 12, 1, 1.234e5, "hello", 0, 4, "12345"); |
1092 | + CONVTESTGOOD(a, -1234567891, negative integer, 12, 1, 1.234e5, "hello", 0, 4, "-1234567891"); |
1093 | + CONVTESTGOOD(a, true, true boolean, 12, 1, 1.234e5, "hello", 0, 4, "true"); |
1094 | + CONVTESTGOOD(a, false, false boolean, 12, 1, 1.234e5, "hello", 0, 4, "false"); |
1095 | + CONVTESTGOOD(a, 123e4, small positive double, 12, 1, 1.234e5, "hello", 0, 4, "1230000"); |
1096 | + CONVTESTGOOD(a, -123e24, negative double, 12, 1, 1.234e5, "hello", 0, 4, "-1.23e+26"); |
1097 | + CONVTESTGOOD(a, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "hello", 0, 4, "-1.23456789123e+26"); |
1098 | + CONVTESTGOOD(a, {}, empty argument, 12, 1, 1.234e5, "hello", 0, 4, 0); |
1099 | + CONVTESTGOOD(a, '', empty string, 12, 1, 1.234e5, "hello", 0, 4, ""); |
1100 | + |
1101 | /* Registering and running filter callbacks */ |
1102 | |
1103 | #define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \ |
1104 | testHead("Filter chain test, "Type" filter"); \ |
1105 | offset = 0; \ |
1106 | - e1 = e_alloc | e_ok; c1 = 0; \ |
1107 | + e1 = e_alloc | e_ok; c1 = 0; e2 = 0; \ |
1108 | testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \ |
1109 | if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ |
1110 | e1 = e_open | ExpReg; c1 = 0; \ |
1111 | diff --git a/modules/database/test/std/filters/decTest.c b/modules/database/test/std/filters/decTest.c |
1112 | index e0961af..1eefe5d 100644 |
1113 | --- a/modules/database/test/std/filters/decTest.c |
1114 | +++ b/modules/database/test/std/filters/decTest.c |
1115 | @@ -251,8 +251,8 @@ MAIN(decTest) |
1116 | |
1117 | /* Decimation (N=4) */ |
1118 | |
1119 | - testHead("Decimation (n=4)"); |
1120 | - testOk(!!(pch = dbChannelCreate("x.VAL{dec:{n:4}}")), |
1121 | + testHead("Decimation (n=4) with simplified syntax"); |
1122 | + testOk(!!(pch = dbChannelCreate("x.VAL{dec:4}")), |
1123 | "dbChannel with plugin dec (n=4) created"); |
1124 | |
1125 | checkAndOpenChannel(pch, plug); |
1126 | diff --git a/modules/database/test/std/filters/tsTest.c b/modules/database/test/std/filters/tsTest.c |
1127 | index bd0b799..31ceb99 100644 |
1128 | --- a/modules/database/test/std/filters/tsTest.c |
1129 | +++ b/modules/database/test/std/filters/tsTest.c |
1130 | @@ -74,7 +74,7 @@ MAIN(tsTest) |
1131 | |
1132 | testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); |
1133 | |
1134 | - testOk(!!(pch = dbChannelCreate("x.VAL{ts:{}}")), "dbChannel with plugin ts created"); |
1135 | + testOk(!!(pch = dbChannelCreate("x.{ts}")), "dbChannel with plugin ts created"); |
1136 | testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); |
1137 | |
1138 | memset(&fl, PATTERN, sizeof(fl)); |
Question: Should the old chfPluginArgString be dropped and be replaced by the new chfPluginArgStr ingAlloc support (redefining the meaning of chfPluginArgStr ing)?