Windows, test-wrapper: add MIME type lookup

In this commit:

- implement logic to look up the MIME type of a
  file from its name

We'll use this information to write the Undeclared
Outputs Manifest file, which lists the undeclared
outputs of the tests along with their size and
MIME type.

See: https://github.com/bazelbuild/bazel/issues/5508

Change-Id: I7606114ad3e82da662bece63997d03d1d9c2fc8e

Closes #6634.

Change-Id: I0f31c333aa1967092ed9086bbfb43cf8c39016d1
PiperOrigin-RevId: 220770240
diff --git a/tools/test/windows/tw.cc b/tools/test/windows/tw.cc
index 2d5faf8..4aa34f0 100644
--- a/tools/test/windows/tw.cc
+++ b/tools/test/windows/tw.cc
@@ -568,6 +568,26 @@
   return true;
 }
 
+// Returns the MIME type of the file name.
+// If the MIME type is unknown or an error occurs, the method returns
+// "application/octet-stream".
+std::string GetMimeType(const std::string& filename) {
+  static constexpr char* kDefaultMimeType = "application/octet-stream";
+  std::string::size_type pos = filename.find_last_of('.');
+  if (pos == std::string::npos) {
+    return kDefaultMimeType;
+  }
+  char data[1000];
+  DWORD data_size = 1000 * sizeof(char);
+  if (RegGetValueA(HKEY_CLASSES_ROOT, filename.c_str() + pos, "Content Type",
+                   RRF_RT_REG_SZ, NULL, data, &data_size) == ERROR_SUCCESS) {
+    return data;
+  }
+  // The file extension is unknown, or it does not have a "Content Type" value,
+  // or the value is too long. We don't care; just return the default.
+  return kDefaultMimeType;
+}
+
 bool ExportXmlPath(const Path& cwd) {
   Path result;
   if (!GetPathEnv(L"XML_OUTPUT_FILE", &result)) {
@@ -1091,6 +1111,10 @@
          CreateZip(root, files, zip);
 }
 
+std::string TestOnly_GetMimeType(const std::string& filename) {
+  return GetMimeType(filename);
+}
+
 bool TestOnly_AsMixedPath(const std::wstring& path, std::string* result) {
   return WcsToAcp(
       AsMixedPath(bazel::windows::HasUncPrefix(path.c_str()) ? path.substr(4)
diff --git a/tools/test/windows/tw.h b/tools/test/windows/tw.h
index ed09612..7ba458a 100644
--- a/tools/test/windows/tw.h
+++ b/tools/test/windows/tw.h
@@ -115,6 +115,9 @@
                         const std::vector<FileInfo>& files,
                         const std::wstring& abs_zip);
 
+// Returns the MIME type of a file. The file does not need to exist.
+std::string TestOnly_GetMimeType(const std::string& filename);
+
 bool TestOnly_AsMixedPath(const std::wstring& path, std::string* result);
 
 }  // namespace testing
diff --git a/tools/test/windows/tw_test.cc b/tools/test/windows/tw_test.cc
index f8e0a31..8072bb3 100644
--- a/tools/test/windows/tw_test.cc
+++ b/tools/test/windows/tw_test.cc
@@ -39,6 +39,7 @@
 using bazel::tools::test_wrapper::testing::TestOnly_CreateZip;
 using bazel::tools::test_wrapper::testing::TestOnly_GetEnv;
 using bazel::tools::test_wrapper::testing::TestOnly_GetFileListRelativeTo;
+using bazel::tools::test_wrapper::testing::TestOnly_GetMimeType;
 using bazel::tools::test_wrapper::testing::TestOnly_ToZipEntryPaths;
 
 class TestWrapperWindowsTest : public ::testing::Test {
@@ -336,4 +337,17 @@
   EXPECT_EQ(memcmp(extracted[8].data.get(), "hello", 5), 0);
 }
 
+TEST_F(TestWrapperWindowsTest, TestGetMimeType) {
+  // As of 2018-11-08, TestOnly_GetMimeType looks up the MIME type from the
+  // registry under `HKCR\<extension>\Content Type`, e.g.
+  // 'HKCR\.bmp\Content Type`.
+  // Bazel's CI machines run Windows Server 2016 Core, whose registry contains
+  // the Content Type for .ico and .bmp but not for common types such as .txt,
+  // hence the file types we choose to test for.
+  EXPECT_EQ(TestOnly_GetMimeType("foo.ico"), std::string("image/x-icon"));
+  EXPECT_EQ(TestOnly_GetMimeType("foo.bmp"), std::string("image/bmp"));
+  EXPECT_EQ(TestOnly_GetMimeType("foo"),
+            std::string("application/octet-stream"));
+}
+
 }  // namespace