Merge ~dirk.zimoch/epics-base:chfPluginImprovements into ~epics-core/epics-base/+git/epics-base:7.0

Proposed by Dirk Zimoch
Status: Needs review
Proposed branch: ~dirk.zimoch/epics-base:chfPluginImprovements
Merge into: ~epics-core/epics-base/+git/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)
Reviewer Review Type Date Requested Status
EPICS Core Developers Pending
Review via email: mp+399636@code.launchpad.net

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, chfPluginArgStringAlloc works on char* members. It performs the following actions:
* If a chfPluginArgStringAlloc is provided by the user, the char* member is first NULLed, then filled with a dynamically allocated string (which may fail due to memory shortage)
* 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:{parameter:value}}
* If no parameter is requires, {filter} can be used instead of {filter:{}}
3. Documentarion changed: Use JSON5 features in examples.

To post a comment you must log in.
Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Question: Should the old chfPluginArgString be dropped and be replaced by the new chfPluginArgStringAlloc support (redefining the meaning of chfPluginArgString)?

Revision history for this message
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:{onlyparameter:value}}
allow: {filter:value}

Revision history for this message
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

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Re-pushed, this time with tests.

4395cf4... by Dirk Zimoch

simplified filter syntax for no argument

Revision history for this message
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.{ts,arr:{i:3},dec:1}"
The filter is created as if called with {}.

Revision history for this message
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

Revision history for this message
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 chfPluginArgStringAlloc.

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md
index 17b039c..b79e5f1 100644
--- a/documentation/RELEASE_NOTES.md
+++ b/documentation/RELEASE_NOTES.md
@@ -17,6 +17,22 @@ should also be read to understand what has changed since earlier releases.
1717
18<!-- Insert new items immediately below here ... -->18<!-- Insert new items immediately below here ... -->
1919
20## Channel filter improvements
21
22The new chfStringAlloc and chfTagStringAlloc macros allows to store string
23arguments with arbitrary length in char* members instead of fixed size arrays.
24Memory is automaticaly allocated and released.
25The "sync" filter has has been updated to allow arbitrarily long state names.
26
27Channel filters that require only one parameter (either they have only one
28parameter or only one parameter is required) can be used with a simplified
29syntax where only the value of that parameter is given instead of an object
30with one member.
31
32Channel filters that require no parameter at all can be used with a simplified
33syntax where only the filter name is given but not even an empty parameter set.
34This extends the JSON5 syntax.
35
20### IOCsh sets `${PWD}`36### IOCsh sets `${PWD}`
2137
22IOC shell will now ensure `${PWD}` is set on startup,38IOC shell will now ensure `${PWD}` is set on startup,
@@ -105,7 +121,6 @@ that the variables referenced by output pointers are initialized.
105#endif121#endif
106```122```
107123
108
109-----124-----
110125
111## EPICS Release 7.0.5126## EPICS Release 7.0.5
diff --git a/modules/database/src/ioc/db/chfPlugin.c b/modules/database/src/ioc/db/chfPlugin.c
index c84e086..4f103e3 100644
--- a/modules/database/src/ioc/db/chfPlugin.c
+++ b/modules/database/src/ioc/db/chfPlugin.c
@@ -70,6 +70,7 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val)
70 const chfPluginEnumType *emap;70 const chfPluginEnumType *emap;
71 double *dval;71 double *dval;
72 char *sval;72 char *sval;
73 char **psval;
73 int ret;74 int ret;
74 char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */75 char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */
7576
@@ -104,6 +105,11 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val)
104 strncpy(sval, buff, opt->size-1);105 strncpy(sval, buff, opt->size-1);
105 sval[opt->size-1]='\0';106 sval[opt->size-1]='\0';
106 break;107 break;
108 case chfPluginArgStringAlloc:
109 psval = (char **) (user + opt->dataOffset);
110 *psval = realloc(*psval, 22);
111 if (*psval) sprintf(*psval, "%ld", (long)val);
112 break;
107 case chfPluginArgEnum:113 case chfPluginArgEnum:
108 eval = (int*) (user + opt->dataOffset);114 eval = (int*) (user + opt->dataOffset);
109 for (emap = opt->enums; emap && emap->name; emap++) {115 for (emap = opt->enums; emap && emap->name; emap++) {
@@ -116,7 +122,7 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val)
116 return -1;122 return -1;
117 }123 }
118 break;124 break;
119 case chfPluginArgInvalid:125 default:
120 return -1;126 return -1;
121 }127 }
122 return 0;128 return 0;
@@ -131,6 +137,7 @@ static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val)
131 epicsInt32 *ival;137 epicsInt32 *ival;
132 double *dval;138 double *dval;
133 char *sval;139 char *sval;
140 char **psval;
134141
135#ifdef DEBUG_CHF142#ifdef DEBUG_CHF
136 printf("Got a boolean for %s (type %d): %d\n",143 printf("Got a boolean for %s (type %d): %d\n",
@@ -162,8 +169,12 @@ static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val)
162 strncpy(sval, val ? "true" : "false", opt->size - 1);169 strncpy(sval, val ? "true" : "false", opt->size - 1);
163 sval[opt->size - 1] = '\0';170 sval[opt->size - 1] = '\0';
164 break;171 break;
165 case chfPluginArgEnum:172 case chfPluginArgStringAlloc:
166 case chfPluginArgInvalid:173 psval = (char **) (user + opt->dataOffset);
174 *psval = realloc(*psval, 6);
175 if (*psval) strcpy(*psval, val ? "true" : "false");
176 break;
177 default:
167 return -1;178 return -1;
168 }179 }
169 return 0;180 return 0;
@@ -180,6 +191,7 @@ store_double_value(const chfPluginArgDef *opt, void *vuser, double val)
180 epicsInt32 *ival;191 epicsInt32 *ival;
181 double *dval;192 double *dval;
182 char *sval;193 char *sval;
194 char **psval;
183 int i;195 int i;
184196
185#ifdef DEBUG_CHF197#ifdef DEBUG_CHF
@@ -217,8 +229,12 @@ store_double_value(const chfPluginArgDef *opt, void *vuser, double val)
217 return -1;229 return -1;
218 }230 }
219 break;231 break;
220 case chfPluginArgEnum:232 case chfPluginArgStringAlloc:
221 case chfPluginArgInvalid:233 psval = (char **) (user + opt->dataOffset);
234 *psval = realloc(*psval, 25);
235 if (*psval) epicsSnprintf(*psval, 24, "%.17g", val);
236 break;
237 default:
222 return -1;238 return -1;
223 }239 }
224 return 0;240 return 0;
@@ -237,6 +253,7 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val,
237 const chfPluginEnumType *emap;253 const chfPluginEnumType *emap;
238 double *dval;254 double *dval;
239 char *sval;255 char *sval;
256 char **psval;
240 char *end;257 char *end;
241 size_t i;258 size_t i;
242259
@@ -246,6 +263,7 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val,
246#endif263#endif
247264
248 if (!opt->convert && opt->optType != chfPluginArgString &&265 if (!opt->convert && opt->optType != chfPluginArgString &&
266 opt->optType != chfPluginArgStringAlloc &&
249 opt->optType != chfPluginArgEnum) {267 opt->optType != chfPluginArgEnum) {
250 return -1;268 return -1;
251 }269 }
@@ -279,6 +297,11 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val,
279 strncpy(sval, val, i);297 strncpy(sval, val, i);
280 sval[i] = '\0';298 sval[i] = '\0';
281 break;299 break;
300 case chfPluginArgStringAlloc:
301 psval = (char **) (user + opt->dataOffset);
302 free(*psval);
303 *psval = epicsStrnDup(val, len);
304 break;
282 case chfPluginArgEnum:305 case chfPluginArgEnum:
283 eval = (int*) (user + opt->dataOffset);306 eval = (int*) (user + opt->dataOffset);
284 for (emap = opt->enums; emap && emap->name; emap++) {307 for (emap = opt->enums; emap && emap->name; emap++) {
@@ -291,7 +314,7 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val,
291 return -1;314 return -1;
292 }315 }
293 break;316 break;
294 case chfPluginArgInvalid:317 default:
295 return -1;318 return -1;
296 }319 }
297 return 0;320 return 0;
@@ -314,6 +337,7 @@ static parse_result parse_start(chFilter *filter)
314{337{
315 chfPlugin *p = (chfPlugin*) filter->plug->puser;338 chfPlugin *p = (chfPlugin*) filter->plug->puser;
316 chfFilter *f;339 chfFilter *f;
340 size_t i;
317341
318 /* Filter context */342 /* Filter context */
319 /* FIXME: Use a free-list */343 /* FIXME: Use a free-list */
@@ -339,25 +363,61 @@ static parse_result parse_start(chFilter *filter)
339 }363 }
340 }364 }
341365
366 /* If there is only one argument or only one required argument,
367 allow shortcut without map */
368 if (p->nopts == 1)
369 f->nextParam = 0;
370 else
371 for (i = 0; i < p->nopts; i++) {
372 if (p->opts[i].required) {
373 if (f->nextParam == -1) {
374 f->nextParam = i;
375 } else {
376 f->nextParam = -1;
377 break;
378 }
379 }
380 }
381 if (f->nextParam >= 0 &&
382 p->opts[f->nextParam].optType == chfPluginArgStringAlloc) {
383 *(char **) ((char*) f->puser + p->opts[f->nextParam].dataOffset) = NULL;
384 }
385
342 filter->puser = (void*) f;386 filter->puser = (void*) f;
343387
344 return parse_continue;388 return parse_continue;
345389
346 errplugin:390 errplugin:
347 free(f->found);
348 errbitarray:391 errbitarray:
349 free(f); /* FIXME: Use a free-list */392 freeInstanceData(f);
350 errfctx:393 errfctx:
351 return parse_stop;394 return parse_stop;
352}395}
353396
397static void freePvt(chFilter *filter) {
398 chfPlugin *p = (chfPlugin*) filter->plug->puser;
399 chfFilter *f = (chfFilter*) filter->puser;
400 int i;
401
402 /* free all chfPluginArgStringAlloc options we had allocated */
403 for(i = 0; i < p->nopts; i++) {
404 if (p->opts[i].optType == chfPluginArgStringAlloc &&
405 (f->found[i/32] & (1<<(i%32)))) {
406 char **psval = (char **) ((char*) f->puser + p->opts[i].dataOffset);
407 free(*psval);
408 *psval = NULL;
409 }
410 }
411 if (p->pif->freePvt) p->pif->freePvt(f->puser);
412}
413
354static void parse_abort(chFilter *filter) {414static void parse_abort(chFilter *filter) {
355 chfPlugin *p = (chfPlugin*) filter->plug->puser;415 chfPlugin *p = (chfPlugin*) filter->plug->puser;
356 chfFilter *f = (chfFilter*) filter->puser;416 chfFilter *f = (chfFilter*) filter->puser;
357417
358 /* Call the plugin to tell it we're aborting */418 /* Call the plugin to tell it we're aborting */
359 if (p->pif->parse_error) p->pif->parse_error(f->puser);419 if (p->pif->parse_error) p->pif->parse_error(f->puser);
360 if (p->pif->freePvt) p->pif->freePvt(f->puser);420 freePvt(filter);
361 freeInstanceData(f);421 freeInstanceData(f);
362}422}
363423
@@ -365,22 +425,11 @@ static parse_result parse_end(chFilter *filter)
365{425{
366 chfPlugin *p = (chfPlugin*) filter->plug->puser;426 chfPlugin *p = (chfPlugin*) filter->plug->puser;
367 chfFilter *f = (chfFilter*) filter->puser;427 chfFilter *f = (chfFilter*) filter->puser;
368 int i;
369
370 /* Check if all required arguments were supplied */
371 for(i = 0; i < (p->nopts/32)+1; i++) {
372 if ((f->found[i] & p->required[i]) != p->required[i]) {
373 if (p->pif->parse_error) p->pif->parse_error(f->puser);
374 if (p->pif->freePvt) p->pif->freePvt(f->puser);
375 freeInstanceData(f);
376 return parse_stop;
377 }
378 }
379428
380 /* Call the plugin to tell it we're done */429 /* Call the plugin to tell it we're done */
381 if (p->pif->parse_ok) {430 if (p->pif->parse_ok) {
382 if (p->pif->parse_ok(f->puser)) {431 if (p->pif->parse_ok(f->puser)) {
383 if (p->pif->freePvt) p->pif->freePvt(f->puser);432 freePvt(filter);
384 freeInstanceData(f);433 freeInstanceData(f);
385 return parse_stop;434 return parse_stop;
386 }435 }
@@ -478,6 +527,12 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen)
478 *tag = opts[i].choice;527 *tag = opts[i].choice;
479 }528 }
480529
530 /* NULL all found chfPluginArgStringAlloc options */
531 if (opts[i].optType == chfPluginArgStringAlloc &&
532 !(f->found[i/32] & 1<<(i%32))) {
533 char **psval = (char **) ((char*) f->puser + opts[i].dataOffset);
534 *psval = NULL;
535 }
481 f->found[i/32] |= 1<<(i%32);536 f->found[i/32] |= 1<<(i%32);
482 /* Mark tag and all other options pointing to the same data as found */537 /* Mark tag and all other options pointing to the same data as found */
483 for (cur = opts, j = 0; cur && cur->name; cur++, j++) {538 for (cur = opts, j = 0; cur && cur->name; cur++, j++) {
@@ -491,6 +546,15 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen)
491546
492static parse_result parse_end_map(chFilter *filter)547static parse_result parse_end_map(chFilter *filter)
493{548{
549 chfPlugin *p = (chfPlugin*) filter->plug->puser;
550 chfFilter *f = (chfFilter*) filter->puser;
551 int i;
552
553 /* Check if all required arguments were supplied */
554 for(i = 0; i < (p->nopts/32)+1; i++) {
555 if ((f->found[i] & p->required[i]) != p->required[i])
556 return parse_stop;
557 }
494 return parse_continue;558 return parse_continue;
495}559}
496560
@@ -545,9 +609,8 @@ static void channel_close(chFilter *filter)
545 chfFilter *f = (chfFilter*) filter->puser;609 chfFilter *f = (chfFilter*) filter->puser;
546610
547 if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser);611 if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser);
548 if (p->pif->freePvt) p->pif->freePvt(f->puser);612 freePvt(filter);
549 free(f->found);613 freeInstanceData(f);
550 free(f); /* FIXME: Use a free-list */
551}614}
552615
553static void plugin_free(void* puser)616static void plugin_free(void* puser)
@@ -649,7 +712,16 @@ chfPluginRegister(const char* key, const chfPluginIf *pif,
649 return -1;712 return -1;
650 }713 }
651 break;714 break;
652 case chfPluginArgInvalid:715 case chfPluginArgStringAlloc:
716 if (cur->size != sizeof(char*)) {
717 /* Catch if someone has given us a something else than a pointer
718 */
719 errlogPrintf("Plugin %s: %d bytes not a pointer type for string %s\n",
720 key, cur->size, cur->name);
721 return -1;
722 }
723 break;
724 default:
653 errlogPrintf("Plugin %s: storage type for %s is not defined\n",725 errlogPrintf("Plugin %s: storage type for %s is not defined\n",
654 key, cur->name);726 key, cur->name);
655 return -1;727 return -1;
diff --git a/modules/database/src/ioc/db/chfPlugin.h b/modules/database/src/ioc/db/chfPlugin.h
index 5867a66..eed218d 100644
--- a/modules/database/src/ioc/db/chfPlugin.h
+++ b/modules/database/src/ioc/db/chfPlugin.h
@@ -52,6 +52,7 @@ struct db_field_log;
52 * epicsInt32 ival2;52 * epicsInt32 ival2;
53 * int enumval;53 * int enumval;
54 * char strval[20];54 * char strval[20];
55 * char* str;
55 * char boolval;56 * char boolval;
56 * } myStruct;57 * } myStruct;
57 *58 *
@@ -64,6 +65,7 @@ struct db_field_log;
64 * chfInt32 (myStruct, ival2, "Second" , 1, 0),65 * chfInt32 (myStruct, ival2, "Second" , 1, 0),
65 * chfDouble (myStruct, dval, "Double" , 1, 0),66 * chfDouble (myStruct, dval, "Double" , 1, 0),
66 * chfString (myStruct, strval , "String" , 1, 0),67 * chfString (myStruct, strval , "String" , 1, 0),
68 * chfStringAlloc(myStruct, str , "String" , 1, 0),
67 * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum),69 * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum),
68 * chfBoolean (myStruct, boolval, "Bool" , 1, 0),70 * chfBoolean (myStruct, boolval, "Bool" , 1, 0),
69 * chfPluginEnd71 * chfPluginEnd
@@ -225,7 +227,8 @@ typedef enum chfPluginArg {
225 chfPluginArgInt32,227 chfPluginArgInt32,
226 chfPluginArgDouble,228 chfPluginArgDouble,
227 chfPluginArgString,229 chfPluginArgString,
228 chfPluginArgEnum230 chfPluginArgEnum,
231 chfPluginArgStringAlloc
229} chfPluginArg;232} chfPluginArg;
230233
231typedef struct chfPluginEnumType {234typedef struct chfPluginEnumType {
@@ -268,6 +271,10 @@ typedef struct chfPluginArgDef {
268 {Name, chfPluginArgEnum, Req, Conv, 0, 0, 0, \271 {Name, chfPluginArgEnum, Req, Conv, 0, 0, 0, \
269 OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}272 OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}
270273
274#define chfStringAlloc(Struct, Member, Name, Req, Conv) \
275 {Name, chfPluginArgStringAlloc, Req, Conv, 0, 0, 0, \
276 OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
277
271/* Tagged arguments */278/* Tagged arguments */
272279
273#define chfTagInt32(Struct, Member, Name, Tag, Choice, Req, Conv) \280#define chfTagInt32(Struct, Member, Name, Tag, Choice, Req, Conv) \
@@ -290,6 +297,10 @@ typedef struct chfPluginArgDef {
290 {Name, chfPluginArgEnum, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \297 {Name, chfPluginArgEnum, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
291 OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}298 OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}
292299
300#define chfTagStringAlloc(Struct, Member, Name, Tag, Choice, Req, Conv) \
301 {Name, chfPluginArgStringAlloc, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
302 OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
303
293#define chfPluginArgEnd {0}304#define chfPluginArgEnd {0}
294305
295/* Extra output when parsing and converting */306/* Extra output when parsing and converting */
diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
index a806f3c..edf2124 100644
--- a/modules/database/src/ioc/db/dbChannel.c
+++ b/modules/database/src/ioc/db/dbChannel.c
@@ -291,6 +291,32 @@ static long chf_parse(dbChannel *chan, const char **pjson)
291 case yajl_status_error: {291 case yajl_status_error: {
292 unsigned char *err;292 unsigned char *err;
293293
294 if (parser.depth == 0 && parser.filter &&
295 (json[ylen-1] == ',' || json[ylen-1] == '}')) {
296 /* We get here only if filter is called without arguments. */
297 /* Let's fake ":{}" */
298 if (chf_start_map(&parser) == parse_continue &&
299 chf_end_map(&parser) == parse_continue) {
300 *pjson += ylen;
301 status = 0;
302 if (json[ylen]) {
303 /* some filters left, let's parse them too */
304 char *jsoncopy = epicsStrDup(json+ylen-1);
305 if (!jsoncopy) {
306 printf("dbChannelCreate: out of memory\n");
307 break;
308 }
309 jsoncopy[0] = '{';
310 json = jsoncopy;
311 status = chf_parse(chan, &json);
312 ylen = json-jsoncopy-1;
313 *pjson += ylen;
314 free(jsoncopy);
315 }
316 break;
317 }
318 }
319
294 err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);320 err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
295 printf("dbChannelCreate: %s\n", err);321 printf("dbChannelCreate: %s\n", err);
296 yajl_free_error(yh, err);322 yajl_free_error(yh, err);
diff --git a/modules/database/src/std/filters/filters.dbd.pod b/modules/database/src/std/filters/filters.dbd.pod
index 7b31466..5c1219d 100644
--- a/modules/database/src/std/filters/filters.dbd.pod
+++ b/modules/database/src/std/filters/filters.dbd.pod
@@ -40,6 +40,13 @@ Parameters to that filter are provided as the value part of the name/value pair,
40and will normally appear as a child JSON object consisting of name/value pairs40and will normally appear as a child JSON object consisting of name/value pairs
41inside a nested pair of braces C< {} >.41inside a nested pair of braces C< {} >.
4242
43If a filter takes only one parameter or only one parameter is required, a short
44form may be used where the parameter vallue is passed directly instead of
45an object in braces C< {} >.
46
47If a filter does not need any parameter, the value part can be skipped
48completely. Only the filter name is needed in this case.
49
43=head4 Example Filter50=head4 Example Filter
4451
45Given a record called C<test:channel> the following would apply a filter C<f> to52Given a record called C<test:channel> the following would apply a filter C<f> to
@@ -69,12 +76,12 @@ to the EPICS epoch if the record has never processed.
6976
70=head4 Parameters77=head4 Parameters
7178
72None, use an empty pair of braces.79None.
7380
74=head4 Example81=head4 Example
7582
76 Hal$ caget -a 'test:channel.{"ts":{}}'83 Hal$ caget -a 'test:channel.{"ts"}'
77 test:channel.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID84 test:channel.{"ts"} 2012-08-28 22:10:31.192547 0 UDF INVALID
78 Hal$ caget -a 'test:channel'85 Hal$ caget -a 'test:channel'
79 test:channel <undefined> 0 UDF INVALID86 test:channel <undefined> 0 UDF INVALID
8087
@@ -281,9 +288,10 @@ client connects.
281=head4 Example288=head4 Example
282289
283To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel290To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel
284once every minute:291once every minute. As the "dec" filter requires only one parameter, the short
292syntax may be used:
285293
286 Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}'294 Hal$ camonitor 'test:channel' 'test:channel.{"dec":60}'
287 ...295 ...
288296
289=cut297=cut
diff --git a/modules/database/src/std/filters/sync.c b/modules/database/src/std/filters/sync.c
index 8140c36..f31f677 100644
--- a/modules/database/src/std/filters/sync.c
+++ b/modules/database/src/std/filters/sync.c
@@ -21,8 +21,6 @@
21#include "epicsAssert.h"21#include "epicsAssert.h"
22#include "epicsExport.h"22#include "epicsExport.h"
2323
24#define STATE_NAME_LENGTH 20
25
26typedef enum syncMode {24typedef enum syncMode {
27 syncModeBefore=0,25 syncModeBefore=0,
28 syncModeFirst=1,26 syncModeFirst=1,
@@ -45,7 +43,7 @@ chfPluginEnumType modeEnum[] = {
4543
46typedef struct myStruct {44typedef struct myStruct {
47 syncMode mode;45 syncMode mode;
48 char state[STATE_NAME_LENGTH];46 char* state;
49 dbStateId id;47 dbStateId id;
50 db_field_log *lastfl;48 db_field_log *lastfl;
51 int laststate:1;49 int laststate:1;
@@ -55,14 +53,14 @@ static void *myStructFreeList;
5553
56static const54static const
57chfPluginArgDef opts[] = {55chfPluginArgDef opts[] = {
58 chfEnum (myStruct, mode, "m", 1, 1, modeEnum),56 chfEnum (myStruct, mode, "m", 1, 1, modeEnum),
59 chfString (myStruct, state, "s", 1, 0),57 chfStringAlloc (myStruct, state, "s", 1, 0),
60 chfTagString (myStruct, state, "before", mode, 0, 1, 0),58 chfTagStringAlloc (myStruct, state, "before", mode, 0, 1, 0),
61 chfTagString (myStruct, state, "first", mode, 1, 1, 0),59 chfTagStringAlloc (myStruct, state, "first", mode, 1, 1, 0),
62 chfTagString (myStruct, state, "last", mode, 2, 1, 0),60 chfTagStringAlloc (myStruct, state, "last", mode, 2, 1, 0),
63 chfTagString (myStruct, state, "after", mode, 3, 1, 0),61 chfTagStringAlloc (myStruct, state, "after", mode, 3, 1, 0),
64 chfTagString (myStruct, state, "while", mode, 4, 1, 0),62 chfTagStringAlloc (myStruct, state, "while", mode, 4, 1, 0),
65 chfTagString (myStruct, state, "unless", mode, 5, 1, 0),63 chfTagStringAlloc (myStruct, state, "unless", mode, 5, 1, 0),
66 chfPluginArgEnd64 chfPluginArgEnd
67};65};
6866
diff --git a/modules/database/test/ioc/db/chfPluginTest.c b/modules/database/test/ioc/db/chfPluginTest.c
index 4a1667c..6d281fc 100644
--- a/modules/database/test/ioc/db/chfPluginTest.c
+++ b/modules/database/test/ioc/db/chfPluginTest.c
@@ -68,6 +68,7 @@ typedef struct myStruct {
68 char c1[2];68 char c1[2];
69 int offpre;69 int offpre;
70 int offpost;70 int offpost;
71 char* astr;
71} myStruct;72} myStruct;
7273
73static const74static const
@@ -75,54 +76,76 @@ chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} };
7576
76static const77static const
77chfPluginArgDef sloppyTaggedOpts[] = {78chfPluginArgDef sloppyTaggedOpts[] = {
78 chfInt32 (myStruct, tval, "t" , 0, 0),79 chfInt32 (myStruct, tval, "t" , 0, 0),
79 chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0),80 chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0),
80 chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0),81 chfTagBoolean (myStruct, flag, "F" , tval, 2, 0, 0),
81 chfTagDouble (myStruct, dval, "D" , tval, 3, 0, 0),82 chfTagDouble (myStruct, dval, "D" , tval, 3, 0, 0),
82 chfTagString (myStruct, str, "S" , tval, 4, 0, 0),83 chfTagString (myStruct, str, "S" , tval, 4, 0, 0),
83 chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum),84 chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum),
85 chfTagStringAlloc(myStruct, astr, "A" , tval, 6, 0, 0),
84 chfPluginArgEnd86 chfPluginArgEnd
85};87};
8688
87static const89static const
88chfPluginArgDef strictTaggedOpts[] = {90chfPluginArgDef strictTaggedOpts[] = {
89 chfInt32 (myStruct, tval, "t" , 1, 0),91 chfInt32 (myStruct, tval, "t" , 1, 0),
90 chfBoolean (myStruct, flag, "f" , 1, 0),92 chfBoolean (myStruct, flag, "f" , 1, 0),
91 chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0),93 chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0),
92 chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0),94 chfTagBoolean (myStruct, flag, "F" , tval, 2, 0, 0),
93 chfTagDouble (myStruct, dval, "D" , tval, 3, 1, 0),95 chfTagDouble (myStruct, dval, "D" , tval, 3, 1, 0),
94 chfTagDouble (myStruct, dval, "D2", tval, 4, 1, 0),96 chfTagDouble (myStruct, dval, "D2", tval, 4, 1, 0),
95 chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum),97 chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum),
98 chfTagStringAlloc(myStruct, astr, "A", tval, 6, 0, 0),
96 chfPluginArgEnd99 chfPluginArgEnd
97};100};
98101
99static const102static const
100chfPluginArgDef strictOpts[] = {103chfPluginArgDef strictOpts[] = {
101 chfInt32 (myStruct, ival, "i" , 1, 0),104 chfInt32 (myStruct, ival, "i" , 1, 0),
102 chfBoolean(myStruct, flag, "f" , 1, 0),105 chfBoolean (myStruct, flag, "f" , 1, 0),
103 chfDouble (myStruct, dval, "d" , 1, 0),106 chfDouble (myStruct, dval, "d" , 1, 0),
104 chfString (myStruct, str, "s" , 1, 0),107 chfString (myStruct, str, "s" , 1, 0),
105 chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum),108 chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum),
109 chfStringAlloc(myStruct, astr, "a", 1, 0),
106 chfPluginArgEnd110 chfPluginArgEnd
107};111};
108112
109static const113static const
110chfPluginArgDef noconvOpts[] = {114chfPluginArgDef noconvOpts[] = {
111 chfInt32 (myStruct, ival, "i" , 0, 0),115 chfInt32 (myStruct, ival, "i" , 0, 0),
112 chfBoolean(myStruct, flag, "f" , 0, 0),116 chfBoolean (myStruct, flag, "f" , 0, 0),
113 chfDouble (myStruct, dval, "d" , 0, 0),117 chfDouble (myStruct, dval, "d" , 0, 0),
114 chfString (myStruct, str, "s" , 0, 0),118 chfString (myStruct, str, "s" , 0, 0),
115 chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum),119 chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum),
120 chfStringAlloc(myStruct, astr, "a", 0, 0),
116 chfPluginArgEnd121 chfPluginArgEnd
117};122};
118123
119static const124static const
120chfPluginArgDef sloppyOpts[] = {125chfPluginArgDef sloppyOpts[] = {
121 chfInt32 (myStruct, ival, "i" , 0, 1),126 chfInt32 (myStruct, ival, "i" , 0, 1),
122 chfBoolean(myStruct, flag, "f" , 0, 1),127 chfBoolean (myStruct, flag, "f" , 0, 1),
123 chfDouble (myStruct, dval, "d" , 0, 1),128 chfDouble (myStruct, dval, "d" , 0, 1),
124 chfString (myStruct, str, "s" , 0, 1),129 chfString (myStruct, str, "s" , 0, 1),
125 chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum),130 chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum),
131 chfStringAlloc(myStruct, astr, "a", 0, 1),
132 chfPluginArgEnd
133};
134
135static const
136chfPluginArgDef onlyOneOpts[] = {
137 chfInt32 (myStruct, ival, "i" , 0, 0),
138 chfPluginArgEnd
139};
140
141static const
142chfPluginArgDef onlyOneRequiredOpts[] = {
143 chfInt32 (myStruct, ival, "i" , 0, 0),
144 chfBoolean (myStruct, flag, "f" , 0, 0),
145 chfDouble (myStruct, dval, "d" , 1, 0),
146 chfString (myStruct, str, "s" , 0, 0),
147 chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum),
148 chfStringAlloc(myStruct, astr, "a", 0, 0),
126 chfPluginArgEnd149 chfPluginArgEnd
127};150};
128151
@@ -154,6 +177,7 @@ chfPluginArgDef brokenOpts4[] = {
154int p_ok_return = 0;177int p_ok_return = 0;
155int c_open_return = 0;178int c_open_return = 0;
156void *puser1, *puser2;179void *puser1, *puser2;
180char defaultstring[] = "default";
157181
158static void clearStruct(void *p) {182static void clearStruct(void *p) {
159 myStruct *my = (myStruct*) p;183 myStruct *my = (myStruct*) p;
@@ -168,6 +192,7 @@ static void clearStruct(void *p) {
168 my->dval = 1.234e5;192 my->dval = 1.234e5;
169 strcpy(my->str, "hello");193 strcpy(my->str, "hello");
170 my->enumval = 4;194 my->enumval = 4;
195 my->astr = defaultstring;
171}196}
172197
173static char inst(void* user) {198static char inst(void* user) {
@@ -177,7 +202,6 @@ static char inst(void* user) {
177static void * allocPvt(void)202static void * allocPvt(void)
178{203{
179 myStruct *my = (myStruct*) calloc(1, sizeof(myStruct));204 myStruct *my = (myStruct*) calloc(1, sizeof(myStruct));
180
181 if (!puser1) {205 if (!puser1) {
182 puser1 = my;206 puser1 = my;
183 testOk(e1 & e_alloc, "allocPvt (1) called");207 testOk(e1 & e_alloc, "allocPvt (1) called");
@@ -194,7 +218,7 @@ static void * allocPvt(void)
194static void * allocPvtFail(void)218static void * allocPvtFail(void)
195{219{
196 if (!puser1) {220 if (!puser1) {
197 testOk(e1 & e_alloc, "allocPvt (1) called");221 testOk(e1 & e_alloc, "allocPvtFail (1) called");
198 c1 |= e_alloc;222 c1 |= e_alloc;
199 }223 }
200 return NULL;224 return NULL;
@@ -202,7 +226,10 @@ static void * allocPvtFail(void)
202226
203static void freePvt(void *user)227static void freePvt(void *user)
204{228{
205 if (user == puser1) {229 myStruct *my = (myStruct*) user;
230 if ((user == puser1 || user == puser2) && (my->astr && my->astr != defaultstring)) {
231 testFail("freePvt: allocated string not free'd");
232 } else if (user == puser1) {
206 testOk(e1 & e_free, "freePvt (1) called");233 testOk(e1 & e_free, "freePvt (1) called");
207 c1 |= e_free;234 c1 |= e_free;
208 free(user);235 free(user);
@@ -499,9 +526,9 @@ static chfPluginIf allocFailPif = {
499};526};
500527
501static int checkValues(myStruct *my,528static int checkValues(myStruct *my,
502 char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c) {529 char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c, char *s3) {
503 int ret = 1;530 int ret = 1;
504 int s1fail, s2fail;531 int s1fail, s2fail, s3fail;
505 int s2valid = (s2 && s2[0] != '\0');532 int s2valid = (s2 && s2[0] != '\0');
506533
507 if (!my) return 0;534 if (!my) return 0;
@@ -526,6 +553,11 @@ static int checkValues(myStruct *my,
526 if (s2valid && s2fail) testDiag("Fail: my->str (%s) != s (%s)", my->str, s2);553 if (s2valid && s2fail) testDiag("Fail: my->str (%s) != s (%s)", my->str, s2);
527 ret = 0;554 ret = 0;
528 }555 }
556 s3fail = (!s3 && my->astr) || (s3 && !my->astr) || (s3 && strcmp(s3, my->astr));
557 if (s3fail) {
558 testDiag("Fail: my->str (%s) != s (%s)", my->astr, s3);
559 ret = 0;
560 }
529 return ret;561 return ret;
530}562}
531563
@@ -549,7 +581,7 @@ MAIN(chfPluginTest)
549#endif581#endif
550#endif582#endif
551583
552 testPlan(1433);584 testPlan(1612);
553585
554 dbChannelInit();586 dbChannelInit();
555 db_init_events();587 db_init_events();
@@ -599,6 +631,10 @@ MAIN(chfPluginTest)
599 "register plugin noconv");631 "register plugin noconv");
600 testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts),632 testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts),
601 "register plugin sloppy");633 "register plugin sloppy");
634 testOk(!chfPluginRegister("onlyOne", &myPif, onlyOneOpts),
635 "register plugin onlyOne");
636 testOk(!chfPluginRegister("onlyOneRequired", &myPif, onlyOneRequiredOpts),
637 "register plugin onlyOneRequired");
602 testOk(!chfPluginRegister("pre", &prePif, sloppyOpts),638 testOk(!chfPluginRegister("pre", &prePif, sloppyOpts),
603 "register plugin pre");639 "register plugin pre");
604 testOk(!chfPluginRegister("post", &postPif, sloppyOpts),640 testOk(!chfPluginRegister("post", &postPif, sloppyOpts),
@@ -629,7 +665,7 @@ MAIN(chfPluginTest)
629 testOk(!!(pch = dbChannelCreate(665 testOk(!!(pch = dbChannelCreate(
630 "x.{'strict-tagged':{D:1.2e15,f:false}}")),666 "x.{'strict-tagged':{D:1.2e15,f:false}}")),
631 "create channel for strict-tagged parsing: D (t and d) and f");667 "create channel for strict-tagged parsing: D (t and d) and f");
632 testOk(checkValues(puser1, 3, 12, 0, 1.2e15, "hello", 0, 4),668 testOk(checkValues(puser1, 3, 12, 0, 1.2e15, "hello", 0, 4, "default"),
633 "guards intact, values correct");669 "guards intact, values correct");
634 if (!testOk(c1 == e1, "all expected calls happened"))670 if (!testOk(c1 == e1, "all expected calls happened"))
635 testDiag("expected %#x - called %#x", e1, c1);671 testDiag("expected %#x - called %#x", e1, c1);
@@ -643,7 +679,7 @@ MAIN(chfPluginTest)
643 testOk(!!(pch = dbChannelCreate(679 testOk(!!(pch = dbChannelCreate(
644 "x.{'strict-tagged':{D2:1.2e15,f:false}}")),680 "x.{'strict-tagged':{D2:1.2e15,f:false}}")),
645 "create channel for strict-tagged parsing: D2 (t and d) and f");681 "create channel for strict-tagged parsing: D2 (t and d) and f");
646 testOk(checkValues(puser1, 4, 12, 0, 1.2e15, "hello", 0, 4),682 testOk(checkValues(puser1, 4, 12, 0, 1.2e15, "hello", 0, 4, "default"),
647 "guards intact, values correct");683 "guards intact, values correct");
648 if (!testOk(c1 == e1, "all expected calls happened"))684 if (!testOk(c1 == e1, "all expected calls happened"))
649 testDiag("expected %#x - called %#x", e1, c1);685 testDiag("expected %#x - called %#x", e1, c1);
@@ -673,12 +709,26 @@ MAIN(chfPluginTest)
673709
674 testHead("SLOPPY TAGGED parsing: all ok");710 testHead("SLOPPY TAGGED parsing: all ok");
675711
712 /* no tag */
713 e1 = e_alloc | e_ok; c1 = 0;
714 testOk(!!(pch = dbChannelCreate(
715 "x.{'sloppy-tagged':{}}")),
716 "create channel for sloppy-tagged parsing: no argument");
717 testOk(checkValues(puser1, 99, 12, 1, 1.234e5, "hello", 0, 4, "default"),
718 "guards intact, values correct");
719 if (!testOk(c1 == e1, "all expected calls happened"))
720 testDiag("expected %#x - called %#x", e1, c1);
721 e1 = e_close | e_free; c1 = 0;
722 if (pch) dbChannelDelete(pch);
723 testOk(!puser1, "user part cleaned up");
724 if (!testOk(c1 == e1, "all expected calls happened"))
725 testDiag("expected %#x - called %#x", e1, c1);
676 /* tag i */726 /* tag i */
677 e1 = e_alloc | e_ok; c1 = 0;727 e1 = e_alloc | e_ok; c1 = 0;
678 testOk(!!(pch = dbChannelCreate(728 testOk(!!(pch = dbChannelCreate(
679 "x.{'sloppy-tagged':{I:1}}")),729 "x.{'sloppy-tagged':{I:1}}")),
680 "create channel for sloppy-tagged parsing: I");730 "create channel for sloppy-tagged parsing: I");
681 testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4),731 testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4, "default"),
682 "guards intact, values correct");732 "guards intact, values correct");
683 if (!testOk(c1 == e1, "all expected calls happened"))733 if (!testOk(c1 == e1, "all expected calls happened"))
684 testDiag("expected %#x - called %#x", e1, c1);734 testDiag("expected %#x - called %#x", e1, c1);
@@ -692,7 +742,7 @@ MAIN(chfPluginTest)
692 testOk(!!(pch = dbChannelCreate(742 testOk(!!(pch = dbChannelCreate(
693 "x.{'sloppy-tagged':{F:false}}")),743 "x.{'sloppy-tagged':{F:false}}")),
694 "create channel for sloppy-tagged parsing: F");744 "create channel for sloppy-tagged parsing: F");
695 testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4),745 testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4, "default"),
696 "guards intact, values correct");746 "guards intact, values correct");
697 if (!testOk(c1 == e1, "all expected calls happened"))747 if (!testOk(c1 == e1, "all expected calls happened"))
698 testDiag("expected %#x - called %#x", e1, c1);748 testDiag("expected %#x - called %#x", e1, c1);
@@ -706,7 +756,7 @@ MAIN(chfPluginTest)
706 testOk(!!(pch = dbChannelCreate(756 testOk(!!(pch = dbChannelCreate(
707 "x.{'sloppy-tagged':{D:1.2e15}}")),757 "x.{'sloppy-tagged':{D:1.2e15}}")),
708 "create channel for sloppy-tagged parsing: D");758 "create channel for sloppy-tagged parsing: D");
709 testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4),759 testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4, "default"),
710 "guards intact, values correct");760 "guards intact, values correct");
711 if (!testOk(c1 == e1, "all expected calls happened"))761 if (!testOk(c1 == e1, "all expected calls happened"))
712 testDiag("expected %#x - called %#x", e1, c1);762 testDiag("expected %#x - called %#x", e1, c1);
@@ -720,7 +770,7 @@ MAIN(chfPluginTest)
720 testOk(!!(pch = dbChannelCreate(770 testOk(!!(pch = dbChannelCreate(
721 "x.{'sloppy-tagged':{S:'bar'}}")),771 "x.{'sloppy-tagged':{S:'bar'}}")),
722 "create channel for sloppy-tagged parsing: S");772 "create channel for sloppy-tagged parsing: S");
723 testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4),773 testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4, "default"),
724 "guards intact, values correct");774 "guards intact, values correct");
725 if (!testOk(c1 == e1, "all expected calls happened"))775 if (!testOk(c1 == e1, "all expected calls happened"))
726 testDiag("expected %#x - called %#x", e1, c1);776 testDiag("expected %#x - called %#x", e1, c1);
@@ -734,7 +784,21 @@ MAIN(chfPluginTest)
734 testOk(!!(pch = dbChannelCreate(784 testOk(!!(pch = dbChannelCreate(
735 "x.{'sloppy-tagged':{C:'R'}}")),785 "x.{'sloppy-tagged':{C:'R'}}")),
736 "create channel for sloppy-tagged parsing: C");786 "create channel for sloppy-tagged parsing: C");
737 testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1),787 testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1, "default"),
788 "guards intact, values correct");
789 if (!testOk(c1 == e1, "all expected calls happened"))
790 testDiag("expected %#x - called %#x", e1, c1);
791 e1 = e_close | e_free; c1 = 0;
792 if (pch) dbChannelDelete(pch);
793 testOk(!puser1, "user part cleaned up");
794 if (!testOk(c1 == e1, "all expected calls happened"))
795 testDiag("expected %#x - called %#x", e1, c1);
796 /* tag A */
797 e1 = e_alloc | e_ok; c1 = 0;
798 testOk(!!(pch = dbChannelCreate(
799 "x.{'sloppy-tagged':{A:'bar'}}")),
800 "create channel for sloppy-tagged parsing: A");
801 testOk(checkValues(puser1, 6, 12, 1, 1.234e5, "hello", 0, 4, "bar"),
738 "guards intact, values correct");802 "guards intact, values correct");
739 if (!testOk(c1 == e1, "all expected calls happened"))803 if (!testOk(c1 == e1, "all expected calls happened"))
740 testDiag("expected %#x - called %#x", e1, c1);804 testDiag("expected %#x - called %#x", e1, c1);
@@ -749,9 +813,9 @@ MAIN(chfPluginTest)
749 /* All perfect */813 /* All perfect */
750 testHead("STRICT parsing: all ok");814 testHead("STRICT parsing: all ok");
751 e1 = e_alloc | e_ok; c1 = 0;815 e1 = e_alloc | e_ok; c1 = 0;
752 testOk(!!(pch = dbChannelCreate("x.{strict:{i:1,f:false,d:1.2e15,s:'bar',c:'R'}}")),816 testOk(!!(pch = dbChannelCreate("x.{strict:{i:1,f:false,d:1.2e15,s:'bar',c:'R',a:'foo'}}")),
753 "create channel for strict parsing: JSON correct");817 "create channel for strict parsing: JSON correct");
754 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1),818 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1, "foo"),
755 "guards intact, values correct");819 "guards intact, values correct");
756 if (!testOk(c1 == e1, "all expected calls happened"))820 if (!testOk(c1 == e1, "all expected calls happened"))
757 testDiag("expected %#x - called %#x", e1, c1);821 testDiag("expected %#x - called %#x", e1, c1);
@@ -765,39 +829,46 @@ MAIN(chfPluginTest)
765 testHead("STRICT parsing: any missing parameter must fail");829 testHead("STRICT parsing: any missing parameter must fail");
766 e1 = e_alloc | e_error | e_free; c1 = 0;830 e1 = e_alloc | e_error | e_free; c1 = 0;
767 testOk(!(pch = dbChannelCreate(831 testOk(!(pch = dbChannelCreate(
768 "x.{strict:{i:1,f:false,d:1.2e15,s:'bar'}}")),832 "x.{strict:{i:1,f:false,d:1.2e15,s:'bar',a:'foo'}}")),
769 "create channel for strict parsing: c missing");833 "create channel for strict parsing: c missing");
770 testOk(!puser1, "user part cleaned up");834 testOk(!puser1, "user part cleaned up");
771 if (!testOk(c1 == e1, "all expected calls happened"))835 if (!testOk(c1 == e1, "all expected calls happened"))
772 testDiag("expected %#x - called %#x", e1, c1);836 testDiag("expected %#x - called %#x", e1, c1);
773 e1 = e_alloc | e_error | e_free; c1 = 0;837 e1 = e_alloc | e_error | e_free; c1 = 0;
774 testOk(!(pch = dbChannelCreate(838 testOk(!(pch = dbChannelCreate(
775 "x.{strict:{f:false,i:1,d:1.2e15,c:'R'}}")),839 "x.{strict:{f:false,i:1,d:1.2e15,c:'R',a:'foo'}}")),
776 "create channel for strict parsing: s missing");840 "create channel for strict parsing: s missing");
777 testOk(!puser1, "user part cleaned up");841 testOk(!puser1, "user part cleaned up");
778 if (!testOk(c1 == e1, "all expected calls happened"))842 if (!testOk(c1 == e1, "all expected calls happened"))
779 testDiag("expected %#x - called %#x", e1, c1);843 testDiag("expected %#x - called %#x", e1, c1);
780 e1 = e_alloc | e_error | e_free; c1 = 0;844 e1 = e_alloc | e_error | e_free; c1 = 0;
781 testOk(!(pch = dbChannelCreate(845 testOk(!(pch = dbChannelCreate(
782 "x.{strict:{i:1,c:'R',f:false,s:'bar'}}")),846 "x.{strict:{i:1,c:'R',f:false,s:'bar',a:'foo'}}")),
783 "create channel for strict parsing: d missing");847 "create channel for strict parsing: d missing");
784 testOk(!puser1, "user part cleaned up");848 testOk(!puser1, "user part cleaned up");
785 if (!testOk(c1 == e1, "all expected calls happened"))849 if (!testOk(c1 == e1, "all expected calls happened"))
786 testDiag("expected %#x - called %#x", e1, c1);850 testDiag("expected %#x - called %#x", e1, c1);
787 e1 = e_alloc | e_error | e_free; c1 = 0;851 e1 = e_alloc | e_error | e_free; c1 = 0;
788 testOk(!(pch = dbChannelCreate(852 testOk(!(pch = dbChannelCreate(
789 "x.{strict:{d:1.2e15,c:'R',i:1,s:'bar'}}")),853 "x.{strict:{d:1.2e15,c:'R',i:1,s:'bar',a:'foo'}}")),
790 "create channel for strict parsing: f missing");854 "create channel for strict parsing: f missing");
791 testOk(!puser1, "user part cleaned up");855 testOk(!puser1, "user part cleaned up");
792 if (!testOk(c1 == e1, "all expected calls happened"))856 if (!testOk(c1 == e1, "all expected calls happened"))
793 testDiag("expected %#x - called %#x", e1, c1);857 testDiag("expected %#x - called %#x", e1, c1);
794 e1 = e_alloc | e_error | e_free; c1 = 0;858 e1 = e_alloc | e_error | e_free; c1 = 0;
795 testOk(!(pch = dbChannelCreate(859 testOk(!(pch = dbChannelCreate(
796 "x.{strict:{c:'R',s:'bar',f:false,d:1.2e15}}")),860 "x.{strict:{c:'R',s:'bar',f:false,d:1.2e15,a:'foo'}}")),
797 "create channel for strict parsing: i missing");861 "create channel for strict parsing: i missing");
798 testOk(!puser1, "user part cleaned up");862 testOk(!puser1, "user part cleaned up");
799 if (!testOk(c1 == e1, "all expected calls happened"))863 if (!testOk(c1 == e1, "all expected calls happened"))
800 testDiag("expected %#x - called %#x", e1, c1);864 testDiag("expected %#x - called %#x", e1, c1);
865 e1 = e_alloc | e_error | e_free; c1 = 0;
866 testOk(!(pch = dbChannelCreate(
867 "x.{strict:{i:1,f:false,d:1.2e15,s:'bar',c:'R'}}")),
868 "create channel for strict parsing: a missing");
869 testOk(!puser1, "user part cleaned up");
870 if (!testOk(c1 == e1, "all expected calls happened"))
871 testDiag("expected %#x - called %#x", e1, c1);
801872
802 /* NOCONV parsing: optional, no conversion */873 /* NOCONV parsing: optional, no conversion */
803874
@@ -805,9 +876,9 @@ MAIN(chfPluginTest)
805 testHead("NOCONV parsing: missing parameters get default value");876 testHead("NOCONV parsing: missing parameters get default value");
806 e1 = e_alloc | e_ok; c1 = 0;877 e1 = e_alloc | e_ok; c1 = 0;
807 testOk(!!(pch = dbChannelCreate(878 testOk(!!(pch = dbChannelCreate(
808 "x.{noconv:{i:1,f:false,d:1.2e15,s:'bar'}}")),879 "x.{noconv:{i:1,f:false,d:1.2e15,s:'bar',a:'foo'}}")),
809 "create channel for noconv parsing: c missing");880 "create channel for noconv parsing: c missing");
810 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 4),881 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 4, "foo"),
811 "guards intact, values correct");882 "guards intact, values correct");
812 if (!testOk(c1 == e1, "all expected calls happened"))883 if (!testOk(c1 == e1, "all expected calls happened"))
813 testDiag("expected %#x - called %#x", e1, c1);884 testDiag("expected %#x - called %#x", e1, c1);
@@ -819,36 +890,90 @@ MAIN(chfPluginTest)
819890
820 e1 = e_any;891 e1 = e_any;
821 testOk(!!(pch = dbChannelCreate(892 testOk(!!(pch = dbChannelCreate(
822 "x.{noconv:{i:1,f:false,d:1.2e15,c:'R'}}")),893 "x.{noconv:{i:1,f:false,d:1.2e15,c:'R',a:'foo'}}")),
823 "create channel for noconv parsing: s missing");894 "create channel for noconv parsing: s missing");
824 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "hello", 0, 1),895 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "hello", 0, 1, "foo"),
825 "guards intact, values correct");896 "guards intact, values correct");
826 if (pch) dbChannelDelete(pch);897 if (pch) dbChannelDelete(pch);
827898
828 testOk(!!(pch = dbChannelCreate(899 testOk(!!(pch = dbChannelCreate(
829 "x.{noconv:{i:1,f:false,s:'bar',c:'R'}}")),900 "x.{noconv:{i:1,f:false,s:'bar',c:'R',a:'foo'}}")),
830 "create channel for noconv parsing: d missing");901 "create channel for noconv parsing: d missing");
831 testOk(checkValues(puser1, 99, 1, 0, 1.234e5, "bar", 0, 1),902 testOk(checkValues(puser1, 99, 1, 0, 1.234e5, "bar", 0, 1, "foo"),
832 "guards intact, values correct");903 "guards intact, values correct");
833 if (pch) dbChannelDelete(pch);904 if (pch) dbChannelDelete(pch);
834905
835 testOk(!!(pch = dbChannelCreate(906 testOk(!!(pch = dbChannelCreate(
836 "x.{noconv:{i:1,d:1.2e15,s:'bar',c:'R'}}")),907 "x.{noconv:{i:1,d:1.2e15,s:'bar',c:'R',a:'foo'}}")),
837 "create channel for noconv parsing: f missing");908 "create channel for noconv parsing: f missing");
838 testOk(checkValues(puser1, 99, 1, 1, 1.2e15, "bar", 0, 1),909 testOk(checkValues(puser1, 99, 1, 1, 1.2e15, "bar", 0, 1, "foo"),
839 "guards intact, values correct");910 "guards intact, values correct");
840 if (pch) dbChannelDelete(pch);911 if (pch) dbChannelDelete(pch);
841912
842 testOk(!!(pch = dbChannelCreate(913 testOk(!!(pch = dbChannelCreate(
843 "x.{noconv:{f:false,d:1.2e15,s:'bar',c:'R'}}")),914 "x.{noconv:{f:false,d:1.2e15,s:'bar',c:'R',a:'foo'}}")),
844 "create channel for noconv parsing: i missing");915 "create channel for noconv parsing: i missing");
845 testOk(checkValues(puser1, 99, 12, 0, 1.2e15, "bar", 0, 1),916 testOk(checkValues(puser1, 99, 12, 0, 1.2e15, "bar", 0, 1, "foo"),
917 "guards intact, values correct");
918 if (pch) dbChannelDelete(pch);
919
920 testOk(!!(pch = dbChannelCreate(
921 "x.{noconv:{i:1,f:false,d:1.2e15,s:'bar',c:'R'}}")),
922 "create channel for noconv parsing: a missing");
923 testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1, "default"),
924 "guards intact, values correct");
925 if (pch) dbChannelDelete(pch);
926
927 /* SIMPLIFIED parsing: only one argument */
928
929 /* */
930 testHead("SIMPLIFIED parsing: all ok");
931
932 /* only one arg (i) defined */
933 testOk(!!(pch = dbChannelCreate("x.{onlyOne:1}")),
934 "create channel for onlyOne parsing: single arg");
935 testOk(checkValues(puser1, 99, 1, 1, 1.234e5, "hello", 0, 4, "default"),
936 "guards intact, values correct");
937 if (pch) dbChannelDelete(pch);
938
939 /* only one arg (d) required */
940 testOk(!!(pch = dbChannelCreate("x.{onlyOneRequired:1.23}")),
941 "create channel for onlyOneRequired parsing: single arg");
942 testOk(checkValues(puser1, 99, 12, 1, 1.23, "hello", 0, 4, "default"),
943 "guards intact, values correct");
944 if (pch) dbChannelDelete(pch);
945
946 /* ambigous arg fails */
947 testOk(!(pch = dbChannelCreate("x.{sloppy:1}")),
948 "create channel for sloppy parsing: ambigous arg");
949 if (pch) dbChannelDelete(pch);
950
951 /* No arg at all */
952 testOk(!!(pch = dbChannelCreate("x.{sloppy}")),
953 "create channel for sloppy parsing: no arg at all");
954 testOk(checkValues(puser1, 99, 12, 1, 1.234e5, "hello", 0, 4, "default"),
846 "guards intact, values correct");955 "guards intact, values correct");
847 if (pch) dbChannelDelete(pch);956 if (pch) dbChannelDelete(pch);
848957
958 /* No arg at all followed by another filter */
959 testHead("Chains of filters without args"); \
960 e1 = e_any; e2 = e_any;
961 testOk(!!(pch = dbChannelCreate("x.{sloppy,sloppy}")),
962 "create channel for chained argless filters: comma separated");
963 if (pch) dbChannelDelete(pch);
964 testOk(!!(pch = dbChannelCreate("x.{sloppy , sloppy}")),
965 "create channel for chained argless filters: comma and space separated");
966 if (pch) dbChannelDelete(pch);
967 testOk(!(pch = dbChannelCreate("x.{sloppy#sloppy}")),
968 "create channel for chained argless filters fail: garbage separator");
969 if (pch) dbChannelDelete(pch);
970 testOk(!(pch = dbChannelCreate("x.{sloppy")),
971 "create channel for chained argless filters fail: missing end");
972 if (pch) dbChannelDelete(pch);
973
849 /* Reject wrong types */974 /* Reject wrong types */
850#define WRONGTYPETEST(Var, Val, Typ) \975#define WRONGTYPETEST(Var, Val, Typ) \
851 e1 = e_alloc | e_error | e_free; c1 = 0; \976 e1 = e_alloc | e_error | e_free; c1 = 0; e2 = 0; \
852 testOk(!(pch = dbChannelCreate("x.{noconv:{'"#Var"':"#Val"}}")), \977 testOk(!(pch = dbChannelCreate("x.{noconv:{'"#Var"':"#Val"}}")), \
853 "create channel for noconv parsing: wrong type "#Typ" for "#Var); \978 "create channel for noconv parsing: wrong type "#Typ" for "#Var); \
854 testOk(!puser1, "user part cleaned up"); \979 testOk(!puser1, "user part cleaned up"); \
@@ -872,15 +997,18 @@ MAIN(chfPluginTest)
872 WRONGTYPETEST(c, 1.23, double);997 WRONGTYPETEST(c, 1.23, double);
873 WRONGTYPETEST(c, true, boolean);998 WRONGTYPETEST(c, true, boolean);
874 WRONGTYPETEST(c, 2, integer);999 WRONGTYPETEST(c, 2, integer);
1000 WRONGTYPETEST(a, 1.23, double);
1001 WRONGTYPETEST(a, true, boolean);
1002 WRONGTYPETEST(a, 123, integer);
8751003
876 /* SLOPPY parsing: optional, with conversion */1004 /* SLOPPY parsing: optional, with conversion */
8771005
878#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval1, Sval2, Cval) \1006#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval1, Sval2, Cval, Dstr) \
879 e1 = e_alloc | e_ok; c1 = 0; \1007 e1 = e_alloc | e_ok; c1 = 0; e2 = 0; \
880 testDiag("Calling dbChannelCreate x.{sloppy:{"#Var":"#Val"}}"); \1008 testDiag("Calling dbChannelCreate x.{sloppy:{"#Var":"#Val"}}"); \
881 testOk(!!(pch = dbChannelCreate("x.{sloppy:{"#Var":"#Val"}}")), \1009 testOk(!!(pch = dbChannelCreate("x.{sloppy:{"#Var":"#Val"}}")), \
882 "create channel for sloppy parsing: "#Typ" (good) for "#Var); \1010 "create channel for sloppy parsing: "#Typ" (good) for "#Var); \
883 testOk(checkValues(puser1, 99, Ival, Fval, Dval, Sval1, Sval2, Cval), \1011 testOk(checkValues(puser1, 99, Ival, Fval, Dval, Sval1, Sval2, Cval, Dstr), \
884 "guards intact, values correct"); \1012 "guards intact, values correct"); \
885 if (!testOk(c1 == e1, "create channel: all expected calls happened")) \1013 if (!testOk(c1 == e1, "create channel: all expected calls happened")) \
886 testDiag("expected %#x - called %#x", e1, c1); \1014 testDiag("expected %#x - called %#x", e1, c1); \
@@ -891,7 +1019,7 @@ MAIN(chfPluginTest)
891 testDiag("expected %#x - called %#x", e1, c1);1019 testDiag("expected %#x - called %#x", e1, c1);
8921020
893#define CONVTESTBAD(Var, Val, Typ) \1021#define CONVTESTBAD(Var, Val, Typ) \
894 e1 = e_alloc | e_error | e_free; c1 = 0; \1022 e1 = e_alloc | e_error | e_free; c1 = 0; e2 = 0; \
895 testDiag("Calling dbChannelCreate x.{sloppy:{"#Var":"#Val"}}"); \1023 testDiag("Calling dbChannelCreate x.{sloppy:{"#Var":"#Val"}}"); \
896 testOk(!(pch = dbChannelCreate("x.{sloppy:{"#Var":"#Val"}}")), \1024 testOk(!(pch = dbChannelCreate("x.{sloppy:{"#Var":"#Val"}}")), \
897 "create channel for sloppy parsing: "#Typ" (bad) for "#Var); \1025 "create channel for sloppy parsing: "#Typ" (bad) for "#Var); \
@@ -901,67 +1029,79 @@ MAIN(chfPluginTest)
9011029
902 /* To integer */1030 /* To integer */
903 testHead("SLOPPY parsing: conversion to integer");1031 testHead("SLOPPY parsing: conversion to integer");
904 CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 0, 4);1032 CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 0, 4, "default");
905 CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 0, 4);1033 CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 0, 4, "default");
906 CONVTESTBAD(i, "9234567890", out-of-range string);1034 CONVTESTBAD(i, "9234567890", out-of-range string);
907 CONVTESTBAD(i, ".4", invalid string);1035 CONVTESTBAD(i, ".4", invalid string);
908 CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 0, 4);1036 CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 0, 4, "default");
909 CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 0, 4);1037 CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 0, 4, "default");
910 CONVTESTBAD(i, 34.7e14, out-of-range double);1038 CONVTESTBAD(i, 34.7e14, out-of-range double);
9111039
912 /* To boolean */1040 /* To boolean */
913 testHead("SLOPPY parsing: conversion to boolean");1041 testHead("SLOPPY parsing: conversion to boolean");
914 CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 0, 4);1042 CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 0, 4, "default");
915 CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 0, 4);1043 CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 0, 4, "default");
916 CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 0, 4);1044 CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 0, 4, "default");
917 CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 0, 4);1045 CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 0, 4, "default");
918 CONVTESTBAD(f, ".4", invalid .4 string);1046 CONVTESTBAD(f, ".4", invalid .4 string);
919 CONVTESTBAD(f, "Flase", misspelled invalid string);1047 CONVTESTBAD(f, "Flase", misspelled invalid string);
920 CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 0, 4);1048 CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 0, 4, "default");
921 CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 0, 4);1049 CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 0, 4, "default");
922 CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 0, 4);1050 CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 0, 4, "default");
923 CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 0, 4);1051 CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 0, 4, "default");
924 CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 0, 4);1052 CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 0, 4, "default");
925 CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 0, 4);1053 CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 0, 4, "default");
926 CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 0, 4);1054 CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 0, 4, "default");
9271055
928 /* To double */1056 /* To double */
929 testHead("SLOPPY parsing: conversion to double");1057 testHead("SLOPPY parsing: conversion to double");
930 CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 0, 4);1058 CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 0, 4, "default");
931 CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 0, 4);1059 CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 0, 4, "default");
932 CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 0, 4);1060 CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 0, 4, "default");
933 CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 0, 4);1061 CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 0, 4, "default");
934 CONVTESTBAD(d, "1.67e407", out-of-range double string);1062 CONVTESTBAD(d, "1.67e407", out-of-range double string);
935 CONVTESTBAD(d, "blubb", invalid blubb string);1063 CONVTESTBAD(d, "blubb", invalid blubb string);
936 CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 0, 4);1064 CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 0, 4, "default");
937 CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 0, 4);1065 CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 0, 4, "default");
938 CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 0, 4);1066 CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 0, 4, "default");
939 CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 0, 4);1067 CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 0, 4, "default");
9401068
941 /* To string */1069 /* To string */
942 testHead("SLOPPY parsing: conversion to string");1070 testHead("SLOPPY parsing: conversion to string");
943 CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 0, 4);1071 CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 0, 4, "default");
944 CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 0, 4);1072 CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 0, 4, "default");
945 CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 0, 4);1073 CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 0, 4, "default");
946 CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 0, 4);1074 CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 0, 4, "default");
947 CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 0, 4);1075 CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 0, 4, "default");
948 CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", "-1.23e+026", 4);1076 CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", "-1.23e+026", 4, "default");
949 CONVTESTGOOD(s, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "-1.23456789123e+26", "-1.23456789123e+026", 4);1077 CONVTESTGOOD(s, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "-1.23456789123e+26", "-1.23456789123e+026", 4, "default");
9501078
951 /* To Enum */1079 /* To Enum */
952 testHead("SLOPPY parsing: conversion to enum");1080 testHead("SLOPPY parsing: conversion to enum");
953 CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 0, 2);1081 CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 0, 2, "default");
954 CONVTESTBAD(c, 3, invalid integer choice);1082 CONVTESTBAD(c, 3, invalid integer choice);
955 CONVTESTBAD(c, 3.2, double);1083 CONVTESTBAD(c, 3.2, double);
956 CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 0, 1);1084 CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 0, 1, "default");
957 CONVTESTBAD(c, "blubb", invalid string choice);1085 CONVTESTBAD(c, "blubb", invalid string choice);
9581086
1087 /* To allocated string */
1088 testHead("SLOPPY parsing: conversion to string");
1089 CONVTESTGOOD(a, 12345, positive integer, 12, 1, 1.234e5, "hello", 0, 4, "12345");
1090 CONVTESTGOOD(a, -1234567891, negative integer, 12, 1, 1.234e5, "hello", 0, 4, "-1234567891");
1091 CONVTESTGOOD(a, true, true boolean, 12, 1, 1.234e5, "hello", 0, 4, "true");
1092 CONVTESTGOOD(a, false, false boolean, 12, 1, 1.234e5, "hello", 0, 4, "false");
1093 CONVTESTGOOD(a, 123e4, small positive double, 12, 1, 1.234e5, "hello", 0, 4, "1230000");
1094 CONVTESTGOOD(a, -123e24, negative double, 12, 1, 1.234e5, "hello", 0, 4, "-1.23e+26");
1095 CONVTESTGOOD(a, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "hello", 0, 4, "-1.23456789123e+26");
1096 CONVTESTGOOD(a, {}, empty argument, 12, 1, 1.234e5, "hello", 0, 4, 0);
1097 CONVTESTGOOD(a, '', empty string, 12, 1, 1.234e5, "hello", 0, 4, "");
1098
959 /* Registering and running filter callbacks */1099 /* Registering and running filter callbacks */
9601100
961#define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \1101#define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \
962 testHead("Filter chain test, "Type" filter"); \1102 testHead("Filter chain test, "Type" filter"); \
963 offset = 0; \1103 offset = 0; \
964 e1 = e_alloc | e_ok; c1 = 0; \1104 e1 = e_alloc | e_ok; c1 = 0; e2 = 0; \
965 testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \1105 testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \
966 if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \1106 if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
967 e1 = e_open | ExpReg; c1 = 0; \1107 e1 = e_open | ExpReg; c1 = 0; \
diff --git a/modules/database/test/std/filters/decTest.c b/modules/database/test/std/filters/decTest.c
index e0961af..1eefe5d 100644
--- a/modules/database/test/std/filters/decTest.c
+++ b/modules/database/test/std/filters/decTest.c
@@ -251,8 +251,8 @@ MAIN(decTest)
251251
252 /* Decimation (N=4) */252 /* Decimation (N=4) */
253253
254 testHead("Decimation (n=4)");254 testHead("Decimation (n=4) with simplified syntax");
255 testOk(!!(pch = dbChannelCreate("x.VAL{dec:{n:4}}")),255 testOk(!!(pch = dbChannelCreate("x.VAL{dec:4}")),
256 "dbChannel with plugin dec (n=4) created");256 "dbChannel with plugin dec (n=4) created");
257257
258 checkAndOpenChannel(pch, plug);258 checkAndOpenChannel(pch, plug);
diff --git a/modules/database/test/std/filters/tsTest.c b/modules/database/test/std/filters/tsTest.c
index bd0b799..31ceb99 100644
--- a/modules/database/test/std/filters/tsTest.c
+++ b/modules/database/test/std/filters/tsTest.c
@@ -74,7 +74,7 @@ MAIN(tsTest)
7474
75 testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly");75 testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly");
7676
77 testOk(!!(pch = dbChannelCreate("x.VAL{ts:{}}")), "dbChannel with plugin ts created");77 testOk(!!(pch = dbChannelCreate("x.{ts}")), "dbChannel with plugin ts created");
78 testOk((ellCount(&pch->filters) == 1), "channel has one plugin");78 testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
7979
80 memset(&fl, PATTERN, sizeof(fl));80 memset(&fl, PATTERN, sizeof(fl));

Subscribers

People subscribed via source and target branches