Implements support for calling Test::RecordProperty() outside of a test.
This commit is contained in:
parent
5f18b68bfc
commit
f5fa71f728
|
@ -158,6 +158,7 @@ class StreamingListenerTest;
|
||||||
class TestResultAccessor;
|
class TestResultAccessor;
|
||||||
class TestEventListenersAccessor;
|
class TestEventListenersAccessor;
|
||||||
class TestEventRepeater;
|
class TestEventRepeater;
|
||||||
|
class UnitTestRecordPropertyTestHelper;
|
||||||
class WindowsDeathTest;
|
class WindowsDeathTest;
|
||||||
class UnitTestImpl* GetUnitTestImpl();
|
class UnitTestImpl* GetUnitTestImpl();
|
||||||
void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
|
void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
|
||||||
|
@ -381,20 +382,21 @@ class GTEST_API_ Test {
|
||||||
// non-fatal) failure.
|
// non-fatal) failure.
|
||||||
static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
|
static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
|
||||||
|
|
||||||
// Logs a property for the current test. Only the last value for a given
|
// Logs a property for the current test, test case, or for the entire
|
||||||
// key is remembered.
|
// invocation of the test program when used outside of the context of a
|
||||||
// These are public static so they can be called from utility functions
|
// test case. Only the last value for a given key is remembered. These
|
||||||
// that are not members of the test fixture.
|
// are public static so they can be called from utility functions that are
|
||||||
// The arguments are const char* instead strings, as Google Test is used
|
// not members of the test fixture. Calls to RecordProperty made during
|
||||||
// on platforms where string doesn't compile.
|
// lifespan of the test (from the moment its constructor starts to the
|
||||||
//
|
// moment its destructor finishes) will be output in XML as attributes of
|
||||||
// Note that a driving consideration for these RecordProperty methods
|
// the <testcase> element. Properties recorded from fixture's
|
||||||
// was to produce xml output suited to the Greenspan charting utility,
|
// SetUpTestCase or TearDownTestCase are logged as attributes of the
|
||||||
// which at present will only chart values that fit in a 32-bit int. It
|
// corresponding <testsuite> element. Calls to RecordProperty made in the
|
||||||
// is the user's responsibility to restrict their values to 32-bit ints
|
// global context (before or after invocation of RUN_ALL_TESTS and from
|
||||||
// if they intend them to be used with Greenspan.
|
// SetUp/TearDown method of Environment objects registered with Google
|
||||||
static void RecordProperty(const char* key, const char* value);
|
// Test) will be output as attributes of the <testsuites> element.
|
||||||
static void RecordProperty(const char* key, int value);
|
static void RecordProperty(const std::string& key, const std::string& value);
|
||||||
|
static void RecordProperty(const std::string& key, int value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Creates a Test object.
|
// Creates a Test object.
|
||||||
|
@ -463,7 +465,7 @@ class TestProperty {
|
||||||
// C'tor. TestProperty does NOT have a default constructor.
|
// C'tor. TestProperty does NOT have a default constructor.
|
||||||
// Always use this constructor (with parameters) to create a
|
// Always use this constructor (with parameters) to create a
|
||||||
// TestProperty object.
|
// TestProperty object.
|
||||||
TestProperty(const char* a_key, const char* a_value) :
|
TestProperty(const std::string& a_key, const std::string& a_value) :
|
||||||
key_(a_key), value_(a_value) {
|
key_(a_key), value_(a_value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,7 +480,7 @@ class TestProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets a new value, overriding the one supplied in the constructor.
|
// Sets a new value, overriding the one supplied in the constructor.
|
||||||
void SetValue(const char* new_value) {
|
void SetValue(const std::string& new_value) {
|
||||||
value_ = new_value;
|
value_ = new_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,6 +539,7 @@ class GTEST_API_ TestResult {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class TestInfo;
|
friend class TestInfo;
|
||||||
|
friend class TestCase;
|
||||||
friend class UnitTest;
|
friend class UnitTest;
|
||||||
friend class internal::DefaultGlobalTestPartResultReporter;
|
friend class internal::DefaultGlobalTestPartResultReporter;
|
||||||
friend class internal::ExecDeathTest;
|
friend class internal::ExecDeathTest;
|
||||||
|
@ -561,13 +564,16 @@ class GTEST_API_ TestResult {
|
||||||
// a non-fatal failure if invalid (e.g., if it conflicts with reserved
|
// a non-fatal failure if invalid (e.g., if it conflicts with reserved
|
||||||
// key names). If a property is already recorded for the same key, the
|
// key names). If a property is already recorded for the same key, the
|
||||||
// value will be updated, rather than storing multiple values for the same
|
// value will be updated, rather than storing multiple values for the same
|
||||||
// key.
|
// key. xml_element specifies the element for which the property is being
|
||||||
void RecordProperty(const TestProperty& test_property);
|
// recorded and is used for validation.
|
||||||
|
void RecordProperty(const std::string& xml_element,
|
||||||
|
const TestProperty& test_property);
|
||||||
|
|
||||||
// Adds a failure if the key is a reserved attribute of Google Test
|
// Adds a failure if the key is a reserved attribute of Google Test
|
||||||
// testcase tags. Returns true if the property is valid.
|
// testcase tags. Returns true if the property is valid.
|
||||||
// TODO(russr): Validate attribute names are legal and human readable.
|
// TODO(russr): Validate attribute names are legal and human readable.
|
||||||
static bool ValidateTestProperty(const TestProperty& test_property);
|
static bool ValidateTestProperty(const std::string& xml_element,
|
||||||
|
const TestProperty& test_property);
|
||||||
|
|
||||||
// Adds a test part result to the list.
|
// Adds a test part result to the list.
|
||||||
void AddTestPartResult(const TestPartResult& test_part_result);
|
void AddTestPartResult(const TestPartResult& test_part_result);
|
||||||
|
@ -792,6 +798,10 @@ class GTEST_API_ TestCase {
|
||||||
// total_test_count() - 1. If i is not in that range, returns NULL.
|
// total_test_count() - 1. If i is not in that range, returns NULL.
|
||||||
const TestInfo* GetTestInfo(int i) const;
|
const TestInfo* GetTestInfo(int i) const;
|
||||||
|
|
||||||
|
// Returns the TestResult that holds test properties recorded during
|
||||||
|
// execution of SetUpTestCase and TearDownTestCase.
|
||||||
|
const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Test;
|
friend class Test;
|
||||||
friend class internal::UnitTestImpl;
|
friend class internal::UnitTestImpl;
|
||||||
|
@ -880,6 +890,9 @@ class GTEST_API_ TestCase {
|
||||||
bool should_run_;
|
bool should_run_;
|
||||||
// Elapsed time, in milliseconds.
|
// Elapsed time, in milliseconds.
|
||||||
TimeInMillis elapsed_time_;
|
TimeInMillis elapsed_time_;
|
||||||
|
// Holds test properties recorded during execution of SetUpTestCase and
|
||||||
|
// TearDownTestCase.
|
||||||
|
TestResult ad_hoc_test_result_;
|
||||||
|
|
||||||
// We disallow copying TestCases.
|
// We disallow copying TestCases.
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase);
|
||||||
|
@ -1165,6 +1178,10 @@ class GTEST_API_ UnitTest {
|
||||||
// total_test_case_count() - 1. If i is not in that range, returns NULL.
|
// total_test_case_count() - 1. If i is not in that range, returns NULL.
|
||||||
const TestCase* GetTestCase(int i) const;
|
const TestCase* GetTestCase(int i) const;
|
||||||
|
|
||||||
|
// Returns the TestResult containing information on test failures and
|
||||||
|
// properties logged outside of individual test cases.
|
||||||
|
const TestResult& ad_hoc_test_result() const;
|
||||||
|
|
||||||
// Returns the list of event listeners that can be used to track events
|
// Returns the list of event listeners that can be used to track events
|
||||||
// inside Google Test.
|
// inside Google Test.
|
||||||
TestEventListeners& listeners();
|
TestEventListeners& listeners();
|
||||||
|
@ -1192,9 +1209,12 @@ class GTEST_API_ UnitTest {
|
||||||
const std::string& os_stack_trace)
|
const std::string& os_stack_trace)
|
||||||
GTEST_LOCK_EXCLUDED_(mutex_);
|
GTEST_LOCK_EXCLUDED_(mutex_);
|
||||||
|
|
||||||
// Adds a TestProperty to the current TestResult object. If the result already
|
// Adds a TestProperty to the current TestResult object when invoked from
|
||||||
// contains a property with the same key, the value will be updated.
|
// inside a test, to current TestCase's ad_hoc_test_result_ when invoked
|
||||||
void RecordPropertyForCurrentTest(const char* key, const char* value);
|
// from SetUpTestCase or TearDownTestCase, or to the global property set
|
||||||
|
// when invoked elsewhere. If the result already contains a property with
|
||||||
|
// the same key, the value will be updated.
|
||||||
|
void RecordProperty(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
// Gets the i-th test case among all the test cases. i can range from 0 to
|
// Gets the i-th test case among all the test cases. i can range from 0 to
|
||||||
// total_test_case_count() - 1. If i is not in that range, returns NULL.
|
// total_test_case_count() - 1. If i is not in that range, returns NULL.
|
||||||
|
@ -1210,6 +1230,7 @@ class GTEST_API_ UnitTest {
|
||||||
friend class internal::AssertHelper;
|
friend class internal::AssertHelper;
|
||||||
friend class internal::ScopedTrace;
|
friend class internal::ScopedTrace;
|
||||||
friend class internal::StreamingListenerTest;
|
friend class internal::StreamingListenerTest;
|
||||||
|
friend class internal::UnitTestRecordPropertyTestHelper;
|
||||||
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(
|
||||||
|
|
|
@ -348,8 +348,7 @@ class TestPropertyKeyIs {
|
||||||
// Constructor.
|
// Constructor.
|
||||||
//
|
//
|
||||||
// TestPropertyKeyIs has NO default constructor.
|
// TestPropertyKeyIs has NO default constructor.
|
||||||
explicit TestPropertyKeyIs(const char* key)
|
explicit TestPropertyKeyIs(const std::string& key) : key_(key) {}
|
||||||
: key_(key) {}
|
|
||||||
|
|
||||||
// Returns true iff the test name of test property matches on key_.
|
// Returns true iff the test name of test property matches on key_.
|
||||||
bool operator()(const TestProperty& test_property) const {
|
bool operator()(const TestProperty& test_property) const {
|
||||||
|
@ -712,6 +711,12 @@ class GTEST_API_ UnitTestImpl {
|
||||||
ad_hoc_test_result_.Clear();
|
ad_hoc_test_result_.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds a TestProperty to the current TestResult object when invoked in a
|
||||||
|
// context of a test or a test case, or to the global property set. If the
|
||||||
|
// result already contains a property with the same key, the value will be
|
||||||
|
// updated.
|
||||||
|
void RecordProperty(const TestProperty& test_property);
|
||||||
|
|
||||||
enum ReactionToSharding {
|
enum ReactionToSharding {
|
||||||
HONOR_SHARDING_PROTOCOL,
|
HONOR_SHARDING_PROTOCOL,
|
||||||
IGNORE_SHARDING_PROTOCOL
|
IGNORE_SHARDING_PROTOCOL
|
||||||
|
@ -1038,8 +1043,9 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
|
||||||
class TestResultAccessor {
|
class TestResultAccessor {
|
||||||
public:
|
public:
|
||||||
static void RecordProperty(TestResult* test_result,
|
static void RecordProperty(TestResult* test_result,
|
||||||
|
const std::string& xml_element,
|
||||||
const TestProperty& property) {
|
const TestProperty& property) {
|
||||||
test_result->RecordProperty(property);
|
test_result->RecordProperty(xml_element, property);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ClearTestPartResults(TestResult* test_result) {
|
static void ClearTestPartResults(TestResult* test_result) {
|
||||||
|
|
320
src/gtest.cc
320
src/gtest.cc
|
@ -1716,8 +1716,9 @@ void TestResult::AddTestPartResult(const TestPartResult& test_part_result) {
|
||||||
// Adds a test property to the list. If a property with the same key as the
|
// Adds a test property to the list. If a property with the same key as the
|
||||||
// supplied property is already represented, the value of this test_property
|
// supplied property is already represented, the value of this test_property
|
||||||
// replaces the old value for that key.
|
// replaces the old value for that key.
|
||||||
void TestResult::RecordProperty(const TestProperty& test_property) {
|
void TestResult::RecordProperty(const std::string& xml_element,
|
||||||
if (!ValidateTestProperty(test_property)) {
|
const TestProperty& test_property) {
|
||||||
|
if (!ValidateTestProperty(xml_element, test_property)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
internal::MutexLock lock(&test_properites_mutex_);
|
internal::MutexLock lock(&test_properites_mutex_);
|
||||||
|
@ -1731,21 +1732,94 @@ void TestResult::RecordProperty(const TestProperty& test_property) {
|
||||||
property_with_matching_key->SetValue(test_property.value());
|
property_with_matching_key->SetValue(test_property.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a failure if the key is a reserved attribute of Google Test
|
// The list of reserved attributes used in the <testsuites> element of XML
|
||||||
// testcase tags. Returns true if the property is valid.
|
// output.
|
||||||
bool TestResult::ValidateTestProperty(const TestProperty& test_property) {
|
static const char* const kReservedTestSuitesAttributes[] = {
|
||||||
const std::string& key = test_property.key();
|
"disabled",
|
||||||
if (key == "name" || key == "status" || key == "time" || key == "classname") {
|
"errors",
|
||||||
ADD_FAILURE()
|
"failures",
|
||||||
<< "Reserved key used in RecordProperty(): "
|
"name",
|
||||||
<< key
|
"random_seed",
|
||||||
<< " ('name', 'status', 'time', and 'classname' are reserved by "
|
"tests",
|
||||||
<< GTEST_NAME_ << ")";
|
"time",
|
||||||
|
"timestamp"
|
||||||
|
};
|
||||||
|
|
||||||
|
// The list of reserved attributes used in the <testsuite> element of XML
|
||||||
|
// output.
|
||||||
|
static const char* const kReservedTestSuiteAttributes[] = {
|
||||||
|
"disabled",
|
||||||
|
"errors",
|
||||||
|
"failures",
|
||||||
|
"name",
|
||||||
|
"tests",
|
||||||
|
"time"
|
||||||
|
};
|
||||||
|
|
||||||
|
// The list of reserved attributes used in the <testcase> element of XML output.
|
||||||
|
static const char* const kReservedTestCaseAttributes[] = {
|
||||||
|
"classname",
|
||||||
|
"name",
|
||||||
|
"status",
|
||||||
|
"time",
|
||||||
|
"type_param",
|
||||||
|
"value_param"
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int kSize>
|
||||||
|
std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) {
|
||||||
|
return std::vector<std::string>(array, array + kSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> GetReservedAttributesForElement(
|
||||||
|
const std::string& xml_element) {
|
||||||
|
if (xml_element == "testsuites") {
|
||||||
|
return ArrayAsVector(kReservedTestSuitesAttributes);
|
||||||
|
} else if (xml_element == "testsuite") {
|
||||||
|
return ArrayAsVector(kReservedTestSuiteAttributes);
|
||||||
|
} else if (xml_element == "testcase") {
|
||||||
|
return ArrayAsVector(kReservedTestCaseAttributes);
|
||||||
|
} else {
|
||||||
|
GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element;
|
||||||
|
}
|
||||||
|
// This code is unreachable but some compilers may not realizes that.
|
||||||
|
return std::vector<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string FormatWordList(const std::vector<std::string>& words) {
|
||||||
|
Message word_list;
|
||||||
|
for (size_t i = 0; i < words.size(); ++i) {
|
||||||
|
if (i > 0 && words.size() > 2) {
|
||||||
|
word_list << ", ";
|
||||||
|
}
|
||||||
|
if (i == words.size() - 1) {
|
||||||
|
word_list << "and ";
|
||||||
|
}
|
||||||
|
word_list << "'" << words[i] << "'";
|
||||||
|
}
|
||||||
|
return word_list.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateTestPropertyName(const std::string& property_name,
|
||||||
|
const std::vector<std::string>& reserved_names) {
|
||||||
|
if (std::find(reserved_names.begin(), reserved_names.end(), property_name) !=
|
||||||
|
reserved_names.end()) {
|
||||||
|
ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name
|
||||||
|
<< " (" << FormatWordList(reserved_names)
|
||||||
|
<< " are reserved by " << GTEST_NAME_ << ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds a failure if the key is a reserved attribute of the element named
|
||||||
|
// xml_element. Returns true if the property is valid.
|
||||||
|
bool TestResult::ValidateTestProperty(const std::string& xml_element,
|
||||||
|
const TestProperty& test_property) {
|
||||||
|
return ValidateTestPropertyName(test_property.key(),
|
||||||
|
GetReservedAttributesForElement(xml_element));
|
||||||
|
}
|
||||||
|
|
||||||
// Clears the object.
|
// Clears the object.
|
||||||
void TestResult::Clear() {
|
void TestResult::Clear() {
|
||||||
test_part_results_.clear();
|
test_part_results_.clear();
|
||||||
|
@ -1821,12 +1895,12 @@ void Test::TearDown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows user supplied key value pairs to be recorded for later output.
|
// Allows user supplied key value pairs to be recorded for later output.
|
||||||
void Test::RecordProperty(const char* key, const char* value) {
|
void Test::RecordProperty(const std::string& key, const std::string& value) {
|
||||||
UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value);
|
UnitTest::GetInstance()->RecordProperty(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows user supplied key value pairs to be recorded for later output.
|
// Allows user supplied key value pairs to be recorded for later output.
|
||||||
void Test::RecordProperty(const char* key, int value) {
|
void Test::RecordProperty(const std::string& key, int value) {
|
||||||
Message value_message;
|
Message value_message;
|
||||||
value_message << value;
|
value_message << value;
|
||||||
RecordProperty(key, value_message.GetString().c_str());
|
RecordProperty(key, value_message.GetString().c_str());
|
||||||
|
@ -2355,6 +2429,7 @@ void TestCase::Run() {
|
||||||
|
|
||||||
// Clears the results of all tests in this test case.
|
// Clears the results of all tests in this test case.
|
||||||
void TestCase::ClearResult() {
|
void TestCase::ClearResult() {
|
||||||
|
ad_hoc_test_result_.Clear();
|
||||||
ForEach(test_info_list_, TestInfo::ClearTestResult);
|
ForEach(test_info_list_, TestInfo::ClearTestResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2925,13 +3000,13 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
|
||||||
// is_attribute is true, the text is meant to appear as an attribute
|
// is_attribute is true, the text is meant to appear as an attribute
|
||||||
// value, and normalizable whitespace is preserved by replacing it
|
// value, and normalizable whitespace is preserved by replacing it
|
||||||
// with character references.
|
// with character references.
|
||||||
static std::string EscapeXml(const char* str, bool is_attribute);
|
static std::string EscapeXml(const std::string& str, bool is_attribute);
|
||||||
|
|
||||||
// Returns the given string with all characters invalid in XML removed.
|
// Returns the given string with all characters invalid in XML removed.
|
||||||
static string RemoveInvalidXmlCharacters(const string& str);
|
static std::string RemoveInvalidXmlCharacters(const std::string& str);
|
||||||
|
|
||||||
// Convenience wrapper around EscapeXml when str is an attribute value.
|
// Convenience wrapper around EscapeXml when str is an attribute value.
|
||||||
static std::string EscapeXmlAttribute(const char* str) {
|
static std::string EscapeXmlAttribute(const std::string& str) {
|
||||||
return EscapeXml(str, true);
|
return EscapeXml(str, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2940,6 +3015,13 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
|
||||||
return EscapeXml(str, false);
|
return EscapeXml(str, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verifies that the given attribute belongs to the given element and
|
||||||
|
// streams the attribute as XML.
|
||||||
|
static void OutputXmlAttribute(std::ostream* stream,
|
||||||
|
const std::string& element_name,
|
||||||
|
const std::string& name,
|
||||||
|
const std::string& value);
|
||||||
|
|
||||||
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
|
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
|
||||||
static void OutputXmlCDataSection(::std::ostream* stream, const char* data);
|
static void OutputXmlCDataSection(::std::ostream* stream, const char* data);
|
||||||
|
|
||||||
|
@ -2949,10 +3031,12 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
|
||||||
const TestInfo& test_info);
|
const TestInfo& test_info);
|
||||||
|
|
||||||
// Prints an XML representation of a TestCase object
|
// Prints an XML representation of a TestCase object
|
||||||
static void PrintXmlTestCase(FILE* out, const TestCase& test_case);
|
static void PrintXmlTestCase(::std::ostream* stream,
|
||||||
|
const TestCase& test_case);
|
||||||
|
|
||||||
// Prints an XML summary of unit_test to output stream out.
|
// Prints an XML summary of unit_test to output stream out.
|
||||||
static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test);
|
static void PrintXmlUnitTest(::std::ostream* stream,
|
||||||
|
const UnitTest& unit_test);
|
||||||
|
|
||||||
// Produces a string representing the test properties in a result as space
|
// Produces a string representing the test properties in a result as space
|
||||||
// delimited XML attributes based on the property key="value" pairs.
|
// delimited XML attributes based on the property key="value" pairs.
|
||||||
|
@ -3003,7 +3087,9 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
PrintXmlUnitTest(xmlout, unit_test);
|
std::stringstream stream;
|
||||||
|
PrintXmlUnitTest(&stream, unit_test);
|
||||||
|
fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
|
||||||
fclose(xmlout);
|
fclose(xmlout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3020,12 +3106,12 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
|
||||||
// TODO(wan): It might be nice to have a minimally invasive, human-readable
|
// TODO(wan): It might be nice to have a minimally invasive, human-readable
|
||||||
// escaping scheme for invalid characters, rather than dropping them.
|
// escaping scheme for invalid characters, rather than dropping them.
|
||||||
std::string XmlUnitTestResultPrinter::EscapeXml(
|
std::string XmlUnitTestResultPrinter::EscapeXml(
|
||||||
const char* str, bool is_attribute) {
|
const std::string& str, bool is_attribute) {
|
||||||
Message m;
|
Message m;
|
||||||
|
|
||||||
if (str != NULL) {
|
for (size_t i = 0; i < str.size(); ++i) {
|
||||||
for (const char* src = str; *src; ++src) {
|
const char ch = str[i];
|
||||||
switch (*src) {
|
switch (ch) {
|
||||||
case '<':
|
case '<':
|
||||||
m << "<";
|
m << "<";
|
||||||
break;
|
break;
|
||||||
|
@ -3048,17 +3134,16 @@ std::string XmlUnitTestResultPrinter::EscapeXml(
|
||||||
m << '"';
|
m << '"';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (IsValidXmlCharacter(*src)) {
|
if (IsValidXmlCharacter(ch)) {
|
||||||
if (is_attribute && IsNormalizableWhitespace(*src))
|
if (is_attribute && IsNormalizableWhitespace(ch))
|
||||||
m << "&#x" << String::FormatByte(static_cast<unsigned char>(*src))
|
m << "&#x" << String::FormatByte(static_cast<unsigned char>(ch))
|
||||||
<< ";";
|
<< ";";
|
||||||
else
|
else
|
||||||
m << *src;
|
m << ch;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return m.GetString();
|
return m.GetString();
|
||||||
}
|
}
|
||||||
|
@ -3066,10 +3151,11 @@ std::string XmlUnitTestResultPrinter::EscapeXml(
|
||||||
// Returns the given string with all characters invalid in XML removed.
|
// Returns the given string with all characters invalid in XML removed.
|
||||||
// Currently invalid characters are dropped from the string. An
|
// Currently invalid characters are dropped from the string. An
|
||||||
// alternative is to replace them with certain characters such as . or ?.
|
// alternative is to replace them with certain characters such as . or ?.
|
||||||
string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) {
|
std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(
|
||||||
string output;
|
const std::string& str) {
|
||||||
|
std::string output;
|
||||||
output.reserve(str.size());
|
output.reserve(str.size());
|
||||||
for (string::const_iterator it = str.begin(); it != str.end(); ++it)
|
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
|
||||||
if (IsValidXmlCharacter(*it))
|
if (IsValidXmlCharacter(*it))
|
||||||
output.push_back(*it);
|
output.push_back(*it);
|
||||||
|
|
||||||
|
@ -3145,30 +3231,47 @@ void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream,
|
||||||
*stream << "]]>";
|
*stream << "]]>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XmlUnitTestResultPrinter::OutputXmlAttribute(
|
||||||
|
std::ostream* stream,
|
||||||
|
const std::string& element_name,
|
||||||
|
const std::string& name,
|
||||||
|
const std::string& value) {
|
||||||
|
const std::vector<std::string>& allowed_names =
|
||||||
|
GetReservedAttributesForElement(element_name);
|
||||||
|
|
||||||
|
GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
|
||||||
|
allowed_names.end())
|
||||||
|
<< "Attribute " << name << " is not allowed for element <" << element_name
|
||||||
|
<< ">.";
|
||||||
|
|
||||||
|
*stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
// Prints an XML representation of a TestInfo object.
|
// Prints an XML representation of a TestInfo object.
|
||||||
// TODO(wan): There is also value in printing properties with the plain printer.
|
// TODO(wan): There is also value in printing properties with the plain printer.
|
||||||
void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
|
void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
|
||||||
const char* test_case_name,
|
const char* test_case_name,
|
||||||
const TestInfo& test_info) {
|
const TestInfo& test_info) {
|
||||||
const TestResult& result = *test_info.result();
|
const TestResult& result = *test_info.result();
|
||||||
*stream << " <testcase name=\""
|
const std::string kTestcase = "testcase";
|
||||||
<< EscapeXmlAttribute(test_info.name()).c_str() << "\"";
|
|
||||||
|
*stream << " <testcase";
|
||||||
|
OutputXmlAttribute(stream, kTestcase, "name", test_info.name());
|
||||||
|
|
||||||
if (test_info.value_param() != NULL) {
|
if (test_info.value_param() != NULL) {
|
||||||
*stream << " value_param=\"" << EscapeXmlAttribute(test_info.value_param())
|
OutputXmlAttribute(stream, kTestcase, "value_param",
|
||||||
<< "\"";
|
test_info.value_param());
|
||||||
}
|
}
|
||||||
if (test_info.type_param() != NULL) {
|
if (test_info.type_param() != NULL) {
|
||||||
*stream << " type_param=\"" << EscapeXmlAttribute(test_info.type_param())
|
OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param());
|
||||||
<< "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*stream << " status=\""
|
OutputXmlAttribute(stream, kTestcase, "status",
|
||||||
<< (test_info.should_run() ? "run" : "notrun")
|
test_info.should_run() ? "run" : "notrun");
|
||||||
<< "\" time=\""
|
OutputXmlAttribute(stream, kTestcase, "time",
|
||||||
<< FormatTimeInMillisAsSeconds(result.elapsed_time())
|
FormatTimeInMillisAsSeconds(result.elapsed_time()));
|
||||||
<< "\" classname=\"" << EscapeXmlAttribute(test_case_name).c_str()
|
OutputXmlAttribute(stream, kTestcase, "classname", test_case_name);
|
||||||
<< "\"" << TestPropertiesAsXmlAttributes(result).c_str();
|
*stream << TestPropertiesAsXmlAttributes(result);
|
||||||
|
|
||||||
int failures = 0;
|
int failures = 0;
|
||||||
for (int i = 0; i < result.total_part_count(); ++i) {
|
for (int i = 0; i < result.total_part_count(); ++i) {
|
||||||
|
@ -3196,45 +3299,64 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints an XML representation of a TestCase object
|
// Prints an XML representation of a TestCase object
|
||||||
void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out,
|
void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream,
|
||||||
const TestCase& test_case) {
|
const TestCase& test_case) {
|
||||||
fprintf(out,
|
const std::string kTestsuite = "testsuite";
|
||||||
" <testsuite name=\"%s\" tests=\"%d\" failures=\"%d\" "
|
*stream << " <" << kTestsuite;
|
||||||
"disabled=\"%d\" ",
|
OutputXmlAttribute(stream, kTestsuite, "name", test_case.name());
|
||||||
EscapeXmlAttribute(test_case.name()).c_str(),
|
OutputXmlAttribute(stream, kTestsuite, "tests",
|
||||||
test_case.total_test_count(),
|
StreamableToString(test_case.total_test_count()));
|
||||||
test_case.failed_test_count(),
|
OutputXmlAttribute(stream, kTestsuite, "failures",
|
||||||
test_case.disabled_test_count());
|
StreamableToString(test_case.failed_test_count()));
|
||||||
fprintf(out,
|
OutputXmlAttribute(stream, kTestsuite, "disabled",
|
||||||
"errors=\"0\" time=\"%s\">\n",
|
StreamableToString(test_case.disabled_test_count()));
|
||||||
FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str());
|
OutputXmlAttribute(stream, kTestsuite, "errors", "0");
|
||||||
for (int i = 0; i < test_case.total_test_count(); ++i) {
|
OutputXmlAttribute(stream, kTestsuite, "time",
|
||||||
::std::stringstream stream;
|
FormatTimeInMillisAsSeconds(test_case.elapsed_time()));
|
||||||
OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i));
|
*stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result())
|
||||||
fprintf(out, "%s", StringStreamToString(&stream).c_str());
|
<< ">\n";
|
||||||
}
|
|
||||||
fprintf(out, " </testsuite>\n");
|
for (int i = 0; i < test_case.total_test_count(); ++i)
|
||||||
|
OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i));
|
||||||
|
*stream << " </" << kTestsuite << ">\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints an XML summary of unit_test to output stream out.
|
// Prints an XML summary of unit_test to output stream out.
|
||||||
void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out,
|
void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream,
|
||||||
const UnitTest& unit_test) {
|
const UnitTest& unit_test) {
|
||||||
fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
const std::string kTestsuites = "testsuites";
|
||||||
fprintf(out,
|
|
||||||
"<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\" "
|
*stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||||
"errors=\"0\" timestamp=\"%s\" time=\"%s\" ",
|
*stream << "<" << kTestsuites;
|
||||||
unit_test.total_test_count(),
|
|
||||||
unit_test.failed_test_count(),
|
OutputXmlAttribute(stream, kTestsuites, "tests",
|
||||||
unit_test.disabled_test_count(),
|
StreamableToString(unit_test.total_test_count()));
|
||||||
FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()).c_str(),
|
OutputXmlAttribute(stream, kTestsuites, "failures",
|
||||||
FormatTimeInMillisAsSeconds(unit_test.elapsed_time()).c_str());
|
StreamableToString(unit_test.failed_test_count()));
|
||||||
|
OutputXmlAttribute(stream, kTestsuites, "disabled",
|
||||||
|
StreamableToString(unit_test.disabled_test_count()));
|
||||||
|
OutputXmlAttribute(stream, kTestsuites, "errors", "0");
|
||||||
|
OutputXmlAttribute(
|
||||||
|
stream, kTestsuites, "timestamp",
|
||||||
|
FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()));
|
||||||
|
OutputXmlAttribute(stream, kTestsuites, "time",
|
||||||
|
FormatTimeInMillisAsSeconds(unit_test.elapsed_time()));
|
||||||
|
|
||||||
if (GTEST_FLAG(shuffle)) {
|
if (GTEST_FLAG(shuffle)) {
|
||||||
fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed());
|
OutputXmlAttribute(stream, kTestsuites, "random_seed",
|
||||||
|
StreamableToString(unit_test.random_seed()));
|
||||||
}
|
}
|
||||||
fprintf(out, "name=\"AllTests\">\n");
|
|
||||||
for (int i = 0; i < unit_test.total_test_case_count(); ++i)
|
*stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result());
|
||||||
PrintXmlTestCase(out, *unit_test.GetTestCase(i));
|
|
||||||
fprintf(out, "</testsuites>\n");
|
OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
|
||||||
|
*stream << ">\n";
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
|
||||||
|
PrintXmlTestCase(stream, *unit_test.GetTestCase(i));
|
||||||
|
}
|
||||||
|
*stream << "</" << kTestsuites << ">\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produces a string representing the test properties in a result as space
|
// Produces a string representing the test properties in a result as space
|
||||||
|
@ -3452,7 +3574,7 @@ void TestEventListeners::SuppressEventForwarding() {
|
||||||
// We don't protect this under mutex_ as a user is not supposed to
|
// We don't protect this under mutex_ as a user is not supposed to
|
||||||
// call this before main() starts, from which point on the return
|
// call this before main() starts, from which point on the return
|
||||||
// value will never change.
|
// value will never change.
|
||||||
UnitTest * UnitTest::GetInstance() {
|
UnitTest* UnitTest::GetInstance() {
|
||||||
// When compiled with MSVC 7.1 in optimized mode, destroying the
|
// When compiled with MSVC 7.1 in optimized mode, destroying the
|
||||||
// UnitTest object upon exiting the program messes up the exit code,
|
// UnitTest object upon exiting the program messes up the exit code,
|
||||||
// causing successful tests to appear failed. We have to use a
|
// causing successful tests to appear failed. We have to use a
|
||||||
|
@ -3537,6 +3659,12 @@ const TestCase* UnitTest::GetTestCase(int i) const {
|
||||||
return impl()->GetTestCase(i);
|
return impl()->GetTestCase(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the TestResult containing information on test failures and
|
||||||
|
// properties logged outside of individual test cases.
|
||||||
|
const TestResult& UnitTest::ad_hoc_test_result() const {
|
||||||
|
return *impl()->ad_hoc_test_result();
|
||||||
|
}
|
||||||
|
|
||||||
// Gets the i-th test case among all the test cases. i can range from 0 to
|
// Gets the i-th test case among all the test cases. i can range from 0 to
|
||||||
// total_test_case_count() - 1. If i is not in that range, returns NULL.
|
// total_test_case_count() - 1. If i is not in that range, returns NULL.
|
||||||
TestCase* UnitTest::GetMutableTestCase(int i) {
|
TestCase* UnitTest::GetMutableTestCase(int i) {
|
||||||
|
@ -3635,12 +3763,14 @@ void UnitTest::AddTestPartResult(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates and adds a property to the current TestResult. If a property matching
|
// Adds a TestProperty to the current TestResult object when invoked from
|
||||||
// the supplied value already exists, updates its value instead.
|
// inside a test, to current TestCase's ad_hoc_test_result_ when invoked
|
||||||
void UnitTest::RecordPropertyForCurrentTest(const char* key,
|
// from SetUpTestCase or TearDownTestCase, or to the global property set
|
||||||
const char* value) {
|
// when invoked elsewhere. If the result already contains a property with
|
||||||
const TestProperty test_property(key, value);
|
// the same key, the value will be updated.
|
||||||
impl_->current_test_result()->RecordProperty(test_property);
|
void UnitTest::RecordProperty(const std::string& key,
|
||||||
|
const std::string& value) {
|
||||||
|
impl_->RecordProperty(TestProperty(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs all tests in this UnitTest object and prints the result.
|
// Runs all tests in this UnitTest object and prints the result.
|
||||||
|
@ -3813,6 +3943,28 @@ UnitTestImpl::~UnitTestImpl() {
|
||||||
delete os_stack_trace_getter_;
|
delete os_stack_trace_getter_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds a TestProperty to the current TestResult object when invoked in a
|
||||||
|
// context of a test, to current test case's ad_hoc_test_result when invoke
|
||||||
|
// from SetUpTestCase/TearDownTestCase, or to the global property set
|
||||||
|
// otherwise. If the result already contains a property with the same key,
|
||||||
|
// the value will be updated.
|
||||||
|
void UnitTestImpl::RecordProperty(const TestProperty& test_property) {
|
||||||
|
std::string xml_element;
|
||||||
|
TestResult* test_result; // TestResult appropriate for property recording.
|
||||||
|
|
||||||
|
if (current_test_info_ != NULL) {
|
||||||
|
xml_element = "testcase";
|
||||||
|
test_result = &(current_test_info_->result_);
|
||||||
|
} else if (current_test_case_ != NULL) {
|
||||||
|
xml_element = "testsuite";
|
||||||
|
test_result = &(current_test_case_->ad_hoc_test_result_);
|
||||||
|
} else {
|
||||||
|
xml_element = "testsuites";
|
||||||
|
test_result = &ad_hoc_test_result_;
|
||||||
|
}
|
||||||
|
test_result->RecordProperty(xml_element, test_property);
|
||||||
|
}
|
||||||
|
|
||||||
#if GTEST_HAS_DEATH_TEST
|
#if GTEST_HAS_DEATH_TEST
|
||||||
// Disables event forwarding if the control is currently in a death test
|
// Disables event forwarding if the control is currently in a death test
|
||||||
// subprocess. Must not be called before InitGoogleTest.
|
// subprocess. Must not be called before InitGoogleTest.
|
||||||
|
|
|
@ -180,6 +180,18 @@ class TestEventListenersAccessor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UnitTestRecordPropertyTestHelper : public Test {
|
||||||
|
protected:
|
||||||
|
UnitTestRecordPropertyTestHelper() {}
|
||||||
|
|
||||||
|
// Forwards to UnitTest::RecordProperty() to bypass access controls.
|
||||||
|
void UnitTestRecordProperty(const char* key, const std::string& value) {
|
||||||
|
unit_test_.RecordProperty(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnitTest unit_test_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
|
||||||
|
@ -188,6 +200,7 @@ using testing::AssertionResult;
|
||||||
using testing::AssertionSuccess;
|
using testing::AssertionSuccess;
|
||||||
using testing::DoubleLE;
|
using testing::DoubleLE;
|
||||||
using testing::EmptyTestEventListener;
|
using testing::EmptyTestEventListener;
|
||||||
|
using testing::Environment;
|
||||||
using testing::FloatLE;
|
using testing::FloatLE;
|
||||||
using testing::GTEST_FLAG(also_run_disabled_tests);
|
using testing::GTEST_FLAG(also_run_disabled_tests);
|
||||||
using testing::GTEST_FLAG(break_on_failure);
|
using testing::GTEST_FLAG(break_on_failure);
|
||||||
|
@ -213,6 +226,7 @@ using testing::StaticAssertTypeEq;
|
||||||
using testing::Test;
|
using testing::Test;
|
||||||
using testing::TestCase;
|
using testing::TestCase;
|
||||||
using testing::TestEventListeners;
|
using testing::TestEventListeners;
|
||||||
|
using testing::TestInfo;
|
||||||
using testing::TestPartResult;
|
using testing::TestPartResult;
|
||||||
using testing::TestPartResultArray;
|
using testing::TestPartResultArray;
|
||||||
using testing::TestProperty;
|
using testing::TestProperty;
|
||||||
|
@ -1447,7 +1461,7 @@ TEST(TestResultPropertyTest, NoPropertiesFoundWhenNoneAreAdded) {
|
||||||
TEST(TestResultPropertyTest, OnePropertyFoundWhenAdded) {
|
TEST(TestResultPropertyTest, OnePropertyFoundWhenAdded) {
|
||||||
TestResult test_result;
|
TestResult test_result;
|
||||||
TestProperty property("key_1", "1");
|
TestProperty property("key_1", "1");
|
||||||
TestResultAccessor::RecordProperty(&test_result, property);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property);
|
||||||
ASSERT_EQ(1, test_result.test_property_count());
|
ASSERT_EQ(1, test_result.test_property_count());
|
||||||
const TestProperty& actual_property = test_result.GetTestProperty(0);
|
const TestProperty& actual_property = test_result.GetTestProperty(0);
|
||||||
EXPECT_STREQ("key_1", actual_property.key());
|
EXPECT_STREQ("key_1", actual_property.key());
|
||||||
|
@ -1459,8 +1473,8 @@ TEST(TestResultPropertyTest, MultiplePropertiesFoundWhenAdded) {
|
||||||
TestResult test_result;
|
TestResult test_result;
|
||||||
TestProperty property_1("key_1", "1");
|
TestProperty property_1("key_1", "1");
|
||||||
TestProperty property_2("key_2", "2");
|
TestProperty property_2("key_2", "2");
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_1);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_1);
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_2);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_2);
|
||||||
ASSERT_EQ(2, test_result.test_property_count());
|
ASSERT_EQ(2, test_result.test_property_count());
|
||||||
const TestProperty& actual_property_1 = test_result.GetTestProperty(0);
|
const TestProperty& actual_property_1 = test_result.GetTestProperty(0);
|
||||||
EXPECT_STREQ("key_1", actual_property_1.key());
|
EXPECT_STREQ("key_1", actual_property_1.key());
|
||||||
|
@ -1478,10 +1492,10 @@ TEST(TestResultPropertyTest, OverridesValuesForDuplicateKeys) {
|
||||||
TestProperty property_2_1("key_2", "2");
|
TestProperty property_2_1("key_2", "2");
|
||||||
TestProperty property_1_2("key_1", "12");
|
TestProperty property_1_2("key_1", "12");
|
||||||
TestProperty property_2_2("key_2", "22");
|
TestProperty property_2_2("key_2", "22");
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_1_1);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_1_1);
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_2_1);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_2_1);
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_1_2);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_1_2);
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_2_2);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_2_2);
|
||||||
|
|
||||||
ASSERT_EQ(2, test_result.test_property_count());
|
ASSERT_EQ(2, test_result.test_property_count());
|
||||||
const TestProperty& actual_property_1 = test_result.GetTestProperty(0);
|
const TestProperty& actual_property_1 = test_result.GetTestProperty(0);
|
||||||
|
@ -1494,14 +1508,14 @@ TEST(TestResultPropertyTest, OverridesValuesForDuplicateKeys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests TestResult::GetTestProperty().
|
// Tests TestResult::GetTestProperty().
|
||||||
TEST(TestResultPropertyDeathTest, GetTestProperty) {
|
TEST(TestResultPropertyTest, GetTestProperty) {
|
||||||
TestResult test_result;
|
TestResult test_result;
|
||||||
TestProperty property_1("key_1", "1");
|
TestProperty property_1("key_1", "1");
|
||||||
TestProperty property_2("key_2", "2");
|
TestProperty property_2("key_2", "2");
|
||||||
TestProperty property_3("key_3", "3");
|
TestProperty property_3("key_3", "3");
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_1);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_1);
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_2);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_2);
|
||||||
TestResultAccessor::RecordProperty(&test_result, property_3);
|
TestResultAccessor::RecordProperty(&test_result, "testcase", property_3);
|
||||||
|
|
||||||
const TestProperty& fetched_property_1 = test_result.GetTestProperty(0);
|
const TestProperty& fetched_property_1 = test_result.GetTestProperty(0);
|
||||||
const TestProperty& fetched_property_2 = test_result.GetTestProperty(1);
|
const TestProperty& fetched_property_2 = test_result.GetTestProperty(1);
|
||||||
|
@ -1520,42 +1534,6 @@ TEST(TestResultPropertyDeathTest, GetTestProperty) {
|
||||||
EXPECT_DEATH_IF_SUPPORTED(test_result.GetTestProperty(-1), "");
|
EXPECT_DEATH_IF_SUPPORTED(test_result.GetTestProperty(-1), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a property using a reserved key is supplied to this function, it tests
|
|
||||||
// that a non-fatal failure is added, a fatal failure is not added, and that the
|
|
||||||
// property is not recorded.
|
|
||||||
void ExpectNonFatalFailureRecordingPropertyWithReservedKey(const char* key) {
|
|
||||||
TestResult test_result;
|
|
||||||
TestProperty property(key, "1");
|
|
||||||
EXPECT_NONFATAL_FAILURE(
|
|
||||||
TestResultAccessor::RecordProperty(&test_result, property),
|
|
||||||
"Reserved key");
|
|
||||||
ASSERT_EQ(0, test_result.test_property_count()) << "Not recorded";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempting to recording a property with the Reserved literal "name"
|
|
||||||
// should add a non-fatal failure and the property should not be recorded.
|
|
||||||
TEST(TestResultPropertyTest, AddFailureWhenUsingReservedKeyCalledName) {
|
|
||||||
ExpectNonFatalFailureRecordingPropertyWithReservedKey("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempting to recording a property with the Reserved literal "status"
|
|
||||||
// should add a non-fatal failure and the property should not be recorded.
|
|
||||||
TEST(TestResultPropertyTest, AddFailureWhenUsingReservedKeyCalledStatus) {
|
|
||||||
ExpectNonFatalFailureRecordingPropertyWithReservedKey("status");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempting to recording a property with the Reserved literal "time"
|
|
||||||
// should add a non-fatal failure and the property should not be recorded.
|
|
||||||
TEST(TestResultPropertyTest, AddFailureWhenUsingReservedKeyCalledTime) {
|
|
||||||
ExpectNonFatalFailureRecordingPropertyWithReservedKey("time");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempting to recording a property with the Reserved literal "classname"
|
|
||||||
// should add a non-fatal failure and the property should not be recorded.
|
|
||||||
TEST(TestResultPropertyTest, AddFailureWhenUsingReservedKeyCalledClassname) {
|
|
||||||
ExpectNonFatalFailureRecordingPropertyWithReservedKey("classname");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that GTestFlagSaver works on Windows and Mac.
|
// Tests that GTestFlagSaver works on Windows and Mac.
|
||||||
|
|
||||||
class GTestFlagSaverTest : public Test {
|
class GTestFlagSaverTest : public Test {
|
||||||
|
@ -1961,6 +1939,168 @@ TEST(UnitTestTest, ReturnsPlausibleTimestamp) {
|
||||||
EXPECT_LE(UnitTest::GetInstance()->start_timestamp(), GetTimeInMillis());
|
EXPECT_LE(UnitTest::GetInstance()->start_timestamp(), GetTimeInMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When a property using a reserved key is supplied to this function, it
|
||||||
|
// tests that a non-fatal failure is added, a fatal failure is not added,
|
||||||
|
// and that the property is not recorded.
|
||||||
|
void ExpectNonFatalFailureRecordingPropertyWithReservedKey(
|
||||||
|
const TestResult& test_result, const char* key) {
|
||||||
|
EXPECT_NONFATAL_FAILURE(Test::RecordProperty(key, "1"), "Reserved key");
|
||||||
|
ASSERT_EQ(0, test_result.test_property_count()) << "Property for key '" << key
|
||||||
|
<< "' recorded unexpectedly.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
const char* key) {
|
||||||
|
const TestInfo* test_info = UnitTest::GetInstance()->current_test_info();
|
||||||
|
ASSERT_TRUE(test_info != NULL);
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKey(*test_info->result(),
|
||||||
|
key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
const char* key) {
|
||||||
|
const TestCase* test_case = UnitTest::GetInstance()->current_test_case();
|
||||||
|
ASSERT_TRUE(test_case != NULL);
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKey(
|
||||||
|
test_case->ad_hoc_test_result(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
const char* key) {
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKey(
|
||||||
|
UnitTest::GetInstance()->ad_hoc_test_result(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that property recording functions in UnitTest outside of tests
|
||||||
|
// functions correcly. Creating a separate instance of UnitTest ensures it
|
||||||
|
// is in a state similar to the UnitTest's singleton's between tests.
|
||||||
|
class UnitTestRecordPropertyTest :
|
||||||
|
public testing::internal::UnitTestRecordPropertyTestHelper {
|
||||||
|
public:
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
"disabled");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
"errors");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
"failures");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
"name");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
"tests");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase(
|
||||||
|
"time");
|
||||||
|
|
||||||
|
Test::RecordProperty("test_case_key_1", "1");
|
||||||
|
const TestCase* test_case = UnitTest::GetInstance()->current_test_case();
|
||||||
|
ASSERT_TRUE(test_case != NULL);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, test_case->ad_hoc_test_result().test_property_count());
|
||||||
|
EXPECT_STREQ("test_case_key_1",
|
||||||
|
test_case->ad_hoc_test_result().GetTestProperty(0).key());
|
||||||
|
EXPECT_STREQ("1",
|
||||||
|
test_case->ad_hoc_test_result().GetTestProperty(0).value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tests TestResult has the expected property when added.
|
||||||
|
TEST_F(UnitTestRecordPropertyTest, OnePropertyFoundWhenAdded) {
|
||||||
|
UnitTestRecordProperty("key_1", "1");
|
||||||
|
|
||||||
|
ASSERT_EQ(1, unit_test_.ad_hoc_test_result().test_property_count());
|
||||||
|
|
||||||
|
EXPECT_STREQ("key_1",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(0).key());
|
||||||
|
EXPECT_STREQ("1",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(0).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests TestResult has multiple properties when added.
|
||||||
|
TEST_F(UnitTestRecordPropertyTest, MultiplePropertiesFoundWhenAdded) {
|
||||||
|
UnitTestRecordProperty("key_1", "1");
|
||||||
|
UnitTestRecordProperty("key_2", "2");
|
||||||
|
|
||||||
|
ASSERT_EQ(2, unit_test_.ad_hoc_test_result().test_property_count());
|
||||||
|
|
||||||
|
EXPECT_STREQ("key_1",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(0).key());
|
||||||
|
EXPECT_STREQ("1", unit_test_.ad_hoc_test_result().GetTestProperty(0).value());
|
||||||
|
|
||||||
|
EXPECT_STREQ("key_2",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(1).key());
|
||||||
|
EXPECT_STREQ("2", unit_test_.ad_hoc_test_result().GetTestProperty(1).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests TestResult::RecordProperty() overrides values for duplicate keys.
|
||||||
|
TEST_F(UnitTestRecordPropertyTest, OverridesValuesForDuplicateKeys) {
|
||||||
|
UnitTestRecordProperty("key_1", "1");
|
||||||
|
UnitTestRecordProperty("key_2", "2");
|
||||||
|
UnitTestRecordProperty("key_1", "12");
|
||||||
|
UnitTestRecordProperty("key_2", "22");
|
||||||
|
|
||||||
|
ASSERT_EQ(2, unit_test_.ad_hoc_test_result().test_property_count());
|
||||||
|
|
||||||
|
EXPECT_STREQ("key_1",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(0).key());
|
||||||
|
EXPECT_STREQ("12",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(0).value());
|
||||||
|
|
||||||
|
EXPECT_STREQ("key_2",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(1).key());
|
||||||
|
EXPECT_STREQ("22",
|
||||||
|
unit_test_.ad_hoc_test_result().GetTestProperty(1).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UnitTestRecordPropertyTest,
|
||||||
|
AddFailureInsideTestsWhenUsingTestCaseReservedKeys) {
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
"name");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
"value_param");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
"type_param");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
"status");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
"time");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest(
|
||||||
|
"classname");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UnitTestRecordPropertyTest,
|
||||||
|
AddRecordWithReservedKeysGeneratesCorrectPropertyList) {
|
||||||
|
EXPECT_NONFATAL_FAILURE(
|
||||||
|
Test::RecordProperty("name", "1"),
|
||||||
|
"'classname', 'name', 'status', 'time', 'type_param', and 'value_param'"
|
||||||
|
" are reserved");
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnitTestRecordPropertyTestEnvironment : public Environment {
|
||||||
|
public:
|
||||||
|
virtual void TearDown() {
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"tests");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"failures");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"disabled");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"errors");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"name");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"timestamp");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"time");
|
||||||
|
ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase(
|
||||||
|
"random_seed");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This will test property recording outside of any test or test case.
|
||||||
|
static Environment* record_property_env =
|
||||||
|
AddGlobalTestEnvironment(new UnitTestRecordPropertyTestEnvironment);
|
||||||
|
|
||||||
// This group of tests is for predicate assertions (ASSERT_PRED*, etc)
|
// This group of tests is for predicate assertions (ASSERT_PRED*, etc)
|
||||||
// of various arities. They do not attempt to be exhaustive. Rather,
|
// of various arities. They do not attempt to be exhaustive. Rather,
|
||||||
// view them as smoke tests that can be easily reviewed and verified.
|
// view them as smoke tests that can be easily reviewed and verified.
|
||||||
|
|
|
@ -57,7 +57,7 @@ else:
|
||||||
STACK_TRACE_TEMPLATE = ''
|
STACK_TRACE_TEMPLATE = ''
|
||||||
|
|
||||||
EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<testsuites tests="23" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests">
|
<testsuites tests="23" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
|
||||||
<testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" errors="0" time="*">
|
<testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" errors="0" time="*">
|
||||||
<testcase name="Succeeds" status="run" time="*" classname="SuccessfulTest"/>
|
<testcase name="Succeeds" status="run" time="*" classname="SuccessfulTest"/>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
@ -97,7 +97,7 @@ Invalid characters in brackets []%(stack)s]]></failure>
|
||||||
<testsuite name="DisabledTest" tests="1" failures="0" disabled="1" errors="0" time="*">
|
<testsuite name="DisabledTest" tests="1" failures="0" disabled="1" errors="0" time="*">
|
||||||
<testcase name="DISABLED_test_not_run" status="notrun" time="*" classname="DisabledTest"/>
|
<testcase name="DISABLED_test_not_run" status="notrun" time="*" classname="DisabledTest"/>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="PropertyRecordingTest" tests="4" failures="0" disabled="0" errors="0" time="*">
|
<testsuite name="PropertyRecordingTest" tests="4" failures="0" disabled="0" errors="0" time="*" SetUpTestCase="yes" TearDownTestCase="aye">
|
||||||
<testcase name="OneProperty" status="run" time="*" classname="PropertyRecordingTest" key_1="1"/>
|
<testcase name="OneProperty" status="run" time="*" classname="PropertyRecordingTest" key_1="1"/>
|
||||||
<testcase name="IntValuedProperty" status="run" time="*" classname="PropertyRecordingTest" key_int="1"/>
|
<testcase name="IntValuedProperty" status="run" time="*" classname="PropertyRecordingTest" key_int="1"/>
|
||||||
<testcase name="ThreeProperties" status="run" time="*" classname="PropertyRecordingTest" key_1="1" key_2="2" key_3="3"/>
|
<testcase name="ThreeProperties" status="run" time="*" classname="PropertyRecordingTest" key_1="1" key_2="2" key_3="3"/>
|
||||||
|
|
|
@ -95,6 +95,9 @@ TEST(InvalidCharactersTest, InvalidCharactersInMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PropertyRecordingTest : public Test {
|
class PropertyRecordingTest : public Test {
|
||||||
|
public:
|
||||||
|
static void SetUpTestCase() { RecordProperty("SetUpTestCase", "yes"); }
|
||||||
|
static void TearDownTestCase() { RecordProperty("TearDownTestCase", "aye"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(PropertyRecordingTest, OneProperty) {
|
TEST_F(PropertyRecordingTest, OneProperty) {
|
||||||
|
@ -120,12 +123,12 @@ TEST(NoFixtureTest, RecordProperty) {
|
||||||
RecordProperty("key", "1");
|
RecordProperty("key", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExternalUtilityThatCallsRecordProperty(const char* key, int value) {
|
void ExternalUtilityThatCallsRecordProperty(const std::string& key, int value) {
|
||||||
testing::Test::RecordProperty(key, value);
|
testing::Test::RecordProperty(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExternalUtilityThatCallsRecordProperty(const char* key,
|
void ExternalUtilityThatCallsRecordProperty(const std::string& key,
|
||||||
const char* value) {
|
const std::string& value) {
|
||||||
testing::Test::RecordProperty(key, value);
|
testing::Test::RecordProperty(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,5 +176,6 @@ int main(int argc, char** argv) {
|
||||||
TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
|
TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
|
||||||
delete listeners.Release(listeners.default_xml_generator());
|
delete listeners.Release(listeners.default_xml_generator());
|
||||||
}
|
}
|
||||||
|
testing::Test::RecordProperty("ad_hoc_property", "42");
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,9 @@ class GTestXMLTestCase(gtest_test_utils.TestCase):
|
||||||
actual_attributes = actual_node .attributes
|
actual_attributes = actual_node .attributes
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
expected_attributes.length, actual_attributes.length,
|
expected_attributes.length, actual_attributes.length,
|
||||||
'attribute numbers differ in element ' + actual_node.tagName)
|
'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % (
|
||||||
|
actual_node.tagName, expected_attributes.keys(),
|
||||||
|
actual_attributes.keys()))
|
||||||
for i in range(expected_attributes.length):
|
for i in range(expected_attributes.length):
|
||||||
expected_attr = expected_attributes.item(i)
|
expected_attr = expected_attributes.item(i)
|
||||||
actual_attr = actual_attributes.get(expected_attr.name)
|
actual_attr = actual_attributes.get(expected_attr.name)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user