Extend and add lots of tests in chfPluginTest.c

* Add tests for callback registration (pre and post)
* Add tests for callback execution (pre and post)
* Add tests for report function and array destructor
* Add test for combinations of two plugins (that register pre eventq, post eventq or in
  both chains), checking the order of calls and db_field_log changes independently
* Make test messages more verbose and comprehensive
This commit is contained in:
Ralph Lange
2012-04-27 13:21:57 -04:00
committed by Michael Davidsaver
parent f8e9a7643a
commit 47e2e228ab

View File

@@ -19,20 +19,30 @@
#include "testMain.h"
#define PATTERN 0x55555555
#define TYPE_START 0xAAA
#define R_INTRO "abc"
#define R_LEVEL 42
/* Expected call bit definitions */
/* Expected / actually run callback bit definitions */
#define e_alloc 0x00000001
#define e_free 0x00000002
#define e_error 0x00000004
#define e_ok 0x00000008
#define e_open 0x00000010
#define e_reg_pre_cb 0x00000020
#define e_report 0x00000040
#define e_close 0x00000080
#define e_reg_pre 0x00000020
#define e_reg_post 0x00000040
#define e_report 0x00000080
#define e_close 0x00000100
#define e_pre 0x00000200
#define e_post 0x00000400
#define e_dtor 0x00000800
unsigned int e, c;
unsigned int e1, e2, c1, c2;
unsigned int offset;
db_field_log *dtorpfl;
#define e_any (e_alloc | e_free | e_error | e_ok | e_open | e_reg_pre_cb| e_report | e_close)
#define e_any (e_alloc | e_free | e_error | e_ok | e_open \
| e_reg_pre | e_reg_post | e_pre | e_post | e_dtor | e_report | e_close)
typedef struct myStruct {
int sent1;
@@ -48,6 +58,8 @@ typedef struct myStruct {
int sent6;
char c;
char c1[2];
int offpre;
int offpost;
} myStruct;
static const
@@ -86,13 +98,13 @@ chfPluginArgDef sloppyOpts[] = {
/* Options defs with not enough room provided */
static const
chfPluginArgDef brokenOpts1[] = {
chfInt32 (myStruct, c, "i" , 0, 1),
chfInt32 (myStruct, c1, "i" , 0, 1),
chfPluginArgEnd
};
static const
chfPluginArgDef brokenOpts2[] = {
chfDouble(myStruct, c, "d" , 0, 1),
chfDouble(myStruct, c1, "d" , 0, 1),
chfPluginArgEnd
};
@@ -104,13 +116,13 @@ chfPluginArgDef brokenOpts3[] = {
static const
chfPluginArgDef brokenOpts4[] = {
chfEnum (myStruct, c, "c" , 0, 1, colorEnum),
chfEnum (myStruct, c1, "c" , 0, 1, colorEnum),
chfPluginArgEnd
};
int p_ok_return = 0;
int c_open_return = 0;
void *puser;
void *puser1, *puser2;
static void clearStruct(void *p) {
myStruct *my = (myStruct*) p;
@@ -125,74 +137,230 @@ static void clearStruct(void *p) {
my->enumval = 4;
}
static char inst(void* user) {
return user == puser1 ? '1' : user == puser2 ? '2' : 'x';
}
static void * allocPvt(void)
{
testOk(e & e_alloc, "allocPvt called");
c |= e_alloc;
myStruct *my = (myStruct*) calloc(1, sizeof(myStruct));
myStruct *my = puser = (myStruct*) calloc(1, sizeof(myStruct));
if (!puser1) {
puser1 = my;
testOk(e1 & e_alloc, "allocPvt (1) called");
c1 |= e_alloc;
} else if (!puser2) {
puser2 = my;
testOk(e2 & e_alloc, "allocPvt (2) called");
c2 |= e_alloc;
}
clearStruct (my);
return my;
}
static void freePvt(void *user)
{
testOk(e & e_free, "freePvt called");
c |= e_free;
testOk(user == puser, "user pointer correct");
free(user);
puser = NULL;
testOk(user == puser1 || user == puser2, "freePvt: user pointer valid");
if (user == puser1) {
testOk(e1 & e_free, "freePvt (1) called");
c1 |= e_free;
free(user);
puser1 = NULL;
} else if (user == puser2) {
testOk(e2 & e_free, "freePvt (2) called");
c2 |= e_free;
free(user);
puser2 = NULL;
}
}
static void parse_error(void)
static void parse_error(void *user)
{
testOk(e & e_error, "parse_error called");
c |= e_error;
testOk(user == puser1 || user == puser2, "parse_error: user pointer valid");
if (user == puser1) {
testOk(e1 & e_error, "parse_error (1) called");
c1 |= e_error;
} else if (user == puser2) {
testOk(e2 & e_error, "parse_error (2) called");
c2 |= e_error;
}
}
static int parse_ok(void *user)
{
testOk(e & e_ok, "parse_ok called");
c |= e_ok;
testOk(user == puser, "user pointer correct");
testOk(user == puser1 || user == puser2, "parse_ok: user pointer valid");
if (user == puser1) {
testOk(e1 & e_ok, "parse_ok (1) called");
c1 |= e_ok;
} else if (user == puser2) {
testOk(e2 & e_ok, "parse_ok (2) called");
c2 |= e_ok;
}
return p_ok_return;
}
static long channel_open(dbChannel *chan, void *user)
{
testOk(e & e_open, "channel_open called");
c |= e_open;
testOk(user == puser, "user pointer correct");
testOk(user == puser1 || user == puser2, "channel_open: user pointer valid");
if (user == puser1) {
testOk(e1 & e_open, "channel_open (1) called");
c1 |= e_open;
} else if (user == puser2) {
testOk(e2 & e_open, "channel_open (2) called");
c2 |= e_open;
}
return c_open_return;
}
static dbfl_free1(db_field_log *pfl) {
testOk(e1 & e_dtor, "dbfl_free (1) called");
testOk(dtorpfl == pfl, "dbfl_free (1): db_field_log pointer correct");
dtorpfl = NULL;
c1 |= e_dtor;
}
static dbfl_free2(db_field_log *pfl) {
testOk(e2 & e_dtor, "dbfl_free (2) called");
testOk(dtorpfl == pfl, "dbfl_free (2): db_field_log pointer correct");
dtorpfl = NULL;
c2 |= e_dtor;
}
static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) {
myStruct *my = (myStruct*)user;
dbfl_freeFunc *dtor = NULL;
testOk(user == puser1 || user == puser2, "pre: user pointer valid");
if (my == puser1) {
testOk(e1 & e_pre, "pre (1) called");
testOk(!(c2 & e_pre), "pre (2) has not been called before pre (1)");
c1 |= e_pre;
dtor = dbfl_free1;
} else if (my == puser2) {
testOk(e2 & e_pre, "pre (2) called");
testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before pre (2)");
c2 |= e_pre;
dtor = dbfl_free2;
}
testOk(!(c1 & e_post), "post (1) has not been called before pre (%c)", inst(user));
testOk(!(c2 & e_post), "post (2) has not been called before pre (%c)", inst(user));
if (!testOk(pLog->field_type == TYPE_START + my->offpre, "pre (%c) got field log of expected type", inst(user)))
testDiag("expected: %d, got %d", TYPE_START + my->offpre, pLog->field_type);
pLog->field_type++;
if (my->offpre == 0) { /* The first one registers a dtor and saves pfl */
pLog->u.r.dtor = dtor;
dtorpfl = pLog;
}
return pLog;
}
static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) {
myStruct *my = (myStruct*)user;
dbfl_freeFunc *dtor = NULL;
testOk(user == puser1 || user == puser2, "post: user pointer valid");
if (my == puser1) {
testOk(e1 & e_post, "post (1) called");
testOk(!(c2 & e_post), "post (2) has not been called before post (1)");
c1 |= e_post;
dtor = dbfl_free1;
} else if (my == puser2) {
testOk(e2 & e_post, "post (2) called");
testOk(!(e1 & e_post) || c1 & e_post, "post (1) has been called before post (2)");
c2 |= e_post;
dtor = dbfl_free2;
}
testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before post (%c)", inst(user));
testOk(!(e2 & e_pre) || c2 & e_pre, "pre (2) has been called before post (%c)", inst(user));
if (!testOk(pLog->field_type == TYPE_START + my->offpost, "post (%c) got field log of expected type", inst(user)))
testDiag("expected: %d, got %d", TYPE_START + my->offpost, pLog->field_type);
pLog->field_type++;
if (my->offpost == 0) { /* The first one registers a dtor and remembers pfl */
pLog->u.r.dtor = dtor;
dtorpfl = pLog;
}
return pLog;
}
static void channelRegisterPre(dbChannel *chan, void *user,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
{
myStruct *my = (myStruct*)user;
testOk(user == puser1 || user == puser2, "register_pre: user pointer valid");
if (my == puser1) {
testOk(e1 & e_reg_pre, "register_pre (1) called");
testOk(!(c2 & e_reg_pre), "register_pre (2) has not been called before register_pre (1)");
c1 |= e_reg_pre;
} else if (my == puser2) {
testOk(e2 & e_reg_pre, "register_pre (2) called");
testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_pre (2)");
c2 |= e_reg_pre;
}
testOk(!(c1 & e_reg_post), "register_post (1) has not been called before register_pre (%c)", inst(user));
testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_pre (%c)", inst(user));
my->offpre = offset++;
probe->field_type++;
*cb_out = pre;
*arg_out = user;
}
static void channelRegisterPost(dbChannel *chan, void *user,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
{
myStruct *my = (myStruct*)user;
testOk(user == puser1 || user == puser2, "register_post: user pointer valid");
if (my == puser1) {
testOk(e1 & e_reg_post, "register_post (1) called");
testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_post (1)");
c1 |= e_reg_post;
} else if (my == puser2) {
testOk(e2 & e_reg_post, "register_post (2) called");
testOk(!(e1 & e_reg_post) || c1 & e_reg_post, "register_post (1) has been called before register_post (2)");
c2 |= e_reg_post;
}
testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_post (%c)", inst(user));
testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, "register_pre (2) has been called before register_post (%c)", inst(user));
my->offpost = offset++;
probe->field_type++;
*cb_out = post;
*arg_out = user;
}
static void channel_report(dbChannel *chan, void *user, const char *intro, int level)
{
testOk(e & e_report, "channel_report called");
c |= e_report;
testOk(user == puser, "user pointer correct");
testOk(user == puser1 || user == puser2, "channel_report: user pointer valid");
testOk(intro == R_INTRO, "channel_report: intro string correct");
testOk(level == R_LEVEL - 1, "channel_report: level correct");
if (user == puser1) {
testOk(e1 & e_report, "channel_report (1) called");
c1 |= e_report;
} else if (user == puser2) {
testOk(e2 & e_report, "channel_report (2) called");
c2 |= e_report;
}
}
static void channel_close(dbChannel *chan, void *user)
{
testOk(e & e_close, "channel_close called");
c |= e_close;
testOk(user == puser, "user pointer correct");
testOk(user == puser1 || user == puser2, "channel_close: user pointer valid");
if (user == puser1) {
testOk(e1 & e_close, "channel_close (1) called");
c1 |= e_close;
} else if (user == puser2) {
testOk(e2 & e_close, "channel_close (2) called");
c2 |= e_close;
}
}
static chfPluginIf myPif = {
@@ -209,6 +377,34 @@ static chfPluginIf myPif = {
channel_close
};
static chfPluginIf prePif = {
allocPvt,
freePvt,
parse_error,
parse_ok,
channel_open,
channelRegisterPre,
NULL,
channel_report,
channel_close
};
static chfPluginIf postPif = {
allocPvt,
freePvt,
parse_error,
parse_ok,
channel_open,
NULL,
channelRegisterPost,
channel_report,
channel_close
};
static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, int c) {
if (!my) return 0;
if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN
@@ -221,99 +417,118 @@ static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, in
}
}
static void testHead (char* title) {
testDiag("--------------------------------------------------------");
testDiag(title);
testDiag("--------------------------------------------------------");
}
MAIN(chfPluginTest)
{
dbChannel *pch;
db_field_log *pfl;
dbEventCtx evCtx = db_init_events();
testPlan(667);
testPlan(1481);
/* Enum to string conversion */
testHead("Enum to string conversion");
testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, "Enum to string: R");
testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, "Enum to string: G");
testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, "Enum to string: B");
testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index");
testHead("Set up database");
testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL));
testOk(!!pdbbase, "pdbbase was set");
testHead("Try to register buggy plugins");
testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer");
testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double");
testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string");
testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum");
testHead("Register plugins");
testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict");
testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv");
testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), "register plugin sloppy");
testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), "register plugin pre");
testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post");
/* STRICT parsing: mandatory, no conversion */
/* All perfect */
e = e_alloc | e_ok; c = 0;
testHead("STRICT parsing: all ok");
e1 = e_alloc | e_ok; c1 = 0;
testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct");
testOk(checkValues(puser, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
e = e_close | e_free; c = 0;
testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e1 = e_close | e_free; c1 = 0;
if (pch) dbChannelDelete(pch);
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
/* Any one missing must fail */
e = e_alloc | e_error | e_free; c = 0;
testHead("STRICT parsing: any missing parameter must fail");
e1 = e_alloc | e_error | e_free; c1 = 0;
testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "strict parsing: c missing");
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
e = e_alloc | e_error | e_free; c = 0;
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e1 = e_alloc | e_error | e_free; c1 = 0;
testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), "strict parsing: s missing");
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
e = e_alloc | e_error | e_free; c = 0;
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e1 = e_alloc | e_error | e_free; c1 = 0;
testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), "strict parsing: d missing");
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
e = e_alloc | e_error | e_free; c = 0;
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e1 = e_alloc | e_error | e_free; c1 = 0;
testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), "strict parsing: f missing");
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
e = e_alloc | e_error | e_free; c = 0;
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e1 = e_alloc | e_error | e_free; c1 = 0;
testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), "strict parsing: i missing");
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
/* NOCONV parsing: optional, no conversion */
/* Any one missing must leave the default intact */
e = e_alloc | e_ok; c = 0;
testHead("NOCONV parsing: any missing parameter must fall back to default value");
e1 = e_alloc | e_ok; c1 = 0;
testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "noconv parsing: c missing");
testOk(checkValues(puser, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
e = e_close | e_free; c = 0;
testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e1 = e_close | e_free; c1 = 0;
if (pch) dbChannelDelete(pch);
testOk(!puser, "user part cleaned up");
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
testOk(!puser1, "user part cleaned up");
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
e = e_any;
e1 = e_any;
testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), "noconv parsing: s missing");
testOk(checkValues(puser, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct");
testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct");
if (pch) dbChannelDelete(pch);
testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: d missing");
testOk(checkValues(puser, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct");
testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct");
if (pch) dbChannelDelete(pch);
testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: f missing");
testOk(checkValues(puser, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct");
testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct");
if (pch) dbChannelDelete(pch);
testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: i missing");
testOk(checkValues(puser, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct");
testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct");
if (pch) dbChannelDelete(pch);
/* Reject wrong types */
#define WRONGTYPETEST(Var, Val, Typ) \
e = e_alloc | e_error | e_free; c = 0; \
e1 = e_alloc | e_error | e_free; c1 = 0; \
testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), "noconv parsing: wrong type "#Typ" for "#Var); \
testOk(!puser, "user part cleaned up"); \
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
testOk(!puser1, "user part cleaned up"); \
if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
testHead("NOCONV parsing: rejection of wrong parameter types");
WRONGTYPETEST(i, 123.0, double);
WRONGTYPETEST(i, true, boolean);
@@ -334,22 +549,23 @@ MAIN(chfPluginTest)
/* SLOPPY parsing: optional, with conversion */
#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval, Cval) \
e = e_alloc | e_ok; c = 0; \
e1 = e_alloc | e_ok; c1 = 0; \
testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (good) for "#Var); \
testOk(checkValues(puser, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); \
e = e_close | e_free; c = 0; \
testOk(checkValues(puser1, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \
if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
e1 = e_close | e_free; c1 = 0; \
if (pch) dbChannelDelete(pch); \
testOk(!puser, "user part cleaned up"); \
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
testOk(!puser1, "user part cleaned up"); \
if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
#define CONVTESTBAD(Var, Val, Typ) \
e = e_alloc | e_error | e_free; c = 0; \
e1 = e_alloc | e_error | e_free; c1 = 0; \
testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (bad) for "#Var); \
testOk(!puser, "user part cleaned up"); \
if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c);
testOk(!puser1, "user part cleaned up"); \
if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
/* To integer */
testHead("SLOPPY parsing: conversion to integer");
CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 4);
CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 4);
CONVTESTBAD(i, "9234567890", out-of-range string);
@@ -359,10 +575,11 @@ MAIN(chfPluginTest)
CONVTESTBAD(i, 34.7e14, out-of-range double);
/* To boolean */
testHead("SLOPPY parsing: conversion to boolean");
CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 4);
CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 4);
CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 4);
CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4);
CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); /* Any one missing must leave the default intact */
CONVTESTBAD(f, ".4", invalid .4 string);
CONVTESTBAD(f, "Flase", misspelled invalid string);
CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 4);
@@ -374,6 +591,7 @@ MAIN(chfPluginTest)
CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 4);
/* To double */
testHead("SLOPPY parsing: conversion to double");
CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 4);
CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 4);
CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 4);
@@ -386,6 +604,7 @@ MAIN(chfPluginTest)
CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 4);
/* To string */
testHead("SLOPPY parsing: conversion to string");
CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 4);
CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 4);
CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 4);
@@ -395,12 +614,93 @@ MAIN(chfPluginTest)
CONVTESTGOOD(s, -1.23456789123456789e26, large rounded negative double, 12, 1, 1.234e5, "-1.234567891235e+26", 4);
/* To Enum */
testHead("SLOPPY parsing: conversion to enum");
CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 2);
CONVTESTBAD(c, 3, invalid integer choice);
CONVTESTBAD(c, 3.2, double);
CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 1);
CONVTESTBAD(c, "blubb", invalid string choice);
/* Registering and running filter callbacks */
#define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \
testHead("Filter chain test, "Type" filter"); \
offset = 0; \
e1 = e_alloc | e_ok; c1 = 0; \
testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \
if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
e1 = e_open | ExpReg; c1 = 0; \
testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \
if (!testOk(c1 == e1, "open channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
e1 = ExpRun; c1 = 0; \
testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \
pfl->type = dbfl_type_ref; \
pfl->field_type = TYPE_START; \
testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \
testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \
testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \
db_delete_field_log(pfl); \
if (!testOk(c1 == e1, "run filter chains: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
e1 = e_report; c1 = 0; \
dbChannelShow(pch, R_INTRO, R_LEVEL); \
if (!testOk(c1 == e1, "report: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
e1 = e_close | e_free; c1 = 0; \
if (pch) dbChannelDelete(pch); \
if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
#define CHAINTEST2(Type, Json, ExpReg1, ExpRun1, ExpReg2, ExpRun2, DType) \
testHead("Filter chain test, "Type" filters"); \
offset = 0; \
e1 = e_alloc | e_ok; c1 = 0; \
e2 = e_alloc | e_ok; c2 = 0; \
testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filters"); \
if (!testOk(c1 == e1, "create channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
if (!testOk(c2 == e2, "create channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
e1 = e_open | ExpReg1; c1 = 0; \
e2 = e_open | ExpReg2; c2 = 0; \
if (pch) testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \
if (!testOk(c1 == e1, "open channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
if (!testOk(c2 == e2, "open channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
e1 = ExpRun1; c1 = 0; \
e2 = ExpRun2; c2 = 0; \
if (pch) testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \
pfl->type = dbfl_type_ref; \
pfl->field_type = TYPE_START; \
if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \
if (pch) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \
testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \
db_delete_field_log(pfl); \
if (!testOk(c1 == e1, "run filter chains (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
e1 = e_report; c1 = 0; \
e2 = e_report; c2 = 0; \
dbChannelShow(pch, R_INTRO, R_LEVEL); \
if (!testOk(c1 == e1, "report (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
if (!testOk(c2 == e2, "report (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
e1 = e_close | e_free; c1 = 0; \
e2 = e_close | e_free; c2 = 0; \
if (pch) dbChannelDelete(pch); \
if (!testOk(c1 == e1, "delete channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
if (!testOk(c2 == e2, "delete channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2);
CHAINTEST1("1 pre", "{\"pre\":{}}", e_reg_pre, e_pre | e_dtor, 1); /* One filter, pre chain */
CHAINTEST1("1 post", "{\"post\":{}}", e_reg_post, e_post | e_dtor, 1); /* One filter, post chain */
CHAINTEST1("1 both", "{\"sloppy\":{}}", e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 2); /* One, both chains */
CHAINTEST2("2 pre", "{\"pre\":{},\"pre\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_pre, e_pre, 2); /* Two filters, pre chain */
CHAINTEST2("2 post", "{\"post\":{},\"post\":{}}", e_reg_post, e_post | e_dtor, e_reg_post, e_post, 2); /* Two filters, post chain */
CHAINTEST2("2 both", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains */
e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 4);
CHAINTEST2("1 pre, 1 post", "{\"pre\":{},\"post\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_post, e_post, 2); /* Two, pre then post */
CHAINTEST2("1 post, 1 pre", "{\"post\":{},\"pre\":{}}", e_reg_post, e_post, e_reg_pre, e_pre | e_dtor, 2); /* Two, post then pre */
CHAINTEST2("1 pre, 1 both", "{\"pre\":{},\"sloppy\":{}}", /* Two, pre then both */
e_reg_pre, e_pre | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 3);
CHAINTEST2("1 both, 1 pre", "{\"sloppy\":{},\"pre\":{}}", /* Two, both then pre */
e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre, e_pre, 3);
CHAINTEST2("1 post, 1 both", "{\"post\":{},\"sloppy\":{}}", /* Two, post then both */
e_reg_post, e_post, e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 3);
CHAINTEST2("1 both, 1 post", "{\"sloppy\":{},\"post\":{}}", /* Two, both then post */
e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_post, e_post, 3);
dbFreeBase(pdbbase);
return testDone();