Browse Source

Move test file writing to our common testing `file_helpers` (#4971)

This will make it easy to share across tests. Factored out of a change
adding new uses of the file.
Chandler Carruth 1 year ago
parent
commit
cf29449faf

+ 4 - 1
testing/base/BUILD

@@ -107,7 +107,10 @@ cc_library(
     testonly = 1,
     srcs = ["file_helpers.cpp"],
     hdrs = ["file_helpers.h"],
-    deps = ["//common:error"],
+    deps = [
+        "//common:error",
+        "@googletest//:gtest",
+    ],
 )
 
 cc_library(

+ 37 - 0
testing/base/file_helpers.cpp

@@ -4,8 +4,12 @@
 
 #include "testing/base/file_helpers.h"
 
+#include <gtest/gtest.h>
+
+#include <cstdlib>
 #include <fstream>
 #include <sstream>
+#include <system_error>
 
 namespace Carbon::Testing {
 
@@ -22,4 +26,37 @@ auto ReadFile(std::filesystem::path path) -> ErrorOr<std::string> {
   return buffer.str();
 }
 
+auto WriteTestFile(llvm::StringRef name, llvm::StringRef contents)
+    -> ErrorOr<std::filesystem::path> {
+  std::filesystem::path test_tmpdir;
+  if (char* tmpdir_env = getenv("TEST_TMPDIR"); tmpdir_env != nullptr) {
+    test_tmpdir = std::string(tmpdir_env);
+  } else {
+    test_tmpdir = std::filesystem::temp_directory_path();
+  }
+
+  const auto* unit_test = ::testing::UnitTest::GetInstance();
+  const auto* test_info = unit_test->current_test_info();
+  std::filesystem::path test_file =
+      test_tmpdir / llvm::formatv("{0}_{1}_{2}", test_info->test_suite_name(),
+                                  test_info->name(), name)
+                        .str();
+  // Make debugging a bit easier by cleaning up any files from previous runs.
+  // This is only necessary when not run in Bazel's test environment.
+  std::filesystem::remove(test_file);
+  if (std::filesystem::exists(test_file)) {
+    return Error(
+        llvm::formatv("Unable to remove an existing file: {0}", test_file));
+  }
+
+  std::error_code ec;
+  llvm::raw_fd_ostream test_file_stream(test_file.string(), ec);
+  if (ec) {
+    return Error(llvm::formatv("Test file error: {0}", ec.message()));
+  }
+  test_file_stream << contents;
+
+  return test_file;
+}
+
 }  // namespace Carbon::Testing

+ 17 - 0
testing/base/file_helpers.h

@@ -15,6 +15,23 @@ namespace Carbon::Testing {
 // Reads a file to string.
 auto ReadFile(std::filesystem::path path) -> ErrorOr<std::string>;
 
+// Writes a test file to disk and returns an error or the full path to the file.
+//
+// Note that this expects to be run from within a GoogleTest test, and relies on
+// global state of GoogleTest.
+//
+// This locates a suitable temporary directory for the test, creates a file with
+// the requested name in that directory, and writes the provided content to that
+// file. The full path to the written file is returned.
+//
+// Where possible, this will use a Bazel-provided test temporary directory.
+// However, if unavailable, it falls back to a system temporary directory. This
+// helps tests be runnable outside of Bazel, for example under a debugger. It
+// also works to create test file names that are unlikely to conflict with other
+// tests when run.
+auto WriteTestFile(llvm::StringRef name, llvm::StringRef contents)
+    -> ErrorOr<std::filesystem::path>;
+
 }  // namespace Carbon::Testing
 
 #endif  // CARBON_TESTING_BASE_FILE_HELPERS_H_

+ 1 - 0
toolchain/driver/BUILD

@@ -50,6 +50,7 @@ cc_test(
         "//common:ostream",
         "//common:raw_string_ostream",
         "//testing/base:capture_std_streams",
+        "//testing/base:file_helpers",
         "//testing/base:global_exe_path",
         "//testing/base:gtest_main",
         "@googletest//:gtest",

+ 7 - 38
toolchain/driver/clang_runner_test.cpp

@@ -20,6 +20,7 @@
 #include "llvm/Support/Program.h"
 #include "llvm/TargetParser/Host.h"
 #include "testing/base/capture_std_streams.h"
+#include "testing/base/file_helpers.h"
 #include "testing/base/global_exe_path.h"
 
 namespace Carbon {
@@ -56,38 +57,6 @@ TEST(ClangRunnerTest, Version) {
                              install_paths.llvm_install_bin()));
 }
 
-// Utility to write a test file. We don't need the full power provided here yet,
-// but we anticipate adding more tests such as compiling basic C++ code in the
-// future and this provides a basis for building those tests.
-static auto WriteTestFile(llvm::StringRef name_suffix, llvm::Twine contents)
-    -> std::filesystem::path {
-  std::filesystem::path test_tmpdir;
-  if (char* tmpdir_env = getenv("TEST_TMPDIR"); tmpdir_env != nullptr) {
-    test_tmpdir = std::string(tmpdir_env);
-  } else {
-    test_tmpdir = std::filesystem::temp_directory_path();
-  }
-
-  const auto* unit_test = ::testing::UnitTest::GetInstance();
-  const auto* test_info = unit_test->current_test_info();
-  std::filesystem::path test_file =
-      test_tmpdir / llvm::formatv("{0}_{1}_{2}", test_info->test_suite_name(),
-                                  test_info->name(), name_suffix)
-                        .str();
-  // Make debugging a bit easier by cleaning up any files from previous runs.
-  // This is only necessary when not run in Bazel's test environment.
-  std::filesystem::remove(test_file);
-  CARBON_CHECK(!std::filesystem::exists(test_file));
-
-  {
-    std::error_code ec;
-    llvm::raw_fd_ostream test_file_stream(test_file.string(), ec);
-    CARBON_CHECK(!ec, "Test file error: {0}", ec.message());
-    test_file_stream << contents;
-  }
-  return test_file;
-}
-
 // It's hard to write a portable and reliable unittest for all the layers of the
 // Clang driver because they work hard to interact with the underlying
 // filesystem and operating system. For now, we just check that a link command
@@ -97,8 +66,8 @@ static auto WriteTestFile(llvm::StringRef name_suffix, llvm::Twine contents)
 // test more complete Clang functionality here.
 TEST(ClangRunnerTest, LinkCommandEcho) {
   // Just create some empty files to use in a synthetic link command below.
-  std::filesystem::path foo_file = WriteTestFile("foo.o", "");
-  std::filesystem::path bar_file = WriteTestFile("bar.o", "");
+  std::filesystem::path foo_file = *Testing::WriteTestFile("foo.o", "");
+  std::filesystem::path bar_file = *Testing::WriteTestFile("bar.o", "");
 
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
@@ -131,8 +100,8 @@ TEST(ClangRunnerTest, LinkCommandEcho) {
 
 TEST(ClangRunnerTest, DashC) {
   std::filesystem::path test_file =
-      WriteTestFile("test.cpp", "int test() { return 0; }");
-  std::filesystem::path test_output = WriteTestFile("test.o", "");
+      *Testing::WriteTestFile("test.cpp", "int test() { return 0; }");
+  std::filesystem::path test_output = *Testing::WriteTestFile("test.o", "");
 
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
@@ -158,14 +127,14 @@ TEST(ClangRunnerTest, DashC) {
 }
 
 TEST(ClangRunnerTest, BuitinHeaders) {
-  std::filesystem::path test_file = WriteTestFile("test.c", R"cpp(
+  std::filesystem::path test_file = *Testing::WriteTestFile("test.c", R"cpp(
 #include <stdalign.h>
 
 #ifndef alignas
 #error included the wrong header
 #endif
   )cpp");
-  std::filesystem::path test_output = WriteTestFile("test.o", "");
+  std::filesystem::path test_output = *Testing::WriteTestFile("test.o", "");
 
   const auto install_paths =
       InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());