file_test_base.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. // Implementation-wise, this:
  5. //
  6. // - Uses the registered `FileTestFactory` to construct `FileTestBase`
  7. // instances.
  8. // - Constructs a `FileTestCase` that wraps each `FileTestBase` instance to
  9. // register with googletest, and to provide the actual `TestBody`.
  10. // - Using `FileTestEventListener`, runs tests in parallel prior to normal
  11. // googletest execution.
  12. // - This is required to support `--gtest_filter` and access `should_run`.
  13. // - Runs each `FileTestBase` instance to cache the `TestFile` on
  14. // `FileTestInfo`.
  15. // - Determines whether autoupdate would make changes, autoupdating if
  16. // requested.
  17. // - When googletest would normally execute the test, `FileTestCase::TestBody`
  18. // instead uses the cached state on `FileTestInfo`.
  19. // - This only occurs when neither autoupdating nor dumping output.
  20. #include "testing/file_test/file_test_base.h"
  21. #include <filesystem>
  22. #include <optional>
  23. #include <string>
  24. #include <utility>
  25. #include "absl/flags/flag.h"
  26. #include "absl/flags/parse.h"
  27. #include "absl/strings/str_join.h"
  28. #include "absl/strings/str_split.h"
  29. #include "common/check.h"
  30. #include "common/error.h"
  31. #include "common/exe_path.h"
  32. #include "common/init_llvm.h"
  33. #include "common/raw_string_ostream.h"
  34. #include "llvm/ADT/StringExtras.h"
  35. #include "llvm/Support/CrashRecoveryContext.h"
  36. #include "llvm/Support/FormatVariadic.h"
  37. #include "llvm/Support/MemoryBuffer.h"
  38. #include "llvm/Support/PrettyStackTrace.h"
  39. #include "llvm/Support/Process.h"
  40. #include "llvm/Support/ThreadPool.h"
  41. #include "testing/base/file_helpers.h"
  42. #include "testing/file_test/autoupdate.h"
  43. #include "testing/file_test/run_test.h"
  44. #include "testing/file_test/test_file.h"
  45. ABSL_FLAG(std::vector<std::string>, file_tests, {},
  46. "A comma-separated list of repo-relative names of test files. "
  47. "Similar to and overrides `--gtest_filter`, but doesn't require the "
  48. "test class name to be known.");
  49. ABSL_FLAG(std::string, test_targets_file, "",
  50. "A path to a file containing repo-relative names of test files.");
  51. ABSL_FLAG(bool, autoupdate, false,
  52. "Instead of verifying files match test output, autoupdate files "
  53. "based on test output.");
  54. ABSL_FLAG(unsigned int, threads, 0,
  55. "Number of threads to use when autoupdating tests, or 0 to "
  56. "automatically determine a thread count.");
  57. ABSL_FLAG(bool, dump_output, false,
  58. "Instead of verifying files match test output, directly dump output "
  59. "to stderr.");
  60. namespace Carbon::Testing {
  61. // Information for a test case.
  62. struct FileTestInfo {
  63. // The name.
  64. std::string test_name;
  65. // A factory function for creating the test object.
  66. std::function<auto()->FileTestBase*> factory_fn;
  67. // gtest's information about the test.
  68. ::testing::TestInfo* registered_test;
  69. // The test result, set after running.
  70. std::optional<ErrorOr<TestFile>> test_result;
  71. // Whether running autoupdate would change (or when autoupdating, already
  72. // changed) the test file. This may be true even if output passes test
  73. // expectations.
  74. bool autoupdate_differs = false;
  75. };
  76. // Adapts a `FileTestBase` instance to gtest for outputting results.
  77. class FileTestCase : public testing::Test {
  78. public:
  79. explicit FileTestCase(FileTestInfo* test_info) : test_info_(test_info) {}
  80. // Runs a test and compares output. This keeps output split by line so that
  81. // issues are a little easier to identify by the different line.
  82. auto TestBody() -> void final;
  83. private:
  84. FileTestInfo* test_info_;
  85. };
  86. // Splits outputs to string_view because gtest handles string_view by default.
  87. static auto SplitOutput(llvm::StringRef output)
  88. -> llvm::SmallVector<std::string_view> {
  89. if (output.empty()) {
  90. return {};
  91. }
  92. llvm::SmallVector<llvm::StringRef> lines;
  93. llvm::StringRef(output).split(lines, "\n");
  94. return llvm::SmallVector<std::string_view>(lines.begin(), lines.end());
  95. }
  96. // Verify that the success and `fail_` prefix use correspond. Separately handle
  97. // both cases for clearer test failures.
  98. static auto CompareFailPrefix(llvm::StringRef filename, bool success) -> void {
  99. if (success) {
  100. EXPECT_FALSE(filename.starts_with("fail_"))
  101. << "`" << filename
  102. << "` succeeded; if success is expected, remove the `fail_` "
  103. "prefix.";
  104. } else {
  105. EXPECT_TRUE(filename.starts_with("fail_"))
  106. << "`" << filename
  107. << "` failed; if failure is expected, add the `fail_` prefix.";
  108. }
  109. }
  110. // Modes for GetBazelCommand.
  111. enum class BazelMode : uint8_t {
  112. Autoupdate,
  113. Dump,
  114. Test,
  115. };
  116. // Returns the requested bazel command string for the given execution mode.
  117. static auto GetBazelCommand(BazelMode mode, llvm::StringRef test_name)
  118. -> std::string {
  119. RawStringOstream args;
  120. const char* target = getenv("TEST_TARGET");
  121. args << "bazel " << ((mode == BazelMode::Test) ? "test" : "run") << " "
  122. << (target ? target : "<target>") << " ";
  123. switch (mode) {
  124. case BazelMode::Autoupdate:
  125. args << "-- --autoupdate ";
  126. break;
  127. case BazelMode::Dump:
  128. args << "-- --dump_output ";
  129. break;
  130. case BazelMode::Test:
  131. args << "--test_arg=";
  132. break;
  133. }
  134. args << "--file_tests=";
  135. args << test_name;
  136. return args.TakeStr();
  137. }
  138. // Runs the FileTestAutoupdater, returning the result.
  139. static auto RunAutoupdater(FileTestBase* test_base, const TestFile& test_file,
  140. bool dry_run) -> bool {
  141. if (!test_file.autoupdate_line_number) {
  142. return false;
  143. }
  144. llvm::SmallVector<llvm::StringRef> filenames;
  145. filenames.reserve(test_file.non_check_lines.size());
  146. if (test_file.has_splits) {
  147. // There are splits, so we provide an empty name for the first file.
  148. filenames.push_back({});
  149. }
  150. for (const auto& file : test_file.file_splits) {
  151. filenames.push_back(file.filename);
  152. }
  153. llvm::ArrayRef expected_filenames = filenames;
  154. if (filenames.size() > 1) {
  155. expected_filenames = expected_filenames.drop_front();
  156. }
  157. return FileTestAutoupdater(
  158. std::filesystem::absolute(test_base->test_name().str()),
  159. GetBazelCommand(BazelMode::Test, test_base->test_name()),
  160. GetBazelCommand(BazelMode::Dump, test_base->test_name()),
  161. test_file.input_content, filenames,
  162. *test_file.autoupdate_line_number, test_file.autoupdate_split,
  163. test_file.non_check_lines, test_file.actual_stdout,
  164. test_file.actual_stderr,
  165. test_base->GetDefaultFileRE(expected_filenames),
  166. test_base->GetLineNumberReplacements(expected_filenames),
  167. [&](std::string& line) {
  168. test_base->DoExtraCheckReplacements(line);
  169. })
  170. .Run(dry_run);
  171. }
  172. auto FileTestCase::TestBody() -> void {
  173. if (absl::GetFlag(FLAGS_autoupdate) || absl::GetFlag(FLAGS_dump_output)) {
  174. return;
  175. }
  176. CARBON_CHECK(test_info_->test_result,
  177. "Expected test to be run prior to TestBody: {0}",
  178. test_info_->test_name);
  179. ASSERT_TRUE(test_info_->test_result->ok())
  180. << test_info_->test_result->error();
  181. auto test_filename = std::filesystem::path(test_info_->test_name).filename();
  182. // Check success/failure against `fail_` prefixes.
  183. TestFile& test_file = **(test_info_->test_result);
  184. if (test_file.run_result.per_file_success.empty()) {
  185. CompareFailPrefix(test_filename.string(), test_file.run_result.success);
  186. } else {
  187. bool require_overall_failure = false;
  188. for (const auto& [filename, success] :
  189. test_file.run_result.per_file_success) {
  190. CompareFailPrefix(filename, success);
  191. if (!success) {
  192. require_overall_failure = true;
  193. }
  194. }
  195. if (require_overall_failure) {
  196. EXPECT_FALSE(test_file.run_result.success)
  197. << "There is a per-file failure expectation, so the overall result "
  198. "should have been a failure.";
  199. } else {
  200. // Individual files all succeeded, so the prefix is enforced on the main
  201. // test file.
  202. CompareFailPrefix(test_filename.string(), test_file.run_result.success);
  203. }
  204. }
  205. // Check results. Include a reminder for NOAUTOUPDATE tests.
  206. std::unique_ptr<testing::ScopedTrace> scoped_trace;
  207. if (!test_file.autoupdate_line_number) {
  208. scoped_trace = std::make_unique<testing::ScopedTrace>(
  209. __FILE__, __LINE__,
  210. "This file is NOAUTOUPDATE, so expected differences require manual "
  211. "updates.");
  212. }
  213. if (test_file.check_subset) {
  214. EXPECT_THAT(SplitOutput(test_file.actual_stdout),
  215. IsSupersetOf(test_file.expected_stdout));
  216. EXPECT_THAT(SplitOutput(test_file.actual_stderr),
  217. IsSupersetOf(test_file.expected_stderr));
  218. } else {
  219. EXPECT_THAT(SplitOutput(test_file.actual_stdout),
  220. ElementsAreArray(test_file.expected_stdout));
  221. EXPECT_THAT(SplitOutput(test_file.actual_stderr),
  222. ElementsAreArray(test_file.expected_stderr));
  223. }
  224. if (HasFailure()) {
  225. llvm::errs() << "\nTo test this file alone, run:\n "
  226. << GetBazelCommand(BazelMode::Test, test_info_->test_name)
  227. << "\n\n";
  228. if (test_file.autoupdate_line_number) {
  229. llvm::errs() << "\nThis test is NOAUTOUPDATE.\n\n";
  230. }
  231. }
  232. if (test_info_->autoupdate_differs) {
  233. ADD_FAILURE() << "Autoupdate would make changes to the file content. Run:\n"
  234. << GetBazelCommand(BazelMode::Autoupdate,
  235. test_info_->test_name);
  236. }
  237. }
  238. auto FileTestBase::GetLineNumberReplacements(
  239. llvm::ArrayRef<llvm::StringRef> filenames)
  240. -> llvm::SmallVector<LineNumberReplacement> {
  241. return {{.has_file = true,
  242. .re = std::make_shared<RE2>(
  243. llvm::formatv(R"(({0}):(\d+)?)", llvm::join(filenames, "|"))),
  244. .line_formatv = R"({0})"}};
  245. }
  246. // If `--file_tests` is set, transform it into a `--gtest_filter`.
  247. static auto MaybeApplyFileTestsFlag(llvm::StringRef factory_name) -> void {
  248. if (absl::GetFlag(FLAGS_file_tests).empty()) {
  249. return;
  250. }
  251. RawStringOstream filter;
  252. llvm::ListSeparator sep(":");
  253. for (const auto& file : absl::GetFlag(FLAGS_file_tests)) {
  254. filter << sep << factory_name << "." << file;
  255. }
  256. absl::SetFlag(&FLAGS_gtest_filter, filter.TakeStr());
  257. }
  258. // Loads tests from the manifest file, and registers them for execution. The
  259. // vector is taken as an output parameter so that the address of entries is
  260. // stable for the factory.
  261. static auto RegisterTests(FileTestFactory* test_factory,
  262. llvm::StringRef exe_path,
  263. llvm::SmallVectorImpl<FileTestInfo>& tests)
  264. -> ErrorOr<Success> {
  265. if (absl::GetFlag(FLAGS_test_targets_file).empty()) {
  266. return Error("Missing --test_targets_file.");
  267. }
  268. CARBON_ASSIGN_OR_RETURN(auto test_manifest,
  269. ReadFile(absl::GetFlag(FLAGS_test_targets_file)));
  270. // Prepare the vector first, so that the location of entries won't change.
  271. for (const auto& test_name :
  272. absl::StrSplit(test_manifest, "\n", absl::SkipEmpty())) {
  273. tests.push_back({.test_name = std::string(test_name)});
  274. }
  275. // Amend entries with factory functions.
  276. for (auto& test : tests) {
  277. llvm::StringRef test_name = test.test_name;
  278. test.factory_fn = [test_factory, exe_path, test_name]() {
  279. return test_factory->factory_fn(exe_path, test_name);
  280. };
  281. test.registered_test = testing::RegisterTest(
  282. test_factory->name, test_name.data(), nullptr, test_name.data(),
  283. __FILE__, __LINE__, [&test]() { return new FileTestCase(&test); });
  284. }
  285. return Success();
  286. }
  287. // Implements the parallel test execution through gtest's listener support.
  288. class FileTestEventListener : public testing::EmptyTestEventListener {
  289. public:
  290. explicit FileTestEventListener(llvm::MutableArrayRef<FileTestInfo> tests)
  291. : tests_(tests) {}
  292. // Runs test during start, after `should_run` is initialized. This is
  293. // multi-threaded to get extra speed.
  294. auto OnTestProgramStart(const testing::UnitTest& /*unit_test*/)
  295. -> void override;
  296. private:
  297. llvm::MutableArrayRef<FileTestInfo> tests_;
  298. };
  299. auto FileTestEventListener::OnTestProgramStart(
  300. const testing::UnitTest& /*unit_test*/) -> void {
  301. llvm::CrashRecoveryContext::Enable();
  302. llvm::DefaultThreadPool pool(
  303. {.ThreadsRequested = absl::GetFlag(FLAGS_dump_output)
  304. ? 1
  305. : absl::GetFlag(FLAGS_threads)});
  306. if (!absl::GetFlag(FLAGS_dump_output)) {
  307. llvm::errs() << "Running tests with " << pool.getMaxConcurrency()
  308. << " thread(s)\n";
  309. }
  310. // Guard access to both `llvm::errs` and `crashed`.
  311. bool crashed = false;
  312. std::mutex output_mutex;
  313. for (auto& test : tests_) {
  314. if (!test.registered_test->should_run()) {
  315. continue;
  316. }
  317. pool.async([&output_mutex, &crashed, &test] {
  318. // If any thread crashed, don't try running more.
  319. {
  320. std::unique_lock<std::mutex> lock(output_mutex);
  321. if (crashed) {
  322. return;
  323. }
  324. }
  325. // Use a crash recovery context to try to get a stack trace when
  326. // multiple threads may crash in parallel, which otherwise leads to the
  327. // program aborting without printing a stack trace.
  328. llvm::CrashRecoveryContext crc;
  329. crc.DumpStackAndCleanupOnFailure = true;
  330. bool thread_crashed = !crc.RunSafely([&] {
  331. std::unique_ptr<FileTestBase> test_instance(test.factory_fn());
  332. // Add a crash trace entry with the single-file test command.
  333. std::string test_command =
  334. GetBazelCommand(BazelMode::Test, test.test_name);
  335. llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str());
  336. if (absl::GetFlag(FLAGS_dump_output)) {
  337. std::unique_lock<std::mutex> lock(output_mutex);
  338. llvm::errs() << "\n--- Dumping: " << test.test_name << "\n\n";
  339. }
  340. test.test_result = ProcessTestFileAndRun(
  341. test_instance.get(), &output_mutex,
  342. absl::GetFlag(FLAGS_dump_output), absl::GetFlag(FLAGS_autoupdate));
  343. if (!test.test_result->ok()) {
  344. std::unique_lock<std::mutex> lock(output_mutex);
  345. llvm::errs() << "\n" << test.test_result->error().message() << "\n";
  346. return;
  347. }
  348. test.autoupdate_differs =
  349. RunAutoupdater(test_instance.get(), **test.test_result,
  350. /*dry_run=*/!absl::GetFlag(FLAGS_autoupdate));
  351. std::unique_lock<std::mutex> lock(output_mutex);
  352. if (absl::GetFlag(FLAGS_dump_output)) {
  353. llvm::outs().flush();
  354. const TestFile& test_file = **test.test_result;
  355. llvm::errs() << "\n--- Exit with success: "
  356. << (test_file.run_result.success ? "true" : "false")
  357. << "\n--- Autoupdate differs: "
  358. << (test.autoupdate_differs ? "true" : "false") << "\n";
  359. } else {
  360. llvm::errs() << (test.autoupdate_differs ? "!" : ".");
  361. }
  362. });
  363. if (thread_crashed) {
  364. std::unique_lock<std::mutex> lock(output_mutex);
  365. crashed = true;
  366. }
  367. });
  368. }
  369. pool.wait();
  370. if (crashed) {
  371. // Abort rather than returning so that we don't get a LeakSanitizer report.
  372. // We expect to have leaked memory if one or more of our tests crashed.
  373. std::abort();
  374. }
  375. llvm::errs() << "\nDone!\n";
  376. }
  377. // Implements main() within the Carbon::Testing namespace for convenience.
  378. static auto Main(int argc, char** argv) -> ErrorOr<int> {
  379. Carbon::InitLLVM init_llvm(argc, argv);
  380. testing::InitGoogleTest(&argc, argv);
  381. auto args = absl::ParseCommandLine(argc, argv);
  382. if (args.size() > 1) {
  383. ErrorBuilder b;
  384. b << "Unexpected arguments:";
  385. for (char* arg : llvm::ArrayRef(args).drop_front()) {
  386. b << " " << FormatEscaped(arg);
  387. }
  388. return b;
  389. }
  390. std::string exe_path = FindExecutablePath(argv[0]);
  391. // Tests might try to read from stdin. Ensure those reads fail by closing
  392. // stdin and reopening it as /dev/null. Note that STDIN_FILENO doesn't exist
  393. // on Windows, but POSIX requires it to be 0.
  394. if (std::error_code error =
  395. llvm::sys::Process::SafelyCloseFileDescriptor(0)) {
  396. return Error("Unable to close standard input: " + error.message());
  397. }
  398. if (std::error_code error =
  399. llvm::sys::Process::FixupStandardFileDescriptors()) {
  400. return Error("Unable to correct standard file descriptors: " +
  401. error.message());
  402. }
  403. if (absl::GetFlag(FLAGS_autoupdate) && absl::GetFlag(FLAGS_dump_output)) {
  404. return Error("--autoupdate and --dump_output are mutually exclusive.");
  405. }
  406. auto test_factory = GetFileTestFactory();
  407. MaybeApplyFileTestsFlag(test_factory.name);
  408. // Inline 0 entries because it will always be too large to store on the stack.
  409. llvm::SmallVector<FileTestInfo, 0> tests;
  410. CARBON_RETURN_IF_ERROR(RegisterTests(&test_factory, exe_path, tests));
  411. testing::TestEventListeners& listeners =
  412. testing::UnitTest::GetInstance()->listeners();
  413. if (absl::GetFlag(FLAGS_autoupdate) || absl::GetFlag(FLAGS_dump_output)) {
  414. // Suppress all of the default output.
  415. delete listeners.Release(listeners.default_result_printer());
  416. }
  417. // Use a listener to run tests in parallel.
  418. listeners.Append(new FileTestEventListener(tests));
  419. return RUN_ALL_TESTS();
  420. }
  421. } // namespace Carbon::Testing
  422. auto main(int argc, char** argv) -> int {
  423. if (auto result = Carbon::Testing::Main(argc, argv); result.ok()) {
  424. return *result;
  425. } else {
  426. llvm::errs() << result.error() << "\n";
  427. return EXIT_FAILURE;
  428. }
  429. }