Fixes gUnit streaming output format.
This commit is contained in:
parent
cc1fdb58ca
commit
ba072ccca4
@ -154,6 +154,7 @@ class ExecDeathTest;
|
||||
class NoExecDeathTest;
|
||||
class FinalSuccessChecker;
|
||||
class GTestFlagSaver;
|
||||
class StreamingListenerTest;
|
||||
class TestResultAccessor;
|
||||
class TestEventListenersAccessor;
|
||||
class TestEventRepeater;
|
||||
@ -679,6 +680,7 @@ class GTEST_API_ TestInfo {
|
||||
friend class Test;
|
||||
friend class TestCase;
|
||||
friend class internal::UnitTestImpl;
|
||||
friend class internal::StreamingListenerTest;
|
||||
friend TestInfo* internal::MakeAndRegisterTestInfo(
|
||||
const char* test_case_name,
|
||||
const char* name,
|
||||
@ -1219,6 +1221,7 @@ class GTEST_API_ UnitTest {
|
||||
friend class Test;
|
||||
friend class internal::AssertHelper;
|
||||
friend class internal::ScopedTrace;
|
||||
friend class internal::StreamingListenerTest;
|
||||
friend Environment* AddGlobalTestEnvironment(Environment* env);
|
||||
friend internal::UnitTestImpl* internal::GetUnitTestImpl();
|
||||
friend void internal::ReportFailureInUnknownLocation(
|
||||
|
@ -58,6 +58,11 @@
|
||||
|
||||
#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
|
||||
# include <windows.h> // NOLINT
|
||||
#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 testing
|
||||
|
||||
|
112
src/gtest.cc
112
src/gtest.cc
@ -3230,116 +3230,6 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
|
||||
|
||||
#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,
|
||||
// replaces them by "%xx" where xx is their hexadecimal value. For
|
||||
// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
|
||||
@ -3364,7 +3254,7 @@ string StreamingListener::UrlEncode(const char* str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void StreamingListener::MakeConnection() {
|
||||
void StreamingListener::SocketWriter::MakeConnection() {
|
||||
GTEST_CHECK_(sockfd_ == -1)
|
||||
<< "MakeConnection() can't be called when there is already a connection.";
|
||||
|
||||
|
@ -79,6 +79,81 @@ TEST(CommandLineFlagsTest, CanBeAccessedInCodeOnceGTestHIsIncluded) {
|
||||
namespace testing {
|
||||
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
|
||||
// that are needed to test it.
|
||||
class TestEventListenersAccessor {
|
||||
|
Loading…
Reference in New Issue
Block a user