blob: 77e297e95cd050a24278e764e5843153c6c543a7 [file] [log] [blame]
// Copyright 2020 The PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(crbug.com/1009553): Remove this wrapper after finding a way to plumb a
// workable temporary path into googletest on Android.
// This wrapper lets us compile gtest-port.cc without its stream redirection
// code. We replace this code with a variant that works on all Chrome platforms.
// This is a temporary workaround until we get good code upstream.
//
// Stream redirection requires the ability to create files in a temporary
// directory. Traditionally, this directory has been /sdcard on Android.
// Commit bf0fe874a27bd6c9a4a35b98e662d2d02f8879a2 changed the Android
// directory to /data/local/tmp, which is not writable in Chrome's testing
// setup. We work around this problem by using the old code for now.
//
// It is tempting to consider disabling the stream redirection code altogether,
// by setting GTEST_HAS_STREAM_REDIRECTION to 0 in googletest's BUILD.gn.
// This breaks gtest-death-test.cc, which assumes the existence of
// testing::internal::{GetCapturedStderr,CaptureStderr} without any macro
// checking.
#define GTEST_HAS_STREAM_REDIRECTION 0
#include "third_party/googletest/src/googletest/src/gtest-port.cc"
namespace testing {
namespace internal {
// Object that captures an output stream (stdout/stderr).
class CapturedStream {
public:
// The ctor redirects the stream to a temporary file.
explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
std::string temp_dir = ::testing::TempDir();
// testing::TempDir() should return a directory with a path separator.
// However, this rule was documented fairly recently, so we normalize across
// implementations with and without a trailing path separator.
if (temp_dir.back() != GTEST_PATH_SEP_[0])
temp_dir.push_back(GTEST_PATH_SEP_[0]);
#if GTEST_OS_WINDOWS
char temp_file_path[MAX_PATH + 1] = {'\0'}; // NOLINT
const UINT success = ::GetTempFileNameA(temp_dir.c_str(), "gtest_redir",
0, // Generate unique file name.
temp_file_path);
GTEST_CHECK_(success != 0)
<< "Unable to create a temporary file in " << temp_dir;
const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE);
GTEST_CHECK_(captured_fd != -1)
<< "Unable to open temporary file " << temp_file_path;
filename_ = temp_file_path;
#else
std::string name_template = temp_dir + "gtest_captured_stream.XXXXXX";
// mkstemp() modifies the string bytes in place, and does not go beyond the
// string's length. This results in well-defined behavior in C++17.
//
// The const_cast is needed below C++17. The constraints on std::string
// implementations in C++11 and above make assumption behind the const_cast
// fairly safe.
const int captured_fd = ::mkstemp(const_cast<char*>(name_template.data()));
GTEST_CHECK_(captured_fd != -1)
<< "Failed to create tmp file " << name_template
<< " for test; does the test have write access to the directory?";
filename_ = std::move(name_template);
#endif // GTEST_OS_WINDOWS
fflush(nullptr);
dup2(captured_fd, fd_);
close(captured_fd);
}
~CapturedStream() { remove(filename_.c_str()); }
std::string GetCapturedString() {
if (uncaptured_fd_ != -1) {
// Restores the original stream.
fflush(nullptr);
dup2(uncaptured_fd_, fd_);
close(uncaptured_fd_);
uncaptured_fd_ = -1;
}
FILE* const file = posix::FOpen(filename_.c_str(), "r");
if (file == nullptr) {
GTEST_LOG_(FATAL) << "Failed to open tmp file " << filename_
<< " for capturing stream.";
}
const std::string content = ReadEntireFile(file);
posix::FClose(file);
return content;
}
private:
const int fd_; // A stream to capture.
int uncaptured_fd_;
// Name of the temporary file holding the stderr output.
::std::string filename_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
};
GTEST_DISABLE_MSC_DEPRECATED_POP_()
static CapturedStream* g_captured_stderr = nullptr;
static CapturedStream* g_captured_stdout = nullptr;
// Starts capturing an output stream (stdout/stderr).
static void CaptureStream(int fd,
const char* stream_name,
CapturedStream** stream) {
if (*stream != nullptr) {
GTEST_LOG_(FATAL) << "Only one " << stream_name
<< " capturer can exist at a time.";
}
*stream = new CapturedStream(fd);
}
// Stops capturing the output stream and returns the captured string.
static std::string GetCapturedStream(CapturedStream** captured_stream) {
const std::string content = (*captured_stream)->GetCapturedString();
delete *captured_stream;
*captured_stream = nullptr;
return content;
}
// Starts capturing stdout.
void CaptureStdout() {
CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout);
}
// Starts capturing stderr.
void CaptureStderr() {
CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr);
}
// Stops capturing stdout and returns the captured string.
std::string GetCapturedStdout() {
return GetCapturedStream(&g_captured_stdout);
}
// Stops capturing stderr and returns the captured string.
std::string GetCapturedStderr() {
return GetCapturedStream(&g_captured_stderr);
}
} // namespace internal
} // namespace testing