testNoisyArithmetic.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/calctest/testNoisyArithmetic.cpp#3 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2004-2009 SQLstream, Inc.
00006 // Copyright (C) 2009-2009 LucidEra, Inc.
00007 //
00008 // This program is free software; you can redistribute it and/or modify it
00009 // under the terms of the GNU General Public License as published by the Free
00010 // Software Foundation; either version 2 of the License, or (at your option)
00011 // any later version approved by The Eigenbase Project.
00012 //
00013 // This program is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU General Public License
00019 // along with this program; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 //
00022 // ---
00023 //
00024 // test NoisyArithmetic functions
00025 //
00026 //    TODO:
00027 //        1. Doesn't allow octal/hex format for unsigned types.
00028 //        2. Doesn't test for thread/re-entrance safety.
00029 //
00030 */
00031 
00032 #include "fennel/common/CommonPreamble.h"
00033 
00034 #include <boost/test/unit_test_suite.hpp>
00035 
00036 // FIXME jvs 12-Aug-2007:  This file had compilation errors on Windows
00037 // so I disabled it on that platform for now.
00038 #ifndef __MSVC__
00039 #include <assert.h>
00040 #include <stdio.h>
00041 #include <sysexits.h>
00042 
00043 #include "fennel/calculator/NoisyArithmetic.h"
00044 
00045 using namespace fennel;
00046 
00047 /* --- */
00048 #define OP_ADD    1
00049 #define OP_SUB    2
00050 #define OP_MUL    3
00051 #define OP_DIV    4
00052 #define OP_NEG    5
00053 
00054 #define T_C       1
00055 #define T_SC      2
00056 #define T_UC      3
00057 #define T_S       4
00058 #define T_US      5
00059 #define T_I32     6
00060 #define T_UI32    7
00061 #define T_I64     8
00062 #define T_UI64    9
00063 #define T_F       10
00064 #define T_D       11
00065 #define T_LD      12
00066 
00067 #define EX_NONE    0
00068 #define EX_DATA    1
00069 #define EX_ERROR   2
00070 
00071 /* --- */
00072 static struct TOp {
00073     const char *pName;
00074     int iOpCode;
00075     int iArity;
00076 } g_Ops[] = {
00077     { "add", OP_ADD, 2 },
00078     { "sub", OP_SUB, 2 },
00079     { "mul", OP_MUL, 2 },
00080     { "div", OP_DIV, 2 },
00081     { "neg", OP_NEG, 1 },
00082     { 0, 0 },
00083 };
00084 static struct TType {
00085     const char *pName;
00086     int iCode;
00087     const char *pScanfFormat;
00088     const char *pPrintfFormat;
00089 } g_Types[] = {
00090     { "char", T_C, "%hhi", "%hhd" },
00091     { "signed char", T_SC, "%hhi", "%hhd" },
00092     { "unsigned char", T_UC, "%hhu", "%hhu" },
00093     { "short", T_S, "%hi", "%hd"  },
00094     { "unsigned short", T_US, "%hu", "%hu" },
00095     { "int", T_I32, "%i", "%d"  },
00096     { "unsigned int", T_UI32, "%u", "%u" },
00097     { "long long int", T_I64,  "%lli", "%lld"  },
00098     { "long long unsigned int", T_UI64, "%llu", "%llu" },
00099     { "float", T_F, "%e", "%e" },
00100     { "double", T_D, "%le", "%le" },
00101     { "long double", T_LD, "%lle", "%lle" },
00102     { 0, 0 },
00103 };
00104 enum {
00105     SHORT_MSG_LENGTH = 7,
00106     LONG_MSG_LENGTH/*hack*/ = 512,
00107     FIXED_ARGS = 3
00108 };
00109 union TSuper {
00110     char c;
00111     unsigned char uc;
00112     short s;
00113     unsigned short us;
00114     int i32;
00115     unsigned int ui32;
00116     long long int i64;
00117     unsigned long long int ui64;
00118     float f;
00119     double d;
00120     long double ld;
00121     //char msg[SHORT_MSG_LENGTH];
00122     char msg[LONG_MSG_LENGTH];/*hack - TODO*/
00123     char _cptr[];
00124 };
00125 
00126 /* --- */
00127 class Noisy_no_error : public std::runtime_error
00128 {
00129 public:
00130     explicit Noisy_no_error()
00131     :    std::runtime_error("") {
00132         }
00133 };
00134 
00135 /* --- */
00136 static int g_iProgramReturnCode = 0;
00137 
00138 /* --- */
00139 inline int min(int a, int b)
00140 {
00141     return (a < b) ? a : b;
00142 }
00143 
00144 /* --- */
00145 template <typename TMPL>
00146 bool DoOp_0(
00147     int iOpCode, int iExType, const char *pExpected,
00148     char *pOp0, char *pOp1, char *pResult)
00149 {
00150     TMPL *tOp0 = reinterpret_cast<TMPL *>(pOp0);
00151     TMPL *tOp1 = reinterpret_cast<TMPL *>(pOp1);
00152     TMPL *tResult = reinterpret_cast<TMPL *>(pResult);
00153     TProgramCounter pc(0);
00154     switch (iOpCode) {
00155     case OP_ADD:
00156         *tResult = Noisy<TMPL>::add(pc, *tOp0, *tOp1, 0);
00157         break;
00158 
00159     case OP_SUB:
00160         *tResult = Noisy<TMPL>::sub(pc, *tOp0, *tOp1, 0);
00161         break;
00162 
00163     case OP_MUL:
00164         *tResult = Noisy<TMPL>::mul(pc, *tOp0, *tOp1, 0);
00165         break;
00166 
00167     case OP_DIV:
00168         *tResult = Noisy<TMPL>::div(pc, *tOp0, *tOp1, 0);
00169         break;
00170 
00171     case OP_NEG:
00172         *tResult = Noisy<TMPL>::neg(pc, *tOp0, 0);
00173         break;
00174 
00175     default:
00176         assert(0 /* op not implemented */);
00177     }
00178     switch (iExType) {
00179     case EX_NONE:
00180         return true;
00181     case EX_ERROR:
00182         throw Noisy_no_error();
00183         assert(0);
00184     case EX_DATA:
00185         break;
00186     default:
00187         assert(0);
00188     };
00189     const TMPL *tExpected = reinterpret_cast<const TMPL *>(pExpected);
00190     return (*tResult) == (*tExpected);
00191 }
00192 
00193 /* --- */
00194 static bool DoOp_1(
00195     int iTypeCode, int iOpCode, int iExType,
00196     const char *pExpected, char *pOp0, char *pOp1, char *pResult)
00197 {
00198     switch (iTypeCode) {
00199 #define DOOP(type) \
00200     return DoOp_0<type>(iOpCode, iExType, pExpected, pOp0, pOp1, pResult); \
00201     break;
00202 
00203     case T_C:
00204         DOOP(char);
00205     case T_SC:
00206         DOOP(signed char);
00207     case T_UC:
00208         DOOP(unsigned char);
00209     case T_S:
00210         DOOP(short);
00211     case T_US:
00212        DOOP(unsigned short);
00213     case T_I32:
00214         DOOP(int);
00215     case T_UI32:
00216         DOOP(unsigned int);
00217     case T_I64:
00218         DOOP(long long int);
00219     case T_UI64:
00220         DOOP(unsigned long long int);
00221     case T_F:
00222         DOOP(float);
00223     case T_D:
00224         DOOP(double);
00225     case T_LD:
00226         DOOP(long double);
00227 #undef DOOP
00228     default:
00229         assert(0 /* type not implemented */);
00230     }
00231     return false;    /* !++(__compiler_unhappy)++ */
00232 }
00233 
00234 /* --- */
00235 static bool DoOp_2(
00236     int iType, int iOpCode, int iExType,
00237     const char *pExpected, char *pOp0, char *pOp1, char *pResult)
00238 {
00239     try {
00240         bool bRet = DoOp_1(
00241             g_Types[iType].iCode,
00242             iOpCode,
00243             iExType,
00244             pExpected,
00245             pOp0,
00246             pOp1,
00247             pResult);
00248         sprintf(pResult, g_Types[iType].pPrintfFormat, *pResult);
00249         return bRet;
00250     } catch (Noisy_no_error &e) {
00251         sprintf(pResult, g_Types[iType].pPrintfFormat, *pResult);
00252         return false;
00253     } catch (CalcMessage &msg) {
00254         *pResult = '!';
00255         strncpy(&(pResult[1]), msg.str, SHORT_MSG_LENGTH - 2);
00256         pResult[SHORT_MSG_LENGTH - 1] = '\0';
00257         if (iExType == EX_NONE) {
00258             return true;
00259         }
00260         return strcmp(pResult, pExpected) == 0;
00261     }
00262 }
00263 
00264 /* --- */
00265 template <typename TMPL>
00266 bool SetConstant(const char *pConstant, TMPL *pData, int iLine)
00267 {
00268     assert(pConstant);
00269     if (false) {
00270     } else if (!strcmp(pConstant, "MAX")) {
00271         *pData = std::numeric_limits<TMPL>::max();
00272     } else if (!strcmp(pConstant, "MIN")) {
00273         *pData = std::numeric_limits<TMPL>::min();
00274     } else {
00275         fprintf(stderr, "Invalid constant '%s' at line %d\n", pConstant, iLine);
00276         return false;
00277     }
00278     return true;
00279 }
00280 
00281 /* --- */
00282 static bool SetConstant(
00283     const char *pConstant,
00284     int iTypeCode,
00285     char *pData,
00286     int iLine)
00287 {
00288     switch (iTypeCode) {
00289 #define DOOP(type) \
00290     return SetConstant<type>(pConstant, reinterpret_cast<type *>(pData), iLine)
00291     case T_C:
00292         DOOP(char);
00293     case T_SC:
00294         DOOP(signed char);
00295     case T_UC:
00296         DOOP(unsigned char);
00297     case T_S:
00298         DOOP(short);
00299     case T_US:
00300         DOOP(unsigned short);
00301     case T_I32:
00302         DOOP(int);
00303     case T_UI32:
00304         DOOP(unsigned int);
00305     case T_I64:
00306         DOOP(long long int);
00307     case T_UI64:
00308         DOOP(unsigned long long int);
00309     case T_F:
00310         DOOP(float);
00311     case T_D:
00312         DOOP(double);
00313     case T_LD:
00314         DOOP(long double);
00315 #undef DOOP
00316     default:
00317         assert(0);
00318         return false;
00319     }
00320 }
00321 
00322 /* --- */
00323 static bool ReadArgument(
00324     int iLine, const char *pFile, int iArg, TType &tType,
00325     const char *ppArgs[], TSuper &t2Read)
00326 {
00327     const char *pValue = ppArgs[iArg];
00328     assert(pValue);
00329     if (*pValue=='&') {
00330         return SetConstant(&(pValue[1]), tType.iCode, t2Read._cptr, iLine);
00331     } else if (1 != sscanf(pValue, tType.pScanfFormat, t2Read._cptr)) {
00332         fprintf(
00333             stderr, "Error in argument %d at %s line %d\n", iArg,
00334             pFile, iLine);
00335         return false;
00336     }
00337     return true;
00338 }
00339 
00340 /* --- */
00341 static bool RunTest(
00342     int iLine, const char *pFile, const char *ppArgs[], int iArgCount)
00343 {
00344     // op type expected-result op0 [op1]
00345     if (iArgCount < FIXED_ARGS) {
00346         fprintf(
00347             stderr, "Bad number of arguments (%d) at %s line %d\n",
00348             iArgCount, pFile, iLine);
00349         return false;
00350     }
00351     assert(ppArgs[0]);
00352     int iOp = -1;
00353     int iArity = -1;
00354     for (int i = 0; g_Ops[i].pName; i++) {
00355         if (!strcasecmp(ppArgs[0], g_Ops[i].pName)) {
00356             iOp = i;
00357             break;
00358         }
00359     }
00360     if (iOp == -1) {
00361         fprintf(
00362             stderr, "No such operation '%s' at %s line %d\n",
00363             ppArgs[0], pFile, iLine);
00364         return false;
00365     }
00366     iArity = g_Ops[iOp].iArity;
00367     if (iArgCount < (FIXED_ARGS + iArity)) {
00368         fprintf(
00369             stderr,
00370             "Bad number of arguments (%d) for operation '%s' at %s line %d\n",
00371             iArgCount - FIXED_ARGS, g_Ops[iOp].pName, pFile, iLine);
00372         return false;
00373     }
00374     int iType = -1;
00375     assert(ppArgs[1]);
00376     for (int i = 0; g_Types[i].pName; i++) {
00377         if (!strcasecmp(ppArgs[1], g_Types[i].pName)) {
00378             iType = i;
00379             break;
00380         }
00381     }
00382     if (iType == -1) {
00383         fprintf(
00384             stderr, "No such type '%s' at %s line %d\n",
00385             ppArgs[1], pFile, iLine);
00386         return false;
00387     }
00388 
00389     /* --- Its important that this union is used and not a char * buffer
00390     to ensure correct [d/q]word alighnment of certain variables
00391     --- */
00392     TSuper tExpected;
00393     TSuper tOp0;
00394     TSuper tOp1;
00395     TSuper tResult;
00396 
00397     /* --- */
00398     int iExType;
00399     switch (*ppArgs[2]) {
00400     case '*':
00401         iExType = EX_NONE;
00402         break;
00403     case '!':
00404         iExType = EX_ERROR;
00405         strncpy(tExpected._cptr, ppArgs[2], SHORT_MSG_LENGTH);
00406         break;
00407     default:
00408         if (!ReadArgument(iLine, pFile, 2, g_Types[iType], ppArgs, tExpected)) {
00409             return false;
00410         }
00411         iExType = EX_DATA;
00412     }
00413     if (iArity > 0
00414         && !ReadArgument(iLine, pFile, 3, g_Types[iType], ppArgs, tOp0))
00415     {
00416         return false;
00417     }
00418     if (iArity > 1
00419         && !ReadArgument(iLine, pFile, 4, g_Types[iType], ppArgs, tOp1))
00420     {
00421         return false;
00422     }
00423 
00424     /* --- */
00425     if (!DoOp_2(
00426         iType, g_Ops[iOp].iOpCode, iExType,
00427         tExpected._cptr, tOp0._cptr, tOp1._cptr, tResult._cptr))
00428     {
00429         fprintf(stdout, "Line %d: Calculated result [", iLine);
00430         fprintf(stdout, "%s", tResult._cptr);
00431         fprintf(stdout, "] does not match expected [");
00432         fprintf(stdout, "%s", ppArgs[2]);
00433         fprintf(stdout, "].\n");
00434 
00435         fflush(stdout);
00436 
00437         g_iProgramReturnCode = 1;
00438         }
00439 
00440     return true;
00441 }
00442 
00443 /* --- */
00444 static char *CanonifyArg(char *pArg)
00445 {
00446     assert(pArg);
00447     while (*pArg && *pArg!=':' && isspace(*pArg)) {
00448         pArg++;    /* trim leading whitespace */
00449     }
00450     if (*pArg == '#') {
00451         return 0;
00452     }
00453     return pArg;
00454 }
00455 
00456 /* --- */
00457 static bool ProcessALine(
00458     int iLine, const char *pFile, char *pLine, size_t tLength)
00459 {
00460     enum { MAX_ARGS = 11 };
00461     const char *ppArgs[MAX_ARGS];
00462     int iArgCount = 0;
00463     char *pStart = pLine;
00464     for (int i = 0; iArgCount < MAX_ARGS; i++) {
00465         if (!pLine[i] || pLine[i] == ':') {
00466             ppArgs[iArgCount] = CanonifyArg(pStart);
00467             if (!ppArgs[iArgCount]) {
00468                 break;    /* rest of line is a comment */
00469             }
00470             iArgCount++;
00471             if (!pLine[i]) {
00472                 break;
00473             }
00474             pLine[i] = '\0';
00475             pStart = &(pLine[i + 1]);
00476         }
00477     }
00478     assert(iArgCount <= MAX_ARGS);
00479     assert(iArgCount);
00480     iArgCount--;
00481     if (iArgCount < 2) {
00482         return true; /* comment or empty line */
00483     }
00484     if (iArgCount == MAX_ARGS) {
00485         fprintf(
00486             stderr, "Too many arguments (max=%d) at %s line %d\n",
00487             MAX_ARGS - 1, pFile, iLine);
00488         return false;
00489     }
00490 
00491     return RunTest(iLine, pFile, &(ppArgs[1]), iArgCount);
00492 }
00493 
00494 /* --- return 0 unless (system/format/data) error --- */
00495 int InputFromStream(const char *pFilename)
00496 {
00497     enum { BUF_SIZE = 1023 };
00498     char szBuf[BUF_SIZE + 1];
00499     int iLine;
00500     FILE *pIn = stdin;
00501     FILE *pIn2Close = 0;
00502 
00503     /* --- */
00504     if (strcmp(pFilename, "-")) {
00505         pIn = ::fopen(pFilename, "r");
00506         if (!pIn) {
00507             perror(pFilename);
00508             return EX_IOERR;
00509         }
00510         pIn2Close = pIn;
00511     } else {
00512         pFilename = "(stdin)";
00513     }
00514 
00515     /* --- */
00516     szBuf[BUF_SIZE] = '\0';
00517     iLine = 0;
00518     for (;;) {
00519         const char *pRead = fgets(szBuf, BUF_SIZE, pIn);
00520         if (!pRead) {
00521             if (pIn2Close) {
00522                 ::fclose(pIn2Close);
00523             }
00524             return feof(pIn) ? g_iProgramReturnCode : EX_IOERR;
00525         }
00526         assert(pRead == szBuf);
00527         size_t tRead = strlen(pRead) - 1;
00528         iLine++;    /* line could be too long .... TODO */
00529         if (!tRead) {
00530             continue;    /* EOF exactly after BUF_SIZE chars */
00531         }
00532         if (szBuf[tRead] == '\r' || szBuf[tRead] == '\n') {
00533             while (szBuf[tRead] == '\r' || szBuf[tRead] == '\n') {
00534                 szBuf[tRead--] = '\0';
00535             }
00536         }
00537         assert(szBuf[tRead] != '\r' && szBuf[tRead] != '\n');
00538         if (!*szBuf) {
00539             continue;
00540         }
00541         if (!ProcessALine(iLine, pFilename, szBuf, tRead)) {
00542             /* real error, like invalid format */
00543             if (pIn2Close) {
00544                 ::fclose(pIn2Close);
00545             }
00546             return EX_DATAERR;
00547         }
00548     }
00549 
00550     /* --- shouldn't get here --- */
00551     assert(0);
00552     if (pIn2Close) {
00553         ::fclose(pIn2Close);
00554     }
00555     return 1;
00556 }
00557 
00558 /* --- return 0 unless (system/format/data) error --- */
00559 int ProcessCppLine(
00560     int iLine, const char *pInputFileName,
00561     const char *pLine, int iLength)
00562 {
00563     enum { BUF_SIZE = 1023 };
00564     char szBuf[BUF_SIZE + 1];
00565 
00566     /* --- */
00567     strncpy(szBuf, pLine, BUF_SIZE);
00568     szBuf[BUF_SIZE+1] ='\0';
00569 
00570     /* --- */
00571     return ProcessALine(
00572         iLine, pInputFileName, szBuf,
00573         min(iLength, BUF_SIZE));
00574 }
00575 
00576 /* --- */
00577 int main(int iArgc, const char *ppArgv[])
00578 {
00579     // this test is causing a seg. fault, and its not valuable as part
00580     // of build tests
00581     return 0;
00582 
00583     /* --- */
00584     if (iArgc < 1/*how?*/ || iArgc > 2) {
00585         fprintf(
00586             stderr, "Usage: %s [<filename>|-]\n", iArgc > 0 ? ppArgv[0] : "");
00587         ::exit(EX_USAGE);
00588     }
00589 
00590     /* --- */
00591     if (iArgc == 2) {
00592         /* read from file */
00593         return InputFromStream(ppArgv[1]);
00594     }
00595 
00596     /* else */
00597     /* read generated C++ from this file */
00598     int iLine = 1;
00599 #define GENERATED_FILE "testNoisyArithmeticGen.hpp"
00600 #define EXPR(line)                                                  \
00601     if (!ProcessCppLine(iLine++, GENERATED_FILE,                    \
00602            #line, sizeof(line) - 1))                                \
00603         return EX_DATAERR;
00604 #include GENERATED_FILE
00605 #undef EXPR
00606     return 0;
00607 }
00608 
00609 #endif
00610 
00611 boost::unit_test_framework::test_suite *init_unit_test_suite(int, char **)
00612 {
00613     return NULL;
00614 }
00615 
00616 // End testNoisyArithmetic.cpp

Generated on Mon Jun 22 04:00:14 2009 for Fennel by  doxygen 1.5.1