utmain.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. #include "junit.h"
  2. #include "plugin.h"
  3. #include "registar.h"
  4. #include "utmain.h"
  5. #include <library/cpp/colorizer/colors.h>
  6. #include <library/cpp/json/writer/json.h>
  7. #include <library/cpp/json/writer/json_value.h>
  8. #include <library/cpp/testing/common/env.h>
  9. #include <library/cpp/testing/hook/hook.h>
  10. #include <util/datetime/base.h>
  11. #include <util/generic/hash.h>
  12. #include <util/generic/hash_set.h>
  13. #include <util/generic/scope.h>
  14. #include <util/generic/string.h>
  15. #include <util/generic/yexception.h>
  16. #include <util/network/init.h>
  17. #include <util/stream/file.h>
  18. #include <util/stream/output.h>
  19. #include <util/string/join.h>
  20. #include <util/string/util.h>
  21. #include <util/system/defaults.h>
  22. #include <util/system/env.h>
  23. #include <util/system/execpath.h>
  24. #include <util/system/valgrind.h>
  25. #include <util/system/shellcommand.h>
  26. #include <filesystem>
  27. #if defined(_win_)
  28. #include <fcntl.h>
  29. #include <io.h>
  30. #include <windows.h>
  31. #include <crtdbg.h>
  32. #endif
  33. #if defined(_unix_)
  34. #include <unistd.h>
  35. #endif
  36. #ifdef WITH_VALGRIND
  37. #define NOTE_IN_VALGRIND(test) VALGRIND_PRINTF("%s::%s", test->unit->name.data(), test->name)
  38. #else
  39. #define NOTE_IN_VALGRIND(test)
  40. #endif
  41. const size_t MAX_COMMENT_MESSAGE_LENGTH = 1024 * 1024; // 1 MB
  42. using namespace NUnitTest;
  43. class TNullTraceWriterProcessor: public ITestSuiteProcessor {
  44. };
  45. class TMultiTraceProcessor: public ITestSuiteProcessor {
  46. public:
  47. TMultiTraceProcessor(std::vector<std::shared_ptr<ITestSuiteProcessor>>&& processors)
  48. : Processors(std::move(processors))
  49. {
  50. }
  51. void SetForkTestsParams(bool forkTests, bool isForked) override {
  52. ITestSuiteProcessor::SetForkTestsParams(forkTests, isForked);
  53. for (const auto& proc : Processors) {
  54. proc->SetForkTestsParams(forkTests, isForked);
  55. }
  56. }
  57. private:
  58. void OnStart() override {
  59. for (const auto& proc : Processors) {
  60. proc->Start();
  61. }
  62. }
  63. void OnEnd() override {
  64. for (const auto& proc : Processors) {
  65. proc->End();
  66. }
  67. }
  68. void OnUnitStart(const TUnit* unit) override {
  69. for (const auto& proc : Processors) {
  70. proc->UnitStart(*unit);
  71. }
  72. }
  73. void OnUnitStop(const TUnit* unit) override {
  74. for (const auto& proc : Processors) {
  75. proc->UnitStop(*unit);
  76. }
  77. }
  78. void OnError(const TError* error) override {
  79. for (const auto& proc : Processors) {
  80. proc->Error(*error);
  81. }
  82. }
  83. void OnFinish(const TFinish* finish) override {
  84. for (const auto& proc : Processors) {
  85. proc->Finish(*finish);
  86. }
  87. }
  88. void OnBeforeTest(const TTest* test) override {
  89. for (const auto& proc : Processors) {
  90. proc->BeforeTest(*test);
  91. }
  92. }
  93. private:
  94. std::vector<std::shared_ptr<ITestSuiteProcessor>> Processors;
  95. };
  96. class TTraceWriterProcessor: public ITestSuiteProcessor {
  97. public:
  98. inline TTraceWriterProcessor(const char* traceFilePath, EOpenMode mode)
  99. : PrevTime(TInstant::Now())
  100. {
  101. TraceFile = new TUnbufferedFileOutput(TFile(traceFilePath, mode | WrOnly | Seq));
  102. }
  103. private:
  104. TAutoPtr<TUnbufferedFileOutput> TraceFile;
  105. TString TraceFilePath;
  106. TInstant PrevTime;
  107. TVector<TString> ErrorMessages;
  108. inline void Trace(const TString eventName, const NJson::TJsonValue eventValue) {
  109. NJsonWriter::TBuf json(NJsonWriter::HEM_UNSAFE);
  110. json.BeginObject();
  111. json.WriteKey("name").WriteString(eventName);
  112. json.WriteKey("value").WriteJsonValue(&eventValue);
  113. json.WriteKey("timestamp").WriteDouble(TInstant::Now().SecondsFloat(), PREC_NDIGITS, 14);
  114. json.EndObject();
  115. json.FlushTo(TraceFile.Get());
  116. *TraceFile << "\n";
  117. }
  118. inline void TraceSubtestFinished(const char* className, const char* subtestName, const char* status, const TString comment, const TTestContext* context) {
  119. const TInstant now = TInstant::Now();
  120. NJson::TJsonValue event;
  121. event.InsertValue("class", className);
  122. event.InsertValue("subtest", subtestName);
  123. event.InsertValue("status", status);
  124. event.InsertValue("comment", comment.data());
  125. event.InsertValue("time", (now - PrevTime).SecondsFloat());
  126. if (context) {
  127. for (const auto& metric : context->Metrics) {
  128. event["metrics"].InsertValue(metric.first, metric.second);
  129. }
  130. }
  131. Trace("subtest-finished", event);
  132. PrevTime = now;
  133. TString marker = Join("", "\n###subtest-finished:", className, "::", subtestName, "\n");
  134. Cout << marker;
  135. Cout.Flush();
  136. Cerr << comment;
  137. Cerr << marker;
  138. Cerr.Flush();
  139. }
  140. virtual TString BuildComment(const char* message, const char* backTrace) {
  141. return NUnitTest::GetFormatTag("bad") +
  142. TString(message).substr(0, MAX_COMMENT_MESSAGE_LENGTH) +
  143. NUnitTest::GetResetTag() +
  144. TString("\n") +
  145. NUnitTest::GetFormatTag("alt1") +
  146. TString(backTrace).substr(0, MAX_COMMENT_MESSAGE_LENGTH) +
  147. NUnitTest::GetResetTag();
  148. }
  149. void OnBeforeTest(const TTest* test) override {
  150. NJson::TJsonValue event;
  151. event.InsertValue("class", test->unit->name);
  152. event.InsertValue("subtest", test->name);
  153. Trace("subtest-started", event);
  154. TString marker = Join("", "\n###subtest-started:", test->unit->name, "::", test->name, "\n");
  155. Cout << marker;
  156. Cout.Flush();
  157. Cerr << marker;
  158. Cerr.Flush();
  159. }
  160. void OnUnitStart(const TUnit* unit) override {
  161. NJson::TJsonValue event;
  162. event.InsertValue("class", unit->name);
  163. }
  164. void OnUnitStop(const TUnit* unit) override {
  165. NJson::TJsonValue event;
  166. event.InsertValue("class", unit->name);
  167. }
  168. void OnError(const TError* descr) override {
  169. const TString comment = BuildComment(descr->msg, descr->BackTrace.data());
  170. ErrorMessages.push_back(comment);
  171. }
  172. void OnFinish(const TFinish* descr) override {
  173. if (descr->Success) {
  174. TraceSubtestFinished(descr->test->unit->name.data(), descr->test->name, "good", "", descr->Context);
  175. } else {
  176. TStringBuilder msgs;
  177. for (const TString& m : ErrorMessages) {
  178. if (msgs) {
  179. msgs << TStringBuf("\n");
  180. }
  181. msgs << m;
  182. }
  183. if (msgs) {
  184. msgs << TStringBuf("\n");
  185. }
  186. TraceSubtestFinished(descr->test->unit->name.data(), descr->test->name, "fail", msgs, descr->Context);
  187. ErrorMessages.clear();
  188. }
  189. }
  190. };
  191. class TColoredProcessor: public ITestSuiteProcessor, public NColorizer::TColors {
  192. public:
  193. inline TColoredProcessor(const TString& appName)
  194. : PrintBeforeSuite_(true)
  195. , PrintBeforeTest_(true)
  196. , PrintAfterTest_(true)
  197. , PrintAfterSuite_(true)
  198. , PrintTimes_(false)
  199. , PrintSummary_(true)
  200. , PrevTime_(TInstant::Now())
  201. , ShowFails(true)
  202. , Start(0)
  203. , End(Max<size_t>())
  204. , AppName(appName)
  205. , Loop(false)
  206. , ForkExitedCorrectly(false)
  207. , TraceProcessor(new TNullTraceWriterProcessor())
  208. {
  209. }
  210. ~TColoredProcessor() override {
  211. }
  212. inline void Disable(const char* name) {
  213. size_t colon = TString(name).find("::");
  214. if (colon == TString::npos) {
  215. DisabledSuites_.insert(name);
  216. } else {
  217. TString suite = TString(name).substr(0, colon);
  218. DisabledTests_.insert(name);
  219. }
  220. }
  221. inline void Enable(const char* name) {
  222. size_t colon = TString(name).rfind("::");
  223. if (colon == TString::npos) {
  224. EnabledSuites_.insert(name);
  225. EnabledTests_.insert(TString() + name + "::*");
  226. } else {
  227. TString suite = TString(name).substr(0, colon);
  228. EnabledSuites_.insert(suite);
  229. EnabledSuites_.insert(name);
  230. EnabledTests_.insert(name);
  231. EnabledTests_.insert(TString() + name + "::*");
  232. }
  233. }
  234. inline void FilterFromFile(TString filename) {
  235. TString filterLine;
  236. TFileInput filtersStream(filename);
  237. while (filtersStream.ReadLine(filterLine)) {
  238. if (filterLine.StartsWith("-")) {
  239. Disable(filterLine.c_str() + 1);
  240. } else if(filterLine.StartsWith("+")) {
  241. Enable(filterLine.c_str() + 1);
  242. }
  243. }
  244. }
  245. inline void SetPrintBeforeSuite(bool print) {
  246. PrintBeforeSuite_ = print;
  247. }
  248. inline void SetPrintAfterSuite(bool print) {
  249. PrintAfterSuite_ = print;
  250. }
  251. inline void SetPrintBeforeTest(bool print) {
  252. PrintBeforeTest_ = print;
  253. }
  254. inline void SetPrintAfterTest(bool print) {
  255. PrintAfterTest_ = print;
  256. }
  257. inline void SetPrintTimes(bool print) {
  258. PrintTimes_ = print;
  259. }
  260. inline void SetPrintSummary(bool print) {
  261. PrintSummary_ = print;
  262. }
  263. inline bool GetPrintSummary() {
  264. return PrintSummary_;
  265. }
  266. inline void SetShowFails(bool show) {
  267. ShowFails = show;
  268. }
  269. inline void BeQuiet() {
  270. SetPrintTimes(false);
  271. SetPrintBeforeSuite(false);
  272. SetPrintAfterSuite(false);
  273. SetPrintBeforeTest(false);
  274. SetPrintAfterTest(false);
  275. SetPrintSummary(false);
  276. }
  277. inline void SetStart(size_t val) {
  278. Start = val;
  279. }
  280. inline void SetEnd(size_t val) {
  281. End = val;
  282. }
  283. inline void SetForkTestsParams(bool forkTests, bool isForked) override {
  284. ITestSuiteProcessor::SetForkTestsParams(forkTests, isForked);
  285. TraceProcessor->SetForkTestsParams(forkTests, isForked);
  286. SetIsTTY(GetIsForked() || CalcIsTTY(stderr));
  287. }
  288. inline void SetLoop(bool loop) {
  289. Loop = loop;
  290. }
  291. inline bool IsLoop() const {
  292. return Loop;
  293. }
  294. inline void SetTraceProcessor(std::shared_ptr<ITestSuiteProcessor> traceProcessor) {
  295. TraceProcessor = std::move(traceProcessor);
  296. }
  297. private:
  298. void OnUnitStart(const TUnit* unit) override {
  299. TraceProcessor->UnitStart(*unit);
  300. if (GetIsForked()) {
  301. return;
  302. }
  303. if (PrintBeforeSuite_ || PrintBeforeTest_) {
  304. fprintf(stderr, "%s<-----%s %s\n", LightBlueColor().data(), OldColor().data(), unit->name.data());
  305. }
  306. }
  307. void OnUnitStop(const TUnit* unit) override {
  308. TraceProcessor->UnitStop(*unit);
  309. if (GetIsForked()) {
  310. return;
  311. }
  312. if (!PrintAfterSuite_) {
  313. return;
  314. }
  315. fprintf(stderr, "%s----->%s %s -> ok: %s%u%s",
  316. LightBlueColor().data(), OldColor().data(), unit->name.data(),
  317. LightGreenColor().data(), GoodTestsInCurrentUnit(), OldColor().data());
  318. if (FailTestsInCurrentUnit()) {
  319. fprintf(stderr, ", err: %s%u%s",
  320. LightRedColor().data(), FailTestsInCurrentUnit(), OldColor().data());
  321. }
  322. fprintf(stderr, "\n");
  323. }
  324. void OnBeforeTest(const TTest* test) override {
  325. if (!GetIsForked() && PrintBeforeTest_) {
  326. fprintf(stderr, "[%sexec%s] %s::%s...\n", LightBlueColor().data(), OldColor().data(), test->unit->name.data(), test->name);
  327. }
  328. TraceProcessor->BeforeTest(*test);
  329. }
  330. void OnError(const TError* descr) override {
  331. TraceProcessor->Error(*descr);
  332. if (!GetIsForked() && ForkExitedCorrectly) {
  333. return;
  334. }
  335. if (!PrintAfterTest_) {
  336. return;
  337. }
  338. const TString err = Sprintf("[%sFAIL%s] %s::%s -> %s%s%s\n%s%s%s", LightRedColor().data(), OldColor().data(),
  339. descr->test->unit->name.data(),
  340. descr->test->name,
  341. LightRedColor().data(), descr->msg, OldColor().data(), LightCyanColor().data(), descr->BackTrace.data(), OldColor().data());
  342. const TDuration test_duration = SaveTestDuration();
  343. if (ShowFails) {
  344. if (PrintTimes_) {
  345. Fails.push_back(Sprintf("%s %s", test_duration.ToString().data(), err.data()));
  346. } else {
  347. Fails.push_back(err);
  348. }
  349. }
  350. fprintf(stderr, "%s", err.data());
  351. NOTE_IN_VALGRIND(descr->test);
  352. PrintTimes(test_duration);
  353. if (GetIsForked()) {
  354. fprintf(stderr, "%s", ForkCorrectExitMsg);
  355. }
  356. }
  357. void OnFinish(const TFinish* descr) override {
  358. TraceProcessor->Finish(*descr);
  359. if (!GetIsForked() && ForkExitedCorrectly) {
  360. return;
  361. }
  362. if (!PrintAfterTest_) {
  363. return;
  364. }
  365. if (descr->Success) {
  366. fprintf(stderr, "[%sgood%s] %s::%s\n", LightGreenColor().data(), OldColor().data(),
  367. descr->test->unit->name.data(),
  368. descr->test->name);
  369. NOTE_IN_VALGRIND(descr->test);
  370. PrintTimes(SaveTestDuration());
  371. if (GetIsForked()) {
  372. fprintf(stderr, "%s", ForkCorrectExitMsg);
  373. }
  374. }
  375. }
  376. inline TDuration SaveTestDuration() {
  377. const TInstant now = TInstant::Now();
  378. TDuration d = now - PrevTime_;
  379. PrevTime_ = now;
  380. return d;
  381. }
  382. inline void PrintTimes(TDuration d) {
  383. if (!PrintTimes_) {
  384. return;
  385. }
  386. Cerr << d << "\n";
  387. }
  388. void OnEnd() override {
  389. TraceProcessor->End();
  390. if (GetIsForked()) {
  391. return;
  392. }
  393. if (!PrintSummary_) {
  394. return;
  395. }
  396. fprintf(stderr, "[%sDONE%s] ok: %s%u%s",
  397. YellowColor().data(), OldColor().data(),
  398. LightGreenColor().data(), GoodTests(), OldColor().data());
  399. if (FailTests())
  400. fprintf(stderr, ", err: %s%u%s",
  401. LightRedColor().data(), FailTests(), OldColor().data());
  402. fprintf(stderr, "\n");
  403. if (ShowFails) {
  404. for (size_t i = 0; i < Fails.size(); ++i) {
  405. printf("%s", Fails[i].data());
  406. }
  407. }
  408. }
  409. bool CheckAccess(TString name, size_t num) override {
  410. if (num < Start) {
  411. return false;
  412. }
  413. if (num >= End) {
  414. return false;
  415. }
  416. if (DisabledSuites_.find(name.data()) != DisabledSuites_.end()) {
  417. return false;
  418. }
  419. if (EnabledSuites_.empty()) {
  420. return true;
  421. }
  422. return EnabledSuites_.find(name.data()) != EnabledSuites_.end();
  423. }
  424. bool CheckAccessTest(TString suite, const char* test) override {
  425. TString name = suite + "::" + test;
  426. if (DisabledTests_.find(name) != DisabledTests_.end()) {
  427. return false;
  428. }
  429. if (EnabledTests_.empty()) {
  430. return true;
  431. }
  432. if (EnabledTests_.find(TString() + suite + "::*") != EnabledTests_.end()) {
  433. return true;
  434. }
  435. return EnabledTests_.find(name) != EnabledTests_.end();
  436. }
  437. void Run(std::function<void()> f, const TString& suite, const char* name, const bool forceFork) override {
  438. if (!(GetForkTests() || forceFork) || GetIsForked()) {
  439. return f();
  440. }
  441. TList<TString> args(1, "--is-forked-internal");
  442. args.push_back(Sprintf("+%s::%s", suite.data(), name));
  443. // stdin is ignored - unittest should not need them...
  444. TShellCommandOptions options;
  445. options
  446. .SetUseShell(false)
  447. .SetCloseAllFdsOnExec(true)
  448. .SetAsync(false)
  449. .SetLatency(1);
  450. TShellCommand cmd(AppName, args, options);
  451. cmd.Run();
  452. const TString& err = cmd.GetError();
  453. const size_t msgIndex = err.find(ForkCorrectExitMsg);
  454. // everything is printed by parent process except test's result output ("good" or "fail")
  455. // which is printed by child. If there was no output - parent process prints default message.
  456. ForkExitedCorrectly = msgIndex != TString::npos;
  457. // TODO: stderr output is always printed after stdout
  458. Cout.Write(cmd.GetOutput());
  459. Cerr.Write(err.c_str(), Min(msgIndex, err.size()));
  460. // do not use default case, so gcc will warn if new element in enum will be added
  461. switch (cmd.GetStatus()) {
  462. case TShellCommand::SHELL_FINISHED: {
  463. // test could fail with zero status if it calls exit(0) in the middle.
  464. if (ForkExitedCorrectly)
  465. break;
  466. [[fallthrough]];
  467. }
  468. case TShellCommand::SHELL_ERROR: {
  469. ythrow yexception() << "Forked test failed";
  470. }
  471. case TShellCommand::SHELL_NONE: {
  472. ythrow yexception() << "Forked test finished with unknown status";
  473. }
  474. case TShellCommand::SHELL_RUNNING: {
  475. Y_ABORT_UNLESS(false, "This can't happen, we used sync mode, it's a bug!");
  476. }
  477. case TShellCommand::SHELL_INTERNAL_ERROR: {
  478. ythrow yexception() << "Forked test failed with internal error: " << cmd.GetInternalError();
  479. }
  480. }
  481. }
  482. private:
  483. bool PrintBeforeSuite_;
  484. bool PrintBeforeTest_;
  485. bool PrintAfterTest_;
  486. bool PrintAfterSuite_;
  487. bool PrintTimes_;
  488. bool PrintSummary_;
  489. THashSet<TString> DisabledSuites_;
  490. THashSet<TString> EnabledSuites_;
  491. THashSet<TString> DisabledTests_;
  492. THashSet<TString> EnabledTests_;
  493. TInstant PrevTime_;
  494. bool ShowFails;
  495. TVector<TString> Fails;
  496. size_t Start;
  497. size_t End;
  498. TString AppName;
  499. bool Loop;
  500. static const char* const ForkCorrectExitMsg;
  501. bool ForkExitedCorrectly;
  502. std::shared_ptr<ITestSuiteProcessor> TraceProcessor;
  503. };
  504. const char* const TColoredProcessor::ForkCorrectExitMsg = "--END--";
  505. class TEnumeratingProcessor: public ITestSuiteProcessor {
  506. public:
  507. TEnumeratingProcessor(bool verbose, IOutputStream& stream) noexcept
  508. : Verbose_(verbose)
  509. , Stream_(stream)
  510. {
  511. }
  512. ~TEnumeratingProcessor() override {
  513. }
  514. bool CheckAccess(TString name, size_t /*num*/) override {
  515. if (Verbose_) {
  516. return true;
  517. } else {
  518. Stream_ << name << "\n";
  519. return false;
  520. }
  521. }
  522. bool CheckAccessTest(TString suite, const char* name) override {
  523. Stream_ << suite << "::" << name << "\n";
  524. return false;
  525. }
  526. private:
  527. bool Verbose_;
  528. IOutputStream& Stream_;
  529. };
  530. #ifdef _win_
  531. class TWinEnvironment {
  532. public:
  533. TWinEnvironment()
  534. : OutputCP(GetConsoleOutputCP())
  535. {
  536. setmode(fileno(stdout), _O_BINARY);
  537. SetConsoleOutputCP(CP_UTF8);
  538. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  539. if (!IsDebuggerPresent()) {
  540. _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
  541. _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
  542. _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
  543. _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
  544. }
  545. }
  546. ~TWinEnvironment() {
  547. if (!IsDebuggerPresent()) {
  548. _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  549. _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
  550. }
  551. SetConsoleOutputCP(OutputCP); // restore original output CP at program exit
  552. }
  553. private:
  554. UINT OutputCP; // original codepage
  555. };
  556. static const TWinEnvironment Instance;
  557. #endif // _win_
  558. static int DoList(bool verbose, IOutputStream& stream) {
  559. TEnumeratingProcessor eproc(verbose, stream);
  560. TTestFactory::Instance().SetProcessor(&eproc);
  561. TTestFactory::Instance().Execute();
  562. return 0;
  563. }
  564. static int DoUsage(const char* progname) {
  565. Cout << "Usage: " << progname << " [options] [[+|-]test]...\n\n"
  566. << "Options:\n"
  567. << " -h, --help print this help message\n"
  568. << " -l, --list print a list of available tests\n"
  569. << " -A --list-verbose print a list of available subtests\n"
  570. << " --print-before-test print each test name before running it\n"
  571. << " --print-before-suite print each test suite name before running it\n"
  572. << " --show-fails print a list of all failed tests at the end\n"
  573. << " --dont-show-fails do not print a list of all failed tests at the end\n"
  574. << " --print-times print wall clock duration of each test\n"
  575. << " --fork-tests run each test in a separate process\n"
  576. << " --trace-path path to the trace file to be generated\n"
  577. << " --trace-path-append path to the trace file to be appended\n"
  578. << " --filter-file path to the test filters ([+|-]test) file (" << Y_UNITTEST_TEST_FILTER_FILE_OPTION << ")\n";
  579. return 0;
  580. }
  581. #if defined(_linux_) && defined(CLANG_COVERAGE)
  582. extern "C" int __llvm_profile_write_file(void);
  583. static void GracefulShutdownHandler(int) {
  584. try {
  585. __llvm_profile_write_file();
  586. } catch (...) {
  587. }
  588. abort();
  589. }
  590. #endif
  591. int NUnitTest::RunMain(int argc, char** argv) {
  592. #if defined(_linux_) && defined(CLANG_COVERAGE)
  593. {
  594. struct sigaction sa;
  595. memset(&sa, 0, sizeof(sa));
  596. sa.sa_handler = GracefulShutdownHandler;
  597. sa.sa_flags = SA_SIGINFO | SA_RESTART;
  598. Y_ABORT_UNLESS(!sigaction(SIGUSR2, &sa, nullptr));
  599. }
  600. #endif
  601. NTesting::THook::CallBeforeInit();
  602. InitNetworkSubSystem();
  603. Singleton<::NPrivate::TTestEnv>();
  604. try {
  605. GetExecPath();
  606. } catch (...) {
  607. }
  608. #ifndef UT_SKIP_EXCEPTIONS
  609. try {
  610. #endif
  611. NTesting::THook::CallBeforeRun();
  612. Y_DEFER {
  613. NTesting::THook::CallAfterRun();
  614. };
  615. NPlugin::OnStartMain(argc, argv);
  616. Y_DEFER {
  617. NPlugin::OnStopMain(argc, argv);
  618. };
  619. TColoredProcessor processor(GetExecPath());
  620. IOutputStream* listStream = &Cout;
  621. THolder<IOutputStream> listFile;
  622. enum EListType {
  623. DONT_LIST,
  624. LIST,
  625. LIST_VERBOSE
  626. };
  627. EListType listTests = DONT_LIST;
  628. bool hasJUnitProcessor = false;
  629. bool forkTests = false;
  630. bool isForked = false;
  631. std::vector<std::shared_ptr<ITestSuiteProcessor>> traceProcessors;
  632. // load filters from environment variable
  633. TString filterFn = GetEnv(Y_UNITTEST_TEST_FILTER_FILE_OPTION);
  634. if (!filterFn.empty()) {
  635. processor.FilterFromFile(filterFn);
  636. }
  637. auto processJunitOption = [&](const TStringBuf& v) {
  638. if (!hasJUnitProcessor) {
  639. hasJUnitProcessor = true;
  640. bool xmlFormat = false;
  641. constexpr TStringBuf xmlPrefix = "xml:";
  642. constexpr TStringBuf jsonPrefix = "json:";
  643. if ((xmlFormat = v.StartsWith(xmlPrefix)) || v.StartsWith(jsonPrefix)) {
  644. TStringBuf fileName = v;
  645. const TStringBuf prefix = xmlFormat ? xmlPrefix : jsonPrefix;
  646. fileName = fileName.SubString(prefix.size(), TStringBuf::npos);
  647. const TJUnitProcessor::EOutputFormat format = xmlFormat ? TJUnitProcessor::EOutputFormat::Xml : TJUnitProcessor::EOutputFormat::Json;
  648. NUnitTest::ShouldColorizeDiff = false;
  649. traceProcessors.push_back(std::make_shared<TJUnitProcessor>(TString(fileName),
  650. std::filesystem::path(argv[0]).stem().string(),
  651. format));
  652. }
  653. }
  654. };
  655. for (size_t i = 1; i < (size_t)argc; ++i) {
  656. const char* name = argv[i];
  657. if (name && *name) {
  658. if (strcmp(name, "--help") == 0 || strcmp(name, "-h") == 0) {
  659. return DoUsage(argv[0]);
  660. } else if (strcmp(name, "--list") == 0 || strcmp(name, "-l") == 0) {
  661. listTests = LIST;
  662. } else if (strcmp(name, "--list-verbose") == 0 || strcmp(name, "-A") == 0) {
  663. listTests = LIST_VERBOSE;
  664. } else if (strcmp(name, "--print-before-suite=false") == 0) {
  665. processor.SetPrintBeforeSuite(false);
  666. } else if (strcmp(name, "--print-before-test=false") == 0) {
  667. processor.SetPrintBeforeTest(false);
  668. } else if (strcmp(name, "--print-before-suite") == 0) {
  669. processor.SetPrintBeforeSuite(true);
  670. } else if (strcmp(name, "--print-before-test") == 0) {
  671. processor.SetPrintBeforeTest(true);
  672. } else if (strcmp(name, "--show-fails") == 0) {
  673. processor.SetShowFails(true);
  674. } else if (strcmp(name, "--dont-show-fails") == 0) {
  675. processor.SetShowFails(false);
  676. } else if (strcmp(name, "--print-times") == 0) {
  677. processor.SetPrintTimes(true);
  678. } else if (strcmp(name, "--from") == 0) {
  679. ++i;
  680. processor.SetStart(FromString<size_t>(argv[i]));
  681. } else if (strcmp(name, "--to") == 0) {
  682. ++i;
  683. processor.SetEnd(FromString<size_t>(argv[i]));
  684. } else if (strcmp(name, "--fork-tests") == 0) {
  685. forkTests = true;
  686. } else if (strcmp(name, "--is-forked-internal") == 0) {
  687. isForked = true;
  688. } else if (strcmp(name, "--loop") == 0) {
  689. processor.SetLoop(true);
  690. } else if (strcmp(name, "--trace-path") == 0) {
  691. ++i;
  692. processor.BeQuiet();
  693. NUnitTest::ShouldColorizeDiff = false;
  694. traceProcessors.push_back(std::make_shared<TTraceWriterProcessor>(argv[i], CreateAlways));
  695. } else if (strcmp(name, "--trace-path-append") == 0) {
  696. ++i;
  697. processor.BeQuiet();
  698. NUnitTest::ShouldColorizeDiff = false;
  699. traceProcessors.push_back(std::make_shared<TTraceWriterProcessor>(argv[i], OpenAlways | ForAppend));
  700. } else if (strcmp(name, "--list-path") == 0) {
  701. ++i;
  702. listFile = MakeHolder<TFixedBufferFileOutput>(argv[i]);
  703. listStream = listFile.Get();
  704. } else if (strcmp(name, "--test-param") == 0) {
  705. ++i;
  706. TString param(argv[i]);
  707. size_t assign = param.find('=');
  708. Singleton<::NPrivate::TTestEnv>()->AddTestParam(param.substr(0, assign), param.substr(assign + 1));
  709. } else if (strcmp(name, "--output") == 0) {
  710. ++i;
  711. Y_ENSURE((int)i < argc);
  712. processJunitOption(argv[i]);
  713. } else if (strcmp(name, "--filter-file") == 0) {
  714. ++i;
  715. TString filename(argv[i]);
  716. processor.FilterFromFile(filename);
  717. } else if (TString(name).StartsWith("--")) {
  718. return DoUsage(argv[0]), 1;
  719. } else if (*name == '-') {
  720. processor.Disable(name + 1);
  721. } else if (*name == '+') {
  722. processor.Enable(name + 1);
  723. } else {
  724. processor.Enable(name);
  725. }
  726. }
  727. }
  728. if (listTests != DONT_LIST) {
  729. return DoList(listTests == LIST_VERBOSE, *listStream);
  730. }
  731. if (!hasJUnitProcessor) {
  732. if (TString oo = GetEnv(Y_UNITTEST_OUTPUT_CMDLINE_OPTION)) {
  733. processJunitOption(oo);
  734. }
  735. }
  736. if (traceProcessors.size() > 1) {
  737. processor.SetTraceProcessor(std::make_shared<TMultiTraceProcessor>(std::move(traceProcessors)));
  738. } else if (traceProcessors.size() == 1) {
  739. processor.SetTraceProcessor(std::move(traceProcessors[0]));
  740. }
  741. processor.SetForkTestsParams(forkTests, isForked);
  742. TTestFactory::Instance().SetProcessor(&processor);
  743. unsigned ret;
  744. for (;;) {
  745. ret = TTestFactory::Instance().Execute();
  746. if (!processor.GetIsForked() && ret && processor.GetPrintSummary()) {
  747. Cerr << "SOME TESTS FAILED!!!!" << Endl;
  748. }
  749. if (0 != ret || !processor.IsLoop()) {
  750. break;
  751. }
  752. }
  753. return ret;
  754. #ifndef UT_SKIP_EXCEPTIONS
  755. } catch (...) {
  756. Cerr << "caught exception in test suite(" << CurrentExceptionMessage() << ")" << Endl;
  757. }
  758. #endif
  759. return 1;
  760. }