/* 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 .
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
#include
#include
#include
#include
#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 (%d bytes):\n%s\n", (int)strlen((char*)rsCStrGetSzStr(pstrPrg)), rsCStrGetSzStr(pstrPrg));
printf("expected (%d bytes):\n%s\n", (int)strlen((char*)rsCStrGetSzStr(pstrOut)), 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;
int iParse;
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: ". Anything
* after nbr is simply ignored.
*/
if(sscanf(lnptr, "result: %d", &iParse) != 1) {
printf("error in result line, scanf failed, line: '%s'\n", lnptr);
ABORT_FINALIZE(RS_RET_ERR);
}
iRetExpected = iParse;
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