1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 // Author: kenton@google.com (Kenton Varda)
9 // emulates google3/testing/base/public/googletest.cc
10
11 #include "google/protobuf/testing/googletest.h"
12
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17
18 #include "absl/log/absl_check.h"
19 #include "absl/log/absl_log.h"
20 #include "absl/strings/match.h"
21 #include "absl/strings/str_cat.h"
22 #include "absl/strings/str_replace.h"
23 #include "google/protobuf/io/io_win32.h"
24 #include "google/protobuf/testing/file.h"
25 #include <gtest/gtest.h>
26
27 #ifdef _MSC_VER
28 // #include <direct.h>
29 #else
30 #include <unistd.h>
31 #endif
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <iostream>
35 #include <fstream>
36
37 namespace google {
38 namespace protobuf {
39
40 #ifdef _WIN32
41 // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
42 // them like we do below.
43 using google::protobuf::io::win32::close;
44 using google::protobuf::io::win32::dup2;
45 using google::protobuf::io::win32::dup;
46 using google::protobuf::io::win32::mkdir;
47 using google::protobuf::io::win32::open;
48 #endif
49
50 #ifndef O_BINARY
51 #ifdef _O_BINARY
52 #define O_BINARY _O_BINARY
53 #else
54 #define O_BINARY 0 // If this isn't defined, the platform doesn't need it.
55 #endif
56 #endif
57
TestSourceDir()58 std::string TestSourceDir() {
59 #ifndef GOOGLE_THIRD_PARTY_PROTOBUF
60 #ifdef GOOGLE_PROTOBUF_TEST_SOURCE_PATH
61 return GOOGLE_PROTOBUF_TEST_SOURCE_PATH;
62 #else
63 #ifndef _MSC_VER
64 // automake sets the "srcdir" environment variable.
65 char* result = getenv("srcdir");
66 if (result != nullptr) {
67 return result;
68 }
69 #endif // _MSC_VER
70
71 // Look for the "src" directory.
72 std::string prefix = ".";
73
74 // Keep looking further up the directory tree until we find
75 // src/.../descriptor.cc. It is important to look for a particular file,
76 // keeping in mind that with Bazel builds the directory structure under
77 // bazel-bin/ looks similar to the main directory tree in the Git repo.
78 while (!File::Exists(
79 absl::StrCat(prefix, "/src/google/protobuf/descriptor.cc"))) {
80 if (!File::Exists(prefix)) {
81 ABSL_LOG(FATAL)
82 << "Could not find protobuf source code. Please run tests from "
83 "somewhere within the protobuf source package.";
84 }
85 absl::StrAppend(&prefix, "/..");
86 }
87 absl::StrAppend(&prefix, "/src");
88 return prefix;
89 #endif // GOOGLE_PROTOBUF_TEST_SOURCE_PATH
90 #else
91 return "third_party/protobuf/src";
92 #endif // GOOGLE_THIRD_PARTY_PROTOBUF
93 }
94
95 namespace {
96
GetTemporaryDirectoryName()97 std::string GetTemporaryDirectoryName() {
98 std::string result = absl::StrCat(testing::TempDir(), "protobuf_tempdir");
99 #ifdef _WIN32
100 // The Win32 API accepts forward slashes as a path delimiter as long as the
101 // path doesn't use the "\\?\" prefix.
102 // Let's avoid confusion and use only forward slashes.
103 result = absl::StrReplaceAll(result, {{"\\", "/"}});
104 #endif // _WIN32
105 return result;
106 }
107
108 // Creates a temporary directory on demand and deletes it when the process
109 // quits.
110 class TempDirDeleter {
111 public:
TempDirDeleter()112 TempDirDeleter() {}
~TempDirDeleter()113 ~TempDirDeleter() {
114 if (!name_.empty()) {
115 File::DeleteRecursively(name_, nullptr, nullptr);
116 }
117 }
118
GetTempDir()119 std::string GetTempDir() {
120 if (name_.empty()) {
121 name_ = GetTemporaryDirectoryName();
122 ABSL_CHECK(mkdir(name_.c_str(), 0777) == 0) << strerror(errno);
123
124 // Stick a file in the directory that tells people what this is, in case
125 // we abort and don't get a chance to delete it.
126 File::WriteStringToFileOrDie(
127 "", absl::StrCat(name_, "/TEMP_DIR_FOR_PROTOBUF_TESTS"));
128 }
129 return name_;
130 }
131
132 private:
133 std::string name_;
134 };
135
136 TempDirDeleter temp_dir_deleter_;
137
138 } // namespace
139
TestTempDir()140 std::string TestTempDir() { return temp_dir_deleter_.GetTempDir(); }
141
142 // TODO: Share duplicated code below. Too busy/lazy for now.
143
144 static std::string stdout_capture_filename_;
145 static std::string stderr_capture_filename_;
146 static int original_stdout_ = -1;
147 static int original_stderr_ = -1;
148
CaptureTestStdout()149 void CaptureTestStdout() {
150 ABSL_CHECK_EQ(original_stdout_, -1) << "Already capturing.";
151
152 stdout_capture_filename_ = absl::StrCat(TestTempDir(), "/captured_stdout");
153
154 int fd = open(stdout_capture_filename_.c_str(),
155 O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777);
156 ABSL_CHECK(fd >= 0) << "open: " << strerror(errno);
157
158 original_stdout_ = dup(1);
159 close(1);
160 dup2(fd, 1);
161 close(fd);
162 }
163
CaptureTestStderr()164 void CaptureTestStderr() {
165 ABSL_CHECK_EQ(original_stderr_, -1) << "Already capturing.";
166
167 stderr_capture_filename_ = absl::StrCat(TestTempDir(), "/captured_stderr");
168
169 int fd = open(stderr_capture_filename_.c_str(),
170 O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777);
171 ABSL_CHECK(fd >= 0) << "open: " << strerror(errno);
172
173 original_stderr_ = dup(2);
174 close(2);
175 dup2(fd, 2);
176 close(fd);
177 }
178
GetCapturedTestStdout()179 std::string GetCapturedTestStdout() {
180 ABSL_CHECK_NE(original_stdout_, -1) << "Not capturing.";
181
182 close(1);
183 dup2(original_stdout_, 1);
184 close(original_stdout_);
185 original_stdout_ = -1;
186
187 std::string result;
188 File::ReadFileToStringOrDie(stdout_capture_filename_, &result);
189
190 remove(stdout_capture_filename_.c_str());
191
192 return result;
193 }
194
GetCapturedTestStderr()195 std::string GetCapturedTestStderr() {
196 ABSL_CHECK_NE(original_stderr_, -1) << "Not capturing.";
197
198 close(2);
199 dup2(original_stderr_, 2);
200 close(original_stderr_);
201 original_stderr_ = -1;
202
203 std::string result;
204 File::ReadFileToStringOrDie(stderr_capture_filename_, &result);
205
206 remove(stderr_capture_filename_.c_str());
207
208 return result;
209 }
210
211 namespace {
212
213 // Force shutdown at process exit so that we can test for memory leaks. To
214 // actually check for leaks, I suggest using the heap checker included with
215 // google-perftools. Set it to "draconian" mode to ensure that every last
216 // call to malloc() has a corresponding free().
217 struct ForceShutdown {
~ForceShutdowngoogle::protobuf::__anon154c45850211::ForceShutdown218 ~ForceShutdown() {
219 ShutdownProtobufLibrary();
220 // Test to shutdown the library twice, which should succeed.
221 ShutdownProtobufLibrary();
222 }
223 } force_shutdown;
224
225 } // namespace
226
227 } // namespace protobuf
228 } // namespace google
229