Add tests for embed_file

PiperOrigin-RevId: 623470785
Change-Id: I40c3bf7667ffcf888d241ca1d5f96b74e3e5ad68
diff --git a/sandboxed_api/BUILD.bazel b/sandboxed_api/BUILD.bazel
index 1acbe53..dcc27a2 100644
--- a/sandboxed_api/BUILD.bazel
+++ b/sandboxed_api/BUILD.bazel
@@ -57,6 +57,18 @@
     ],
 )
 
+cc_test(
+    name = "embed_file_test",
+    srcs = ["embed_file_test.cc"],
+    copts = sapi_platform_copts(),
+    deps = [
+        ":embed_file",
+        "@com_google_absl//absl/memory",
+        "@com_google_absl//absl/strings",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 # The main Sandboxed-API library
 cc_library(
     name = "sapi",
@@ -83,6 +95,7 @@
         "//sandboxed_api/sandbox2:client",
         "//sandboxed_api/sandbox2:comms",
         "//sandboxed_api/util:file_base",
+        "//sandboxed_api/util:fileops",
         "//sandboxed_api/util:runfiles",
         "//sandboxed_api/util:status",
         "@com_google_absl//absl/base:core_headers",
diff --git a/sandboxed_api/CMakeLists.txt b/sandboxed_api/CMakeLists.txt
index ec1ebef..a45eac2 100644
--- a/sandboxed_api/CMakeLists.txt
+++ b/sandboxed_api/CMakeLists.txt
@@ -56,10 +56,10 @@
   PRIVATE absl::strings
           sandbox2::util
           sapi::base
-          sapi::fileops
           sapi::raw_logging
   PUBLIC absl::flat_hash_map
          absl::synchronization
+         sapi::fileops
 )
 
 # sandboxed_api:sapi
@@ -194,6 +194,17 @@
 )
 
 if(BUILD_TESTING AND SAPI_BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING)
+  # sandboxed_api:embed_file_test
+  add_executable(embed_file_test
+    embed_file_test.cc
+  )
+  target_link_libraries(embed_file_test PRIVATE
+    absl::memory
+    absl::strings
+    sapi::embed_file
+    sapi::test_main
+  )
+
   # sandboxed_api:testing
   add_library(sapi_testing ${SAPI_LIB_TYPE}
     testing.cc
diff --git a/sandboxed_api/embed_file.cc b/sandboxed_api/embed_file.cc
index 62dfc5a..f274a59 100644
--- a/sandboxed_api/embed_file.cc
+++ b/sandboxed_api/embed_file.cc
@@ -31,6 +31,8 @@
 
 namespace {
 
+using ::sapi::file_util::fileops::FDCloser;
+
 #ifndef F_ADD_SEALS
 #define F_ADD_SEALS 1033
 #define F_SEAL_SEAL 0x0001
@@ -108,8 +110,8 @@
     SAPI_RAW_VLOG(3,
                   "Returning pre-existing embed file entry for '%s', fd: %d "
                   "(orig name: '%s')",
-                  toc->name, entry->second, entry->first->name);
-    return entry->second;
+                  toc->name, entry->second.get(), entry->first->name);
+    return entry->second.get();
   }
 
   int embed_fd = CreateFdForFileToc(toc);
@@ -121,7 +123,7 @@
   SAPI_RAW_VLOG(1, "Created new embed file entry for '%s' with fd: %d",
                 toc->name, embed_fd);
 
-  file_tocs_[toc] = embed_fd;
+  file_tocs_[toc] = FDCloser(embed_fd);
   return embed_fd;
 }
 
diff --git a/sandboxed_api/embed_file.h b/sandboxed_api/embed_file.h
index 131da31..a437c62 100644
--- a/sandboxed_api/embed_file.h
+++ b/sandboxed_api/embed_file.h
@@ -19,9 +19,12 @@
 #include "absl/base/thread_annotations.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/synchronization/mutex.h"
+#include "sandboxed_api/util/fileops.h"
 
 namespace sapi {
 
+class EmbedFileTestPeer;
+
 // The class provides primitives for converting FileToc structures into
 // executable files.
 class EmbedFile {
@@ -39,6 +42,7 @@
   int GetDupFdForFileToc(const FileToc* toc);
 
  private:
+  friend class EmbedFileTestPeer;  // For testing.
   // Creates an executable file for a given FileToc, and return its
   // file-descriptors (-1 in case of errors).
   static int CreateFdForFileToc(const FileToc* toc);
@@ -46,7 +50,7 @@
   EmbedFile() = default;
 
   // List of File TOCs and corresponding file-descriptors.
-  absl::flat_hash_map<const FileToc*, int> file_tocs_
+  absl::flat_hash_map<const FileToc*, file_util::fileops::FDCloser> file_tocs_
       ABSL_GUARDED_BY(file_tocs_mutex_);
   absl::Mutex file_tocs_mutex_;
 };
diff --git a/sandboxed_api/embed_file_test.cc b/sandboxed_api/embed_file_test.cc
new file mode 100644
index 0000000..cb82a85
--- /dev/null
+++ b/sandboxed_api/embed_file_test.cc
@@ -0,0 +1,86 @@
+#include "sandboxed_api/embed_file.h"
+
+#include <memory>
+#include <string>
+
+#include "sandboxed_api/file_toc.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+
+namespace sapi {
+
+class EmbedFileTestPeer {
+ public:
+  static std::unique_ptr<EmbedFile> NewInstance() {
+    return absl::WrapUnique(new EmbedFile());
+  }
+};
+
+namespace {
+
+using ::testing::Eq;
+using ::testing::Ne;
+
+constexpr absl::string_view kRegularContents = "Hello world!";
+constexpr FileToc kRegularToc = {
+    .name = "regular",
+    .data = kRegularContents.data(),
+    .size = kRegularContents.size(),
+    .md5digest = {},  // MD5 is unused in SAPI implementation
+};
+
+constexpr FileToc kFaultyToc = {
+    .name = "regular",
+    .data = nullptr,
+    .size = 100,
+    .md5digest = {},  // MD5 is unused in SAPI implementation
+};
+
+TEST(EmbedFileTest, GetRegularFd) {
+  std::unique_ptr<EmbedFile> embed_file = EmbedFileTestPeer::NewInstance();
+  int fd = embed_file->GetFdForFileToc(&kRegularToc);
+  EXPECT_THAT(fd, Ne(-1));
+}
+
+TEST(EmbedFileTest, DuplicateGetFdIsSame) {
+  std::unique_ptr<EmbedFile> embed_file = EmbedFileTestPeer::NewInstance();
+  int fd = embed_file->GetFdForFileToc(&kRegularToc);
+  EXPECT_THAT(fd, Ne(-1));
+  int fd2 = embed_file->GetFdForFileToc(&kRegularToc);
+  EXPECT_THAT(fd, Eq(fd2));
+}
+
+TEST(EmbedFileTest, GetDupFdReturnsFreshFd) {
+  std::unique_ptr<EmbedFile> embed_file = EmbedFileTestPeer::NewInstance();
+  int fd = embed_file->GetFdForFileToc(&kRegularToc);
+  EXPECT_THAT(fd, Ne(-1));
+  int dup_fd = embed_file->GetDupFdForFileToc(&kRegularToc);
+  EXPECT_THAT(fd, Ne(dup_fd));
+  close(dup_fd);
+}
+
+TEST(EmbedFileTest, FaultyTocFails) {
+  std::unique_ptr<EmbedFile> embed_file = EmbedFileTestPeer::NewInstance();
+  int fd = embed_file->GetFdForFileToc(&kFaultyToc);
+  EXPECT_THAT(fd, Eq(-1));
+  int dup_fd = embed_file->GetDupFdForFileToc(&kFaultyToc);
+  EXPECT_THAT(dup_fd, Eq(-1));
+}
+
+TEST(EmbedFileTest, OverlongNameTocFails) {
+  std::string overlong_name(1000, 'a');
+  FileToc overlong_name_toc = {
+      .name = overlong_name.c_str(),
+      .data = kRegularContents.data(),
+      .size = kRegularContents.size(),
+      .md5digest = {},  // MD5 is unused in SAPI implementation
+  };
+  std::unique_ptr<EmbedFile> embed_file = EmbedFileTestPeer::NewInstance();
+  int fd = embed_file->GetFdForFileToc(&overlong_name_toc);
+  EXPECT_THAT(fd, Eq(-1));
+}
+
+}  // namespace
+}  // namespace sapi