aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2010-01-27 22:27:30 +0000
committerzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2010-01-27 22:27:30 +0000
commitfd6f2a8a4b3fe8beb31f26b774b460727c410b66 (patch)
tree3425e1293b03fd10bf46272484c6abc2753308ae
parent27a65a9d67db865e9fba8224780fd2b7a71fe7d1 (diff)
downloadgoogletest-fd6f2a8a4b3fe8beb31f26b774b460727c410b66.tar.gz
googletest-fd6f2a8a4b3fe8beb31f26b774b460727c410b66.tar.bz2
googletest-fd6f2a8a4b3fe8beb31f26b774b460727c410b66.zip
Implements stdout capturing (by Vlad Losev); fixes compiler error on NVCC (by Zhanyong Wan).
-rw-r--r--include/gtest/internal/gtest-port.h23
-rw-r--r--src/gtest-port.cc126
-rw-r--r--src/gtest.cc3
-rw-r--r--test/gtest-port_test.cc46
4 files changed, 137 insertions, 61 deletions
diff --git a/include/gtest/internal/gtest-port.h b/include/gtest/internal/gtest-port.h
index 60ca49ee..467f6974 100644
--- a/include/gtest/internal/gtest-port.h
+++ b/include/gtest/internal/gtest-port.h
@@ -132,7 +132,10 @@
// LogToStderr() - directs all log messages to stderr.
// 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.
// GetCapturedStderr() - stops capturing stderr and returns the captured
// string.
@@ -353,12 +356,15 @@
#ifndef GTEST_USE_OWN_TR1_TUPLE
// 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
// 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
// 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
#else
#define GTEST_USE_OWN_TR1_TUPLE 1
@@ -690,13 +696,22 @@ class GTestLog {
inline void LogToStderr() {}
inline void FlushInfoLog() { fflush(NULL); }
+#if !GTEST_OS_WINDOWS_MOBILE
+
// Defines the stderr capturer:
+// CaptureStdout - starts capturing stdout.
+// GetCapturedStdout - stops capturing stdout and returns the captured string.
// CaptureStderr - starts capturing stderr.
// GetCapturedStderr - stops capturing stderr and returns the captured string.
-
+//
+void CaptureStdout();
+String GetCapturedStdout();
void CaptureStderr();
String GetCapturedStderr();
+#endif // !GTEST_OS_WINDOWS_MOBILE
+
+
#if GTEST_HAS_DEATH_TEST
// A copy of all command line arguments. Set by InitGoogleTest().
diff --git a/src/gtest-port.cc b/src/gtest-port.cc
index de169e2a..1890a802 100644
--- a/src/gtest-port.cc
+++ b/src/gtest-port.cc
@@ -68,8 +68,10 @@ namespace internal {
#if defined(_MSC_VER) || defined(__BORLANDC__)
// MSVC and C++Builder do not provide a definition of STDERR_FILENO.
+const int kStdOutFileno = 1;
const int kStdErrFileno = 2;
#else
+const int kStdOutFileno = STDOUT_FILENO;
const int kStdErrFileno = STDERR_FILENO;
#endif // _MSC_VER
@@ -439,18 +441,14 @@ GTestLog::~GTestLog() {
#pragma warning(disable: 4996)
#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:
- // The ctor redirects stderr to a temporary file.
- CapturedStderr() {
-#if GTEST_OS_WINDOWS_MOBILE
- // Not supported on Windows CE.
- posix::Abort();
-#else
- uncaptured_fd_ = dup(kStdErrFileno);
-
+ // The ctor redirects the stream to a temporary file.
+ CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
#if GTEST_OS_WINDOWS
char temp_dir_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
// current directory, so we create the temporary file in the /tmp
// directory instead.
- char name_template[] = "/tmp/captured_stderr.XXXXXX";
+ char name_template[] = "/tmp/captured_stream.XXXXXX";
const int captured_fd = mkstemp(name_template);
filename_ = name_template;
#endif // GTEST_OS_WINDOWS
fflush(NULL);
- dup2(captured_fd, kStdErrFileno);
+ dup2(captured_fd, fd_);
close(captured_fd);
-#endif // GTEST_OS_WINDOWS_MOBILE
}
- ~CapturedStderr() {
-#if !GTEST_OS_WINDOWS_MOBILE
+ ~CapturedStream() {
remove(filename_.c_str());
-#endif // !GTEST_OS_WINDOWS_MOBILE
}
- // Stops redirecting stderr.
- void StopCapture() {
-#if !GTEST_OS_WINDOWS_MOBILE
- // Restores the original stream.
- fflush(NULL);
- dup2(uncaptured_fd_, kStdErrFileno);
- close(uncaptured_fd_);
- uncaptured_fd_ = -1;
-#endif // !GTEST_OS_WINDOWS_MOBILE
- }
+ String GetCapturedString() {
+ if (uncaptured_fd_ != -1) {
+ // Restores the original stream.
+ fflush(NULL);
+ dup2(uncaptured_fd_, fd_);
+ close(uncaptured_fd_);
+ uncaptured_fd_ = -1;
+ }
- // Returns the name of the temporary file holding the stderr output.
- // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we
- // can use it here.
- ::std::string filename() const { return filename_; }
+ FILE* const file = posix::FOpen(filename_.c_str(), "r");
+ const String content = ReadEntireFile(file);
+ posix::FClose(file);
+ return content;
+ }
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_;
+ // Name of the temporary file holding the stderr output.
::std::string filename_;
-};
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif // _MSC_VER
-static CapturedStderr* g_captured_stderr = NULL;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
+};
// 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);
return static_cast<size_t>(ftell(file));
}
// 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);
char* const buffer = new char[file_size];
@@ -535,30 +533,50 @@ static String ReadEntireFile(FILE * file) {
return content;
}
-// Starts capturing stderr.
-void CaptureStderr() {
- if (g_captured_stderr != NULL) {
- GTEST_LOG_(FATAL) << "Only one stderr capturer can exist at one time.";
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+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.
-// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can
-// use it here.
-String GetCapturedStderr() {
- g_captured_stderr->StopCapture();
+// Stops capturing the output stream and returns the captured string.
+String GetCapturedStream(CapturedStream** captured_stream) {
+ const String content = (*captured_stream)->GetCapturedString();
- FILE* const file = posix::FOpen(g_captured_stderr->filename().c_str(), "r");
- const String content = ReadEntireFile(file);
- posix::FClose(file);
-
- delete g_captured_stderr;
- g_captured_stderr = NULL;
+ delete *captured_stream;
+ *captured_stream = NULL;
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
// A copy of all command line arguments. Set by InitGoogleTest().
diff --git a/src/gtest.cc b/src/gtest.cc
index 11ce571b..f5de645b 100644
--- a/src/gtest.cc
+++ b/src/gtest.cc
@@ -2634,6 +2634,9 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
SetConsoleTextAttribute(stdout_handle,
GetColorAttribute(color) | FOREGROUND_INTENSITY);
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.
SetConsoleTextAttribute(stdout_handle, old_color_attrs);
diff --git a/test/gtest-port_test.cc b/test/gtest-port_test.cc
index 551c98b2..3576c2b8 100644
--- a/test/gtest-port_test.cc
+++ b/test/gtest-port_test.cc
@@ -33,6 +33,8 @@
#include <gtest/internal/gtest-port.h>
+#include <stdio.h>
+
#if GTEST_OS_MAC
#include <pthread.h>
#include <time.h>
@@ -699,11 +701,49 @@ TEST(RETest, PartialMatchWorks) {
#endif // GTEST_USES_POSIX_RE
-TEST(CaptureStderrTest, CapturesStdErr) {
+#if !GTEST_OS_WINDOWS_MOBILE
+
+TEST(CaptureTest, CapturesStdout) {
+ 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, "abc");
- ASSERT_STREQ("abc", GetCapturedStderr().c_str());
+ 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 testing