file_test_base.h 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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. #ifndef CARBON_TESTING_FILE_TEST_FILE_TEST_BASE_H_
  5. #define CARBON_TESTING_FILE_TEST_FILE_TEST_BASE_H_
  6. #include <gmock/gmock.h>
  7. #include <gtest/gtest.h>
  8. #include <filesystem>
  9. #include <functional>
  10. #include <vector>
  11. #include "llvm/ADT/SmallVector.h"
  12. #include "llvm/ADT/StringRef.h"
  13. #include "llvm/Support/raw_os_ostream.h"
  14. #include "llvm/Support/raw_ostream.h"
  15. namespace Carbon::Testing {
  16. // A framework for testing files. Children implement `RegisterTestFiles` with
  17. // calls to `RegisterTests` using a factory that constructs the child.
  18. // `RunWithFiles` must also be implemented and will be called as part of
  19. // individual test executions. This framework includes a `main` implementation,
  20. // so users must not provide one.
  21. //
  22. // Tests should have CHECK lines similar to `FileCheck` syntax:
  23. // https://llvm.org/docs/CommandGuide/FileCheck.html
  24. //
  25. // Special nuances are that stdout and stderr will look like `// CHECK:STDOUT:
  26. // ...` and `// CHECK:STDERR: ...` respectively. `[[@LINE+offset]` and
  27. // `{{regex}}` syntaxes should also work.
  28. //
  29. // `autoupdate_testdata.py` automatically constructs compatible lines.
  30. class FileTestBase : public testing::Test {
  31. public:
  32. struct TestFile {
  33. explicit TestFile(std::string filename, llvm::StringRef content)
  34. : filename(std::move(filename)), content(content) {}
  35. friend void PrintTo(const TestFile& f, std::ostream* os) {
  36. // Print content escaped.
  37. llvm::raw_os_ostream os_wrap(*os);
  38. os_wrap << "TestFile(" << f.filename << ", \"";
  39. os_wrap.write_escaped(f.content);
  40. os_wrap << "\")";
  41. }
  42. std::string filename;
  43. llvm::StringRef content;
  44. };
  45. explicit FileTestBase(const std::filesystem::path& path) : path_(&path) {}
  46. // Used by children to register tests with gtest.
  47. static auto RegisterTests(
  48. const char* fixture_label,
  49. const llvm::SmallVector<std::filesystem::path>& paths,
  50. std::function<FileTestBase*(const std::filesystem::path&)> factory)
  51. -> void;
  52. template <typename FileTestChildT>
  53. static auto RegisterTests(
  54. const char* fixture_label,
  55. const llvm::SmallVector<std::filesystem::path>& paths) -> void {
  56. RegisterTests(fixture_label, paths, [](const std::filesystem::path& path) {
  57. return new FileTestChildT(path);
  58. });
  59. }
  60. // Implemented by children to run the test. Called by the TestBody
  61. // implementation, which will validate stdout and stderr. The return value
  62. // should be false when "fail_" is in the filename.
  63. virtual auto RunWithFiles(const llvm::SmallVector<TestFile>& test_files,
  64. llvm::raw_pwrite_stream& stdout,
  65. llvm::raw_pwrite_stream& stderr) -> bool = 0;
  66. // Runs a test and compares output. This keeps output split by line so that
  67. // issues are a little easier to identify by the different line.
  68. auto TestBody() -> void final;
  69. // Returns the full path of the file being tested.
  70. auto path() -> const std::filesystem::path& { return *path_; };
  71. private:
  72. // Processes the test input, producing test files and expected output.
  73. auto ProcessTestFile(
  74. llvm::StringRef file_content, llvm::SmallVector<TestFile>& test_files,
  75. llvm::SmallVector<testing::Matcher<std::string>>& expected_stdout,
  76. llvm::SmallVector<testing::Matcher<std::string>>& expected_stderr)
  77. -> void;
  78. // Transforms an expectation on a given line from `FileCheck` syntax into a
  79. // standard regex matcher.
  80. static auto TransformExpectation(int line_index, llvm::StringRef in)
  81. -> testing::Matcher<std::string>;
  82. const std::filesystem::path* path_;
  83. };
  84. // Must be implemented by the individual file_test to initialize tests.
  85. extern auto RegisterFileTests(
  86. const llvm::SmallVector<std::filesystem::path>& paths) -> void;
  87. } // namespace Carbon::Testing
  88. #endif // CARBON_TESTING_FILE_TEST_FILE_TEST_BASE_H_