Implements stdout capturing (by Vlad Losev); fixes compiler error on NVCC (by Zhanyong Wan).

This commit is contained in:
zhanyong.wan 2010-01-27 22:27:30 +00:00
parent 27a65a9d67
commit fd6f2a8a4b
4 changed files with 138 additions and 62 deletions

View File

@ -132,7 +132,10 @@
// LogToStderr() - directs all log messages to stderr. // LogToStderr() - directs all log messages to stderr.
// FlushInfoLog() - flushes informational log messages. // FlushInfoLog() - flushes informational log messages.
// //
// Stderr capturing: // Stdout and stderr capturing:
// CaptureStdout() - starts capturing stdout.
// GetCapturedStdout() - stops capturing stdout and returns the captured
// string.
// CaptureStderr() - starts capturing stderr. // CaptureStderr() - starts capturing stderr.
// GetCapturedStderr() - stops capturing stderr and returns the captured // GetCapturedStderr() - stops capturing stderr and returns the captured
// string. // string.
@ -353,12 +356,15 @@
#ifndef GTEST_USE_OWN_TR1_TUPLE #ifndef GTEST_USE_OWN_TR1_TUPLE
// The user didn't tell us, so we need to figure it out. // The user didn't tell us, so we need to figure it out.
// We use our own tr1 tuple if we aren't sure the user has an // We use our own TR1 tuple if we aren't sure the user has an
// implementation of it already. At this time, GCC 4.0.0+ and MSVC // implementation of it already. At this time, GCC 4.0.0+ and MSVC
// 2010 are the only mainstream compilers that come with a TR1 tuple // 2010 are the only mainstream compilers that come with a TR1 tuple
// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by
// defining __GNUC__ and friends, but cannot compile GCC's tuple
// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB // implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB
// Feature Pack download, which we cannot assume the user has. // Feature Pack download, which we cannot assume the user has.
#if (defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)) || _MSC_VER >= 1600 #if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000)) \
|| _MSC_VER >= 1600
#define GTEST_USE_OWN_TR1_TUPLE 0 #define GTEST_USE_OWN_TR1_TUPLE 0
#else #else
#define GTEST_USE_OWN_TR1_TUPLE 1 #define GTEST_USE_OWN_TR1_TUPLE 1
@ -690,13 +696,22 @@ class GTestLog {
inline void LogToStderr() {} inline void LogToStderr() {}
inline void FlushInfoLog() { fflush(NULL); } inline void FlushInfoLog() { fflush(NULL); }
#if !GTEST_OS_WINDOWS_MOBILE
// Defines the stderr capturer: // Defines the stderr capturer:
// CaptureStdout - starts capturing stdout.
// GetCapturedStdout - stops capturing stdout and returns the captured string.
// CaptureStderr - starts capturing stderr. // CaptureStderr - starts capturing stderr.
// GetCapturedStderr - stops capturing stderr and returns the captured string. // GetCapturedStderr - stops capturing stderr and returns the captured string.
//
void CaptureStdout();
String GetCapturedStdout();
void CaptureStderr(); void CaptureStderr();
String GetCapturedStderr(); String GetCapturedStderr();
#endif // !GTEST_OS_WINDOWS_MOBILE
#if GTEST_HAS_DEATH_TEST #if GTEST_HAS_DEATH_TEST
// A copy of all command line arguments. Set by InitGoogleTest(). // A copy of all command line arguments. Set by InitGoogleTest().

View File

@ -68,8 +68,10 @@ namespace internal {
#if defined(_MSC_VER) || defined(__BORLANDC__) #if defined(_MSC_VER) || defined(__BORLANDC__)
// MSVC and C++Builder do not provide a definition of STDERR_FILENO. // MSVC and C++Builder do not provide a definition of STDERR_FILENO.
const int kStdOutFileno = 1;
const int kStdErrFileno = 2; const int kStdErrFileno = 2;
#else #else
const int kStdOutFileno = STDOUT_FILENO;
const int kStdErrFileno = STDERR_FILENO; const int kStdErrFileno = STDERR_FILENO;
#endif // _MSC_VER #endif // _MSC_VER
@ -439,18 +441,14 @@ GTestLog::~GTestLog() {
#pragma warning(disable: 4996) #pragma warning(disable: 4996)
#endif // _MSC_VER #endif // _MSC_VER
// Defines the stderr capturer. // Stream capturing is not supported on Windows Mobile.
#if !GTEST_OS_WINDOWS_MOBILE
class CapturedStderr { // Object that captures an output stream (stdout/stderr).
class CapturedStream {
public: public:
// The ctor redirects stderr to a temporary file. // The ctor redirects the stream to a temporary file.
CapturedStderr() { CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
#if GTEST_OS_WINDOWS_MOBILE
// Not supported on Windows CE.
posix::Abort();
#else
uncaptured_fd_ = dup(kStdErrFileno);
#if GTEST_OS_WINDOWS #if GTEST_OS_WINDOWS
char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT
char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT
@ -463,57 +461,57 @@ class CapturedStderr {
// There's no guarantee that a test has write access to the // There's no guarantee that a test has write access to the
// current directory, so we create the temporary file in the /tmp // current directory, so we create the temporary file in the /tmp
// directory instead. // directory instead.
char name_template[] = "/tmp/captured_stderr.XXXXXX"; char name_template[] = "/tmp/captured_stream.XXXXXX";
const int captured_fd = mkstemp(name_template); const int captured_fd = mkstemp(name_template);
filename_ = name_template; filename_ = name_template;
#endif // GTEST_OS_WINDOWS #endif // GTEST_OS_WINDOWS
fflush(NULL); fflush(NULL);
dup2(captured_fd, kStdErrFileno); dup2(captured_fd, fd_);
close(captured_fd); close(captured_fd);
#endif // GTEST_OS_WINDOWS_MOBILE
} }
~CapturedStderr() { ~CapturedStream() {
#if !GTEST_OS_WINDOWS_MOBILE
remove(filename_.c_str()); remove(filename_.c_str());
#endif // !GTEST_OS_WINDOWS_MOBILE
} }
// Stops redirecting stderr. String GetCapturedString() {
void StopCapture() { if (uncaptured_fd_ != -1) {
#if !GTEST_OS_WINDOWS_MOBILE // Restores the original stream.
// Restores the original stream. fflush(NULL);
fflush(NULL); dup2(uncaptured_fd_, fd_);
dup2(uncaptured_fd_, kStdErrFileno); close(uncaptured_fd_);
close(uncaptured_fd_); uncaptured_fd_ = -1;
uncaptured_fd_ = -1; }
#endif // !GTEST_OS_WINDOWS_MOBILE
}
// Returns the name of the temporary file holding the stderr output. FILE* const file = posix::FOpen(filename_.c_str(), "r");
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we const String content = ReadEntireFile(file);
// can use it here. posix::FClose(file);
::std::string filename() const { return filename_; } return content;
}
private: private:
// Reads the entire content of a file as a String.
static String ReadEntireFile(FILE* file);
// Returns the size (in bytes) of a file.
static size_t GetFileSize(FILE* file);
const int fd_; // A stream to capture.
int uncaptured_fd_; int uncaptured_fd_;
// Name of the temporary file holding the stderr output.
::std::string filename_; ::std::string filename_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
}; };
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
static CapturedStderr* g_captured_stderr = NULL;
// Returns the size (in bytes) of a file. // Returns the size (in bytes) of a file.
static size_t GetFileSize(FILE * file) { size_t CapturedStream::GetFileSize(FILE* file) {
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
return static_cast<size_t>(ftell(file)); return static_cast<size_t>(ftell(file));
} }
// Reads the entire content of a file as a string. // Reads the entire content of a file as a string.
static String ReadEntireFile(FILE * file) { String CapturedStream::ReadEntireFile(FILE* file) {
const size_t file_size = GetFileSize(file); const size_t file_size = GetFileSize(file);
char* const buffer = new char[file_size]; char* const buffer = new char[file_size];
@ -535,30 +533,50 @@ static String ReadEntireFile(FILE * file) {
return content; return content;
} }
// Starts capturing stderr. #ifdef _MSC_VER
void CaptureStderr() { #pragma warning(pop)
if (g_captured_stderr != NULL) { #endif // _MSC_VER
GTEST_LOG_(FATAL) << "Only one stderr capturer can exist at one time.";
static CapturedStream* g_captured_stderr = NULL;
static CapturedStream* g_captured_stdout = NULL;
// Starts capturing an output stream (stdout/stderr).
void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
if (*stream != NULL) {
GTEST_LOG_(FATAL) << "Only one " << stream_name
<< " capturer can exist at a time.";
} }
g_captured_stderr = new CapturedStderr; *stream = new CapturedStream(fd);
} }
// Stops capturing stderr and returns the captured string. // Stops capturing the output stream and returns the captured string.
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can String GetCapturedStream(CapturedStream** captured_stream) {
// use it here. const String content = (*captured_stream)->GetCapturedString();
String GetCapturedStderr() {
g_captured_stderr->StopCapture();
FILE* const file = posix::FOpen(g_captured_stderr->filename().c_str(), "r"); delete *captured_stream;
const String content = ReadEntireFile(file); *captured_stream = NULL;
posix::FClose(file);
delete g_captured_stderr;
g_captured_stderr = NULL;
return content; 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.
String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); }
// Stops capturing stderr and returns the captured string.
String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); }
#endif // !GTEST_OS_WINDOWS_MOBILE
#if GTEST_HAS_DEATH_TEST #if GTEST_HAS_DEATH_TEST
// A copy of all command line arguments. Set by InitGoogleTest(). // A copy of all command line arguments. Set by InitGoogleTest().

View File

@ -2634,6 +2634,9 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
SetConsoleTextAttribute(stdout_handle, SetConsoleTextAttribute(stdout_handle,
GetColorAttribute(color) | FOREGROUND_INTENSITY); GetColorAttribute(color) | FOREGROUND_INTENSITY);
vprintf(fmt, args); vprintf(fmt, args);
// Unless we flush stream buffers now the next SetConsoleTextAttribute
// call can reset the color before the output reaches the console.
fflush(stdout);
// Restores the text color. // Restores the text color.
SetConsoleTextAttribute(stdout_handle, old_color_attrs); SetConsoleTextAttribute(stdout_handle, old_color_attrs);

View File

@ -33,6 +33,8 @@
#include <gtest/internal/gtest-port.h> #include <gtest/internal/gtest-port.h>
#include <stdio.h>
#if GTEST_OS_MAC #if GTEST_OS_MAC
#include <pthread.h> #include <pthread.h>
#include <time.h> #include <time.h>
@ -699,11 +701,49 @@ TEST(RETest, PartialMatchWorks) {
#endif // GTEST_USES_POSIX_RE #endif // GTEST_USES_POSIX_RE
TEST(CaptureStderrTest, CapturesStdErr) { #if !GTEST_OS_WINDOWS_MOBILE
CaptureStderr();
fprintf(stderr, "abc"); TEST(CaptureTest, CapturesStdout) {
ASSERT_STREQ("abc", GetCapturedStderr().c_str()); CaptureStdout();
fprintf(stdout, "abc");
EXPECT_STREQ("abc", GetCapturedStdout().c_str());
CaptureStdout();
fprintf(stdout, "def%cghi", '\0');
EXPECT_EQ(::std::string("def\0ghi", 7), ::std::string(GetCapturedStdout()));
} }
TEST(CaptureTest, CapturesStderr) {
CaptureStderr();
fprintf(stderr, "jkl");
EXPECT_STREQ("jkl", GetCapturedStderr().c_str());
CaptureStderr();
fprintf(stderr, "jkl%cmno", '\0');
EXPECT_EQ(::std::string("jkl\0mno", 7), ::std::string(GetCapturedStderr()));
}
// Tests that stdout and stderr capture don't interfere with each other.
TEST(CaptureTest, CapturesStdoutAndStderr) {
CaptureStdout();
CaptureStderr();
fprintf(stdout, "pqr");
fprintf(stderr, "stu");
EXPECT_STREQ("pqr", GetCapturedStdout().c_str());
EXPECT_STREQ("stu", GetCapturedStderr().c_str());
}
TEST(CaptureDeathTest, CannotReenterStdoutCapture) {
CaptureStdout();
EXPECT_DEATH_IF_SUPPORTED(CaptureStdout();,
"Only one stdout capturer can exist at a time");
GetCapturedStdout();
// We cannot test stderr capturing using death tests as they use it
// themselves.
}
#endif // !GTEST_OS_WINDOWS_MOBILE
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing