unittests.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifdef _USE_CPPUNIT
  14. #include "unittests.hpp"
  15. #include "jstats.h"
  16. #include "jregexp.hpp"
  17. #include "jfile.hpp"
  18. #include "deftype.hpp"
  19. #include "rmtfile.hpp"
  20. #include "thorhelper.hpp"
  21. #include "libbase58.h"
  22. /*
  23. * This is the main unittest driver for HPCC. From here,
  24. * all unit tests, be they internal or external (API),
  25. * will run.
  26. *
  27. * All internal unit tests, written on the same source
  28. * files as the implementation they're testing, can be
  29. * dynamically linked via the helper class below.
  30. *
  31. * All external unit tests (API tests, test-driven
  32. * development, interface documentation and general
  33. * usability tests) should be implemented as source
  34. * files within the same directory as this file, and
  35. * statically linked together.
  36. *
  37. * CPPUnit will automatically recognise and run them all.
  38. */
  39. void usage()
  40. {
  41. printf("\n"
  42. "Usage:\n"
  43. " unittests <options> <testnames>\n"
  44. "\n"
  45. "Options:\n"
  46. " -a --all Include all tests, including timing and stress tests\n"
  47. " -d --load path Dynamically load a library/all libraries in a directory.\n"
  48. " By default, the HPCCSystems lib directory is loaded.\n"
  49. " -e --exact Match subsequent test names exactly\n"
  50. " -h --help Display this help text\n"
  51. " -l --list List matching tests but do not execute them\n"
  52. " -x --exclude Exclude subsequent test names\n"
  53. "\n");
  54. }
  55. bool matchName(const char *name, const StringArray &patterns)
  56. {
  57. ForEachItemIn(idx, patterns)
  58. {
  59. bool match;
  60. const char *pattern = patterns.item(idx);
  61. if (strchr(pattern, '*'))
  62. {
  63. match = WildMatch(name, pattern, true);
  64. }
  65. else
  66. match = streq(name, pattern);
  67. if (match)
  68. return true;
  69. }
  70. return false;
  71. }
  72. LoadedObject *loadDll(const char *thisDll)
  73. {
  74. try
  75. {
  76. DBGLOG("Loading %s", thisDll);
  77. return new LoadedObject(thisDll);
  78. }
  79. catch (IException *E)
  80. {
  81. E->Release();
  82. }
  83. catch (...)
  84. {
  85. }
  86. return NULL;
  87. }
  88. void loadDlls(IArray &objects, const char * libDirectory)
  89. {
  90. const char * mask = "*" SharedObjectExtension;
  91. Owned<IFile> libDir = createIFile(libDirectory);
  92. Owned<IDirectoryIterator> libFiles = libDir->directoryFiles(mask,false,false);
  93. ForEach(*libFiles)
  94. {
  95. const char *thisDll = libFiles->query().queryFilename();
  96. if (!strstr(thisDll, "javaembed")) // Bit of a hack, but loading this if java not present terminates...
  97. if (!strstr(thisDll, "py2embed")) // These two clash, so ...
  98. if (!strstr(thisDll, "py3embed")) // ... best to load neither...
  99. {
  100. LoadedObject *loaded = loadDll(thisDll);
  101. if (loaded)
  102. objects.append(*loaded);
  103. }
  104. }
  105. }
  106. static constexpr const char * defaultYaml = R"!!(
  107. version: "1.0"
  108. unittests:
  109. name: unittests
  110. )!!";
  111. int main(int argc, const char *argv[])
  112. {
  113. InitModuleObjects();
  114. StringArray includeNames;
  115. StringArray excludeNames;
  116. StringArray loadLocations;
  117. bool wildMatch = true;
  118. bool exclude = false;
  119. bool includeAll = false;
  120. bool verbose = false;
  121. bool list = false;
  122. bool useDefaultLocations = true;
  123. //NB: not actually used for now, but required initialization for anything that may call getGlobalConfig*() or getComponentConfig*()
  124. Owned<IPropertyTree> globals = loadConfiguration(defaultYaml, argv, "unittests", nullptr, nullptr, nullptr, nullptr, false);
  125. for (int argNo = 1; argNo < argc; argNo++)
  126. {
  127. const char *arg = argv[argNo];
  128. if (arg[0]=='-')
  129. {
  130. if (streq(arg, "-x") || streq(arg, "--exclude"))
  131. exclude = true;
  132. else if (streq(arg, "-v") || streq(arg, "--verbose"))
  133. verbose = true;
  134. else if (streq(arg, "-e") || streq(arg, "--exact"))
  135. wildMatch = false;
  136. else if (streq(arg, "-a") || streq(arg, "--all"))
  137. includeAll = true;
  138. else if (streq(arg, "-l") || streq(arg, "--list"))
  139. list = true;
  140. else if (streq(arg, "-d") || streq(arg, "--load"))
  141. {
  142. useDefaultLocations = false;
  143. argNo++;
  144. if (argNo<argc)
  145. loadLocations.append(argv[argNo]);
  146. }
  147. else
  148. {
  149. usage();
  150. exit(streq(arg, "-h") || streq(arg, "--help")?0:4);
  151. }
  152. }
  153. else
  154. {
  155. VStringBuffer pattern("*%s*", arg);
  156. if (wildMatch && !strchr(arg, '*'))
  157. arg = pattern.str();
  158. if (exclude)
  159. excludeNames.append(arg);
  160. else
  161. includeNames.append(arg);
  162. }
  163. }
  164. if (verbose)
  165. queryStderrLogMsgHandler()->setMessageFields(MSGFIELD_time|MSGFIELD_microTime|MSGFIELD_milliTime|MSGFIELD_thread);
  166. else
  167. removeLog();
  168. if (!includeAll && includeNames.empty())
  169. {
  170. excludeNames.append("*stress*");
  171. excludeNames.append("*timing*");
  172. excludeNames.append("*slow*");
  173. }
  174. if (!includeNames.length())
  175. includeNames.append("*");
  176. if (useDefaultLocations)
  177. {
  178. StringBuffer binDir;
  179. makeAbsolutePath(argv[0], binDir, true);
  180. // Default library location depends on the executable location...
  181. StringBuffer dir;
  182. splitFilename(binDir.str(), &dir, &dir, NULL, NULL);
  183. dir.replaceString(PATHSEPSTR "bin" PATHSEPSTR, PATHSEPSTR "lib" PATHSEPSTR);
  184. if (verbose)
  185. DBGLOG("Adding default library location %s", dir.str());
  186. loadLocations.append(dir);
  187. #ifdef _DEBUG
  188. dir.replaceString(PATHSEPSTR "lib" PATHSEPSTR, PATHSEPSTR "libs" PATHSEPSTR);
  189. loadLocations.append(dir);
  190. if (verbose)
  191. DBGLOG("Adding default library location %s", dir.str());
  192. #endif
  193. }
  194. IArray objects;
  195. ForEachItemIn(idx, loadLocations)
  196. {
  197. const char *location = loadLocations.item(idx);
  198. Owned<IFile> file = createIFile(location);
  199. switch (file->isDirectory())
  200. {
  201. case fileBool::notFound:
  202. if (verbose && !useDefaultLocations)
  203. DBGLOG("Specified library location %s not found", location);
  204. break;
  205. case fileBool::foundYes:
  206. loadDlls(objects, location);
  207. break;
  208. case fileBool::foundNo:
  209. LoadedObject *loaded = loadDll(location);
  210. if (loaded)
  211. objects.append(*loaded);
  212. break;
  213. }
  214. }
  215. bool wasSuccessful = false;
  216. {
  217. // New scope as we need the TestRunner to be destroyed before unloading the dlls...
  218. CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  219. CppUnit::TextUi::TestRunner runner;
  220. CppUnit::Test *all = registry.makeTest();
  221. int numTests = all->getChildTestCount();
  222. for (int i = 0; i < numTests; i++)
  223. {
  224. CppUnit::Test *sub = all->getChildTestAt(i);
  225. std::string name = sub->getName();
  226. if (matchName(name.c_str(), includeNames))
  227. {
  228. if (matchName(name.c_str(), excludeNames))
  229. {
  230. if (verbose)
  231. DBGLOG("Excluding test %s", name.c_str());
  232. }
  233. else if (list)
  234. printf("%s\n", name.c_str());
  235. else
  236. {
  237. if (verbose)
  238. DBGLOG("Including test %s", name.c_str());
  239. runner.addTest(sub);
  240. }
  241. }
  242. }
  243. wasSuccessful = list || runner.run( "", false );
  244. }
  245. releaseAtoms();
  246. ClearTypeCache(); // Clear this cache before the file hooks are unloaded
  247. removeFileHooks();
  248. objects.kill();
  249. ExitModuleObjects();
  250. return wasSuccessful ? 0 : 1; // 0 == exit code success
  251. }
  252. //MORE: This can't be included in jlib because of the dll dependency
  253. class InternalStatisticsTest : public CppUnit::TestFixture
  254. {
  255. CPPUNIT_TEST_SUITE( InternalStatisticsTest );
  256. CPPUNIT_TEST(testMappings);
  257. CPPUNIT_TEST_SUITE_END();
  258. void testMappings()
  259. {
  260. try
  261. {
  262. verifyStatisticFunctions();
  263. }
  264. catch (IException * e)
  265. {
  266. StringBuffer msg;
  267. fprintf(stderr, "Failure: %s", e->errorMessage(msg).str());
  268. e->Release();
  269. ASSERT(false);
  270. }
  271. }
  272. };
  273. CPPUNIT_TEST_SUITE_REGISTRATION( InternalStatisticsTest );
  274. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( InternalStatisticsTest, "StatisticsTest" );
  275. class PtreeThreadingStressTest : public CppUnit::TestFixture
  276. {
  277. CPPUNIT_TEST_SUITE( PtreeThreadingStressTest );
  278. CPPUNIT_TEST(testContention);
  279. CPPUNIT_TEST_SUITE_END();
  280. void testContention()
  281. {
  282. _testContention(ipt_lowmem);
  283. _testContention(ipt_fast);
  284. }
  285. void _testContention(byte flags)
  286. {
  287. enum ContentionMode { max_contention, some_contention, min_contention, some_control, min_control };
  288. class casyncfor: public CAsyncFor
  289. {
  290. volatile int v = 0;
  291. void donothing()
  292. {
  293. v++;
  294. }
  295. byte flags = ipt_none;
  296. ContentionMode mode = max_contention;
  297. int iterations = 0;
  298. const char *desc = nullptr;
  299. public:
  300. casyncfor(const char *_desc, byte _flags, ContentionMode _mode, int _iter)
  301. : flags(_flags), mode(_mode), iterations(_iter), desc(_desc)
  302. {
  303. };
  304. double For(unsigned num, unsigned maxatonce, double overhead = 0.0)
  305. {
  306. unsigned start = msTick();
  307. CAsyncFor::For(num, maxatonce);
  308. unsigned elapsed = msTick()-start;
  309. double looptime = (elapsed * 1.0) / (iterations*num);
  310. if (mode < 3)
  311. DBGLOG("%s (%s) test completed in %u ms (%f ms/iter)", desc, flags & ipt_fast ? "fast" : "lowmem", elapsed, looptime-overhead);
  312. return looptime;
  313. }
  314. void Do(unsigned i)
  315. {
  316. for (unsigned i = 0; i < iterations; i++)
  317. {
  318. Owned<IPropertyTree> p = mode >= some_control ? nullptr : createPTreeFromXMLString(
  319. "<W_LOCAL buildVersion='community_6.0.0-trunk0Debug[heads/cass-wu-part3-0-g10b954-dirty]'"
  320. " cloneable='1'"
  321. " clusterName=''"
  322. " codeVersion='158'"
  323. " eclVersion='6.0.0'"
  324. " hash='2796091347'"
  325. " state='completed'"
  326. " xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>"
  327. " <Debug>"
  328. " <debugquery>1</debugquery>"
  329. " <expandpersistinputdependencies>1</expandpersistinputdependencies>"
  330. " <savecpptempfiles>1</savecpptempfiles>"
  331. " <saveecltempfiles>1</saveecltempfiles>"
  332. " <spanmultiplecpp>0</spanmultiplecpp>"
  333. " <standaloneexe>1</standaloneexe>"
  334. " <targetclustertype>hthor</targetclustertype>"
  335. " </Debug>"
  336. " <FilesRead>"
  337. " <File name='myfile' useCount='2' cluster = 'mycluster'/>"
  338. " <File name='mysuperfile' useCount='2' cluster = 'mycluster'>"
  339. " <Subfile name='myfile'/>"
  340. " </File>"
  341. "</FilesRead>"
  342. " <Graphs>"
  343. " <Graph name='graph1' type='activities'>"
  344. " <xgmml>"
  345. " <graph wfid='2'>"
  346. " <node id='1'>"
  347. " <att>"
  348. " <graph>"
  349. " <att name='rootGraph' value='1'/>"
  350. " <edge id='2_0' source='2' target='3'/>"
  351. " <edge id='3_0' source='3' target='4'/>"
  352. " <edge id='4_0' source='4' target='5'/>"
  353. " <node id='2' label='Inline Row&#10;{1}'>"
  354. " <att name='definition' value='./sets.ecl(2,13)'/>"
  355. " <att name='_kind' value='148'/>"
  356. " <att name='ecl' value='ROW(TRANSFORM({ integer8 v },SELF.v := 1;));&#10;'/>"
  357. " <att name='recordSize' value='8'/>"
  358. " <att name='predictedCount' value='1'/>"
  359. " </node>"
  360. " <node id='3' label='Filter'>"
  361. " <att name='definition' value='./sets.ecl(3,15)'/>"
  362. " <att name='_kind' value='5'/>"
  363. " <att name='ecl' value='FILTER(v = STORED(&apos;one&apos;));&#10;'/>"
  364. " <att name='recordSize' value='8'/>"
  365. " <att name='predictedCount' value='0..?[disk]'/>"
  366. " </node>"
  367. " <node id='4' label='Count'>"
  368. " <att name='_kind' value='125'/>"
  369. " <att name='ecl' value='TABLE({ integer8 value := COUNT(group) });&#10;'/>"
  370. " <att name='recordSize' value='8'/>"
  371. " <att name='predictedCount' value='1'/>"
  372. " </node>"
  373. " <node id='5' label='Store&#10;Internal(&apos;wf2&apos;)'>"
  374. " <att name='_kind' value='22'/>"
  375. " <att name='ecl' value='extractresult(value, named(&apos;wf2&apos;));&#10;'/>"
  376. " <att name='recordSize' value='8'/>"
  377. " </node>"
  378. " </graph>"
  379. " </att>"
  380. " </node>"
  381. " </graph>"
  382. " </xgmml>"
  383. " </Graph>"
  384. " <Graph name='graph2' type='activities'>"
  385. " <xgmml>"
  386. " <graph wfid='3'>"
  387. " <node id='6'>"
  388. " <att>"
  389. " <graph>"
  390. " <att name='rootGraph' value='1'/>"
  391. " <edge id='7_0' source='7' target='8'/>"
  392. " <edge id='8_0' source='8' target='9'/>"
  393. " <node id='7' label='Inline Row&#10;{1}'>"
  394. " <att name='definition' value='./sets.ecl(2,13)'/>"
  395. " <att name='_kind' value='148'/>"
  396. " <att name='ecl' value='ROW(TRANSFORM({ integer8 v },SELF.v := 1;));&#10;'/>"
  397. " <att name='recordSize' value='8'/>"
  398. " <att name='predictedCount' value='1'/>"
  399. " </node>"
  400. " <node id='8' label='Filter'>"
  401. " <att name='definition' value='./sets.ecl(5,1)'/>"
  402. " <att name='_kind' value='5'/>"
  403. " <att name='ecl' value='FILTER(v = INTERNAL(&apos;wf2&apos;));&#10;'/>"
  404. " <att name='recordSize' value='8'/>"
  405. " <att name='predictedCount' value='0..?[disk]'/>"
  406. " </node>"
  407. " <node id='9' label='Output&#10;Result #1'>"
  408. " <att name='definition' value='./sets.ecl(1,1)'/>"
  409. " <att name='name' value='sets'/>"
  410. " <att name='definition' value='./sets.ecl(5,1)'/>"
  411. " <att name='_kind' value='16'/>"
  412. " <att name='ecl' value='OUTPUT(..., workunit);&#10;'/>"
  413. " <att name='recordSize' value='8'/>"
  414. " </node>"
  415. " </graph>"
  416. " </att>"
  417. " </node>"
  418. " </graph>"
  419. " </xgmml>"
  420. " </Graph>"
  421. " </Graphs>"
  422. " <Query fetchEntire='1'>"
  423. " <Associated>"
  424. " <File desc='a.out.cpp'"
  425. " filename='/Users/rchapman/HPCC-Platform/ossd/a.out.cpp'"
  426. " ip='192.168.2.203'"
  427. " type='cpp'/>"
  428. " </Associated>"
  429. " </Query>"
  430. " <Results>"
  431. " <Result isScalar='0'"
  432. " name='Result 1'"
  433. " recordSizeEntry='mf1'"
  434. " rowLimit='-1'"
  435. " sequence='0'"
  436. " status='calculated'>"
  437. " <rowCount>1</rowCount>"
  438. " <SchemaRaw xsi:type='SOAP-ENC:base64'>"
  439. " dgABCAEAGBAAAAB7IGludGVnZXI4IHYgfTsK </SchemaRaw>"
  440. " <totalRowCount>1</totalRowCount>"
  441. " <Value xsi:type='SOAP-ENC:base64'>"
  442. " AQAAAAAAAAA= </Value>"
  443. " </Result>"
  444. " </Results>"
  445. " <State>completed</State>"
  446. " <Statistics>"
  447. " <Statistic c='eclcc'"
  448. " count='1'"
  449. " creator='eclcc'"
  450. " kind='TimeElapsed'"
  451. " s='compile'"
  452. " scope='compile:parseTime'"
  453. " ts='1431603789722535'"
  454. " unit='ns'"
  455. " value='805622'/>"
  456. " <Statistic c='unknown'"
  457. " count='1'"
  458. " creator='unknownRichards-iMac.local'"
  459. " kind='WhenQueryStarted'"
  460. " s='global'"
  461. " scope='workunit'"
  462. " ts='1431603790007020'"
  463. " unit='ts'"
  464. " value='1431603790007001'/>"
  465. " <Statistic c='unknown'"
  466. " count='1'"
  467. " creator='unknownRichards-iMac.local'"
  468. " desc='Graph graph1'"
  469. " kind='TimeElapsed'"
  470. " s='graph'"
  471. " scope='graph1'"
  472. " ts='1431603790007912'"
  473. " unit='ns'"
  474. " value='0'/>"
  475. " </Statistics>"
  476. " <Temporaries>"
  477. " <Variable name='wf2' status='calculated'>"
  478. " <rowCount>1</rowCount>"
  479. " <totalRowCount>1</totalRowCount>"
  480. " <Value xsi:type='SOAP-ENC:base64'>"
  481. " AQAAAAAAAAA= </Value>"
  482. " </Variable>"
  483. " </Temporaries>"
  484. " <Tracing>"
  485. " <EclAgentBuild>community_6.0.0-trunk0Debug[heads/cass-wu-part3-0-g10b954-dirty]</EclAgentBuild>"
  486. " </Tracing>"
  487. " <Variables>"
  488. " <Variable name='one' sequence='-1' status='calculated'>"
  489. " <rowCount>1</rowCount>"
  490. " <SchemaRaw xsi:type='SOAP-ENC:base64'>"
  491. " b25lAAEIAQAYAAAAAA== </SchemaRaw>"
  492. " <totalRowCount>1</totalRowCount>"
  493. " <Value xsi:type='SOAP-ENC:base64'>"
  494. " AQAAAAAAAAA= </Value>"
  495. " </Variable>"
  496. " </Variables>"
  497. " <Workflow>"
  498. " <Item mode='normal'"
  499. " state='done'"
  500. " type='normal'"
  501. " wfid='1'/>"
  502. " <Item mode='normal'"
  503. " state='done'"
  504. " type='normal'"
  505. " wfid='2'>"
  506. " <Dependency wfid='1'/>"
  507. " </Item>"
  508. " <Item mode='normal'"
  509. " state='done'"
  510. " type='normal'"
  511. " wfid='3'>"
  512. " <Dependency wfid='2'/>"
  513. " <Schedule/>"
  514. " </Item>"
  515. " </Workflow>"
  516. "</W_LOCAL>"
  517. , flags);
  518. switch(mode)
  519. {
  520. case some_contention: case some_control: for (int j = 0; j < 100000; j++) donothing(); break;
  521. case min_contention: case min_control: for (int j = 0; j < 1000000; j++) donothing(); break;
  522. }
  523. }
  524. }
  525. } max("maxContention",flags,max_contention,1000),
  526. some("someContention",flags,some_contention,200),
  527. min("minContention",flags,min_contention,200),
  528. csome("control some",flags,some_control,200),
  529. cmin("control min",flags,min_control,200),
  530. seq("single",flags,max_contention,1000);
  531. max.For(8,8);
  532. some.For(8,8,csome.For(8,8));
  533. min.For(8,8,cmin.For(8,8));
  534. seq.For(8,1);
  535. }
  536. };
  537. CPPUNIT_TEST_SUITE_REGISTRATION( PtreeThreadingStressTest );
  538. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( PtreeThreadingStressTest, "PtreeThreadingStressTest" );
  539. //MORE: This can't be included in jlib because of the dll dependency
  540. class StringBufferTest : public CppUnit::TestFixture
  541. {
  542. CPPUNIT_TEST_SUITE( StringBufferTest );
  543. CPPUNIT_TEST(testReplace);
  544. CPPUNIT_TEST_SUITE_END();
  545. void testReplace()
  546. {
  547. StringBuffer r ("1 bb c");
  548. r.replaceString(" ", "x");
  549. ASSERT(streq(r, "1xbbxc"));
  550. }
  551. };
  552. CPPUNIT_TEST_SUITE_REGISTRATION( StringBufferTest );
  553. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StringBufferTest, "StringBufferTest" );
  554. StringBuffer &mbToBase58(StringBuffer &s, const MemoryBuffer &data)
  555. {
  556. size_t b58Length = data.length() * 2 + 1;
  557. ASSERT(b58enc(s.clear().reserve(b58Length), &b58Length, data.toByteArray(), data.length()));
  558. s.setLength(b58Length);
  559. return s;
  560. }
  561. StringBuffer &base64ToBase58(StringBuffer &s, const char *b64)
  562. {
  563. MemoryBuffer mb;
  564. JBASE64_Decode(b64, mb);
  565. return mbToBase58(s, mb);
  566. }
  567. StringBuffer &textToBase58(StringBuffer &s, const char *text)
  568. {
  569. MemoryBuffer mb;
  570. mb.append((size_t)strlen(text), text);
  571. return mbToBase58(s, mb);
  572. }
  573. MemoryBuffer &base58ToMb(MemoryBuffer &data, const char *b58)
  574. {
  575. size_t len = strlen(b58);
  576. size_t offset = len;
  577. b58tobin(data.clear().reserveTruncate(len), &len, b58, 0);
  578. offset -= len;
  579. if (offset) //if we ever start using b58tobin we should fix this weird behavior
  580. {
  581. MemoryBuffer weird;
  582. weird.append(len, data.toByteArray()+offset);
  583. data.swapWith(weird);
  584. }
  585. return data;
  586. }
  587. StringBuffer &base58ToBase64(StringBuffer &s, const char *b58)
  588. {
  589. MemoryBuffer mb;
  590. base58ToMb(mb, b58);
  591. JBASE64_Encode(mb.toByteArray(), mb.length(), s.clear(),true);
  592. return s;
  593. }
  594. StringBuffer &base58ToText(StringBuffer &s, const char *b58)
  595. {
  596. MemoryBuffer mb;
  597. base58ToMb(mb, b58);
  598. return s.clear().append(mb.length(), mb.toByteArray());
  599. }
  600. class Base58Test : public CppUnit::TestFixture
  601. {
  602. CPPUNIT_TEST_SUITE( Base58Test );
  603. CPPUNIT_TEST(testEncodeDecode);
  604. CPPUNIT_TEST_SUITE_END();
  605. void doTestEncodeDecodeText(const char *text, const char *b58)
  606. {
  607. StringBuffer s;
  608. ASSERT(streq(textToBase58(s, text), b58));
  609. ASSERT(streq(base58ToText(s, b58), text));
  610. }
  611. void doTestEncodeDecodeBase64(const char *b64, const char *b58)
  612. {
  613. StringBuffer s;
  614. ASSERT(streq(base64ToBase58(s, b64), b58));
  615. ASSERT(streq(base58ToBase64(s, b58), b64));
  616. }
  617. void testEncodeDecode()
  618. {
  619. StringBuffer s;
  620. //short string
  621. doTestEncodeDecodeText("1", "r");
  622. //text string
  623. doTestEncodeDecodeText("Fifty-eight is the sum of the first seven prime numbers.", "2ubdTkzo5vaWL4FKQGro88zp8v6Q5EftVBq2fbZsWCDRzQxGDb1heKFsMReJNhsRsK6TfvrgqVeRB");
  624. //hex 005A1FC5DD9E6F03819FCA94A2D89669469667F9A074655946
  625. doTestEncodeDecodeBase64("AFofxd2ebwOBn8qUotiWaUaWZ/mgdGVZRg==", "19DXstMaV43WpYg4ceREiiTv2UntmoiA9j");
  626. //hex FEEFAEF022FA
  627. doTestEncodeDecodeBase64("/u+u8CL6", "3Bx9Y4pUR");
  628. //hex FFFEEFAEF022FA
  629. doTestEncodeDecodeBase64("//7vrvAi+g==", "AhfV5sjWb3");
  630. //This input causes the loop iteration counter to go negative
  631. //hex 00CEF022FA
  632. doTestEncodeDecodeBase64("AM7wIvo=", "16Ho7Hs");
  633. //empty input
  634. MemoryBuffer mb;
  635. ASSERT(streq(mbToBase58(s, mb), ""));
  636. }
  637. };
  638. CPPUNIT_TEST_SUITE_REGISTRATION( Base58Test );
  639. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( Base58Test, "Base58Test" );
  640. thread_local unsigned temp = 0; // Avoids clever compilers optimizing everything away
  641. static unsigned skip(unsigned j)
  642. {
  643. temp += j;
  644. return j+1;
  645. }
  646. static unsigned call_from_thread(unsigned count)
  647. {
  648. unsigned tot = count;
  649. for (int j = 0; j < count; j++)
  650. tot += skip(j);
  651. return tot;
  652. }
  653. class ThreadedPersistStressTest : public CppUnit::TestFixture
  654. {
  655. CPPUNIT_TEST_SUITE( ThreadedPersistStressTest );
  656. CPPUNIT_TEST(testThreads);
  657. CPPUNIT_TEST_SUITE_END();
  658. void testThreads()
  659. {
  660. testThreadsX(0);
  661. testThreadsX(1);
  662. testThreadsX(2);
  663. testThreadsX(3);
  664. testThreadsX(4);
  665. }
  666. void testThreadsX(unsigned mode)
  667. {
  668. unsigned iters = 10000;
  669. testThreadsXX(mode, 10, iters);
  670. testThreadsXX(mode, 1000, iters);
  671. testThreadsXX(mode, 2000, iters);
  672. testThreadsXX(mode, 4000, iters);
  673. testThreadsXX(mode, 8000, iters);
  674. testThreadsXX(mode, 16000, iters);
  675. testThreadsXX(mode, 32000, iters);
  676. testThreadsXX(mode, 64000, iters);
  677. }
  678. void testThreadsXX(unsigned mode, unsigned count, unsigned iters)
  679. {
  680. unsigned start = msTick();
  681. class Thread : public IThreaded
  682. {
  683. public:
  684. Thread(unsigned _count) : count(_count) {}
  685. virtual void threadmain() override
  686. {
  687. ret = call_from_thread(count);
  688. }
  689. unsigned count;
  690. unsigned ret = 0;
  691. } t1(count), t2(count), t3(count);
  692. switch (mode)
  693. {
  694. case 0:
  695. {
  696. unsigned ret = 0;
  697. CThreadedPersistent thread1("1", &t1), thread2("2", &t2), thread3("3", &t3);
  698. for (unsigned i = 0; i < iters; i++)
  699. {
  700. thread1.start();
  701. thread2.start();
  702. thread3.start();
  703. ret = call_from_thread(count);
  704. thread1.join(INFINITE);
  705. thread2.join(INFINITE);
  706. thread3.join(INFINITE);
  707. }
  708. ret += t1.ret + t2.ret + t3.ret;
  709. DBGLOG("ThreadedPersistant %d , %d, %d", count, msTick() - start, ret);
  710. break;
  711. }
  712. case 1:
  713. {
  714. unsigned ret = 0;
  715. for (unsigned i = 0; i < iters; i++)
  716. {
  717. t1.threadmain();
  718. t2.threadmain();
  719. t3.threadmain();
  720. ret = call_from_thread(count);
  721. }
  722. ret += t1.ret + t2.ret + t3.ret;
  723. DBGLOG("Sequential %d , %d, %d", count, msTick() - start, ret);
  724. break;
  725. }
  726. case 2:
  727. {
  728. unsigned ret = 0;
  729. CThreaded tthread1("1", &t1), tthread2("2", &t2), tthread3("3", &t3);
  730. for (unsigned i = 0; i < iters; i++)
  731. {
  732. tthread1.start();
  733. tthread2.start();
  734. tthread3.start();
  735. ret = call_from_thread(count);
  736. tthread1.join();
  737. tthread2.join();
  738. tthread3.join();
  739. }
  740. ret += t1.ret + t2.ret + t3.ret;
  741. DBGLOG("CThreaded %d , %d, %d", count, msTick() - start, ret);
  742. break;
  743. }
  744. case 3:
  745. {
  746. unsigned ret = 0;
  747. for (unsigned i = 0; i < iters; i++)
  748. {
  749. class casyncfor: public CAsyncFor
  750. {
  751. public:
  752. casyncfor(unsigned _count) :count(_count), ret(0) {}
  753. void Do(unsigned i)
  754. {
  755. ret += call_from_thread(count);
  756. }
  757. unsigned count;
  758. unsigned ret;
  759. } afor(count);
  760. afor.For(4, 4);
  761. ret = afor.ret;
  762. }
  763. DBGLOG("AsyncFor %d , %d, %d", count, msTick() - start, ret);
  764. break;
  765. }
  766. case 4:
  767. {
  768. CPersistentTask task1("1", &t1), task2("2", &t2), task3("3", &t3);
  769. unsigned ret = 0;
  770. for (unsigned i = 0; i < iters; i++)
  771. {
  772. task1.start();
  773. task2.start();
  774. task3.start();
  775. ret = call_from_thread(count);
  776. task1.join(INFINITE);
  777. task2.join(INFINITE);
  778. task3.join(INFINITE);
  779. }
  780. ret += t1.ret + t2.ret + t3.ret;
  781. DBGLOG("PersistantTask %d , %d, %d", count, msTick() - start, ret);
  782. break;
  783. }
  784. }
  785. }
  786. };
  787. CPPUNIT_TEST_SUITE_REGISTRATION( ThreadedPersistStressTest );
  788. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ThreadedPersistStressTest, "ThreadedPersistStressTest" );
  789. #ifndef _WIN32
  790. class PipeRunTest : public CppUnit::TestFixture
  791. {
  792. CPPUNIT_TEST_SUITE( PipeRunTest );
  793. CPPUNIT_TEST(testRun);
  794. CPPUNIT_TEST_SUITE_END();
  795. void testRun()
  796. {
  797. Owned<IPipeProcess> pipe = createPipeProcess();
  798. setenv("OLDVAR", "old", 1);
  799. setenv("TESTVAR", "oldtest", 1);
  800. pipe->setenv("TESTVAR", "well");
  801. pipe->setenv("TESTVAR", "hello");
  802. pipe->setenv("AX", "ax");
  803. pipe->setenv("BCD", "bcd");
  804. pipe->setenv("ABCD", "abcd");
  805. ASSERT(pipe->run("/bin/bash", "/bin/bash -c 'echo $TESTVAR $OLDVAR $AX $BCD $ABCD'", ".", false, true, false));
  806. byte buf[4096];
  807. size32_t read = pipe->read(sizeof(buf),buf);
  808. ASSERT(read==22);
  809. ASSERT(memcmp(buf, "hello old ax bcd abcd\n", 22)==0);
  810. ASSERT(pipe->wait()==0);
  811. }
  812. };
  813. CPPUNIT_TEST_SUITE_REGISTRATION( PipeRunTest );
  814. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( PipeRunTest, "PipeRunTest" );
  815. #endif
  816. #endif // _USE_CPPUNIT