utmain.cpp 24 KB

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