From 47e2e228ab83623bbc628ad3f69ffe23d479384e Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:57 -0400 Subject: [PATCH] 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 --- src/ioc/db/test/chfPluginTest.c | 460 ++++++++++++++++++++++++++------ 1 file changed, 380 insertions(+), 80 deletions(-) diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index ac0e579e5..38bc43c6f 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -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();