file_test_base.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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. #include "testing/file_test/file_test_base.h"
  5. #include <filesystem>
  6. #include <optional>
  7. #include <string>
  8. #include <utility>
  9. #include "absl/flags/flag.h"
  10. #include "absl/flags/parse.h"
  11. #include "common/check.h"
  12. #include "common/error.h"
  13. #include "common/exe_path.h"
  14. #include "common/init_llvm.h"
  15. #include "common/raw_string_ostream.h"
  16. #include "llvm/ADT/StringExtras.h"
  17. #include "llvm/Support/CrashRecoveryContext.h"
  18. #include "llvm/Support/FormatVariadic.h"
  19. #include "llvm/Support/MemoryBuffer.h"
  20. #include "llvm/Support/PrettyStackTrace.h"
  21. #include "llvm/Support/Process.h"
  22. #include "llvm/Support/ThreadPool.h"
  23. #include "testing/base/file_helpers.h"
  24. #include "testing/file_test/autoupdate.h"
  25. #include "testing/file_test/run_test.h"
  26. #include "testing/file_test/test_file.h"
  27. ABSL_FLAG(std::vector<std::string>, file_tests, {},
  28. "A comma-separated list of repo-relative names of test files. "
  29. "Overrides test_targets_file.");
  30. ABSL_FLAG(std::string, test_targets_file, "",
  31. "A path to a file containing repo-relative names of test files.");
  32. ABSL_FLAG(bool, autoupdate, false,
  33. "Instead of verifying files match test output, autoupdate files "
  34. "based on test output.");
  35. ABSL_FLAG(unsigned int, threads, 0,
  36. "Number of threads to use when autoupdating tests, or 0 to "
  37. "automatically determine a thread count.");
  38. ABSL_FLAG(bool, dump_output, false,
  39. "Instead of verifying files match test output, directly dump output "
  40. "to stderr.");
  41. namespace Carbon::Testing {
  42. // Splits outputs to string_view because gtest handles string_view by default.
  43. static auto SplitOutput(llvm::StringRef output)
  44. -> llvm::SmallVector<std::string_view> {
  45. if (output.empty()) {
  46. return {};
  47. }
  48. llvm::SmallVector<llvm::StringRef> lines;
  49. llvm::StringRef(output).split(lines, "\n");
  50. return llvm::SmallVector<std::string_view>(lines.begin(), lines.end());
  51. }
  52. // Verify that the success and `fail_` prefix use correspond. Separately handle
  53. // both cases for clearer test failures.
  54. static auto CompareFailPrefix(llvm::StringRef filename, bool success) -> void {
  55. if (success) {
  56. EXPECT_FALSE(filename.starts_with("fail_"))
  57. << "`" << filename
  58. << "` succeeded; if success is expected, remove the `fail_` "
  59. "prefix.";
  60. } else {
  61. EXPECT_TRUE(filename.starts_with("fail_"))
  62. << "`" << filename
  63. << "` failed; if failure is expected, add the `fail_` prefix.";
  64. }
  65. }
  66. // Modes for GetBazelCommand.
  67. enum class BazelMode : uint8_t {
  68. Autoupdate,
  69. Dump,
  70. Test,
  71. };
  72. // Returns the requested bazel command string for the given execution mode.
  73. static auto GetBazelCommand(BazelMode mode, llvm::StringRef test_name)
  74. -> std::string {
  75. RawStringOstream args;
  76. const char* target = getenv("TEST_TARGET");
  77. args << "bazel " << ((mode == BazelMode::Test) ? "test" : "run") << " "
  78. << (target ? target : "<target>") << " ";
  79. switch (mode) {
  80. case BazelMode::Autoupdate:
  81. args << "-- --autoupdate ";
  82. break;
  83. case BazelMode::Dump:
  84. args << "-- --dump_output ";
  85. break;
  86. case BazelMode::Test:
  87. args << "--test_arg=";
  88. break;
  89. }
  90. args << "--file_tests=";
  91. args << test_name;
  92. return args.TakeStr();
  93. }
  94. // Runs the FileTestAutoupdater, returning the result.
  95. static auto RunAutoupdater(FileTestBase* test_base, const TestFile& test_file,
  96. bool dry_run) -> bool {
  97. if (!test_file.autoupdate_line_number) {
  98. return false;
  99. }
  100. llvm::SmallVector<llvm::StringRef> filenames;
  101. filenames.reserve(test_file.non_check_lines.size());
  102. if (test_file.has_splits) {
  103. // There are splits, so we provide an empty name for the first file.
  104. filenames.push_back({});
  105. }
  106. for (const auto& file : test_file.file_splits) {
  107. filenames.push_back(file.filename);
  108. }
  109. llvm::ArrayRef expected_filenames = filenames;
  110. if (filenames.size() > 1) {
  111. expected_filenames = expected_filenames.drop_front();
  112. }
  113. return FileTestAutoupdater(
  114. std::filesystem::absolute(test_base->test_name().str()),
  115. GetBazelCommand(BazelMode::Test, test_base->test_name()),
  116. GetBazelCommand(BazelMode::Dump, test_base->test_name()),
  117. test_file.input_content, filenames,
  118. *test_file.autoupdate_line_number, test_file.autoupdate_split,
  119. test_file.non_check_lines, test_file.actual_stdout,
  120. test_file.actual_stderr,
  121. test_base->GetDefaultFileRE(expected_filenames),
  122. test_base->GetLineNumberReplacements(expected_filenames),
  123. [&](std::string& line) {
  124. test_base->DoExtraCheckReplacements(line);
  125. })
  126. .Run(dry_run);
  127. }
  128. // Runs a test and compares output. This keeps output split by line so that
  129. // issues are a little easier to identify by the different line.
  130. auto FileTestBase::TestBody() -> void {
  131. // Add a crash trace entry with the single-file test command.
  132. std::string test_command = GetBazelCommand(BazelMode::Test, test_name_);
  133. llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str());
  134. llvm::errs() << "\nTo test this file alone, run:\n " << test_command
  135. << "\n\n";
  136. ErrorOr<TestFile> test_file =
  137. ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/false,
  138. absl::GetFlag(FLAGS_autoupdate));
  139. ASSERT_TRUE(test_file.ok()) << test_file.error();
  140. auto test_filename = std::filesystem::path(test_name_.str()).filename();
  141. // Check success/failure against `fail_` prefixes.
  142. if (test_file->run_result.per_file_success.empty()) {
  143. CompareFailPrefix(test_filename.string(), test_file->run_result.success);
  144. } else {
  145. bool require_overall_failure = false;
  146. for (const auto& [filename, success] :
  147. test_file->run_result.per_file_success) {
  148. CompareFailPrefix(filename, success);
  149. if (!success) {
  150. require_overall_failure = true;
  151. }
  152. }
  153. if (require_overall_failure) {
  154. EXPECT_FALSE(test_file->run_result.success)
  155. << "There is a per-file failure expectation, so the overall result "
  156. "should have been a failure.";
  157. } else {
  158. // Individual files all succeeded, so the prefix is enforced on the main
  159. // test file.
  160. CompareFailPrefix(test_filename.string(), test_file->run_result.success);
  161. }
  162. }
  163. // Check results. Include a reminder of the autoupdate command for any
  164. // stdout/stderr differences.
  165. std::string update_message;
  166. if (test_file->autoupdate_line_number) {
  167. update_message = llvm::formatv(
  168. "If these differences are expected, try the autoupdater:\n {0}",
  169. GetBazelCommand(BazelMode::Autoupdate, test_name_));
  170. } else {
  171. update_message =
  172. "If these differences are expected, content must be updated manually.";
  173. }
  174. SCOPED_TRACE(update_message);
  175. if (test_file->check_subset) {
  176. EXPECT_THAT(SplitOutput(test_file->actual_stdout),
  177. IsSupersetOf(test_file->expected_stdout));
  178. EXPECT_THAT(SplitOutput(test_file->actual_stderr),
  179. IsSupersetOf(test_file->expected_stderr));
  180. } else {
  181. EXPECT_THAT(SplitOutput(test_file->actual_stdout),
  182. ElementsAreArray(test_file->expected_stdout));
  183. EXPECT_THAT(SplitOutput(test_file->actual_stderr),
  184. ElementsAreArray(test_file->expected_stderr));
  185. }
  186. // If there are no other test failures, check if autoupdate would make
  187. // changes. We don't do this when there _are_ failures because the
  188. // SCOPED_TRACE already contains the autoupdate reminder.
  189. if (!HasFailure() && RunAutoupdater(this, *test_file, /*dry_run=*/true)) {
  190. ADD_FAILURE() << "Autoupdate would make changes to the file content.";
  191. }
  192. }
  193. auto FileTestBase::Autoupdate() -> ErrorOr<bool> {
  194. // Add a crash trace entry mentioning which file we're updating.
  195. std::string stack_trace_string =
  196. llvm::formatv("performing autoupdate for {0}", test_name_);
  197. llvm::PrettyStackTraceString stack_trace_entry(stack_trace_string.c_str());
  198. CARBON_ASSIGN_OR_RETURN(
  199. TestFile test_file,
  200. ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/false,
  201. absl::GetFlag(FLAGS_autoupdate)));
  202. return RunAutoupdater(this, test_file, /*dry_run=*/false);
  203. }
  204. auto FileTestBase::DumpOutput() -> ErrorOr<Success> {
  205. std::string banner(79, '=');
  206. banner.append("\n");
  207. llvm::errs() << banner << "= " << test_name_ << "\n";
  208. CARBON_ASSIGN_OR_RETURN(
  209. TestFile test_file,
  210. ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/true,
  211. absl::GetFlag(FLAGS_autoupdate)));
  212. llvm::errs() << banner << test_file.actual_stdout << banner
  213. << "= Exit with success: "
  214. << (test_file.run_result.success ? "true" : "false") << "\n"
  215. << banner;
  216. return Success();
  217. }
  218. auto FileTestBase::GetLineNumberReplacements(
  219. llvm::ArrayRef<llvm::StringRef> filenames)
  220. -> llvm::SmallVector<LineNumberReplacement> {
  221. return {{.has_file = true,
  222. .re = std::make_shared<RE2>(
  223. llvm::formatv(R"(({0}):(\d+)?)", llvm::join(filenames, "|"))),
  224. .line_formatv = R"({0})"}};
  225. }
  226. // Returns the tests to run.
  227. static auto GetTests() -> llvm::SmallVector<std::string> {
  228. // Prefer a user-specified list if present.
  229. auto specific_tests = absl::GetFlag(FLAGS_file_tests);
  230. if (!specific_tests.empty()) {
  231. return llvm::SmallVector<std::string>(specific_tests.begin(),
  232. specific_tests.end());
  233. }
  234. // Extracts tests from the target file.
  235. CARBON_CHECK(!absl::GetFlag(FLAGS_test_targets_file).empty(),
  236. "Missing --test_targets_file.");
  237. auto content = ReadFile(absl::GetFlag(FLAGS_test_targets_file));
  238. CARBON_CHECK(content.ok(), "{0}", content.error());
  239. llvm::SmallVector<std::string> all_tests;
  240. for (llvm::StringRef file_ref : llvm::split(*content, "\n")) {
  241. if (file_ref.empty()) {
  242. continue;
  243. }
  244. all_tests.push_back(file_ref.str());
  245. }
  246. return all_tests;
  247. }
  248. // Runs autoupdate for the given tests. This is multi-threaded to try to get a
  249. // little extra speed.
  250. static auto RunAutoupdate(llvm::StringRef exe_path,
  251. llvm::ArrayRef<std::string> tests,
  252. FileTestFactory& test_factory) -> int {
  253. llvm::CrashRecoveryContext::Enable();
  254. llvm::DefaultThreadPool pool(
  255. {.ThreadsRequested = absl::GetFlag(FLAGS_threads)});
  256. // Guard access to both `llvm::errs` and `crashed`.
  257. std::mutex mutex;
  258. bool crashed = false;
  259. for (const auto& test_name : tests) {
  260. pool.async([&test_factory, &mutex, &exe_path, &crashed, test_name] {
  261. // If any thread crashed, don't try running more.
  262. {
  263. std::unique_lock<std::mutex> lock(mutex);
  264. if (crashed) {
  265. return;
  266. }
  267. }
  268. // Use a crash recovery context to try to get a stack trace when
  269. // multiple threads may crash in parallel, which otherwise leads to the
  270. // program aborting without printing a stack trace.
  271. llvm::CrashRecoveryContext crc;
  272. crc.DumpStackAndCleanupOnFailure = true;
  273. bool thread_crashed = !crc.RunSafely([&] {
  274. std::unique_ptr<FileTestBase> test(
  275. test_factory.factory_fn(exe_path, &mutex, test_name));
  276. auto result = test->Autoupdate();
  277. std::unique_lock<std::mutex> lock(mutex);
  278. if (result.ok()) {
  279. llvm::errs() << (*result ? "!" : ".");
  280. } else {
  281. llvm::errs() << "\n" << result.error().message() << "\n";
  282. }
  283. });
  284. if (thread_crashed) {
  285. std::unique_lock<std::mutex> lock(mutex);
  286. crashed = true;
  287. }
  288. });
  289. }
  290. pool.wait();
  291. if (crashed) {
  292. // Abort rather than returning so that we don't get a LeakSanitizer report.
  293. // We expect to have leaked memory if one or more of our tests crashed.
  294. std::abort();
  295. }
  296. llvm::errs() << "\nDone!\n";
  297. return EXIT_SUCCESS;
  298. }
  299. // Implements main() within the Carbon::Testing namespace for convenience.
  300. static auto Main(int argc, char** argv) -> int {
  301. Carbon::InitLLVM init_llvm(argc, argv);
  302. testing::InitGoogleTest(&argc, argv);
  303. auto args = absl::ParseCommandLine(argc, argv);
  304. if (args.size() > 1) {
  305. llvm::errs() << "Unexpected arguments:";
  306. for (char* arg : llvm::ArrayRef(args).drop_front()) {
  307. llvm::errs() << " ";
  308. llvm::errs().write_escaped(arg);
  309. }
  310. llvm::errs() << "\n";
  311. return EXIT_FAILURE;
  312. }
  313. std::string exe_path = FindExecutablePath(argv[0]);
  314. // Tests might try to read from stdin. Ensure those reads fail by closing
  315. // stdin and reopening it as /dev/null. Note that STDIN_FILENO doesn't exist
  316. // on Windows, but POSIX requires it to be 0.
  317. if (std::error_code error =
  318. llvm::sys::Process::SafelyCloseFileDescriptor(0)) {
  319. llvm::errs() << "Unable to close standard input: " << error.message()
  320. << "\n";
  321. return EXIT_FAILURE;
  322. }
  323. if (std::error_code error =
  324. llvm::sys::Process::FixupStandardFileDescriptors()) {
  325. llvm::errs() << "Unable to correct standard file descriptors: "
  326. << error.message() << "\n";
  327. return EXIT_FAILURE;
  328. }
  329. if (absl::GetFlag(FLAGS_autoupdate) && absl::GetFlag(FLAGS_dump_output)) {
  330. llvm::errs() << "--autoupdate and --dump_output are mutually exclusive.\n";
  331. return EXIT_FAILURE;
  332. }
  333. llvm::SmallVector<std::string> tests = GetTests();
  334. auto test_factory = GetFileTestFactory();
  335. if (absl::GetFlag(FLAGS_autoupdate)) {
  336. return RunAutoupdate(exe_path, tests, test_factory);
  337. } else if (absl::GetFlag(FLAGS_dump_output)) {
  338. for (const auto& test_name : tests) {
  339. std::unique_ptr<FileTestBase> test(
  340. test_factory.factory_fn(exe_path, nullptr, test_name));
  341. auto result = test->DumpOutput();
  342. if (!result.ok()) {
  343. llvm::errs() << "\n" << result.error().message() << "\n";
  344. }
  345. }
  346. llvm::errs() << "\nDone!\n";
  347. return EXIT_SUCCESS;
  348. } else {
  349. for (const std::string& test_name : tests) {
  350. testing::RegisterTest(
  351. test_factory.name, test_name.c_str(), nullptr, test_name.c_str(),
  352. __FILE__, __LINE__,
  353. [&test_factory, &exe_path, test_name = test_name]() {
  354. return test_factory.factory_fn(exe_path, nullptr, test_name);
  355. });
  356. }
  357. return RUN_ALL_TESTS();
  358. }
  359. }
  360. } // namespace Carbon::Testing
  361. auto main(int argc, char** argv) -> int {
  362. return Carbon::Testing::Main(argc, argv);
  363. }