Fixes gUnit streaming output format.
This commit is contained in:
parent
cc1fdb58ca
commit
ba072ccca4
@ -154,6 +154,7 @@ class ExecDeathTest;
|
|||||||
class NoExecDeathTest;
|
class NoExecDeathTest;
|
||||||
class FinalSuccessChecker;
|
class FinalSuccessChecker;
|
||||||
class GTestFlagSaver;
|
class GTestFlagSaver;
|
||||||
|
class StreamingListenerTest;
|
||||||
class TestResultAccessor;
|
class TestResultAccessor;
|
||||||
class TestEventListenersAccessor;
|
class TestEventListenersAccessor;
|
||||||
class TestEventRepeater;
|
class TestEventRepeater;
|
||||||
@ -679,6 +680,7 @@ class GTEST_API_ TestInfo {
|
|||||||
friend class Test;
|
friend class Test;
|
||||||
friend class TestCase;
|
friend class TestCase;
|
||||||
friend class internal::UnitTestImpl;
|
friend class internal::UnitTestImpl;
|
||||||
|
friend class internal::StreamingListenerTest;
|
||||||
friend TestInfo* internal::MakeAndRegisterTestInfo(
|
friend TestInfo* internal::MakeAndRegisterTestInfo(
|
||||||
const char* test_case_name,
|
const char* test_case_name,
|
||||||
const char* name,
|
const char* name,
|
||||||
@ -1219,6 +1221,7 @@ class GTEST_API_ UnitTest {
|
|||||||
friend class Test;
|
friend class Test;
|
||||||
friend class internal::AssertHelper;
|
friend class internal::AssertHelper;
|
||||||
friend class internal::ScopedTrace;
|
friend class internal::ScopedTrace;
|
||||||
|
friend class internal::StreamingListenerTest;
|
||||||
friend Environment* AddGlobalTestEnvironment(Environment* env);
|
friend Environment* AddGlobalTestEnvironment(Environment* env);
|
||||||
friend internal::UnitTestImpl* internal::GetUnitTestImpl();
|
friend internal::UnitTestImpl* internal::GetUnitTestImpl();
|
||||||
friend void internal::ReportFailureInUnknownLocation(
|
friend void internal::ReportFailureInUnknownLocation(
|
||||||
|
@ -58,6 +58,11 @@
|
|||||||
|
|
||||||
#include "gtest/internal/gtest-port.h"
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
#if GTEST_CAN_STREAM_RESULTS_
|
||||||
|
# include <arpa/inet.h> // NOLINT
|
||||||
|
# include <netdb.h> // NOLINT
|
||||||
|
#endif
|
||||||
|
|
||||||
#if GTEST_OS_WINDOWS
|
#if GTEST_OS_WINDOWS
|
||||||
# include <windows.h> // NOLINT
|
# include <windows.h> // NOLINT
|
||||||
#endif // GTEST_OS_WINDOWS
|
#endif // GTEST_OS_WINDOWS
|
||||||
@ -1048,6 +1053,154 @@ class TestResultAccessor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if GTEST_CAN_STREAM_RESULTS_
|
||||||
|
|
||||||
|
// Streams test results to the given port on the given host machine.
|
||||||
|
class StreamingListener : public EmptyTestEventListener {
|
||||||
|
public:
|
||||||
|
// Abstract base class for writing strings to a socket.
|
||||||
|
class AbstractSocketWriter {
|
||||||
|
public:
|
||||||
|
virtual ~AbstractSocketWriter() {}
|
||||||
|
|
||||||
|
// Sends a string to the socket.
|
||||||
|
virtual void Send(const string& message) = 0;
|
||||||
|
|
||||||
|
// Closes the socket.
|
||||||
|
virtual void CloseConnection() {}
|
||||||
|
|
||||||
|
// Sends a string and a newline to the socket.
|
||||||
|
void SendLn(const string& message) {
|
||||||
|
Send(message + "\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Concrete class for actually writing strings to a socket.
|
||||||
|
class SocketWriter : public AbstractSocketWriter {
|
||||||
|
public:
|
||||||
|
SocketWriter(const string& host, const string& port)
|
||||||
|
: sockfd_(-1), host_name_(host), port_num_(port) {
|
||||||
|
MakeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SocketWriter() {
|
||||||
|
if (sockfd_ != -1)
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends a string to the socket.
|
||||||
|
virtual void Send(const string& message) {
|
||||||
|
GTEST_CHECK_(sockfd_ != -1)
|
||||||
|
<< "Send() can be called only when there is a connection.";
|
||||||
|
|
||||||
|
const int len = static_cast<int>(message.length());
|
||||||
|
if (write(sockfd_, message.c_str(), len) != len) {
|
||||||
|
GTEST_LOG_(WARNING)
|
||||||
|
<< "stream_result_to: failed to stream to "
|
||||||
|
<< host_name_ << ":" << port_num_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Creates a client socket and connects to the server.
|
||||||
|
void MakeConnection();
|
||||||
|
|
||||||
|
// Closes the socket.
|
||||||
|
void CloseConnection() {
|
||||||
|
GTEST_CHECK_(sockfd_ != -1)
|
||||||
|
<< "CloseConnection() can be called only when there is a connection.";
|
||||||
|
|
||||||
|
close(sockfd_);
|
||||||
|
sockfd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sockfd_; // socket file descriptor
|
||||||
|
const string host_name_;
|
||||||
|
const string port_num_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter);
|
||||||
|
}; // class SocketWriter
|
||||||
|
|
||||||
|
// Escapes '=', '&', '%', and '\n' characters in str as "%xx".
|
||||||
|
static string UrlEncode(const char* str);
|
||||||
|
|
||||||
|
StreamingListener(const string& host, const string& port)
|
||||||
|
: socket_writer_(new SocketWriter(host, port)) { Start(); }
|
||||||
|
|
||||||
|
explicit StreamingListener(AbstractSocketWriter* socket_writer)
|
||||||
|
: socket_writer_(socket_writer) { Start(); }
|
||||||
|
|
||||||
|
void OnTestProgramStart(const UnitTest& /* unit_test */) {
|
||||||
|
SendLn("event=TestProgramStart");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestProgramEnd(const UnitTest& unit_test) {
|
||||||
|
// Note that Google Test current only report elapsed time for each
|
||||||
|
// test iteration, not for the entire test program.
|
||||||
|
SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed()));
|
||||||
|
|
||||||
|
// Notify the streaming server to stop.
|
||||||
|
socket_writer_->CloseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) {
|
||||||
|
SendLn("event=TestIterationStart&iteration=" +
|
||||||
|
StreamableToString(iteration));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) {
|
||||||
|
SendLn("event=TestIterationEnd&passed=" +
|
||||||
|
FormatBool(unit_test.Passed()) + "&elapsed_time=" +
|
||||||
|
StreamableToString(unit_test.elapsed_time()) + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestCaseStart(const TestCase& test_case) {
|
||||||
|
SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestCaseEnd(const TestCase& test_case) {
|
||||||
|
SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed())
|
||||||
|
+ "&elapsed_time=" + StreamableToString(test_case.elapsed_time())
|
||||||
|
+ "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestStart(const TestInfo& test_info) {
|
||||||
|
SendLn(std::string("event=TestStart&name=") + test_info.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestEnd(const TestInfo& test_info) {
|
||||||
|
SendLn("event=TestEnd&passed=" +
|
||||||
|
FormatBool((test_info.result())->Passed()) +
|
||||||
|
"&elapsed_time=" +
|
||||||
|
StreamableToString((test_info.result())->elapsed_time()) + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestPartResult(const TestPartResult& test_part_result) {
|
||||||
|
const char* file_name = test_part_result.file_name();
|
||||||
|
if (file_name == NULL)
|
||||||
|
file_name = "";
|
||||||
|
SendLn("event=TestPartResult&file=" + UrlEncode(file_name) +
|
||||||
|
"&line=" + StreamableToString(test_part_result.line_number()) +
|
||||||
|
"&message=" + UrlEncode(test_part_result.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sends the given message and a newline to the socket.
|
||||||
|
void SendLn(const string& message) { socket_writer_->SendLn(message); }
|
||||||
|
|
||||||
|
// Called at the start of streaming to notify the receiver what
|
||||||
|
// protocol we are using.
|
||||||
|
void Start() { SendLn("gtest_streaming_protocol_version=1.0"); }
|
||||||
|
|
||||||
|
string FormatBool(bool value) { return value ? "1" : "0"; }
|
||||||
|
|
||||||
|
const scoped_ptr<AbstractSocketWriter> socket_writer_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
|
||||||
|
}; // class StreamingListener
|
||||||
|
|
||||||
|
#endif // GTEST_CAN_STREAM_RESULTS_
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
|
||||||
|
112
src/gtest.cc
112
src/gtest.cc
@ -3230,116 +3230,6 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
|
|||||||
|
|
||||||
#if GTEST_CAN_STREAM_RESULTS_
|
#if GTEST_CAN_STREAM_RESULTS_
|
||||||
|
|
||||||
// Streams test results to the given port on the given host machine.
|
|
||||||
class StreamingListener : public EmptyTestEventListener {
|
|
||||||
public:
|
|
||||||
// Escapes '=', '&', '%', and '\n' characters in str as "%xx".
|
|
||||||
static string UrlEncode(const char* str);
|
|
||||||
|
|
||||||
StreamingListener(const string& host, const string& port)
|
|
||||||
: sockfd_(-1), host_name_(host), port_num_(port) {
|
|
||||||
MakeConnection();
|
|
||||||
SendLn("gtest_streaming_protocol_version=1.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~StreamingListener() {
|
|
||||||
if (sockfd_ != -1)
|
|
||||||
CloseConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestProgramStart(const UnitTest& /* unit_test */) {
|
|
||||||
SendLn("event=TestProgramStart");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestProgramEnd(const UnitTest& unit_test) {
|
|
||||||
// Note that Google Test current only report elapsed time for each
|
|
||||||
// test iteration, not for the entire test program.
|
|
||||||
SendLn("event=TestProgramEnd&passed=" +
|
|
||||||
StreamableToString(unit_test.Passed()));
|
|
||||||
|
|
||||||
// Notify the streaming server to stop.
|
|
||||||
CloseConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) {
|
|
||||||
SendLn("event=TestIterationStart&iteration=" +
|
|
||||||
StreamableToString(iteration));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) {
|
|
||||||
SendLn("event=TestIterationEnd&passed=" +
|
|
||||||
StreamableToString(unit_test.Passed()) + "&elapsed_time=" +
|
|
||||||
StreamableToString(unit_test.elapsed_time()) + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestCaseStart(const TestCase& test_case) {
|
|
||||||
SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestCaseEnd(const TestCase& test_case) {
|
|
||||||
SendLn("event=TestCaseEnd&passed=" + StreamableToString(test_case.Passed())
|
|
||||||
+ "&elapsed_time=" + StreamableToString(test_case.elapsed_time())
|
|
||||||
+ "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestStart(const TestInfo& test_info) {
|
|
||||||
SendLn(std::string("event=TestStart&name=") + test_info.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestEnd(const TestInfo& test_info) {
|
|
||||||
SendLn("event=TestEnd&passed=" +
|
|
||||||
StreamableToString((test_info.result())->Passed()) +
|
|
||||||
"&elapsed_time=" +
|
|
||||||
StreamableToString((test_info.result())->elapsed_time()) + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnTestPartResult(const TestPartResult& test_part_result) {
|
|
||||||
const char* file_name = test_part_result.file_name();
|
|
||||||
if (file_name == NULL)
|
|
||||||
file_name = "";
|
|
||||||
SendLn("event=TestPartResult&file=" + UrlEncode(file_name) +
|
|
||||||
"&line=" + StreamableToString(test_part_result.line_number()) +
|
|
||||||
"&message=" + UrlEncode(test_part_result.message()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Creates a client socket and connects to the server.
|
|
||||||
void MakeConnection();
|
|
||||||
|
|
||||||
// Closes the socket.
|
|
||||||
void CloseConnection() {
|
|
||||||
GTEST_CHECK_(sockfd_ != -1)
|
|
||||||
<< "CloseConnection() can be called only when there is a connection.";
|
|
||||||
|
|
||||||
close(sockfd_);
|
|
||||||
sockfd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends a string to the socket.
|
|
||||||
void Send(const string& message) {
|
|
||||||
GTEST_CHECK_(sockfd_ != -1)
|
|
||||||
<< "Send() can be called only when there is a connection.";
|
|
||||||
|
|
||||||
const int len = static_cast<int>(message.length());
|
|
||||||
if (write(sockfd_, message.c_str(), len) != len) {
|
|
||||||
GTEST_LOG_(WARNING)
|
|
||||||
<< "stream_result_to: failed to stream to "
|
|
||||||
<< host_name_ << ":" << port_num_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends a string and a newline to the socket.
|
|
||||||
void SendLn(const string& message) {
|
|
||||||
Send(message + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int sockfd_; // socket file descriptor
|
|
||||||
const string host_name_;
|
|
||||||
const string port_num_;
|
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
|
|
||||||
}; // class StreamingListener
|
|
||||||
|
|
||||||
// Checks if str contains '=', '&', '%' or '\n' characters. If yes,
|
// Checks if str contains '=', '&', '%' or '\n' characters. If yes,
|
||||||
// replaces them by "%xx" where xx is their hexadecimal value. For
|
// replaces them by "%xx" where xx is their hexadecimal value. For
|
||||||
// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
|
// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
|
||||||
@ -3364,7 +3254,7 @@ string StreamingListener::UrlEncode(const char* str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamingListener::MakeConnection() {
|
void StreamingListener::SocketWriter::MakeConnection() {
|
||||||
GTEST_CHECK_(sockfd_ == -1)
|
GTEST_CHECK_(sockfd_ == -1)
|
||||||
<< "MakeConnection() can't be called when there is already a connection.";
|
<< "MakeConnection() can't be called when there is already a connection.";
|
||||||
|
|
||||||
|
@ -79,6 +79,81 @@ TEST(CommandLineFlagsTest, CanBeAccessedInCodeOnceGTestHIsIncluded) {
|
|||||||
namespace testing {
|
namespace testing {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
#if GTEST_CAN_STREAM_RESULTS_
|
||||||
|
|
||||||
|
class StreamingListenerTest : public Test {
|
||||||
|
public:
|
||||||
|
class FakeSocketWriter : public StreamingListener::AbstractSocketWriter {
|
||||||
|
public:
|
||||||
|
// Sends a string to the socket.
|
||||||
|
virtual void Send(const string& message) { output_ += message; }
|
||||||
|
|
||||||
|
string output_;
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamingListenerTest()
|
||||||
|
: fake_sock_writer_(new FakeSocketWriter),
|
||||||
|
streamer_(fake_sock_writer_),
|
||||||
|
test_info_obj_("FooTest", "Bar", NULL, NULL, 0, NULL) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
string* output() { return &(fake_sock_writer_->output_); }
|
||||||
|
|
||||||
|
FakeSocketWriter* const fake_sock_writer_;
|
||||||
|
StreamingListener streamer_;
|
||||||
|
UnitTest unit_test_;
|
||||||
|
TestInfo test_info_obj_; // The name test_info_ was taken by testing::Test.
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestProgramEnd) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestProgramEnd(unit_test_);
|
||||||
|
EXPECT_EQ("event=TestProgramEnd&passed=1\n", *output());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestIterationEnd) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestIterationEnd(unit_test_, 42);
|
||||||
|
EXPECT_EQ("event=TestIterationEnd&passed=1&elapsed_time=0ms\n", *output());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestCaseStart) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestCaseStart(TestCase("FooTest", "Bar", NULL, NULL));
|
||||||
|
EXPECT_EQ("event=TestCaseStart&name=FooTest\n", *output());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestCaseEnd) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestCaseEnd(TestCase("FooTest", "Bar", NULL, NULL));
|
||||||
|
EXPECT_EQ("event=TestCaseEnd&passed=1&elapsed_time=0ms\n", *output());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestStart) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestStart(test_info_obj_);
|
||||||
|
EXPECT_EQ("event=TestStart&name=Bar\n", *output());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestEnd) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestEnd(test_info_obj_);
|
||||||
|
EXPECT_EQ("event=TestEnd&passed=1&elapsed_time=0ms\n", *output());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StreamingListenerTest, OnTestPartResult) {
|
||||||
|
*output() = "";
|
||||||
|
streamer_.OnTestPartResult(TestPartResult(
|
||||||
|
TestPartResult::kFatalFailure, "foo.cc", 42, "failed=\n&%"));
|
||||||
|
|
||||||
|
// Meta characters in the failure message should be properly escaped.
|
||||||
|
EXPECT_EQ(
|
||||||
|
"event=TestPartResult&file=foo.cc&line=42&message=failed%3D%0A%26%25\n",
|
||||||
|
*output());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GTEST_CAN_STREAM_RESULTS_
|
||||||
|
|
||||||
// Provides access to otherwise private parts of the TestEventListeners class
|
// Provides access to otherwise private parts of the TestEventListeners class
|
||||||
// that are needed to test it.
|
// that are needed to test it.
|
||||||
class TestEventListenersAccessor {
|
class TestEventListenersAccessor {
|
||||||
|
Loading…
Reference in New Issue
Block a user