driver_test.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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 "toolchain/driver/driver.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include <filesystem>
  8. #include <fstream>
  9. #include <optional>
  10. #include <string>
  11. #include <system_error>
  12. #include <utility>
  13. #include "common/error_test_helpers.h"
  14. #include "common/filesystem.h"
  15. #include "common/raw_string_ostream.h"
  16. #include "llvm/ADT/ScopeExit.h"
  17. #include "llvm/Object/Binary.h"
  18. #include "llvm/Support/Error.h"
  19. #include "llvm/Support/FormatVariadic.h"
  20. #include "llvm/Support/JSON.h"
  21. #include "testing/base/file_helpers.h"
  22. #include "testing/base/global_exe_path.h"
  23. #include "toolchain/testing/yaml_test_helpers.h"
  24. namespace Carbon {
  25. namespace {
  26. using ::testing::_;
  27. using ::testing::ContainsRegex;
  28. using ::testing::HasSubstr;
  29. using Testing::IsSuccess;
  30. using ::testing::Ne;
  31. using ::testing::NotNull;
  32. using ::testing::StrEq;
  33. namespace Yaml = ::Carbon::Testing::Yaml;
  34. class DriverTest : public testing::Test {
  35. public:
  36. DriverTest()
  37. : installation_(
  38. InstallPaths::MakeForBazelRunfiles(Testing::GetExePath())),
  39. driver_(fs_, &installation_, /*input_stream=*/nullptr,
  40. &test_output_stream_, &test_error_stream_) {
  41. test_tmpdir_ = Testing::GetTempDirectory();
  42. }
  43. auto MakeTestFile(llvm::StringRef text,
  44. llvm::StringRef filename = "test_file.carbon")
  45. -> llvm::StringRef {
  46. fs_->addFile(filename, /*ModificationTime=*/0,
  47. llvm::MemoryBuffer::getMemBuffer(text));
  48. return filename;
  49. }
  50. // Makes a temp directory and changes the working directory to it. Returns an
  51. // LLVM `scope_exit` that will restore the working directory and remove the
  52. // temporary directory (and everything it contains) when destroyed.
  53. auto ScopedTempWorkingDir() {
  54. // Save our current working directory.
  55. std::error_code ec;
  56. auto original_dir = std::filesystem::current_path(ec);
  57. CARBON_CHECK(!ec, "{0}", ec.message());
  58. Driver original_driver = std::move(driver_);
  59. const auto* unit_test = ::testing::UnitTest::GetInstance();
  60. const auto* test_info = unit_test->current_test_info();
  61. std::filesystem::path test_dir = test_tmpdir_.append(
  62. llvm::formatv("{0}_{1}", test_info->test_suite_name(),
  63. test_info->name())
  64. .str());
  65. std::filesystem::create_directory(test_dir, ec);
  66. CARBON_CHECK(!ec, "Could not create test working dir '{0}': {1}", test_dir,
  67. ec.message());
  68. std::filesystem::current_path(test_dir, ec);
  69. CARBON_CHECK(!ec, "Could not change the current working dir to '{0}': {1}",
  70. test_dir, ec.message());
  71. // Build an overlay filesystem between the in-memory one and this new
  72. // directory.
  73. llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> overlay_fs =
  74. new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem());
  75. overlay_fs->pushOverlay(fs_);
  76. // Rebuild the driver around this filesystem.
  77. driver_ = Driver(overlay_fs, &installation_, /*input_stream=*/nullptr,
  78. &test_output_stream_, &test_error_stream_);
  79. return llvm::scope_exit([this, original_dir, original_driver, test_dir] {
  80. std::error_code ec;
  81. std::filesystem::current_path(original_dir, ec);
  82. CARBON_CHECK(!ec,
  83. "Could not change the current working dir to '{0}': {1}",
  84. original_dir, ec.message());
  85. driver_ = original_driver;
  86. std::filesystem::remove_all(test_dir, ec);
  87. CARBON_CHECK(!ec, "Could not remove the test working dir '{0}': {1}",
  88. test_dir, ec.message());
  89. });
  90. }
  91. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs_ =
  92. new llvm::vfs::InMemoryFileSystem;
  93. const InstallPaths installation_;
  94. RawStringOstream test_output_stream_;
  95. RawStringOstream test_error_stream_;
  96. // Some tests work directly with files in the test temporary directory.
  97. std::filesystem::path test_tmpdir_;
  98. Driver driver_;
  99. };
  100. TEST_F(DriverTest, BadCommandErrors) {
  101. EXPECT_FALSE(driver_.RunCommand({}).success);
  102. EXPECT_THAT(test_error_stream_.TakeStr(), HasSubstr("error:"));
  103. EXPECT_FALSE(driver_.RunCommand({"foo"}).success);
  104. EXPECT_THAT(test_error_stream_.TakeStr(), HasSubstr("error:"));
  105. EXPECT_FALSE(driver_.RunCommand({"foo --bar --baz"}).success);
  106. EXPECT_THAT(test_error_stream_.TakeStr(), HasSubstr("error:"));
  107. }
  108. TEST_F(DriverTest, CompileCommandErrors) {
  109. // No input file. This error message is important so check all of it.
  110. EXPECT_FALSE(driver_.RunCommand({"compile"}).success);
  111. EXPECT_THAT(
  112. test_error_stream_.TakeStr(),
  113. StrEq("error: not all required positional arguments were provided; first "
  114. "missing and required positional argument: `FILE`\n"));
  115. // Pass non-existing file
  116. EXPECT_FALSE(driver_
  117. .RunCommand({"compile", "--dump-mem-usage",
  118. "non-existing-file.carbon"})
  119. .success);
  120. EXPECT_THAT(
  121. test_error_stream_.TakeStr(),
  122. ContainsRegex("No such file or directory[\\n]*non-existing-file.carbon"));
  123. // Flush output stream, because it's ok that it's not empty here.
  124. test_output_stream_.TakeStr();
  125. // Invalid output filename. No reliably error message here.
  126. // TODO: Likely want a different filename on Windows.
  127. auto empty_file = MakeTestFile("");
  128. EXPECT_FALSE(driver_
  129. .RunCommand({"compile", "--no-prelude-import",
  130. "--output=/dev/empty", empty_file})
  131. .success);
  132. EXPECT_THAT(test_error_stream_.TakeStr(),
  133. ContainsRegex("error: .*/dev/empty.*"));
  134. }
  135. TEST_F(DriverTest, DumpTokens) {
  136. auto file = MakeTestFile("Hello World");
  137. EXPECT_TRUE(driver_
  138. .RunCommand({"compile", "--no-prelude-import", "--phase=lex",
  139. "--dump-tokens", file})
  140. .success);
  141. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  142. // Verify there is output without examining it.
  143. EXPECT_THAT(Yaml::Value::FromText(test_output_stream_.TakeStr()),
  144. Yaml::IsYaml(_));
  145. }
  146. TEST_F(DriverTest, DumpParseTree) {
  147. auto file = MakeTestFile("var v: () = ();");
  148. EXPECT_TRUE(driver_
  149. .RunCommand({"compile", "--no-prelude-import",
  150. "--phase=parse", "--dump-parse-tree", file})
  151. .success);
  152. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  153. // Verify there is output without examining it.
  154. EXPECT_THAT(Yaml::Value::FromText(test_output_stream_.TakeStr()),
  155. Yaml::IsYaml(_));
  156. }
  157. TEST_F(DriverTest, StdoutOutput) {
  158. // Use explicit filenames so we can look for those to validate output.
  159. MakeTestFile("fn Run() {}", "test.carbon");
  160. EXPECT_TRUE(driver_
  161. .RunCommand({"compile", "--no-prelude-import", "--output=-",
  162. "test.carbon"})
  163. .success);
  164. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  165. // The default is textual assembly.
  166. EXPECT_THAT(test_output_stream_.TakeStr(), ContainsRegex("main:"));
  167. EXPECT_TRUE(driver_
  168. .RunCommand({"compile", "--no-prelude-import", "--output=-",
  169. "--force-obj-output", "test.carbon"})
  170. .success);
  171. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  172. std::string output = test_output_stream_.TakeStr();
  173. auto result =
  174. llvm::object::createBinary(llvm::MemoryBufferRef(output, "test_output"));
  175. if (auto error = result.takeError()) {
  176. FAIL() << toString(std::move(error));
  177. }
  178. EXPECT_TRUE(result->get()->isObject());
  179. }
  180. TEST_F(DriverTest, FileOutput) {
  181. auto scope = ScopedTempWorkingDir();
  182. // Use explicit filenames as the default output filename is computed from
  183. // this, and we can use this to validate output.
  184. MakeTestFile("fn Run() {}", "test.carbon");
  185. // Object output (the default) uses `.o`.
  186. // TODO: This should actually reflect the platform defaults.
  187. EXPECT_TRUE(
  188. driver_.RunCommand({"compile", "--no-prelude-import", "test.carbon"})
  189. .success);
  190. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  191. // Ensure we wrote an object file of some form with the correct name.
  192. auto result = llvm::object::createBinary("test.o");
  193. if (auto error = result.takeError()) {
  194. FAIL() << toString(std::move(error));
  195. }
  196. EXPECT_TRUE(result->getBinary()->isObject());
  197. // Assembly output uses `.s`.
  198. // TODO: This should actually reflect the platform defaults.
  199. EXPECT_TRUE(driver_
  200. .RunCommand({"compile", "--no-prelude-import", "--asm-output",
  201. "test.carbon"})
  202. .success);
  203. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  204. // TODO: This may need to be tailored to other assembly formats.
  205. EXPECT_THAT(*Testing::ReadFile("test.s"), ContainsRegex("main:"));
  206. }
  207. TEST_F(DriverTest, Link) {
  208. auto scope = ScopedTempWorkingDir();
  209. // First compile a file to get a linkable object.
  210. MakeTestFile("fn Run() {}", "test.carbon");
  211. ASSERT_TRUE(
  212. driver_.RunCommand({"compile", "--no-prelude-import", "test.carbon"})
  213. .success)
  214. << test_error_stream_.TakeStr();
  215. // Now link this into a binary. Note that we suppress building runtimes on
  216. // demand here as no runtimes should be needed for the empty program.
  217. EXPECT_TRUE(driver_
  218. .RunCommand({"--no-build-runtimes", "link", "--output=test",
  219. "test.o"})
  220. .success);
  221. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  222. // Ensure we wrote an executable file of some form with the correct name.
  223. // TODO: We may need to update this if we implicitly synthesize a
  224. // platform-specific `.exe` suffix or something similar.
  225. auto result = llvm::object::createBinary("test");
  226. if (auto error = result.takeError()) {
  227. FAIL() << toString(std::move(error));
  228. }
  229. // Executables are also classified as object files.
  230. EXPECT_TRUE(result->getBinary()->isObject());
  231. }
  232. TEST_F(DriverTest, ConfigJson) {
  233. EXPECT_TRUE(driver_.RunCommand({"config", "--json"}).success);
  234. EXPECT_THAT(test_error_stream_.TakeStr(), StrEq(""));
  235. // Make sure the output parses as JSON.
  236. std::string output = test_output_stream_.TakeStr();
  237. llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(output);
  238. if (auto error = json_value.takeError()) {
  239. FAIL() << "Unable to parse to JSON: " << toString(std::move(error))
  240. << "\nOriginal text:\n"
  241. << output << "\n";
  242. }
  243. llvm::json::Object* json_obj = json_value->getAsObject();
  244. ASSERT_THAT(json_obj, NotNull());
  245. // Check relevant paths in the output point to existing directories.
  246. std::optional<llvm::StringRef> install_root =
  247. json_obj->getString("INSTALL_ROOT");
  248. ASSERT_THAT(install_root, Ne(std::nullopt));
  249. EXPECT_THAT(Filesystem::Cwd().OpenDir(install_root->str()), IsSuccess(_));
  250. std::optional<llvm::StringRef> clang_sysroot =
  251. json_obj->getString("CLANG_SYSROOT");
  252. ASSERT_THAT(clang_sysroot, Ne(std::nullopt));
  253. EXPECT_THAT(Filesystem::Cwd().OpenDir(clang_sysroot->str()), IsSuccess(_));
  254. }
  255. } // namespace
  256. } // namespace Carbon