00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "fennel/common/CommonPreamble.h"
00033
00034 #include <boost/test/unit_test_suite.hpp>
00035
00036
00037
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 = 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
00122 char msg[LONG_MSG_LENGTH];
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 );
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 );
00230 }
00231 return false;
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
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
00390
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++;
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;
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;
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
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++;
00529 if (!tRead) {
00530 continue;
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
00543 if (pIn2Close) {
00544 ::fclose(pIn2Close);
00545 }
00546 return EX_DATAERR;
00547 }
00548 }
00549
00550
00551 assert(0);
00552 if (pIn2Close) {
00553 ::fclose(pIn2Close);
00554 }
00555 return 1;
00556 }
00557
00558
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
00580
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
00593 return InputFromStream(ppArgv[1]);
00594 }
00595
00596
00597
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