file_test_base.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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. ValidateRun();
  141. auto test_filename = std::filesystem::path(test_name_.str()).filename();
  142. // Check success/failure against `fail_` prefixes.
  143. if (test_file->run_result.per_file_success.empty()) {
  144. CompareFailPrefix(test_filename.string(), test_file->run_result.success);
  145. } else {
  146. bool require_overall_failure = false;
  147. for (const auto& [filename, success] :
  148. test_file->run_result.per_file_success) {
  149. CompareFailPrefix(filename, success);
  150. if (!success) {
  151. require_overall_failure = true;
  152. }
  153. }
  154. if (require_overall_failure) {
  155. EXPECT_FALSE(test_file->run_result.success)
  156. << "There is a per-file failure expectation, so the overall result "
  157. "should have been a failure.";
  158. } else {
  159. // Individual files all succeeded, so the prefix is enforced on the main
  160. // test file.
  161. CompareFailPrefix(test_filename.string(), test_file->run_result.success);
  162. }
  163. }
  164. // Check results. Include a reminder of the autoupdate command for any
  165. // stdout/stderr differences.
  166. std::string update_message;
  167. if (test_file->autoupdate_line_number) {
  168. update_message = llvm::formatv(
  169. "If these differences are expected, try the autoupdater:\n {0}",
  170. GetBazelCommand(BazelMode::Autoupdate, test_name_));
  171. } else {
  172. update_message =
  173. "If these differences are expected, content must be updated manually.";
  174. }
  175. SCOPED_TRACE(update_message);
  176. if (test_file->check_subset) {
  177. EXPECT_THAT(SplitOutput(test_file->actual_stdout),
  178. IsSupersetOf(test_file->expected_stdout));
  179. EXPECT_THAT(SplitOutput(test_file->actual_stderr),
  180. IsSupersetOf(test_file->expected_stderr));
  181. } else {
  182. EXPECT_THAT(SplitOutput(test_file->actual_stdout),
  183. ElementsAreArray(test_file->expected_stdout));
  184. EXPECT_THAT(SplitOutput(test_file->actual_stderr),
  185. ElementsAreArray(test_file->expected_stderr));
  186. }
  187. // If there are no other test failures, check if autoupdate would make
  188. // changes. We don't do this when there _are_ failures because the
  189. // SCOPED_TRACE already contains the autoupdate reminder.
  190. if (!HasFailure() && RunAutoupdater(this, *test_file, /*dry_run=*/true)) {
  191. ADD_FAILURE() << "Autoupdate would make changes to the file content.";
  192. }
  193. }
  194. auto FileTestBase::Autoupdate() -> ErrorOr<bool> {
  195. // Add a crash trace entry mentioning which file we're updating.
  196. std::string stack_trace_string =
  197. llvm::formatv("performing autoupdate for {0}", test_name_);
  198. llvm::PrettyStackTraceString stack_trace_entry(stack_trace_string.c_str());
  199. CARBON_ASSIGN_OR_RETURN(
  200. TestFile test_file,
  201. ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/false,
  202. absl::GetFlag(FLAGS_autoupdate)));
  203. return RunAutoupdater(this, test_file, /*dry_run=*/false);
  204. }
  205. auto FileTestBase::DumpOutput() -> ErrorOr<Success> {
  206. std::string banner(79, '=');
  207. banner.append("\n");
  208. llvm::errs() << banner << "= " << test_name_ << "\n";
  209. CARBON_ASSIGN_OR_RETURN(
  210. TestFile test_file,
  211. ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/true,
  212. absl::GetFlag(FLAGS_autoupdate)));
  213. llvm::errs() << banner << test_file.actual_stdout << banner
  214. << "= Exit with success: "
  215. << (test_file.run_result.success ? "true" : "false") << "\n"
  216. << banner;
  217. return Success();
  218. }
  219. auto FileTestBase::GetLineNumberReplacements(
  220. llvm::ArrayRef<llvm::StringRef> filenames)
  221. -> llvm::SmallVector<LineNumberReplacement> {
  222. return {{.has_file = true,
  223. .re = std::make_shared<RE2>(
  224. llvm::formatv(R"(({0}):(\d+)?)", llvm::join(filenames, "|"))),
  225. .line_formatv = R"({0})"}};
  226. }
  227. // Returns the tests to run.
  228. static auto GetTests() -> llvm::SmallVector<std::string> {
  229. // Prefer a user-specified list if present.
  230. auto specific_tests = absl::GetFlag(FLAGS_file_tests);
  231. if (!specific_tests.empty()) {
  232. return llvm::SmallVector<std::string>(specific_tests.begin(),
  233. specific_tests.end());
  234. }
  235. // Extracts tests from the target file.
  236. CARBON_CHECK(!absl::GetFlag(FLAGS_test_targets_file).empty(),
  237. "Missing --test_targets_file.");
  238. auto content = ReadFile(absl::GetFlag(FLAGS_test_targets_file));
  239. CARBON_CHECK(content.ok(), "{0}", content.error());
  240. llvm::SmallVector<std::string> all_tests;
  241. for (llvm::StringRef file_ref : llvm::split(*content, "\n")) {
  242. if (file_ref.empty()) {
  243. continue;
  244. }
  245. all_tests.push_back(file_ref.str());
  246. }
  247. return all_tests;
  248. }
  249. // Runs autoupdate for the given tests. This is multi-threaded to try to get a
  250. // little extra speed.
  251. static auto RunAutoupdate(llvm::StringRef exe_path,
  252. llvm::ArrayRef<std::string> tests,
  253. FileTestFactory& test_factory) -> int {
  254. llvm::CrashRecoveryContext::Enable();
  255. llvm::DefaultThreadPool pool(
  256. {.ThreadsRequested = absl::GetFlag(FLAGS_threads)});
  257. // Guard access to both `llvm::errs` and `crashed`.
  258. std::mutex mutex;
  259. bool crashed = false;
  260. for (const auto& test_name : tests) {
  261. pool.async([&test_factory, &mutex, &exe_path, &crashed, test_name] {
  262. // If any thread crashed, don't try running more.
  263. {
  264. std::unique_lock<std::mutex> lock(mutex);
  265. if (crashed) {
  266. return;
  267. }
  268. }
  269. // Use a crash recovery context to try to get a stack trace when
  270. // multiple threads may crash in parallel, which otherwise leads to the
  271. // program aborting without printing a stack trace.
  272. llvm::CrashRecoveryContext crc;
  273. crc.DumpStackAndCleanupOnFailure = true;
  274. bool thread_crashed = !crc.RunSafely([&] {
  275. std::unique_ptr<FileTestBase> test(
  276. test_factory.factory_fn(exe_path, &mutex, test_name));
  277. auto result = test->Autoupdate();
  278. std::unique_lock<std::mutex> lock(mutex);
  279. if (result.ok()) {
  280. llvm::errs() << (*result ? "!" : ".");
  281. } else {
  282. llvm::errs() << "\n" << result.error().message() << "\n";
  283. }
  284. });
  285. if (thread_crashed) {
  286. std::unique_lock<std::mutex> lock(mutex);
  287. crashed = true;
  288. }
  289. });
  290. }
  291. pool.wait();
  292. if (crashed) {
  293. // Abort rather than returning so that we don't get a LeakSanitizer report.
  294. // We expect to have leaked memory if one or more of our tests crashed.
  295. std::abort();
  296. }
  297. llvm::errs() << "\nDone!\n";
  298. return EXIT_SUCCESS;
  299. }
  300. // Implements main() within the Carbon::Testing namespace for convenience.
  301. static auto Main(int argc, char** argv) -> int {
  302. Carbon::InitLLVM init_llvm(argc, argv);
  303. testing::InitGoogleTest(&argc, argv);
  304. auto args = absl::ParseCommandLine(argc, argv);
  305. if (args.size() > 1) {
  306. llvm::errs() << "Unexpected arguments:";
  307. for (char* arg : llvm::ArrayRef(args).drop_front()) {
  308. llvm::errs() << " ";
  309. llvm::errs().write_escaped(arg);
  310. }
  311. llvm::errs() << "\n";
  312. return EXIT_FAILURE;
  313. }
  314. std::string exe_path = FindExecutablePath(argv[0]);
  315. // Tests might try to read from stdin. Ensure those reads fail by closing
  316. // stdin and reopening it as /dev/null. Note that STDIN_FILENO doesn't exist
  317. // on Windows, but POSIX requires it to be 0.
  318. if (std::error_code error =
  319. llvm::sys::Process::SafelyCloseFileDescriptor(0)) {
  320. llvm::errs() << "Unable to close standard input: " << error.message()
  321. << "\n";
  322. return EXIT_FAILURE;
  323. }
  324. if (std::error_code error =
  325. llvm::sys::Process::FixupStandardFileDescriptors()) {
  326. llvm::errs() << "Unable to correct standard file descriptors: "
  327. << error.message() << "\n";
  328. return EXIT_FAILURE;
  329. }
  330. if (absl::GetFlag(FLAGS_autoupdate) && absl::GetFlag(FLAGS_dump_output)) {
  331. llvm::errs() << "--autoupdate and --dump_output are mutually exclusive.\n";
  332. return EXIT_FAILURE;
  333. }
  334. llvm::SmallVector<std::string> tests = GetTests();
  335. auto test_factory = GetFileTestFactory();
  336. if (absl::GetFlag(FLAGS_autoupdate)) {
  337. return RunAutoupdate(exe_path, tests, test_factory);
  338. } else if (absl::GetFlag(FLAGS_dump_output)) {
  339. for (const auto& test_name : tests) {
  340. std::unique_ptr<FileTestBase> test(
  341. test_factory.factory_fn(exe_path, nullptr, test_name));
  342. auto result = test->DumpOutput();
  343. if (!result.ok()) {
  344. llvm::errs() << "\n" << result.error().message() << "\n";
  345. }
  346. }
  347. llvm::errs() << "\nDone!\n";
  348. return EXIT_SUCCESS;
  349. } else {
  350. for (const std::string& test_name : tests) {
  351. testing::RegisterTest(
  352. test_factory.name, test_name.c_str(), nullptr, test_name.c_str(),
  353. __FILE__, __LINE__,
  354. [&test_factory, &exe_path, test_name = test_name]() {
  355. return test_factory.factory_fn(exe_path, nullptr, test_name);
  356. });
  357. }
  358. return RUN_ALL_TESTS();
  359. }
  360. }
  361. } // namespace Carbon::Testing
  362. auto main(int argc, char** argv) -> int {
  363. return Carbon::Testing::Main(argc, argv);
  364. }