file_test_base_test.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include "common/ostream.h"
  8. #include "llvm/ADT/StringExtras.h"
  9. #include "llvm/ADT/StringSwitch.h"
  10. #include "llvm/Support/FormatVariadic.h"
  11. namespace Carbon::Testing {
  12. namespace {
  13. class FileTestBaseTest : public FileTestBase {
  14. public:
  15. FileTestBaseTest(llvm::StringRef /*exe_path*/, std::mutex* output_mutex,
  16. llvm::StringRef test_name)
  17. : FileTestBase(output_mutex, test_name) {}
  18. auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
  19. llvm::vfs::InMemoryFileSystem& fs, llvm::raw_pwrite_stream& stdout,
  20. llvm::raw_pwrite_stream& stderr) -> ErrorOr<RunResult> override;
  21. auto GetArgReplacements() -> llvm::StringMap<std::string> override {
  22. return {{"replacement", "replaced"}};
  23. }
  24. auto GetDefaultArgs() -> llvm::SmallVector<std::string> override {
  25. return {"default_args", "%s"};
  26. }
  27. auto GetDefaultFileRE(llvm::ArrayRef<llvm::StringRef> filenames)
  28. -> std::optional<RE2> override {
  29. return std::make_optional<RE2>(
  30. llvm::formatv(R"(file: ({0}))", llvm::join(filenames, "|")));
  31. }
  32. auto GetLineNumberReplacements(llvm::ArrayRef<llvm::StringRef> filenames)
  33. -> llvm::SmallVector<LineNumberReplacement> override {
  34. auto replacements = FileTestBase::GetLineNumberReplacements(filenames);
  35. auto filename = std::filesystem::path(test_name().str()).filename();
  36. if (llvm::StringRef(filename).starts_with("file_only_re_")) {
  37. replacements.push_back({.has_file = false,
  38. .re = std::make_shared<RE2>(R"(line: (\d+))"),
  39. .line_formatv = "{0}"});
  40. }
  41. return replacements;
  42. }
  43. };
  44. // Prints arguments so that they can be validated in tests.
  45. static auto PrintArgs(llvm::ArrayRef<llvm::StringRef> args,
  46. llvm::raw_pwrite_stream& stdout) -> void {
  47. llvm::ListSeparator sep;
  48. stdout << args.size() << " args: ";
  49. for (auto arg : args) {
  50. stdout << sep << "`" << arg << "`";
  51. }
  52. stdout << "\n";
  53. }
  54. // Verifies arguments are well-structured, and returns the files in them.
  55. static auto GetFilesFromArgs(llvm::ArrayRef<llvm::StringRef> args,
  56. llvm::vfs::InMemoryFileSystem& fs)
  57. -> ErrorOr<llvm::ArrayRef<llvm::StringRef>> {
  58. if (args.empty() || args.front() != "default_args") {
  59. return ErrorBuilder() << "missing `default_args` argument";
  60. }
  61. args = args.drop_front();
  62. for (auto arg : args) {
  63. if (!fs.exists(arg)) {
  64. return ErrorBuilder() << "Missing file: " << arg;
  65. }
  66. }
  67. return args;
  68. }
  69. // Parameters used to by individual test handlers, for easy value forwarding.
  70. struct TestParams {
  71. // These are the arguments to `Run()`.
  72. llvm::vfs::InMemoryFileSystem& fs;
  73. llvm::raw_pwrite_stream& stdout;
  74. llvm::raw_pwrite_stream& stderr;
  75. // This is assigned after construction.
  76. llvm::ArrayRef<llvm::StringRef> files;
  77. };
  78. // Does printing and returns expected results for alternating_files.carbon.
  79. static auto TestAlternatingFiles(TestParams& params)
  80. -> ErrorOr<FileTestBaseTest::RunResult> {
  81. params.stdout << "unattached message 1\n"
  82. << "a.carbon:2: message 2\n"
  83. << "b.carbon:5: message 3\n"
  84. << "a.carbon:2: message 4\n"
  85. << "b.carbon:5: message 5\n"
  86. << "unattached message 6\n";
  87. params.stderr << "unattached message 1\n"
  88. << "a.carbon:2: message 2\n"
  89. << "b.carbon:5: message 3\n"
  90. << "a.carbon:2: message 4\n"
  91. << "b.carbon:5: message 5\n"
  92. << "unattached message 6\n";
  93. return {{.success = true}};
  94. }
  95. // Does printing and returns expected results for capture_console_output.carbon.
  96. static auto TestCaptureConsoleOutput(TestParams& params)
  97. -> ErrorOr<FileTestBaseTest::RunResult> {
  98. llvm::errs() << "llvm::errs\n";
  99. params.stderr << "params.stderr\n";
  100. llvm::outs() << "llvm::outs\n";
  101. params.stdout << "params.stdout\n";
  102. return {{.success = true}};
  103. }
  104. // Does printing and returns expected results for example.carbon.
  105. static auto TestExample(TestParams& params)
  106. -> ErrorOr<FileTestBaseTest::RunResult> {
  107. int delta_line = 10;
  108. params.stdout << "something\n"
  109. << "\n"
  110. << "example.carbon:" << delta_line + 1 << ": Line delta\n"
  111. << "example.carbon:" << delta_line << ": Negative line delta\n"
  112. << "+*[]{}\n"
  113. << "Foo baz\n";
  114. return {{.success = true}};
  115. }
  116. // Does printing and returns expected results for fail_example.carbon.
  117. static auto TestFailExample(TestParams& params)
  118. -> ErrorOr<FileTestBaseTest::RunResult> {
  119. params.stderr << "Oops\n";
  120. return {{.success = false}};
  121. }
  122. // Does printing and returns expected results for
  123. // file_only_re_multi_file.carbon.
  124. static auto TestFileOnlyREMultiFile(TestParams& params)
  125. -> ErrorOr<FileTestBaseTest::RunResult> {
  126. int msg_count = 0;
  127. params.stdout << "unattached message " << ++msg_count << "\n"
  128. << "file: a.carbon\n"
  129. << "unattached message " << ++msg_count << "\n"
  130. << "line: 3: attached message " << ++msg_count << "\n"
  131. << "unattached message " << ++msg_count << "\n"
  132. << "line: 8: late message " << ++msg_count << "\n"
  133. << "unattached message " << ++msg_count << "\n"
  134. << "file: b.carbon\n"
  135. << "line: 2: attached message " << ++msg_count << "\n"
  136. << "unattached message " << ++msg_count << "\n"
  137. << "line: 7: late message " << ++msg_count << "\n"
  138. << "unattached message " << ++msg_count << "\n";
  139. return {{.success = true}};
  140. }
  141. // Does printing and returns expected results for file_only_re_one_file.carbon.
  142. static auto TestFileOnlyREOneFile(TestParams& params)
  143. -> ErrorOr<FileTestBaseTest::RunResult> {
  144. params.stdout << "unattached message 1\n"
  145. << "file: file_only_re_one_file.carbon\n"
  146. << "line: 1\n"
  147. << "unattached message 2\n";
  148. return {{.success = true}};
  149. }
  150. // Does printing and returns expected results for unattached_multi_file.carbon.
  151. static auto TestUnattachedMultiFile(TestParams& params)
  152. -> ErrorOr<FileTestBaseTest::RunResult> {
  153. params.stdout << "unattached message 1\n"
  154. << "unattached message 2\n";
  155. params.stderr << "unattached message 3\n"
  156. << "unattached message 4\n";
  157. return {{.success = true}};
  158. }
  159. // Does printing and returns expected results for:
  160. // - fail_multi_success_overall_fail.carbon
  161. // - multi_success.carbon
  162. // - multi_success_and_fail.carbon
  163. //
  164. // Parameters indicate overall and per-file success.
  165. static auto HandleMultiSuccessTests(bool overall, bool a, bool b)
  166. -> ErrorOr<FileTestBaseTest::RunResult> {
  167. FileTestBaseTest::RunResult result = {.success = overall};
  168. result.per_file_success.push_back({a ? "a.carbon" : "fail_a.carbon", a});
  169. result.per_file_success.push_back({b ? "b.carbon" : "fail_b.carbon", b});
  170. return result;
  171. }
  172. // Echoes back non-comment file content. Used for default file handling.
  173. static auto EchoFileContent(TestParams& params)
  174. -> ErrorOr<FileTestBaseTest::RunResult> {
  175. // By default, echo non-comment content of files back.
  176. for (auto test_file : params.files) {
  177. // Describe file contents to stdout to validate splitting.
  178. auto file = params.fs.getBufferForFile(test_file, /*FileSize=*/-1,
  179. /*RequiresNullTerminator=*/false);
  180. if (file.getError()) {
  181. return Error(file.getError().message());
  182. }
  183. llvm::StringRef buffer = file.get()->getBuffer();
  184. for (int line_number = 1; !buffer.empty(); ++line_number) {
  185. auto [line, remainder] = buffer.split('\n');
  186. if (!line.empty() && !line.starts_with("//")) {
  187. params.stdout << test_file << ":" << line_number << ": " << line
  188. << "\n";
  189. }
  190. buffer = remainder;
  191. }
  192. }
  193. return {{.success = true}};
  194. }
  195. auto FileTestBaseTest::Run(const llvm::SmallVector<llvm::StringRef>& test_args,
  196. llvm::vfs::InMemoryFileSystem& fs,
  197. llvm::raw_pwrite_stream& stdout,
  198. llvm::raw_pwrite_stream& stderr)
  199. -> ErrorOr<RunResult> {
  200. PrintArgs(test_args, stdout);
  201. auto filename = std::filesystem::path(test_name().str()).filename();
  202. if (filename == "args.carbon") {
  203. // 'args.carbon' has custom arguments, so don't do regular argument
  204. // validation for it.
  205. return {{.success = true}};
  206. }
  207. // Choose the test function based on filename.
  208. auto test_fn =
  209. llvm::StringSwitch<std::function<ErrorOr<RunResult>(TestParams&)>>(
  210. filename.string())
  211. .Case("alternating_files.carbon", &TestAlternatingFiles)
  212. .Case("capture_console_output.carbon", &TestCaptureConsoleOutput)
  213. .Case("example.carbon", &TestExample)
  214. .Case("fail_example.carbon", &TestFailExample)
  215. .Case("file_only_re_one_file.carbon", &TestFileOnlyREOneFile)
  216. .Case("file_only_re_multi_file.carbon", &TestFileOnlyREMultiFile)
  217. .Case("unattached_multi_file.carbon", &TestUnattachedMultiFile)
  218. .Case("fail_multi_success_overall_fail.carbon",
  219. [&](TestParams&) {
  220. return HandleMultiSuccessTests(/*overall=*/false, /*a=*/true,
  221. /*b=*/true);
  222. })
  223. .Case("multi_success.carbon",
  224. [&](TestParams&) {
  225. return HandleMultiSuccessTests(/*overall=*/true, /*a=*/true,
  226. /*b=*/true);
  227. })
  228. .Case("multi_success_and_fail.carbon",
  229. [&](TestParams&) {
  230. return HandleMultiSuccessTests(/*overall=*/false, /*a=*/true,
  231. /*b=*/false);
  232. })
  233. .Default(&EchoFileContent);
  234. // Call the appropriate test function for the file.
  235. TestParams params = {.fs = fs, .stdout = stdout, .stderr = stderr};
  236. CARBON_ASSIGN_OR_RETURN(params.files, GetFilesFromArgs(test_args, fs));
  237. return test_fn(params);
  238. }
  239. } // namespace
  240. CARBON_FILE_TEST_FACTORY(FileTestBaseTest)
  241. } // namespace Carbon::Testing