testlib.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. #ifndef _TESTLIB_H_
  2. #define _TESTLIB_H_
  3. /*
  4. *
  5. * Copyright (c) 2005-2008
  6. */
  7. #define VERSION "0.5.1"
  8. /*
  9. * Mike Mirzayanov
  10. *
  11. * This material is provided "as is", with absolutely no warranty expressed
  12. * or implied. Any use is at your own risk.
  13. *
  14. * Permission to use or copy this software for any purpose is hereby granted
  15. * without fee, provided the above notices are retained on all copies.
  16. * Permission to modify the code and to distribute modified code is granted,
  17. * provided the above notices are retained, and a notice that the code was
  18. * modified is included with the above copyright notice.
  19. *
  20. */
  21. /* NOTE: This file contains testlib library for C++.
  22. *
  23. * Program, using testlib running format:
  24. * check.exe <Input_File> <Output_File> <Answer_File> [<Result_File> [-appes]],
  25. *
  26. * If result file is specified it will contain results.
  27. */
  28. char* latestFeatures[] = {
  29. "Now no footer added to each report by default (use directive FOOTER to switch on)",
  30. "Now every checker has a name, use setName(const char* format, ...) to set it",
  31. "Now it is compatible with TTS (by Kittens Computing)",
  32. "Added \'ensure(condition, message = \"\")\' feature, it works like assert()",
  33. "Fixed compatibility with MS C++ 7.1",
  34. "Added footer with exit code information",
  35. "Added compatibility with EJUDGE (compile with EJUDGE directive)"
  36. };
  37. #include <cstdio>
  38. #include <cctype>
  39. #include <string>
  40. #include <string.h>
  41. #include <stdarg.h>
  42. #include <process.h>
  43. #if ( _WIN32 || __WIN32__ )
  44. #include <windows.h>
  45. #else
  46. #define WORD unsigned short
  47. #endif
  48. #define ABS(f) ((f) < 0 ? -(f) : (f))
  49. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  50. #define MAX(a, b) ((a) > (b) ? (a) : (b))
  51. #define OUTPUT_BUFFER_SIZE (2097152)
  52. #define LF (char)10
  53. #define CR (char)13
  54. #define TAB (char)9
  55. #define SPACE (char)' '
  56. #define EOFCHAR (EOF)
  57. #define EOFREMAP SPACE
  58. #define NUMBERBEFORE LF, CR, SPACE, TAB, EOFCHAR
  59. #define NUMBERAFTER LF, CR, TAB, EOFCHAR
  60. #define LINEAFTER LF, CR, EOFCHAR
  61. #define BLANKS LF, CR, SPACE, TAB
  62. #define EOLNCHAR LF, CR, EOFCHAR
  63. #ifndef EJUDGE
  64. #define OK_EXIT_CODE 0
  65. #define WA_EXIT_CODE 1
  66. #define PE_EXIT_CODE 2
  67. #define FAIL_EXIT_CODE 3
  68. #define DIRT_EXIT_CODE 4
  69. #else
  70. #define OK_EXIT_CODE 0
  71. #define WA_EXIT_CODE 5
  72. #define PE_EXIT_CODE 4
  73. #define FAIL_EXIT_CODE 6
  74. #define DIRT_EXIT_CODE 6
  75. #endif
  76. inline bool isEofChar(char c)
  77. {
  78. return (c == EOFCHAR);
  79. }
  80. inline bool isEofRemap(char c)
  81. {
  82. return (c == EOFREMAP);
  83. }
  84. inline bool isNumberBefore(char c)
  85. {
  86. return (c == LF || c == CR || c == SPACE || c == TAB);
  87. }
  88. inline bool isNumberAfter(char c)
  89. {
  90. return (c == LF || c == CR || c == SPACE || c == TAB || c == EOFCHAR || c == (char)26);
  91. }
  92. inline bool isLineAfter(char c)
  93. {
  94. return (c == LF || c == CR || c == EOFCHAR || c == (char)26);
  95. }
  96. inline bool isBlanks(char c)
  97. {
  98. return (c == LF || c == CR || c == SPACE || c == TAB);
  99. }
  100. inline bool isEolnChar(char c)
  101. {
  102. return (c == LF || c == CR || c == EOFCHAR || c == (char)26);
  103. }
  104. struct TCharSet
  105. {
  106. unsigned int data[64];
  107. void insert(char c)
  108. {
  109. int pc = (int)c;
  110. data[pc >> 3] |= (1 << (pc & 7));
  111. }
  112. bool count(char c)
  113. {
  114. unsigned int pc = (unsigned char)c;
  115. return (data[pc >> 3] & (1 << (pc & 7))) != 0;
  116. }
  117. void clear()
  118. {
  119. memset(data, 0, sizeof(data));
  120. }
  121. TCharSet()
  122. {
  123. clear();
  124. }
  125. TCharSet(char c0)
  126. {
  127. clear();
  128. insert(c0);
  129. }
  130. TCharSet(char c0, char c1)
  131. {
  132. clear();
  133. insert(c0); insert(c1);
  134. }
  135. TCharSet(char c0, char c1, char c2)
  136. {
  137. clear();
  138. insert(c0); insert(c1); insert(c2);
  139. }
  140. TCharSet(char c0, char c1, char c2, char c3)
  141. {
  142. clear();
  143. insert(c0); insert(c1); insert(c2); insert(c3);
  144. }
  145. TCharSet(char c0, char c1, char c2, char c3, char c4)
  146. {
  147. clear();
  148. insert(c0); insert(c1); insert(c2); insert(c3); insert(c4);
  149. }
  150. };
  151. enum TMode
  152. {
  153. _input, _output, _answer
  154. };
  155. enum TResult
  156. {
  157. _ok, _wa, _pe, _fail, _dirt
  158. };
  159. const std::string outcomes[] =
  160. {"accepted", "wrong-answer", "presentation-error", "fail", "fail"};
  161. struct InStream
  162. {
  163. InStream();
  164. std::FILE * file;
  165. std::string name;
  166. TMode mode;
  167. bool opened;
  168. void init(std::string fileName, TMode mode);
  169. char curChar();
  170. void skipChar();
  171. char nextChar();
  172. void reset();
  173. bool eof();
  174. bool seekEof();
  175. bool eoln();
  176. bool seekEoln();
  177. void nextLine();
  178. void skip(TCharSet setof);
  179. std::string readWord(TCharSet before, TCharSet after);
  180. std::string readWord();
  181. int readLongint();
  182. int readInteger();
  183. int readInt();
  184. double readReal();
  185. double readDouble();
  186. std::string readString();
  187. void quit(TResult result, const char * msg);
  188. void quits(TResult result, std::string msg);
  189. void close();
  190. const static WORD LightGray = 0x07;
  191. const static WORD LightRed = 0x0c;
  192. const static WORD LightCyan = 0x0b;
  193. const static WORD LightGreen = 0x0a;
  194. static void textColor(WORD color);
  195. static void quitscr(WORD color, const char * msg);
  196. static void quitscrS(WORD color, std::string msg);
  197. void xmlSafeWrite(std::FILE * file, const char * msg);
  198. };
  199. InStream inf;
  200. InStream ouf;
  201. InStream ans;
  202. bool appesMode;
  203. std::string resultName;
  204. std::string checkerName = "untitled checker";
  205. /* implementation
  206. */
  207. InStream::InStream()
  208. {
  209. file = NULL;
  210. name = "";
  211. mode = _input;
  212. }
  213. int resultExitCode(TResult r)
  214. {
  215. if (r == _ok)
  216. return OK_EXIT_CODE;
  217. if (r == _wa)
  218. return WA_EXIT_CODE;
  219. if (r == _pe)
  220. return PE_EXIT_CODE;
  221. if (r == _fail)
  222. return FAIL_EXIT_CODE;
  223. if (r == _dirt)
  224. return DIRT_EXIT_CODE;
  225. return FAIL_EXIT_CODE;
  226. }
  227. void InStream::textColor(WORD color)
  228. {
  229. #if ( _WIN32 || __WIN32__ )
  230. HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
  231. SetConsoleTextAttribute(handle, color);
  232. #endif
  233. }
  234. void halt(int exitCode)
  235. {
  236. #ifdef FOOTER
  237. InStream::textColor(InStream::LightGray);
  238. std::printf("Checker: \"%s\"\n", checkerName.c_str());
  239. std::printf("Exit code: %d\n", exitCode);
  240. InStream::textColor(InStream::LightGray);
  241. #endif
  242. std::exit(exitCode);
  243. }
  244. void InStream::quit(TResult result, const char * msg)
  245. {
  246. if (mode != _output && result != _fail)
  247. quits(_fail, std::string(msg) + " (" + name + ")");
  248. std::FILE * resultFile;
  249. std::string errorName;
  250. if (result == _ok)
  251. {
  252. if (!ouf.seekEof())
  253. quit(_dirt, "Extra information in the output file");
  254. }
  255. switch (result)
  256. {
  257. case _fail:
  258. errorName = "FAIL ";
  259. quitscrS(LightRed, errorName);
  260. break;
  261. case _dirt:
  262. errorName = "wrong output format ";
  263. quitscrS(LightCyan, errorName);
  264. result = _pe;
  265. break;
  266. case _pe:
  267. errorName = "wrong output format ";
  268. quitscrS(LightRed, errorName);
  269. break;
  270. case _ok:
  271. errorName = "ok ";
  272. quitscrS(LightGreen, errorName);
  273. break;
  274. case _wa:
  275. errorName = "wrong answer ";
  276. quitscrS(LightRed, errorName);
  277. break;
  278. default:
  279. quit(_fail, "What is the code ??? ");
  280. }
  281. if (resultName != "")
  282. {
  283. resultFile = std::fopen(resultName.c_str(), "w");
  284. if (resultFile == NULL)
  285. quit(_fail, "Can not write to Result file");
  286. if (appesMode)
  287. {
  288. fprintf(resultFile, "<?xml version=\"1.0\" encoding=\"windows-1251\"?>");
  289. fprintf(resultFile, "<result outcome = \"%s\">", outcomes[(int)result].c_str());
  290. xmlSafeWrite(resultFile, msg);
  291. fprintf(resultFile, "</result>\n");
  292. }
  293. else
  294. {
  295. /** old-style format
  296. * fprintf(resultFile, ".Testlib Result Number = %d\n", (int)result);
  297. * fprintf(resultFile, ".Result name (optional) = %s\n", errorName.c_str());
  298. * fprintf(resultFile, ".Check Comments = %s\n", msg);
  299. */
  300. fprintf(resultFile, "%s", msg);
  301. }
  302. if (NULL == resultFile || fclose(resultFile) != 0)
  303. quit(_fail, "Can not write to Result file");
  304. }
  305. quitscr(LightGray, msg);
  306. std::printf("\n");
  307. if (inf.file)
  308. fclose(inf.file);
  309. if (ouf.file)
  310. fclose(ouf.file);
  311. if (ans.file)
  312. fclose(ans.file);
  313. textColor(LightGray);
  314. if (resultName != "")
  315. std::printf("See file to check exit message\n");
  316. halt(resultExitCode(result));
  317. }
  318. void InStream::quits(TResult result, std::string msg)
  319. {
  320. InStream::quit(result, msg.c_str());
  321. }
  322. void InStream::xmlSafeWrite(std::FILE * file, const char * msg)
  323. {
  324. size_t lmsg = strlen(msg);
  325. for (size_t i = 0; i < lmsg; i++)
  326. {
  327. if (msg[i] == '&')
  328. {
  329. fprintf(file, "%s", "&amp;");
  330. continue;
  331. }
  332. if (msg[i] == '<')
  333. {
  334. fprintf(file, "%s", "&lt;");
  335. continue;
  336. }
  337. if (msg[i] == '>')
  338. {
  339. fprintf(file, "%s", "&gt;");
  340. continue;
  341. }
  342. if (msg[i] == '"')
  343. {
  344. fprintf(file, "%s", "&quot;");
  345. continue;
  346. }
  347. if (0 <= msg[i] && msg[i] <= 31)
  348. {
  349. fprintf(file, "%c", '.');
  350. continue;
  351. }
  352. fprintf(file, "%c", msg[i]);
  353. }
  354. }
  355. void InStream::quitscrS(WORD color, std::string msg)
  356. {
  357. quitscr(color, msg.c_str());
  358. }
  359. void InStream::quitscr(WORD color, const char * msg)
  360. {
  361. if (resultName == "")
  362. {
  363. textColor(color);
  364. std::printf("%s", msg);
  365. textColor(LightGray);
  366. }
  367. }
  368. void InStream::reset()
  369. {
  370. if (opened)
  371. close();
  372. if (NULL == (file = std::fopen(name.c_str(), "r")))
  373. {
  374. if (mode == _output)
  375. quits(_pe, std::string("File not found: \"") + name + "\"");
  376. }
  377. opened = true;
  378. }
  379. void InStream::init(std::string fileName, TMode mode)
  380. {
  381. opened = false;
  382. name = fileName;
  383. this->mode = mode;
  384. reset();
  385. }
  386. char InStream::curChar()
  387. {
  388. char c = (char)getc(file);
  389. ungetc(c, file);
  390. return c;
  391. }
  392. char InStream::nextChar()
  393. {
  394. return (char)getc(file);
  395. }
  396. void InStream::skipChar()
  397. {
  398. getc(file);
  399. }
  400. std::string InStream::readWord(TCharSet before, TCharSet after)
  401. {
  402. char cur;
  403. while (before.count(cur = (char)getc(file)) == 1);
  404. if (cur == EOFCHAR && !after.count(cur))
  405. {
  406. quit(_pe, "Unexpected end of file");
  407. }
  408. std::string result = "";
  409. while (!(after.count(cur) || cur == EOFCHAR))
  410. {
  411. result += cur;
  412. cur = (char)getc(file);
  413. }
  414. ungetc(cur, file);
  415. return result;
  416. }
  417. std::string InStream::readWord()
  418. {
  419. return readWord(TCharSet(BLANKS), TCharSet(BLANKS));
  420. }
  421. int InStream::readInteger()
  422. {
  423. char cur;
  424. while (isNumberBefore(cur = (char)getc(file)));
  425. if (cur == EOFCHAR)
  426. quit(_pe, "Unexpected end of file - integer expected");
  427. ungetc(cur, file);
  428. int retval;
  429. if (fscanf(file, "%d", &retval) != 1)
  430. // todo: show insted-of
  431. quit(_pe, "Expected integer");
  432. return retval;
  433. }
  434. int InStream::readLongint()
  435. {
  436. return readInteger();
  437. }
  438. int InStream::readInt()
  439. {
  440. return readInteger();
  441. }
  442. double InStream::readReal()
  443. {
  444. if (seekEof())
  445. quit(_pe, "Unexpected end of file - double expected");
  446. double retval;
  447. if (fscanf(file, "%lf", &retval) != 1)
  448. // todo: show insted-of
  449. quit(_pe, "Expected double");
  450. return retval;
  451. }
  452. double InStream::readDouble()
  453. {
  454. return readReal();
  455. }
  456. void InStream::skip(TCharSet setof)
  457. {
  458. char cur;
  459. while (setof.count(cur = (char)getc(file)) == 1);
  460. ungetc(cur, file);
  461. }
  462. bool InStream::eof()
  463. {
  464. return (NULL == file || feof(file) != 0);
  465. }
  466. bool InStream::seekEof()
  467. {
  468. if (NULL == file)
  469. return true;
  470. char cur;
  471. while (isBlanks(cur = (char)getc(file)));
  472. ungetc(cur, file);
  473. return (NULL == file || feof(file) != 0 || cur == EOF);
  474. }
  475. bool InStream::eoln()
  476. {
  477. if (NULL == file)
  478. return true;
  479. char c = curChar();
  480. return isEolnChar(c);
  481. }
  482. bool InStream::seekEoln()
  483. {
  484. if (NULL == file)
  485. return true;
  486. char cur;
  487. do
  488. {
  489. cur = (char)getc(file);
  490. }
  491. while (cur == SPACE || cur == TAB);
  492. ungetc(cur, file);
  493. return isEolnChar(cur);
  494. }
  495. void InStream::nextLine()
  496. {
  497. if (NULL == file)
  498. return;
  499. char cur;
  500. while (!isEolnChar(cur = (char)getc(file)));
  501. if (cur == CR)
  502. {
  503. cur = (char)getc(file);
  504. if (cur != LF)
  505. ungetc(cur, file);
  506. }
  507. else
  508. {
  509. if (cur != LF)
  510. ungetc(cur, file);
  511. }
  512. }
  513. std::string InStream::readString()
  514. {
  515. if (NULL == file)
  516. quit(_pe, "Expected line");
  517. std::string retval = "";
  518. char cur;
  519. while (!isEolnChar(cur = (char)getc(file)))
  520. retval += cur;
  521. if (cur == CR)
  522. {
  523. cur = (char)getc(file);
  524. if (cur != LF)
  525. ungetc(cur, file);
  526. }
  527. else
  528. {
  529. if (cur != LF)
  530. ungetc(cur, file);
  531. }
  532. return retval;
  533. }
  534. void InStream::close()
  535. {
  536. if (opened)
  537. fclose(file);
  538. opened = false;
  539. }
  540. void quit(TResult result, std::string msg)
  541. {
  542. ouf.quit(result, msg.c_str());
  543. }
  544. void quit(TResult result, char * msg)
  545. {
  546. ouf.quit(result, msg);
  547. }
  548. void quitf(TResult result, const char * format, ...)
  549. {
  550. char * buffer = new char [OUTPUT_BUFFER_SIZE];
  551. va_list ap;
  552. va_start(ap, format);
  553. std::vsprintf(buffer, format, ap);
  554. va_end(ap);
  555. std::string output(buffer);
  556. delete[] buffer;
  557. quit(result, output);
  558. }
  559. void registerTestlibCmd(int argc, char * argv[])
  560. {
  561. if (argc > 1 && !strcmp("--help", argv[1]))
  562. {
  563. InStream::textColor(InStream::LightCyan);
  564. std::printf("TESTLIB %s ", VERSION);
  565. std::printf("by Mike Mirzayanov, copyright(c) 2005-2006\n");
  566. std::printf("Checker name: \"%s\"\n", checkerName.c_str());
  567. InStream::textColor(InStream::LightGray);
  568. std::printf("\n");
  569. std::printf("Latest features: \n");
  570. for (int i = 0; i < sizeof(latestFeatures) / sizeof(char*); i++)
  571. {
  572. std::printf("*) %s\n", latestFeatures[i]);
  573. }
  574. std::printf("\n");
  575. std::printf("Program must be run with the following arguments: \n");
  576. std::printf(" <input-file> <output-file> <answer-file> [<report-file> [<-appes>]]\n\n");
  577. std::exit(0);
  578. }
  579. if (sizeof(int) != 4)
  580. quit(_fail, "'testlib' unit assumes 'sizeof(integer) = 4'");
  581. if (argc < 4 || argc > 6)
  582. {
  583. quit(_fail, std::string("Program must be run with the following arguments: ") +
  584. std::string("<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]") +
  585. "\nUse \"--help\" to get help information");
  586. }
  587. if (argc == 4)
  588. {
  589. resultName = "";
  590. appesMode = false;
  591. }
  592. if (argc == 5)
  593. {
  594. resultName = argv[4];
  595. appesMode = false;
  596. }
  597. if (argc == 6)
  598. {
  599. if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5]))
  600. {
  601. quit(_fail, std::string("Program must be run with the following arguments: ") +
  602. "<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");
  603. }
  604. else
  605. {
  606. resultName = argv[4];
  607. appesMode = true;
  608. }
  609. }
  610. inf.init(argv[1], _input);
  611. ouf.init(argv[2], _output);
  612. ans.init(argv[3], _answer);
  613. }
  614. void registerTestlib(int argc, ...)
  615. {
  616. if (argc < 3 || argc > 5)
  617. quit(_fail, std::string("Program must be run with the following arguments: ") +
  618. "<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");
  619. char ** argv = new char*[argc + 1];
  620. va_list ap;
  621. va_start(ap, argc);
  622. argv[0] = NULL;
  623. for (int i = 0; i < argc; i++)
  624. {
  625. argv[i + 1] = va_arg(ap, char *);
  626. }
  627. va_end(ap);
  628. registerTestlibCmd(argc + 1, argv);
  629. delete[] argv;
  630. }
  631. inline bool isNaN(double r)
  632. {
  633. return ((r != r) == true) && ((r == r) == false) && ((1.0 > r) == false) && ((1.0 < r) == false);
  634. }
  635. inline bool isInfinite(double r)
  636. {
  637. return (r > 1E100 || r < -1E100);
  638. }
  639. bool doubleCompare(double expected, double result, double MAX_DOUBLE_ERROR)
  640. {
  641. if(isNaN(expected))
  642. {
  643. return isNaN(result);
  644. }
  645. else
  646. if(isInfinite(expected))
  647. {
  648. if(expected > 0)
  649. {
  650. return result > 0 && isInfinite(result);
  651. }
  652. else
  653. {
  654. return result < 0 && isInfinite(result);
  655. }
  656. }
  657. else
  658. if(isNaN(result) || isInfinite(result))
  659. {
  660. return false;
  661. }
  662. else
  663. if(ABS(result - expected) < MAX_DOUBLE_ERROR)
  664. {
  665. return true;
  666. }
  667. else
  668. {
  669. double minv = MIN(expected * (1.0 - MAX_DOUBLE_ERROR),
  670. expected * (1.0 + MAX_DOUBLE_ERROR));
  671. double maxv = MAX(expected * (1.0 - MAX_DOUBLE_ERROR),
  672. expected * (1.0 + MAX_DOUBLE_ERROR));
  673. return result > minv && result < maxv;
  674. }
  675. }
  676. void ensure(bool cond, const std::string msg = "")
  677. {
  678. if (!cond)
  679. {
  680. quitf(_fail, msg.c_str());
  681. }
  682. }
  683. void setName(const char* format, ...)
  684. {
  685. char * buffer = new char [OUTPUT_BUFFER_SIZE];
  686. va_list ap;
  687. va_start(ap, format);
  688. std::vsprintf(buffer, format, ap);
  689. va_end(ap);
  690. std::string name(buffer);
  691. delete[] buffer;
  692. checkerName = name;
  693. }
  694. #endif