diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | runtime/expr.c | 2 | ||||
-rw-r--r-- | runtime/vmprg.c | 1 | ||||
-rw-r--r-- | tests/1.rstest | 26 | ||||
-rw-r--r-- | tests/2.rstest | 10 | ||||
-rw-r--r-- | tests/Makefile.am | 11 | ||||
-rw-r--r-- | tests/err1.rstest | 7 | ||||
-rw-r--r-- | tests/rscript-parse.c | 108 | ||||
-rw-r--r-- | tests/rscript.c | 258 | ||||
-rw-r--r-- | tests/testbench.h | 3 |
11 files changed, 313 insertions, 116 deletions
@@ -34,3 +34,4 @@ tmp* log logfile debug +core.* @@ -1,3 +1,5 @@ +- added a generic test driver for RainerScripts plus some test cases + to the testbench --------------------------------------------------------------------------- Version 3.19.9 (rgerhards), 2008-07-?? - added tutorial for creating a TLS-secured syslog infrastructure diff --git a/runtime/expr.c b/runtime/expr.c index 9a314855..ee5b9e2c 100644 --- a/runtime/expr.c +++ b/runtime/expr.c @@ -114,7 +114,7 @@ terminal(expr_t *pThis, ctok_t *tok) finalize_it: if(pToken != NULL) { - CHKiRet(ctok_token.Destruct(&pToken)); /* "eat" processed token */ + ctok_token.Destruct(&pToken); /* "eat" processed token */ } RETiRet; diff --git a/runtime/vmprg.c b/runtime/vmprg.c index c7354fe4..705e6948 100644 --- a/runtime/vmprg.c +++ b/runtime/vmprg.c @@ -103,7 +103,6 @@ Obj2Str(vmprg_t *pThis, cstr_t *pstrPrg) ISOBJ_TYPE_assert(pThis, vmprg); assert(pstrPrg != NULL); - CHKiRet(rsCStrAppendStr(pstrPrg, (uchar*)"program contents:\n")); i = 0; /* "program counter" */ for(pOp = pThis->vmopRoot ; pOp != NULL ; pOp = pOp->pNext) { lenAddr = snprintf((char*)szAddr, sizeof(szAddr), "%8.8d: ", i++); diff --git a/tests/1.rstest b/tests/1.rstest new file mode 100644 index 00000000..5c152589 --- /dev/null +++ b/tests/1.rstest @@ -0,0 +1,26 @@ +# a simple RainerScript test +result: 0 +in: +'test 1' <> $var or /* some comment */($SEVERITY == -4 +5 -(3 * - 2) and $fromhost == '127.0.0.1') then +$$$ +out: +00000000: PUSHCONSTANT test 1[cstr] +00000001: PUSHMSGVAR var[cstr] +00000002: != +00000003: PUSHMSGVAR severity[cstr] +00000004: PUSHCONSTANT 4[nbr] +00000005: UNARY_MINUS +00000006: PUSHCONSTANT 5[nbr] +00000007: + +00000008: PUSHCONSTANT 3[nbr] +00000009: PUSHCONSTANT 2[nbr] +00000010: UNARY_MINUS +00000011: * +00000012: - +00000013: == +00000014: PUSHMSGVAR fromhost[cstr] +00000015: PUSHCONSTANT 127.0.0.1[cstr] +00000016: == +00000017: and +00000018: or +$$$ diff --git a/tests/2.rstest b/tests/2.rstest new file mode 100644 index 00000000..7fb5b799 --- /dev/null +++ b/tests/2.rstest @@ -0,0 +1,10 @@ +# a simple RainerScript test +result: 0 +in: +$msg contains 'test' then +$$$ +out: +00000000: PUSHMSGVAR msg[cstr] +00000001: PUSHCONSTANT test[cstr] +00000002: contains +$$$ diff --git a/tests/Makefile.am b/tests/Makefile.am index d85a56f8..3ced4769 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,14 +1,15 @@ -check_PROGRAMS = rt_init rscript_parse +check_PROGRAMS = rt_init rscript TESTS = $(check_PROGRAMS) test_files = testbench.h runtime-dummy.c +EXTRA_DIST=*.rstest rt_init_SOURCES = rt-init.c $(test_files) rt_init_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) rt_init_LDADD = $(rsrt_libs) $(zlib_libs) $(pthreads_libs) rt_init_LDFLAGS = -export-dynamic -rscript_parse_SOURCES = rscript-parse.c $(test_files) -rscript_parse_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) -rscript_parse_LDADD = $(rsrt_libs) $(zlib_libs) $(pthreads_libs) -rscript_parse_LDFLAGS = -export-dynamic +rscript_SOURCES = rscript.c $(test_files) +rscript_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) +rscript_LDADD = $(rsrt_libs) $(zlib_libs) $(pthreads_libs) +rscript_LDFLAGS = -export-dynamic diff --git a/tests/err1.rstest b/tests/err1.rstest new file mode 100644 index 00000000..8c56887e --- /dev/null +++ b/tests/err1.rstest @@ -0,0 +1,7 @@ +# This test case check for an error condition +result: -2051 +in: +'test 1' <> == $hostname +$$$ +out: +$$$ diff --git a/tests/rscript-parse.c b/tests/rscript-parse.c deleted file mode 100644 index 01ddb7d1..00000000 --- a/tests/rscript-parse.c +++ /dev/null @@ -1,108 +0,0 @@ -/* This test checks runtime initialization and exit. Other than that, it - * also serves as the most simplistic sample of how a test can be coded. - * - * Part of the testbench for rsyslog. - * Copyright 2008 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of rsyslog. - * - * Rsyslog is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Rsyslog is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" -#include <stdio.h> - -#include "rsyslog.h" -#include "testbench.h" -#include "ctok.h" -#include "expr.h" - -MODULE_TYPE_TESTBENCH -/* define addtional objects we need for our tests */ -DEFobjCurrIf(expr) -DEFobjCurrIf(ctok) -DEFobjCurrIf(ctok_token) -DEFobjCurrIf(vmprg) - -BEGINInit -CODESTARTInit - pErrObj = "expr"; CHKiRet(objUse(expr, CORE_COMPONENT)); - pErrObj = "ctok"; CHKiRet(objUse(ctok, CORE_COMPONENT)); - pErrObj = "ctok_token"; CHKiRet(objUse(ctok_token, CORE_COMPONENT)); - pErrObj = "vmprg"; CHKiRet(objUse(vmprg, CORE_COMPONENT)); -ENDInit - -BEGINExit -CODESTARTExit -ENDExit - -BEGINTest - cstr_t *pstrPrg; - ctok_t *tok; - ctok_token_t *pToken; - expr_t *pExpr; - /* the string below is an expression as defined up to 3.19.x - note that the - * then and the space after it MUST be present! - */ - //uchar szExpr[] = " $msg contains 'test' then "; - uchar szExpr[] = "'test 1' <> $var or /* some comment */($SEVERITY == -4 +5 -(3 * - 2) and $fromhost == '127.0.0.1') then "; - /*uchar szSynErr[] = "$msg == 1 and syntaxerror ";*/ -CODESTARTTest - /* we first need a tokenizer... */ - CHKiRet(ctok.Construct(&tok)); - CHKiRet(ctok.Setpp(tok, szExpr)); - CHKiRet(ctok.ConstructFinalize(tok)); - - /* now construct our expression */ - CHKiRet(expr.Construct(&pExpr)); - CHKiRet(expr.ConstructFinalize(pExpr)); - - /* ready to go... */ - CHKiRet(expr.Parse(pExpr, tok)); - - /* we now need to parse off the "then" - and note an error if it is - * missing... - * - * rgerhards, 2008-07-01: we disable the check below, because I can not - * find the cause of the misalignment. The problem is that pToken structure has - * a different member alignment inside the runtime library then inside of - * this program. I checked compiler options, but could not find the cause. - * Should anyone have any insight, I'd really appreciate if you drop me - * a line. - */ - CHKiRet(ctok.GetToken(tok, &pToken)); - if(pToken->tok != ctok_THEN) { - ctok_token.Destruct(&pToken); - ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); - } - - ctok_token.Destruct(&pToken); /* no longer needed */ - - CHKiRet(rsCStrConstruct(&pstrPrg)); - CHKiRet(vmprg.Obj2Str(pExpr->pVmprg, pstrPrg)); - - printf("string returned: '%s'\n", rsCStrGetSzStr(pstrPrg)); - rsCStrDestruct(&pstrPrg); - - /* we are done, so we now need to restore things */ - CHKiRet(ctok.Destruct(&tok)); -finalize_it: - /* here we may do custom error reporting */ - if(iRet != RS_RET_OK) { - uchar *pp; - ctok.Getpp(tok, &pp); - printf("error on or before '%s'\n", pp); - } -ENDTest diff --git a/tests/rscript.c b/tests/rscript.c new file mode 100644 index 00000000..f82107ed --- /dev/null +++ b/tests/rscript.c @@ -0,0 +1,258 @@ +/* This test checks runtime initialization and exit. Other than that, it + * also serves as the most simplistic sample of how a test can be coded. + * + * Part of the testbench for rsyslog. + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" +#include <stdio.h> +#include <glob.h> +#include <sys/stat.h> + +#include "rsyslog.h" +#include "testbench.h" +#include "ctok.h" +#include "expr.h" + +MODULE_TYPE_TESTBENCH +/* define addtional objects we need for our tests */ +DEFobjCurrIf(expr) +DEFobjCurrIf(ctok) +DEFobjCurrIf(ctok_token) +DEFobjCurrIf(vmprg) + +BEGINInit +CODESTARTInit + pErrObj = "expr"; CHKiRet(objUse(expr, CORE_COMPONENT)); + pErrObj = "ctok"; CHKiRet(objUse(ctok, CORE_COMPONENT)); + pErrObj = "ctok_token"; CHKiRet(objUse(ctok_token, CORE_COMPONENT)); + pErrObj = "vmprg"; CHKiRet(objUse(vmprg, CORE_COMPONENT)); +ENDInit + +BEGINExit +CODESTARTExit +ENDExit + + +/* perform a single test. This involves compiling the test script, + * checking the result of the compilation (iRet) and a check of the + * generated program (via a simple strcmp). The resulting program + * check is only done if the test should not detect a syntax error + * (for obvious reasons, there is no point in checking the result of + * a failed compilation). + * rgerhards, 2008-07--07 + */ +static rsRetVal +PerformTest(cstr_t *pstrIn, rsRetVal iRetExpected, cstr_t *pstrOut) +{ + cstr_t *pstrPrg = NULL; + ctok_t *tok = NULL; + ctok_token_t *pToken = NULL; + expr_t *pExpr; + rsRetVal localRet; + DEFiRet; + + /* we first need a tokenizer... */ + CHKiRet(ctok.Construct(&tok)); + CHKiRet(ctok.Setpp(tok, rsCStrGetSzStr(pstrIn))); + CHKiRet(ctok.ConstructFinalize(tok)); + + /* now construct our expression */ + CHKiRet(expr.Construct(&pExpr)); + CHKiRet(expr.ConstructFinalize(pExpr)); + + /* ready to go... */ + localRet = expr.Parse(pExpr, tok); + + /* check if we expected an error */ + if(localRet != iRetExpected) { + printf("Error in compile return code. Expected %d, received %d\n", + iRetExpected, localRet); + CHKiRet(rsCStrConstruct(&pstrPrg)); + CHKiRet(vmprg.Obj2Str(pExpr->pVmprg, pstrPrg)); + printf("generated vmprg:\n%s\n", rsCStrGetSzStr(pstrPrg)); + ABORT_FINALIZE(iRetExpected == RS_RET_OK ? localRet : RS_RET_ERR); + } + + if(iRetExpected != RS_RET_OK) + FINALIZE; /* if we tested an error case, we are done */ + + /* OK, we got a compiled program, so now let's compare that */ + + CHKiRet(rsCStrConstruct(&pstrPrg)); + CHKiRet(vmprg.Obj2Str(pExpr->pVmprg, pstrPrg)); + + if(strcmp((char*)rsCStrGetSzStr(pstrPrg), (char*)rsCStrGetSzStr(pstrOut))) { + printf("error: compiled program different from expected result!\n"); + printf("generated vmprg:\n%s\n", rsCStrGetSzStr(pstrPrg)); + printf("expected:\n%s\n", rsCStrGetSzStr(pstrOut)); + ABORT_FINALIZE(RS_RET_ERR); + } + +finalize_it: + /* we are done, so we now need to restore things */ + if(pToken != NULL) + ctok_token.Destruct(&pToken); /* no longer needed */ + if(pstrPrg != NULL) + rsCStrDestruct(&pstrPrg); + if(tok != NULL) + ctok.Destruct(&tok); + RETiRet; +} + + +/* a helper macro to generate some often-used code... */ +#define CHKEOF \ + if(feof(fp)) { \ + printf("error: unexpected end of control file %s\n", pszFileName); \ + ABORT_FINALIZE(RS_RET_ERR); \ + } +/* process a single test file + * Note that we do not do a real parser here. The effort is not + * justified by what we need to do. So it is a quick shot. + * rgerhards, 2008-07-07 + */ +static rsRetVal +ProcessTestFile(uchar *pszFileName) +{ + FILE *fp; + char *lnptr = NULL; + size_t lenLn; + cstr_t *pstrIn = NULL; + cstr_t *pstrOut = NULL; + rsRetVal iRetExpected; + DEFiRet; + + if((fp = fopen((char*)pszFileName, "r")) == NULL) { + perror((char*)pszFileName); + ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); + } + + /* skip comments at start of file */ + + getline(&lnptr, &lenLn, fp); + while(!feof(fp)) { + if(*lnptr == '#') + getline(&lnptr, &lenLn, fp); + else + break; /* first non-comment */ + } + CHKEOF; + + /* once we had a comment, the next line MUST be "result: <nbr>". Anything + * after nbr is simply ignored. + */ + if(sscanf(lnptr, "result: %d", &iRetExpected) != 1) { + printf("error in result line, scanf failed, line: '%s'\n", lnptr); + ABORT_FINALIZE(RS_RET_ERR); + } + getline(&lnptr, &lenLn, fp); CHKEOF; + + /* and now we look for "in:" (and again ignore the rest...) */ + if(strncmp(lnptr, "in:", 3)) { + printf("error: expected 'in:'-line, but got: '%s'\n", lnptr); + ABORT_FINALIZE(RS_RET_ERR); + } + /* if we reach this point, we need to read in the input script. It is + * terminated by a line with three sole $ ($$$\n) + */ + CHKiRet(rsCStrConstruct(&pstrIn)); + getline(&lnptr, &lenLn, fp); CHKEOF; + while(strncmp(lnptr, "$$$\n", 4)) { + CHKiRet(rsCStrAppendStr(pstrIn, (uchar*)lnptr)); + getline(&lnptr, &lenLn, fp); CHKEOF; + } + getline(&lnptr, &lenLn, fp); CHKEOF; /* skip $$$-line */ + + /* and now we look for "out:" (and again ignore the rest...) */ + if(strncmp(lnptr, "out:", 4)) { + printf("error: expected 'out:'-line, but got: '%s'\n", lnptr); + ABORT_FINALIZE(RS_RET_ERR); + } + /* if we reach this point, we need to read in the expected program code. It is + * terminated by a line with three sole $ ($$$\n) + */ + CHKiRet(rsCStrConstruct(&pstrOut)); + getline(&lnptr, &lenLn, fp); CHKEOF; + while(strncmp(lnptr, "$$$\n", 4)) { + CHKiRet(rsCStrAppendStr(pstrOut, (uchar*)lnptr)); + getline(&lnptr, &lenLn, fp); CHKEOF; + } + + /* un-comment for testing: + * printf("iRet: %d, script: %s\n, out: %s\n", iRetExpected, rsCStrGetSzStr(pstrIn),rsCStrGetSzStr(pstrOut)); + */ + if(rsCStrGetSzStr(pstrIn) == NULL) { + printf("error: input script is empty!\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + if(rsCStrGetSzStr(pstrOut) == NULL && iRetExpected == RS_RET_OK) { + printf("error: output script is empty!\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + + CHKiRet(PerformTest(pstrIn, iRetExpected, pstrOut)); + +finalize_it: + if(pstrIn != NULL) + rsCStrDestruct(&pstrIn); + if(pstrOut != NULL) + rsCStrDestruct(&pstrOut); + RETiRet; +} + + +/* This test is parameterized. It search for test control files and + * loads all that it finds. To add tests, simply create new .rstest + * files. + * rgerhards, 2008-07-07 + */ +BEGINTest + uchar *testFile; + glob_t testFiles; + size_t i = 0; + struct stat fileInfo; +CODESTARTTest + glob("*.rstest", GLOB_MARK, NULL, &testFiles); + + for(i = 0; i < testFiles.gl_pathc; i++) { + testFile = (uchar*) testFiles.gl_pathv[i]; + + if(stat((char*) testFile, &fileInfo) != 0) + continue; /* continue with the next file if we can't stat() the file */ + + /* all regular files are run through the test logic. Symlinks don't work. */ + if(S_ISREG(fileInfo.st_mode)) { /* config file */ + printf("processing RainerScript test file '%s'...\n", testFile); + iRet = ProcessTestFile((uchar*) testFile); + if(iRet != RS_RET_OK) { + /* in this case, re-run with debugging on */ + printf("processing test case failed with %d, re-running with debug messages:\n", + iRet); + Debug = 1; /* these two are dirty, but we need them today... */ + debugging_on = 1; + CHKiRet(ProcessTestFile((uchar*) testFile)); + } + } + } + globfree(&testFiles); + +finalize_it: +ENDTest diff --git a/tests/testbench.h b/tests/testbench.h index 6f26724a..12687743 100644 --- a/tests/testbench.h +++ b/tests/testbench.h @@ -40,7 +40,8 @@ int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[]) CHKiRet(doTest()); \ CHKiRet(doExit()); \ finalize_it: \ - printf("test returns iRet %d\n", iRet); \ + if(iRet != RS_RET_OK) \ + printf("test returns iRet %d\n", iRet); \ RETiRet; \ } |